<commit_before>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package queue provides queue implementations that can be used for
// asynchronous scheduling of fetch actions.
package queue

import (
	"context"
	"errors"
	"fmt"
	"hash/fnv"
	"io"
	"math"
	"strings"
	"time"

	cloudtasks "cloud.google.com/go/cloudtasks/apiv2"
	"github.com/golang/protobuf/ptypes"
	"golang.org/x/pkgsite/internal/config"
	"golang.org/x/pkgsite/internal/derrors"
	"golang.org/x/pkgsite/internal/experiment"
	"golang.org/x/pkgsite/internal/log"
	"golang.org/x/pkgsite/internal/middleware"
	taskspb "google.golang.org/genproto/googleapis/cloud/tasks/v2"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// A Queue provides an interface for asynchronous scheduling of fetch actions.
type Queue interface {
	ScheduleFetch(ctx context.Context, modulePath, version, suffix string, disableProxyFetch bool) (bool, error)
}

// New creates a new Queue with name queueName based on the configuration
// in cfg. When running locally, Queue uses numWorkers concurrent workers.
func New(ctx context.Context, cfg *config.Config, queueName string, numWorkers int, expGetter middleware.ExperimentGetter, processFunc inMemoryProcessFunc) (Queue, error) {
	if !cfg.OnGCP() {
		experiments, err := expGetter(ctx)
		if err != nil {
			return nil, err
		}
		var names []string
		for _, e := range experiments {
			if e.Rollout > 0 {
				names = append(names, e.Name)
			}
		}
		return NewInMemory(ctx, numWorkers, names, processFunc), nil
	}

	client, err := cloudtasks.NewClient(ctx)
	if err != nil {
		return nil, err
	}
	g, err := newGCP(cfg, client, queueName)
	if err != nil {
		return nil, err
	}
	log.Infof(ctx, "enqueuing at %s with queueURL=%q", g.queueName, g.queueURL)
	return g, nil
}

// GCP provides a Queue implementation backed by the Google Cloud Tasks
// API.
type GCP struct {
	client    *cloudtasks.Client
	queueName string // full GCP name of the queue
	queueURL  string // non-AppEngine URL to post tasks to
	// token holds information that lets the task queue construct an authorized request to the worker.
	// Since the worker sits behind the IAP, the queue needs an identity token that includes the
	// identity of a service account that has access, and the client ID for the IAP.
	// We use the service account of the current process.
	token *taskspb.HttpRequest_OidcToken
}

// NewGCP returns a new Queue that can be used to enqueue tasks using the
// cloud tasks API.  The given queueID should be the name of the queue in the
// cloud tasks console.
func newGCP(cfg *config.Config, client *cloudtasks.Client, queueID string) (_ *GCP, err error) {
	defer derrors.Wrap(&err, "newGCP(cfg, client, %q)", queueID)
	if queueID == "" {
		return nil, errors.New("empty queueID")
	}
	if cfg.ProjectID == "" {
		return nil, errors.New("empty ProjectID")
	}
	if cfg.LocationID == "" {
		return nil, errors.New("empty LocationID")
	}
	if cfg.QueueURL == "" {
		return nil, errors.New("empty QueueURL")
	}
	if cfg.ServiceAccount == "" {
		return nil, errors.New("empty ServiceAccount")
	}
	if cfg.QueueAudience == "" {
		return nil, errors.New("empty QueueAudience")
	}
	return &GCP{
		client:    client,
		queueName: fmt.Sprintf("projects/%s/locations/%s/queues/%s", cfg.ProjectID, cfg.LocationID, queueID),
		queueURL:  cfg.QueueURL,
		token: &taskspb.HttpRequest_OidcToken{
			OidcToken: &taskspb.OidcToken{
				ServiceAccountEmail: cfg.ServiceAccount,
				Audience:            cfg.QueueAudience,
			},
		},
	}, nil
}

// ScheduleFetch enqueues a task on GCP to fetch the given modulePath and
// version. It returns an error if there was an error hashing the task name, or
// an error pushing the task to GCP. If the task was a duplicate, it returns (false, nil).
func (q *GCP) ScheduleFetch(ctx context.Context, modulePath, version, suffix string, disableProxyFetch bool) (enqueued bool, err error) {
	// the new taskqueue API requires a deadline of <= 30s
	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
	defer cancel()
	defer derrors.Wrap(&err, "queue.ScheduleFetch(%q, %q, %q)", modulePath, version, suffix)

	req := q.newTaskRequest(modulePath, version, suffix, disableProxyFetch)
	enqueued = true
	if _, err := q.client.CreateTask(ctx, req); err != nil {
		if status.Code(err) == codes.AlreadyExists {
			log.Debugf(ctx, "ignoring duplicate task ID %s: %s@%s", req.Task.Name, modulePath, version)
			enqueued = false
		} else {
			return false, fmt.Errorf("q.client.CreateTask(ctx, req): %v", err)
		}
	}
	return enqueued, nil
}

// Maximum timeout for HTTP tasks.
// See https://cloud.google.com/tasks/docs/creating-http-target-tasks.
const maxCloudTasksTimeout = 30 * time.Minute

const (
	DisableProxyFetchParam = "proxyfetch"
	DisableProxyFetchValue = "off"
)

func (q *GCP) newTaskRequest(modulePath, version, suffix string, disableProxyFetch bool) *taskspb.CreateTaskRequest {
	taskID := newTaskID(modulePath, version)
	relativeURI := fmt.Sprintf("/fetch/%s/@v/%s", modulePath, version)
	if disableProxyFetch {
		relativeURI += fmt.Sprintf("?%s=%s", DisableProxyFetchParam, DisableProxyFetchValue)
	}
	task := &taskspb.Task{
		Name:             fmt.Sprintf("%s/tasks/%s", q.queueName, taskID),
		DispatchDeadline: ptypes.DurationProto(maxCloudTasksTimeout),
	}
	task.MessageType = &taskspb.Task_HttpRequest{
		HttpRequest: &taskspb.HttpRequest{
			HttpMethod:          taskspb.HttpMethod_POST,
			Url:                 q.queueURL + relativeURI,
			AuthorizationHeader: q.token,
		},
	}
	req := &taskspb.CreateTaskRequest{
		Parent: q.queueName,
		Task:   task,
	}
	// If suffix is non-empty, append it to the task name. This lets us force reprocessing
	// of tasks that would normally be de-duplicated.
	if suffix != "" {
		req.Task.Name += "-" + suffix
	}
	return req
}

// Create a task ID for the given module path and version.
// Task IDs can contain only letters ([A-Za-z]), numbers ([0-9]), hyphens (-), or underscores (_).
func newTaskID(modulePath, version string) string {
	mv := modulePath + "@" + version
	// Compute a hash to use as a prefix, so the task IDs are distributed uniformly.
	// See https://cloud.google.com/tasks/docs/reference/rpc/google.cloud.tasks.v2#task
	// under "Task De-duplication".
	hasher := fnv.New32()
	io.WriteString(hasher, mv)
	hash := hasher.Sum32() % math.MaxUint16
	// Escape the name so it contains only valid characters. Do our best to make it readable.
	var b strings.Builder
	for _, r := range mv {
		switch {
		case r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' || r >= '0' && r <= '9' || r == '-':
			b.WriteRune(r)
		case r == '_':
			b.WriteString("__")
		case r == '/':
			b.WriteString("_-")
		case r == '@':
			b.WriteString("_v")
		case r == '.':
			b.WriteString("_o")
		default:
			fmt.Fprintf(&b, "_%04x", r)
		}
	}
	return fmt.Sprintf("%04x-%s", hash, &b)
}

type moduleVersion struct {
	modulePath, version string
}

// InMemory is a Queue implementation that schedules in-process fetch
// operations. Unlike the GCP task queue, it will not automatically retry tasks
// on failure.
//
// This should only be used for local development.
type InMemory struct {
	queue       chan moduleVersion
	sem         chan struct{}
	experiments []string
}

type inMemoryProcessFunc func(context.Context, string, string) (int, error)

// NewInMemory creates a new InMemory that asynchronously fetches
// from proxyClient and stores in db. It uses workerCount parallelism to
// execute these fetches.
func NewInMemory(ctx context.Context, workerCount int, experiments []string, processFunc inMemoryProcessFunc) *InMemory {
	q := &InMemory{
		queue:       make(chan moduleVersion, 1000),
		sem:         make(chan struct{}, workerCount),
		experiments: experiments,
	}
	go func() {
		for v := range q.queue {
			select {
			case <-ctx.Done():
				return
			case q.sem <- struct{}{}:
			}

			// If a worker is available, make a request to the fetch service inside a
			// goroutine and wait for it to finish.
			go func(v moduleVersion) {
				defer func() { <-q.sem }()

				log.Infof(ctx, "Fetch requested: %q %q (workerCount = %d)", v.modulePath, v.version, cap(q.sem))

				fetchCtx, cancel := context.WithTimeout(ctx, 5*time.Minute)
				fetchCtx = experiment.NewContext(fetchCtx, experiments...)
				defer cancel()

				if _, err := processFunc(fetchCtx, v.modulePath, v.version); err != nil {
					log.Error(fetchCtx, err)
				}
			}(v)
		}
	}()
	return q
}

// ScheduleFetch pushes a fetch task into the local queue to be processed
// asynchronously.
func (q *InMemory) ScheduleFetch(ctx context.Context, modulePath, version, _ string, _ bool) (bool, error) {
	q.queue <- moduleVersion{modulePath, version}
	return true, nil
}

// WaitForTesting waits for all queued requests to finish. It should only be
// used by test code.
func (q InMemory) WaitForTesting(ctx context.Context) {
	for i := 0; i < cap(q.sem); i++ {
		select {
		case <-ctx.Done():
			return
		case q.sem <- struct{}{}:
		}
	}
	close(q.queue)
}
<commit_msg>internal/queue: remove CreateTask timeout<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package queue provides queue implementations that can be used for
// asynchronous scheduling of fetch actions.
package queue

import (
	"context"
	"errors"
	"fmt"
	"hash/fnv"
	"io"
	"math"
	"strings"
	"time"

	cloudtasks "cloud.google.com/go/cloudtasks/apiv2"
	"github.com/golang/protobuf/ptypes"
	"golang.org/x/pkgsite/internal/config"
	"golang.org/x/pkgsite/internal/derrors"
	"golang.org/x/pkgsite/internal/experiment"
	"golang.org/x/pkgsite/internal/log"
	"golang.org/x/pkgsite/internal/middleware"
	taskspb "google.golang.org/genproto/googleapis/cloud/tasks/v2"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// A Queue provides an interface for asynchronous scheduling of fetch actions.
type Queue interface {
	ScheduleFetch(ctx context.Context, modulePath, version, suffix string, disableProxyFetch bool) (bool, error)
}

// New creates a new Queue with name queueName based on the configuration
// in cfg. When running locally, Queue uses numWorkers concurrent workers.
func New(ctx context.Context, cfg *config.Config, queueName string, numWorkers int, expGetter middleware.ExperimentGetter, processFunc inMemoryProcessFunc) (Queue, error) {
	if !cfg.OnGCP() {
		experiments, err := expGetter(ctx)
		if err != nil {
			return nil, err
		}
		var names []string
		for _, e := range experiments {
			if e.Rollout > 0 {
				names = append(names, e.Name)
			}
		}
		return NewInMemory(ctx, numWorkers, names, processFunc), nil
	}

	client, err := cloudtasks.NewClient(ctx)
	if err != nil {
		return nil, err
	}
	g, err := newGCP(cfg, client, queueName)
	if err != nil {
		return nil, err
	}
	log.Infof(ctx, "enqueuing at %s with queueURL=%q", g.queueName, g.queueURL)
	return g, nil
}

// GCP provides a Queue implementation backed by the Google Cloud Tasks
// API.
type GCP struct {
	client    *cloudtasks.Client
	queueName string // full GCP name of the queue
	queueURL  string // non-AppEngine URL to post tasks to
	// token holds information that lets the task queue construct an authorized request to the worker.
	// Since the worker sits behind the IAP, the queue needs an identity token that includes the
	// identity of a service account that has access, and the client ID for the IAP.
	// We use the service account of the current process.
	token *taskspb.HttpRequest_OidcToken
}

// NewGCP returns a new Queue that can be used to enqueue tasks using the
// cloud tasks API.  The given queueID should be the name of the queue in the
// cloud tasks console.
func newGCP(cfg *config.Config, client *cloudtasks.Client, queueID string) (_ *GCP, err error) {
	defer derrors.Wrap(&err, "newGCP(cfg, client, %q)", queueID)
	if queueID == "" {
		return nil, errors.New("empty queueID")
	}
	if cfg.ProjectID == "" {
		return nil, errors.New("empty ProjectID")
	}
	if cfg.LocationID == "" {
		return nil, errors.New("empty LocationID")
	}
	if cfg.QueueURL == "" {
		return nil, errors.New("empty QueueURL")
	}
	if cfg.ServiceAccount == "" {
		return nil, errors.New("empty ServiceAccount")
	}
	if cfg.QueueAudience == "" {
		return nil, errors.New("empty QueueAudience")
	}
	return &GCP{
		client:    client,
		queueName: fmt.Sprintf("projects/%s/locations/%s/queues/%s", cfg.ProjectID, cfg.LocationID, queueID),
		queueURL:  cfg.QueueURL,
		token: &taskspb.HttpRequest_OidcToken{
			OidcToken: &taskspb.OidcToken{
				ServiceAccountEmail: cfg.ServiceAccount,
				Audience:            cfg.QueueAudience,
			},
		},
	}, nil
}

// ScheduleFetch enqueues a task on GCP to fetch the given modulePath and
// version. It returns an error if there was an error hashing the task name, or
// an error pushing the task to GCP. If the task was a duplicate, it returns (false, nil).
func (q *GCP) ScheduleFetch(ctx context.Context, modulePath, version, suffix string, disableProxyFetch bool) (enqueued bool, err error) {
	defer derrors.WrapStack(&err, "queue.ScheduleFetch(%q, %q, %q)", modulePath, version, suffix)

	req := q.newTaskRequest(modulePath, version, suffix, disableProxyFetch)
	enqueued = true
	if _, err := q.client.CreateTask(ctx, req); err != nil {
		if status.Code(err) == codes.AlreadyExists {
			log.Debugf(ctx, "ignoring duplicate task ID %s: %s@%s", req.Task.Name, modulePath, version)
			enqueued = false
		} else {
			return false, fmt.Errorf("q.client.CreateTask(ctx, req): %v", err)
		}
	}
	return enqueued, nil
}

// Maximum timeout for HTTP tasks.
// See https://cloud.google.com/tasks/docs/creating-http-target-tasks.
const maxCloudTasksTimeout = 30 * time.Minute

const (
	DisableProxyFetchParam = "proxyfetch"
	DisableProxyFetchValue = "off"
)

func (q *GCP) newTaskRequest(modulePath, version, suffix string, disableProxyFetch bool) *taskspb.CreateTaskRequest {
	taskID := newTaskID(modulePath, version)
	relativeURI := fmt.Sprintf("/fetch/%s/@v/%s", modulePath, version)
	if disableProxyFetch {
		relativeURI += fmt.Sprintf("?%s=%s", DisableProxyFetchParam, DisableProxyFetchValue)
	}
	task := &taskspb.Task{
		Name:             fmt.Sprintf("%s/tasks/%s", q.queueName, taskID),
		DispatchDeadline: ptypes.DurationProto(maxCloudTasksTimeout),
	}
	task.MessageType = &taskspb.Task_HttpRequest{
		HttpRequest: &taskspb.HttpRequest{
			HttpMethod:          taskspb.HttpMethod_POST,
			Url:                 q.queueURL + relativeURI,
			AuthorizationHeader: q.token,
		},
	}
	req := &taskspb.CreateTaskRequest{
		Parent: q.queueName,
		Task:   task,
	}
	// If suffix is non-empty, append it to the task name. This lets us force reprocessing
	// of tasks that would normally be de-duplicated.
	if suffix != "" {
		req.Task.Name += "-" + suffix
	}
	return req
}

// Create a task ID for the given module path and version.
// Task IDs can contain only letters ([A-Za-z]), numbers ([0-9]), hyphens (-), or underscores (_).
func newTaskID(modulePath, version string) string {
	mv := modulePath + "@" + version
	// Compute a hash to use as a prefix, so the task IDs are distributed uniformly.
	// See https://cloud.google.com/tasks/docs/reference/rpc/google.cloud.tasks.v2#task
	// under "Task De-duplication".
	hasher := fnv.New32()
	io.WriteString(hasher, mv)
	hash := hasher.Sum32() % math.MaxUint16
	// Escape the name so it contains only valid characters. Do our best to make it readable.
	var b strings.Builder
	for _, r := range mv {
		switch {
		case r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' || r >= '0' && r <= '9' || r == '-':
			b.WriteRune(r)
		case r == '_':
			b.WriteString("__")
		case r == '/':
			b.WriteString("_-")
		case r == '@':
			b.WriteString("_v")
		case r == '.':
			b.WriteString("_o")
		default:
			fmt.Fprintf(&b, "_%04x", r)
		}
	}
	return fmt.Sprintf("%04x-%s", hash, &b)
}

type moduleVersion struct {
	modulePath, version string
}

// InMemory is a Queue implementation that schedules in-process fetch
// operations. Unlike the GCP task queue, it will not automatically retry tasks
// on failure.
//
// This should only be used for local development.
type InMemory struct {
	queue       chan moduleVersion
	sem         chan struct{}
	experiments []string
}

type inMemoryProcessFunc func(context.Context, string, string) (int, error)

// NewInMemory creates a new InMemory that asynchronously fetches
// from proxyClient and stores in db. It uses workerCount parallelism to
// execute these fetches.
func NewInMemory(ctx context.Context, workerCount int, experiments []string, processFunc inMemoryProcessFunc) *InMemory {
	q := &InMemory{
		queue:       make(chan moduleVersion, 1000),
		sem:         make(chan struct{}, workerCount),
		experiments: experiments,
	}
	go func() {
		for v := range q.queue {
			select {
			case <-ctx.Done():
				return
			case q.sem <- struct{}{}:
			}

			// If a worker is available, make a request to the fetch service inside a
			// goroutine and wait for it to finish.
			go func(v moduleVersion) {
				defer func() { <-q.sem }()

				log.Infof(ctx, "Fetch requested: %q %q (workerCount = %d)", v.modulePath, v.version, cap(q.sem))

				fetchCtx, cancel := context.WithTimeout(ctx, 5*time.Minute)
				fetchCtx = experiment.NewContext(fetchCtx, experiments...)
				defer cancel()

				if _, err := processFunc(fetchCtx, v.modulePath, v.version); err != nil {
					log.Error(fetchCtx, err)
				}
			}(v)
		}
	}()
	return q
}

// ScheduleFetch pushes a fetch task into the local queue to be processed
// asynchronously.
func (q *InMemory) ScheduleFetch(ctx context.Context, modulePath, version, _ string, _ bool) (bool, error) {
	q.queue <- moduleVersion{modulePath, version}
	return true, nil
}

// WaitForTesting waits for all queued requests to finish. It should only be
// used by test code.
func (q InMemory) WaitForTesting(ctx context.Context) {
	for i := 0; i < cap(q.sem); i++ {
		select {
		case <-ctx.Done():
			return
		case q.sem <- struct{}{}:
		}
	}
	close(q.queue)
}
<|endoftext|>


<commit_before>package mount // import "github.com/docker/docker/pkg/mount"

import (
	"sort"
	"strconv"
	"strings"

	"github.com/sirupsen/logrus"
)

// mountError records an error from mount or unmount operation
type mountError struct {
	op             string
	source, target string
	flags          uintptr
	data           string
	err            error
}

func (e *mountError) Error() string {
	out := e.op + " "

	if e.source != "" {
		out += e.source + ":" + e.target
	} else {
		out += e.target
	}

	if e.flags != uintptr(0) {
		out += ", flags: 0x" + strconv.FormatUint(uint64(e.flags), 16)
	}
	if e.data != "" {
		out += ", data: " + e.data
	}

	out += ": " + e.err.Error()
	return out
}

// Cause returns the underlying cause of the error
func (e *mountError) Cause() error {
	return e.err
}

// FilterFunc is a type defining a callback function
// to filter out unwanted entries. It takes a pointer
// to an Info struct (not fully populated, currently
// only Mountpoint is filled in), and returns two booleans:
//  - skip: true if the entry should be skipped
//  - stop: true if parsing should be stopped after the entry
type FilterFunc func(*Info) (skip, stop bool)

// PrefixFilter discards all entries whose mount points
// do not start with a prefix specified
func PrefixFilter(prefix string) FilterFunc {
	return func(m *Info) (bool, bool) {
		skip := !strings.HasPrefix(m.Mountpoint, prefix)
		return skip, false
	}
}

// SingleEntryFilter looks for a specific entry
func SingleEntryFilter(mp string) FilterFunc {
	return func(m *Info) (bool, bool) {
		if m.Mountpoint == mp {
			return false, true // don't skip, stop now
		}
		return true, false // skip, keep going
	}
}

// ParentsFilter returns all entries whose mount points
// can be parents of a path specified, discarding others.
// For example, given `/var/lib/docker/something`, entries
// like `/var/lib/docker`, `/var` and `/` are returned.
func ParentsFilter(path string) FilterFunc {
	return func(m *Info) (bool, bool) {
		skip := !strings.HasPrefix(path, m.Mountpoint)
		return skip, false
	}
}

// GetMounts retrieves a list of mounts for the current running process,
// with an optional filter applied (use nil for no filter).
func GetMounts(f FilterFunc) ([]*Info, error) {
	return parseMountTable(f)
}

// Mounted determines if a specified mountpoint has been mounted.
// On Linux it looks at /proc/self/mountinfo.
func Mounted(mountpoint string) (bool, error) {
	entries, err := GetMounts(SingleEntryFilter(mountpoint))
	if err != nil {
		return false, err
	}

	return len(entries) > 0, nil
}

// Mount will mount filesystem according to the specified configuration, on the
// condition that the target path is *not* already mounted. Options must be
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
// flags.go for supported option flags.
func Mount(device, target, mType, options string) error {
	flag, _ := parseOptions(options)
	if flag&REMOUNT != REMOUNT {
		if mounted, err := Mounted(target); err != nil || mounted {
			return err
		}
	}
	return ForceMount(device, target, mType, options)
}

// ForceMount will mount a filesystem according to the specified configuration,
// *regardless* if the target path is not already mounted. Options must be
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
// flags.go for supported option flags.
func ForceMount(device, target, mType, options string) error {
	flag, data := parseOptions(options)
	return mount(device, target, mType, uintptr(flag), data)
}

// Unmount lazily unmounts a filesystem on supported platforms, otherwise
// does a normal unmount.
func Unmount(target string) error {
	return unmount(target, mntDetach)
}

// RecursiveUnmount unmounts the target and all mounts underneath, starting with
// the deepsest mount first.
func RecursiveUnmount(target string) error {
	mounts, err := parseMountTable(PrefixFilter(target))
	if err != nil {
		return err
	}

	// Make the deepest mount be first
	sort.Slice(mounts, func(i, j int) bool {
		return len(mounts[i].Mountpoint) > len(mounts[j].Mountpoint)
	})

	for i, m := range mounts {
		logrus.Debugf("Trying to unmount %s", m.Mountpoint)
		err = unmount(m.Mountpoint, mntDetach)
		if err != nil {
			if i == len(mounts)-1 { // last mount
				if mounted, e := Mounted(m.Mountpoint); e != nil || mounted {
					return err
				}
			} else {
				// This is some submount, we can ignore this error for now, the final unmount will fail if this is a real problem
				logrus.WithError(err).Warnf("Failed to unmount submount %s", m.Mountpoint)
			}
		}

		logrus.Debugf("Unmounted %s", m.Mountpoint)
	}
	return nil
}
<commit_msg>pkg/mount: Mount: minor optimization<commit_after>package mount // import "github.com/docker/docker/pkg/mount"

import (
	"sort"
	"strconv"
	"strings"

	"github.com/sirupsen/logrus"
)

// mountError records an error from mount or unmount operation
type mountError struct {
	op             string
	source, target string
	flags          uintptr
	data           string
	err            error
}

func (e *mountError) Error() string {
	out := e.op + " "

	if e.source != "" {
		out += e.source + ":" + e.target
	} else {
		out += e.target
	}

	if e.flags != uintptr(0) {
		out += ", flags: 0x" + strconv.FormatUint(uint64(e.flags), 16)
	}
	if e.data != "" {
		out += ", data: " + e.data
	}

	out += ": " + e.err.Error()
	return out
}

// Cause returns the underlying cause of the error
func (e *mountError) Cause() error {
	return e.err
}

// FilterFunc is a type defining a callback function
// to filter out unwanted entries. It takes a pointer
// to an Info struct (not fully populated, currently
// only Mountpoint is filled in), and returns two booleans:
//  - skip: true if the entry should be skipped
//  - stop: true if parsing should be stopped after the entry
type FilterFunc func(*Info) (skip, stop bool)

// PrefixFilter discards all entries whose mount points
// do not start with a prefix specified
func PrefixFilter(prefix string) FilterFunc {
	return func(m *Info) (bool, bool) {
		skip := !strings.HasPrefix(m.Mountpoint, prefix)
		return skip, false
	}
}

// SingleEntryFilter looks for a specific entry
func SingleEntryFilter(mp string) FilterFunc {
	return func(m *Info) (bool, bool) {
		if m.Mountpoint == mp {
			return false, true // don't skip, stop now
		}
		return true, false // skip, keep going
	}
}

// ParentsFilter returns all entries whose mount points
// can be parents of a path specified, discarding others.
// For example, given `/var/lib/docker/something`, entries
// like `/var/lib/docker`, `/var` and `/` are returned.
func ParentsFilter(path string) FilterFunc {
	return func(m *Info) (bool, bool) {
		skip := !strings.HasPrefix(path, m.Mountpoint)
		return skip, false
	}
}

// GetMounts retrieves a list of mounts for the current running process,
// with an optional filter applied (use nil for no filter).
func GetMounts(f FilterFunc) ([]*Info, error) {
	return parseMountTable(f)
}

// Mounted determines if a specified mountpoint has been mounted.
// On Linux it looks at /proc/self/mountinfo.
func Mounted(mountpoint string) (bool, error) {
	entries, err := GetMounts(SingleEntryFilter(mountpoint))
	if err != nil {
		return false, err
	}

	return len(entries) > 0, nil
}

// Mount will mount filesystem according to the specified configuration, on the
// condition that the target path is *not* already mounted. Options must be
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
// flags.go for supported option flags.
func Mount(device, target, mType, options string) error {
	flag, data := parseOptions(options)
	if flag&REMOUNT != REMOUNT {
		if mounted, err := Mounted(target); err != nil || mounted {
			return err
		}
	}
	return mount(device, target, mType, uintptr(flag), data)
}

// ForceMount will mount a filesystem according to the specified configuration,
// *regardless* if the target path is not already mounted. Options must be
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
// flags.go for supported option flags.
func ForceMount(device, target, mType, options string) error {
	flag, data := parseOptions(options)
	return mount(device, target, mType, uintptr(flag), data)
}

// Unmount lazily unmounts a filesystem on supported platforms, otherwise
// does a normal unmount.
func Unmount(target string) error {
	return unmount(target, mntDetach)
}

// RecursiveUnmount unmounts the target and all mounts underneath, starting with
// the deepsest mount first.
func RecursiveUnmount(target string) error {
	mounts, err := parseMountTable(PrefixFilter(target))
	if err != nil {
		return err
	}

	// Make the deepest mount be first
	sort.Slice(mounts, func(i, j int) bool {
		return len(mounts[i].Mountpoint) > len(mounts[j].Mountpoint)
	})

	for i, m := range mounts {
		logrus.Debugf("Trying to unmount %s", m.Mountpoint)
		err = unmount(m.Mountpoint, mntDetach)
		if err != nil {
			if i == len(mounts)-1 { // last mount
				if mounted, e := Mounted(m.Mountpoint); e != nil || mounted {
					return err
				}
			} else {
				// This is some submount, we can ignore this error for now, the final unmount will fail if this is a real problem
				logrus.WithError(err).Warnf("Failed to unmount submount %s", m.Mountpoint)
			}
		}

		logrus.Debugf("Unmounted %s", m.Mountpoint)
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2018 Mirantis

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package network

import (
	"net"
	"os"

	cnicurrent "github.com/containernetworking/cni/pkg/types/current"
)

// InterfaceType presents type of network interface instance
type InterfaceType int

const (
	InterfaceTypeTap InterfaceType = iota
	InterfaceTypeVF
)

type InterfaceDescription struct {
	// Type contains interface type designator
	Type InterfaceType
	// Fo contains open File object pointing to tap device inside network
	// namespace or to control file in sysfs for sr-iov VF.
	// It may be nil if the interface was recovered after restarting Virtlet.
	// It's only needed during the initial VM startup
	Fo *os.File
	// Name contains original interface name for sr-iov interface
	Name string
	// HardwareAddr contains original hardware address for CNI-created
	// veth link
	HardwareAddr net.HardwareAddr
	// PCIAddress contains a pci address for sr-iov vf interface
	PCIAddress string
	// MTU contains max transfer unit value for interface
	MTU uint16
}

// ContainerSideNetwork struct describes the container (VM) network
// namespace properties
type ContainerSideNetwork struct {
	// Result contains CNI result object describing the network settings
	Result *cnicurrent.Result
	// NsPath specifies the path to the container network namespace
	NsPath string
	// Interfaces contains a list of interfaces with data needed
	// to configure them
	Interfaces []*InterfaceDescription
}
<commit_msg>Fix linter issues in pkg/network/<commit_after>/*
Copyright 2018 Mirantis

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package network

import (
	"net"
	"os"

	cnicurrent "github.com/containernetworking/cni/pkg/types/current"
)

// InterfaceType presents type of network interface instance.
type InterfaceType int

const (
	// InterfaceTypeTap is a marker for tap type interface.
	InterfaceTypeTap InterfaceType = iota
	// InterfaceTypeVF is a marker for SR-IOV VF type interface.
	InterfaceTypeVF
)

// InterfaceDescription holds all data required by tapmanager to identify
// network interface.
type InterfaceDescription struct {
	// Type contains interface type designator.
	Type InterfaceType
	// Fo contains open File object pointing to tap device inside network
	// namespace or to control file in sysfs for sr-iov VF.
	// It may be nil if the interface was recovered after restarting Virtlet.
	// It's only needed during the initial VM startup.
	Fo *os.File
	// Name contains original interface name for sr-iov interface.
	Name string
	// HardwareAddr contains original hardware address for CNI-created
	// veth link.
	HardwareAddr net.HardwareAddr
	// PCIAddress contains a pci address for sr-iov vf interface.
	PCIAddress string
	// MTU contains max transfer unit value for interface.
	MTU uint16
}

// ContainerSideNetwork struct describes the container (VM) network
// namespace properties.
type ContainerSideNetwork struct {
	// Result contains CNI result object describing the network settings.
	Result *cnicurrent.Result
	// NsPath specifies the path to the container network namespace.
	NsPath string
	// Interfaces contains a list of interfaces with data needed
	// to configure them.
	Interfaces []*InterfaceDescription
}
<|endoftext|>


<commit_before>/*
 * Quick - Quick key value store for config files and persistent state files
 *
 * Minio Client (C) 2015 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package quick

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"runtime"
	"strings"
	"sync"

	"github.com/minio/minio/internal/github.com/fatih/structs"
	"github.com/minio/minio/pkg/probe"
)

// Config - generic config interface functions
type Config interface {
	String() string
	Version() string
	Save(string) *probe.Error
	Load(string) *probe.Error
	Data() interface{}
	Diff(Config) ([]structs.Field, *probe.Error)
	DeepDiff(Config) ([]structs.Field, *probe.Error)
}

// config - implements quick.Config interface
type config struct {
	data interface{}
	lock *sync.RWMutex
}

// CheckData - checks the validity of config data. Data sould be of type struct and contain a string type field called "Version"
func CheckData(data interface{}) *probe.Error {
	if !structs.IsStruct(data) {
		return probe.NewError(fmt.Errorf("Invalid argument type. Expecing \"struct\" type."))
	}

	st := structs.New(data)
	f, ok := st.FieldOk("Version")
	if !ok {
		return probe.NewError(fmt.Errorf("Invalid type of struct argument. No [%s.Version] field found.", st.Name()))
	}

	if f.Kind() != reflect.String {
		return probe.NewError(fmt.Errorf("Invalid type of struct argument. Expecting \"string\" type [%s.Version] field.", st.Name()))
	}

	return nil
}

// New - instantiate a new config
func New(data interface{}) (Config, *probe.Error) {
	err := CheckData(data)
	if err != nil {
		return nil, err.Trace()
	}

	d := new(config)
	d.data = data
	d.lock = new(sync.RWMutex)
	return d, nil
}

// Version returns the current config file format version
func (d config) Version() string {
	st := structs.New(d.data)

	f, ok := st.FieldOk("Version")
	if !ok {
		return ""
	}

	val := f.Value()
	ver, ok := val.(string)
	if ok {
		return ver
	}
	return ""
}

// String converts JSON config to printable string
func (d config) String() string {
	configBytes, _ := json.MarshalIndent(d.data, "", "\t")
	return string(configBytes)
}

// Save writes config data in JSON format to a file.
func (d config) Save(filename string) *probe.Error {
	d.lock.Lock()
	defer d.lock.Unlock()

	jsonData, err := json.MarshalIndent(d.data, "", "\t")
	if err != nil {
		return probe.NewError(err)
	}

	if runtime.GOOS == "windows" {
		jsonData = []byte(strings.Replace(string(jsonData), "\n", "\r\n", -1))
	}

	err = ioutil.WriteFile(filename, jsonData, 0600)
	if err != nil {
		return probe.NewError(err)
	}

	return nil
}

// Load - loads JSON config from file and merge with currently set values
func (d *config) Load(filename string) *probe.Error {
	d.lock.Lock()
	defer d.lock.Unlock()

	_, err := os.Stat(filename)
	if err != nil {
		return probe.NewError(err)
	}

	fileData, err := ioutil.ReadFile(filename)
	if err != nil {
		return probe.NewError(err)
	}

	if runtime.GOOS == "windows" {
		fileData = []byte(strings.Replace(string(fileData), "\r\n", "\n", -1))
	}

	err = json.Unmarshal(fileData, d.data)
	if err != nil {
		return probe.NewError(err)
	}

	if err := CheckData(d.data); err != nil {
		return err.Trace()
	}

	st := structs.New(d.data)
	f, ok := st.FieldOk("Version")
	if !ok {
		return probe.NewError(fmt.Errorf("Argument struct [%s] does not contain field \"Version\".", st.Name()))
	}

	if (*d).Version() != f.Value() {
		return probe.NewError(fmt.Errorf("Version mismatch"))
	}

	return nil
}

// Data - grab internal data map for reading
func (d config) Data() interface{} {
	return d.data
}

//Diff  - list fields that are in A but not in B
func (d config) Diff(c Config) ([]structs.Field, *probe.Error) {
	var fields []structs.Field
	err := CheckData(c.Data())
	if err != nil {
		return []structs.Field{}, err.Trace()
	}

	currFields := structs.Fields(d.Data())
	newFields := structs.Fields(c.Data())

	found := false
	for _, currField := range currFields {
		found = false
		for _, newField := range newFields {
			if reflect.DeepEqual(currField.Name(), newField.Name()) {
				found = true
			}
		}
		if !found {
			fields = append(fields, *currField)
		}
	}
	return fields, nil
}

//DeepDiff  - list fields in A that are missing or not equal to fields in B
func (d config) DeepDiff(c Config) ([]structs.Field, *probe.Error) {
	var fields []structs.Field
	err := CheckData(c.Data())
	if err != nil {
		return []structs.Field{}, err.Trace()
	}

	currFields := structs.Fields(d.Data())
	newFields := structs.Fields(c.Data())

	found := false
	for _, currField := range currFields {
		found = false
		for _, newField := range newFields {
			if reflect.DeepEqual(currField.Value(), newField.Value()) {
				found = true
			}
		}
		if !found {
			fields = append(fields, *currField)
		}
	}
	return fields, nil
}
<commit_msg>Add a new quick.Load() function to load directly any config file provided a quick compatible struct{} is also provided<commit_after>/*
 * Quick - Quick key value store for config files and persistent state files
 *
 * Minio Client (C) 2015 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package quick

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"runtime"
	"strings"
	"sync"

	"github.com/minio/minio/internal/github.com/fatih/structs"
	"github.com/minio/minio/pkg/probe"
)

// Config - generic config interface functions
type Config interface {
	String() string
	Version() string
	Save(string) *probe.Error
	Load(string) *probe.Error
	Data() interface{}
	Diff(Config) ([]structs.Field, *probe.Error)
	DeepDiff(Config) ([]structs.Field, *probe.Error)
}

// config - implements quick.Config interface
type config struct {
	data interface{}
	lock *sync.RWMutex
}

// CheckData - checks the validity of config data. Data sould be of type struct and contain a string type field called "Version"
func CheckData(data interface{}) *probe.Error {
	if !structs.IsStruct(data) {
		return probe.NewError(fmt.Errorf("Invalid argument type. Expecing \"struct\" type."))
	}

	st := structs.New(data)
	f, ok := st.FieldOk("Version")
	if !ok {
		return probe.NewError(fmt.Errorf("Invalid type of struct argument. No [%s.Version] field found.", st.Name()))
	}

	if f.Kind() != reflect.String {
		return probe.NewError(fmt.Errorf("Invalid type of struct argument. Expecting \"string\" type [%s.Version] field.", st.Name()))
	}

	return nil
}

// New - instantiate a new config
func New(data interface{}) (Config, *probe.Error) {
	err := CheckData(data)
	if err != nil {
		return nil, err.Trace()
	}

	d := new(config)
	d.data = data
	d.lock = new(sync.RWMutex)
	return d, nil
}

// Version returns the current config file format version
func (d config) Version() string {
	st := structs.New(d.data)

	f, ok := st.FieldOk("Version")
	if !ok {
		return ""
	}

	val := f.Value()
	ver, ok := val.(string)
	if ok {
		return ver
	}
	return ""
}

// String converts JSON config to printable string
func (d config) String() string {
	configBytes, _ := json.MarshalIndent(d.data, "", "\t")
	return string(configBytes)
}

// Save writes config data in JSON format to a file.
func (d config) Save(filename string) *probe.Error {
	d.lock.Lock()
	defer d.lock.Unlock()

	jsonData, err := json.MarshalIndent(d.data, "", "\t")
	if err != nil {
		return probe.NewError(err)
	}

	if runtime.GOOS == "windows" {
		jsonData = []byte(strings.Replace(string(jsonData), "\n", "\r\n", -1))
	}

	err = ioutil.WriteFile(filename, jsonData, 0600)
	if err != nil {
		return probe.NewError(err)
	}

	return nil
}

// Load - loads json config
func Load(filename string, data interface{}) (Config, *probe.Error) {
	_, err := os.Stat(filename)
	if err != nil {
		return nil, probe.NewError(err)
	}

	fileData, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, probe.NewError(err)
	}

	if runtime.GOOS == "windows" {
		fileData = []byte(strings.Replace(string(fileData), "\r\n", "\n", -1))
	}

	err = json.Unmarshal(fileData, &data)
	if err != nil {
		return nil, probe.NewError(err)
	}

	config, perr := New(data)
	if perr != nil {
		return nil, perr.Trace()
	}

	return config, nil
}

// Load - loads JSON config from file and merge with currently set values
func (d *config) Load(filename string) *probe.Error {
	d.lock.Lock()
	defer d.lock.Unlock()

	_, err := os.Stat(filename)
	if err != nil {
		return probe.NewError(err)
	}

	fileData, err := ioutil.ReadFile(filename)
	if err != nil {
		return probe.NewError(err)
	}

	if runtime.GOOS == "windows" {
		fileData = []byte(strings.Replace(string(fileData), "\r\n", "\n", -1))
	}

	st := structs.New(d.data)
	f, ok := st.FieldOk("Version")
	if !ok {
		return probe.NewError(fmt.Errorf("Argument struct [%s] does not contain field \"Version\".", st.Name()))
	}

	err = json.Unmarshal(fileData, d.data)
	if err != nil {
		return probe.NewError(err)
	}

	if err := CheckData(d.data); err != nil {
		return err.Trace()
	}

	if (*d).Version() != f.Value() {
		return probe.NewError(fmt.Errorf("Version mismatch"))
	}

	return nil
}

// Data - grab internal data map for reading
func (d config) Data() interface{} {
	return d.data
}

//Diff  - list fields that are in A but not in B
func (d config) Diff(c Config) ([]structs.Field, *probe.Error) {
	var fields []structs.Field
	err := CheckData(c.Data())
	if err != nil {
		return []structs.Field{}, err.Trace()
	}

	currFields := structs.Fields(d.Data())
	newFields := structs.Fields(c.Data())

	found := false
	for _, currField := range currFields {
		found = false
		for _, newField := range newFields {
			if reflect.DeepEqual(currField.Name(), newField.Name()) {
				found = true
			}
		}
		if !found {
			fields = append(fields, *currField)
		}
	}
	return fields, nil
}

//DeepDiff  - list fields in A that are missing or not equal to fields in B
func (d config) DeepDiff(c Config) ([]structs.Field, *probe.Error) {
	var fields []structs.Field
	err := CheckData(c.Data())
	if err != nil {
		return []structs.Field{}, err.Trace()
	}

	currFields := structs.Fields(d.Data())
	newFields := structs.Fields(c.Data())

	found := false
	for _, currField := range currFields {
		found = false
		for _, newField := range newFields {
			if reflect.DeepEqual(currField.Value(), newField.Value()) {
				found = true
			}
		}
		if !found {
			fields = append(fields, *currField)
		}
	}
	return fields, nil
}
<|endoftext|>


<commit_before>package store

import (
	"net/url"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/garyburd/redigo/redis"
	"github.com/ulule/limiter"
)

const (
	// InMemory storage
	InMemory = "memory"
	// Redis storage
	Redis = "redis"
	// None Nullable storage
	None = "none"
)

// Store is the common interface for datastores.
type Store interface {
	Exists(key string) (bool, error)
	Get(key string) (string, error)
	Remove(key string) error
	Set(key string, value string, expire int64) error
	ToLimiterStore(prefix string) (limiter.Store, error)
}

// Options are options for store.
type Options struct {
	// Prefix is the prefix to use for the key.
	Prefix string

	// MaxRetry is the maximum number of retry under race conditions.
	MaxRetry int

	// CleanUpInterval is the interval for cleanup.
	CleanUpInterval time.Duration
}

// Build creates a new storage based on the provided DSN
func Build(dsn string) (Store, error) {
	url, err := url.Parse(dsn)
	if nil != err {
		log.Panic(err)
	}
	log.WithField("type", url.Scheme).Debug("Initializing storage")

	switch url.Scheme {
	case InMemory:
		return NewInMemoryStore(), nil
	case Redis:
		// Create a Redis pool.
		pool := &redis.Pool{
			MaxIdle:     3,
			IdleTimeout: 240 * time.Second,
			Dial:        func() (redis.Conn, error) { return redis.DialURL(dsn) },
		}

		log.Debugf("Trying to connect to redis pool: %s", dsn)
		return NewRedisStore(pool)
	}

	return nil, ErrUnknownStorage
}
<commit_msg>Returned the error<commit_after>package store

import (
	"net/url"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/garyburd/redigo/redis"
	"github.com/ulule/limiter"
)

const (
	// InMemory storage
	InMemory = "memory"
	// Redis storage
	Redis = "redis"
	// None Nullable storage
	None = "none"
)

// Store is the common interface for datastores.
type Store interface {
	Exists(key string) (bool, error)
	Get(key string) (string, error)
	Remove(key string) error
	Set(key string, value string, expire int64) error
	ToLimiterStore(prefix string) (limiter.Store, error)
}

// Options are options for store.
type Options struct {
	// Prefix is the prefix to use for the key.
	Prefix string

	// MaxRetry is the maximum number of retry under race conditions.
	MaxRetry int

	// CleanUpInterval is the interval for cleanup.
	CleanUpInterval time.Duration
}

// Build creates a new storage based on the provided DSN
func Build(dsn string) (Store, error) {
	url, err := url.Parse(dsn)
	if nil != err {
		return nil, err
	}
	log.WithField("type", url.Scheme).Debug("Initializing storage")

	switch url.Scheme {
	case InMemory:
		return NewInMemoryStore(), nil
	case Redis:
		// Create a Redis pool.
		pool := &redis.Pool{
			MaxIdle:     3,
			IdleTimeout: 240 * time.Second,
			Dial:        func() (redis.Conn, error) { return redis.DialURL(dsn) },
		}

		log.Debugf("Trying to connect to redis pool: %s", dsn)
		return NewRedisStore(pool)
	}

	return nil, ErrUnknownStorage
}
<|endoftext|>


<commit_before>/*
Copyright 2022 Intel Corporation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package utils

import (
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"
)

func setCPUFreqValue(cpu ID, setting string, value int) error {
	str := fmt.Sprintf("/sys/devices/system/cpu/cpu%d/cpufreq/%s", cpu, setting)

	if err := ioutil.WriteFile(str, []byte(strconv.Itoa(value)), 0644); err != nil {
		return err
	}

	return nil
}

// GetCPUFreqValue returns information of the currently used CPU frequency
func GetCPUFreqValue(cpu ID, setting string) (int, error) {
	str := fmt.Sprintf("/sys/devices/system/cpu/cpu%d/cpufreq/%s", cpu, setting)

	raw, err := ioutil.ReadFile(str)
	if err != nil {
		return 0, err
	}

	value, err := strconv.Atoi(strings.TrimSpace(string(raw)))
	if err != nil {
		return 0, err
	}

	return value, nil
}

// SetCPUScalingMinFreq sets the scaling_min_freq value of a given CPU
func SetCPUScalingMinFreq(cpu ID, freq int) error {
	return setCPUFreqValue(cpu, "scaling_min_freq", freq)
}

// SetCPUScalingMaxFreq sets the scaling_max_freq value of a given CPU
func SetCPUScalingMaxFreq(cpu ID, freq int) error {
	return setCPUFreqValue(cpu, "scaling_max_freq", freq)
}

// SetCPUsScalingMinFreq sets the scaling_min_freq value of a given set of CPUs
func SetCPUsScalingMinFreq(cpus []ID, freq int) error {
	for cpu := range cpus {
		if err := SetCPUScalingMinFreq(cpu, freq); err != nil {
			return err
		}
	}

	return nil
}

// SetCPUsScalingMaxFreq sets the scaling_max_freq value of a given set of CPUs
func SetCPUsScalingMaxFreq(cpus []ID, freq int) error {
	for cpu := range cpus {
		if err := SetCPUScalingMaxFreq(cpu, freq); err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>sysfs: fix setting of cpufreq attributes<commit_after>/*
Copyright 2022 Intel Corporation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package utils

import (
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"
)

func setCPUFreqValue(cpu ID, setting string, value int) error {
	str := fmt.Sprintf("/sys/devices/system/cpu/cpu%d/cpufreq/%s", cpu, setting)

	if err := ioutil.WriteFile(str, []byte(strconv.Itoa(value)), 0644); err != nil {
		return err
	}

	return nil
}

// GetCPUFreqValue returns information of the currently used CPU frequency
func GetCPUFreqValue(cpu ID, setting string) (int, error) {
	str := fmt.Sprintf("/sys/devices/system/cpu/cpu%d/cpufreq/%s", cpu, setting)

	raw, err := ioutil.ReadFile(str)
	if err != nil {
		return 0, err
	}

	value, err := strconv.Atoi(strings.TrimSpace(string(raw)))
	if err != nil {
		return 0, err
	}

	return value, nil
}

// SetCPUScalingMinFreq sets the scaling_min_freq value of a given CPU
func SetCPUScalingMinFreq(cpu ID, freq int) error {
	return setCPUFreqValue(cpu, "scaling_min_freq", freq)
}

// SetCPUScalingMaxFreq sets the scaling_max_freq value of a given CPU
func SetCPUScalingMaxFreq(cpu ID, freq int) error {
	return setCPUFreqValue(cpu, "scaling_max_freq", freq)
}

// SetCPUsScalingMinFreq sets the scaling_min_freq value of a given set of CPUs
func SetCPUsScalingMinFreq(cpus []ID, freq int) error {
	for _, cpu := range cpus {
		if err := SetCPUScalingMinFreq(cpu, freq); err != nil {
			return err
		}
	}

	return nil
}

// SetCPUsScalingMaxFreq sets the scaling_max_freq value of a given set of CPUs
func SetCPUsScalingMaxFreq(cpus []ID, freq int) error {
	for _, cpu := range cpus {
		if err := SetCPUScalingMaxFreq(cpu, freq); err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Package reboot is a plugin that supports rebooting the host running the worker.
//
// In some cases, especially running without a very tight
// sandbox, this is desirable after specific tests.
//
// In other cases, reboots are useful after a configured uptime, to
// cycle the host's configuration.
//
// To reboot after tasks, users must add the boolean "reboot" payload
// attribute.
package reboot

import (
	"fmt"

	"sync"
	"time"

	schematypes "github.com/taskcluster/go-schematypes"
	"github.com/taskcluster/taskcluster-worker/plugins"
	"github.com/taskcluster/taskcluster-worker/runtime"
)

type plugin struct {
	plugins.PluginBase
	environment *runtime.Environment

	// the command to reboot the host
	rebootCommand []string

	// while this is held for reading, there are tasks running
	runningTasks sync.RWMutex
}

type payloadType struct {
	RebootWhenDone bool `json:"reboot"`
}

type config struct {
	RebootAfter   int      `json:"rebootAfter"`
	RebootCommand []string `json:"rebootCommand"`
}

type taskPlugin struct {
	plugins.TaskPluginBase
	plugin         *plugin
	rebootWhenDone bool
}

type pluginProvider struct {
	plugins.PluginProviderBase
}

var payloadSchema = schematypes.Object{
	Properties: schematypes.Properties{
		"reboot": schematypes.Boolean{
			MetaData: schematypes.MetaData{
				Title:       "Reboot machine",
				Description: "If true, reboot the machine after task is finished.",
			},
		},
	},
}

var configSchema = schematypes.Object{
	Properties: schematypes.Properties{
		"rebootAfter": schematypes.Integer{
			MetaData: schematypes.MetaData{
				Title: "Reboot After",
				Description: `When the worker is idle after this many hours, it will reboot.
					If zero, no idle reboot will occur.`,
			},
			Minimum: 0,
			Maximum: 10000,
		},
		"rebootCommand": schematypes.Array{
			MetaData: schematypes.MetaData{
				Title:       "Reboot Command",
				Description: `The command to use to reboot the host, as a list of strings`,
			},
			Items: schematypes.String{},
		},
	},
}

func init() {
	plugins.Register("reboot", pluginProvider{})
}

func (pluginProvider) ConfigSchema() schematypes.Schema {
	return configSchema
}

func (pluginProvider) NewPlugin(options plugins.PluginOptions) (plugins.Plugin, error) {
	var c config
	if err := schematypes.MustMap(configSchema, options.Config, &c); err != nil {
		return nil, fmt.Errorf("While reading reboot plugin config: %s", err)
	}

	plugin := &plugin{
		PluginBase:    plugins.PluginBase{},
		environment:   options.Environment,
		rebootCommand: c.RebootCommand,
		runningTasks:  sync.RWMutex{},
	}

	if c.RebootAfter > 0 {
		// After rebootAfter hours, call the reboot method. If the lock is held by one or more
		// running tasks, the reboot will not occur until they finish (but no further tasks will
		// be able to start)
		time.AfterFunc(time.Duration(c.RebootAfter)*time.Second, func() {
			plugin.environment.Monitor.Warn("Host rebootAfter has expired; scheduling reboot")
			plugin.reboot()
		})
	}

	return plugin, nil
}

// Try to reboot the host, waiting until it is running no tasks.  The reboot entails
// signalling the worker, which will begin an orderly shutdown, so no further tasks
// will be started once this function returns.
func (pl *plugin) reboot() {
	// Try to get a write lock on the mutex; this will wait until all read locks
	// (running tasks) are complete, and will preclude any later read locks.
	pl.runningTasks.Lock()

	// By this time, we should have stopped accepting new tasks, so it is safe
	// to unlock and continue any task completion
	defer pl.runningTasks.Unlock()

	// Reboot and/or die trying. The lock is still held, so no other tasks can
	// start while this process plays out
	pl.environment.Monitor.Warn("Initiating reboot")
	initiateReboot(pl.rebootCommand)
}

func (*plugin) PayloadSchema() schematypes.Object {
	return payloadSchema
}

func (pl *plugin) NewTaskPlugin(options plugins.TaskPluginOptions) (plugins.TaskPlugin, error) {
	var p payloadType
	err := payloadSchema.Map(options.Payload, &p)
	if err == schematypes.ErrTypeMismatch {
		panic("internal error -- type mismatch")
	} else if err != nil {
		return nil, fmt.Errorf("While reading reboot plugin payload: %s", err)
	}

	pl.runningTasks.RLock()
	return &taskPlugin{
		TaskPluginBase: plugins.TaskPluginBase{},
		plugin:         pl,
		rebootWhenDone: p.RebootWhenDone,
	}, nil
}

func (tp *taskPlugin) Dispose() error {
	tp.plugin.runningTasks.RUnlock()

	// Note: in a multi-task environment, this will not reboot immediately, but
	// will wait until all tasks are complete.
	if tp.rebootWhenDone {
		tp.plugin.environment.Monitor.Warn("Task specified reboot after completion; scheduling reboot")
		tp.plugin.reboot()
	}

	return nil
}
<commit_msg>Minor refactoring to cleanup<commit_after>// Package reboot is a plugin that supports rebooting the host running the worker.
//
// In some cases, especially running without a very tight
// sandbox, this is desirable after specific tests.
//
// In other cases, reboots are useful after a configured uptime, to
// cycle the host's configuration.
//
// To reboot after tasks, users must add the boolean "reboot" payload
// attribute.
package reboot

import (
	"sync"
	"time"

	schematypes "github.com/taskcluster/go-schematypes"
	"github.com/taskcluster/taskcluster-worker/plugins"
	"github.com/taskcluster/taskcluster-worker/runtime"
)

type plugin struct {
	plugins.PluginBase
	environment *runtime.Environment

	// the command to reboot the host
	rebootCommand []string

	// while this is held for reading, there are tasks running
	runningTasks sync.RWMutex
}

type payloadType struct {
	RebootWhenDone bool `json:"reboot"`
}

type config struct {
	RebootAfter   int      `json:"rebootAfter"`
	RebootCommand []string `json:"rebootCommand"`
}

type taskPlugin struct {
	plugins.TaskPluginBase
	plugin         *plugin
	rebootWhenDone bool
}

type pluginProvider struct {
	plugins.PluginProviderBase
}

var payloadSchema = schematypes.Object{
	Properties: schematypes.Properties{
		"reboot": schematypes.Boolean{
			MetaData: schematypes.MetaData{
				Title:       "Reboot machine",
				Description: "If true, reboot the machine after task is finished.",
			},
		},
	},
}

var configSchema = schematypes.Object{
	Properties: schematypes.Properties{
		"rebootAfter": schematypes.Integer{
			MetaData: schematypes.MetaData{
				Title: "Reboot After",
				Description: `When the worker is idle after this many hours, it will reboot.
					If zero, no idle reboot will occur.`,
			},
			Minimum: 0,
			Maximum: 10000,
		},
		"rebootCommand": schematypes.Array{
			MetaData: schematypes.MetaData{
				Title:       "Reboot Command",
				Description: `The command to use to reboot the host, as a list of strings`,
			},
			Items: schematypes.String{},
		},
	},
}

func init() {
	plugins.Register("reboot", pluginProvider{})
}

func (pluginProvider) ConfigSchema() schematypes.Schema {
	return configSchema
}

func (pluginProvider) NewPlugin(options plugins.PluginOptions) (plugins.Plugin, error) {
	var c config
	schematypes.MustValidateAndMap(configSchema, options.Config, &c)

	plugin := &plugin{
		PluginBase:    plugins.PluginBase{},
		environment:   options.Environment,
		rebootCommand: c.RebootCommand,
		runningTasks:  sync.RWMutex{},
	}

	if c.RebootAfter > 0 {
		// After rebootAfter hours, call the reboot method. If the lock is held by one or more
		// running tasks, the reboot will not occur until they finish (but no further tasks will
		// be able to start)
		time.AfterFunc(time.Duration(c.RebootAfter)*time.Second, func() {
			plugin.environment.Monitor.Warn("Host rebootAfter has expired; scheduling reboot")
			plugin.reboot()
		})
	}

	return plugin, nil
}

// Try to reboot the host, waiting until it is running no tasks.  The reboot entails
// signalling the worker, which will begin an orderly shutdown, so no further tasks
// will be started once this function returns.
func (pl *plugin) reboot() {
	// Try to get a write lock on the mutex; this will wait until all read locks
	// (running tasks) are complete, and will preclude any later read locks.
	pl.runningTasks.Lock()

	// By this time, we should have stopped accepting new tasks, so it is safe
	// to unlock and continue any task completion
	defer pl.runningTasks.Unlock()

	// Reboot and/or die trying. The lock is still held, so no other tasks can
	// start while this process plays out
	pl.environment.Monitor.Warn("Initiating reboot")
	initiateReboot(pl.rebootCommand)
}

func (*plugin) PayloadSchema() schematypes.Object {
	return payloadSchema
}

func (pl *plugin) NewTaskPlugin(options plugins.TaskPluginOptions) (plugins.TaskPlugin, error) {
	var p payloadType
	schematypes.MustValidateAndMap(payloadSchema, options.Payload, &p)

	pl.runningTasks.RLock()
	return &taskPlugin{
		TaskPluginBase: plugins.TaskPluginBase{},
		plugin:         pl,
		rebootWhenDone: p.RebootWhenDone,
	}, nil
}

func (tp *taskPlugin) Dispose() error {
	tp.plugin.runningTasks.RUnlock()

	// Note: in a multi-task environment, this will not reboot immediately, but
	// will wait until all tasks are complete.
	if tp.rebootWhenDone {
		tp.plugin.environment.Monitor.Warn("Task specified reboot after completion; scheduling reboot")
		tp.plugin.reboot()
	}

	return nil
}
<|endoftext|>


<commit_before>package interpreter

import (
	"fmt"
	"reflect"
	"time"

	"github.com/richardwilkes/goblin/util"
)

func (env *Env) loadBuiltins() {
	env.Define("len", func(v interface{}) int64 {
		rv := reflect.ValueOf(v)
		if rv.Kind() == reflect.Interface {
			rv = rv.Elem()
		}
		k := rv.Kind()
		if k == reflect.String || k == reflect.Array || k == reflect.Slice || k == reflect.Map {
			return int64(rv.Len())
		}
		return 0
	})

	env.Define("keys", func(v interface{}) []interface{} {
		rv := reflect.ValueOf(v)
		if rv.Kind() == reflect.Interface {
			rv = rv.Elem()
		}
		if rv.Kind() == reflect.Map {
			k := rv.MapKeys()
			keys := make([]interface{}, len(k))
			for i := range k {
				keys[i] = k[i].Interface()
			}
			return keys
		}
		return []interface{}{}
	})

	env.Define("range", func(args ...int64) []int64 {
		count := len(args)
		if count == 0 {
			return []int64{}
		}
		var min, max int64
		if count == 1 {
			max = args[0] - 1
			if max < 0 {
				return []int64{}
			}
		} else {
			min = args[0]
			max = args[1]
		}
		if max < min {
			max = min
		}
		arr := make([]int64, 1+max-min)
		for i := min; i <= max; i++ {
			arr[i-min] = i
		}
		return arr
	})

	env.Define("sleep", func(spec string) {
		if d, err := time.ParseDuration(spec); err == nil && d > 0 {
			timer := time.NewTimer(d)
			select {
			case <-timer.C:
			case <-(*env.interrupt):
				timer.Stop()
				env.Interrupt() // Re-interrupt, since we just ate the previous one
			}
		}
	})

	env.Define("toString", func(v interface{}) string {
		if s, ok := v.(string); ok {
			return s
		}
		if b, ok := v.([]byte); ok {
			return string(b)
		}
		return fmt.Sprint(v)
	})

	env.Define("toInt", func(v interface{}) int64 {
		return util.ToInt64(reflect.ValueOf(v))
	})

	env.Define("toFloat", func(v interface{}) float64 {
		return util.ToFloat64(reflect.ValueOf(v))
	})

	env.Define("toBool", func(v interface{}) bool {
		return util.ToBool(reflect.ValueOf(v))
	})

	env.Define("toChar", func(s rune) string {
		return string(s)
	})

	env.Define("toRune", func(s string) rune {
		if len(s) == 0 {
			return 0
		}
		return []rune(s)[0]
	})

	env.Define("toByteSlice", func(s string) []byte {
		return []byte(s)
	})

	env.Define("toRuneSlice", func(s string) []rune {
		return []rune(s)
	})

	env.Define("toBoolSlice", func(v []interface{}) []bool {
		var result []bool
		util.ToSlice(v, &result)
		return result
	})

	env.Define("toIntSlice", func(v []interface{}) []int64 {
		var result []int64
		util.ToSlice(v, &result)
		return result
	})

	env.Define("toFloatSlice", func(v []interface{}) []float64 {
		var result []float64
		util.ToSlice(v, &result)
		return result
	})

	env.Define("toStringSlice", func(v []interface{}) []string {
		var result []string
		util.ToSlice(v, &result)
		return result
	})

	env.Define("typeOf", func(v interface{}) string {
		t := reflect.TypeOf(v)
		if t == nil {
			return "<nil>"
		}
		return reflect.TypeOf(v).String()
	})

	env.Define("defined", func(s string) bool {
		_, err := env.Get(s)
		return err == nil
	})

	env.Define("print", fmt.Print)
	env.Define("println", fmt.Println)
	env.Define("printf", fmt.Printf)

	env.DefineType("int", int64(0))
	env.DefineType("float", float64(0.0))
	env.DefineType("bool", true)
	env.DefineType("string", "")
}
<commit_msg>Define sort as a builtin<commit_after>package interpreter

import (
	"fmt"
	"reflect"
	"sort"
	"time"

	"github.com/richardwilkes/goblin/util"
)

func (env *Env) loadBuiltins() {
	env.Define("len", func(v interface{}) int64 {
		rv := reflect.ValueOf(v)
		if rv.Kind() == reflect.Interface {
			rv = rv.Elem()
		}
		k := rv.Kind()
		if k == reflect.String || k == reflect.Array || k == reflect.Slice || k == reflect.Map {
			return int64(rv.Len())
		}
		return 0
	})

	env.Define("keys", func(v interface{}) []interface{} {
		rv := reflect.ValueOf(v)
		if rv.Kind() == reflect.Interface {
			rv = rv.Elem()
		}
		if rv.Kind() == reflect.Map {
			k := rv.MapKeys()
			keys := make([]interface{}, len(k))
			for i := range k {
				keys[i] = k[i].Interface()
			}
			return keys
		}
		return []interface{}{}
	})

	env.Define("range", func(args ...int64) []int64 {
		count := len(args)
		if count == 0 {
			return []int64{}
		}
		var min, max int64
		if count == 1 {
			max = args[0] - 1
			if max < 0 {
				return []int64{}
			}
		} else {
			min = args[0]
			max = args[1]
		}
		if max < min {
			max = min
		}
		arr := make([]int64, 1+max-min)
		for i := min; i <= max; i++ {
			arr[i-min] = i
		}
		return arr
	})

	env.Define("sort", sort.Slice)

	env.Define("sleep", func(spec string) {
		if d, err := time.ParseDuration(spec); err == nil && d > 0 {
			timer := time.NewTimer(d)
			select {
			case <-timer.C:
			case <-(*env.interrupt):
				timer.Stop()
				env.Interrupt() // Re-interrupt, since we just ate the previous one
			}
		}
	})

	env.Define("toString", func(v interface{}) string {
		if s, ok := v.(string); ok {
			return s
		}
		if b, ok := v.([]byte); ok {
			return string(b)
		}
		return fmt.Sprint(v)
	})

	env.Define("toInt", func(v interface{}) int64 {
		return util.ToInt64(reflect.ValueOf(v))
	})

	env.Define("toFloat", func(v interface{}) float64 {
		return util.ToFloat64(reflect.ValueOf(v))
	})

	env.Define("toBool", func(v interface{}) bool {
		return util.ToBool(reflect.ValueOf(v))
	})

	env.Define("toChar", func(s rune) string {
		return string(s)
	})

	env.Define("toRune", func(s string) rune {
		if len(s) == 0 {
			return 0
		}
		return []rune(s)[0]
	})

	env.Define("toByteSlice", func(s string) []byte {
		return []byte(s)
	})

	env.Define("toRuneSlice", func(s string) []rune {
		return []rune(s)
	})

	env.Define("toBoolSlice", func(v []interface{}) []bool {
		var result []bool
		util.ToSlice(v, &result)
		return result
	})

	env.Define("toIntSlice", func(v []interface{}) []int64 {
		var result []int64
		util.ToSlice(v, &result)
		return result
	})

	env.Define("toFloatSlice", func(v []interface{}) []float64 {
		var result []float64
		util.ToSlice(v, &result)
		return result
	})

	env.Define("toStringSlice", func(v []interface{}) []string {
		var result []string
		util.ToSlice(v, &result)
		return result
	})

	env.Define("typeOf", func(v interface{}) string {
		t := reflect.TypeOf(v)
		if t == nil {
			return "<nil>"
		}
		return reflect.TypeOf(v).String()
	})

	env.Define("defined", func(s string) bool {
		_, err := env.Get(s)
		return err == nil
	})

	env.Define("print", fmt.Print)
	env.Define("println", fmt.Println)
	env.Define("printf", fmt.Printf)

	env.DefineType("int", int64(0))
	env.DefineType("float", float64(0.0))
	env.DefineType("bool", true)
	env.DefineType("string", "")
}
<|endoftext|>


<commit_before>package itembase

import (
	"encoding/json"
	"log"
	"strings"
	"time"
)

// TODO: Some entities/models don't have the full set of fields from the API.
// Some of the implementation detail structs (Contacts, Billing, pagination
// containers, etc.) could perhaps be unexported.

type ProfileID string

func (profileID ProfileID) String() string {
	return string(profileID)
}

// A Profile represents a user profile entity from the itembase API.
//
// See http://sandbox.api.itembase.io/swagger-ui/
type Profile struct {
	Active    bool   `json:"active,omitempty"`
	AvatarURL string `json:"avatar_url,omitempty"`
	Contact   struct {
		Contact []Contact `json:"contact,omitempty"`
	} `json:"contact,omitempty"`
	CreatedAt         *time.Time `json:"created_at,omitempty"`
	Currency          string     `json:"currency,omitempty"`
	DisplayName       string     `json:"display_name,omitempty"`
	ID                ProfileID  `json:"id"`
	Language          string     `json:"language,omitempty"`
	Locale            string     `json:"locale,omitempty"`
	OriginalReference string     `json:"original_reference,omitempty"`
	PlatformID        string     `json:"platform_id,omitempty"`
	PlatformName      string     `json:"platform_name,omitempty"`
	SourceID          string     `json:"source_id,omitempty"`
	Status            string     `json:"status,omitempty"`
	Type              string     `json:"type,omitempty"`
	UpdatedAt         *time.Time `json:"updated_at,omitempty"`
	URL               string     `json:"url,omitempty"`
}

// An Address represents a mailing address model from the itembase API.
type Address struct {
	City    string `json:"city,omitempty"`
	Country string `json:"country,omitempty"`
	Line1   string `json:"line_1,omitempty"`
	Name    string `json:"name,omitempty"`
	Zip     string `json:"zip,omitempty"`
}

// A Contact represents a container of contact information from itembase API
// models.
type Contact struct {
	Addresses []Address `json:"addresses,omitempty"`
	Emails    []struct {
		Value string `json:"value,omitempty"`
	} `json:"emails,omitempty"`
	Phones []interface{} `json:"phones,omitempty"`
}

// GetName returns a string with a combined FirstName and
// LastName of a Buyer Profile
func (buyer *Buyer) GetName() string {
	return buyer.FirstName + " " + buyer.LastName
}

type BuyerID string

func (buyerID BuyerID) String() string {
	return string(buyerID)
}

// A Buyer represents a buyer entity from the itembase API.
//
// See http://sandbox.api.itembase.io/swagger-ui/
type Buyer struct {
	Active            bool       `json:"active,omitempty"`
	Contact           Contact    `json:"contact,omitempty"`
	CreatedAt         *time.Time `json:"created_at,omitempty"`
	Currency          string     `json:"currency,omitempty"`
	DateOfBirth       string     `json:"date_of_birth,omitempty"`
	FirstName         string     `json:"first_name,omitempty"`
	ID                BuyerID    `json:"id"`
	Language          string     `json:"language,omitempty"`
	LastName          string     `json:"last_name,omitempty"`
	Locale            string     `json:"locale,omitempty"`
	Note              string     `json:"note,omitempty"`
	OptOut            bool       `json:"opt_out,omitempty"`
	OriginalReference string     `json:"original_reference,omitempty"`
	SourceID          string     `json:"source_id,omitempty"`
	Status            string     `json:"status,omitempty"`
	Type              string     `json:"type,omitempty"`
	UpdatedAt         *time.Time `json:"updated_at,omitempty"`
	URL               string     `json:"url,omitempty"`
}

// GetEmail returns an Email for a Profile
func (buyer *Buyer) GetEmail() string {
	if len(buyer.Contact.Emails) > 0 {
		for _, EmailValue := range buyer.Contact.Emails {
			return EmailValue.Value
		}
	}

	return ""
}

// GetEmails returns all Emails for a Profile
func (buyer *Buyer) GetEmails() (emails []string) {
	if len(buyer.Contact.Emails) > 0 {
		for _, EmailValue := range buyer.Contact.Emails {
			emails = append(emails, EmailValue.Value)
		}
	}

	return
}

// A Category represents a product category model from the itembase API.
type Category struct {
	CategoryID string `json:"category_id,omitempty"`
	Language   string `json:"language,omitempty"`
	Value      string `json:"value,omitempty"`
}

// A ProductDescription represents a product description model from the itembase
// API, which may be in a specified language.
type ProductDescription struct {
	Language string `json:"language,omitempty"`
	Value    string `json:"value,omitempty"`
}

// A Brand represents a product brand model from the itembase API.
type Brand struct {
	Name struct {
		Language string `json:"language,omitempty"`
		Value    string `json:"value,omitempty"`
	} `json:"name,omitempty"`
}

type Identifier struct {
	ID string `json:"id,omitempty"`
}

type StockInformation struct {
	InStock        bool    `json:"in_stock,omitempty"`
	InventoryLevel float64 `json:"inventory_level,omitempty"`
	InventoryUnit  string  `json:"inventory_unit,omitempty"`
}

type ProductID string

func (productID ProductID) String() string {
	return string(productID)
}

// A Product represents a product entity from the itembase API.
//
// See http://sandbox.api.itembase.io/swagger-ui/
type Product struct {
	Active      bool                 `json:"active,omitempty"`
	Brand       Brand                `json:"brand,omitempty"`
	Categories  []Category           `json:"categories,omitempty"`
	Condition   string               `json:"condition,omitempty"`
	CreatedAt   *time.Time           `json:"created_at,omitempty"`
	Currency    string               `json:"currency,omitempty"`
	Description []ProductDescription `json:"description,omitempty"`
	ID          ProductID            `json:"id"`
	Identifier  Identifier           `json:"identifier,omitempty"`
	Name        []struct {
		Language string `json:"language,omitempty"`
		Value    string `json:"value,omitempty"`
	} `json:"name,omitempty"`
	OriginalReference string `json:"original_reference,omitempty"`
	PictureUrls       []struct {
		URLOriginal string `json:"url_original,omitempty"`
	} `json:"picture_urls,omitempty"`
	PricePerUnit float64 `json:"price_per_unit,omitempty"`
	Shipping     []struct {
		Price           float64 `json:"price,omitempty"`
		ShippingService string  `json:"shipping_service,omitempty"`
	} `json:"shipping,omitempty"`
	SourceID         string           `json:"source_id,omitempty"`
	StockInformation StockInformation `json:"stock_information,omitempty"`
	Tax              float64          `json:"tax,omitempty"`
	TaxRate          float64          `json:"tax_rate,omitempty"`
	UpdatedAt        *time.Time       `json:"updated_at,omitempty"`
	URL              string           `json:"url,omitempty"`
	Variants         []interface{}    `json:"variants,omitempty"`
}

func (product *Product) InStock() bool {
	return product.StockInformation.InStock
}

// Returns name for specified preferred language if present
func (product *Product) GetName(preferredLanguage string) (name string, ok bool) {

	for _, productName := range product.Name {
		if preferredLanguage == productName.Language {
			return cleanItembaseUnicode(productName.Value), true
		}
	}

	// if []struct{} is empty, return empty string
	return "", false
}

// Returns any name for Product
func (product *Product) GetDefaultName() (name string, ok bool) {

	for _, productName := range product.Name {
		return cleanItembaseUnicode(productName.Value), true
	}

	return "", false

}

func cleanItembaseUnicode(str string) string {
	str = strings.Replace(str, "\u00a0", " ", -1)
	str = strings.Replace(str, "\ufeff", "", -1)
	return str
}

// Billing represents a model from the itembase API containing the billing
// address of a Transaction.
type Billing struct {
	Address Address `json:"address,omitempty"`
}

type Shipping struct {
	Address Address `json:"address,omitempty"`
}

// Status describes a transactions' status
type Status struct {
	Global   string `json:"global,omitempty"`
	Payment  string `json:"payment,omitempty"`
	Shipping string `json:"shipping,omitempty"`
}

type TransactionID string

func (transactionID TransactionID) String() string {
	return string(transactionID)
}

// A Transaction represents a transaction entity from the itembase API.
//
// See http://sandbox.api.itembase.io/swagger-ui/
type Transaction struct {
	Billing           Billing       `json:"billing,omitempty"`
	Buyer             Buyer         `json:"buyer,omitempty"`
	CreatedAt         *time.Time    `json:"created_at,omitempty"`
	Currency          string        `json:"currency,omitempty"`
	ID                TransactionID `json:"id"`
	OriginalReference string        `json:"original_reference,omitempty"`
	Products          []Product     `json:"products,omitempty"`
	Shipping          Shipping      `json:"shipping,omitempty"`
	SourceID          string        `json:"source_id,omitempty"`
	Status            Status        `json:"status,omitempty"`
	TotalPrice        float64       `json:"total_price,omitempty"`
	TotalPriceNet     float64       `json:"total_price_net,omitempty"`
	TotalTax          float64       `json:"total_tax,omitempty"`
	UpdatedAt         *time.Time    `json:"updated_at,omitempty"`
}

func (t *Transaction) Completed() bool {
	if t.Status.Global == "completed" {
		return true
	}
	return false
}

// ItembaseResponse is a container for any Itembase response.
// It returns the resultset, Number of found documents and Number of documents returned
type ItembaseResponse struct {
	Documents            []interface{} `json:"documents"`
	NumDocumentsFound    int           `json:"num_documents_found"`
	NumDocumentsReturned int           `json:"num_documents_returned"`
}

// Transactions is a container for pagination of Transaction entities.
type Transactions struct {
	Transactions []Transaction `json:"documents"`
}

func (transactions *Transactions) Add(transaction interface{}) {

	var newTransaction Transaction
	convertTo(transaction, &newTransaction)
	transactions.Transactions = append(transactions.Transactions, newTransaction)

}

func (transactions *Transactions) Completed() (filteredTransactions Transactions) {
	for _, transaction := range transactions.Transactions {
		if transaction.Completed() {
			filteredTransactions.Add(transaction)
		}
	}

	return
}

// Profiles is a container for pagination of Profile entities.
type Profiles struct {
	Profiles []Profile `json:"documents"`
}

func (profiles *Profiles) Add(profile interface{}) {

	var newProfile Profile
	convertTo(profile, &newProfile)
	profiles.Profiles = append(profiles.Profiles, newProfile)

}

// Products is a container for pagination of Product entities.
type Products struct {
	Products []Product `json:"documents"`
}

func (products *Products) Add(product interface{}) {

	var newProduct Product
	convertTo(product, &newProduct)
	products.Products = append(products.Products, newProduct)

}

func (products *Products) InStock() (filteredProducts Products) {
	for _, product := range products.Products {
		if product.InStock() {
			filteredProducts.Add(product)
		}
	}
	return
}

// Get Products based on shopID
func (products *Products) ByShop(shopID string) (filteredProducts Products) {
	for _, product := range products.Products {
		if product.SourceID == shopID {
			filteredProducts.Add(product)
		}
	}
	return
}

// Buyers is a container for pagination of Buyer entities.
type Buyers struct {
	Buyers []Buyer `json:"documents"`
}

func (buyers *Buyers) Add(buyer interface{}) {

	var newBuyer Buyer
	convertTo(buyer, &newBuyer)
	buyers.Buyers = append(buyers.Buyers, newBuyer)

}

func (buyers *Buyers) ByShop(shopID string) (filteredBuyers Buyers) {

	for _, buyer := range buyers.Buyers {
		if buyer.SourceID == shopID {
			filteredBuyers.Add(buyer)
		}
	}
	return
}

// A User represents a user entity from the itembase API, such as returned from
// the "me" endpoint.
type User struct {
	UUID              string `json:"uuid"`
	Username          string `json:"username,omitempty"`
	FirstName         string `json:"first_name,omitempty"`
	LastName          string `json:"last_name,omitempty"`
	MiddleName        string `json:"middle_name,omitempty"`
	NameFormat        string `json:"name_format,omitempty"`
	Locale            string `json:"locale,omitempty"`
	Email             string `json:"email,omitempty"`
	PreferredCurrency string `json:"preferred_currency,omitempty"`
}

func convertTo(inputInterface, outputType interface{}) {

	jsonBLOB, err := json.Marshal(inputInterface)
	if err != nil {
		log.Fatal(err)
	}

	err = json.Unmarshal(jsonBLOB, &outputType)
	if err != nil {
		log.Fatal(err)
	}

}
<commit_msg>Expose ConverTo and add error handling<commit_after>package itembase

import (
	"encoding/json"
	"log"
	"strings"
	"time"
)

// TODO: Some entities/models don't have the full set of fields from the API.
// Some of the implementation detail structs (Contacts, Billing, pagination
// containers, etc.) could perhaps be unexported.

type ProfileID string

func (profileID ProfileID) String() string {
	return string(profileID)
}

// A Profile represents a user profile entity from the itembase API.
//
// See http://sandbox.api.itembase.io/swagger-ui/
type Profile struct {
	Active    bool   `json:"active,omitempty"`
	AvatarURL string `json:"avatar_url,omitempty"`
	Contact   struct {
		Contact []Contact `json:"contact,omitempty"`
	} `json:"contact,omitempty"`
	CreatedAt         *time.Time `json:"created_at,omitempty"`
	Currency          string     `json:"currency,omitempty"`
	DisplayName       string     `json:"display_name,omitempty"`
	ID                ProfileID  `json:"id"`
	Language          string     `json:"language,omitempty"`
	Locale            string     `json:"locale,omitempty"`
	OriginalReference string     `json:"original_reference,omitempty"`
	PlatformID        string     `json:"platform_id,omitempty"`
	PlatformName      string     `json:"platform_name,omitempty"`
	SourceID          string     `json:"source_id,omitempty"`
	Status            string     `json:"status,omitempty"`
	Type              string     `json:"type,omitempty"`
	UpdatedAt         *time.Time `json:"updated_at,omitempty"`
	URL               string     `json:"url,omitempty"`
}

// An Address represents a mailing address model from the itembase API.
type Address struct {
	City    string `json:"city,omitempty"`
	Country string `json:"country,omitempty"`
	Line1   string `json:"line_1,omitempty"`
	Name    string `json:"name,omitempty"`
	Zip     string `json:"zip,omitempty"`
}

// A Contact represents a container of contact information from itembase API
// models.
type Contact struct {
	Addresses []Address `json:"addresses,omitempty"`
	Emails    []struct {
		Value string `json:"value,omitempty"`
	} `json:"emails,omitempty"`
	Phones []interface{} `json:"phones,omitempty"`
}

// GetName returns a string with a combined FirstName and
// LastName of a Buyer Profile
func (buyer *Buyer) GetName() string {
	return buyer.FirstName + " " + buyer.LastName
}

type BuyerID string

func (buyerID BuyerID) String() string {
	return string(buyerID)
}

// A Buyer represents a buyer entity from the itembase API.
//
// See http://sandbox.api.itembase.io/swagger-ui/
type Buyer struct {
	Active            bool       `json:"active,omitempty"`
	Contact           Contact    `json:"contact,omitempty"`
	CreatedAt         *time.Time `json:"created_at,omitempty"`
	Currency          string     `json:"currency,omitempty"`
	DateOfBirth       string     `json:"date_of_birth,omitempty"`
	FirstName         string     `json:"first_name,omitempty"`
	ID                BuyerID    `json:"id"`
	Language          string     `json:"language,omitempty"`
	LastName          string     `json:"last_name,omitempty"`
	Locale            string     `json:"locale,omitempty"`
	Note              string     `json:"note,omitempty"`
	OptOut            bool       `json:"opt_out,omitempty"`
	OriginalReference string     `json:"original_reference,omitempty"`
	SourceID          string     `json:"source_id,omitempty"`
	Status            string     `json:"status,omitempty"`
	Type              string     `json:"type,omitempty"`
	UpdatedAt         *time.Time `json:"updated_at,omitempty"`
	URL               string     `json:"url,omitempty"`
}

// GetEmail returns an Email for a Profile
func (buyer *Buyer) GetEmail() string {
	if len(buyer.Contact.Emails) > 0 {
		for _, EmailValue := range buyer.Contact.Emails {
			return EmailValue.Value
		}
	}

	return ""
}

// GetEmails returns all Emails for a Profile
func (buyer *Buyer) GetEmails() (emails []string) {
	if len(buyer.Contact.Emails) > 0 {
		for _, EmailValue := range buyer.Contact.Emails {
			emails = append(emails, EmailValue.Value)
		}
	}

	return
}

// A Category represents a product category model from the itembase API.
type Category struct {
	CategoryID string `json:"category_id,omitempty"`
	Language   string `json:"language,omitempty"`
	Value      string `json:"value,omitempty"`
}

// A ProductDescription represents a product description model from the itembase
// API, which may be in a specified language.
type ProductDescription struct {
	Language string `json:"language,omitempty"`
	Value    string `json:"value,omitempty"`
}

// A Brand represents a product brand model from the itembase API.
type Brand struct {
	Name struct {
		Language string `json:"language,omitempty"`
		Value    string `json:"value,omitempty"`
	} `json:"name,omitempty"`
}

type Identifier struct {
	ID string `json:"id,omitempty"`
}

type StockInformation struct {
	InStock        bool    `json:"in_stock,omitempty"`
	InventoryLevel float64 `json:"inventory_level,omitempty"`
	InventoryUnit  string  `json:"inventory_unit,omitempty"`
}

type ProductID string

func (productID ProductID) String() string {
	return string(productID)
}

// A Product represents a product entity from the itembase API.
//
// See http://sandbox.api.itembase.io/swagger-ui/
type Product struct {
	Active      bool                 `json:"active,omitempty"`
	Brand       Brand                `json:"brand,omitempty"`
	Categories  []Category           `json:"categories,omitempty"`
	Condition   string               `json:"condition,omitempty"`
	CreatedAt   *time.Time           `json:"created_at,omitempty"`
	Currency    string               `json:"currency,omitempty"`
	Description []ProductDescription `json:"description,omitempty"`
	ID          ProductID            `json:"id"`
	Identifier  Identifier           `json:"identifier,omitempty"`
	Name        []struct {
		Language string `json:"language,omitempty"`
		Value    string `json:"value,omitempty"`
	} `json:"name,omitempty"`
	OriginalReference string `json:"original_reference,omitempty"`
	PictureUrls       []struct {
		URLOriginal string `json:"url_original,omitempty"`
	} `json:"picture_urls,omitempty"`
	PricePerUnit float64 `json:"price_per_unit,omitempty"`
	Shipping     []struct {
		Price           float64 `json:"price,omitempty"`
		ShippingService string  `json:"shipping_service,omitempty"`
	} `json:"shipping,omitempty"`
	SourceID         string           `json:"source_id,omitempty"`
	StockInformation StockInformation `json:"stock_information,omitempty"`
	Tax              float64          `json:"tax,omitempty"`
	TaxRate          float64          `json:"tax_rate,omitempty"`
	UpdatedAt        *time.Time       `json:"updated_at,omitempty"`
	URL              string           `json:"url,omitempty"`
	Variants         []interface{}    `json:"variants,omitempty"`
}

func (product *Product) InStock() bool {
	return product.StockInformation.InStock
}

// Returns name for specified preferred language if present
func (product *Product) GetName(preferredLanguage string) (name string, ok bool) {

	for _, productName := range product.Name {
		if preferredLanguage == productName.Language {
			return cleanItembaseUnicode(productName.Value), true
		}
	}

	// if []struct{} is empty, return empty string
	return "", false
}

// Returns any name for Product
func (product *Product) GetDefaultName() (name string, ok bool) {

	for _, productName := range product.Name {
		return cleanItembaseUnicode(productName.Value), true
	}

	return "", false

}

func cleanItembaseUnicode(str string) string {
	str = strings.Replace(str, "\u00a0", " ", -1)
	str = strings.Replace(str, "\ufeff", "", -1)
	return str
}

// Billing represents a model from the itembase API containing the billing
// address of a Transaction.
type Billing struct {
	Address Address `json:"address,omitempty"`
}

type Shipping struct {
	Address Address `json:"address,omitempty"`
}

// Status describes a transactions' status
type Status struct {
	Global   string `json:"global,omitempty"`
	Payment  string `json:"payment,omitempty"`
	Shipping string `json:"shipping,omitempty"`
}

type TransactionID string

func (transactionID TransactionID) String() string {
	return string(transactionID)
}

// A Transaction represents a transaction entity from the itembase API.
//
// See http://sandbox.api.itembase.io/swagger-ui/
type Transaction struct {
	Billing           Billing       `json:"billing,omitempty"`
	Buyer             Buyer         `json:"buyer,omitempty"`
	CreatedAt         *time.Time    `json:"created_at,omitempty"`
	Currency          string        `json:"currency,omitempty"`
	ID                TransactionID `json:"id"`
	OriginalReference string        `json:"original_reference,omitempty"`
	Products          []Product     `json:"products,omitempty"`
	Shipping          Shipping      `json:"shipping,omitempty"`
	SourceID          string        `json:"source_id,omitempty"`
	Status            Status        `json:"status,omitempty"`
	TotalPrice        float64       `json:"total_price,omitempty"`
	TotalPriceNet     float64       `json:"total_price_net,omitempty"`
	TotalTax          float64       `json:"total_tax,omitempty"`
	UpdatedAt         *time.Time    `json:"updated_at,omitempty"`
}

func (t *Transaction) Completed() bool {
	if t.Status.Global == "completed" {
		return true
	}
	return false
}

// ItembaseResponse is a container for any Itembase response.
// It returns the resultset, Number of found documents and Number of documents returned
type ItembaseResponse struct {
	Documents            []interface{} `json:"documents"`
	NumDocumentsFound    int           `json:"num_documents_found"`
	NumDocumentsReturned int           `json:"num_documents_returned"`
}

// Transactions is a container for pagination of Transaction entities.
type Transactions struct {
	Transactions []Transaction `json:"documents"`
}

func (transactions *Transactions) Add(transaction interface{}) {

	var newTransaction Transaction
	convertTo(transaction, &newTransaction)
	transactions.Transactions = append(transactions.Transactions, newTransaction)

}

func (transactions *Transactions) Completed() (filteredTransactions Transactions) {
	for _, transaction := range transactions.Transactions {
		if transaction.Completed() {
			filteredTransactions.Add(transaction)
		}
	}

	return
}

// Profiles is a container for pagination of Profile entities.
type Profiles struct {
	Profiles []Profile `json:"documents"`
}

func (profiles *Profiles) Add(profile interface{}) {

	var newProfile Profile
	convertTo(profile, &newProfile)
	profiles.Profiles = append(profiles.Profiles, newProfile)

}

// Products is a container for pagination of Product entities.
type Products struct {
	Products []Product `json:"documents"`
}

func (products *Products) Add(product interface{}) {

	var newProduct Product
	convertTo(product, &newProduct)
	products.Products = append(products.Products, newProduct)

}

func (products *Products) InStock() (filteredProducts Products) {
	for _, product := range products.Products {
		if product.InStock() {
			filteredProducts.Add(product)
		}
	}
	return
}

// Get Products based on shopID
func (products *Products) ByShop(shopID string) (filteredProducts Products) {
	for _, product := range products.Products {
		if product.SourceID == shopID {
			filteredProducts.Add(product)
		}
	}
	return
}

// Buyers is a container for pagination of Buyer entities.
type Buyers struct {
	Buyers []Buyer `json:"documents"`
}

func (buyers *Buyers) Add(buyer interface{}) {

	var newBuyer Buyer
	convertTo(buyer, &newBuyer)
	buyers.Buyers = append(buyers.Buyers, newBuyer)

}

func (buyers *Buyers) ByShop(shopID string) (filteredBuyers Buyers) {

	for _, buyer := range buyers.Buyers {
		if buyer.SourceID == shopID {
			filteredBuyers.Add(buyer)
		}
	}
	return
}

// A User represents a user entity from the itembase API, such as returned from
// the "me" endpoint.
type User struct {
	UUID              string `json:"uuid"`
	Username          string `json:"username,omitempty"`
	FirstName         string `json:"first_name,omitempty"`
	LastName          string `json:"last_name,omitempty"`
	MiddleName        string `json:"middle_name,omitempty"`
	NameFormat        string `json:"name_format,omitempty"`
	Locale            string `json:"locale,omitempty"`
	Email             string `json:"email,omitempty"`
	PreferredCurrency string `json:"preferred_currency,omitempty"`
}

func ConvertTo(inputInterface, outputType interface{}) error {

	jsonBLOB, err := json.Marshal(inputInterface)
	if err != nil {
		log.Fatal(err)
		return err
	}

	err = json.Unmarshal(jsonBLOB, &outputType)
	if err != nil {
		log.Fatal(err)
		return err
	}

	return nil

}
<|endoftext|>


<commit_before>package procfinder

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"
)

func readNetTCP() ([]string, error) {
	procTCP, err := ioutil.ReadFile("/proc/net/tcp")

	if err != nil {
		return nil, err
	}

	lines := strings.Split(string(procTCP), "\n")

	return lines[1 : len(lines)-1], nil
}

// Process represents a unix based process. This provides the direct path to the exe that
// it was originally spawned with, along with the nicename and process ID.
type Process struct {
	PID         string
	Name        string
	Exe         string
	User        string
	IP          string
	Port        int64
	ForeignIP   string
	ForeignPort int64
}

func removeEmpty(array []string) []string {
	// remove empty data from line
	var newArray []string
	for _, i := range array {
		if i != "" {
			newArray = append(newArray, i)
		}
	}

	return newArray
}

// convert hexadecimal to decimal
func hexToDec(h string) (d int64) {
	d, _ = strconv.ParseInt(h, 16, 32)

	return d
}

// convert the ipv4 to decimal. would need to rearrange the ip because the default value
// is in little Endian order.
func ip(ip string) string {
	var out string

	// check ip size. if greater than 8, it is ipv6
	if len(ip) > 8 {
		i := []string{ip[30:32], ip[28:30], ip[26:28], ip[24:26], ip[22:24], ip[20:22], ip[18:20], ip[16:18], ip[14:16], ip[12:14], ip[10:12], ip[8:10], ip[6:8], ip[4:6], ip[2:4], ip[0:2]}
		out = fmt.Sprintf("%v%v:%v%v:%v%v:%v%v:%v%v:%v%v:%v%v:%v%v", i[14], i[15], i[13], i[12], i[10], i[11], i[8], i[9], i[6], i[7], i[4], i[5], i[2], i[3], i[0], i[1])
	} else {
		// ipv4
		i := []int64{hexToDec(ip[6:8]), hexToDec(ip[4:6]), hexToDec(ip[2:4]), hexToDec(ip[0:2])}

		out = fmt.Sprintf("%v.%v.%v.%v", i[0], i[1], i[2], i[3])
	}

	return out
}

// loop through all fd dirs of process on /proc to compare the inode and get the pid
func getPid(inode string) (pid string) {
	d, err := filepath.Glob("/proc/[0-9]*/fd/[0-9]*")
	if err != nil {
		return pid
	}

	for _, item := range d {
		path, _ := os.Readlink(item)
		if strings.Contains(path, inode) {
			pid = strings.Split(item, "/")[2]
		}
	}

	return pid
}

func getProcessExe(pid string) string {
	exe := fmt.Sprintf("/proc/%s/exe", pid)
	path, _ := os.Readlink(exe)
	return path
}

func getProcessName(pid string) (name string) {
	tmp, err := ioutil.ReadFile(fmt.Sprintf("/proc/%s/comm", pid))

	if err != nil {
		return ""
	}

	return strings.Split(string(tmp), "\n")[0]
}

func getUser(uid string) string {
	u, _ := user.LookupId(uid)
	return u.Username
}

// GetProcs crawls /proc/ for all pids that have bound ports
func GetProcs() (pl []*Process, err error) {
	tcp, err := readNetTCP()

	if err != nil {
		return nil, err
	}

	for _, line := range tcp {
		lineArray := removeEmpty(strings.Split(strings.TrimSpace(line), " "))
		ipPort := strings.Split(lineArray[1], ":")
		fipPort := strings.Split(lineArray[2], ":")

		proc := &Process{
			IP:          ip(ipPort[0]),
			Port:        hexToDec(ipPort[1]),
			ForeignIP:   ip(fipPort[0]),
			ForeignPort: hexToDec(fipPort[1]),
			User:        getUser(lineArray[7]),
			PID:         getPid(lineArray[9]),
		}

		proc.Exe = getProcessExe(proc.PID)
		proc.Name = getProcessName(proc.PID)

		pl = append(pl, proc)
	}

	return pl, nil
}
<commit_msg>return 0 if hex is out of range<commit_after>package procfinder

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"
)

func readNetTCP() ([]string, error) {
	procTCP, err := ioutil.ReadFile("/proc/net/tcp")

	if err != nil {
		return nil, err
	}

	lines := strings.Split(string(procTCP), "\n")

	return lines[1 : len(lines)-1], nil
}

// Process represents a unix based process. This provides the direct path to the exe that
// it was originally spawned with, along with the nicename and process ID.
type Process struct {
	PID         string
	Name        string
	Exe         string
	User        string
	IP          string
	Port        int64
	ForeignIP   string
	ForeignPort int64
}

func removeEmpty(array []string) []string {
	// remove empty data from line
	var newArray []string
	for _, i := range array {
		if i != "" {
			newArray = append(newArray, i)
		}
	}

	return newArray
}

// convert hexadecimal to decimal
func hexToDec(h string) int64 {
	d, err := strconv.ParseInt(h, 16, 32)

	if err != nil {
		return 0
	}

	return d
}

// convert the ipv4 to decimal. would need to rearrange the ip because the default value
// is in little Endian order.
func ip(ip string) string {
	var out string

	// check ip size. if greater than 8, it is ipv6
	if len(ip) > 8 {
		i := []string{ip[30:32], ip[28:30], ip[26:28], ip[24:26], ip[22:24], ip[20:22], ip[18:20], ip[16:18], ip[14:16], ip[12:14], ip[10:12], ip[8:10], ip[6:8], ip[4:6], ip[2:4], ip[0:2]}
		out = fmt.Sprintf("%v%v:%v%v:%v%v:%v%v:%v%v:%v%v:%v%v:%v%v", i[14], i[15], i[13], i[12], i[10], i[11], i[8], i[9], i[6], i[7], i[4], i[5], i[2], i[3], i[0], i[1])
	} else {
		// ipv4
		i := []int64{hexToDec(ip[6:8]), hexToDec(ip[4:6]), hexToDec(ip[2:4]), hexToDec(ip[0:2])}

		out = fmt.Sprintf("%v.%v.%v.%v", i[0], i[1], i[2], i[3])
	}

	return out
}

// loop through all fd dirs of process on /proc to compare the inode and get the pid
func getPid(inode string) (pid string) {
	d, err := filepath.Glob("/proc/[0-9]*/fd/[0-9]*")
	if err != nil {
		return pid
	}

	for _, item := range d {
		path, _ := os.Readlink(item)
		if strings.Contains(path, inode) {
			pid = strings.Split(item, "/")[2]
		}
	}

	return pid
}

func getProcessExe(pid string) string {
	exe := fmt.Sprintf("/proc/%s/exe", pid)
	path, _ := os.Readlink(exe)
	return path
}

func getProcessName(pid string) (name string) {
	tmp, err := ioutil.ReadFile(fmt.Sprintf("/proc/%s/comm", pid))

	if err != nil {
		return ""
	}

	return strings.Split(string(tmp), "\n")[0]
}

func getUser(uid string) string {
	u, _ := user.LookupId(uid)
	return u.Username
}

// GetProcs crawls /proc/ for all pids that have bound ports
func GetProcs() (pl []*Process, err error) {
	tcp, err := readNetTCP()

	if err != nil {
		return nil, err
	}

	for _, line := range tcp {
		lineArray := removeEmpty(strings.Split(strings.TrimSpace(line), " "))
		ipPort := strings.Split(lineArray[1], ":")
		fipPort := strings.Split(lineArray[2], ":")

		proc := &Process{
			IP:          ip(ipPort[0]),
			Port:        hexToDec(ipPort[1]),
			ForeignIP:   ip(fipPort[0]),
			ForeignPort: hexToDec(fipPort[1]),
			User:        getUser(lineArray[7]),
			PID:         getPid(lineArray[9]),
		}

		proc.Exe = getProcessExe(proc.PID)
		proc.Name = getProcessName(proc.PID)

		pl = append(pl, proc)
	}

	return pl, nil
}
<|endoftext|>