diff --git a/Gopkg.lock b/Gopkg.lock index 935a7c1b167..ecbf44f3d67 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -53,6 +53,14 @@ pruneopts = "NUT" revision = "de5bf2ad457846296e2031421a34e2568e304e35" +[[projects]] + digest = "1:36d54915655a1d3d14d2fbb0854f588d5ed9ed8b7c6fd287fbaa52000615cd1f" + name = "github.com/StackExchange/wmi" + packages = ["."] + pruneopts = "NUT" + revision = "cbe66965904dbe8a6cd589e2298e5d8b986bd7dd" + version = "1.1.0" + [[projects]] branch = "master" digest = "1:c9335bca964be4fd1d245e44d798f106f191a4d7dfd0217228a8fd58f0e93606" @@ -191,6 +199,17 @@ revision = "7536572e8d55209135cd5e7ccf7fce43dca217ab" version = "v0.1.0" +[[projects]] + digest = "1:022649cb675f044f3b7b6ab58c528dad7b11eee526ecd792a1b04ee4b0c692da" + name = "github.com/go-ole/go-ole" + packages = [ + ".", + "oleutil", + ] + pruneopts = "NUT" + revision = "97b6244175ae18ea6eef668034fd6565847501c9" + version = "v1.2.4" + [[projects]] digest = "1:260f7ebefc63024c8dfe2c9f1a2935a89fa4213637a1f522f592f80c001cc441" name = "github.com/go-openapi/jsonpointer" @@ -717,6 +736,20 @@ revision = "2ea7272aa4c7de5c8a568dff504404e8dc49945d" version = "v1.2.1" +[[projects]] + digest = "1:a3795b4e2d3bf4f6dcce18f1eeca71b670fce0216c9e1f7dbf4f3a96802e4560" + name = "github.com/shirou/gopsutil" + packages = [ + "cpu", + "internal/common", + "mem", + "net", + "process", + ] + pruneopts = "NUT" + revision = "7e94bb8bcde053b6d6c98bda5145e9742c913c39" + version = "v2.20.7" + [[projects]] digest = "1:90cf76d709ce9b057e7d75bd245bf7c1242d21ba4f908fb22c7a2a96d1dcc0ca" name = "github.com/spf13/afero" @@ -1470,6 +1503,7 @@ "github.com/onsi/ginkgo", "github.com/onsi/gomega", "github.com/prometheus/client_golang/prometheus", + "github.com/shirou/gopsutil/process", "github.com/spf13/viper", "github.com/tidwall/gjson", "golang.org/x/net/context", diff --git a/Gopkg.toml b/Gopkg.toml index 18d4ee5a655..70968340186 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -123,3 +123,7 @@ required = [ [[constraint]] name = "github.com/tidwall/gjson" version = "1.6.0" + +[[constraint]] + name = "github.com/shirou/gopsutil" + version = "2.20.7" diff --git a/cmd/metricscollector/v1beta1/file-metricscollector/main.go b/cmd/metricscollector/v1beta1/file-metricscollector/main.go index 7621344a968..98b457d5657 100644 --- a/cmd/metricscollector/v1beta1/file-metricscollector/main.go +++ b/cmd/metricscollector/v1beta1/file-metricscollector/main.go @@ -54,14 +54,14 @@ import ( ) var ( - metricsFileName = flag.String("path", "", "Metrics File Path") - trialName = flag.String("t", "", "Trial Name") - managerService = flag.String("s", "", "Katib Manager service") - metricNames = flag.String("m", "", "Metric names") - filters = flag.String("f", "", "Metric filters") - pollInterval = flag.Duration("p", common.DefaultPollInterval, "Poll interval to check if main process of worker container exit") - timeout = flag.Duration("timeout", common.DefaultTimeout, "Timeout to check if main process of worker container exit") - waitAll = flag.Bool("w", common.DefaultWaitAll, "Whether wait for all other main process of container exiting") + managerServiceAddr = flag.String("s", "", "Katib Manager service") + trialName = flag.String("t", "", "Trial Name") + metricsFilePath = flag.String("path", "", "Metrics File Path") + metricNames = flag.String("m", "", "Metric names") + metricFilters = flag.String("f", "", "Metric filters") + pollInterval = flag.Duration("p", common.DefaultPollInterval, "Poll interval between running processes check") + timeout = flag.Duration("timeout", common.DefaultTimeout, "Timeout before invoke error during running processes check") + waitAll = flag.Bool("w", common.DefaultWaitAll, "Whether wait for all other main process of container exiting") ) func printMetricsFile(mFile string) { @@ -86,18 +86,18 @@ func main() { flag.Parse() klog.Infof("Trial Name: %s", *trialName) - go printMetricsFile(*metricsFileName) + go printMetricsFile(*metricsFilePath) wopts := common.WaitPidsOpts{ PollInterval: *pollInterval, Timeout: *timeout, WaitAll: *waitAll, - CompletedMarkedDirPath: filepath.Dir(*metricsFileName), + CompletedMarkedDirPath: filepath.Dir(*metricsFilePath), } - if err := common.Wait(wopts); err != nil { + if err := common.WaitMainProcesses(wopts); err != nil { klog.Fatalf("Failed to wait for worker container: %v", err) } - conn, err := grpc.Dial(*managerService, grpc.WithInsecure()) + conn, err := grpc.Dial(*managerServiceAddr, grpc.WithInsecure()) if err != nil { klog.Fatalf("could not connect: %v", err) } @@ -109,10 +109,10 @@ func main() { metricList = strings.Split(*metricNames, ";") } var filterList []string - if len(*filters) != 0 { - filterList = strings.Split(*filters, ";") + if len(*metricFilters) != 0 { + filterList = strings.Split(*metricFilters, ";") } - olog, err := filemc.CollectObservationLog(*metricsFileName, metricList, filterList) + olog, err := filemc.CollectObservationLog(*metricsFilePath, metricList, filterList) if err != nil { klog.Fatalf("Failed to collect logs: %v", err) } diff --git a/cmd/metricscollector/v1beta1/tfevent-metricscollector/main.py b/cmd/metricscollector/v1beta1/tfevent-metricscollector/main.py index 710eeae06d8..4eec6b3bb24 100644 --- a/cmd/metricscollector/v1beta1/tfevent-metricscollector/main.py +++ b/cmd/metricscollector/v1beta1/tfevent-metricscollector/main.py @@ -1,7 +1,8 @@ import grpc import argparse import api_pb2 -from pns import WaitOtherMainProcesses +from pns import WaitMainProcesses +import const from tfevent_loader import MetricsCollector from logging import getLogger, StreamHandler, INFO @@ -13,12 +14,16 @@ def parse_options(): description='TF-Event MetricsCollector', add_help=True ) - parser.add_argument("-s", "--manager_server_addr", - type=str, default="katib-db-manager:6789") + + parser.add_argument("-s", "--manager_server_addr", type=str, default="") parser.add_argument("-t", "--trial_name", type=str, default="") - parser.add_argument("-path", "--dir_path", type=str, default="/log") + parser.add_argument("-path", "--metrics_file_dir", type=str, default=const.DEFAULT_METRICS_FILE_DIR) parser.add_argument("-m", "--metric_names", type=str, default="") parser.add_argument("-f", "--metric_filters", type=str, default="") + parser.add_argument("-p", "--poll_interval", type=int, default=const.DEFAULT_POLL_INTERVAL) + parser.add_argument("-timeout", "--timeout", type=int, default=const.DEFAULT_TIMEOUT) + parser.add_argument("-w", "--wait_all", type=bool, default=const.DEFAULT_WAIT_ALL) + opt = parser.parse_args() return opt @@ -36,10 +41,14 @@ def parse_options(): raise Exception("Invalid katib manager service address: %s" % opt.manager_server_addr) - WaitOtherMainProcesses(completed_marked_dir=opt.dir_path) + WaitMainProcesses( + pool_interval=opt.poll_interval, + timout=opt.timeout, + wait_all=opt.wait_all, + completed_marked_dir=opt.metrics_file_dir) mc = MetricsCollector(opt.metric_names.split(';')) - observation_log = mc.parse_file(opt.dir_path) + observation_log = mc.parse_file(opt.metrics_file_dir) channel = grpc.beta.implementations.insecure_channel( manager_server[0], int(manager_server[1])) diff --git a/pkg/metricscollector/v1beta1/common/const.go b/pkg/metricscollector/v1beta1/common/const.go index 7428f526468..0a432c07af2 100644 --- a/pkg/metricscollector/v1beta1/common/const.go +++ b/pkg/metricscollector/v1beta1/common/const.go @@ -21,16 +21,25 @@ import ( ) const ( + // DefaultPollInterval is the default value for interval between running processes check DefaultPollInterval = time.Second - DefaultTimeout = 0 - DefaultWaitAll = false - - MetricCollectorContainerName = "metrics-collector" - MetricLoggerCollectorContainerName = "metrics-logger-and-collector" - + // DefaultTimeout is the default value for timeout before invoke error during running processes check + // To run without timeout set value to 0 + DefaultTimeout = 0 + // DefaultWaitAll is the default value whether wait for all other main process of container exiting + DefaultWaitAll = true + // TrainingCompleted is the job finished marker in $$$$.pid file when main process is completed TrainingCompleted = "completed" + // DefaultFilter is the default metrics collector filter to parse the metrics. + // Metrics must be printed this way + // loss=0.3 + // accuracy=0.98 DefaultFilter = `([\w|-]+)\s*=\s*((-?\d+)(\.\d+)?)` + + // TODO (andreyvelich): Do we need to maintain 2 names? Should we leave only 1? + MetricCollectorContainerName = "metrics-collector" + MetricLoggerCollectorContainerName = "metrics-logger-and-collector" ) var ( diff --git a/pkg/metricscollector/v1beta1/common/const.py b/pkg/metricscollector/v1beta1/common/const.py new file mode 100644 index 00000000000..f64df59b0a9 --- /dev/null +++ b/pkg/metricscollector/v1beta1/common/const.py @@ -0,0 +1,10 @@ +# Default value for interval between running processes check +DEFAULT_POLL_INTERVAL = 1 +# Default value for timeout before invoke error during running processes check +DEFAULT_TIMEOUT = 0 +# Default value whether wait for all other main process of container exiting +DEFAULT_WAIT_ALL = True +# Default value for directory where TF event metrics are reported +DEFAULT_METRICS_FILE_DIR = "/log" +# Job finished marker in $$$$.pid file when main process is completed +TRAINING_COMPLETED = "completed" diff --git a/pkg/metricscollector/v1beta1/common/pns.go b/pkg/metricscollector/v1beta1/common/pns.go index c423d2285e6..2c3a0ba3ca4 100644 --- a/pkg/metricscollector/v1beta1/common/pns.go +++ b/pkg/metricscollector/v1beta1/common/pns.go @@ -24,11 +24,10 @@ import ( "strings" "time" - gops "github.com/mitchellh/go-ps" + psutil "github.com/shirou/gopsutil/process" ) -var ErrWaitPidTimeout = fmt.Errorf("Timed out waiting for PID to complete") - +// WaitPidsOpts is the input options for metrics collector type WaitPidsOpts struct { PollInterval time.Duration Timeout time.Duration @@ -36,93 +35,131 @@ type WaitPidsOpts struct { CompletedMarkedDirPath string } -func Wait(opts WaitPidsOpts) error { - pids, err := GetOtherMainProcesses() +// WaitMainProcesses holds metrics collector parser until required pids are finished. +func WaitMainProcesses(opts WaitPidsOpts) error { + + if runtime.GOOS != "linux" { + return fmt.Errorf("Platform '%s' unsupported", runtime.GOOS) + } + + pids, mainPid, err := GetMainProcesses(opts.CompletedMarkedDirPath) if err != nil { return err } - return WaitPIDS(pids, opts) + + return WaitPIDs(pids, mainPid, opts) } -func GetOtherMainProcesses() ([]int, error) { - pids := []int{} - allProcs, err := gops.Processes() +// GetMainProcesses returns array with all running processes pids +// and main process pid which metrics collector is waiting. +func GetMainProcesses(completedMarkedDirPath string) (map[int]bool, int, error) { + pids := make(map[int]bool) + allPids, err := psutil.Pids() + mainPid := 0 + if err != nil { - return pids, fmt.Errorf("Failed to list processes: %v", err) + return nil, 0, fmt.Errorf("Failed to list processes: %v", err) } thisPID := os.Getpid() - for _, proc := range allProcs { - pid := proc.Pid() - if pid == 1 || pid == thisPID || proc.PPid() != 0 { - // ignore the pause container, our own pid, and non-root processes + for _, pid := range allPids { + // Create process object from pid + proc, err := psutil.NewProcess(pid) + if err != nil { + return nil, 0, fmt.Errorf("Failed to create new Process from pid %v, error: %v", pid, err) + } + + // Get parent process + ppid, err := proc.Ppid() + if err != nil { + return nil, 0, fmt.Errorf("Unable to get parent pid for pid: %v, error: %v", ppid, err) + } + + // Ignore the pause container, our own pid, and non-root processes (parent pid != 0) + if pid == 1 || pid == int32(thisPID) || ppid != 0 { continue } - pids = append(pids, pid) - } - return pids, nil -} -func WaitPIDS(pids []int, opts ...WaitPidsOpts) error { - if runtime.GOOS != "linux" { - return fmt.Errorf("Platform '%s' unsupported", runtime.GOOS) - } - if len(pids) == 0 { - return nil - } - waitAll := false - var timeout time.Duration - var pollInterval = time.Second - if len(opts) > 0 { - if opts[0].PollInterval != 0 { - pollInterval = opts[0].PollInterval + // Read the process command line + cmdline, err := proc.Cmdline() + if err != nil { + return nil, 0, fmt.Errorf("Unable to get cmdline from pid %v, error: %v", pid, err) } - if opts[0].Timeout != 0 { - timeout = opts[0].Timeout + + // By default mainPid is the first process. + // Command line contains completed marker for the main pid + // For example: echo completed > /var/log/katib/$$$$.pid + // completedMarkedDirPath is the directory for completed marker, e.g. /var/log/katib + if mainPid == 0 || + strings.Contains(cmdline, fmt.Sprintf("echo %s > %s", TrainingCompleted, completedMarkedDirPath)) { + mainPid = int(pid) } - waitAll = opts[0].WaitAll + + pids[int(pid)] = true } - finishedPids := []int{} + return pids, mainPid, nil +} + +// WaitPIDs waits until all pids are finished. +// If waitAll == false WaitPIDs waits until main process is finished. +func WaitPIDs(pids map[int]bool, mainPid int, opts WaitPidsOpts) error { - ticker := time.NewTicker(pollInterval) - defer ticker.Stop() - var timoutCh <-chan time.Time - if timeout != 0 { - timoutCh = time.NewTimer(timeout).C - } - for { - select { - case <-ticker.C: - for _, pid := range pids { - path := fmt.Sprintf("/proc/%d", pid) - _, err := os.Stat(path) - if err != nil { - if os.IsNotExist(err) { - if opts[0].CompletedMarkedDirPath != "" { - markFile := filepath.Join(opts[0].CompletedMarkedDirPath, fmt.Sprintf("%d.pid", pid)) - if data, err := ioutil.ReadFile(markFile); err != nil { - return fmt.Errorf("Process %d hadn't completed: %v", pid, err) - } else { - if strings.TrimSpace(string(data)) != TrainingCompleted { - return fmt.Errorf("Process %d hadn't completed", pid) - } + // notFinishedPids contains pids that are not finished yet + notFinishedPids := pids + + // Get info from options + waitAll := opts.WaitAll + timeout := opts.Timeout + endTime := time.Now().Add(timeout) + pollInterval := opts.PollInterval + + // Start main wait loop + // We should exit when timeout is out or notFinishedPids is empty + for (timeout == 0 || time.Now().Before(endTime)) && len(notFinishedPids) > 0 { + // Start loop over not finished pids + for pid := range notFinishedPids { + // If pid is completed /proc/ dir doesn't exist + path := fmt.Sprintf("/proc/%d", pid) + _, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + if pid == mainPid { + // For mainPid we check if file with "completed" marker exists if CompletedMarkedDirPath is set + if opts.CompletedMarkedDirPath != "" { + markFile := filepath.Join(opts.CompletedMarkedDirPath, fmt.Sprintf("%d.pid", pid)) + // Read file with "completed" marker + if contents, err := ioutil.ReadFile(markFile); err != nil { + return fmt.Errorf("Unable to read file %v for pid %v, error: %v", markFile, pid, err) + // Check if file contains "completed" marker + } else if strings.TrimSpace(string(contents)) != TrainingCompleted { + return fmt.Errorf("Unable to find marker: %v in file: %v with contents: %v for pid: %v", + TrainingCompleted, markFile, string(contents), pid) } } - if waitAll { - finishedPids = append(finishedPids, pid) - if len(finishedPids) == len(pids) { - return nil - } - } else { + // Delete main pid from map with pids + delete(notFinishedPids, pid) + // Exit loop if wait all is false because main pid is finished + if !waitAll { return nil } + // Delete not main pid from map with pids + } else { + delete(notFinishedPids, pid) } + // We should receive only not exist error when we check /proc/ dir + } else { return fmt.Errorf("Fail to check process info: %v", err) } } - case <-timoutCh: - return ErrWaitPidTimeout } + // Sleep for pollInterval seconds before next attempt + time.Sleep(pollInterval) + } + + // After main loop notFinishedPids map should be empty + if len(notFinishedPids) != 0 { + return fmt.Errorf("Timed out waiting for pids to complete") } + return nil } diff --git a/pkg/metricscollector/v1beta1/common/pns.py b/pkg/metricscollector/v1beta1/common/pns.py index 04e8ff7758d..873955c34d0 100644 --- a/pkg/metricscollector/v1beta1/common/pns.py +++ b/pkg/metricscollector/v1beta1/common/pns.py @@ -1,48 +1,103 @@ import os import psutil +import sys import time +import const -def GetOtherMainProcesses(): - this_pid = psutil.Process().pid +def WaitMainProcesses(pool_interval, timout, wait_all, completed_marked_dir): + """ + Hold metrics collector parser until required pids are finished + """ + + if not sys.platform.startswith('linux'): + raise Exception("Platform '{}' unsupported".format(sys.platform)) + + pids, main_pid = GetMainProcesses(completed_marked_dir) + + return WaitPIDs(pids, main_pid, pool_interval, timout, wait_all, completed_marked_dir) + + +def GetMainProcesses(completed_marked_dir): + """ + Return array with all running processes pids and main process pid which metrics collector is waiting. + """ pids = set() + main_pid = 0 + this_pid = psutil.Process().pid + for proc in psutil.process_iter(): + # Get pid from process object pid = proc.pid + + # Get parent process ppid = proc.ppid() + + # Ignore the pause container, our own pid, and non-root processes (parent pid != 0) if pid == 1 or pid == this_pid or ppid != 0: - # ignore the pause container, our own pid, and non-root processes continue + + # Read the process command line, join all cmdlines in one string + cmd_lind = " ".join(proc.cmdline()) + + # By default main_pid is the first process. + # Command line contains completed marker for the main pid + # For example: echo completed > /var/log/katib/$$$$.pid + # completed_marked_dir is the directory for completed marker, e.g. /var/log/katib + if main_pid == 0 or ("echo {} > {}".format(const.TRAINING_COMPLETED, completed_marked_dir) in cmd_lind): + main_pid = pid + pids.add(pid) - return pids + + return pids, main_pid -def WaitPIDs(pids, poll_interval_seconds=1, timeout_seconds=0, is_wait_all=False, completed_marked_dir=""): +def WaitPIDs(pids, main_pid, pool_interval, timout, wait_all, completed_marked_dir): + """ + Waits until all pids are finished. + If waitAll == false WaitPIDs waits until main process is finished. + """ start = 0 - pids = set(pids) - if poll_interval_seconds <= 0: + # not_finished_pids contains pids that are not finished yet + not_finished_pids = set(pids) + + if pool_interval <= 0: raise Exception("Poll interval seconds must be a positive integer") - while (timeout_seconds <= 0 or start < timeout_seconds) and len(pids) > 0: - stop_pids = set() - for pid in pids: - path = "/proc/%d" % pid - if os.path.isdir(path): - continue - else: - if completed_marked_dir: - mark_file = os.path.join(completed_marked_dir, "%d.pid" % pid) - with open(mark_file) as file_obj: - contents = file_obj.read() - if contents.strip() != "completed": - raise Exception("Pid %d hadn't completed" % pid) - if is_wait_all: - stop_pids.add(pid) + + # Start main wait loop + # We should exit when timeout is out or not_finished_pids is empty + while (timout <= 0 or start < timout) and len(not_finished_pids) > 0: + finished_pids = set() + for pid in not_finished_pids: + # If pid is completed /proc/ dir doesn't exist + path = "/proc/{}".format(pid) + if not os.path.exists(path): + if pid == main_pid: + # For main_pid we check if file with "completed" marker exists if completed_marked_dir is set + if completed_marked_dir: + mark_file = os.path.join(completed_marked_dir, "{}.pid".format(pid)) + # Check if file contains "completed" marker + with open(mark_file) as file_obj: + contents = file_obj.read() + if contents.strip() != const.TRAINING_COMPLETED: + raise Exception( + "Unable to find marker: {} in file: {} with contents: {} for pid: {}".format( + const.TRAINING_COMPLETED, mark_file, contents, pid)) + # Add main pid to finished pids set + finished_pids.add(pid) + # Exit loop if wait all is false because main pid is finished + if not wait_all: + return + # Add not main pid to finished pids set else: - return - if is_wait_all: - pids = pids - stop_pids - time.sleep(poll_interval_seconds) - start = start + poll_interval_seconds + finished_pids.add(pid) + # Update not finished pids set with finished_pids set + not_finished_pids = not_finished_pids - finished_pids + # Sleep for pool_interval seconds before next attempt + time.sleep(pool_interval) + start = start + pool_interval -def WaitOtherMainProcesses(poll_interval_seconds=1, timeout_seconds=0, is_wait_all=False, completed_marked_dir=""): - return WaitPIDs(GetOtherMainProcesses(), poll_interval_seconds, timeout_seconds, is_wait_all, completed_marked_dir) + # After main loop not finished pids set should be empty + if not_finished_pids: + raise Exception("Timed out waiting for pids to complete") diff --git a/vendor/github.com/StackExchange/wmi/LICENSE b/vendor/github.com/StackExchange/wmi/LICENSE new file mode 100644 index 00000000000..ae80b67209e --- /dev/null +++ b/vendor/github.com/StackExchange/wmi/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Stack Exchange + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/StackExchange/wmi/swbemservices.go b/vendor/github.com/StackExchange/wmi/swbemservices.go new file mode 100644 index 00000000000..3ff87563037 --- /dev/null +++ b/vendor/github.com/StackExchange/wmi/swbemservices.go @@ -0,0 +1,260 @@ +// +build windows + +package wmi + +import ( + "fmt" + "reflect" + "runtime" + "sync" + + "github.com/go-ole/go-ole" + "github.com/go-ole/go-ole/oleutil" +) + +// SWbemServices is used to access wmi. See https://msdn.microsoft.com/en-us/library/aa393719(v=vs.85).aspx +type SWbemServices struct { + //TODO: track namespace. Not sure if we can re connect to a different namespace using the same instance + cWMIClient *Client //This could also be an embedded struct, but then we would need to branch on Client vs SWbemServices in the Query method + sWbemLocatorIUnknown *ole.IUnknown + sWbemLocatorIDispatch *ole.IDispatch + queries chan *queryRequest + closeError chan error + lQueryorClose sync.Mutex +} + +type queryRequest struct { + query string + dst interface{} + args []interface{} + finished chan error +} + +// InitializeSWbemServices will return a new SWbemServices object that can be used to query WMI +func InitializeSWbemServices(c *Client, connectServerArgs ...interface{}) (*SWbemServices, error) { + //fmt.Println("InitializeSWbemServices: Starting") + //TODO: implement connectServerArgs as optional argument for init with connectServer call + s := new(SWbemServices) + s.cWMIClient = c + s.queries = make(chan *queryRequest) + initError := make(chan error) + go s.process(initError) + + err, ok := <-initError + if ok { + return nil, err //Send error to caller + } + //fmt.Println("InitializeSWbemServices: Finished") + return s, nil +} + +// Close will clear and release all of the SWbemServices resources +func (s *SWbemServices) Close() error { + s.lQueryorClose.Lock() + if s == nil || s.sWbemLocatorIDispatch == nil { + s.lQueryorClose.Unlock() + return fmt.Errorf("SWbemServices is not Initialized") + } + if s.queries == nil { + s.lQueryorClose.Unlock() + return fmt.Errorf("SWbemServices has been closed") + } + //fmt.Println("Close: sending close request") + var result error + ce := make(chan error) + s.closeError = ce //Race condition if multiple callers to close. May need to lock here + close(s.queries) //Tell background to shut things down + s.lQueryorClose.Unlock() + err, ok := <-ce + if ok { + result = err + } + //fmt.Println("Close: finished") + return result +} + +func (s *SWbemServices) process(initError chan error) { + //fmt.Println("process: starting background thread initialization") + //All OLE/WMI calls must happen on the same initialized thead, so lock this goroutine + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED) + if err != nil { + oleCode := err.(*ole.OleError).Code() + if oleCode != ole.S_OK && oleCode != S_FALSE { + initError <- fmt.Errorf("ole.CoInitializeEx error: %v", err) + return + } + } + defer ole.CoUninitialize() + + unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator") + if err != nil { + initError <- fmt.Errorf("CreateObject SWbemLocator error: %v", err) + return + } else if unknown == nil { + initError <- ErrNilCreateObject + return + } + defer unknown.Release() + s.sWbemLocatorIUnknown = unknown + + dispatch, err := s.sWbemLocatorIUnknown.QueryInterface(ole.IID_IDispatch) + if err != nil { + initError <- fmt.Errorf("SWbemLocator QueryInterface error: %v", err) + return + } + defer dispatch.Release() + s.sWbemLocatorIDispatch = dispatch + + // we can't do the ConnectServer call outside the loop unless we find a way to track and re-init the connectServerArgs + //fmt.Println("process: initialized. closing initError") + close(initError) + //fmt.Println("process: waiting for queries") + for q := range s.queries { + //fmt.Printf("process: new query: len(query)=%d\n", len(q.query)) + errQuery := s.queryBackground(q) + //fmt.Println("process: s.queryBackground finished") + if errQuery != nil { + q.finished <- errQuery + } + close(q.finished) + } + //fmt.Println("process: queries channel closed") + s.queries = nil //set channel to nil so we know it is closed + //TODO: I think the Release/Clear calls can panic if things are in a bad state. + //TODO: May need to recover from panics and send error to method caller instead. + close(s.closeError) +} + +// Query runs the WQL query using a SWbemServices instance and appends the values to dst. +// +// dst must have type *[]S or *[]*S, for some struct type S. Fields selected in +// the query must have the same name in dst. Supported types are all signed and +// unsigned integers, time.Time, string, bool, or a pointer to one of those. +// Array types are not supported. +// +// By default, the local machine and default namespace are used. These can be +// changed using connectServerArgs. See +// http://msdn.microsoft.com/en-us/library/aa393720.aspx for details. +func (s *SWbemServices) Query(query string, dst interface{}, connectServerArgs ...interface{}) error { + s.lQueryorClose.Lock() + if s == nil || s.sWbemLocatorIDispatch == nil { + s.lQueryorClose.Unlock() + return fmt.Errorf("SWbemServices is not Initialized") + } + if s.queries == nil { + s.lQueryorClose.Unlock() + return fmt.Errorf("SWbemServices has been closed") + } + + //fmt.Println("Query: Sending query request") + qr := queryRequest{ + query: query, + dst: dst, + args: connectServerArgs, + finished: make(chan error), + } + s.queries <- &qr + s.lQueryorClose.Unlock() + err, ok := <-qr.finished + if ok { + //fmt.Println("Query: Finished with error") + return err //Send error to caller + } + //fmt.Println("Query: Finished") + return nil +} + +func (s *SWbemServices) queryBackground(q *queryRequest) error { + if s == nil || s.sWbemLocatorIDispatch == nil { + return fmt.Errorf("SWbemServices is not Initialized") + } + wmi := s.sWbemLocatorIDispatch //Should just rename in the code, but this will help as we break things apart + //fmt.Println("queryBackground: Starting") + + dv := reflect.ValueOf(q.dst) + if dv.Kind() != reflect.Ptr || dv.IsNil() { + return ErrInvalidEntityType + } + dv = dv.Elem() + mat, elemType := checkMultiArg(dv) + if mat == multiArgTypeInvalid { + return ErrInvalidEntityType + } + + // service is a SWbemServices + serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", q.args...) + if err != nil { + return err + } + service := serviceRaw.ToIDispatch() + defer serviceRaw.Clear() + + // result is a SWBemObjectSet + resultRaw, err := oleutil.CallMethod(service, "ExecQuery", q.query) + if err != nil { + return err + } + result := resultRaw.ToIDispatch() + defer resultRaw.Clear() + + count, err := oleInt64(result, "Count") + if err != nil { + return err + } + + enumProperty, err := result.GetProperty("_NewEnum") + if err != nil { + return err + } + defer enumProperty.Clear() + + enum, err := enumProperty.ToIUnknown().IEnumVARIANT(ole.IID_IEnumVariant) + if err != nil { + return err + } + if enum == nil { + return fmt.Errorf("can't get IEnumVARIANT, enum is nil") + } + defer enum.Release() + + // Initialize a slice with Count capacity + dv.Set(reflect.MakeSlice(dv.Type(), 0, int(count))) + + var errFieldMismatch error + for itemRaw, length, err := enum.Next(1); length > 0; itemRaw, length, err = enum.Next(1) { + if err != nil { + return err + } + + err := func() error { + // item is a SWbemObject, but really a Win32_Process + item := itemRaw.ToIDispatch() + defer item.Release() + + ev := reflect.New(elemType) + if err = s.cWMIClient.loadEntity(ev.Interface(), item); err != nil { + if _, ok := err.(*ErrFieldMismatch); ok { + // We continue loading entities even in the face of field mismatch errors. + // If we encounter any other error, that other error is returned. Otherwise, + // an ErrFieldMismatch is returned. + errFieldMismatch = err + } else { + return err + } + } + if mat != multiArgTypeStructPtr { + ev = ev.Elem() + } + dv.Set(reflect.Append(dv, ev)) + return nil + }() + if err != nil { + return err + } + } + //fmt.Println("queryBackground: Finished") + return errFieldMismatch +} diff --git a/vendor/github.com/StackExchange/wmi/wmi.go b/vendor/github.com/StackExchange/wmi/wmi.go new file mode 100644 index 00000000000..eab18cbfee0 --- /dev/null +++ b/vendor/github.com/StackExchange/wmi/wmi.go @@ -0,0 +1,501 @@ +// +build windows + +/* +Package wmi provides a WQL interface for WMI on Windows. + +Example code to print names of running processes: + + type Win32_Process struct { + Name string + } + + func main() { + var dst []Win32_Process + q := wmi.CreateQuery(&dst, "") + err := wmi.Query(q, &dst) + if err != nil { + log.Fatal(err) + } + for i, v := range dst { + println(i, v.Name) + } + } + +*/ +package wmi + +import ( + "bytes" + "errors" + "fmt" + "log" + "os" + "reflect" + "runtime" + "strconv" + "strings" + "sync" + "time" + + "github.com/go-ole/go-ole" + "github.com/go-ole/go-ole/oleutil" +) + +var l = log.New(os.Stdout, "", log.LstdFlags) + +var ( + ErrInvalidEntityType = errors.New("wmi: invalid entity type") + // ErrNilCreateObject is the error returned if CreateObject returns nil even + // if the error was nil. + ErrNilCreateObject = errors.New("wmi: create object returned nil") + lock sync.Mutex +) + +// S_FALSE is returned by CoInitializeEx if it was already called on this thread. +const S_FALSE = 0x00000001 + +// QueryNamespace invokes Query with the given namespace on the local machine. +func QueryNamespace(query string, dst interface{}, namespace string) error { + return Query(query, dst, nil, namespace) +} + +// Query runs the WQL query and appends the values to dst. +// +// dst must have type *[]S or *[]*S, for some struct type S. Fields selected in +// the query must have the same name in dst. Supported types are all signed and +// unsigned integers, time.Time, string, bool, or a pointer to one of those. +// Array types are not supported. +// +// By default, the local machine and default namespace are used. These can be +// changed using connectServerArgs. See +// http://msdn.microsoft.com/en-us/library/aa393720.aspx for details. +// +// Query is a wrapper around DefaultClient.Query. +func Query(query string, dst interface{}, connectServerArgs ...interface{}) error { + if DefaultClient.SWbemServicesClient == nil { + return DefaultClient.Query(query, dst, connectServerArgs...) + } + return DefaultClient.SWbemServicesClient.Query(query, dst, connectServerArgs...) +} + +// A Client is an WMI query client. +// +// Its zero value (DefaultClient) is a usable client. +type Client struct { + // NonePtrZero specifies if nil values for fields which aren't pointers + // should be returned as the field types zero value. + // + // Setting this to true allows stucts without pointer fields to be used + // without the risk failure should a nil value returned from WMI. + NonePtrZero bool + + // PtrNil specifies if nil values for pointer fields should be returned + // as nil. + // + // Setting this to true will set pointer fields to nil where WMI + // returned nil, otherwise the types zero value will be returned. + PtrNil bool + + // AllowMissingFields specifies that struct fields not present in the + // query result should not result in an error. + // + // Setting this to true allows custom queries to be used with full + // struct definitions instead of having to define multiple structs. + AllowMissingFields bool + + // SWbemServiceClient is an optional SWbemServices object that can be + // initialized and then reused across multiple queries. If it is null + // then the method will initialize a new temporary client each time. + SWbemServicesClient *SWbemServices +} + +// DefaultClient is the default Client and is used by Query, QueryNamespace +var DefaultClient = &Client{} + +// Query runs the WQL query and appends the values to dst. +// +// dst must have type *[]S or *[]*S, for some struct type S. Fields selected in +// the query must have the same name in dst. Supported types are all signed and +// unsigned integers, time.Time, string, bool, or a pointer to one of those. +// Array types are not supported. +// +// By default, the local machine and default namespace are used. These can be +// changed using connectServerArgs. See +// http://msdn.microsoft.com/en-us/library/aa393720.aspx for details. +func (c *Client) Query(query string, dst interface{}, connectServerArgs ...interface{}) error { + dv := reflect.ValueOf(dst) + if dv.Kind() != reflect.Ptr || dv.IsNil() { + return ErrInvalidEntityType + } + dv = dv.Elem() + mat, elemType := checkMultiArg(dv) + if mat == multiArgTypeInvalid { + return ErrInvalidEntityType + } + + lock.Lock() + defer lock.Unlock() + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED) + if err != nil { + oleCode := err.(*ole.OleError).Code() + if oleCode != ole.S_OK && oleCode != S_FALSE { + return err + } + } + defer ole.CoUninitialize() + + unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator") + if err != nil { + return err + } else if unknown == nil { + return ErrNilCreateObject + } + defer unknown.Release() + + wmi, err := unknown.QueryInterface(ole.IID_IDispatch) + if err != nil { + return err + } + defer wmi.Release() + + // service is a SWbemServices + serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", connectServerArgs...) + if err != nil { + return err + } + service := serviceRaw.ToIDispatch() + defer serviceRaw.Clear() + + // result is a SWBemObjectSet + resultRaw, err := oleutil.CallMethod(service, "ExecQuery", query) + if err != nil { + return err + } + result := resultRaw.ToIDispatch() + defer resultRaw.Clear() + + count, err := oleInt64(result, "Count") + if err != nil { + return err + } + + enumProperty, err := result.GetProperty("_NewEnum") + if err != nil { + return err + } + defer enumProperty.Clear() + + enum, err := enumProperty.ToIUnknown().IEnumVARIANT(ole.IID_IEnumVariant) + if err != nil { + return err + } + if enum == nil { + return fmt.Errorf("can't get IEnumVARIANT, enum is nil") + } + defer enum.Release() + + // Initialize a slice with Count capacity + dv.Set(reflect.MakeSlice(dv.Type(), 0, int(count))) + + var errFieldMismatch error + for itemRaw, length, err := enum.Next(1); length > 0; itemRaw, length, err = enum.Next(1) { + if err != nil { + return err + } + + err := func() error { + // item is a SWbemObject, but really a Win32_Process + item := itemRaw.ToIDispatch() + defer item.Release() + + ev := reflect.New(elemType) + if err = c.loadEntity(ev.Interface(), item); err != nil { + if _, ok := err.(*ErrFieldMismatch); ok { + // We continue loading entities even in the face of field mismatch errors. + // If we encounter any other error, that other error is returned. Otherwise, + // an ErrFieldMismatch is returned. + errFieldMismatch = err + } else { + return err + } + } + if mat != multiArgTypeStructPtr { + ev = ev.Elem() + } + dv.Set(reflect.Append(dv, ev)) + return nil + }() + if err != nil { + return err + } + } + return errFieldMismatch +} + +// ErrFieldMismatch is returned when a field is to be loaded into a different +// type than the one it was stored from, or when a field is missing or +// unexported in the destination struct. +// StructType is the type of the struct pointed to by the destination argument. +type ErrFieldMismatch struct { + StructType reflect.Type + FieldName string + Reason string +} + +func (e *ErrFieldMismatch) Error() string { + return fmt.Sprintf("wmi: cannot load field %q into a %q: %s", + e.FieldName, e.StructType, e.Reason) +} + +var timeType = reflect.TypeOf(time.Time{}) + +// loadEntity loads a SWbemObject into a struct pointer. +func (c *Client) loadEntity(dst interface{}, src *ole.IDispatch) (errFieldMismatch error) { + v := reflect.ValueOf(dst).Elem() + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + of := f + isPtr := f.Kind() == reflect.Ptr + if isPtr { + ptr := reflect.New(f.Type().Elem()) + f.Set(ptr) + f = f.Elem() + } + n := v.Type().Field(i).Name + if !f.CanSet() { + return &ErrFieldMismatch{ + StructType: of.Type(), + FieldName: n, + Reason: "CanSet() is false", + } + } + prop, err := oleutil.GetProperty(src, n) + if err != nil { + if !c.AllowMissingFields { + errFieldMismatch = &ErrFieldMismatch{ + StructType: of.Type(), + FieldName: n, + Reason: "no such struct field", + } + } + continue + } + defer prop.Clear() + + if prop.VT == 0x1 { //VT_NULL + continue + } + + switch val := prop.Value().(type) { + case int8, int16, int32, int64, int: + v := reflect.ValueOf(val).Int() + switch f.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + f.SetInt(v) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + f.SetUint(uint64(v)) + default: + return &ErrFieldMismatch{ + StructType: of.Type(), + FieldName: n, + Reason: "not an integer class", + } + } + case uint8, uint16, uint32, uint64: + v := reflect.ValueOf(val).Uint() + switch f.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + f.SetInt(int64(v)) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + f.SetUint(v) + default: + return &ErrFieldMismatch{ + StructType: of.Type(), + FieldName: n, + Reason: "not an integer class", + } + } + case string: + switch f.Kind() { + case reflect.String: + f.SetString(val) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + iv, err := strconv.ParseInt(val, 10, 64) + if err != nil { + return err + } + f.SetInt(iv) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + uv, err := strconv.ParseUint(val, 10, 64) + if err != nil { + return err + } + f.SetUint(uv) + case reflect.Struct: + switch f.Type() { + case timeType: + if len(val) == 25 { + mins, err := strconv.Atoi(val[22:]) + if err != nil { + return err + } + val = val[:22] + fmt.Sprintf("%02d%02d", mins/60, mins%60) + } + t, err := time.Parse("20060102150405.000000-0700", val) + if err != nil { + return err + } + f.Set(reflect.ValueOf(t)) + } + } + case bool: + switch f.Kind() { + case reflect.Bool: + f.SetBool(val) + default: + return &ErrFieldMismatch{ + StructType: of.Type(), + FieldName: n, + Reason: "not a bool", + } + } + case float32: + switch f.Kind() { + case reflect.Float32: + f.SetFloat(float64(val)) + default: + return &ErrFieldMismatch{ + StructType: of.Type(), + FieldName: n, + Reason: "not a Float32", + } + } + default: + if f.Kind() == reflect.Slice { + switch f.Type().Elem().Kind() { + case reflect.String: + safeArray := prop.ToArray() + if safeArray != nil { + arr := safeArray.ToValueArray() + fArr := reflect.MakeSlice(f.Type(), len(arr), len(arr)) + for i, v := range arr { + s := fArr.Index(i) + s.SetString(v.(string)) + } + f.Set(fArr) + } + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + safeArray := prop.ToArray() + if safeArray != nil { + arr := safeArray.ToValueArray() + fArr := reflect.MakeSlice(f.Type(), len(arr), len(arr)) + for i, v := range arr { + s := fArr.Index(i) + s.SetUint(reflect.ValueOf(v).Uint()) + } + f.Set(fArr) + } + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + safeArray := prop.ToArray() + if safeArray != nil { + arr := safeArray.ToValueArray() + fArr := reflect.MakeSlice(f.Type(), len(arr), len(arr)) + for i, v := range arr { + s := fArr.Index(i) + s.SetInt(reflect.ValueOf(v).Int()) + } + f.Set(fArr) + } + default: + return &ErrFieldMismatch{ + StructType: of.Type(), + FieldName: n, + Reason: fmt.Sprintf("unsupported slice type (%T)", val), + } + } + } else { + typeof := reflect.TypeOf(val) + if typeof == nil && (isPtr || c.NonePtrZero) { + if (isPtr && c.PtrNil) || (!isPtr && c.NonePtrZero) { + of.Set(reflect.Zero(of.Type())) + } + break + } + return &ErrFieldMismatch{ + StructType: of.Type(), + FieldName: n, + Reason: fmt.Sprintf("unsupported type (%T)", val), + } + } + } + } + return errFieldMismatch +} + +type multiArgType int + +const ( + multiArgTypeInvalid multiArgType = iota + multiArgTypeStruct + multiArgTypeStructPtr +) + +// checkMultiArg checks that v has type []S, []*S for some struct type S. +// +// It returns what category the slice's elements are, and the reflect.Type +// that represents S. +func checkMultiArg(v reflect.Value) (m multiArgType, elemType reflect.Type) { + if v.Kind() != reflect.Slice { + return multiArgTypeInvalid, nil + } + elemType = v.Type().Elem() + switch elemType.Kind() { + case reflect.Struct: + return multiArgTypeStruct, elemType + case reflect.Ptr: + elemType = elemType.Elem() + if elemType.Kind() == reflect.Struct { + return multiArgTypeStructPtr, elemType + } + } + return multiArgTypeInvalid, nil +} + +func oleInt64(item *ole.IDispatch, prop string) (int64, error) { + v, err := oleutil.GetProperty(item, prop) + if err != nil { + return 0, err + } + defer v.Clear() + + i := int64(v.Val) + return i, nil +} + +// CreateQuery returns a WQL query string that queries all columns of src. where +// is an optional string that is appended to the query, to be used with WHERE +// clauses. In such a case, the "WHERE" string should appear at the beginning. +func CreateQuery(src interface{}, where string) string { + var b bytes.Buffer + b.WriteString("SELECT ") + s := reflect.Indirect(reflect.ValueOf(src)) + t := s.Type() + if s.Kind() == reflect.Slice { + t = t.Elem() + } + if t.Kind() != reflect.Struct { + return "" + } + var fields []string + for i := 0; i < t.NumField(); i++ { + fields = append(fields, t.Field(i).Name) + } + b.WriteString(strings.Join(fields, ", ")) + b.WriteString(" FROM ") + b.WriteString(t.Name()) + b.WriteString(" " + where) + return b.String() +} diff --git a/vendor/github.com/go-ole/go-ole/LICENSE b/vendor/github.com/go-ole/go-ole/LICENSE new file mode 100644 index 00000000000..623ec06f91c --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2013-2017 Yasuhiro Matsumoto, + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/go-ole/go-ole/com.go b/vendor/github.com/go-ole/go-ole/com.go new file mode 100644 index 00000000000..6f986b1894e --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/com.go @@ -0,0 +1,344 @@ +// +build windows + +package ole + +import ( + "syscall" + "unicode/utf16" + "unsafe" +) + +var ( + procCoInitialize, _ = modole32.FindProc("CoInitialize") + procCoInitializeEx, _ = modole32.FindProc("CoInitializeEx") + procCoUninitialize, _ = modole32.FindProc("CoUninitialize") + procCoCreateInstance, _ = modole32.FindProc("CoCreateInstance") + procCoTaskMemFree, _ = modole32.FindProc("CoTaskMemFree") + procCLSIDFromProgID, _ = modole32.FindProc("CLSIDFromProgID") + procCLSIDFromString, _ = modole32.FindProc("CLSIDFromString") + procStringFromCLSID, _ = modole32.FindProc("StringFromCLSID") + procStringFromIID, _ = modole32.FindProc("StringFromIID") + procIIDFromString, _ = modole32.FindProc("IIDFromString") + procCoGetObject, _ = modole32.FindProc("CoGetObject") + procGetUserDefaultLCID, _ = modkernel32.FindProc("GetUserDefaultLCID") + procCopyMemory, _ = modkernel32.FindProc("RtlMoveMemory") + procVariantInit, _ = modoleaut32.FindProc("VariantInit") + procVariantClear, _ = modoleaut32.FindProc("VariantClear") + procVariantTimeToSystemTime, _ = modoleaut32.FindProc("VariantTimeToSystemTime") + procSysAllocString, _ = modoleaut32.FindProc("SysAllocString") + procSysAllocStringLen, _ = modoleaut32.FindProc("SysAllocStringLen") + procSysFreeString, _ = modoleaut32.FindProc("SysFreeString") + procSysStringLen, _ = modoleaut32.FindProc("SysStringLen") + procCreateDispTypeInfo, _ = modoleaut32.FindProc("CreateDispTypeInfo") + procCreateStdDispatch, _ = modoleaut32.FindProc("CreateStdDispatch") + procGetActiveObject, _ = modoleaut32.FindProc("GetActiveObject") + + procGetMessageW, _ = moduser32.FindProc("GetMessageW") + procDispatchMessageW, _ = moduser32.FindProc("DispatchMessageW") +) + +// coInitialize initializes COM library on current thread. +// +// MSDN documentation suggests that this function should not be called. Call +// CoInitializeEx() instead. The reason has to do with threading and this +// function is only for single-threaded apartments. +// +// That said, most users of the library have gotten away with just this +// function. If you are experiencing threading issues, then use +// CoInitializeEx(). +func coInitialize() (err error) { + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms678543(v=vs.85).aspx + // Suggests that no value should be passed to CoInitialized. + // Could just be Call() since the parameter is optional. <-- Needs testing to be sure. + hr, _, _ := procCoInitialize.Call(uintptr(0)) + if hr != 0 { + err = NewError(hr) + } + return +} + +// coInitializeEx initializes COM library with concurrency model. +func coInitializeEx(coinit uint32) (err error) { + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms695279(v=vs.85).aspx + // Suggests that the first parameter is not only optional but should always be NULL. + hr, _, _ := procCoInitializeEx.Call(uintptr(0), uintptr(coinit)) + if hr != 0 { + err = NewError(hr) + } + return +} + +// CoInitialize initializes COM library on current thread. +// +// MSDN documentation suggests that this function should not be called. Call +// CoInitializeEx() instead. The reason has to do with threading and this +// function is only for single-threaded apartments. +// +// That said, most users of the library have gotten away with just this +// function. If you are experiencing threading issues, then use +// CoInitializeEx(). +func CoInitialize(p uintptr) (err error) { + // p is ignored and won't be used. + // Avoid any variable not used errors. + p = uintptr(0) + return coInitialize() +} + +// CoInitializeEx initializes COM library with concurrency model. +func CoInitializeEx(p uintptr, coinit uint32) (err error) { + // Avoid any variable not used errors. + p = uintptr(0) + return coInitializeEx(coinit) +} + +// CoUninitialize uninitializes COM Library. +func CoUninitialize() { + procCoUninitialize.Call() +} + +// CoTaskMemFree frees memory pointer. +func CoTaskMemFree(memptr uintptr) { + procCoTaskMemFree.Call(memptr) +} + +// CLSIDFromProgID retrieves Class Identifier with the given Program Identifier. +// +// The Programmatic Identifier must be registered, because it will be looked up +// in the Windows Registry. The registry entry has the following keys: CLSID, +// Insertable, Protocol and Shell +// (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx). +// +// programID identifies the class id with less precision and is not guaranteed +// to be unique. These are usually found in the registry under +// HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of +// "Program.Component.Version" with version being optional. +// +// CLSIDFromProgID in Windows API. +func CLSIDFromProgID(progId string) (clsid *GUID, err error) { + var guid GUID + lpszProgID := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId))) + hr, _, _ := procCLSIDFromProgID.Call(lpszProgID, uintptr(unsafe.Pointer(&guid))) + if hr != 0 { + err = NewError(hr) + } + clsid = &guid + return +} + +// CLSIDFromString retrieves Class ID from string representation. +// +// This is technically the string version of the GUID and will convert the +// string to object. +// +// CLSIDFromString in Windows API. +func CLSIDFromString(str string) (clsid *GUID, err error) { + var guid GUID + lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str))) + hr, _, _ := procCLSIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid))) + if hr != 0 { + err = NewError(hr) + } + clsid = &guid + return +} + +// StringFromCLSID returns GUID formated string from GUID object. +func StringFromCLSID(clsid *GUID) (str string, err error) { + var p *uint16 + hr, _, _ := procStringFromCLSID.Call(uintptr(unsafe.Pointer(clsid)), uintptr(unsafe.Pointer(&p))) + if hr != 0 { + err = NewError(hr) + } + str = LpOleStrToString(p) + return +} + +// IIDFromString returns GUID from program ID. +func IIDFromString(progId string) (clsid *GUID, err error) { + var guid GUID + lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId))) + hr, _, _ := procIIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid))) + if hr != 0 { + err = NewError(hr) + } + clsid = &guid + return +} + +// StringFromIID returns GUID formatted string from GUID object. +func StringFromIID(iid *GUID) (str string, err error) { + var p *uint16 + hr, _, _ := procStringFromIID.Call(uintptr(unsafe.Pointer(iid)), uintptr(unsafe.Pointer(&p))) + if hr != 0 { + err = NewError(hr) + } + str = LpOleStrToString(p) + return +} + +// CreateInstance of single uninitialized object with GUID. +func CreateInstance(clsid *GUID, iid *GUID) (unk *IUnknown, err error) { + if iid == nil { + iid = IID_IUnknown + } + hr, _, _ := procCoCreateInstance.Call( + uintptr(unsafe.Pointer(clsid)), + 0, + CLSCTX_SERVER, + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&unk))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// GetActiveObject retrieves pointer to active object. +func GetActiveObject(clsid *GUID, iid *GUID) (unk *IUnknown, err error) { + if iid == nil { + iid = IID_IUnknown + } + hr, _, _ := procGetActiveObject.Call( + uintptr(unsafe.Pointer(clsid)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&unk))) + if hr != 0 { + err = NewError(hr) + } + return +} + +type BindOpts struct { + CbStruct uint32 + GrfFlags uint32 + GrfMode uint32 + TickCountDeadline uint32 +} + +// GetObject retrieves pointer to active object. +func GetObject(programID string, bindOpts *BindOpts, iid *GUID) (unk *IUnknown, err error) { + if bindOpts != nil { + bindOpts.CbStruct = uint32(unsafe.Sizeof(BindOpts{})) + } + if iid == nil { + iid = IID_IUnknown + } + hr, _, _ := procCoGetObject.Call( + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(programID))), + uintptr(unsafe.Pointer(bindOpts)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&unk))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// VariantInit initializes variant. +func VariantInit(v *VARIANT) (err error) { + hr, _, _ := procVariantInit.Call(uintptr(unsafe.Pointer(v))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// VariantClear clears value in Variant settings to VT_EMPTY. +func VariantClear(v *VARIANT) (err error) { + hr, _, _ := procVariantClear.Call(uintptr(unsafe.Pointer(v))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// SysAllocString allocates memory for string and copies string into memory. +func SysAllocString(v string) (ss *int16) { + pss, _, _ := procSysAllocString.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v)))) + ss = (*int16)(unsafe.Pointer(pss)) + return +} + +// SysAllocStringLen copies up to length of given string returning pointer. +func SysAllocStringLen(v string) (ss *int16) { + utf16 := utf16.Encode([]rune(v + "\x00")) + ptr := &utf16[0] + + pss, _, _ := procSysAllocStringLen.Call(uintptr(unsafe.Pointer(ptr)), uintptr(len(utf16)-1)) + ss = (*int16)(unsafe.Pointer(pss)) + return +} + +// SysFreeString frees string system memory. This must be called with SysAllocString. +func SysFreeString(v *int16) (err error) { + hr, _, _ := procSysFreeString.Call(uintptr(unsafe.Pointer(v))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// SysStringLen is the length of the system allocated string. +func SysStringLen(v *int16) uint32 { + l, _, _ := procSysStringLen.Call(uintptr(unsafe.Pointer(v))) + return uint32(l) +} + +// CreateStdDispatch provides default IDispatch implementation for IUnknown. +// +// This handles default IDispatch implementation for objects. It haves a few +// limitations with only supporting one language. It will also only return +// default exception codes. +func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (disp *IDispatch, err error) { + hr, _, _ := procCreateStdDispatch.Call( + uintptr(unsafe.Pointer(unk)), + v, + uintptr(unsafe.Pointer(ptinfo)), + uintptr(unsafe.Pointer(&disp))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch. +// +// This will not handle the full implementation of the interface. +func CreateDispTypeInfo(idata *INTERFACEDATA) (pptinfo *IUnknown, err error) { + hr, _, _ := procCreateDispTypeInfo.Call( + uintptr(unsafe.Pointer(idata)), + uintptr(GetUserDefaultLCID()), + uintptr(unsafe.Pointer(&pptinfo))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// copyMemory moves location of a block of memory. +func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) { + procCopyMemory.Call(uintptr(dest), uintptr(src), uintptr(length)) +} + +// GetUserDefaultLCID retrieves current user default locale. +func GetUserDefaultLCID() (lcid uint32) { + ret, _, _ := procGetUserDefaultLCID.Call() + lcid = uint32(ret) + return +} + +// GetMessage in message queue from runtime. +// +// This function appears to block. PeekMessage does not block. +func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, err error) { + r0, _, err := procGetMessageW.Call(uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(MsgFilterMin), uintptr(MsgFilterMax)) + ret = int32(r0) + return +} + +// DispatchMessage to window procedure. +func DispatchMessage(msg *Msg) (ret int32) { + r0, _, _ := procDispatchMessageW.Call(uintptr(unsafe.Pointer(msg))) + ret = int32(r0) + return +} diff --git a/vendor/github.com/go-ole/go-ole/com_func.go b/vendor/github.com/go-ole/go-ole/com_func.go new file mode 100644 index 00000000000..cef539d9ddd --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/com_func.go @@ -0,0 +1,174 @@ +// +build !windows + +package ole + +import ( + "time" + "unsafe" +) + +// coInitialize initializes COM library on current thread. +// +// MSDN documentation suggests that this function should not be called. Call +// CoInitializeEx() instead. The reason has to do with threading and this +// function is only for single-threaded apartments. +// +// That said, most users of the library have gotten away with just this +// function. If you are experiencing threading issues, then use +// CoInitializeEx(). +func coInitialize() error { + return NewError(E_NOTIMPL) +} + +// coInitializeEx initializes COM library with concurrency model. +func coInitializeEx(coinit uint32) error { + return NewError(E_NOTIMPL) +} + +// CoInitialize initializes COM library on current thread. +// +// MSDN documentation suggests that this function should not be called. Call +// CoInitializeEx() instead. The reason has to do with threading and this +// function is only for single-threaded apartments. +// +// That said, most users of the library have gotten away with just this +// function. If you are experiencing threading issues, then use +// CoInitializeEx(). +func CoInitialize(p uintptr) error { + return NewError(E_NOTIMPL) +} + +// CoInitializeEx initializes COM library with concurrency model. +func CoInitializeEx(p uintptr, coinit uint32) error { + return NewError(E_NOTIMPL) +} + +// CoUninitialize uninitializes COM Library. +func CoUninitialize() {} + +// CoTaskMemFree frees memory pointer. +func CoTaskMemFree(memptr uintptr) {} + +// CLSIDFromProgID retrieves Class Identifier with the given Program Identifier. +// +// The Programmatic Identifier must be registered, because it will be looked up +// in the Windows Registry. The registry entry has the following keys: CLSID, +// Insertable, Protocol and Shell +// (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx). +// +// programID identifies the class id with less precision and is not guaranteed +// to be unique. These are usually found in the registry under +// HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of +// "Program.Component.Version" with version being optional. +// +// CLSIDFromProgID in Windows API. +func CLSIDFromProgID(progId string) (*GUID, error) { + return nil, NewError(E_NOTIMPL) +} + +// CLSIDFromString retrieves Class ID from string representation. +// +// This is technically the string version of the GUID and will convert the +// string to object. +// +// CLSIDFromString in Windows API. +func CLSIDFromString(str string) (*GUID, error) { + return nil, NewError(E_NOTIMPL) +} + +// StringFromCLSID returns GUID formated string from GUID object. +func StringFromCLSID(clsid *GUID) (string, error) { + return "", NewError(E_NOTIMPL) +} + +// IIDFromString returns GUID from program ID. +func IIDFromString(progId string) (*GUID, error) { + return nil, NewError(E_NOTIMPL) +} + +// StringFromIID returns GUID formatted string from GUID object. +func StringFromIID(iid *GUID) (string, error) { + return "", NewError(E_NOTIMPL) +} + +// CreateInstance of single uninitialized object with GUID. +func CreateInstance(clsid *GUID, iid *GUID) (*IUnknown, error) { + return nil, NewError(E_NOTIMPL) +} + +// GetActiveObject retrieves pointer to active object. +func GetActiveObject(clsid *GUID, iid *GUID) (*IUnknown, error) { + return nil, NewError(E_NOTIMPL) +} + +// VariantInit initializes variant. +func VariantInit(v *VARIANT) error { + return NewError(E_NOTIMPL) +} + +// VariantClear clears value in Variant settings to VT_EMPTY. +func VariantClear(v *VARIANT) error { + return NewError(E_NOTIMPL) +} + +// SysAllocString allocates memory for string and copies string into memory. +func SysAllocString(v string) *int16 { + u := int16(0) + return &u +} + +// SysAllocStringLen copies up to length of given string returning pointer. +func SysAllocStringLen(v string) *int16 { + u := int16(0) + return &u +} + +// SysFreeString frees string system memory. This must be called with SysAllocString. +func SysFreeString(v *int16) error { + return NewError(E_NOTIMPL) +} + +// SysStringLen is the length of the system allocated string. +func SysStringLen(v *int16) uint32 { + return uint32(0) +} + +// CreateStdDispatch provides default IDispatch implementation for IUnknown. +// +// This handles default IDispatch implementation for objects. It haves a few +// limitations with only supporting one language. It will also only return +// default exception codes. +func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (*IDispatch, error) { + return nil, NewError(E_NOTIMPL) +} + +// CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch. +// +// This will not handle the full implementation of the interface. +func CreateDispTypeInfo(idata *INTERFACEDATA) (*IUnknown, error) { + return nil, NewError(E_NOTIMPL) +} + +// copyMemory moves location of a block of memory. +func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) {} + +// GetUserDefaultLCID retrieves current user default locale. +func GetUserDefaultLCID() uint32 { + return uint32(0) +} + +// GetMessage in message queue from runtime. +// +// This function appears to block. PeekMessage does not block. +func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (int32, error) { + return int32(0), NewError(E_NOTIMPL) +} + +// DispatchMessage to window procedure. +func DispatchMessage(msg *Msg) int32 { + return int32(0) +} + +func GetVariantDate(value uint64) (time.Time, error) { + return time.Now(), NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/connect.go b/vendor/github.com/go-ole/go-ole/connect.go new file mode 100644 index 00000000000..b2ac2ec67ac --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/connect.go @@ -0,0 +1,192 @@ +package ole + +// Connection contains IUnknown for fluent interface interaction. +// +// Deprecated. Use oleutil package instead. +type Connection struct { + Object *IUnknown // Access COM +} + +// Initialize COM. +func (*Connection) Initialize() (err error) { + return coInitialize() +} + +// Uninitialize COM. +func (*Connection) Uninitialize() { + CoUninitialize() +} + +// Create IUnknown object based first on ProgId and then from String. +func (c *Connection) Create(progId string) (err error) { + var clsid *GUID + clsid, err = CLSIDFromProgID(progId) + if err != nil { + clsid, err = CLSIDFromString(progId) + if err != nil { + return + } + } + + unknown, err := CreateInstance(clsid, IID_IUnknown) + if err != nil { + return + } + c.Object = unknown + + return +} + +// Release IUnknown object. +func (c *Connection) Release() { + c.Object.Release() +} + +// Load COM object from list of programIDs or strings. +func (c *Connection) Load(names ...string) (errors []error) { + var tempErrors []error = make([]error, len(names)) + var numErrors int = 0 + for _, name := range names { + err := c.Create(name) + if err != nil { + tempErrors = append(tempErrors, err) + numErrors += 1 + continue + } + break + } + + copy(errors, tempErrors[0:numErrors]) + return +} + +// Dispatch returns Dispatch object. +func (c *Connection) Dispatch() (object *Dispatch, err error) { + dispatch, err := c.Object.QueryInterface(IID_IDispatch) + if err != nil { + return + } + object = &Dispatch{dispatch} + return +} + +// Dispatch stores IDispatch object. +type Dispatch struct { + Object *IDispatch // Dispatch object. +} + +// Call method on IDispatch with parameters. +func (d *Dispatch) Call(method string, params ...interface{}) (result *VARIANT, err error) { + id, err := d.GetId(method) + if err != nil { + return + } + + result, err = d.Invoke(id, DISPATCH_METHOD, params) + return +} + +// MustCall method on IDispatch with parameters. +func (d *Dispatch) MustCall(method string, params ...interface{}) (result *VARIANT) { + id, err := d.GetId(method) + if err != nil { + panic(err) + } + + result, err = d.Invoke(id, DISPATCH_METHOD, params) + if err != nil { + panic(err) + } + + return +} + +// Get property on IDispatch with parameters. +func (d *Dispatch) Get(name string, params ...interface{}) (result *VARIANT, err error) { + id, err := d.GetId(name) + if err != nil { + return + } + result, err = d.Invoke(id, DISPATCH_PROPERTYGET, params) + return +} + +// MustGet property on IDispatch with parameters. +func (d *Dispatch) MustGet(name string, params ...interface{}) (result *VARIANT) { + id, err := d.GetId(name) + if err != nil { + panic(err) + } + + result, err = d.Invoke(id, DISPATCH_PROPERTYGET, params) + if err != nil { + panic(err) + } + return +} + +// Set property on IDispatch with parameters. +func (d *Dispatch) Set(name string, params ...interface{}) (result *VARIANT, err error) { + id, err := d.GetId(name) + if err != nil { + return + } + result, err = d.Invoke(id, DISPATCH_PROPERTYPUT, params) + return +} + +// MustSet property on IDispatch with parameters. +func (d *Dispatch) MustSet(name string, params ...interface{}) (result *VARIANT) { + id, err := d.GetId(name) + if err != nil { + panic(err) + } + + result, err = d.Invoke(id, DISPATCH_PROPERTYPUT, params) + if err != nil { + panic(err) + } + return +} + +// GetId retrieves ID of name on IDispatch. +func (d *Dispatch) GetId(name string) (id int32, err error) { + var dispid []int32 + dispid, err = d.Object.GetIDsOfName([]string{name}) + if err != nil { + return + } + id = dispid[0] + return +} + +// GetIds retrieves all IDs of names on IDispatch. +func (d *Dispatch) GetIds(names ...string) (dispid []int32, err error) { + dispid, err = d.Object.GetIDsOfName(names) + return +} + +// Invoke IDispatch on DisplayID of dispatch type with parameters. +// +// There have been problems where if send cascading params..., it would error +// out because the parameters would be empty. +func (d *Dispatch) Invoke(id int32, dispatch int16, params []interface{}) (result *VARIANT, err error) { + if len(params) < 1 { + result, err = d.Object.Invoke(id, dispatch) + } else { + result, err = d.Object.Invoke(id, dispatch, params...) + } + return +} + +// Release IDispatch object. +func (d *Dispatch) Release() { + d.Object.Release() +} + +// Connect initializes COM and attempts to load IUnknown based on given names. +func Connect(names ...string) (connection *Connection) { + connection.Initialize() + connection.Load(names...) + return +} diff --git a/vendor/github.com/go-ole/go-ole/constants.go b/vendor/github.com/go-ole/go-ole/constants.go new file mode 100644 index 00000000000..fd0c6d74b0e --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/constants.go @@ -0,0 +1,153 @@ +package ole + +const ( + CLSCTX_INPROC_SERVER = 1 + CLSCTX_INPROC_HANDLER = 2 + CLSCTX_LOCAL_SERVER = 4 + CLSCTX_INPROC_SERVER16 = 8 + CLSCTX_REMOTE_SERVER = 16 + CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER + CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER + CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER +) + +const ( + COINIT_APARTMENTTHREADED = 0x2 + COINIT_MULTITHREADED = 0x0 + COINIT_DISABLE_OLE1DDE = 0x4 + COINIT_SPEED_OVER_MEMORY = 0x8 +) + +const ( + DISPATCH_METHOD = 1 + DISPATCH_PROPERTYGET = 2 + DISPATCH_PROPERTYPUT = 4 + DISPATCH_PROPERTYPUTREF = 8 +) + +const ( + S_OK = 0x00000000 + E_UNEXPECTED = 0x8000FFFF + E_NOTIMPL = 0x80004001 + E_OUTOFMEMORY = 0x8007000E + E_INVALIDARG = 0x80070057 + E_NOINTERFACE = 0x80004002 + E_POINTER = 0x80004003 + E_HANDLE = 0x80070006 + E_ABORT = 0x80004004 + E_FAIL = 0x80004005 + E_ACCESSDENIED = 0x80070005 + E_PENDING = 0x8000000A + + CO_E_CLASSSTRING = 0x800401F3 +) + +const ( + CC_FASTCALL = iota + CC_CDECL + CC_MSCPASCAL + CC_PASCAL = CC_MSCPASCAL + CC_MACPASCAL + CC_STDCALL + CC_FPFASTCALL + CC_SYSCALL + CC_MPWCDECL + CC_MPWPASCAL + CC_MAX = CC_MPWPASCAL +) + +type VT uint16 + +const ( + VT_EMPTY VT = 0x0 + VT_NULL VT = 0x1 + VT_I2 VT = 0x2 + VT_I4 VT = 0x3 + VT_R4 VT = 0x4 + VT_R8 VT = 0x5 + VT_CY VT = 0x6 + VT_DATE VT = 0x7 + VT_BSTR VT = 0x8 + VT_DISPATCH VT = 0x9 + VT_ERROR VT = 0xa + VT_BOOL VT = 0xb + VT_VARIANT VT = 0xc + VT_UNKNOWN VT = 0xd + VT_DECIMAL VT = 0xe + VT_I1 VT = 0x10 + VT_UI1 VT = 0x11 + VT_UI2 VT = 0x12 + VT_UI4 VT = 0x13 + VT_I8 VT = 0x14 + VT_UI8 VT = 0x15 + VT_INT VT = 0x16 + VT_UINT VT = 0x17 + VT_VOID VT = 0x18 + VT_HRESULT VT = 0x19 + VT_PTR VT = 0x1a + VT_SAFEARRAY VT = 0x1b + VT_CARRAY VT = 0x1c + VT_USERDEFINED VT = 0x1d + VT_LPSTR VT = 0x1e + VT_LPWSTR VT = 0x1f + VT_RECORD VT = 0x24 + VT_INT_PTR VT = 0x25 + VT_UINT_PTR VT = 0x26 + VT_FILETIME VT = 0x40 + VT_BLOB VT = 0x41 + VT_STREAM VT = 0x42 + VT_STORAGE VT = 0x43 + VT_STREAMED_OBJECT VT = 0x44 + VT_STORED_OBJECT VT = 0x45 + VT_BLOB_OBJECT VT = 0x46 + VT_CF VT = 0x47 + VT_CLSID VT = 0x48 + VT_BSTR_BLOB VT = 0xfff + VT_VECTOR VT = 0x1000 + VT_ARRAY VT = 0x2000 + VT_BYREF VT = 0x4000 + VT_RESERVED VT = 0x8000 + VT_ILLEGAL VT = 0xffff + VT_ILLEGALMASKED VT = 0xfff + VT_TYPEMASK VT = 0xfff +) + +const ( + DISPID_UNKNOWN = -1 + DISPID_VALUE = 0 + DISPID_PROPERTYPUT = -3 + DISPID_NEWENUM = -4 + DISPID_EVALUATE = -5 + DISPID_CONSTRUCTOR = -6 + DISPID_DESTRUCTOR = -7 + DISPID_COLLECT = -8 +) + +const ( + TKIND_ENUM = 1 + TKIND_RECORD = 2 + TKIND_MODULE = 3 + TKIND_INTERFACE = 4 + TKIND_DISPATCH = 5 + TKIND_COCLASS = 6 + TKIND_ALIAS = 7 + TKIND_UNION = 8 + TKIND_MAX = 9 +) + +// Safe Array Feature Flags + +const ( + FADF_AUTO = 0x0001 + FADF_STATIC = 0x0002 + FADF_EMBEDDED = 0x0004 + FADF_FIXEDSIZE = 0x0010 + FADF_RECORD = 0x0020 + FADF_HAVEIID = 0x0040 + FADF_HAVEVARTYPE = 0x0080 + FADF_BSTR = 0x0100 + FADF_UNKNOWN = 0x0200 + FADF_DISPATCH = 0x0400 + FADF_VARIANT = 0x0800 + FADF_RESERVED = 0xF008 +) diff --git a/vendor/github.com/go-ole/go-ole/error.go b/vendor/github.com/go-ole/go-ole/error.go new file mode 100644 index 00000000000..096b456d3a1 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/error.go @@ -0,0 +1,51 @@ +package ole + +// OleError stores COM errors. +type OleError struct { + hr uintptr + description string + subError error +} + +// NewError creates new error with HResult. +func NewError(hr uintptr) *OleError { + return &OleError{hr: hr} +} + +// NewErrorWithDescription creates new COM error with HResult and description. +func NewErrorWithDescription(hr uintptr, description string) *OleError { + return &OleError{hr: hr, description: description} +} + +// NewErrorWithSubError creates new COM error with parent error. +func NewErrorWithSubError(hr uintptr, description string, err error) *OleError { + return &OleError{hr: hr, description: description, subError: err} +} + +// Code is the HResult. +func (v *OleError) Code() uintptr { + return uintptr(v.hr) +} + +// String description, either manually set or format message with error code. +func (v *OleError) String() string { + if v.description != "" { + return errstr(int(v.hr)) + " (" + v.description + ")" + } + return errstr(int(v.hr)) +} + +// Error implements error interface. +func (v *OleError) Error() string { + return v.String() +} + +// Description retrieves error summary, if there is one. +func (v *OleError) Description() string { + return v.description +} + +// SubError returns parent error, if there is one. +func (v *OleError) SubError() error { + return v.subError +} diff --git a/vendor/github.com/go-ole/go-ole/error_func.go b/vendor/github.com/go-ole/go-ole/error_func.go new file mode 100644 index 00000000000..8a2ffaa2724 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/error_func.go @@ -0,0 +1,8 @@ +// +build !windows + +package ole + +// errstr converts error code to string. +func errstr(errno int) string { + return "" +} diff --git a/vendor/github.com/go-ole/go-ole/error_windows.go b/vendor/github.com/go-ole/go-ole/error_windows.go new file mode 100644 index 00000000000..d0e8e68595c --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/error_windows.go @@ -0,0 +1,24 @@ +// +build windows + +package ole + +import ( + "fmt" + "syscall" + "unicode/utf16" +) + +// errstr converts error code to string. +func errstr(errno int) string { + // ask windows for the remaining errors + var flags uint32 = syscall.FORMAT_MESSAGE_FROM_SYSTEM | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS + b := make([]uint16, 300) + n, err := syscall.FormatMessage(flags, 0, uint32(errno), 0, b, nil) + if err != nil { + return fmt.Sprintf("error %d (FormatMessage failed with: %v)", errno, err) + } + // trim terminating \r and \n + for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- { + } + return string(utf16.Decode(b[:n])) +} diff --git a/vendor/github.com/go-ole/go-ole/guid.go b/vendor/github.com/go-ole/go-ole/guid.go new file mode 100644 index 00000000000..8d20f68fbf4 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/guid.go @@ -0,0 +1,284 @@ +package ole + +var ( + // IID_NULL is null Interface ID, used when no other Interface ID is known. + IID_NULL = NewGUID("{00000000-0000-0000-0000-000000000000}") + + // IID_IUnknown is for IUnknown interfaces. + IID_IUnknown = NewGUID("{00000000-0000-0000-C000-000000000046}") + + // IID_IDispatch is for IDispatch interfaces. + IID_IDispatch = NewGUID("{00020400-0000-0000-C000-000000000046}") + + // IID_IEnumVariant is for IEnumVariant interfaces + IID_IEnumVariant = NewGUID("{00020404-0000-0000-C000-000000000046}") + + // IID_IConnectionPointContainer is for IConnectionPointContainer interfaces. + IID_IConnectionPointContainer = NewGUID("{B196B284-BAB4-101A-B69C-00AA00341D07}") + + // IID_IConnectionPoint is for IConnectionPoint interfaces. + IID_IConnectionPoint = NewGUID("{B196B286-BAB4-101A-B69C-00AA00341D07}") + + // IID_IInspectable is for IInspectable interfaces. + IID_IInspectable = NewGUID("{AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90}") + + // IID_IProvideClassInfo is for IProvideClassInfo interfaces. + IID_IProvideClassInfo = NewGUID("{B196B283-BAB4-101A-B69C-00AA00341D07}") +) + +// These are for testing and not part of any library. +var ( + // IID_ICOMTestString is for ICOMTestString interfaces. + // + // {E0133EB4-C36F-469A-9D3D-C66B84BE19ED} + IID_ICOMTestString = NewGUID("{E0133EB4-C36F-469A-9D3D-C66B84BE19ED}") + + // IID_ICOMTestInt8 is for ICOMTestInt8 interfaces. + // + // {BEB06610-EB84-4155-AF58-E2BFF53680B4} + IID_ICOMTestInt8 = NewGUID("{BEB06610-EB84-4155-AF58-E2BFF53680B4}") + + // IID_ICOMTestInt16 is for ICOMTestInt16 interfaces. + // + // {DAA3F9FA-761E-4976-A860-8364CE55F6FC} + IID_ICOMTestInt16 = NewGUID("{DAA3F9FA-761E-4976-A860-8364CE55F6FC}") + + // IID_ICOMTestInt32 is for ICOMTestInt32 interfaces. + // + // {E3DEDEE7-38A2-4540-91D1-2EEF1D8891B0} + IID_ICOMTestInt32 = NewGUID("{E3DEDEE7-38A2-4540-91D1-2EEF1D8891B0}") + + // IID_ICOMTestInt64 is for ICOMTestInt64 interfaces. + // + // {8D437CBC-B3ED-485C-BC32-C336432A1623} + IID_ICOMTestInt64 = NewGUID("{8D437CBC-B3ED-485C-BC32-C336432A1623}") + + // IID_ICOMTestFloat is for ICOMTestFloat interfaces. + // + // {BF1ED004-EA02-456A-AA55-2AC8AC6B054C} + IID_ICOMTestFloat = NewGUID("{BF1ED004-EA02-456A-AA55-2AC8AC6B054C}") + + // IID_ICOMTestDouble is for ICOMTestDouble interfaces. + // + // {BF908A81-8687-4E93-999F-D86FAB284BA0} + IID_ICOMTestDouble = NewGUID("{BF908A81-8687-4E93-999F-D86FAB284BA0}") + + // IID_ICOMTestBoolean is for ICOMTestBoolean interfaces. + // + // {D530E7A6-4EE8-40D1-8931-3D63B8605010} + IID_ICOMTestBoolean = NewGUID("{D530E7A6-4EE8-40D1-8931-3D63B8605010}") + + // IID_ICOMEchoTestObject is for ICOMEchoTestObject interfaces. + // + // {6485B1EF-D780-4834-A4FE-1EBB51746CA3} + IID_ICOMEchoTestObject = NewGUID("{6485B1EF-D780-4834-A4FE-1EBB51746CA3}") + + // IID_ICOMTestTypes is for ICOMTestTypes interfaces. + // + // {CCA8D7AE-91C0-4277-A8B3-FF4EDF28D3C0} + IID_ICOMTestTypes = NewGUID("{CCA8D7AE-91C0-4277-A8B3-FF4EDF28D3C0}") + + // CLSID_COMEchoTestObject is for COMEchoTestObject class. + // + // {3C24506A-AE9E-4D50-9157-EF317281F1B0} + CLSID_COMEchoTestObject = NewGUID("{3C24506A-AE9E-4D50-9157-EF317281F1B0}") + + // CLSID_COMTestScalarClass is for COMTestScalarClass class. + // + // {865B85C5-0334-4AC6-9EF6-AACEC8FC5E86} + CLSID_COMTestScalarClass = NewGUID("{865B85C5-0334-4AC6-9EF6-AACEC8FC5E86}") +) + +const hextable = "0123456789ABCDEF" +const emptyGUID = "{00000000-0000-0000-0000-000000000000}" + +// GUID is Windows API specific GUID type. +// +// This exists to match Windows GUID type for direct passing for COM. +// Format is in xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx. +type GUID struct { + Data1 uint32 + Data2 uint16 + Data3 uint16 + Data4 [8]byte +} + +// NewGUID converts the given string into a globally unique identifier that is +// compliant with the Windows API. +// +// The supplied string may be in any of these formats: +// +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX +// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} +// +// The conversion of the supplied string is not case-sensitive. +func NewGUID(guid string) *GUID { + d := []byte(guid) + var d1, d2, d3, d4a, d4b []byte + + switch len(d) { + case 38: + if d[0] != '{' || d[37] != '}' { + return nil + } + d = d[1:37] + fallthrough + case 36: + if d[8] != '-' || d[13] != '-' || d[18] != '-' || d[23] != '-' { + return nil + } + d1 = d[0:8] + d2 = d[9:13] + d3 = d[14:18] + d4a = d[19:23] + d4b = d[24:36] + case 32: + d1 = d[0:8] + d2 = d[8:12] + d3 = d[12:16] + d4a = d[16:20] + d4b = d[20:32] + default: + return nil + } + + var g GUID + var ok1, ok2, ok3, ok4 bool + g.Data1, ok1 = decodeHexUint32(d1) + g.Data2, ok2 = decodeHexUint16(d2) + g.Data3, ok3 = decodeHexUint16(d3) + g.Data4, ok4 = decodeHexByte64(d4a, d4b) + if ok1 && ok2 && ok3 && ok4 { + return &g + } + return nil +} + +func decodeHexUint32(src []byte) (value uint32, ok bool) { + var b1, b2, b3, b4 byte + var ok1, ok2, ok3, ok4 bool + b1, ok1 = decodeHexByte(src[0], src[1]) + b2, ok2 = decodeHexByte(src[2], src[3]) + b3, ok3 = decodeHexByte(src[4], src[5]) + b4, ok4 = decodeHexByte(src[6], src[7]) + value = (uint32(b1) << 24) | (uint32(b2) << 16) | (uint32(b3) << 8) | uint32(b4) + ok = ok1 && ok2 && ok3 && ok4 + return +} + +func decodeHexUint16(src []byte) (value uint16, ok bool) { + var b1, b2 byte + var ok1, ok2 bool + b1, ok1 = decodeHexByte(src[0], src[1]) + b2, ok2 = decodeHexByte(src[2], src[3]) + value = (uint16(b1) << 8) | uint16(b2) + ok = ok1 && ok2 + return +} + +func decodeHexByte64(s1 []byte, s2 []byte) (value [8]byte, ok bool) { + var ok1, ok2, ok3, ok4, ok5, ok6, ok7, ok8 bool + value[0], ok1 = decodeHexByte(s1[0], s1[1]) + value[1], ok2 = decodeHexByte(s1[2], s1[3]) + value[2], ok3 = decodeHexByte(s2[0], s2[1]) + value[3], ok4 = decodeHexByte(s2[2], s2[3]) + value[4], ok5 = decodeHexByte(s2[4], s2[5]) + value[5], ok6 = decodeHexByte(s2[6], s2[7]) + value[6], ok7 = decodeHexByte(s2[8], s2[9]) + value[7], ok8 = decodeHexByte(s2[10], s2[11]) + ok = ok1 && ok2 && ok3 && ok4 && ok5 && ok6 && ok7 && ok8 + return +} + +func decodeHexByte(c1, c2 byte) (value byte, ok bool) { + var n1, n2 byte + var ok1, ok2 bool + n1, ok1 = decodeHexChar(c1) + n2, ok2 = decodeHexChar(c2) + value = (n1 << 4) | n2 + ok = ok1 && ok2 + return +} + +func decodeHexChar(c byte) (byte, bool) { + switch { + case '0' <= c && c <= '9': + return c - '0', true + case 'a' <= c && c <= 'f': + return c - 'a' + 10, true + case 'A' <= c && c <= 'F': + return c - 'A' + 10, true + } + + return 0, false +} + +// String converts the GUID to string form. It will adhere to this pattern: +// +// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} +// +// If the GUID is nil, the string representation of an empty GUID is returned: +// +// {00000000-0000-0000-0000-000000000000} +func (guid *GUID) String() string { + if guid == nil { + return emptyGUID + } + + var c [38]byte + c[0] = '{' + putUint32Hex(c[1:9], guid.Data1) + c[9] = '-' + putUint16Hex(c[10:14], guid.Data2) + c[14] = '-' + putUint16Hex(c[15:19], guid.Data3) + c[19] = '-' + putByteHex(c[20:24], guid.Data4[0:2]) + c[24] = '-' + putByteHex(c[25:37], guid.Data4[2:8]) + c[37] = '}' + return string(c[:]) +} + +func putUint32Hex(b []byte, v uint32) { + b[0] = hextable[byte(v>>24)>>4] + b[1] = hextable[byte(v>>24)&0x0f] + b[2] = hextable[byte(v>>16)>>4] + b[3] = hextable[byte(v>>16)&0x0f] + b[4] = hextable[byte(v>>8)>>4] + b[5] = hextable[byte(v>>8)&0x0f] + b[6] = hextable[byte(v)>>4] + b[7] = hextable[byte(v)&0x0f] +} + +func putUint16Hex(b []byte, v uint16) { + b[0] = hextable[byte(v>>8)>>4] + b[1] = hextable[byte(v>>8)&0x0f] + b[2] = hextable[byte(v)>>4] + b[3] = hextable[byte(v)&0x0f] +} + +func putByteHex(dst, src []byte) { + for i := 0; i < len(src); i++ { + dst[i*2] = hextable[src[i]>>4] + dst[i*2+1] = hextable[src[i]&0x0f] + } +} + +// IsEqualGUID compares two GUID. +// +// Not constant time comparison. +func IsEqualGUID(guid1 *GUID, guid2 *GUID) bool { + return guid1.Data1 == guid2.Data1 && + guid1.Data2 == guid2.Data2 && + guid1.Data3 == guid2.Data3 && + guid1.Data4[0] == guid2.Data4[0] && + guid1.Data4[1] == guid2.Data4[1] && + guid1.Data4[2] == guid2.Data4[2] && + guid1.Data4[3] == guid2.Data4[3] && + guid1.Data4[4] == guid2.Data4[4] && + guid1.Data4[5] == guid2.Data4[5] && + guid1.Data4[6] == guid2.Data4[6] && + guid1.Data4[7] == guid2.Data4[7] +} diff --git a/vendor/github.com/go-ole/go-ole/iconnectionpoint.go b/vendor/github.com/go-ole/go-ole/iconnectionpoint.go new file mode 100644 index 00000000000..9e6c49f41f0 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iconnectionpoint.go @@ -0,0 +1,20 @@ +package ole + +import "unsafe" + +type IConnectionPoint struct { + IUnknown +} + +type IConnectionPointVtbl struct { + IUnknownVtbl + GetConnectionInterface uintptr + GetConnectionPointContainer uintptr + Advise uintptr + Unadvise uintptr + EnumConnections uintptr +} + +func (v *IConnectionPoint) VTable() *IConnectionPointVtbl { + return (*IConnectionPointVtbl)(unsafe.Pointer(v.RawVTable)) +} diff --git a/vendor/github.com/go-ole/go-ole/iconnectionpoint_func.go b/vendor/github.com/go-ole/go-ole/iconnectionpoint_func.go new file mode 100644 index 00000000000..5414dc3cd3b --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iconnectionpoint_func.go @@ -0,0 +1,21 @@ +// +build !windows + +package ole + +import "unsafe" + +func (v *IConnectionPoint) GetConnectionInterface(piid **GUID) int32 { + return int32(0) +} + +func (v *IConnectionPoint) Advise(unknown *IUnknown) (uint32, error) { + return uint32(0), NewError(E_NOTIMPL) +} + +func (v *IConnectionPoint) Unadvise(cookie uint32) error { + return NewError(E_NOTIMPL) +} + +func (v *IConnectionPoint) EnumConnections(p *unsafe.Pointer) (err error) { + return NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/iconnectionpoint_windows.go b/vendor/github.com/go-ole/go-ole/iconnectionpoint_windows.go new file mode 100644 index 00000000000..32bc183248d --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iconnectionpoint_windows.go @@ -0,0 +1,43 @@ +// +build windows + +package ole + +import ( + "syscall" + "unsafe" +) + +func (v *IConnectionPoint) GetConnectionInterface(piid **GUID) int32 { + // XXX: This doesn't look like it does what it's supposed to + return release((*IUnknown)(unsafe.Pointer(v))) +} + +func (v *IConnectionPoint) Advise(unknown *IUnknown) (cookie uint32, err error) { + hr, _, _ := syscall.Syscall( + v.VTable().Advise, + 3, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(unknown)), + uintptr(unsafe.Pointer(&cookie))) + if hr != 0 { + err = NewError(hr) + } + return +} + +func (v *IConnectionPoint) Unadvise(cookie uint32) (err error) { + hr, _, _ := syscall.Syscall( + v.VTable().Unadvise, + 2, + uintptr(unsafe.Pointer(v)), + uintptr(cookie), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} + +func (v *IConnectionPoint) EnumConnections(p *unsafe.Pointer) error { + return NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer.go b/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer.go new file mode 100644 index 00000000000..165860d199e --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer.go @@ -0,0 +1,17 @@ +package ole + +import "unsafe" + +type IConnectionPointContainer struct { + IUnknown +} + +type IConnectionPointContainerVtbl struct { + IUnknownVtbl + EnumConnectionPoints uintptr + FindConnectionPoint uintptr +} + +func (v *IConnectionPointContainer) VTable() *IConnectionPointContainerVtbl { + return (*IConnectionPointContainerVtbl)(unsafe.Pointer(v.RawVTable)) +} diff --git a/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_func.go b/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_func.go new file mode 100644 index 00000000000..5dfa42aaebb --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_func.go @@ -0,0 +1,11 @@ +// +build !windows + +package ole + +func (v *IConnectionPointContainer) EnumConnectionPoints(points interface{}) error { + return NewError(E_NOTIMPL) +} + +func (v *IConnectionPointContainer) FindConnectionPoint(iid *GUID, point **IConnectionPoint) error { + return NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_windows.go b/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_windows.go new file mode 100644 index 00000000000..ad30d79efc4 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_windows.go @@ -0,0 +1,25 @@ +// +build windows + +package ole + +import ( + "syscall" + "unsafe" +) + +func (v *IConnectionPointContainer) EnumConnectionPoints(points interface{}) error { + return NewError(E_NOTIMPL) +} + +func (v *IConnectionPointContainer) FindConnectionPoint(iid *GUID, point **IConnectionPoint) (err error) { + hr, _, _ := syscall.Syscall( + v.VTable().FindConnectionPoint, + 3, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(point))) + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/vendor/github.com/go-ole/go-ole/idispatch.go b/vendor/github.com/go-ole/go-ole/idispatch.go new file mode 100644 index 00000000000..d4af1240925 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/idispatch.go @@ -0,0 +1,94 @@ +package ole + +import "unsafe" + +type IDispatch struct { + IUnknown +} + +type IDispatchVtbl struct { + IUnknownVtbl + GetTypeInfoCount uintptr + GetTypeInfo uintptr + GetIDsOfNames uintptr + Invoke uintptr +} + +func (v *IDispatch) VTable() *IDispatchVtbl { + return (*IDispatchVtbl)(unsafe.Pointer(v.RawVTable)) +} + +func (v *IDispatch) GetIDsOfName(names []string) (dispid []int32, err error) { + dispid, err = getIDsOfName(v, names) + return +} + +func (v *IDispatch) Invoke(dispid int32, dispatch int16, params ...interface{}) (result *VARIANT, err error) { + result, err = invoke(v, dispid, dispatch, params...) + return +} + +func (v *IDispatch) GetTypeInfoCount() (c uint32, err error) { + c, err = getTypeInfoCount(v) + return +} + +func (v *IDispatch) GetTypeInfo() (tinfo *ITypeInfo, err error) { + tinfo, err = getTypeInfo(v) + return +} + +// GetSingleIDOfName is a helper that returns single display ID for IDispatch name. +// +// This replaces the common pattern of attempting to get a single name from the list of available +// IDs. It gives the first ID, if it is available. +func (v *IDispatch) GetSingleIDOfName(name string) (displayID int32, err error) { + var displayIDs []int32 + displayIDs, err = v.GetIDsOfName([]string{name}) + if err != nil { + return + } + displayID = displayIDs[0] + return +} + +// InvokeWithOptionalArgs accepts arguments as an array, works like Invoke. +// +// Accepts name and will attempt to retrieve Display ID to pass to Invoke. +// +// Passing params as an array is a workaround that could be fixed in later versions of Go that +// prevent passing empty params. During testing it was discovered that this is an acceptable way of +// getting around not being able to pass params normally. +func (v *IDispatch) InvokeWithOptionalArgs(name string, dispatch int16, params []interface{}) (result *VARIANT, err error) { + displayID, err := v.GetSingleIDOfName(name) + if err != nil { + return + } + + if len(params) < 1 { + result, err = v.Invoke(displayID, dispatch) + } else { + result, err = v.Invoke(displayID, dispatch, params...) + } + + return +} + +// CallMethod invokes named function with arguments on object. +func (v *IDispatch) CallMethod(name string, params ...interface{}) (*VARIANT, error) { + return v.InvokeWithOptionalArgs(name, DISPATCH_METHOD, params) +} + +// GetProperty retrieves the property with the name with the ability to pass arguments. +// +// Most of the time you will not need to pass arguments as most objects do not allow for this +// feature. Or at least, should not allow for this feature. Some servers don't follow best practices +// and this is provided for those edge cases. +func (v *IDispatch) GetProperty(name string, params ...interface{}) (*VARIANT, error) { + return v.InvokeWithOptionalArgs(name, DISPATCH_PROPERTYGET, params) +} + +// PutProperty attempts to mutate a property in the object. +func (v *IDispatch) PutProperty(name string, params ...interface{}) (*VARIANT, error) { + return v.InvokeWithOptionalArgs(name, DISPATCH_PROPERTYPUT, params) +} diff --git a/vendor/github.com/go-ole/go-ole/idispatch_func.go b/vendor/github.com/go-ole/go-ole/idispatch_func.go new file mode 100644 index 00000000000..b8fbbe319f1 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/idispatch_func.go @@ -0,0 +1,19 @@ +// +build !windows + +package ole + +func getIDsOfName(disp *IDispatch, names []string) ([]int32, error) { + return []int32{}, NewError(E_NOTIMPL) +} + +func getTypeInfoCount(disp *IDispatch) (uint32, error) { + return uint32(0), NewError(E_NOTIMPL) +} + +func getTypeInfo(disp *IDispatch) (*ITypeInfo, error) { + return nil, NewError(E_NOTIMPL) +} + +func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (*VARIANT, error) { + return nil, NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/idispatch_windows.go b/vendor/github.com/go-ole/go-ole/idispatch_windows.go new file mode 100644 index 00000000000..6ec180b55fd --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/idispatch_windows.go @@ -0,0 +1,200 @@ +// +build windows + +package ole + +import ( + "math/big" + "syscall" + "time" + "unsafe" +) + +func getIDsOfName(disp *IDispatch, names []string) (dispid []int32, err error) { + wnames := make([]*uint16, len(names)) + for i := 0; i < len(names); i++ { + wnames[i] = syscall.StringToUTF16Ptr(names[i]) + } + dispid = make([]int32, len(names)) + namelen := uint32(len(names)) + hr, _, _ := syscall.Syscall6( + disp.VTable().GetIDsOfNames, + 6, + uintptr(unsafe.Pointer(disp)), + uintptr(unsafe.Pointer(IID_NULL)), + uintptr(unsafe.Pointer(&wnames[0])), + uintptr(namelen), + uintptr(GetUserDefaultLCID()), + uintptr(unsafe.Pointer(&dispid[0]))) + if hr != 0 { + err = NewError(hr) + } + return +} + +func getTypeInfoCount(disp *IDispatch) (c uint32, err error) { + hr, _, _ := syscall.Syscall( + disp.VTable().GetTypeInfoCount, + 2, + uintptr(unsafe.Pointer(disp)), + uintptr(unsafe.Pointer(&c)), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} + +func getTypeInfo(disp *IDispatch) (tinfo *ITypeInfo, err error) { + hr, _, _ := syscall.Syscall( + disp.VTable().GetTypeInfo, + 3, + uintptr(unsafe.Pointer(disp)), + uintptr(GetUserDefaultLCID()), + uintptr(unsafe.Pointer(&tinfo))) + if hr != 0 { + err = NewError(hr) + } + return +} + +func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (result *VARIANT, err error) { + var dispparams DISPPARAMS + + if dispatch&DISPATCH_PROPERTYPUT != 0 { + dispnames := [1]int32{DISPID_PROPERTYPUT} + dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0])) + dispparams.cNamedArgs = 1 + } else if dispatch&DISPATCH_PROPERTYPUTREF != 0 { + dispnames := [1]int32{DISPID_PROPERTYPUT} + dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0])) + dispparams.cNamedArgs = 1 + } + var vargs []VARIANT + if len(params) > 0 { + vargs = make([]VARIANT, len(params)) + for i, v := range params { + //n := len(params)-i-1 + n := len(params) - i - 1 + VariantInit(&vargs[n]) + switch vv := v.(type) { + case bool: + if vv { + vargs[n] = NewVariant(VT_BOOL, 0xffff) + } else { + vargs[n] = NewVariant(VT_BOOL, 0) + } + case *bool: + vargs[n] = NewVariant(VT_BOOL|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*bool))))) + case uint8: + vargs[n] = NewVariant(VT_I1, int64(v.(uint8))) + case *uint8: + vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint8))))) + case int8: + vargs[n] = NewVariant(VT_I1, int64(v.(int8))) + case *int8: + vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint8))))) + case int16: + vargs[n] = NewVariant(VT_I2, int64(v.(int16))) + case *int16: + vargs[n] = NewVariant(VT_I2|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int16))))) + case uint16: + vargs[n] = NewVariant(VT_UI2, int64(v.(uint16))) + case *uint16: + vargs[n] = NewVariant(VT_UI2|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint16))))) + case int32: + vargs[n] = NewVariant(VT_I4, int64(v.(int32))) + case *int32: + vargs[n] = NewVariant(VT_I4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int32))))) + case uint32: + vargs[n] = NewVariant(VT_UI4, int64(v.(uint32))) + case *uint32: + vargs[n] = NewVariant(VT_UI4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint32))))) + case int64: + vargs[n] = NewVariant(VT_I8, int64(v.(int64))) + case *int64: + vargs[n] = NewVariant(VT_I8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int64))))) + case uint64: + vargs[n] = NewVariant(VT_UI8, int64(uintptr(v.(uint64)))) + case *uint64: + vargs[n] = NewVariant(VT_UI8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint64))))) + case int: + vargs[n] = NewVariant(VT_I4, int64(v.(int))) + case *int: + vargs[n] = NewVariant(VT_I4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int))))) + case uint: + vargs[n] = NewVariant(VT_UI4, int64(v.(uint))) + case *uint: + vargs[n] = NewVariant(VT_UI4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint))))) + case float32: + vargs[n] = NewVariant(VT_R4, *(*int64)(unsafe.Pointer(&vv))) + case *float32: + vargs[n] = NewVariant(VT_R4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float32))))) + case float64: + vargs[n] = NewVariant(VT_R8, *(*int64)(unsafe.Pointer(&vv))) + case *float64: + vargs[n] = NewVariant(VT_R8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float64))))) + case *big.Int: + vargs[n] = NewVariant(VT_DECIMAL, v.(*big.Int).Int64()) + case string: + vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(SysAllocStringLen(v.(string)))))) + case *string: + vargs[n] = NewVariant(VT_BSTR|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*string))))) + case time.Time: + s := vv.Format("2006-01-02 15:04:05") + vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(SysAllocStringLen(s))))) + case *time.Time: + s := vv.Format("2006-01-02 15:04:05") + vargs[n] = NewVariant(VT_BSTR|VT_BYREF, int64(uintptr(unsafe.Pointer(&s)))) + case *IDispatch: + vargs[n] = NewVariant(VT_DISPATCH, int64(uintptr(unsafe.Pointer(v.(*IDispatch))))) + case **IDispatch: + vargs[n] = NewVariant(VT_DISPATCH|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(**IDispatch))))) + case nil: + vargs[n] = NewVariant(VT_NULL, 0) + case *VARIANT: + vargs[n] = NewVariant(VT_VARIANT|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*VARIANT))))) + case []byte: + safeByteArray := safeArrayFromByteSlice(v.([]byte)) + vargs[n] = NewVariant(VT_ARRAY|VT_UI1, int64(uintptr(unsafe.Pointer(safeByteArray)))) + defer VariantClear(&vargs[n]) + case []string: + safeByteArray := safeArrayFromStringSlice(v.([]string)) + vargs[n] = NewVariant(VT_ARRAY|VT_BSTR, int64(uintptr(unsafe.Pointer(safeByteArray)))) + defer VariantClear(&vargs[n]) + default: + panic("unknown type") + } + } + dispparams.rgvarg = uintptr(unsafe.Pointer(&vargs[0])) + dispparams.cArgs = uint32(len(params)) + } + + result = new(VARIANT) + var excepInfo EXCEPINFO + VariantInit(result) + hr, _, _ := syscall.Syscall9( + disp.VTable().Invoke, + 9, + uintptr(unsafe.Pointer(disp)), + uintptr(dispid), + uintptr(unsafe.Pointer(IID_NULL)), + uintptr(GetUserDefaultLCID()), + uintptr(dispatch), + uintptr(unsafe.Pointer(&dispparams)), + uintptr(unsafe.Pointer(result)), + uintptr(unsafe.Pointer(&excepInfo)), + 0) + if hr != 0 { + err = NewErrorWithSubError(hr, BstrToString(excepInfo.bstrDescription), excepInfo) + } + for i, varg := range vargs { + n := len(params) - i - 1 + if varg.VT == VT_BSTR && varg.Val != 0 { + SysFreeString(((*int16)(unsafe.Pointer(uintptr(varg.Val))))) + } + if varg.VT == (VT_BSTR|VT_BYREF) && varg.Val != 0 { + *(params[n].(*string)) = LpOleStrToString(*(**uint16)(unsafe.Pointer(uintptr(varg.Val)))) + } + } + return +} diff --git a/vendor/github.com/go-ole/go-ole/ienumvariant.go b/vendor/github.com/go-ole/go-ole/ienumvariant.go new file mode 100644 index 00000000000..24338975443 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/ienumvariant.go @@ -0,0 +1,19 @@ +package ole + +import "unsafe" + +type IEnumVARIANT struct { + IUnknown +} + +type IEnumVARIANTVtbl struct { + IUnknownVtbl + Next uintptr + Skip uintptr + Reset uintptr + Clone uintptr +} + +func (v *IEnumVARIANT) VTable() *IEnumVARIANTVtbl { + return (*IEnumVARIANTVtbl)(unsafe.Pointer(v.RawVTable)) +} diff --git a/vendor/github.com/go-ole/go-ole/ienumvariant_func.go b/vendor/github.com/go-ole/go-ole/ienumvariant_func.go new file mode 100644 index 00000000000..c14848199cb --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/ienumvariant_func.go @@ -0,0 +1,19 @@ +// +build !windows + +package ole + +func (enum *IEnumVARIANT) Clone() (*IEnumVARIANT, error) { + return nil, NewError(E_NOTIMPL) +} + +func (enum *IEnumVARIANT) Reset() error { + return NewError(E_NOTIMPL) +} + +func (enum *IEnumVARIANT) Skip(celt uint) error { + return NewError(E_NOTIMPL) +} + +func (enum *IEnumVARIANT) Next(celt uint) (VARIANT, uint, error) { + return NewVariant(VT_NULL, int64(0)), 0, NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/ienumvariant_windows.go b/vendor/github.com/go-ole/go-ole/ienumvariant_windows.go new file mode 100644 index 00000000000..4781f3b8b00 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/ienumvariant_windows.go @@ -0,0 +1,63 @@ +// +build windows + +package ole + +import ( + "syscall" + "unsafe" +) + +func (enum *IEnumVARIANT) Clone() (cloned *IEnumVARIANT, err error) { + hr, _, _ := syscall.Syscall( + enum.VTable().Clone, + 2, + uintptr(unsafe.Pointer(enum)), + uintptr(unsafe.Pointer(&cloned)), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} + +func (enum *IEnumVARIANT) Reset() (err error) { + hr, _, _ := syscall.Syscall( + enum.VTable().Reset, + 1, + uintptr(unsafe.Pointer(enum)), + 0, + 0) + if hr != 0 { + err = NewError(hr) + } + return +} + +func (enum *IEnumVARIANT) Skip(celt uint) (err error) { + hr, _, _ := syscall.Syscall( + enum.VTable().Skip, + 2, + uintptr(unsafe.Pointer(enum)), + uintptr(celt), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} + +func (enum *IEnumVARIANT) Next(celt uint) (array VARIANT, length uint, err error) { + hr, _, _ := syscall.Syscall6( + enum.VTable().Next, + 4, + uintptr(unsafe.Pointer(enum)), + uintptr(celt), + uintptr(unsafe.Pointer(&array)), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/vendor/github.com/go-ole/go-ole/iinspectable.go b/vendor/github.com/go-ole/go-ole/iinspectable.go new file mode 100644 index 00000000000..f4a19e253af --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iinspectable.go @@ -0,0 +1,18 @@ +package ole + +import "unsafe" + +type IInspectable struct { + IUnknown +} + +type IInspectableVtbl struct { + IUnknownVtbl + GetIIds uintptr + GetRuntimeClassName uintptr + GetTrustLevel uintptr +} + +func (v *IInspectable) VTable() *IInspectableVtbl { + return (*IInspectableVtbl)(unsafe.Pointer(v.RawVTable)) +} diff --git a/vendor/github.com/go-ole/go-ole/iinspectable_func.go b/vendor/github.com/go-ole/go-ole/iinspectable_func.go new file mode 100644 index 00000000000..348829bf062 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iinspectable_func.go @@ -0,0 +1,15 @@ +// +build !windows + +package ole + +func (v *IInspectable) GetIids() ([]*GUID, error) { + return []*GUID{}, NewError(E_NOTIMPL) +} + +func (v *IInspectable) GetRuntimeClassName() (string, error) { + return "", NewError(E_NOTIMPL) +} + +func (v *IInspectable) GetTrustLevel() (uint32, error) { + return uint32(0), NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/iinspectable_windows.go b/vendor/github.com/go-ole/go-ole/iinspectable_windows.go new file mode 100644 index 00000000000..4519a4aa449 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iinspectable_windows.go @@ -0,0 +1,72 @@ +// +build windows + +package ole + +import ( + "bytes" + "encoding/binary" + "reflect" + "syscall" + "unsafe" +) + +func (v *IInspectable) GetIids() (iids []*GUID, err error) { + var count uint32 + var array uintptr + hr, _, _ := syscall.Syscall( + v.VTable().GetIIds, + 3, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(&count)), + uintptr(unsafe.Pointer(&array))) + if hr != 0 { + err = NewError(hr) + return + } + defer CoTaskMemFree(array) + + iids = make([]*GUID, count) + byteCount := count * uint32(unsafe.Sizeof(GUID{})) + slicehdr := reflect.SliceHeader{Data: array, Len: int(byteCount), Cap: int(byteCount)} + byteSlice := *(*[]byte)(unsafe.Pointer(&slicehdr)) + reader := bytes.NewReader(byteSlice) + for i := range iids { + guid := GUID{} + err = binary.Read(reader, binary.LittleEndian, &guid) + if err != nil { + return + } + iids[i] = &guid + } + return +} + +func (v *IInspectable) GetRuntimeClassName() (s string, err error) { + var hstring HString + hr, _, _ := syscall.Syscall( + v.VTable().GetRuntimeClassName, + 2, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(&hstring)), + 0) + if hr != 0 { + err = NewError(hr) + return + } + s = hstring.String() + DeleteHString(hstring) + return +} + +func (v *IInspectable) GetTrustLevel() (level uint32, err error) { + hr, _, _ := syscall.Syscall( + v.VTable().GetTrustLevel, + 2, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(&level)), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/vendor/github.com/go-ole/go-ole/iprovideclassinfo.go b/vendor/github.com/go-ole/go-ole/iprovideclassinfo.go new file mode 100644 index 00000000000..25f3a6f24a9 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iprovideclassinfo.go @@ -0,0 +1,21 @@ +package ole + +import "unsafe" + +type IProvideClassInfo struct { + IUnknown +} + +type IProvideClassInfoVtbl struct { + IUnknownVtbl + GetClassInfo uintptr +} + +func (v *IProvideClassInfo) VTable() *IProvideClassInfoVtbl { + return (*IProvideClassInfoVtbl)(unsafe.Pointer(v.RawVTable)) +} + +func (v *IProvideClassInfo) GetClassInfo() (cinfo *ITypeInfo, err error) { + cinfo, err = getClassInfo(v) + return +} diff --git a/vendor/github.com/go-ole/go-ole/iprovideclassinfo_func.go b/vendor/github.com/go-ole/go-ole/iprovideclassinfo_func.go new file mode 100644 index 00000000000..7e3cb63ea73 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iprovideclassinfo_func.go @@ -0,0 +1,7 @@ +// +build !windows + +package ole + +func getClassInfo(disp *IProvideClassInfo) (tinfo *ITypeInfo, err error) { + return nil, NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/iprovideclassinfo_windows.go b/vendor/github.com/go-ole/go-ole/iprovideclassinfo_windows.go new file mode 100644 index 00000000000..2ad01639497 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iprovideclassinfo_windows.go @@ -0,0 +1,21 @@ +// +build windows + +package ole + +import ( + "syscall" + "unsafe" +) + +func getClassInfo(disp *IProvideClassInfo) (tinfo *ITypeInfo, err error) { + hr, _, _ := syscall.Syscall( + disp.VTable().GetClassInfo, + 2, + uintptr(unsafe.Pointer(disp)), + uintptr(unsafe.Pointer(&tinfo)), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/vendor/github.com/go-ole/go-ole/itypeinfo.go b/vendor/github.com/go-ole/go-ole/itypeinfo.go new file mode 100644 index 00000000000..dd3c5e21bbf --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/itypeinfo.go @@ -0,0 +1,34 @@ +package ole + +import "unsafe" + +type ITypeInfo struct { + IUnknown +} + +type ITypeInfoVtbl struct { + IUnknownVtbl + GetTypeAttr uintptr + GetTypeComp uintptr + GetFuncDesc uintptr + GetVarDesc uintptr + GetNames uintptr + GetRefTypeOfImplType uintptr + GetImplTypeFlags uintptr + GetIDsOfNames uintptr + Invoke uintptr + GetDocumentation uintptr + GetDllEntry uintptr + GetRefTypeInfo uintptr + AddressOfMember uintptr + CreateInstance uintptr + GetMops uintptr + GetContainingTypeLib uintptr + ReleaseTypeAttr uintptr + ReleaseFuncDesc uintptr + ReleaseVarDesc uintptr +} + +func (v *ITypeInfo) VTable() *ITypeInfoVtbl { + return (*ITypeInfoVtbl)(unsafe.Pointer(v.RawVTable)) +} diff --git a/vendor/github.com/go-ole/go-ole/itypeinfo_func.go b/vendor/github.com/go-ole/go-ole/itypeinfo_func.go new file mode 100644 index 00000000000..8364a659bae --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/itypeinfo_func.go @@ -0,0 +1,7 @@ +// +build !windows + +package ole + +func (v *ITypeInfo) GetTypeAttr() (*TYPEATTR, error) { + return nil, NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/itypeinfo_windows.go b/vendor/github.com/go-ole/go-ole/itypeinfo_windows.go new file mode 100644 index 00000000000..54782b3da5d --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/itypeinfo_windows.go @@ -0,0 +1,21 @@ +// +build windows + +package ole + +import ( + "syscall" + "unsafe" +) + +func (v *ITypeInfo) GetTypeAttr() (tattr *TYPEATTR, err error) { + hr, _, _ := syscall.Syscall( + uintptr(v.VTable().GetTypeAttr), + 2, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(&tattr)), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/vendor/github.com/go-ole/go-ole/iunknown.go b/vendor/github.com/go-ole/go-ole/iunknown.go new file mode 100644 index 00000000000..108f28ea610 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iunknown.go @@ -0,0 +1,57 @@ +package ole + +import "unsafe" + +type IUnknown struct { + RawVTable *interface{} +} + +type IUnknownVtbl struct { + QueryInterface uintptr + AddRef uintptr + Release uintptr +} + +type UnknownLike interface { + QueryInterface(iid *GUID) (disp *IDispatch, err error) + AddRef() int32 + Release() int32 +} + +func (v *IUnknown) VTable() *IUnknownVtbl { + return (*IUnknownVtbl)(unsafe.Pointer(v.RawVTable)) +} + +func (v *IUnknown) PutQueryInterface(interfaceID *GUID, obj interface{}) error { + return reflectQueryInterface(v, v.VTable().QueryInterface, interfaceID, obj) +} + +func (v *IUnknown) IDispatch(interfaceID *GUID) (dispatch *IDispatch, err error) { + err = v.PutQueryInterface(interfaceID, &dispatch) + return +} + +func (v *IUnknown) IEnumVARIANT(interfaceID *GUID) (enum *IEnumVARIANT, err error) { + err = v.PutQueryInterface(interfaceID, &enum) + return +} + +func (v *IUnknown) QueryInterface(iid *GUID) (*IDispatch, error) { + return queryInterface(v, iid) +} + +func (v *IUnknown) MustQueryInterface(iid *GUID) (disp *IDispatch) { + unk, err := queryInterface(v, iid) + if err != nil { + panic(err) + } + return unk +} + +func (v *IUnknown) AddRef() int32 { + return addRef(v) +} + +func (v *IUnknown) Release() int32 { + return release(v) +} diff --git a/vendor/github.com/go-ole/go-ole/iunknown_func.go b/vendor/github.com/go-ole/go-ole/iunknown_func.go new file mode 100644 index 00000000000..d0a62cfd730 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iunknown_func.go @@ -0,0 +1,19 @@ +// +build !windows + +package ole + +func reflectQueryInterface(self interface{}, method uintptr, interfaceID *GUID, obj interface{}) (err error) { + return NewError(E_NOTIMPL) +} + +func queryInterface(unk *IUnknown, iid *GUID) (disp *IDispatch, err error) { + return nil, NewError(E_NOTIMPL) +} + +func addRef(unk *IUnknown) int32 { + return 0 +} + +func release(unk *IUnknown) int32 { + return 0 +} diff --git a/vendor/github.com/go-ole/go-ole/iunknown_windows.go b/vendor/github.com/go-ole/go-ole/iunknown_windows.go new file mode 100644 index 00000000000..ede5bb8c173 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iunknown_windows.go @@ -0,0 +1,58 @@ +// +build windows + +package ole + +import ( + "reflect" + "syscall" + "unsafe" +) + +func reflectQueryInterface(self interface{}, method uintptr, interfaceID *GUID, obj interface{}) (err error) { + selfValue := reflect.ValueOf(self).Elem() + objValue := reflect.ValueOf(obj).Elem() + + hr, _, _ := syscall.Syscall( + method, + 3, + selfValue.UnsafeAddr(), + uintptr(unsafe.Pointer(interfaceID)), + objValue.Addr().Pointer()) + if hr != 0 { + err = NewError(hr) + } + return +} + +func queryInterface(unk *IUnknown, iid *GUID) (disp *IDispatch, err error) { + hr, _, _ := syscall.Syscall( + unk.VTable().QueryInterface, + 3, + uintptr(unsafe.Pointer(unk)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&disp))) + if hr != 0 { + err = NewError(hr) + } + return +} + +func addRef(unk *IUnknown) int32 { + ret, _, _ := syscall.Syscall( + unk.VTable().AddRef, + 1, + uintptr(unsafe.Pointer(unk)), + 0, + 0) + return int32(ret) +} + +func release(unk *IUnknown) int32 { + ret, _, _ := syscall.Syscall( + unk.VTable().Release, + 1, + uintptr(unsafe.Pointer(unk)), + 0, + 0) + return int32(ret) +} diff --git a/vendor/github.com/go-ole/go-ole/ole.go b/vendor/github.com/go-ole/go-ole/ole.go new file mode 100644 index 00000000000..e2ae4f4bbfe --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/ole.go @@ -0,0 +1,157 @@ +package ole + +import ( + "fmt" + "strings" +) + +// DISPPARAMS are the arguments that passed to methods or property. +type DISPPARAMS struct { + rgvarg uintptr + rgdispidNamedArgs uintptr + cArgs uint32 + cNamedArgs uint32 +} + +// EXCEPINFO defines exception info. +type EXCEPINFO struct { + wCode uint16 + wReserved uint16 + bstrSource *uint16 + bstrDescription *uint16 + bstrHelpFile *uint16 + dwHelpContext uint32 + pvReserved uintptr + pfnDeferredFillIn uintptr + scode uint32 +} + +// WCode return wCode in EXCEPINFO. +func (e EXCEPINFO) WCode() uint16 { + return e.wCode +} + +// SCODE return scode in EXCEPINFO. +func (e EXCEPINFO) SCODE() uint32 { + return e.scode +} + +// String convert EXCEPINFO to string. +func (e EXCEPINFO) String() string { + var src, desc, hlp string + if e.bstrSource == nil { + src = "" + } else { + src = BstrToString(e.bstrSource) + } + + if e.bstrDescription == nil { + desc = "" + } else { + desc = BstrToString(e.bstrDescription) + } + + if e.bstrHelpFile == nil { + hlp = "" + } else { + hlp = BstrToString(e.bstrHelpFile) + } + + return fmt.Sprintf( + "wCode: %#x, bstrSource: %v, bstrDescription: %v, bstrHelpFile: %v, dwHelpContext: %#x, scode: %#x", + e.wCode, src, desc, hlp, e.dwHelpContext, e.scode, + ) +} + +// Error implements error interface and returns error string. +func (e EXCEPINFO) Error() string { + if e.bstrDescription != nil { + return strings.TrimSpace(BstrToString(e.bstrDescription)) + } + + src := "Unknown" + if e.bstrSource != nil { + src = BstrToString(e.bstrSource) + } + + code := e.scode + if e.wCode != 0 { + code = uint32(e.wCode) + } + + return fmt.Sprintf("%v: %#x", src, code) +} + +// PARAMDATA defines parameter data type. +type PARAMDATA struct { + Name *int16 + Vt uint16 +} + +// METHODDATA defines method info. +type METHODDATA struct { + Name *uint16 + Data *PARAMDATA + Dispid int32 + Meth uint32 + CC int32 + CArgs uint32 + Flags uint16 + VtReturn uint32 +} + +// INTERFACEDATA defines interface info. +type INTERFACEDATA struct { + MethodData *METHODDATA + CMembers uint32 +} + +// Point is 2D vector type. +type Point struct { + X int32 + Y int32 +} + +// Msg is message between processes. +type Msg struct { + Hwnd uint32 + Message uint32 + Wparam int32 + Lparam int32 + Time uint32 + Pt Point +} + +// TYPEDESC defines data type. +type TYPEDESC struct { + Hreftype uint32 + VT uint16 +} + +// IDLDESC defines IDL info. +type IDLDESC struct { + DwReserved uint32 + WIDLFlags uint16 +} + +// TYPEATTR defines type info. +type TYPEATTR struct { + Guid GUID + Lcid uint32 + dwReserved uint32 + MemidConstructor int32 + MemidDestructor int32 + LpstrSchema *uint16 + CbSizeInstance uint32 + Typekind int32 + CFuncs uint16 + CVars uint16 + CImplTypes uint16 + CbSizeVft uint16 + CbAlignment uint16 + WTypeFlags uint16 + WMajorVerNum uint16 + WMinorVerNum uint16 + TdescAlias TYPEDESC + IdldescType IDLDESC +} diff --git a/vendor/github.com/go-ole/go-ole/oleutil/connection.go b/vendor/github.com/go-ole/go-ole/oleutil/connection.go new file mode 100644 index 00000000000..60df73cda00 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/oleutil/connection.go @@ -0,0 +1,100 @@ +// +build windows + +package oleutil + +import ( + "reflect" + "unsafe" + + ole "github.com/go-ole/go-ole" +) + +type stdDispatch struct { + lpVtbl *stdDispatchVtbl + ref int32 + iid *ole.GUID + iface interface{} + funcMap map[string]int32 +} + +type stdDispatchVtbl struct { + pQueryInterface uintptr + pAddRef uintptr + pRelease uintptr + pGetTypeInfoCount uintptr + pGetTypeInfo uintptr + pGetIDsOfNames uintptr + pInvoke uintptr +} + +func dispQueryInterface(this *ole.IUnknown, iid *ole.GUID, punk **ole.IUnknown) uint32 { + pthis := (*stdDispatch)(unsafe.Pointer(this)) + *punk = nil + if ole.IsEqualGUID(iid, ole.IID_IUnknown) || + ole.IsEqualGUID(iid, ole.IID_IDispatch) { + dispAddRef(this) + *punk = this + return ole.S_OK + } + if ole.IsEqualGUID(iid, pthis.iid) { + dispAddRef(this) + *punk = this + return ole.S_OK + } + return ole.E_NOINTERFACE +} + +func dispAddRef(this *ole.IUnknown) int32 { + pthis := (*stdDispatch)(unsafe.Pointer(this)) + pthis.ref++ + return pthis.ref +} + +func dispRelease(this *ole.IUnknown) int32 { + pthis := (*stdDispatch)(unsafe.Pointer(this)) + pthis.ref-- + return pthis.ref +} + +func dispGetIDsOfNames(this *ole.IUnknown, iid *ole.GUID, wnames []*uint16, namelen int, lcid int, pdisp []int32) uintptr { + pthis := (*stdDispatch)(unsafe.Pointer(this)) + names := make([]string, len(wnames)) + for i := 0; i < len(names); i++ { + names[i] = ole.LpOleStrToString(wnames[i]) + } + for n := 0; n < namelen; n++ { + if id, ok := pthis.funcMap[names[n]]; ok { + pdisp[n] = id + } + } + return ole.S_OK +} + +func dispGetTypeInfoCount(pcount *int) uintptr { + if pcount != nil { + *pcount = 0 + } + return ole.S_OK +} + +func dispGetTypeInfo(ptypeif *uintptr) uintptr { + return ole.E_NOTIMPL +} + +func dispInvoke(this *ole.IDispatch, dispid int32, riid *ole.GUID, lcid int, flags int16, dispparams *ole.DISPPARAMS, result *ole.VARIANT, pexcepinfo *ole.EXCEPINFO, nerr *uint) uintptr { + pthis := (*stdDispatch)(unsafe.Pointer(this)) + found := "" + for name, id := range pthis.funcMap { + if id == dispid { + found = name + } + } + if found != "" { + rv := reflect.ValueOf(pthis.iface).Elem() + rm := rv.MethodByName(found) + rr := rm.Call([]reflect.Value{}) + println(len(rr)) + return ole.S_OK + } + return ole.E_NOTIMPL +} diff --git a/vendor/github.com/go-ole/go-ole/oleutil/connection_func.go b/vendor/github.com/go-ole/go-ole/oleutil/connection_func.go new file mode 100644 index 00000000000..8818fb8275a --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/oleutil/connection_func.go @@ -0,0 +1,10 @@ +// +build !windows + +package oleutil + +import ole "github.com/go-ole/go-ole" + +// ConnectObject creates a connection point between two services for communication. +func ConnectObject(disp *ole.IDispatch, iid *ole.GUID, idisp interface{}) (uint32, error) { + return 0, ole.NewError(ole.E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/oleutil/connection_windows.go b/vendor/github.com/go-ole/go-ole/oleutil/connection_windows.go new file mode 100644 index 00000000000..ab9c0d8dcbd --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/oleutil/connection_windows.go @@ -0,0 +1,58 @@ +// +build windows + +package oleutil + +import ( + "reflect" + "syscall" + "unsafe" + + ole "github.com/go-ole/go-ole" +) + +// ConnectObject creates a connection point between two services for communication. +func ConnectObject(disp *ole.IDispatch, iid *ole.GUID, idisp interface{}) (cookie uint32, err error) { + unknown, err := disp.QueryInterface(ole.IID_IConnectionPointContainer) + if err != nil { + return + } + + container := (*ole.IConnectionPointContainer)(unsafe.Pointer(unknown)) + var point *ole.IConnectionPoint + err = container.FindConnectionPoint(iid, &point) + if err != nil { + return + } + if edisp, ok := idisp.(*ole.IUnknown); ok { + cookie, err = point.Advise(edisp) + container.Release() + if err != nil { + return + } + } + rv := reflect.ValueOf(disp).Elem() + if rv.Type().Kind() == reflect.Struct { + dest := &stdDispatch{} + dest.lpVtbl = &stdDispatchVtbl{} + dest.lpVtbl.pQueryInterface = syscall.NewCallback(dispQueryInterface) + dest.lpVtbl.pAddRef = syscall.NewCallback(dispAddRef) + dest.lpVtbl.pRelease = syscall.NewCallback(dispRelease) + dest.lpVtbl.pGetTypeInfoCount = syscall.NewCallback(dispGetTypeInfoCount) + dest.lpVtbl.pGetTypeInfo = syscall.NewCallback(dispGetTypeInfo) + dest.lpVtbl.pGetIDsOfNames = syscall.NewCallback(dispGetIDsOfNames) + dest.lpVtbl.pInvoke = syscall.NewCallback(dispInvoke) + dest.iface = disp + dest.iid = iid + cookie, err = point.Advise((*ole.IUnknown)(unsafe.Pointer(dest))) + container.Release() + if err != nil { + point.Release() + return + } + return + } + + container.Release() + + return 0, ole.NewError(ole.E_INVALIDARG) +} diff --git a/vendor/github.com/go-ole/go-ole/oleutil/go-get.go b/vendor/github.com/go-ole/go-ole/oleutil/go-get.go new file mode 100644 index 00000000000..58347628f24 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/oleutil/go-get.go @@ -0,0 +1,6 @@ +// This file is here so go get succeeds as without it errors with: +// no buildable Go source files in ... +// +// +build !windows + +package oleutil diff --git a/vendor/github.com/go-ole/go-ole/oleutil/oleutil.go b/vendor/github.com/go-ole/go-ole/oleutil/oleutil.go new file mode 100644 index 00000000000..f7803c1e30f --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/oleutil/oleutil.go @@ -0,0 +1,127 @@ +package oleutil + +import ole "github.com/go-ole/go-ole" + +// ClassIDFrom retrieves class ID whether given is program ID or application string. +func ClassIDFrom(programID string) (classID *ole.GUID, err error) { + return ole.ClassIDFrom(programID) +} + +// CreateObject creates object from programID based on interface type. +// +// Only supports IUnknown. +// +// Program ID can be either program ID or application string. +func CreateObject(programID string) (unknown *ole.IUnknown, err error) { + classID, err := ole.ClassIDFrom(programID) + if err != nil { + return + } + + unknown, err = ole.CreateInstance(classID, ole.IID_IUnknown) + if err != nil { + return + } + + return +} + +// GetActiveObject retrieves active object for program ID and interface ID based +// on interface type. +// +// Only supports IUnknown. +// +// Program ID can be either program ID or application string. +func GetActiveObject(programID string) (unknown *ole.IUnknown, err error) { + classID, err := ole.ClassIDFrom(programID) + if err != nil { + return + } + + unknown, err = ole.GetActiveObject(classID, ole.IID_IUnknown) + if err != nil { + return + } + + return +} + +// CallMethod calls method on IDispatch with parameters. +func CallMethod(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT, err error) { + return disp.InvokeWithOptionalArgs(name, ole.DISPATCH_METHOD, params) +} + +// MustCallMethod calls method on IDispatch with parameters or panics. +func MustCallMethod(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT) { + r, err := CallMethod(disp, name, params...) + if err != nil { + panic(err.Error()) + } + return r +} + +// GetProperty retrieves property from IDispatch. +func GetProperty(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT, err error) { + return disp.InvokeWithOptionalArgs(name, ole.DISPATCH_PROPERTYGET, params) +} + +// MustGetProperty retrieves property from IDispatch or panics. +func MustGetProperty(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT) { + r, err := GetProperty(disp, name, params...) + if err != nil { + panic(err.Error()) + } + return r +} + +// PutProperty mutates property. +func PutProperty(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT, err error) { + return disp.InvokeWithOptionalArgs(name, ole.DISPATCH_PROPERTYPUT, params) +} + +// MustPutProperty mutates property or panics. +func MustPutProperty(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT) { + r, err := PutProperty(disp, name, params...) + if err != nil { + panic(err.Error()) + } + return r +} + +// PutPropertyRef mutates property reference. +func PutPropertyRef(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT, err error) { + return disp.InvokeWithOptionalArgs(name, ole.DISPATCH_PROPERTYPUTREF, params) +} + +// MustPutPropertyRef mutates property reference or panics. +func MustPutPropertyRef(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT) { + r, err := PutPropertyRef(disp, name, params...) + if err != nil { + panic(err.Error()) + } + return r +} + +func ForEach(disp *ole.IDispatch, f func(v *ole.VARIANT) error) error { + newEnum, err := disp.GetProperty("_NewEnum") + if err != nil { + return err + } + defer newEnum.Clear() + + enum, err := newEnum.ToIUnknown().IEnumVARIANT(ole.IID_IEnumVariant) + if err != nil { + return err + } + defer enum.Release() + + for item, length, err := enum.Next(1); length > 0; item, length, err = enum.Next(1) { + if err != nil { + return err + } + if ferr := f(&item); ferr != nil { + return ferr + } + } + return nil +} diff --git a/vendor/github.com/go-ole/go-ole/safearray.go b/vendor/github.com/go-ole/go-ole/safearray.go new file mode 100644 index 00000000000..a5201b56c3d --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/safearray.go @@ -0,0 +1,27 @@ +// Package is meant to retrieve and process safe array data returned from COM. + +package ole + +// SafeArrayBound defines the SafeArray boundaries. +type SafeArrayBound struct { + Elements uint32 + LowerBound int32 +} + +// SafeArray is how COM handles arrays. +type SafeArray struct { + Dimensions uint16 + FeaturesFlag uint16 + ElementsSize uint32 + LocksAmount uint32 + Data uint32 + Bounds [16]byte +} + +// SAFEARRAY is obsolete, exists for backwards compatibility. +// Use SafeArray +type SAFEARRAY SafeArray + +// SAFEARRAYBOUND is obsolete, exists for backwards compatibility. +// Use SafeArrayBound +type SAFEARRAYBOUND SafeArrayBound diff --git a/vendor/github.com/go-ole/go-ole/safearray_func.go b/vendor/github.com/go-ole/go-ole/safearray_func.go new file mode 100644 index 00000000000..0dee670ceb6 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/safearray_func.go @@ -0,0 +1,211 @@ +// +build !windows + +package ole + +import ( + "unsafe" +) + +// safeArrayAccessData returns raw array pointer. +// +// AKA: SafeArrayAccessData in Windows API. +func safeArrayAccessData(safearray *SafeArray) (uintptr, error) { + return uintptr(0), NewError(E_NOTIMPL) +} + +// safeArrayUnaccessData releases raw array. +// +// AKA: SafeArrayUnaccessData in Windows API. +func safeArrayUnaccessData(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayAllocData allocates SafeArray. +// +// AKA: SafeArrayAllocData in Windows API. +func safeArrayAllocData(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayAllocDescriptor allocates SafeArray. +// +// AKA: SafeArrayAllocDescriptor in Windows API. +func safeArrayAllocDescriptor(dimensions uint32) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayAllocDescriptorEx allocates SafeArray. +// +// AKA: SafeArrayAllocDescriptorEx in Windows API. +func safeArrayAllocDescriptorEx(variantType VT, dimensions uint32) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayCopy returns copy of SafeArray. +// +// AKA: SafeArrayCopy in Windows API. +func safeArrayCopy(original *SafeArray) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayCopyData duplicates SafeArray into another SafeArray object. +// +// AKA: SafeArrayCopyData in Windows API. +func safeArrayCopyData(original *SafeArray, duplicate *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayCreate creates SafeArray. +// +// AKA: SafeArrayCreate in Windows API. +func safeArrayCreate(variantType VT, dimensions uint32, bounds *SafeArrayBound) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayCreateEx creates SafeArray. +// +// AKA: SafeArrayCreateEx in Windows API. +func safeArrayCreateEx(variantType VT, dimensions uint32, bounds *SafeArrayBound, extra uintptr) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayCreateVector creates SafeArray. +// +// AKA: SafeArrayCreateVector in Windows API. +func safeArrayCreateVector(variantType VT, lowerBound int32, length uint32) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayCreateVectorEx creates SafeArray. +// +// AKA: SafeArrayCreateVectorEx in Windows API. +func safeArrayCreateVectorEx(variantType VT, lowerBound int32, length uint32, extra uintptr) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayDestroy destroys SafeArray object. +// +// AKA: SafeArrayDestroy in Windows API. +func safeArrayDestroy(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayDestroyData destroys SafeArray object. +// +// AKA: SafeArrayDestroyData in Windows API. +func safeArrayDestroyData(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayDestroyDescriptor destroys SafeArray object. +// +// AKA: SafeArrayDestroyDescriptor in Windows API. +func safeArrayDestroyDescriptor(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayGetDim is the amount of dimensions in the SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetDim in Windows API. +func safeArrayGetDim(safearray *SafeArray) (*uint32, error) { + u := uint32(0) + return &u, NewError(E_NOTIMPL) +} + +// safeArrayGetElementSize is the element size in bytes. +// +// AKA: SafeArrayGetElemsize in Windows API. +func safeArrayGetElementSize(safearray *SafeArray) (*uint32, error) { + u := uint32(0) + return &u, NewError(E_NOTIMPL) +} + +// safeArrayGetElement retrieves element at given index. +func safeArrayGetElement(safearray *SafeArray, index int32, pv unsafe.Pointer) error { + return NewError(E_NOTIMPL) +} + +// safeArrayGetElement retrieves element at given index and converts to string. +func safeArrayGetElementString(safearray *SafeArray, index int32) (string, error) { + return "", NewError(E_NOTIMPL) +} + +// safeArrayGetIID is the InterfaceID of the elements in the SafeArray. +// +// AKA: SafeArrayGetIID in Windows API. +func safeArrayGetIID(safearray *SafeArray) (*GUID, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayGetLBound returns lower bounds of SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetLBound in Windows API. +func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (int32, error) { + return int32(0), NewError(E_NOTIMPL) +} + +// safeArrayGetUBound returns upper bounds of SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetUBound in Windows API. +func safeArrayGetUBound(safearray *SafeArray, dimension uint32) (int32, error) { + return int32(0), NewError(E_NOTIMPL) +} + +// safeArrayGetVartype returns data type of SafeArray. +// +// AKA: SafeArrayGetVartype in Windows API. +func safeArrayGetVartype(safearray *SafeArray) (uint16, error) { + return uint16(0), NewError(E_NOTIMPL) +} + +// safeArrayLock locks SafeArray for reading to modify SafeArray. +// +// This must be called during some calls to ensure that another process does not +// read or write to the SafeArray during editing. +// +// AKA: SafeArrayLock in Windows API. +func safeArrayLock(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayUnlock unlocks SafeArray for reading. +// +// AKA: SafeArrayUnlock in Windows API. +func safeArrayUnlock(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayPutElement stores the data element at the specified location in the +// array. +// +// AKA: SafeArrayPutElement in Windows API. +func safeArrayPutElement(safearray *SafeArray, index int64, element uintptr) error { + return NewError(E_NOTIMPL) +} + +// safeArrayGetRecordInfo accesses IRecordInfo info for custom types. +// +// AKA: SafeArrayGetRecordInfo in Windows API. +// +// XXX: Must implement IRecordInfo interface for this to return. +func safeArrayGetRecordInfo(safearray *SafeArray) (interface{}, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArraySetRecordInfo mutates IRecordInfo info for custom types. +// +// AKA: SafeArraySetRecordInfo in Windows API. +// +// XXX: Must implement IRecordInfo interface for this to return. +func safeArraySetRecordInfo(safearray *SafeArray, recordInfo interface{}) error { + return NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/safearray_windows.go b/vendor/github.com/go-ole/go-ole/safearray_windows.go new file mode 100644 index 00000000000..b48a2394d1e --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/safearray_windows.go @@ -0,0 +1,337 @@ +// +build windows + +package ole + +import ( + "unsafe" +) + +var ( + procSafeArrayAccessData, _ = modoleaut32.FindProc("SafeArrayAccessData") + procSafeArrayAllocData, _ = modoleaut32.FindProc("SafeArrayAllocData") + procSafeArrayAllocDescriptor, _ = modoleaut32.FindProc("SafeArrayAllocDescriptor") + procSafeArrayAllocDescriptorEx, _ = modoleaut32.FindProc("SafeArrayAllocDescriptorEx") + procSafeArrayCopy, _ = modoleaut32.FindProc("SafeArrayCopy") + procSafeArrayCopyData, _ = modoleaut32.FindProc("SafeArrayCopyData") + procSafeArrayCreate, _ = modoleaut32.FindProc("SafeArrayCreate") + procSafeArrayCreateEx, _ = modoleaut32.FindProc("SafeArrayCreateEx") + procSafeArrayCreateVector, _ = modoleaut32.FindProc("SafeArrayCreateVector") + procSafeArrayCreateVectorEx, _ = modoleaut32.FindProc("SafeArrayCreateVectorEx") + procSafeArrayDestroy, _ = modoleaut32.FindProc("SafeArrayDestroy") + procSafeArrayDestroyData, _ = modoleaut32.FindProc("SafeArrayDestroyData") + procSafeArrayDestroyDescriptor, _ = modoleaut32.FindProc("SafeArrayDestroyDescriptor") + procSafeArrayGetDim, _ = modoleaut32.FindProc("SafeArrayGetDim") + procSafeArrayGetElement, _ = modoleaut32.FindProc("SafeArrayGetElement") + procSafeArrayGetElemsize, _ = modoleaut32.FindProc("SafeArrayGetElemsize") + procSafeArrayGetIID, _ = modoleaut32.FindProc("SafeArrayGetIID") + procSafeArrayGetLBound, _ = modoleaut32.FindProc("SafeArrayGetLBound") + procSafeArrayGetUBound, _ = modoleaut32.FindProc("SafeArrayGetUBound") + procSafeArrayGetVartype, _ = modoleaut32.FindProc("SafeArrayGetVartype") + procSafeArrayLock, _ = modoleaut32.FindProc("SafeArrayLock") + procSafeArrayPtrOfIndex, _ = modoleaut32.FindProc("SafeArrayPtrOfIndex") + procSafeArrayUnaccessData, _ = modoleaut32.FindProc("SafeArrayUnaccessData") + procSafeArrayUnlock, _ = modoleaut32.FindProc("SafeArrayUnlock") + procSafeArrayPutElement, _ = modoleaut32.FindProc("SafeArrayPutElement") + //procSafeArrayRedim, _ = modoleaut32.FindProc("SafeArrayRedim") // TODO + //procSafeArraySetIID, _ = modoleaut32.FindProc("SafeArraySetIID") // TODO + procSafeArrayGetRecordInfo, _ = modoleaut32.FindProc("SafeArrayGetRecordInfo") + procSafeArraySetRecordInfo, _ = modoleaut32.FindProc("SafeArraySetRecordInfo") +) + +// safeArrayAccessData returns raw array pointer. +// +// AKA: SafeArrayAccessData in Windows API. +// Todo: Test +func safeArrayAccessData(safearray *SafeArray) (element uintptr, err error) { + err = convertHresultToError( + procSafeArrayAccessData.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&element)))) + return +} + +// safeArrayUnaccessData releases raw array. +// +// AKA: SafeArrayUnaccessData in Windows API. +func safeArrayUnaccessData(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayUnaccessData.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayAllocData allocates SafeArray. +// +// AKA: SafeArrayAllocData in Windows API. +func safeArrayAllocData(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayAllocData.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayAllocDescriptor allocates SafeArray. +// +// AKA: SafeArrayAllocDescriptor in Windows API. +func safeArrayAllocDescriptor(dimensions uint32) (safearray *SafeArray, err error) { + err = convertHresultToError( + procSafeArrayAllocDescriptor.Call(uintptr(dimensions), uintptr(unsafe.Pointer(&safearray)))) + return +} + +// safeArrayAllocDescriptorEx allocates SafeArray. +// +// AKA: SafeArrayAllocDescriptorEx in Windows API. +func safeArrayAllocDescriptorEx(variantType VT, dimensions uint32) (safearray *SafeArray, err error) { + err = convertHresultToError( + procSafeArrayAllocDescriptorEx.Call( + uintptr(variantType), + uintptr(dimensions), + uintptr(unsafe.Pointer(&safearray)))) + return +} + +// safeArrayCopy returns copy of SafeArray. +// +// AKA: SafeArrayCopy in Windows API. +func safeArrayCopy(original *SafeArray) (safearray *SafeArray, err error) { + err = convertHresultToError( + procSafeArrayCopy.Call( + uintptr(unsafe.Pointer(original)), + uintptr(unsafe.Pointer(&safearray)))) + return +} + +// safeArrayCopyData duplicates SafeArray into another SafeArray object. +// +// AKA: SafeArrayCopyData in Windows API. +func safeArrayCopyData(original *SafeArray, duplicate *SafeArray) (err error) { + err = convertHresultToError( + procSafeArrayCopyData.Call( + uintptr(unsafe.Pointer(original)), + uintptr(unsafe.Pointer(duplicate)))) + return +} + +// safeArrayCreate creates SafeArray. +// +// AKA: SafeArrayCreate in Windows API. +func safeArrayCreate(variantType VT, dimensions uint32, bounds *SafeArrayBound) (safearray *SafeArray, err error) { + sa, _, err := procSafeArrayCreate.Call( + uintptr(variantType), + uintptr(dimensions), + uintptr(unsafe.Pointer(bounds))) + safearray = (*SafeArray)(unsafe.Pointer(&sa)) + return +} + +// safeArrayCreateEx creates SafeArray. +// +// AKA: SafeArrayCreateEx in Windows API. +func safeArrayCreateEx(variantType VT, dimensions uint32, bounds *SafeArrayBound, extra uintptr) (safearray *SafeArray, err error) { + sa, _, err := procSafeArrayCreateEx.Call( + uintptr(variantType), + uintptr(dimensions), + uintptr(unsafe.Pointer(bounds)), + extra) + safearray = (*SafeArray)(unsafe.Pointer(sa)) + return +} + +// safeArrayCreateVector creates SafeArray. +// +// AKA: SafeArrayCreateVector in Windows API. +func safeArrayCreateVector(variantType VT, lowerBound int32, length uint32) (safearray *SafeArray, err error) { + sa, _, err := procSafeArrayCreateVector.Call( + uintptr(variantType), + uintptr(lowerBound), + uintptr(length)) + safearray = (*SafeArray)(unsafe.Pointer(sa)) + return +} + +// safeArrayCreateVectorEx creates SafeArray. +// +// AKA: SafeArrayCreateVectorEx in Windows API. +func safeArrayCreateVectorEx(variantType VT, lowerBound int32, length uint32, extra uintptr) (safearray *SafeArray, err error) { + sa, _, err := procSafeArrayCreateVectorEx.Call( + uintptr(variantType), + uintptr(lowerBound), + uintptr(length), + extra) + safearray = (*SafeArray)(unsafe.Pointer(sa)) + return +} + +// safeArrayDestroy destroys SafeArray object. +// +// AKA: SafeArrayDestroy in Windows API. +func safeArrayDestroy(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayDestroy.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayDestroyData destroys SafeArray object. +// +// AKA: SafeArrayDestroyData in Windows API. +func safeArrayDestroyData(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayDestroyData.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayDestroyDescriptor destroys SafeArray object. +// +// AKA: SafeArrayDestroyDescriptor in Windows API. +func safeArrayDestroyDescriptor(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayDestroyDescriptor.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayGetDim is the amount of dimensions in the SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetDim in Windows API. +func safeArrayGetDim(safearray *SafeArray) (dimensions *uint32, err error) { + l, _, err := procSafeArrayGetDim.Call(uintptr(unsafe.Pointer(safearray))) + dimensions = (*uint32)(unsafe.Pointer(l)) + return +} + +// safeArrayGetElementSize is the element size in bytes. +// +// AKA: SafeArrayGetElemsize in Windows API. +func safeArrayGetElementSize(safearray *SafeArray) (length *uint32, err error) { + l, _, err := procSafeArrayGetElemsize.Call(uintptr(unsafe.Pointer(safearray))) + length = (*uint32)(unsafe.Pointer(l)) + return +} + +// safeArrayGetElement retrieves element at given index. +func safeArrayGetElement(safearray *SafeArray, index int32, pv unsafe.Pointer) error { + return convertHresultToError( + procSafeArrayGetElement.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&index)), + uintptr(pv))) +} + +// safeArrayGetElementString retrieves element at given index and converts to string. +func safeArrayGetElementString(safearray *SafeArray, index int32) (str string, err error) { + var element *int16 + err = convertHresultToError( + procSafeArrayGetElement.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&index)), + uintptr(unsafe.Pointer(&element)))) + str = BstrToString(*(**uint16)(unsafe.Pointer(&element))) + SysFreeString(element) + return +} + +// safeArrayGetIID is the InterfaceID of the elements in the SafeArray. +// +// AKA: SafeArrayGetIID in Windows API. +func safeArrayGetIID(safearray *SafeArray) (guid *GUID, err error) { + err = convertHresultToError( + procSafeArrayGetIID.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&guid)))) + return +} + +// safeArrayGetLBound returns lower bounds of SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetLBound in Windows API. +func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (lowerBound int32, err error) { + err = convertHresultToError( + procSafeArrayGetLBound.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(dimension), + uintptr(unsafe.Pointer(&lowerBound)))) + return +} + +// safeArrayGetUBound returns upper bounds of SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetUBound in Windows API. +func safeArrayGetUBound(safearray *SafeArray, dimension uint32) (upperBound int32, err error) { + err = convertHresultToError( + procSafeArrayGetUBound.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(dimension), + uintptr(unsafe.Pointer(&upperBound)))) + return +} + +// safeArrayGetVartype returns data type of SafeArray. +// +// AKA: SafeArrayGetVartype in Windows API. +func safeArrayGetVartype(safearray *SafeArray) (varType uint16, err error) { + err = convertHresultToError( + procSafeArrayGetVartype.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&varType)))) + return +} + +// safeArrayLock locks SafeArray for reading to modify SafeArray. +// +// This must be called during some calls to ensure that another process does not +// read or write to the SafeArray during editing. +// +// AKA: SafeArrayLock in Windows API. +func safeArrayLock(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayLock.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayUnlock unlocks SafeArray for reading. +// +// AKA: SafeArrayUnlock in Windows API. +func safeArrayUnlock(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayUnlock.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayPutElement stores the data element at the specified location in the +// array. +// +// AKA: SafeArrayPutElement in Windows API. +func safeArrayPutElement(safearray *SafeArray, index int64, element uintptr) (err error) { + err = convertHresultToError( + procSafeArrayPutElement.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&index)), + uintptr(unsafe.Pointer(element)))) + return +} + +// safeArrayGetRecordInfo accesses IRecordInfo info for custom types. +// +// AKA: SafeArrayGetRecordInfo in Windows API. +// +// XXX: Must implement IRecordInfo interface for this to return. +func safeArrayGetRecordInfo(safearray *SafeArray) (recordInfo interface{}, err error) { + err = convertHresultToError( + procSafeArrayGetRecordInfo.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&recordInfo)))) + return +} + +// safeArraySetRecordInfo mutates IRecordInfo info for custom types. +// +// AKA: SafeArraySetRecordInfo in Windows API. +// +// XXX: Must implement IRecordInfo interface for this to return. +func safeArraySetRecordInfo(safearray *SafeArray, recordInfo interface{}) (err error) { + err = convertHresultToError( + procSafeArraySetRecordInfo.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&recordInfo)))) + return +} diff --git a/vendor/github.com/go-ole/go-ole/safearrayconversion.go b/vendor/github.com/go-ole/go-ole/safearrayconversion.go new file mode 100644 index 00000000000..259f488ec7f --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/safearrayconversion.go @@ -0,0 +1,140 @@ +// Helper for converting SafeArray to array of objects. + +package ole + +import ( + "unsafe" +) + +type SafeArrayConversion struct { + Array *SafeArray +} + +func (sac *SafeArrayConversion) ToStringArray() (strings []string) { + totalElements, _ := sac.TotalElements(0) + strings = make([]string, totalElements) + + for i := int32(0); i < totalElements; i++ { + strings[int32(i)], _ = safeArrayGetElementString(sac.Array, i) + } + + return +} + +func (sac *SafeArrayConversion) ToByteArray() (bytes []byte) { + totalElements, _ := sac.TotalElements(0) + bytes = make([]byte, totalElements) + + for i := int32(0); i < totalElements; i++ { + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&bytes[int32(i)])) + } + + return +} + +func (sac *SafeArrayConversion) ToValueArray() (values []interface{}) { + totalElements, _ := sac.TotalElements(0) + values = make([]interface{}, totalElements) + vt, _ := safeArrayGetVartype(sac.Array) + + for i := int32(0); i < totalElements; i++ { + switch VT(vt) { + case VT_BOOL: + var v bool + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_I1: + var v int8 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_I2: + var v int16 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_I4: + var v int32 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_I8: + var v int64 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_UI1: + var v uint8 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_UI2: + var v uint16 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_UI4: + var v uint32 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_UI8: + var v uint64 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_R4: + var v float32 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_R8: + var v float64 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_BSTR: + var v string + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_VARIANT: + var v VARIANT + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v.Value() + default: + // TODO + } + } + + return +} + +func (sac *SafeArrayConversion) GetType() (varType uint16, err error) { + return safeArrayGetVartype(sac.Array) +} + +func (sac *SafeArrayConversion) GetDimensions() (dimensions *uint32, err error) { + return safeArrayGetDim(sac.Array) +} + +func (sac *SafeArrayConversion) GetSize() (length *uint32, err error) { + return safeArrayGetElementSize(sac.Array) +} + +func (sac *SafeArrayConversion) TotalElements(index uint32) (totalElements int32, err error) { + if index < 1 { + index = 1 + } + + // Get array bounds + var LowerBounds int32 + var UpperBounds int32 + + LowerBounds, err = safeArrayGetLBound(sac.Array, index) + if err != nil { + return + } + + UpperBounds, err = safeArrayGetUBound(sac.Array, index) + if err != nil { + return + } + + totalElements = UpperBounds - LowerBounds + 1 + return +} + +// Release Safe Array memory +func (sac *SafeArrayConversion) Release() { + safeArrayDestroy(sac.Array) +} diff --git a/vendor/github.com/go-ole/go-ole/safearrayslices.go b/vendor/github.com/go-ole/go-ole/safearrayslices.go new file mode 100644 index 00000000000..a9fa885f1d8 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/safearrayslices.go @@ -0,0 +1,33 @@ +// +build windows + +package ole + +import ( + "unsafe" +) + +func safeArrayFromByteSlice(slice []byte) *SafeArray { + array, _ := safeArrayCreateVector(VT_UI1, 0, uint32(len(slice))) + + if array == nil { + panic("Could not convert []byte to SAFEARRAY") + } + + for i, v := range slice { + safeArrayPutElement(array, int64(i), uintptr(unsafe.Pointer(&v))) + } + return array +} + +func safeArrayFromStringSlice(slice []string) *SafeArray { + array, _ := safeArrayCreateVector(VT_BSTR, 0, uint32(len(slice))) + + if array == nil { + panic("Could not convert []string to SAFEARRAY") + } + // SysAllocStringLen(s) + for i, v := range slice { + safeArrayPutElement(array, int64(i), uintptr(unsafe.Pointer(SysAllocStringLen(v)))) + } + return array +} diff --git a/vendor/github.com/go-ole/go-ole/utility.go b/vendor/github.com/go-ole/go-ole/utility.go new file mode 100644 index 00000000000..99ee82dc345 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/utility.go @@ -0,0 +1,101 @@ +package ole + +import ( + "unicode/utf16" + "unsafe" +) + +// ClassIDFrom retrieves class ID whether given is program ID or application string. +// +// Helper that provides check against both Class ID from Program ID and Class ID from string. It is +// faster, if you know which you are using, to use the individual functions, but this will check +// against available functions for you. +func ClassIDFrom(programID string) (classID *GUID, err error) { + classID, err = CLSIDFromProgID(programID) + if err != nil { + classID, err = CLSIDFromString(programID) + if err != nil { + return + } + } + return +} + +// BytePtrToString converts byte pointer to a Go string. +func BytePtrToString(p *byte) string { + a := (*[10000]uint8)(unsafe.Pointer(p)) + i := 0 + for a[i] != 0 { + i++ + } + return string(a[:i]) +} + +// UTF16PtrToString is alias for LpOleStrToString. +// +// Kept for compatibility reasons. +func UTF16PtrToString(p *uint16) string { + return LpOleStrToString(p) +} + +// LpOleStrToString converts COM Unicode to Go string. +func LpOleStrToString(p *uint16) string { + if p == nil { + return "" + } + + length := lpOleStrLen(p) + a := make([]uint16, length) + + ptr := unsafe.Pointer(p) + + for i := 0; i < int(length); i++ { + a[i] = *(*uint16)(ptr) + ptr = unsafe.Pointer(uintptr(ptr) + 2) + } + + return string(utf16.Decode(a)) +} + +// BstrToString converts COM binary string to Go string. +func BstrToString(p *uint16) string { + if p == nil { + return "" + } + length := SysStringLen((*int16)(unsafe.Pointer(p))) + a := make([]uint16, length) + + ptr := unsafe.Pointer(p) + + for i := 0; i < int(length); i++ { + a[i] = *(*uint16)(ptr) + ptr = unsafe.Pointer(uintptr(ptr) + 2) + } + return string(utf16.Decode(a)) +} + +// lpOleStrLen returns the length of Unicode string. +func lpOleStrLen(p *uint16) (length int64) { + if p == nil { + return 0 + } + + ptr := unsafe.Pointer(p) + + for i := 0; ; i++ { + if 0 == *(*uint16)(ptr) { + length = int64(i) + break + } + ptr = unsafe.Pointer(uintptr(ptr) + 2) + } + return +} + +// convertHresultToError converts syscall to error, if call is unsuccessful. +func convertHresultToError(hr uintptr, r2 uintptr, ignore error) (err error) { + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/vendor/github.com/go-ole/go-ole/variables.go b/vendor/github.com/go-ole/go-ole/variables.go new file mode 100644 index 00000000000..ebe00f1cfc9 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variables.go @@ -0,0 +1,16 @@ +// +build windows + +package ole + +import ( + "syscall" +) + +var ( + modcombase = syscall.NewLazyDLL("combase.dll") + modkernel32, _ = syscall.LoadDLL("kernel32.dll") + modole32, _ = syscall.LoadDLL("ole32.dll") + modoleaut32, _ = syscall.LoadDLL("oleaut32.dll") + modmsvcrt, _ = syscall.LoadDLL("msvcrt.dll") + moduser32, _ = syscall.LoadDLL("user32.dll") +) diff --git a/vendor/github.com/go-ole/go-ole/variant.go b/vendor/github.com/go-ole/go-ole/variant.go new file mode 100644 index 00000000000..967a23fea9a --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant.go @@ -0,0 +1,105 @@ +package ole + +import "unsafe" + +// NewVariant returns new variant based on type and value. +func NewVariant(vt VT, val int64) VARIANT { + return VARIANT{VT: vt, Val: val} +} + +// ToIUnknown converts Variant to Unknown object. +func (v *VARIANT) ToIUnknown() *IUnknown { + if v.VT != VT_UNKNOWN { + return nil + } + return (*IUnknown)(unsafe.Pointer(uintptr(v.Val))) +} + +// ToIDispatch converts variant to dispatch object. +func (v *VARIANT) ToIDispatch() *IDispatch { + if v.VT != VT_DISPATCH { + return nil + } + return (*IDispatch)(unsafe.Pointer(uintptr(v.Val))) +} + +// ToArray converts variant to SafeArray helper. +func (v *VARIANT) ToArray() *SafeArrayConversion { + if v.VT != VT_SAFEARRAY { + if v.VT&VT_ARRAY == 0 { + return nil + } + } + var safeArray *SafeArray = (*SafeArray)(unsafe.Pointer(uintptr(v.Val))) + return &SafeArrayConversion{safeArray} +} + +// ToString converts variant to Go string. +func (v *VARIANT) ToString() string { + if v.VT != VT_BSTR { + return "" + } + return BstrToString(*(**uint16)(unsafe.Pointer(&v.Val))) +} + +// Clear the memory of variant object. +func (v *VARIANT) Clear() error { + return VariantClear(v) +} + +// Value returns variant value based on its type. +// +// Currently supported types: 2- and 4-byte integers, strings, bools. +// Note that 64-bit integers, datetimes, and other types are stored as strings +// and will be returned as strings. +// +// Needs to be further converted, because this returns an interface{}. +func (v *VARIANT) Value() interface{} { + switch v.VT { + case VT_I1: + return int8(v.Val) + case VT_UI1: + return uint8(v.Val) + case VT_I2: + return int16(v.Val) + case VT_UI2: + return uint16(v.Val) + case VT_I4: + return int32(v.Val) + case VT_UI4: + return uint32(v.Val) + case VT_I8: + return int64(v.Val) + case VT_UI8: + return uint64(v.Val) + case VT_INT: + return int(v.Val) + case VT_UINT: + return uint(v.Val) + case VT_INT_PTR: + return uintptr(v.Val) // TODO + case VT_UINT_PTR: + return uintptr(v.Val) + case VT_R4: + return *(*float32)(unsafe.Pointer(&v.Val)) + case VT_R8: + return *(*float64)(unsafe.Pointer(&v.Val)) + case VT_BSTR: + return v.ToString() + case VT_DATE: + // VT_DATE type will either return float64 or time.Time. + d := uint64(v.Val) + date, err := GetVariantDate(d) + if err != nil { + return float64(v.Val) + } + return date + case VT_UNKNOWN: + return v.ToIUnknown() + case VT_DISPATCH: + return v.ToIDispatch() + case VT_BOOL: + return v.Val != 0 + } + return nil +} diff --git a/vendor/github.com/go-ole/go-ole/variant_386.go b/vendor/github.com/go-ole/go-ole/variant_386.go new file mode 100644 index 00000000000..e73736bf391 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant_386.go @@ -0,0 +1,11 @@ +// +build 386 + +package ole + +type VARIANT struct { + VT VT // 2 + wReserved1 uint16 // 4 + wReserved2 uint16 // 6 + wReserved3 uint16 // 8 + Val int64 // 16 +} diff --git a/vendor/github.com/go-ole/go-ole/variant_amd64.go b/vendor/github.com/go-ole/go-ole/variant_amd64.go new file mode 100644 index 00000000000..dccdde13233 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant_amd64.go @@ -0,0 +1,12 @@ +// +build amd64 + +package ole + +type VARIANT struct { + VT VT // 2 + wReserved1 uint16 // 4 + wReserved2 uint16 // 6 + wReserved3 uint16 // 8 + Val int64 // 16 + _ [8]byte // 24 +} diff --git a/vendor/github.com/go-ole/go-ole/variant_date_386.go b/vendor/github.com/go-ole/go-ole/variant_date_386.go new file mode 100644 index 00000000000..1b970f63f5f --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant_date_386.go @@ -0,0 +1,22 @@ +// +build windows,386 + +package ole + +import ( + "errors" + "syscall" + "time" + "unsafe" +) + +// GetVariantDate converts COM Variant Time value to Go time.Time. +func GetVariantDate(value uint64) (time.Time, error) { + var st syscall.Systemtime + v1 := uint32(value) + v2 := uint32(value >> 32) + r, _, _ := procVariantTimeToSystemTime.Call(uintptr(v1), uintptr(v2), uintptr(unsafe.Pointer(&st))) + if r != 0 { + return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil + } + return time.Now(), errors.New("Could not convert to time, passing current time.") +} diff --git a/vendor/github.com/go-ole/go-ole/variant_date_amd64.go b/vendor/github.com/go-ole/go-ole/variant_date_amd64.go new file mode 100644 index 00000000000..6952f1f0de6 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant_date_amd64.go @@ -0,0 +1,20 @@ +// +build windows,amd64 + +package ole + +import ( + "errors" + "syscall" + "time" + "unsafe" +) + +// GetVariantDate converts COM Variant Time value to Go time.Time. +func GetVariantDate(value uint64) (time.Time, error) { + var st syscall.Systemtime + r, _, _ := procVariantTimeToSystemTime.Call(uintptr(value), uintptr(unsafe.Pointer(&st))) + if r != 0 { + return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil + } + return time.Now(), errors.New("Could not convert to time, passing current time.") +} diff --git a/vendor/github.com/go-ole/go-ole/variant_ppc64le.go b/vendor/github.com/go-ole/go-ole/variant_ppc64le.go new file mode 100644 index 00000000000..326427a7d14 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant_ppc64le.go @@ -0,0 +1,12 @@ +// +build ppc64le + +package ole + +type VARIANT struct { + VT VT // 2 + wReserved1 uint16 // 4 + wReserved2 uint16 // 6 + wReserved3 uint16 // 8 + Val int64 // 16 + _ [8]byte // 24 +} diff --git a/vendor/github.com/go-ole/go-ole/variant_s390x.go b/vendor/github.com/go-ole/go-ole/variant_s390x.go new file mode 100644 index 00000000000..9874ca66b4f --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant_s390x.go @@ -0,0 +1,12 @@ +// +build s390x + +package ole + +type VARIANT struct { + VT VT // 2 + wReserved1 uint16 // 4 + wReserved2 uint16 // 6 + wReserved3 uint16 // 8 + Val int64 // 16 + _ [8]byte // 24 +} diff --git a/vendor/github.com/go-ole/go-ole/vt_string.go b/vendor/github.com/go-ole/go-ole/vt_string.go new file mode 100644 index 00000000000..729b4a04dd9 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/vt_string.go @@ -0,0 +1,58 @@ +// generated by stringer -output vt_string.go -type VT; DO NOT EDIT + +package ole + +import "fmt" + +const ( + _VT_name_0 = "VT_EMPTYVT_NULLVT_I2VT_I4VT_R4VT_R8VT_CYVT_DATEVT_BSTRVT_DISPATCHVT_ERRORVT_BOOLVT_VARIANTVT_UNKNOWNVT_DECIMAL" + _VT_name_1 = "VT_I1VT_UI1VT_UI2VT_UI4VT_I8VT_UI8VT_INTVT_UINTVT_VOIDVT_HRESULTVT_PTRVT_SAFEARRAYVT_CARRAYVT_USERDEFINEDVT_LPSTRVT_LPWSTR" + _VT_name_2 = "VT_RECORDVT_INT_PTRVT_UINT_PTR" + _VT_name_3 = "VT_FILETIMEVT_BLOBVT_STREAMVT_STORAGEVT_STREAMED_OBJECTVT_STORED_OBJECTVT_BLOB_OBJECTVT_CFVT_CLSID" + _VT_name_4 = "VT_BSTR_BLOBVT_VECTOR" + _VT_name_5 = "VT_ARRAY" + _VT_name_6 = "VT_BYREF" + _VT_name_7 = "VT_RESERVED" + _VT_name_8 = "VT_ILLEGAL" +) + +var ( + _VT_index_0 = [...]uint8{0, 8, 15, 20, 25, 30, 35, 40, 47, 54, 65, 73, 80, 90, 100, 110} + _VT_index_1 = [...]uint8{0, 5, 11, 17, 23, 28, 34, 40, 47, 54, 64, 70, 82, 91, 105, 113, 122} + _VT_index_2 = [...]uint8{0, 9, 19, 30} + _VT_index_3 = [...]uint8{0, 11, 18, 27, 37, 55, 71, 85, 90, 98} + _VT_index_4 = [...]uint8{0, 12, 21} + _VT_index_5 = [...]uint8{0, 8} + _VT_index_6 = [...]uint8{0, 8} + _VT_index_7 = [...]uint8{0, 11} + _VT_index_8 = [...]uint8{0, 10} +) + +func (i VT) String() string { + switch { + case 0 <= i && i <= 14: + return _VT_name_0[_VT_index_0[i]:_VT_index_0[i+1]] + case 16 <= i && i <= 31: + i -= 16 + return _VT_name_1[_VT_index_1[i]:_VT_index_1[i+1]] + case 36 <= i && i <= 38: + i -= 36 + return _VT_name_2[_VT_index_2[i]:_VT_index_2[i+1]] + case 64 <= i && i <= 72: + i -= 64 + return _VT_name_3[_VT_index_3[i]:_VT_index_3[i+1]] + case 4095 <= i && i <= 4096: + i -= 4095 + return _VT_name_4[_VT_index_4[i]:_VT_index_4[i+1]] + case i == 8192: + return _VT_name_5 + case i == 16384: + return _VT_name_6 + case i == 32768: + return _VT_name_7 + case i == 65535: + return _VT_name_8 + default: + return fmt.Sprintf("VT(%d)", i) + } +} diff --git a/vendor/github.com/go-ole/go-ole/winrt.go b/vendor/github.com/go-ole/go-ole/winrt.go new file mode 100644 index 00000000000..4e9eca73244 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/winrt.go @@ -0,0 +1,99 @@ +// +build windows + +package ole + +import ( + "reflect" + "syscall" + "unicode/utf8" + "unsafe" +) + +var ( + procRoInitialize = modcombase.NewProc("RoInitialize") + procRoActivateInstance = modcombase.NewProc("RoActivateInstance") + procRoGetActivationFactory = modcombase.NewProc("RoGetActivationFactory") + procWindowsCreateString = modcombase.NewProc("WindowsCreateString") + procWindowsDeleteString = modcombase.NewProc("WindowsDeleteString") + procWindowsGetStringRawBuffer = modcombase.NewProc("WindowsGetStringRawBuffer") +) + +func RoInitialize(thread_type uint32) (err error) { + hr, _, _ := procRoInitialize.Call(uintptr(thread_type)) + if hr != 0 { + err = NewError(hr) + } + return +} + +func RoActivateInstance(clsid string) (ins *IInspectable, err error) { + hClsid, err := NewHString(clsid) + if err != nil { + return nil, err + } + defer DeleteHString(hClsid) + + hr, _, _ := procRoActivateInstance.Call( + uintptr(unsafe.Pointer(hClsid)), + uintptr(unsafe.Pointer(&ins))) + if hr != 0 { + err = NewError(hr) + } + return +} + +func RoGetActivationFactory(clsid string, iid *GUID) (ins *IInspectable, err error) { + hClsid, err := NewHString(clsid) + if err != nil { + return nil, err + } + defer DeleteHString(hClsid) + + hr, _, _ := procRoGetActivationFactory.Call( + uintptr(unsafe.Pointer(hClsid)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&ins))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// HString is handle string for pointers. +type HString uintptr + +// NewHString returns a new HString for Go string. +func NewHString(s string) (hstring HString, err error) { + u16 := syscall.StringToUTF16Ptr(s) + len := uint32(utf8.RuneCountInString(s)) + hr, _, _ := procWindowsCreateString.Call( + uintptr(unsafe.Pointer(u16)), + uintptr(len), + uintptr(unsafe.Pointer(&hstring))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// DeleteHString deletes HString. +func DeleteHString(hstring HString) (err error) { + hr, _, _ := procWindowsDeleteString.Call(uintptr(hstring)) + if hr != 0 { + err = NewError(hr) + } + return +} + +// String returns Go string value of HString. +func (h HString) String() string { + var u16buf uintptr + var u16len uint32 + u16buf, _, _ = procWindowsGetStringRawBuffer.Call( + uintptr(h), + uintptr(unsafe.Pointer(&u16len))) + + u16hdr := reflect.SliceHeader{Data: u16buf, Len: int(u16len), Cap: int(u16len)} + u16 := *(*[]uint16)(unsafe.Pointer(&u16hdr)) + return syscall.UTF16ToString(u16) +} diff --git a/vendor/github.com/go-ole/go-ole/winrt_doc.go b/vendor/github.com/go-ole/go-ole/winrt_doc.go new file mode 100644 index 00000000000..52e6d74c9ab --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/winrt_doc.go @@ -0,0 +1,36 @@ +// +build !windows + +package ole + +// RoInitialize +func RoInitialize(thread_type uint32) (err error) { + return NewError(E_NOTIMPL) +} + +// RoActivateInstance +func RoActivateInstance(clsid string) (ins *IInspectable, err error) { + return nil, NewError(E_NOTIMPL) +} + +// RoGetActivationFactory +func RoGetActivationFactory(clsid string, iid *GUID) (ins *IInspectable, err error) { + return nil, NewError(E_NOTIMPL) +} + +// HString is handle string for pointers. +type HString uintptr + +// NewHString returns a new HString for Go string. +func NewHString(s string) (hstring HString, err error) { + return HString(uintptr(0)), NewError(E_NOTIMPL) +} + +// DeleteHString deletes HString. +func DeleteHString(hstring HString) (err error) { + return NewError(E_NOTIMPL) +} + +// String returns Go string value of HString. +func (h HString) String() string { + return "" +} diff --git a/vendor/github.com/shirou/gopsutil/LICENSE b/vendor/github.com/shirou/gopsutil/LICENSE new file mode 100644 index 00000000000..da71a5e729f --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/LICENSE @@ -0,0 +1,61 @@ +gopsutil is distributed under BSD license reproduced below. + +Copyright (c) 2014, WAKAYAMA Shirou +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the gopsutil authors nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +------- +internal/common/binary.go in the gopsutil is copied and modifid from golang/encoding/binary.go. + + + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu.go b/vendor/github.com/shirou/gopsutil/cpu/cpu.go new file mode 100644 index 00000000000..d3ea1f245cb --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu.go @@ -0,0 +1,183 @@ +package cpu + +import ( + "context" + "encoding/json" + "fmt" + "math" + "strconv" + "strings" + "sync" + "time" + + "github.com/shirou/gopsutil/internal/common" +) + +// TimesStat contains the amounts of time the CPU has spent performing different +// kinds of work. Time units are in seconds. It is based on linux /proc/stat file. +type TimesStat struct { + CPU string `json:"cpu"` + User float64 `json:"user"` + System float64 `json:"system"` + Idle float64 `json:"idle"` + Nice float64 `json:"nice"` + Iowait float64 `json:"iowait"` + Irq float64 `json:"irq"` + Softirq float64 `json:"softirq"` + Steal float64 `json:"steal"` + Guest float64 `json:"guest"` + GuestNice float64 `json:"guestNice"` +} + +type InfoStat struct { + CPU int32 `json:"cpu"` + VendorID string `json:"vendorId"` + Family string `json:"family"` + Model string `json:"model"` + Stepping int32 `json:"stepping"` + PhysicalID string `json:"physicalId"` + CoreID string `json:"coreId"` + Cores int32 `json:"cores"` + ModelName string `json:"modelName"` + Mhz float64 `json:"mhz"` + CacheSize int32 `json:"cacheSize"` + Flags []string `json:"flags"` + Microcode string `json:"microcode"` +} + +type lastPercent struct { + sync.Mutex + lastCPUTimes []TimesStat + lastPerCPUTimes []TimesStat +} + +var lastCPUPercent lastPercent +var invoke common.Invoker = common.Invoke{} + +func init() { + lastCPUPercent.Lock() + lastCPUPercent.lastCPUTimes, _ = Times(false) + lastCPUPercent.lastPerCPUTimes, _ = Times(true) + lastCPUPercent.Unlock() +} + +// Counts returns the number of physical or logical cores in the system +func Counts(logical bool) (int, error) { + return CountsWithContext(context.Background(), logical) +} + +func (c TimesStat) String() string { + v := []string{ + `"cpu":"` + c.CPU + `"`, + `"user":` + strconv.FormatFloat(c.User, 'f', 1, 64), + `"system":` + strconv.FormatFloat(c.System, 'f', 1, 64), + `"idle":` + strconv.FormatFloat(c.Idle, 'f', 1, 64), + `"nice":` + strconv.FormatFloat(c.Nice, 'f', 1, 64), + `"iowait":` + strconv.FormatFloat(c.Iowait, 'f', 1, 64), + `"irq":` + strconv.FormatFloat(c.Irq, 'f', 1, 64), + `"softirq":` + strconv.FormatFloat(c.Softirq, 'f', 1, 64), + `"steal":` + strconv.FormatFloat(c.Steal, 'f', 1, 64), + `"guest":` + strconv.FormatFloat(c.Guest, 'f', 1, 64), + `"guestNice":` + strconv.FormatFloat(c.GuestNice, 'f', 1, 64), + } + + return `{` + strings.Join(v, ",") + `}` +} + +// Total returns the total number of seconds in a CPUTimesStat +func (c TimesStat) Total() float64 { + total := c.User + c.System + c.Nice + c.Iowait + c.Irq + c.Softirq + + c.Steal + c.Idle + return total +} + +func (c InfoStat) String() string { + s, _ := json.Marshal(c) + return string(s) +} + +func getAllBusy(t TimesStat) (float64, float64) { + busy := t.User + t.System + t.Nice + t.Iowait + t.Irq + + t.Softirq + t.Steal + return busy + t.Idle, busy +} + +func calculateBusy(t1, t2 TimesStat) float64 { + t1All, t1Busy := getAllBusy(t1) + t2All, t2Busy := getAllBusy(t2) + + if t2Busy <= t1Busy { + return 0 + } + if t2All <= t1All { + return 100 + } + return math.Min(100, math.Max(0, (t2Busy-t1Busy)/(t2All-t1All)*100)) +} + +func calculateAllBusy(t1, t2 []TimesStat) ([]float64, error) { + // Make sure the CPU measurements have the same length. + if len(t1) != len(t2) { + return nil, fmt.Errorf( + "received two CPU counts: %d != %d", + len(t1), len(t2), + ) + } + + ret := make([]float64, len(t1)) + for i, t := range t2 { + ret[i] = calculateBusy(t1[i], t) + } + return ret, nil +} + +// Percent calculates the percentage of cpu used either per CPU or combined. +// If an interval of 0 is given it will compare the current cpu times against the last call. +// Returns one value per cpu, or a single value if percpu is set to false. +func Percent(interval time.Duration, percpu bool) ([]float64, error) { + return PercentWithContext(context.Background(), interval, percpu) +} + +func PercentWithContext(ctx context.Context, interval time.Duration, percpu bool) ([]float64, error) { + if interval <= 0 { + return percentUsedFromLastCall(percpu) + } + + // Get CPU usage at the start of the interval. + cpuTimes1, err := Times(percpu) + if err != nil { + return nil, err + } + + time.Sleep(interval) + + // And at the end of the interval. + cpuTimes2, err := Times(percpu) + if err != nil { + return nil, err + } + + return calculateAllBusy(cpuTimes1, cpuTimes2) +} + +func percentUsedFromLastCall(percpu bool) ([]float64, error) { + cpuTimes, err := Times(percpu) + if err != nil { + return nil, err + } + lastCPUPercent.Lock() + defer lastCPUPercent.Unlock() + var lastTimes []TimesStat + if percpu { + lastTimes = lastCPUPercent.lastPerCPUTimes + lastCPUPercent.lastPerCPUTimes = cpuTimes + } else { + lastTimes = lastCPUPercent.lastCPUTimes + lastCPUPercent.lastCPUTimes = cpuTimes + } + + if lastTimes == nil { + return nil, fmt.Errorf("error getting times for cpu percent. lastTimes was nil") + } + return calculateAllBusy(lastTimes, cpuTimes) +} diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin.go new file mode 100644 index 00000000000..3d3455ee684 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin.go @@ -0,0 +1,119 @@ +// +build darwin + +package cpu + +import ( + "context" + "os/exec" + "strconv" + "strings" + + "golang.org/x/sys/unix" +) + +// sys/resource.h +const ( + CPUser = 0 + CPNice = 1 + CPSys = 2 + CPIntr = 3 + CPIdle = 4 + CPUStates = 5 +) + +// default value. from time.h +var ClocksPerSec = float64(128) + +func init() { + getconf, err := exec.LookPath("getconf") + if err != nil { + return + } + out, err := invoke.Command(getconf, "CLK_TCK") + // ignore errors + if err == nil { + i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) + if err == nil { + ClocksPerSec = float64(i) + } + } +} + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + if percpu { + return perCPUTimes() + } + + return allCPUTimes() +} + +// Returns only one CPUInfoStat on FreeBSD +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + var ret []InfoStat + + c := InfoStat{} + c.ModelName, _ = unix.Sysctl("machdep.cpu.brand_string") + family, _ := unix.SysctlUint32("machdep.cpu.family") + c.Family = strconv.FormatUint(uint64(family), 10) + model, _ := unix.SysctlUint32("machdep.cpu.model") + c.Model = strconv.FormatUint(uint64(model), 10) + stepping, _ := unix.SysctlUint32("machdep.cpu.stepping") + c.Stepping = int32(stepping) + features, err := unix.Sysctl("machdep.cpu.features") + if err == nil { + for _, v := range strings.Fields(features) { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } + leaf7Features, err := unix.Sysctl("machdep.cpu.leaf7_features") + if err == nil { + for _, v := range strings.Fields(leaf7Features) { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } + extfeatures, err := unix.Sysctl("machdep.cpu.extfeatures") + if err == nil { + for _, v := range strings.Fields(extfeatures) { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } + cores, _ := unix.SysctlUint32("machdep.cpu.core_count") + c.Cores = int32(cores) + cacheSize, _ := unix.SysctlUint32("machdep.cpu.cache.size") + c.CacheSize = int32(cacheSize) + c.VendorID, _ = unix.Sysctl("machdep.cpu.vendor") + + // Use the rated frequency of the CPU. This is a static value and does not + // account for low power or Turbo Boost modes. + cpuFrequency, err := unix.SysctlUint64("hw.cpufrequency") + if err != nil { + return ret, err + } + c.Mhz = float64(cpuFrequency) / 1000000.0 + + return append(ret, c), nil +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + var cpuArgument string + if logical { + cpuArgument = "hw.logicalcpu" + } else { + cpuArgument = "hw.physicalcpu" + } + + count, err := unix.SysctlUint32(cpuArgument) + if err != nil { + return 0, err + } + + return int(count), nil +} diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin_cgo.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin_cgo.go new file mode 100644 index 00000000000..180e0afa73f --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin_cgo.go @@ -0,0 +1,111 @@ +// +build darwin +// +build cgo + +package cpu + +/* +#include +#include +#include +#include +#include +#include +#if TARGET_OS_MAC +#include +#endif +#include +#include +*/ +import "C" + +import ( + "bytes" + "encoding/binary" + "fmt" + "unsafe" +) + +// these CPU times for darwin is borrowed from influxdb/telegraf. + +func perCPUTimes() ([]TimesStat, error) { + var ( + count C.mach_msg_type_number_t + cpuload *C.processor_cpu_load_info_data_t + ncpu C.natural_t + ) + + status := C.host_processor_info(C.host_t(C.mach_host_self()), + C.PROCESSOR_CPU_LOAD_INFO, + &ncpu, + (*C.processor_info_array_t)(unsafe.Pointer(&cpuload)), + &count) + + if status != C.KERN_SUCCESS { + return nil, fmt.Errorf("host_processor_info error=%d", status) + } + + // jump through some cgo casting hoops and ensure we properly free + // the memory that cpuload points to + target := C.vm_map_t(C.mach_task_self_) + address := C.vm_address_t(uintptr(unsafe.Pointer(cpuload))) + defer C.vm_deallocate(target, address, C.vm_size_t(ncpu)) + + // the body of struct processor_cpu_load_info + // aka processor_cpu_load_info_data_t + var cpu_ticks [C.CPU_STATE_MAX]uint32 + + // copy the cpuload array to a []byte buffer + // where we can binary.Read the data + size := int(ncpu) * binary.Size(cpu_ticks) + buf := (*[1 << 30]byte)(unsafe.Pointer(cpuload))[:size:size] + + bbuf := bytes.NewBuffer(buf) + + var ret []TimesStat + + for i := 0; i < int(ncpu); i++ { + err := binary.Read(bbuf, binary.LittleEndian, &cpu_ticks) + if err != nil { + return nil, err + } + + c := TimesStat{ + CPU: fmt.Sprintf("cpu%d", i), + User: float64(cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec, + System: float64(cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec, + Nice: float64(cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec, + Idle: float64(cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec, + } + + ret = append(ret, c) + } + + return ret, nil +} + +func allCPUTimes() ([]TimesStat, error) { + var count C.mach_msg_type_number_t + var cpuload C.host_cpu_load_info_data_t + + count = C.HOST_CPU_LOAD_INFO_COUNT + + status := C.host_statistics(C.host_t(C.mach_host_self()), + C.HOST_CPU_LOAD_INFO, + C.host_info_t(unsafe.Pointer(&cpuload)), + &count) + + if status != C.KERN_SUCCESS { + return nil, fmt.Errorf("host_statistics error=%d", status) + } + + c := TimesStat{ + CPU: "cpu-total", + User: float64(cpuload.cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec, + System: float64(cpuload.cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec, + Nice: float64(cpuload.cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec, + Idle: float64(cpuload.cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec, + } + + return []TimesStat{c}, nil + +} diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin_nocgo.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin_nocgo.go new file mode 100644 index 00000000000..242b4a8e799 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin_nocgo.go @@ -0,0 +1,14 @@ +// +build darwin +// +build !cgo + +package cpu + +import "github.com/shirou/gopsutil/internal/common" + +func perCPUTimes() ([]TimesStat, error) { + return []TimesStat{}, common.ErrNotImplementedError +} + +func allCPUTimes() ([]TimesStat, error) { + return []TimesStat{}, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_dragonfly.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_dragonfly.go new file mode 100644 index 00000000000..eed5beab789 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_dragonfly.go @@ -0,0 +1,161 @@ +package cpu + +import ( + "context" + "fmt" + "os/exec" + "reflect" + "regexp" + "runtime" + "strconv" + "strings" + "unsafe" + + "github.com/shirou/gopsutil/internal/common" + "golang.org/x/sys/unix" +) + +var ClocksPerSec = float64(128) +var cpuMatch = regexp.MustCompile(`^CPU:`) +var originMatch = regexp.MustCompile(`Origin\s*=\s*"(.+)"\s+Id\s*=\s*(.+)\s+Stepping\s*=\s*(.+)`) +var featuresMatch = regexp.MustCompile(`Features=.+<(.+)>`) +var featuresMatch2 = regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`) +var cpuEnd = regexp.MustCompile(`^Trying to mount root`) +var cpuTimesSize int +var emptyTimes cpuTimes + +func init() { + getconf, err := exec.LookPath("getconf") + if err != nil { + return + } + out, err := invoke.Command(getconf, "CLK_TCK") + // ignore errors + if err == nil { + i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) + if err == nil { + ClocksPerSec = float64(i) + } + } +} + +func timeStat(name string, t *cpuTimes) *TimesStat { + return &TimesStat{ + User: float64(t.User) / ClocksPerSec, + Nice: float64(t.Nice) / ClocksPerSec, + System: float64(t.Sys) / ClocksPerSec, + Idle: float64(t.Idle) / ClocksPerSec, + Irq: float64(t.Intr) / ClocksPerSec, + CPU: name, + } +} + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + if percpu { + buf, err := unix.SysctlRaw("kern.cp_times") + if err != nil { + return nil, err + } + + // We can't do this in init due to the conflict with cpu.init() + if cpuTimesSize == 0 { + cpuTimesSize = int(reflect.TypeOf(cpuTimes{}).Size()) + } + + ncpus := len(buf) / cpuTimesSize + ret := make([]TimesStat, 0, ncpus) + for i := 0; i < ncpus; i++ { + times := (*cpuTimes)(unsafe.Pointer(&buf[i*cpuTimesSize])) + if *times == emptyTimes { + // CPU not present + continue + } + ret = append(ret, *timeStat(fmt.Sprintf("cpu%d", len(ret)), times)) + } + return ret, nil + } + + buf, err := unix.SysctlRaw("kern.cp_time") + if err != nil { + return nil, err + } + + times := (*cpuTimes)(unsafe.Pointer(&buf[0])) + return []TimesStat{*timeStat("cpu-total", times)}, nil +} + +// Returns only one InfoStat on DragonflyBSD. The information regarding core +// count, however is accurate and it is assumed that all InfoStat attributes +// are the same across CPUs. +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + const dmesgBoot = "/var/run/dmesg.boot" + + c, err := parseDmesgBoot(dmesgBoot) + if err != nil { + return nil, err + } + + var u32 uint32 + if u32, err = unix.SysctlUint32("hw.clockrate"); err != nil { + return nil, err + } + c.Mhz = float64(u32) + + var num int + var buf string + if buf, err = unix.Sysctl("hw.cpu_topology.tree"); err != nil { + return nil, err + } + num = strings.Count(buf, "CHIP") + c.Cores = int32(strings.Count(string(buf), "CORE") / num) + + if c.ModelName, err = unix.Sysctl("hw.model"); err != nil { + return nil, err + } + + ret := make([]InfoStat, num) + for i := 0; i < num; i++ { + ret[i] = c + } + + return ret, nil +} + +func parseDmesgBoot(fileName string) (InfoStat, error) { + c := InfoStat{} + lines, _ := common.ReadLines(fileName) + for _, line := range lines { + if matches := cpuEnd.FindStringSubmatch(line); matches != nil { + break + } else if matches := originMatch.FindStringSubmatch(line); matches != nil { + c.VendorID = matches[1] + t, err := strconv.ParseInt(matches[2], 10, 32) + if err != nil { + return c, fmt.Errorf("unable to parse DragonflyBSD CPU stepping information from %q: %v", line, err) + } + c.Stepping = int32(t) + } else if matches := featuresMatch.FindStringSubmatch(line); matches != nil { + for _, v := range strings.Split(matches[1], ",") { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } else if matches := featuresMatch2.FindStringSubmatch(line); matches != nil { + for _, v := range strings.Split(matches[1], ",") { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } + } + + return c, nil +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + return runtime.NumCPU(), nil +} diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_dragonfly_amd64.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_dragonfly_amd64.go new file mode 100644 index 00000000000..57e14528db1 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_dragonfly_amd64.go @@ -0,0 +1,9 @@ +package cpu + +type cpuTimes struct { + User uint64 + Nice uint64 + Sys uint64 + Intr uint64 + Idle uint64 +} diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_fallback.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_fallback.go new file mode 100644 index 00000000000..5551c49d1d8 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_fallback.go @@ -0,0 +1,30 @@ +// +build !darwin,!linux,!freebsd,!openbsd,!solaris,!windows,!dragonfly + +package cpu + +import ( + "context" + "runtime" + + "github.com/shirou/gopsutil/internal/common" +) + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + return []TimesStat{}, common.ErrNotImplementedError +} + +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + return []InfoStat{}, common.ErrNotImplementedError +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + return runtime.NumCPU(), nil +} diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd.go new file mode 100644 index 00000000000..57beffae117 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd.go @@ -0,0 +1,173 @@ +package cpu + +import ( + "context" + "fmt" + "os/exec" + "reflect" + "regexp" + "runtime" + "strconv" + "strings" + "unsafe" + + "github.com/shirou/gopsutil/internal/common" + "golang.org/x/sys/unix" +) + +var ClocksPerSec = float64(128) +var cpuMatch = regexp.MustCompile(`^CPU:`) +var originMatch = regexp.MustCompile(`Origin\s*=\s*"(.+)"\s+Id\s*=\s*(.+)\s+Family\s*=\s*(.+)\s+Model\s*=\s*(.+)\s+Stepping\s*=\s*(.+)`) +var featuresMatch = regexp.MustCompile(`Features=.+<(.+)>`) +var featuresMatch2 = regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`) +var cpuEnd = regexp.MustCompile(`^Trying to mount root`) +var cpuCores = regexp.MustCompile(`FreeBSD/SMP: (\d*) package\(s\) x (\d*) core\(s\)`) +var cpuTimesSize int +var emptyTimes cpuTimes + +func init() { + getconf, err := exec.LookPath("getconf") + if err != nil { + return + } + out, err := invoke.Command(getconf, "CLK_TCK") + // ignore errors + if err == nil { + i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) + if err == nil { + ClocksPerSec = float64(i) + } + } +} + +func timeStat(name string, t *cpuTimes) *TimesStat { + return &TimesStat{ + User: float64(t.User) / ClocksPerSec, + Nice: float64(t.Nice) / ClocksPerSec, + System: float64(t.Sys) / ClocksPerSec, + Idle: float64(t.Idle) / ClocksPerSec, + Irq: float64(t.Intr) / ClocksPerSec, + CPU: name, + } +} + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + if percpu { + buf, err := unix.SysctlRaw("kern.cp_times") + if err != nil { + return nil, err + } + + // We can't do this in init due to the conflict with cpu.init() + if cpuTimesSize == 0 { + cpuTimesSize = int(reflect.TypeOf(cpuTimes{}).Size()) + } + + ncpus := len(buf) / cpuTimesSize + ret := make([]TimesStat, 0, ncpus) + for i := 0; i < ncpus; i++ { + times := (*cpuTimes)(unsafe.Pointer(&buf[i*cpuTimesSize])) + if *times == emptyTimes { + // CPU not present + continue + } + ret = append(ret, *timeStat(fmt.Sprintf("cpu%d", len(ret)), times)) + } + return ret, nil + } + + buf, err := unix.SysctlRaw("kern.cp_time") + if err != nil { + return nil, err + } + + times := (*cpuTimes)(unsafe.Pointer(&buf[0])) + return []TimesStat{*timeStat("cpu-total", times)}, nil +} + +// Returns only one InfoStat on FreeBSD. The information regarding core +// count, however is accurate and it is assumed that all InfoStat attributes +// are the same across CPUs. +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + const dmesgBoot = "/var/run/dmesg.boot" + + c, num, err := parseDmesgBoot(dmesgBoot) + if err != nil { + return nil, err + } + + var u32 uint32 + if u32, err = unix.SysctlUint32("hw.clockrate"); err != nil { + return nil, err + } + c.Mhz = float64(u32) + + if u32, err = unix.SysctlUint32("hw.ncpu"); err != nil { + return nil, err + } + c.Cores = int32(u32) + + if c.ModelName, err = unix.Sysctl("hw.model"); err != nil { + return nil, err + } + + ret := make([]InfoStat, num) + for i := 0; i < num; i++ { + ret[i] = c + } + + return ret, nil +} + +func parseDmesgBoot(fileName string) (InfoStat, int, error) { + c := InfoStat{} + lines, _ := common.ReadLines(fileName) + cpuNum := 1 // default cpu num is 1 + for _, line := range lines { + if matches := cpuEnd.FindStringSubmatch(line); matches != nil { + break + } else if matches := originMatch.FindStringSubmatch(line); matches != nil { + c.VendorID = matches[1] + c.Family = matches[3] + c.Model = matches[4] + t, err := strconv.ParseInt(matches[5], 10, 32) + if err != nil { + return c, 0, fmt.Errorf("unable to parse FreeBSD CPU stepping information from %q: %v", line, err) + } + c.Stepping = int32(t) + } else if matches := featuresMatch.FindStringSubmatch(line); matches != nil { + for _, v := range strings.Split(matches[1], ",") { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } else if matches := featuresMatch2.FindStringSubmatch(line); matches != nil { + for _, v := range strings.Split(matches[1], ",") { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } else if matches := cpuCores.FindStringSubmatch(line); matches != nil { + t, err := strconv.ParseInt(matches[1], 10, 32) + if err != nil { + return c, 0, fmt.Errorf("unable to parse FreeBSD CPU Nums from %q: %v", line, err) + } + cpuNum = int(t) + t2, err := strconv.ParseInt(matches[2], 10, 32) + if err != nil { + return c, 0, fmt.Errorf("unable to parse FreeBSD CPU cores from %q: %v", line, err) + } + c.Cores = int32(t2) + } + } + + return c, cpuNum, nil +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + return runtime.NumCPU(), nil +} diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd_386.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd_386.go new file mode 100644 index 00000000000..8b7f4c321eb --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd_386.go @@ -0,0 +1,9 @@ +package cpu + +type cpuTimes struct { + User uint32 + Nice uint32 + Sys uint32 + Intr uint32 + Idle uint32 +} diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd_amd64.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd_amd64.go new file mode 100644 index 00000000000..57e14528db1 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd_amd64.go @@ -0,0 +1,9 @@ +package cpu + +type cpuTimes struct { + User uint64 + Nice uint64 + Sys uint64 + Intr uint64 + Idle uint64 +} diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd_arm.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd_arm.go new file mode 100644 index 00000000000..8b7f4c321eb --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd_arm.go @@ -0,0 +1,9 @@ +package cpu + +type cpuTimes struct { + User uint32 + Nice uint32 + Sys uint32 + Intr uint32 + Idle uint32 +} diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd_arm64.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd_arm64.go new file mode 100644 index 00000000000..57e14528db1 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd_arm64.go @@ -0,0 +1,9 @@ +package cpu + +type cpuTimes struct { + User uint64 + Nice uint64 + Sys uint64 + Intr uint64 + Idle uint64 +} diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_linux.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_linux.go new file mode 100644 index 00000000000..735bd29ed19 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_linux.go @@ -0,0 +1,352 @@ +// +build linux + +package cpu + +import ( + "context" + "errors" + "fmt" + "os/exec" + "strconv" + "strings" + + "github.com/shirou/gopsutil/internal/common" +) + +var ClocksPerSec = float64(100) + +func init() { + getconf, err := exec.LookPath("getconf") + if err != nil { + return + } + out, err := invoke.CommandWithContext(context.Background(), getconf, "CLK_TCK") + // ignore errors + if err == nil { + i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) + if err == nil { + ClocksPerSec = i + } + } +} + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + filename := common.HostProc("stat") + var lines = []string{} + if percpu { + statlines, err := common.ReadLines(filename) + if err != nil || len(statlines) < 2 { + return []TimesStat{}, nil + } + for _, line := range statlines[1:] { + if !strings.HasPrefix(line, "cpu") { + break + } + lines = append(lines, line) + } + } else { + lines, _ = common.ReadLinesOffsetN(filename, 0, 1) + } + + ret := make([]TimesStat, 0, len(lines)) + + for _, line := range lines { + ct, err := parseStatLine(line) + if err != nil { + continue + } + ret = append(ret, *ct) + + } + return ret, nil +} + +func sysCPUPath(cpu int32, relPath string) string { + return common.HostSys(fmt.Sprintf("devices/system/cpu/cpu%d", cpu), relPath) +} + +func finishCPUInfo(c *InfoStat) error { + var lines []string + var err error + var value float64 + + if len(c.CoreID) == 0 { + lines, err = common.ReadLines(sysCPUPath(c.CPU, "topology/core_id")) + if err == nil { + c.CoreID = lines[0] + } + } + + // override the value of c.Mhz with cpufreq/cpuinfo_max_freq regardless + // of the value from /proc/cpuinfo because we want to report the maximum + // clock-speed of the CPU for c.Mhz, matching the behaviour of Windows + lines, err = common.ReadLines(sysCPUPath(c.CPU, "cpufreq/cpuinfo_max_freq")) + // if we encounter errors below such as there are no cpuinfo_max_freq file, + // we just ignore. so let Mhz is 0. + if err != nil || len(lines) == 0 { + return nil + } + value, err = strconv.ParseFloat(lines[0], 64) + if err != nil { + return nil + } + c.Mhz = value / 1000.0 // value is in kHz + if c.Mhz > 9999 { + c.Mhz = c.Mhz / 1000.0 // value in Hz + } + return nil +} + +// CPUInfo on linux will return 1 item per physical thread. +// +// CPUs have three levels of counting: sockets, cores, threads. +// Cores with HyperThreading count as having 2 threads per core. +// Sockets often come with many physical CPU cores. +// For example a single socket board with two cores each with HT will +// return 4 CPUInfoStat structs on Linux and the "Cores" field set to 1. +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + filename := common.HostProc("cpuinfo") + lines, _ := common.ReadLines(filename) + + var ret []InfoStat + var processorName string + + c := InfoStat{CPU: -1, Cores: 1} + for _, line := range lines { + fields := strings.Split(line, ":") + if len(fields) < 2 { + continue + } + key := strings.TrimSpace(fields[0]) + value := strings.TrimSpace(fields[1]) + + switch key { + case "Processor": + processorName = value + case "processor": + if c.CPU >= 0 { + err := finishCPUInfo(&c) + if err != nil { + return ret, err + } + ret = append(ret, c) + } + c = InfoStat{Cores: 1, ModelName: processorName} + t, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return ret, err + } + c.CPU = int32(t) + case "vendorId", "vendor_id": + c.VendorID = value + case "cpu family": + c.Family = value + case "model": + c.Model = value + case "model name", "cpu": + c.ModelName = value + if strings.Contains(value, "POWER8") || + strings.Contains(value, "POWER7") { + c.Model = strings.Split(value, " ")[0] + c.Family = "POWER" + c.VendorID = "IBM" + } + case "stepping", "revision": + val := value + + if key == "revision" { + val = strings.Split(value, ".")[0] + } + + t, err := strconv.ParseInt(val, 10, 64) + if err != nil { + return ret, err + } + c.Stepping = int32(t) + case "cpu MHz", "clock": + // treat this as the fallback value, thus we ignore error + if t, err := strconv.ParseFloat(strings.Replace(value, "MHz", "", 1), 64); err == nil { + c.Mhz = t + } + case "cache size": + t, err := strconv.ParseInt(strings.Replace(value, " KB", "", 1), 10, 64) + if err != nil { + return ret, err + } + c.CacheSize = int32(t) + case "physical id": + c.PhysicalID = value + case "core id": + c.CoreID = value + case "flags", "Features": + c.Flags = strings.FieldsFunc(value, func(r rune) bool { + return r == ',' || r == ' ' + }) + case "microcode": + c.Microcode = value + } + } + if c.CPU >= 0 { + err := finishCPUInfo(&c) + if err != nil { + return ret, err + } + ret = append(ret, c) + } + return ret, nil +} + +func parseStatLine(line string) (*TimesStat, error) { + fields := strings.Fields(line) + + if len(fields) == 0 { + return nil, errors.New("stat does not contain cpu info") + } + + if strings.HasPrefix(fields[0], "cpu") == false { + return nil, errors.New("not contain cpu") + } + + cpu := fields[0] + if cpu == "cpu" { + cpu = "cpu-total" + } + user, err := strconv.ParseFloat(fields[1], 64) + if err != nil { + return nil, err + } + nice, err := strconv.ParseFloat(fields[2], 64) + if err != nil { + return nil, err + } + system, err := strconv.ParseFloat(fields[3], 64) + if err != nil { + return nil, err + } + idle, err := strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, err + } + iowait, err := strconv.ParseFloat(fields[5], 64) + if err != nil { + return nil, err + } + irq, err := strconv.ParseFloat(fields[6], 64) + if err != nil { + return nil, err + } + softirq, err := strconv.ParseFloat(fields[7], 64) + if err != nil { + return nil, err + } + + ct := &TimesStat{ + CPU: cpu, + User: user / ClocksPerSec, + Nice: nice / ClocksPerSec, + System: system / ClocksPerSec, + Idle: idle / ClocksPerSec, + Iowait: iowait / ClocksPerSec, + Irq: irq / ClocksPerSec, + Softirq: softirq / ClocksPerSec, + } + if len(fields) > 8 { // Linux >= 2.6.11 + steal, err := strconv.ParseFloat(fields[8], 64) + if err != nil { + return nil, err + } + ct.Steal = steal / ClocksPerSec + } + if len(fields) > 9 { // Linux >= 2.6.24 + guest, err := strconv.ParseFloat(fields[9], 64) + if err != nil { + return nil, err + } + ct.Guest = guest / ClocksPerSec + } + if len(fields) > 10 { // Linux >= 3.2.0 + guestNice, err := strconv.ParseFloat(fields[10], 64) + if err != nil { + return nil, err + } + ct.GuestNice = guestNice / ClocksPerSec + } + + return ct, nil +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + if logical { + ret := 0 + // https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_pslinux.py#L599 + procCpuinfo := common.HostProc("cpuinfo") + lines, err := common.ReadLines(procCpuinfo) + if err == nil { + for _, line := range lines { + line = strings.ToLower(line) + if strings.HasPrefix(line, "processor") { + ret++ + } + } + } + if ret == 0 { + procStat := common.HostProc("stat") + lines, err = common.ReadLines(procStat) + if err != nil { + return 0, err + } + for _, line := range lines { + if len(line) >= 4 && strings.HasPrefix(line, "cpu") && '0' <= line[3] && line[3] <= '9' { // `^cpu\d` regexp matching + ret++ + } + } + } + return ret, nil + } + // physical cores https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_pslinux.py#L628 + filename := common.HostProc("cpuinfo") + lines, err := common.ReadLines(filename) + if err != nil { + return 0, err + } + mapping := make(map[int]int) + currentInfo := make(map[string]int) + for _, line := range lines { + line = strings.ToLower(strings.TrimSpace(line)) + if line == "" { + // new section + id, okID := currentInfo["physical id"] + cores, okCores := currentInfo["cpu cores"] + if okID && okCores { + mapping[id] = cores + } + currentInfo = make(map[string]int) + continue + } + fields := strings.Split(line, ":") + if len(fields) < 2 { + continue + } + fields[0] = strings.TrimSpace(fields[0]) + if fields[0] == "physical id" || fields[0] == "cpu cores" { + val, err := strconv.Atoi(strings.TrimSpace(fields[1])) + if err != nil { + continue + } + currentInfo[fields[0]] = val + } + } + ret := 0 + for _, v := range mapping { + ret += v + } + return ret, nil +} diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_openbsd.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_openbsd.go new file mode 100644 index 00000000000..92a8bd75c9a --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_openbsd.go @@ -0,0 +1,195 @@ +// +build openbsd + +package cpu + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "os/exec" + "runtime" + "strconv" + "strings" + "syscall" + + "github.com/shirou/gopsutil/internal/common" + "golang.org/x/sys/unix" +) + +// sys/sched.h +var ( + CPUser = 0 + CPNice = 1 + CPSys = 2 + CPIntr = 3 + CPIdle = 4 + CPUStates = 5 +) + +// sys/sysctl.h +const ( + CTLKern = 1 // "high kernel": proc, limits + CTLHw = 6 // CTL_HW + SMT = 24 // HW_SMT + NCpuOnline = 25 // HW_NCPUONLINE + KernCptime = 40 // KERN_CPTIME + KernCptime2 = 71 // KERN_CPTIME2 +) + +var ClocksPerSec = float64(128) + +func init() { + func() { + getconf, err := exec.LookPath("getconf") + if err != nil { + return + } + out, err := invoke.Command(getconf, "CLK_TCK") + // ignore errors + if err == nil { + i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) + if err == nil { + ClocksPerSec = float64(i) + } + } + }() + func() { + v, err := unix.Sysctl("kern.osrelease") // can't reuse host.PlatformInformation because of circular import + if err != nil { + return + } + v = strings.ToLower(v) + version, err := strconv.ParseFloat(v, 64) + if err != nil { + return + } + if version >= 6.4 { + CPIntr = 4 + CPIdle = 5 + CPUStates = 6 + } + }() +} + +func smt() (bool, error) { + mib := []int32{CTLHw, SMT} + buf, _, err := common.CallSyscall(mib) + if err != nil { + return false, err + } + + var ret bool + br := bytes.NewReader(buf) + if err := binary.Read(br, binary.LittleEndian, &ret); err != nil { + return false, err + } + + return ret, nil +} + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + var ret []TimesStat + + var ncpu int + if percpu { + ncpu, _ = Counts(true) + } else { + ncpu = 1 + } + + smt, err := smt() + if err == syscall.EOPNOTSUPP { + // if hw.smt is not applicable for this platform (e.g. i386), + // pretend it's enabled + smt = true + } else if err != nil { + return nil, err + } + + for i := 0; i < ncpu; i++ { + j := i + if !smt { + j *= 2 + } + + var cpuTimes = make([]int32, CPUStates) + var mib []int32 + if percpu { + mib = []int32{CTLKern, KernCptime2, int32(j)} + } else { + mib = []int32{CTLKern, KernCptime} + } + buf, _, err := common.CallSyscall(mib) + if err != nil { + return ret, err + } + + br := bytes.NewReader(buf) + err = binary.Read(br, binary.LittleEndian, &cpuTimes) + if err != nil { + return ret, err + } + c := TimesStat{ + User: float64(cpuTimes[CPUser]) / ClocksPerSec, + Nice: float64(cpuTimes[CPNice]) / ClocksPerSec, + System: float64(cpuTimes[CPSys]) / ClocksPerSec, + Idle: float64(cpuTimes[CPIdle]) / ClocksPerSec, + Irq: float64(cpuTimes[CPIntr]) / ClocksPerSec, + } + if percpu { + c.CPU = fmt.Sprintf("cpu%d", j) + } else { + c.CPU = "cpu-total" + } + ret = append(ret, c) + } + + return ret, nil +} + +// Returns only one (minimal) CPUInfoStat on OpenBSD +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + var ret []InfoStat + var err error + + c := InfoStat{} + + var u32 uint32 + if u32, err = unix.SysctlUint32("hw.cpuspeed"); err != nil { + return nil, err + } + c.Mhz = float64(u32) + + mib := []int32{CTLHw, NCpuOnline} + buf, _, err := common.CallSyscall(mib) + if err != nil { + return nil, err + } + + var ncpu int32 + br := bytes.NewReader(buf) + err = binary.Read(br, binary.LittleEndian, &ncpu) + if err != nil { + return nil, err + } + c.Cores = ncpu + + if c.ModelName, err = unix.Sysctl("hw.model"); err != nil { + return nil, err + } + + return append(ret, c), nil +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + return runtime.NumCPU(), nil +} diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_solaris.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_solaris.go new file mode 100644 index 00000000000..3de09842402 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_solaris.go @@ -0,0 +1,286 @@ +package cpu + +import ( + "context" + "errors" + "fmt" + "os/exec" + "regexp" + "runtime" + "sort" + "strconv" + "strings" +) + +var ClocksPerSec = float64(128) + +func init() { + getconf, err := exec.LookPath("getconf") + if err != nil { + return + } + out, err := invoke.Command(getconf, "CLK_TCK") + // ignore errors + if err == nil { + i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) + if err == nil { + ClocksPerSec = float64(i) + } + } +} + +//sum all values in a float64 map with float64 keys +func msum(x map[float64]float64) float64 { + total := 0.0 + for _, y := range x { + total += y + } + return total +} + +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + kstatSys, err := exec.LookPath("kstat") + if err != nil { + return nil, fmt.Errorf("cannot find kstat: %s", err) + } + cpu := make(map[float64]float64) + idle := make(map[float64]float64) + user := make(map[float64]float64) + kern := make(map[float64]float64) + iowt := make(map[float64]float64) + //swap := make(map[float64]float64) + kstatSysOut, err := invoke.CommandWithContext(ctx, kstatSys, "-p", "cpu_stat:*:*:/^idle$|^user$|^kernel$|^iowait$|^swap$/") + if err != nil { + return nil, fmt.Errorf("cannot execute kstat: %s", err) + } + re := regexp.MustCompile(`[:\s]+`) + for _, line := range strings.Split(string(kstatSysOut), "\n") { + fields := re.Split(line, -1) + if fields[0] != "cpu_stat" { + continue + } + cpuNumber, err := strconv.ParseFloat(fields[1], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse cpu number: %s", err) + } + cpu[cpuNumber] = cpuNumber + switch fields[3] { + case "idle": + idle[cpuNumber], err = strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse idle: %s", err) + } + case "user": + user[cpuNumber], err = strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse user: %s", err) + } + case "kernel": + kern[cpuNumber], err = strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse kernel: %s", err) + } + case "iowait": + iowt[cpuNumber], err = strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse iowait: %s", err) + } + //not sure how this translates, don't report, add to kernel, something else? + /*case "swap": + swap[cpuNumber], err = strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse swap: %s", err) + } */ + } + } + ret := make([]TimesStat, 0, len(cpu)) + if percpu { + for _, c := range cpu { + ct := &TimesStat{ + CPU: fmt.Sprintf("cpu%d", int(cpu[c])), + Idle: idle[c] / ClocksPerSec, + User: user[c] / ClocksPerSec, + System: kern[c] / ClocksPerSec, + Iowait: iowt[c] / ClocksPerSec, + } + ret = append(ret, *ct) + } + } else { + ct := &TimesStat{ + CPU: "cpu-total", + Idle: msum(idle) / ClocksPerSec, + User: msum(user) / ClocksPerSec, + System: msum(kern) / ClocksPerSec, + Iowait: msum(iowt) / ClocksPerSec, + } + ret = append(ret, *ct) + } + return ret, nil +} + +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + psrInfo, err := exec.LookPath("psrinfo") + if err != nil { + return nil, fmt.Errorf("cannot find psrinfo: %s", err) + } + psrInfoOut, err := invoke.CommandWithContext(ctx, psrInfo, "-p", "-v") + if err != nil { + return nil, fmt.Errorf("cannot execute psrinfo: %s", err) + } + + isaInfo, err := exec.LookPath("isainfo") + if err != nil { + return nil, fmt.Errorf("cannot find isainfo: %s", err) + } + isaInfoOut, err := invoke.CommandWithContext(ctx, isaInfo, "-b", "-v") + if err != nil { + return nil, fmt.Errorf("cannot execute isainfo: %s", err) + } + + procs, err := parseProcessorInfo(string(psrInfoOut)) + if err != nil { + return nil, fmt.Errorf("error parsing psrinfo output: %s", err) + } + + flags, err := parseISAInfo(string(isaInfoOut)) + if err != nil { + return nil, fmt.Errorf("error parsing isainfo output: %s", err) + } + + result := make([]InfoStat, 0, len(flags)) + for _, proc := range procs { + procWithFlags := proc + procWithFlags.Flags = flags + result = append(result, procWithFlags) + } + + return result, nil +} + +var flagsMatch = regexp.MustCompile(`[\w\.]+`) + +func parseISAInfo(cmdOutput string) ([]string, error) { + words := flagsMatch.FindAllString(cmdOutput, -1) + + // Sanity check the output + if len(words) < 4 || words[1] != "bit" || words[3] != "applications" { + return nil, errors.New("attempted to parse invalid isainfo output") + } + + flags := make([]string, len(words)-4) + for i, val := range words[4:] { + flags[i] = val + } + sort.Strings(flags) + + return flags, nil +} + +var psrInfoMatch = regexp.MustCompile(`The physical processor has (?:([\d]+) virtual processor \(([\d]+)\)|([\d]+) cores and ([\d]+) virtual processors[^\n]+)\n(?:\s+ The core has.+\n)*\s+.+ \((\w+) ([\S]+) family (.+) model (.+) step (.+) clock (.+) MHz\)\n[\s]*(.*)`) + +const ( + psrNumCoresOffset = 1 + psrNumCoresHTOffset = 3 + psrNumHTOffset = 4 + psrVendorIDOffset = 5 + psrFamilyOffset = 7 + psrModelOffset = 8 + psrStepOffset = 9 + psrClockOffset = 10 + psrModelNameOffset = 11 +) + +func parseProcessorInfo(cmdOutput string) ([]InfoStat, error) { + matches := psrInfoMatch.FindAllStringSubmatch(cmdOutput, -1) + + var infoStatCount int32 + result := make([]InfoStat, 0, len(matches)) + for physicalIndex, physicalCPU := range matches { + var step int32 + var clock float64 + + if physicalCPU[psrStepOffset] != "" { + stepParsed, err := strconv.ParseInt(physicalCPU[psrStepOffset], 10, 32) + if err != nil { + return nil, fmt.Errorf("cannot parse value %q for step as 32-bit integer: %s", physicalCPU[9], err) + } + step = int32(stepParsed) + } + + if physicalCPU[psrClockOffset] != "" { + clockParsed, err := strconv.ParseInt(physicalCPU[psrClockOffset], 10, 64) + if err != nil { + return nil, fmt.Errorf("cannot parse value %q for clock as 32-bit integer: %s", physicalCPU[10], err) + } + clock = float64(clockParsed) + } + + var err error + var numCores int64 + var numHT int64 + switch { + case physicalCPU[psrNumCoresOffset] != "": + numCores, err = strconv.ParseInt(physicalCPU[psrNumCoresOffset], 10, 32) + if err != nil { + return nil, fmt.Errorf("cannot parse value %q for core count as 32-bit integer: %s", physicalCPU[1], err) + } + + for i := 0; i < int(numCores); i++ { + result = append(result, InfoStat{ + CPU: infoStatCount, + PhysicalID: strconv.Itoa(physicalIndex), + CoreID: strconv.Itoa(i), + Cores: 1, + VendorID: physicalCPU[psrVendorIDOffset], + ModelName: physicalCPU[psrModelNameOffset], + Family: physicalCPU[psrFamilyOffset], + Model: physicalCPU[psrModelOffset], + Stepping: step, + Mhz: clock, + }) + infoStatCount++ + } + case physicalCPU[psrNumCoresHTOffset] != "": + numCores, err = strconv.ParseInt(physicalCPU[psrNumCoresHTOffset], 10, 32) + if err != nil { + return nil, fmt.Errorf("cannot parse value %q for core count as 32-bit integer: %s", physicalCPU[3], err) + } + + numHT, err = strconv.ParseInt(physicalCPU[psrNumHTOffset], 10, 32) + if err != nil { + return nil, fmt.Errorf("cannot parse value %q for hyperthread count as 32-bit integer: %s", physicalCPU[4], err) + } + + for i := 0; i < int(numCores); i++ { + result = append(result, InfoStat{ + CPU: infoStatCount, + PhysicalID: strconv.Itoa(physicalIndex), + CoreID: strconv.Itoa(i), + Cores: int32(numHT) / int32(numCores), + VendorID: physicalCPU[psrVendorIDOffset], + ModelName: physicalCPU[psrModelNameOffset], + Family: physicalCPU[psrFamilyOffset], + Model: physicalCPU[psrModelOffset], + Stepping: step, + Mhz: clock, + }) + infoStatCount++ + } + default: + return nil, errors.New("values for cores with and without hyperthreading are both set") + } + } + return result, nil +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + return runtime.NumCPU(), nil +} diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_windows.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_windows.go new file mode 100644 index 00000000000..ad1750b5c1f --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_windows.go @@ -0,0 +1,255 @@ +// +build windows + +package cpu + +import ( + "context" + "fmt" + "unsafe" + + "github.com/StackExchange/wmi" + "github.com/shirou/gopsutil/internal/common" + "golang.org/x/sys/windows" +) + +var ( + procGetActiveProcessorCount = common.Modkernel32.NewProc("GetActiveProcessorCount") + procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo") +) + +type Win32_Processor struct { + LoadPercentage *uint16 + Family uint16 + Manufacturer string + Name string + NumberOfLogicalProcessors uint32 + NumberOfCores uint32 + ProcessorID *string + Stepping *string + MaxClockSpeed uint32 +} + +// SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION +// defined in windows api doc with the following +// https://docs.microsoft.com/en-us/windows/desktop/api/winternl/nf-winternl-ntquerysysteminformation#system_processor_performance_information +// additional fields documented here +// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/processor_performance.htm +type win32_SystemProcessorPerformanceInformation struct { + IdleTime int64 // idle time in 100ns (this is not a filetime). + KernelTime int64 // kernel time in 100ns. kernel time includes idle time. (this is not a filetime). + UserTime int64 // usertime in 100ns (this is not a filetime). + DpcTime int64 // dpc time in 100ns (this is not a filetime). + InterruptTime int64 // interrupt time in 100ns + InterruptCount uint32 +} + +// Win32_PerfFormattedData_PerfOS_System struct to have count of processes and processor queue length +type Win32_PerfFormattedData_PerfOS_System struct { + Processes uint32 + ProcessorQueueLength uint32 +} + +const ( + ClocksPerSec = 10000000.0 + + // systemProcessorPerformanceInformationClass information class to query with NTQuerySystemInformation + // https://processhacker.sourceforge.io/doc/ntexapi_8h.html#ad5d815b48e8f4da1ef2eb7a2f18a54e0 + win32_SystemProcessorPerformanceInformationClass = 8 + + // size of systemProcessorPerformanceInfoSize in memory + win32_SystemProcessorPerformanceInfoSize = uint32(unsafe.Sizeof(win32_SystemProcessorPerformanceInformation{})) +) + +// Times returns times stat per cpu and combined for all CPUs +func Times(percpu bool) ([]TimesStat, error) { + return TimesWithContext(context.Background(), percpu) +} + +func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + if percpu { + return perCPUTimes() + } + + var ret []TimesStat + var lpIdleTime common.FILETIME + var lpKernelTime common.FILETIME + var lpUserTime common.FILETIME + r, _, _ := common.ProcGetSystemTimes.Call( + uintptr(unsafe.Pointer(&lpIdleTime)), + uintptr(unsafe.Pointer(&lpKernelTime)), + uintptr(unsafe.Pointer(&lpUserTime))) + if r == 0 { + return ret, windows.GetLastError() + } + + LOT := float64(0.0000001) + HIT := (LOT * 4294967296.0) + idle := ((HIT * float64(lpIdleTime.DwHighDateTime)) + (LOT * float64(lpIdleTime.DwLowDateTime))) + user := ((HIT * float64(lpUserTime.DwHighDateTime)) + (LOT * float64(lpUserTime.DwLowDateTime))) + kernel := ((HIT * float64(lpKernelTime.DwHighDateTime)) + (LOT * float64(lpKernelTime.DwLowDateTime))) + system := (kernel - idle) + + ret = append(ret, TimesStat{ + CPU: "cpu-total", + Idle: float64(idle), + User: float64(user), + System: float64(system), + }) + return ret, nil +} + +func Info() ([]InfoStat, error) { + return InfoWithContext(context.Background()) +} + +func InfoWithContext(ctx context.Context) ([]InfoStat, error) { + var ret []InfoStat + var dst []Win32_Processor + q := wmi.CreateQuery(&dst, "") + if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil { + return ret, err + } + + var procID string + for i, l := range dst { + procID = "" + if l.ProcessorID != nil { + procID = *l.ProcessorID + } + + cpu := InfoStat{ + CPU: int32(i), + Family: fmt.Sprintf("%d", l.Family), + VendorID: l.Manufacturer, + ModelName: l.Name, + Cores: int32(l.NumberOfLogicalProcessors), + PhysicalID: procID, + Mhz: float64(l.MaxClockSpeed), + Flags: []string{}, + } + ret = append(ret, cpu) + } + + return ret, nil +} + +// ProcInfo returns processes count and processor queue length in the system. +// There is a single queue for processor even on multiprocessors systems. +func ProcInfo() ([]Win32_PerfFormattedData_PerfOS_System, error) { + return ProcInfoWithContext(context.Background()) +} + +func ProcInfoWithContext(ctx context.Context) ([]Win32_PerfFormattedData_PerfOS_System, error) { + var ret []Win32_PerfFormattedData_PerfOS_System + q := wmi.CreateQuery(&ret, "") + err := common.WMIQueryWithContext(ctx, q, &ret) + if err != nil { + return []Win32_PerfFormattedData_PerfOS_System{}, err + } + return ret, err +} + +// perCPUTimes returns times stat per cpu, per core and overall for all CPUs +func perCPUTimes() ([]TimesStat, error) { + var ret []TimesStat + stats, err := perfInfo() + if err != nil { + return nil, err + } + for core, v := range stats { + c := TimesStat{ + CPU: fmt.Sprintf("cpu%d", core), + User: float64(v.UserTime) / ClocksPerSec, + System: float64(v.KernelTime-v.IdleTime) / ClocksPerSec, + Idle: float64(v.IdleTime) / ClocksPerSec, + Irq: float64(v.InterruptTime) / ClocksPerSec, + } + ret = append(ret, c) + } + return ret, nil +} + +// makes call to Windows API function to retrieve performance information for each core +func perfInfo() ([]win32_SystemProcessorPerformanceInformation, error) { + // Make maxResults large for safety. + // We can't invoke the api call with a results array that's too small. + // If we have more than 2056 cores on a single host, then it's probably the future. + maxBuffer := 2056 + // buffer for results from the windows proc + resultBuffer := make([]win32_SystemProcessorPerformanceInformation, maxBuffer) + // size of the buffer in memory + bufferSize := uintptr(win32_SystemProcessorPerformanceInfoSize) * uintptr(maxBuffer) + // size of the returned response + var retSize uint32 + + // Invoke windows api proc. + // The returned err from the windows dll proc will always be non-nil even when successful. + // See https://godoc.org/golang.org/x/sys/windows#LazyProc.Call for more information + retCode, _, err := common.ProcNtQuerySystemInformation.Call( + win32_SystemProcessorPerformanceInformationClass, // System Information Class -> SystemProcessorPerformanceInformation + uintptr(unsafe.Pointer(&resultBuffer[0])), // pointer to first element in result buffer + bufferSize, // size of the buffer in memory + uintptr(unsafe.Pointer(&retSize)), // pointer to the size of the returned results the windows proc will set this + ) + + // check return code for errors + if retCode != 0 { + return nil, fmt.Errorf("call to NtQuerySystemInformation returned %d. err: %s", retCode, err.Error()) + } + + // calculate the number of returned elements based on the returned size + numReturnedElements := retSize / win32_SystemProcessorPerformanceInfoSize + + // trim results to the number of returned elements + resultBuffer = resultBuffer[:numReturnedElements] + + return resultBuffer, nil +} + +// SystemInfo is an equivalent representation of SYSTEM_INFO in the Windows API. +// https://msdn.microsoft.com/en-us/library/ms724958%28VS.85%29.aspx?f=255&MSPPError=-2147217396 +// https://github.com/elastic/go-windows/blob/bb1581babc04d5cb29a2bfa7a9ac6781c730c8dd/kernel32.go#L43 +type systemInfo struct { + wProcessorArchitecture uint16 + wReserved uint16 + dwPageSize uint32 + lpMinimumApplicationAddress uintptr + lpMaximumApplicationAddress uintptr + dwActiveProcessorMask uintptr + dwNumberOfProcessors uint32 + dwProcessorType uint32 + dwAllocationGranularity uint32 + wProcessorLevel uint16 + wProcessorRevision uint16 +} + +func CountsWithContext(ctx context.Context, logical bool) (int, error) { + if logical { + // https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L97 + err := procGetActiveProcessorCount.Find() + if err == nil { // Win7+ + ret, _, _ := procGetActiveProcessorCount.Call(uintptr(0xffff)) // ALL_PROCESSOR_GROUPS is 0xffff according to Rust's winapi lib https://docs.rs/winapi/*/x86_64-pc-windows-msvc/src/winapi/shared/ntdef.rs.html#120 + if ret != 0 { + return int(ret), nil + } + } + var systemInfo systemInfo + _, _, err = procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo))) + if systemInfo.dwNumberOfProcessors == 0 { + return 0, err + } + return int(systemInfo.dwNumberOfProcessors), nil + } + // physical cores https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L499 + // for the time being, try with unreliable and slow WMI call… + var dst []Win32_Processor + q := wmi.CreateQuery(&dst, "") + if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil { + return 0, err + } + var count uint32 + for _, d := range dst { + count += d.NumberOfCores + } + return int(count), nil +} diff --git a/vendor/github.com/shirou/gopsutil/internal/common/binary.go b/vendor/github.com/shirou/gopsutil/internal/common/binary.go new file mode 100644 index 00000000000..9b5dc55b49a --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/internal/common/binary.go @@ -0,0 +1,634 @@ +package common + +// Copyright 2009 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 binary implements simple translation between numbers and byte +// sequences and encoding and decoding of varints. +// +// Numbers are translated by reading and writing fixed-size values. +// A fixed-size value is either a fixed-size arithmetic +// type (int8, uint8, int16, float32, complex64, ...) +// or an array or struct containing only fixed-size values. +// +// The varint functions encode and decode single integer values using +// a variable-length encoding; smaller values require fewer bytes. +// For a specification, see +// http://code.google.com/apis/protocolbuffers/docs/encoding.html. +// +// This package favors simplicity over efficiency. Clients that require +// high-performance serialization, especially for large data structures, +// should look at more advanced solutions such as the encoding/gob +// package or protocol buffers. +import ( + "errors" + "io" + "math" + "reflect" +) + +// A ByteOrder specifies how to convert byte sequences into +// 16-, 32-, or 64-bit unsigned integers. +type ByteOrder interface { + Uint16([]byte) uint16 + Uint32([]byte) uint32 + Uint64([]byte) uint64 + PutUint16([]byte, uint16) + PutUint32([]byte, uint32) + PutUint64([]byte, uint64) + String() string +} + +// LittleEndian is the little-endian implementation of ByteOrder. +var LittleEndian littleEndian + +// BigEndian is the big-endian implementation of ByteOrder. +var BigEndian bigEndian + +type littleEndian struct{} + +func (littleEndian) Uint16(b []byte) uint16 { return uint16(b[0]) | uint16(b[1])<<8 } + +func (littleEndian) PutUint16(b []byte, v uint16) { + b[0] = byte(v) + b[1] = byte(v >> 8) +} + +func (littleEndian) Uint32(b []byte) uint32 { + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func (littleEndian) PutUint32(b []byte, v uint32) { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) +} + +func (littleEndian) Uint64(b []byte) uint64 { + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 +} + +func (littleEndian) PutUint64(b []byte, v uint64) { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) +} + +func (littleEndian) String() string { return "LittleEndian" } + +func (littleEndian) GoString() string { return "binary.LittleEndian" } + +type bigEndian struct{} + +func (bigEndian) Uint16(b []byte) uint16 { return uint16(b[1]) | uint16(b[0])<<8 } + +func (bigEndian) PutUint16(b []byte, v uint16) { + b[0] = byte(v >> 8) + b[1] = byte(v) +} + +func (bigEndian) Uint32(b []byte) uint32 { + return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 +} + +func (bigEndian) PutUint32(b []byte, v uint32) { + b[0] = byte(v >> 24) + b[1] = byte(v >> 16) + b[2] = byte(v >> 8) + b[3] = byte(v) +} + +func (bigEndian) Uint64(b []byte) uint64 { + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 +} + +func (bigEndian) PutUint64(b []byte, v uint64) { + b[0] = byte(v >> 56) + b[1] = byte(v >> 48) + b[2] = byte(v >> 40) + b[3] = byte(v >> 32) + b[4] = byte(v >> 24) + b[5] = byte(v >> 16) + b[6] = byte(v >> 8) + b[7] = byte(v) +} + +func (bigEndian) String() string { return "BigEndian" } + +func (bigEndian) GoString() string { return "binary.BigEndian" } + +// Read reads structured binary data from r into data. +// Data must be a pointer to a fixed-size value or a slice +// of fixed-size values. +// Bytes read from r are decoded using the specified byte order +// and written to successive fields of the data. +// When reading into structs, the field data for fields with +// blank (_) field names is skipped; i.e., blank field names +// may be used for padding. +// When reading into a struct, all non-blank fields must be exported. +func Read(r io.Reader, order ByteOrder, data interface{}) error { + // Fast path for basic types and slices. + if n := intDataSize(data); n != 0 { + var b [8]byte + var bs []byte + if n > len(b) { + bs = make([]byte, n) + } else { + bs = b[:n] + } + if _, err := io.ReadFull(r, bs); err != nil { + return err + } + switch data := data.(type) { + case *int8: + *data = int8(b[0]) + case *uint8: + *data = b[0] + case *int16: + *data = int16(order.Uint16(bs)) + case *uint16: + *data = order.Uint16(bs) + case *int32: + *data = int32(order.Uint32(bs)) + case *uint32: + *data = order.Uint32(bs) + case *int64: + *data = int64(order.Uint64(bs)) + case *uint64: + *data = order.Uint64(bs) + case []int8: + for i, x := range bs { // Easier to loop over the input for 8-bit values. + data[i] = int8(x) + } + case []uint8: + copy(data, bs) + case []int16: + for i := range data { + data[i] = int16(order.Uint16(bs[2*i:])) + } + case []uint16: + for i := range data { + data[i] = order.Uint16(bs[2*i:]) + } + case []int32: + for i := range data { + data[i] = int32(order.Uint32(bs[4*i:])) + } + case []uint32: + for i := range data { + data[i] = order.Uint32(bs[4*i:]) + } + case []int64: + for i := range data { + data[i] = int64(order.Uint64(bs[8*i:])) + } + case []uint64: + for i := range data { + data[i] = order.Uint64(bs[8*i:]) + } + } + return nil + } + + // Fallback to reflect-based decoding. + v := reflect.ValueOf(data) + size := -1 + switch v.Kind() { + case reflect.Ptr: + v = v.Elem() + size = dataSize(v) + case reflect.Slice: + size = dataSize(v) + } + if size < 0 { + return errors.New("binary.Read: invalid type " + reflect.TypeOf(data).String()) + } + d := &decoder{order: order, buf: make([]byte, size)} + if _, err := io.ReadFull(r, d.buf); err != nil { + return err + } + d.value(v) + return nil +} + +// Write writes the binary representation of data into w. +// Data must be a fixed-size value or a slice of fixed-size +// values, or a pointer to such data. +// Bytes written to w are encoded using the specified byte order +// and read from successive fields of the data. +// When writing structs, zero values are written for fields +// with blank (_) field names. +func Write(w io.Writer, order ByteOrder, data interface{}) error { + // Fast path for basic types and slices. + if n := intDataSize(data); n != 0 { + var b [8]byte + var bs []byte + if n > len(b) { + bs = make([]byte, n) + } else { + bs = b[:n] + } + switch v := data.(type) { + case *int8: + bs = b[:1] + b[0] = byte(*v) + case int8: + bs = b[:1] + b[0] = byte(v) + case []int8: + for i, x := range v { + bs[i] = byte(x) + } + case *uint8: + bs = b[:1] + b[0] = *v + case uint8: + bs = b[:1] + b[0] = byte(v) + case []uint8: + bs = v + case *int16: + bs = b[:2] + order.PutUint16(bs, uint16(*v)) + case int16: + bs = b[:2] + order.PutUint16(bs, uint16(v)) + case []int16: + for i, x := range v { + order.PutUint16(bs[2*i:], uint16(x)) + } + case *uint16: + bs = b[:2] + order.PutUint16(bs, *v) + case uint16: + bs = b[:2] + order.PutUint16(bs, v) + case []uint16: + for i, x := range v { + order.PutUint16(bs[2*i:], x) + } + case *int32: + bs = b[:4] + order.PutUint32(bs, uint32(*v)) + case int32: + bs = b[:4] + order.PutUint32(bs, uint32(v)) + case []int32: + for i, x := range v { + order.PutUint32(bs[4*i:], uint32(x)) + } + case *uint32: + bs = b[:4] + order.PutUint32(bs, *v) + case uint32: + bs = b[:4] + order.PutUint32(bs, v) + case []uint32: + for i, x := range v { + order.PutUint32(bs[4*i:], x) + } + case *int64: + bs = b[:8] + order.PutUint64(bs, uint64(*v)) + case int64: + bs = b[:8] + order.PutUint64(bs, uint64(v)) + case []int64: + for i, x := range v { + order.PutUint64(bs[8*i:], uint64(x)) + } + case *uint64: + bs = b[:8] + order.PutUint64(bs, *v) + case uint64: + bs = b[:8] + order.PutUint64(bs, v) + case []uint64: + for i, x := range v { + order.PutUint64(bs[8*i:], x) + } + } + _, err := w.Write(bs) + return err + } + + // Fallback to reflect-based encoding. + v := reflect.Indirect(reflect.ValueOf(data)) + size := dataSize(v) + if size < 0 { + return errors.New("binary.Write: invalid type " + reflect.TypeOf(data).String()) + } + buf := make([]byte, size) + e := &encoder{order: order, buf: buf} + e.value(v) + _, err := w.Write(buf) + return err +} + +// Size returns how many bytes Write would generate to encode the value v, which +// must be a fixed-size value or a slice of fixed-size values, or a pointer to such data. +// If v is neither of these, Size returns -1. +func Size(v interface{}) int { + return dataSize(reflect.Indirect(reflect.ValueOf(v))) +} + +// dataSize returns the number of bytes the actual data represented by v occupies in memory. +// For compound structures, it sums the sizes of the elements. Thus, for instance, for a slice +// it returns the length of the slice times the element size and does not count the memory +// occupied by the header. If the type of v is not acceptable, dataSize returns -1. +func dataSize(v reflect.Value) int { + if v.Kind() == reflect.Slice { + if s := sizeof(v.Type().Elem()); s >= 0 { + return s * v.Len() + } + return -1 + } + return sizeof(v.Type()) +} + +// sizeof returns the size >= 0 of variables for the given type or -1 if the type is not acceptable. +func sizeof(t reflect.Type) int { + switch t.Kind() { + case reflect.Array: + if s := sizeof(t.Elem()); s >= 0 { + return s * t.Len() + } + + case reflect.Struct: + sum := 0 + for i, n := 0, t.NumField(); i < n; i++ { + s := sizeof(t.Field(i).Type) + if s < 0 { + return -1 + } + sum += s + } + return sum + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.Ptr: + return int(t.Size()) + } + + return -1 +} + +type coder struct { + order ByteOrder + buf []byte +} + +type decoder coder +type encoder coder + +func (d *decoder) uint8() uint8 { + x := d.buf[0] + d.buf = d.buf[1:] + return x +} + +func (e *encoder) uint8(x uint8) { + e.buf[0] = x + e.buf = e.buf[1:] +} + +func (d *decoder) uint16() uint16 { + x := d.order.Uint16(d.buf[0:2]) + d.buf = d.buf[2:] + return x +} + +func (e *encoder) uint16(x uint16) { + e.order.PutUint16(e.buf[0:2], x) + e.buf = e.buf[2:] +} + +func (d *decoder) uint32() uint32 { + x := d.order.Uint32(d.buf[0:4]) + d.buf = d.buf[4:] + return x +} + +func (e *encoder) uint32(x uint32) { + e.order.PutUint32(e.buf[0:4], x) + e.buf = e.buf[4:] +} + +func (d *decoder) uint64() uint64 { + x := d.order.Uint64(d.buf[0:8]) + d.buf = d.buf[8:] + return x +} + +func (e *encoder) uint64(x uint64) { + e.order.PutUint64(e.buf[0:8], x) + e.buf = e.buf[8:] +} + +func (d *decoder) int8() int8 { return int8(d.uint8()) } + +func (e *encoder) int8(x int8) { e.uint8(uint8(x)) } + +func (d *decoder) int16() int16 { return int16(d.uint16()) } + +func (e *encoder) int16(x int16) { e.uint16(uint16(x)) } + +func (d *decoder) int32() int32 { return int32(d.uint32()) } + +func (e *encoder) int32(x int32) { e.uint32(uint32(x)) } + +func (d *decoder) int64() int64 { return int64(d.uint64()) } + +func (e *encoder) int64(x int64) { e.uint64(uint64(x)) } + +func (d *decoder) value(v reflect.Value) { + switch v.Kind() { + case reflect.Array: + l := v.Len() + for i := 0; i < l; i++ { + d.value(v.Index(i)) + } + + case reflect.Struct: + t := v.Type() + l := v.NumField() + for i := 0; i < l; i++ { + // Note: Calling v.CanSet() below is an optimization. + // It would be sufficient to check the field name, + // but creating the StructField info for each field is + // costly (run "go test -bench=ReadStruct" and compare + // results when making changes to this code). + if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { + d.value(v) + } else { + d.skip(v) + } + } + + case reflect.Slice: + l := v.Len() + for i := 0; i < l; i++ { + d.value(v.Index(i)) + } + + case reflect.Int8: + v.SetInt(int64(d.int8())) + case reflect.Int16: + v.SetInt(int64(d.int16())) + case reflect.Int32: + v.SetInt(int64(d.int32())) + case reflect.Int64: + v.SetInt(d.int64()) + + case reflect.Uint8: + v.SetUint(uint64(d.uint8())) + case reflect.Uint16: + v.SetUint(uint64(d.uint16())) + case reflect.Uint32: + v.SetUint(uint64(d.uint32())) + case reflect.Uint64: + v.SetUint(d.uint64()) + + case reflect.Float32: + v.SetFloat(float64(math.Float32frombits(d.uint32()))) + case reflect.Float64: + v.SetFloat(math.Float64frombits(d.uint64())) + + case reflect.Complex64: + v.SetComplex(complex( + float64(math.Float32frombits(d.uint32())), + float64(math.Float32frombits(d.uint32())), + )) + case reflect.Complex128: + v.SetComplex(complex( + math.Float64frombits(d.uint64()), + math.Float64frombits(d.uint64()), + )) + } +} + +func (e *encoder) value(v reflect.Value) { + switch v.Kind() { + case reflect.Array: + l := v.Len() + for i := 0; i < l; i++ { + e.value(v.Index(i)) + } + + case reflect.Struct: + t := v.Type() + l := v.NumField() + for i := 0; i < l; i++ { + // see comment for corresponding code in decoder.value() + if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { + e.value(v) + } else { + e.skip(v) + } + } + + case reflect.Slice: + l := v.Len() + for i := 0; i < l; i++ { + e.value(v.Index(i)) + } + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch v.Type().Kind() { + case reflect.Int8: + e.int8(int8(v.Int())) + case reflect.Int16: + e.int16(int16(v.Int())) + case reflect.Int32: + e.int32(int32(v.Int())) + case reflect.Int64: + e.int64(v.Int()) + } + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch v.Type().Kind() { + case reflect.Uint8: + e.uint8(uint8(v.Uint())) + case reflect.Uint16: + e.uint16(uint16(v.Uint())) + case reflect.Uint32: + e.uint32(uint32(v.Uint())) + case reflect.Uint64: + e.uint64(v.Uint()) + } + + case reflect.Float32, reflect.Float64: + switch v.Type().Kind() { + case reflect.Float32: + e.uint32(math.Float32bits(float32(v.Float()))) + case reflect.Float64: + e.uint64(math.Float64bits(v.Float())) + } + + case reflect.Complex64, reflect.Complex128: + switch v.Type().Kind() { + case reflect.Complex64: + x := v.Complex() + e.uint32(math.Float32bits(float32(real(x)))) + e.uint32(math.Float32bits(float32(imag(x)))) + case reflect.Complex128: + x := v.Complex() + e.uint64(math.Float64bits(real(x))) + e.uint64(math.Float64bits(imag(x))) + } + } +} + +func (d *decoder) skip(v reflect.Value) { + d.buf = d.buf[dataSize(v):] +} + +func (e *encoder) skip(v reflect.Value) { + n := dataSize(v) + for i := range e.buf[0:n] { + e.buf[i] = 0 + } + e.buf = e.buf[n:] +} + +// intDataSize returns the size of the data required to represent the data when encoded. +// It returns zero if the type cannot be implemented by the fast path in Read or Write. +func intDataSize(data interface{}) int { + switch data := data.(type) { + case int8, *int8, *uint8: + return 1 + case []int8: + return len(data) + case []uint8: + return len(data) + case int16, *int16, *uint16: + return 2 + case []int16: + return 2 * len(data) + case []uint16: + return 2 * len(data) + case int32, *int32, *uint32: + return 4 + case []int32: + return 4 * len(data) + case []uint32: + return 4 * len(data) + case int64, *int64, *uint64: + return 8 + case []int64: + return 8 * len(data) + case []uint64: + return 8 * len(data) + } + return 0 +} diff --git a/vendor/github.com/shirou/gopsutil/internal/common/common.go b/vendor/github.com/shirou/gopsutil/internal/common/common.go new file mode 100644 index 00000000000..57e291b10bf --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/internal/common/common.go @@ -0,0 +1,370 @@ +package common + +// +// gopsutil is a port of psutil(http://pythonhosted.org/psutil/). +// This covers these architectures. +// - linux (amd64, arm) +// - freebsd (amd64) +// - windows (amd64) +import ( + "bufio" + "bytes" + "context" + "errors" + "fmt" + "io/ioutil" + "net/url" + "os" + "os/exec" + "path" + "path/filepath" + "reflect" + "runtime" + "strconv" + "strings" + "time" +) + +var ( + Timeout = 3 * time.Second + ErrTimeout = errors.New("command timed out") +) + +type Invoker interface { + Command(string, ...string) ([]byte, error) + CommandWithContext(context.Context, string, ...string) ([]byte, error) +} + +type Invoke struct{} + +func (i Invoke) Command(name string, arg ...string) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), Timeout) + defer cancel() + return i.CommandWithContext(ctx, name, arg...) +} + +func (i Invoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) { + cmd := exec.CommandContext(ctx, name, arg...) + + var buf bytes.Buffer + cmd.Stdout = &buf + cmd.Stderr = &buf + + if err := cmd.Start(); err != nil { + return buf.Bytes(), err + } + + if err := cmd.Wait(); err != nil { + return buf.Bytes(), err + } + + return buf.Bytes(), nil +} + +type FakeInvoke struct { + Suffix string // Suffix species expected file name suffix such as "fail" + Error error // If Error specfied, return the error. +} + +// Command in FakeInvoke returns from expected file if exists. +func (i FakeInvoke) Command(name string, arg ...string) ([]byte, error) { + if i.Error != nil { + return []byte{}, i.Error + } + + arch := runtime.GOOS + + commandName := filepath.Base(name) + + fname := strings.Join(append([]string{commandName}, arg...), "") + fname = url.QueryEscape(fname) + fpath := path.Join("testdata", arch, fname) + if i.Suffix != "" { + fpath += "_" + i.Suffix + } + if PathExists(fpath) { + return ioutil.ReadFile(fpath) + } + return []byte{}, fmt.Errorf("could not find testdata: %s", fpath) +} + +func (i FakeInvoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) { + return i.Command(name, arg...) +} + +var ErrNotImplementedError = errors.New("not implemented yet") + +// ReadFile reads contents from a file +func ReadFile(filename string) (string, error) { + content, err := ioutil.ReadFile(filename) + + if err != nil { + return "", err + } + + return string(content), nil +} + +// ReadLines reads contents from a file and splits them by new lines. +// A convenience wrapper to ReadLinesOffsetN(filename, 0, -1). +func ReadLines(filename string) ([]string, error) { + return ReadLinesOffsetN(filename, 0, -1) +} + +// ReadLines reads contents from file and splits them by new line. +// The offset tells at which line number to start. +// The count determines the number of lines to read (starting from offset): +// n >= 0: at most n lines +// n < 0: whole file +func ReadLinesOffsetN(filename string, offset uint, n int) ([]string, error) { + f, err := os.Open(filename) + if err != nil { + return []string{""}, err + } + defer f.Close() + + var ret []string + + r := bufio.NewReader(f) + for i := 0; i < n+int(offset) || n < 0; i++ { + line, err := r.ReadString('\n') + if err != nil { + break + } + if i < int(offset) { + continue + } + ret = append(ret, strings.Trim(line, "\n")) + } + + return ret, nil +} + +func IntToString(orig []int8) string { + ret := make([]byte, len(orig)) + size := -1 + for i, o := range orig { + if o == 0 { + size = i + break + } + ret[i] = byte(o) + } + if size == -1 { + size = len(orig) + } + + return string(ret[0:size]) +} + +func UintToString(orig []uint8) string { + ret := make([]byte, len(orig)) + size := -1 + for i, o := range orig { + if o == 0 { + size = i + break + } + ret[i] = byte(o) + } + if size == -1 { + size = len(orig) + } + + return string(ret[0:size]) +} + +func ByteToString(orig []byte) string { + n := -1 + l := -1 + for i, b := range orig { + // skip left side null + if l == -1 && b == 0 { + continue + } + if l == -1 { + l = i + } + + if b == 0 { + break + } + n = i + 1 + } + if n == -1 { + return string(orig) + } + return string(orig[l:n]) +} + +// ReadInts reads contents from single line file and returns them as []int32. +func ReadInts(filename string) ([]int64, error) { + f, err := os.Open(filename) + if err != nil { + return []int64{}, err + } + defer f.Close() + + var ret []int64 + + r := bufio.NewReader(f) + + // The int files that this is concerned with should only be one liners. + line, err := r.ReadString('\n') + if err != nil { + return []int64{}, err + } + + i, err := strconv.ParseInt(strings.Trim(line, "\n"), 10, 32) + if err != nil { + return []int64{}, err + } + ret = append(ret, i) + + return ret, nil +} + +// Parse Hex to uint32 without error +func HexToUint32(hex string) uint32 { + vv, _ := strconv.ParseUint(hex, 16, 32) + return uint32(vv) +} + +// Parse to int32 without error +func mustParseInt32(val string) int32 { + vv, _ := strconv.ParseInt(val, 10, 32) + return int32(vv) +} + +// Parse to uint64 without error +func mustParseUint64(val string) uint64 { + vv, _ := strconv.ParseInt(val, 10, 64) + return uint64(vv) +} + +// Parse to Float64 without error +func mustParseFloat64(val string) float64 { + vv, _ := strconv.ParseFloat(val, 64) + return vv +} + +// StringsHas checks the target string slice contains src or not +func StringsHas(target []string, src string) bool { + for _, t := range target { + if strings.TrimSpace(t) == src { + return true + } + } + return false +} + +// StringsContains checks the src in any string of the target string slice +func StringsContains(target []string, src string) bool { + for _, t := range target { + if strings.Contains(t, src) { + return true + } + } + return false +} + +// IntContains checks the src in any int of the target int slice. +func IntContains(target []int, src int) bool { + for _, t := range target { + if src == t { + return true + } + } + return false +} + +// get struct attributes. +// This method is used only for debugging platform dependent code. +func attributes(m interface{}) map[string]reflect.Type { + typ := reflect.TypeOf(m) + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + } + + attrs := make(map[string]reflect.Type) + if typ.Kind() != reflect.Struct { + return nil + } + + for i := 0; i < typ.NumField(); i++ { + p := typ.Field(i) + if !p.Anonymous { + attrs[p.Name] = p.Type + } + } + + return attrs +} + +func PathExists(filename string) bool { + if _, err := os.Stat(filename); err == nil { + return true + } + return false +} + +//GetEnv retrieves the environment variable key. If it does not exist it returns the default. +func GetEnv(key string, dfault string, combineWith ...string) string { + value := os.Getenv(key) + if value == "" { + value = dfault + } + + switch len(combineWith) { + case 0: + return value + case 1: + return filepath.Join(value, combineWith[0]) + default: + all := make([]string, len(combineWith)+1) + all[0] = value + copy(all[1:], combineWith) + return filepath.Join(all...) + } + panic("invalid switch case") +} + +func HostProc(combineWith ...string) string { + return GetEnv("HOST_PROC", "/proc", combineWith...) +} + +func HostSys(combineWith ...string) string { + return GetEnv("HOST_SYS", "/sys", combineWith...) +} + +func HostEtc(combineWith ...string) string { + return GetEnv("HOST_ETC", "/etc", combineWith...) +} + +func HostVar(combineWith ...string) string { + return GetEnv("HOST_VAR", "/var", combineWith...) +} + +func HostRun(combineWith ...string) string { + return GetEnv("HOST_RUN", "/run", combineWith...) +} + +func HostDev(combineWith ...string) string { + return GetEnv("HOST_DEV", "/dev", combineWith...) +} + +// getSysctrlEnv sets LC_ALL=C in a list of env vars for use when running +// sysctl commands (see DoSysctrl). +func getSysctrlEnv(env []string) []string { + foundLC := false + for i, line := range env { + if strings.HasPrefix(line, "LC_ALL") { + env[i] = "LC_ALL=C" + foundLC = true + } + } + if !foundLC { + env = append(env, "LC_ALL=C") + } + return env +} diff --git a/vendor/github.com/shirou/gopsutil/internal/common/common_darwin.go b/vendor/github.com/shirou/gopsutil/internal/common/common_darwin.go new file mode 100644 index 00000000000..dde5c39037a --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/internal/common/common_darwin.go @@ -0,0 +1,69 @@ +// +build darwin + +package common + +import ( + "context" + "os" + "os/exec" + "strings" + "unsafe" + + "golang.org/x/sys/unix" +) + +func DoSysctrlWithContext(ctx context.Context, mib string) ([]string, error) { + sysctl, err := exec.LookPath("sysctl") + if err != nil { + return []string{}, err + } + cmd := exec.CommandContext(ctx, sysctl, "-n", mib) + cmd.Env = getSysctrlEnv(os.Environ()) + out, err := cmd.Output() + if err != nil { + return []string{}, err + } + v := strings.Replace(string(out), "{ ", "", 1) + v = strings.Replace(string(v), " }", "", 1) + values := strings.Fields(string(v)) + + return values, nil +} + +func CallSyscall(mib []int32) ([]byte, uint64, error) { + miblen := uint64(len(mib)) + + // get required buffer size + length := uint64(0) + _, _, err := unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(miblen), + 0, + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + var b []byte + return b, length, err + } + if length == 0 { + var b []byte + return b, length, err + } + // get proc info itself + buf := make([]byte, length) + _, _, err = unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(miblen), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + return buf, length, err + } + + return buf, length, nil +} diff --git a/vendor/github.com/shirou/gopsutil/internal/common/common_freebsd.go b/vendor/github.com/shirou/gopsutil/internal/common/common_freebsd.go new file mode 100644 index 00000000000..85bda0e22cf --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/internal/common/common_freebsd.go @@ -0,0 +1,85 @@ +// +build freebsd openbsd + +package common + +import ( + "fmt" + "os" + "os/exec" + "strings" + "unsafe" + + "golang.org/x/sys/unix" +) + +func SysctlUint(mib string) (uint64, error) { + buf, err := unix.SysctlRaw(mib) + if err != nil { + return 0, err + } + if len(buf) == 8 { // 64 bit + return *(*uint64)(unsafe.Pointer(&buf[0])), nil + } + if len(buf) == 4 { // 32bit + t := *(*uint32)(unsafe.Pointer(&buf[0])) + return uint64(t), nil + } + return 0, fmt.Errorf("unexpected size: %s, %d", mib, len(buf)) +} + +func DoSysctrl(mib string) ([]string, error) { + sysctl, err := exec.LookPath("sysctl") + if err != nil { + return []string{}, err + } + cmd := exec.Command(sysctl, "-n", mib) + cmd.Env = getSysctrlEnv(os.Environ()) + out, err := cmd.Output() + if err != nil { + return []string{}, err + } + v := strings.Replace(string(out), "{ ", "", 1) + v = strings.Replace(string(v), " }", "", 1) + values := strings.Fields(string(v)) + + return values, nil +} + +func CallSyscall(mib []int32) ([]byte, uint64, error) { + mibptr := unsafe.Pointer(&mib[0]) + miblen := uint64(len(mib)) + + // get required buffer size + length := uint64(0) + _, _, err := unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(mibptr), + uintptr(miblen), + 0, + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + var b []byte + return b, length, err + } + if length == 0 { + var b []byte + return b, length, err + } + // get proc info itself + buf := make([]byte, length) + _, _, err = unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(mibptr), + uintptr(miblen), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + return buf, length, err + } + + return buf, length, nil +} diff --git a/vendor/github.com/shirou/gopsutil/internal/common/common_linux.go b/vendor/github.com/shirou/gopsutil/internal/common/common_linux.go new file mode 100644 index 00000000000..0f66f1c3893 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/internal/common/common_linux.go @@ -0,0 +1,265 @@ +// +build linux + +package common + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "time" +) + +func DoSysctrl(mib string) ([]string, error) { + sysctl, err := exec.LookPath("sysctl") + if err != nil { + return []string{}, err + } + cmd := exec.Command(sysctl, "-n", mib) + cmd.Env = getSysctrlEnv(os.Environ()) + out, err := cmd.Output() + if err != nil { + return []string{}, err + } + v := strings.Replace(string(out), "{ ", "", 1) + v = strings.Replace(string(v), " }", "", 1) + values := strings.Fields(string(v)) + + return values, nil +} + +func NumProcs() (uint64, error) { + f, err := os.Open(HostProc()) + if err != nil { + return 0, err + } + defer f.Close() + + list, err := f.Readdirnames(-1) + if err != nil { + return 0, err + } + var cnt uint64 + + for _, v := range list { + if _, err = strconv.ParseUint(v, 10, 64); err == nil { + cnt++ + } + } + + return cnt, nil +} + +func BootTimeWithContext(ctx context.Context) (uint64, error) { + + system, role, err := Virtualization() + if err != nil { + return 0, err + } + + statFile := "stat" + if system == "lxc" && role == "guest" { + // if lxc, /proc/uptime is used. + statFile = "uptime" + } else if system == "docker" && role == "guest" { + // also docker, guest + statFile = "uptime" + } + + filename := HostProc(statFile) + lines, err := ReadLines(filename) + if err != nil { + return 0, err + } + + if statFile == "stat" { + for _, line := range lines { + if strings.HasPrefix(line, "btime") { + f := strings.Fields(line) + if len(f) != 2 { + return 0, fmt.Errorf("wrong btime format") + } + b, err := strconv.ParseInt(f[1], 10, 64) + if err != nil { + return 0, err + } + t := uint64(b) + return t, nil + } + } + } else if statFile == "uptime" { + if len(lines) != 1 { + return 0, fmt.Errorf("wrong uptime format") + } + f := strings.Fields(lines[0]) + b, err := strconv.ParseFloat(f[0], 64) + if err != nil { + return 0, err + } + t := uint64(time.Now().Unix()) - uint64(b) + return t, nil + } + + return 0, fmt.Errorf("could not find btime") +} + +func Virtualization() (string, string, error) { + return VirtualizationWithContext(context.Background()) +} + +func VirtualizationWithContext(ctx context.Context) (string, string, error) { + var system string + var role string + + filename := HostProc("xen") + if PathExists(filename) { + system = "xen" + role = "guest" // assume guest + + if PathExists(filepath.Join(filename, "capabilities")) { + contents, err := ReadLines(filepath.Join(filename, "capabilities")) + if err == nil { + if StringsContains(contents, "control_d") { + role = "host" + } + } + } + } + + filename = HostProc("modules") + if PathExists(filename) { + contents, err := ReadLines(filename) + if err == nil { + if StringsContains(contents, "kvm") { + system = "kvm" + role = "host" + } else if StringsContains(contents, "vboxdrv") { + system = "vbox" + role = "host" + } else if StringsContains(contents, "vboxguest") { + system = "vbox" + role = "guest" + } else if StringsContains(contents, "vmware") { + system = "vmware" + role = "guest" + } + } + } + + filename = HostProc("cpuinfo") + if PathExists(filename) { + contents, err := ReadLines(filename) + if err == nil { + if StringsContains(contents, "QEMU Virtual CPU") || + StringsContains(contents, "Common KVM processor") || + StringsContains(contents, "Common 32-bit KVM processor") { + system = "kvm" + role = "guest" + } + } + } + + filename = HostProc("bus/pci/devices") + if PathExists(filename) { + contents, err := ReadLines(filename) + if err == nil { + if StringsContains(contents, "virtio-pci") { + role = "guest" + } + } + } + + filename = HostProc() + if PathExists(filepath.Join(filename, "bc", "0")) { + system = "openvz" + role = "host" + } else if PathExists(filepath.Join(filename, "vz")) { + system = "openvz" + role = "guest" + } + + // not use dmidecode because it requires root + if PathExists(filepath.Join(filename, "self", "status")) { + contents, err := ReadLines(filepath.Join(filename, "self", "status")) + if err == nil { + + if StringsContains(contents, "s_context:") || + StringsContains(contents, "VxID:") { + system = "linux-vserver" + } + // TODO: guest or host + } + } + + if PathExists(filepath.Join(filename, "1", "environ")) { + contents, err := ReadFile(filepath.Join(filename, "1", "environ")) + + if err == nil { + if strings.Contains(contents, "container=lxc") { + system = "lxc" + role = "guest" + } + } + } + + if PathExists(filepath.Join(filename, "self", "cgroup")) { + contents, err := ReadLines(filepath.Join(filename, "self", "cgroup")) + if err == nil { + if StringsContains(contents, "lxc") { + system = "lxc" + role = "guest" + } else if StringsContains(contents, "docker") { + system = "docker" + role = "guest" + } else if StringsContains(contents, "machine-rkt") { + system = "rkt" + role = "guest" + } else if PathExists("/usr/bin/lxc-version") { + system = "lxc" + role = "host" + } + } + } + + if PathExists(HostEtc("os-release")) { + p, _, err := GetOSRelease() + if err == nil && p == "coreos" { + system = "rkt" // Is it true? + role = "host" + } + } + return system, role, nil +} + +func GetOSRelease() (platform string, version string, err error) { + contents, err := ReadLines(HostEtc("os-release")) + if err != nil { + return "", "", nil // return empty + } + for _, line := range contents { + field := strings.Split(line, "=") + if len(field) < 2 { + continue + } + switch field[0] { + case "ID": // use ID for lowercase + platform = trimQuotes(field[1]) + case "VERSION": + version = trimQuotes(field[1]) + } + } + return platform, version, nil +} + +// Remove quotes of the source string +func trimQuotes(s string) string { + if len(s) >= 2 { + if s[0] == '"' && s[len(s)-1] == '"' { + return s[1 : len(s)-1] + } + } + return s +} diff --git a/vendor/github.com/shirou/gopsutil/internal/common/common_openbsd.go b/vendor/github.com/shirou/gopsutil/internal/common/common_openbsd.go new file mode 100644 index 00000000000..ba73a7eb507 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/internal/common/common_openbsd.go @@ -0,0 +1,69 @@ +// +build openbsd + +package common + +import ( + "os" + "os/exec" + "strings" + "unsafe" + + "golang.org/x/sys/unix" +) + +func DoSysctrl(mib string) ([]string, error) { + sysctl, err := exec.LookPath("sysctl") + if err != nil { + return []string{}, err + } + cmd := exec.Command(sysctl, "-n", mib) + cmd.Env = getSysctrlEnv(os.Environ()) + out, err := cmd.Output() + if err != nil { + return []string{}, err + } + v := strings.Replace(string(out), "{ ", "", 1) + v = strings.Replace(string(v), " }", "", 1) + values := strings.Fields(string(v)) + + return values, nil +} + +func CallSyscall(mib []int32) ([]byte, uint64, error) { + mibptr := unsafe.Pointer(&mib[0]) + miblen := uint64(len(mib)) + + // get required buffer size + length := uint64(0) + _, _, err := unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(mibptr), + uintptr(miblen), + 0, + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + var b []byte + return b, length, err + } + if length == 0 { + var b []byte + return b, length, err + } + // get proc info itself + buf := make([]byte, length) + _, _, err = unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(mibptr), + uintptr(miblen), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + return buf, length, err + } + + return buf, length, nil +} diff --git a/vendor/github.com/shirou/gopsutil/internal/common/common_unix.go b/vendor/github.com/shirou/gopsutil/internal/common/common_unix.go new file mode 100644 index 00000000000..9e393bcfa8b --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/internal/common/common_unix.go @@ -0,0 +1,67 @@ +// +build linux freebsd darwin openbsd + +package common + +import ( + "context" + "os/exec" + "strconv" + "strings" +) + +func CallLsofWithContext(ctx context.Context, invoke Invoker, pid int32, args ...string) ([]string, error) { + var cmd []string + if pid == 0 { // will get from all processes. + cmd = []string{"-a", "-n", "-P"} + } else { + cmd = []string{"-a", "-n", "-P", "-p", strconv.Itoa(int(pid))} + } + cmd = append(cmd, args...) + lsof, err := exec.LookPath("lsof") + if err != nil { + return []string{}, err + } + out, err := invoke.CommandWithContext(ctx, lsof, cmd...) + if err != nil { + // if no pid found, lsof returns code 1. + if err.Error() == "exit status 1" && len(out) == 0 { + return []string{}, nil + } + } + lines := strings.Split(string(out), "\n") + + var ret []string + for _, l := range lines[1:] { + if len(l) == 0 { + continue + } + ret = append(ret, l) + } + return ret, nil +} + +func CallPgrepWithContext(ctx context.Context, invoke Invoker, pid int32) ([]int32, error) { + var cmd []string + cmd = []string{"-P", strconv.Itoa(int(pid))} + pgrep, err := exec.LookPath("pgrep") + if err != nil { + return []int32{}, err + } + out, err := invoke.CommandWithContext(ctx, pgrep, cmd...) + if err != nil { + return []int32{}, err + } + lines := strings.Split(string(out), "\n") + ret := make([]int32, 0, len(lines)) + for _, l := range lines { + if len(l) == 0 { + continue + } + i, err := strconv.Atoi(l) + if err != nil { + continue + } + ret = append(ret, int32(i)) + } + return ret, nil +} diff --git a/vendor/github.com/shirou/gopsutil/internal/common/common_windows.go b/vendor/github.com/shirou/gopsutil/internal/common/common_windows.go new file mode 100644 index 00000000000..9bc05ded88d --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/internal/common/common_windows.go @@ -0,0 +1,172 @@ +// +build windows + +package common + +import ( + "context" + "path/filepath" + "strings" + "syscall" + "unsafe" + + "github.com/StackExchange/wmi" + "golang.org/x/sys/windows" +) + +// for double values +type PDH_FMT_COUNTERVALUE_DOUBLE struct { + CStatus uint32 + DoubleValue float64 +} + +// for 64 bit integer values +type PDH_FMT_COUNTERVALUE_LARGE struct { + CStatus uint32 + LargeValue int64 +} + +// for long values +type PDH_FMT_COUNTERVALUE_LONG struct { + CStatus uint32 + LongValue int32 + padding [4]byte +} + +// windows system const +const ( + ERROR_SUCCESS = 0 + ERROR_FILE_NOT_FOUND = 2 + DRIVE_REMOVABLE = 2 + DRIVE_FIXED = 3 + HKEY_LOCAL_MACHINE = 0x80000002 + RRF_RT_REG_SZ = 0x00000002 + RRF_RT_REG_DWORD = 0x00000010 + PDH_FMT_LONG = 0x00000100 + PDH_FMT_DOUBLE = 0x00000200 + PDH_FMT_LARGE = 0x00000400 + PDH_INVALID_DATA = 0xc0000bc6 + PDH_INVALID_HANDLE = 0xC0000bbc + PDH_NO_DATA = 0x800007d5 +) + +const ( + ProcessBasicInformation = 0 + ProcessWow64Information = 26 +) + +var ( + Modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + ModNt = windows.NewLazySystemDLL("ntdll.dll") + ModPdh = windows.NewLazySystemDLL("pdh.dll") + ModPsapi = windows.NewLazySystemDLL("psapi.dll") + + ProcGetSystemTimes = Modkernel32.NewProc("GetSystemTimes") + ProcNtQuerySystemInformation = ModNt.NewProc("NtQuerySystemInformation") + ProcRtlGetNativeSystemInformation = ModNt.NewProc("RtlGetNativeSystemInformation") + ProcRtlNtStatusToDosError = ModNt.NewProc("RtlNtStatusToDosError") + ProcNtQueryInformationProcess = ModNt.NewProc("NtQueryInformationProcess") + ProcNtReadVirtualMemory = ModNt.NewProc("NtReadVirtualMemory") + ProcNtWow64QueryInformationProcess64 = ModNt.NewProc("NtWow64QueryInformationProcess64") + ProcNtWow64ReadVirtualMemory64 = ModNt.NewProc("NtWow64ReadVirtualMemory64") + + PdhOpenQuery = ModPdh.NewProc("PdhOpenQuery") + PdhAddCounter = ModPdh.NewProc("PdhAddCounterW") + PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData") + PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue") + PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery") + + procQueryDosDeviceW = Modkernel32.NewProc("QueryDosDeviceW") +) + +type FILETIME struct { + DwLowDateTime uint32 + DwHighDateTime uint32 +} + +// borrowed from net/interface_windows.go +func BytePtrToString(p *uint8) string { + a := (*[10000]uint8)(unsafe.Pointer(p)) + i := 0 + for a[i] != 0 { + i++ + } + return string(a[:i]) +} + +// CounterInfo +// copied from https://github.com/mackerelio/mackerel-agent/ +type CounterInfo struct { + PostName string + CounterName string + Counter windows.Handle +} + +// CreateQuery XXX +// copied from https://github.com/mackerelio/mackerel-agent/ +func CreateQuery() (windows.Handle, error) { + var query windows.Handle + r, _, err := PdhOpenQuery.Call(0, 0, uintptr(unsafe.Pointer(&query))) + if r != 0 { + return 0, err + } + return query, nil +} + +// CreateCounter XXX +func CreateCounter(query windows.Handle, pname, cname string) (*CounterInfo, error) { + var counter windows.Handle + r, _, err := PdhAddCounter.Call( + uintptr(query), + uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(cname))), + 0, + uintptr(unsafe.Pointer(&counter))) + if r != 0 { + return nil, err + } + return &CounterInfo{ + PostName: pname, + CounterName: cname, + Counter: counter, + }, nil +} + +// WMIQueryWithContext - wraps wmi.Query with a timed-out context to avoid hanging +func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, connectServerArgs ...interface{}) error { + if _, ok := ctx.Deadline(); !ok { + ctxTimeout, cancel := context.WithTimeout(ctx, Timeout) + defer cancel() + ctx = ctxTimeout + } + + errChan := make(chan error, 1) + go func() { + errChan <- wmi.Query(query, dst, connectServerArgs...) + }() + + select { + case <-ctx.Done(): + return ctx.Err() + case err := <-errChan: + return err + } +} + +// Convert paths using native DOS format like: +// "\Device\HarddiskVolume1\Windows\systemew\file.txt" +// into: +// "C:\Windows\systemew\file.txt" +func ConvertDOSPath(p string) string { + rawDrive := strings.Join(strings.Split(p, `\`)[:3], `\`) + + for d := 'A'; d <= 'Z'; d++ { + szDeviceName := string(d) + ":" + szTarget := make([]uint16, 512) + ret, _, _ := procQueryDosDeviceW.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(szDeviceName))), + uintptr(unsafe.Pointer(&szTarget[0])), + uintptr(len(szTarget))) + if ret != 0 && windows.UTF16ToString(szTarget[:]) == rawDrive { + return filepath.Join(szDeviceName, p[len(rawDrive):]) + } + } + return p +} diff --git a/vendor/github.com/shirou/gopsutil/mem/mem.go b/vendor/github.com/shirou/gopsutil/mem/mem.go new file mode 100644 index 00000000000..dc2aacb59c2 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/mem/mem.go @@ -0,0 +1,105 @@ +package mem + +import ( + "encoding/json" + + "github.com/shirou/gopsutil/internal/common" +) + +var invoke common.Invoker = common.Invoke{} + +// Memory usage statistics. Total, Available and Used contain numbers of bytes +// for human consumption. +// +// The other fields in this struct contain kernel specific values. +type VirtualMemoryStat struct { + // Total amount of RAM on this system + Total uint64 `json:"total"` + + // RAM available for programs to allocate + // + // This value is computed from the kernel specific values. + Available uint64 `json:"available"` + + // RAM used by programs + // + // This value is computed from the kernel specific values. + Used uint64 `json:"used"` + + // Percentage of RAM used by programs + // + // This value is computed from the kernel specific values. + UsedPercent float64 `json:"usedPercent"` + + // This is the kernel's notion of free memory; RAM chips whose bits nobody + // cares about the value of right now. For a human consumable number, + // Available is what you really want. + Free uint64 `json:"free"` + + // OS X / BSD specific numbers: + // http://www.macyourself.com/2010/02/17/what-is-free-wired-active-and-inactive-system-memory-ram/ + Active uint64 `json:"active"` + Inactive uint64 `json:"inactive"` + Wired uint64 `json:"wired"` + + // FreeBSD specific numbers: + // https://reviews.freebsd.org/D8467 + Laundry uint64 `json:"laundry"` + + // Linux specific numbers + // https://www.centos.org/docs/5/html/5.1/Deployment_Guide/s2-proc-meminfo.html + // https://www.kernel.org/doc/Documentation/filesystems/proc.txt + // https://www.kernel.org/doc/Documentation/vm/overcommit-accounting + Buffers uint64 `json:"buffers"` + Cached uint64 `json:"cached"` + Writeback uint64 `json:"writeback"` + Dirty uint64 `json:"dirty"` + WritebackTmp uint64 `json:"writebacktmp"` + Shared uint64 `json:"shared"` + Slab uint64 `json:"slab"` + SReclaimable uint64 `json:"sreclaimable"` + SUnreclaim uint64 `json:"sunreclaim"` + PageTables uint64 `json:"pagetables"` + SwapCached uint64 `json:"swapcached"` + CommitLimit uint64 `json:"commitlimit"` + CommittedAS uint64 `json:"committedas"` + HighTotal uint64 `json:"hightotal"` + HighFree uint64 `json:"highfree"` + LowTotal uint64 `json:"lowtotal"` + LowFree uint64 `json:"lowfree"` + SwapTotal uint64 `json:"swaptotal"` + SwapFree uint64 `json:"swapfree"` + Mapped uint64 `json:"mapped"` + VMallocTotal uint64 `json:"vmalloctotal"` + VMallocUsed uint64 `json:"vmallocused"` + VMallocChunk uint64 `json:"vmallocchunk"` + HugePagesTotal uint64 `json:"hugepagestotal"` + HugePagesFree uint64 `json:"hugepagesfree"` + HugePageSize uint64 `json:"hugepagesize"` +} + +type SwapMemoryStat struct { + Total uint64 `json:"total"` + Used uint64 `json:"used"` + Free uint64 `json:"free"` + UsedPercent float64 `json:"usedPercent"` + Sin uint64 `json:"sin"` + Sout uint64 `json:"sout"` + PgIn uint64 `json:"pgin"` + PgOut uint64 `json:"pgout"` + PgFault uint64 `json:"pgfault"` + + // Linux specific numbers + // https://www.kernel.org/doc/Documentation/cgroup-v2.txt + PgMajFault uint64 `json:"pgmajfault"` +} + +func (m VirtualMemoryStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +func (m SwapMemoryStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} diff --git a/vendor/github.com/shirou/gopsutil/mem/mem_darwin.go b/vendor/github.com/shirou/gopsutil/mem/mem_darwin.go new file mode 100644 index 00000000000..fac748151c7 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/mem/mem_darwin.go @@ -0,0 +1,69 @@ +// +build darwin + +package mem + +import ( + "context" + "encoding/binary" + "fmt" + "unsafe" + + "golang.org/x/sys/unix" +) + +func getHwMemsize() (uint64, error) { + totalString, err := unix.Sysctl("hw.memsize") + if err != nil { + return 0, err + } + + // unix.sysctl() helpfully assumes the result is a null-terminated string and + // removes the last byte of the result if it's 0 :/ + totalString += "\x00" + + total := uint64(binary.LittleEndian.Uint64([]byte(totalString))) + + return total, nil +} + +// xsw_usage in sys/sysctl.h +type swapUsage struct { + Total uint64 + Avail uint64 + Used uint64 + Pagesize int32 + Encrypted bool +} + +// SwapMemory returns swapinfo. +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + // https://github.com/yanllearnn/go-osstat/blob/ae8a279d26f52ec946a03698c7f50a26cfb427e3/memory/memory_darwin.go + var ret *SwapMemoryStat + + value, err := unix.SysctlRaw("vm.swapusage") + if err != nil { + return ret, err + } + if len(value) != 32 { + return ret, fmt.Errorf("unexpected output of sysctl vm.swapusage: %v (len: %d)", value, len(value)) + } + swap := (*swapUsage)(unsafe.Pointer(&value[0])) + + u := float64(0) + if swap.Total != 0 { + u = ((float64(swap.Total) - float64(swap.Avail)) / float64(swap.Total)) * 100.0 + } + + ret = &SwapMemoryStat{ + Total: swap.Total, + Used: swap.Used, + Free: swap.Avail, + UsedPercent: u, + } + + return ret, nil +} diff --git a/vendor/github.com/shirou/gopsutil/mem/mem_darwin_cgo.go b/vendor/github.com/shirou/gopsutil/mem/mem_darwin_cgo.go new file mode 100644 index 00000000000..389f8cdf999 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/mem/mem_darwin_cgo.go @@ -0,0 +1,59 @@ +// +build darwin +// +build cgo + +package mem + +/* +#include +*/ +import "C" + +import ( + "context" + "fmt" + "unsafe" + + "golang.org/x/sys/unix" +) + +// VirtualMemory returns VirtualmemoryStat. +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + count := C.mach_msg_type_number_t(C.HOST_VM_INFO_COUNT) + var vmstat C.vm_statistics_data_t + + status := C.host_statistics(C.host_t(C.mach_host_self()), + C.HOST_VM_INFO, + C.host_info_t(unsafe.Pointer(&vmstat)), + &count) + + if status != C.KERN_SUCCESS { + return nil, fmt.Errorf("host_statistics error=%d", status) + } + + pageSize := uint64(unix.Getpagesize()) + total, err := getHwMemsize() + if err != nil { + return nil, err + } + totalCount := C.natural_t(total / pageSize) + + availableCount := vmstat.inactive_count + vmstat.free_count + usedPercent := 100 * float64(totalCount-availableCount) / float64(totalCount) + + usedCount := totalCount - availableCount + + return &VirtualMemoryStat{ + Total: total, + Available: pageSize * uint64(availableCount), + Used: pageSize * uint64(usedCount), + UsedPercent: usedPercent, + Free: pageSize * uint64(vmstat.free_count), + Active: pageSize * uint64(vmstat.active_count), + Inactive: pageSize * uint64(vmstat.inactive_count), + Wired: pageSize * uint64(vmstat.wire_count), + }, nil +} diff --git a/vendor/github.com/shirou/gopsutil/mem/mem_darwin_nocgo.go b/vendor/github.com/shirou/gopsutil/mem/mem_darwin_nocgo.go new file mode 100644 index 00000000000..dd7c2e600e7 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/mem/mem_darwin_nocgo.go @@ -0,0 +1,94 @@ +// +build darwin +// +build !cgo + +package mem + +import ( + "context" + "os/exec" + "strconv" + "strings" + + "golang.org/x/sys/unix" +) + +// Runs vm_stat and returns Free and inactive pages +func getVMStat(vms *VirtualMemoryStat) error { + vm_stat, err := exec.LookPath("vm_stat") + if err != nil { + return err + } + out, err := invoke.Command(vm_stat) + if err != nil { + return err + } + return parseVMStat(string(out), vms) +} + +func parseVMStat(out string, vms *VirtualMemoryStat) error { + var err error + + lines := strings.Split(out, "\n") + pagesize := uint64(unix.Getpagesize()) + for _, line := range lines { + fields := strings.Split(line, ":") + if len(fields) < 2 { + continue + } + key := strings.TrimSpace(fields[0]) + value := strings.Trim(fields[1], " .") + switch key { + case "Pages free": + free, e := strconv.ParseUint(value, 10, 64) + if e != nil { + err = e + } + vms.Free = free * pagesize + case "Pages inactive": + inactive, e := strconv.ParseUint(value, 10, 64) + if e != nil { + err = e + } + vms.Inactive = inactive * pagesize + case "Pages active": + active, e := strconv.ParseUint(value, 10, 64) + if e != nil { + err = e + } + vms.Active = active * pagesize + case "Pages wired down": + wired, e := strconv.ParseUint(value, 10, 64) + if e != nil { + err = e + } + vms.Wired = wired * pagesize + } + } + return err +} + +// VirtualMemory returns VirtualmemoryStat. +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + ret := &VirtualMemoryStat{} + + total, err := getHwMemsize() + if err != nil { + return nil, err + } + err = getVMStat(ret) + if err != nil { + return nil, err + } + + ret.Available = ret.Free + ret.Inactive + ret.Total = total + + ret.Used = ret.Total - ret.Available + ret.UsedPercent = 100 * float64(ret.Used) / float64(ret.Total) + + return ret, nil +} diff --git a/vendor/github.com/shirou/gopsutil/mem/mem_fallback.go b/vendor/github.com/shirou/gopsutil/mem/mem_fallback.go new file mode 100644 index 00000000000..2a0fd45b32c --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/mem/mem_fallback.go @@ -0,0 +1,25 @@ +// +build !darwin,!linux,!freebsd,!openbsd,!solaris,!windows + +package mem + +import ( + "context" + + "github.com/shirou/gopsutil/internal/common" +) + +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + return nil, common.ErrNotImplementedError +} + +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + return nil, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/mem/mem_freebsd.go b/vendor/github.com/shirou/gopsutil/mem/mem_freebsd.go new file mode 100644 index 00000000000..f91efc9e374 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/mem/mem_freebsd.go @@ -0,0 +1,167 @@ +// +build freebsd + +package mem + +import ( + "context" + "errors" + "unsafe" + + "golang.org/x/sys/unix" + + "github.com/shirou/gopsutil/internal/common" +) + +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + pageSize, err := common.SysctlUint("vm.stats.vm.v_page_size") + if err != nil { + return nil, err + } + physmem, err := common.SysctlUint("hw.physmem") + if err != nil { + return nil, err + } + + free, err := common.SysctlUint("vm.stats.vm.v_free_count") + if err != nil { + return nil, err + } + active, err := common.SysctlUint("vm.stats.vm.v_active_count") + if err != nil { + return nil, err + } + inactive, err := common.SysctlUint("vm.stats.vm.v_inactive_count") + if err != nil { + return nil, err + } + buffers, err := common.SysctlUint("vfs.bufspace") + if err != nil { + return nil, err + } + wired, err := common.SysctlUint("vm.stats.vm.v_wire_count") + if err != nil { + return nil, err + } + var cached, laundry uint64 + osreldate, _ := common.SysctlUint("kern.osreldate") + if osreldate < 1102000 { + cached, err = common.SysctlUint("vm.stats.vm.v_cache_count") + if err != nil { + return nil, err + } + } else { + laundry, err = common.SysctlUint("vm.stats.vm.v_laundry_count") + if err != nil { + return nil, err + } + } + + p := pageSize + ret := &VirtualMemoryStat{ + Total: physmem, + Free: free * p, + Active: active * p, + Inactive: inactive * p, + Cached: cached * p, + Buffers: buffers, + Wired: wired * p, + Laundry: laundry * p, + } + + ret.Available = ret.Inactive + ret.Cached + ret.Free + ret.Laundry + ret.Used = ret.Total - ret.Available + ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0 + + return ret, nil +} + +// Return swapinfo +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} + +// Constants from vm/vm_param.h +// nolint: golint +const ( + XSWDEV_VERSION11 = 1 + XSWDEV_VERSION = 2 +) + +// Types from vm/vm_param.h +type xswdev struct { + Version uint32 // Version is the version + Dev uint64 // Dev is the device identifier + Flags int32 // Flags is the swap flags applied to the device + NBlks int32 // NBlks is the total number of blocks + Used int32 // Used is the number of blocks used +} + +// xswdev11 is a compatibility for under FreeBSD 11 +// sys/vm/swap_pager.c +type xswdev11 struct { + Version uint32 // Version is the version + Dev uint32 // Dev is the device identifier + Flags int32 // Flags is the swap flags applied to the device + NBlks int32 // NBlks is the total number of blocks + Used int32 // Used is the number of blocks used +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + // FreeBSD can have multiple swap devices so we total them up + i, err := common.SysctlUint("vm.nswapdev") + if err != nil { + return nil, err + } + + if i == 0 { + return nil, errors.New("no swap devices found") + } + + c := int(i) + + i, err = common.SysctlUint("vm.stats.vm.v_page_size") + if err != nil { + return nil, err + } + pageSize := i + + var buf []byte + s := &SwapMemoryStat{} + for n := 0; n < c; n++ { + buf, err = unix.SysctlRaw("vm.swap_info", n) + if err != nil { + return nil, err + } + + // first, try to parse with version 2 + xsw := (*xswdev)(unsafe.Pointer(&buf[0])) + if xsw.Version == XSWDEV_VERSION11 { + // this is version 1, so try to parse again + xsw := (*xswdev11)(unsafe.Pointer(&buf[0])) + if xsw.Version != XSWDEV_VERSION11 { + return nil, errors.New("xswdev version mismatch(11)") + } + s.Total += uint64(xsw.NBlks) + s.Used += uint64(xsw.Used) + } else if xsw.Version != XSWDEV_VERSION { + return nil, errors.New("xswdev version mismatch") + } else { + s.Total += uint64(xsw.NBlks) + s.Used += uint64(xsw.Used) + } + + } + + if s.Total != 0 { + s.UsedPercent = float64(s.Used) / float64(s.Total) * 100 + } + s.Total *= pageSize + s.Used *= pageSize + s.Free = s.Total - s.Used + + return s, nil +} diff --git a/vendor/github.com/shirou/gopsutil/mem/mem_linux.go b/vendor/github.com/shirou/gopsutil/mem/mem_linux.go new file mode 100644 index 00000000000..72faf22b93f --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/mem/mem_linux.go @@ -0,0 +1,288 @@ +// +build linux + +package mem + +import ( + "context" + "encoding/json" + "math" + "os" + "strconv" + "strings" + + "github.com/shirou/gopsutil/internal/common" + "golang.org/x/sys/unix" +) + +type VirtualMemoryExStat struct { + ActiveFile uint64 `json:"activefile"` + InactiveFile uint64 `json:"inactivefile"` + ActiveAnon uint64 `json:"activeanon"` + InactiveAnon uint64 `json:"inactiveanon"` + Unevictable uint64 `json:"unevictable"` +} + +func (v VirtualMemoryExStat) String() string { + s, _ := json.Marshal(v) + return string(s) +} + +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + vm, _, err := fillFromMeminfoWithContext(ctx) + if err != nil { + return nil, err + } + return vm, nil +} + +func VirtualMemoryEx() (*VirtualMemoryExStat, error) { + return VirtualMemoryExWithContext(context.Background()) +} + +func VirtualMemoryExWithContext(ctx context.Context) (*VirtualMemoryExStat, error) { + _, vmEx, err := fillFromMeminfoWithContext(ctx) + if err != nil { + return nil, err + } + return vmEx, nil +} + +func fillFromMeminfoWithContext(ctx context.Context) (*VirtualMemoryStat, *VirtualMemoryExStat, error) { + filename := common.HostProc("meminfo") + lines, _ := common.ReadLines(filename) + + // flag if MemAvailable is in /proc/meminfo (kernel 3.14+) + memavail := false + activeFile := false // "Active(file)" not available: 2.6.28 / Dec 2008 + inactiveFile := false // "Inactive(file)" not available: 2.6.28 / Dec 2008 + sReclaimable := false // "SReclaimable:" not available: 2.6.19 / Nov 2006 + + ret := &VirtualMemoryStat{} + retEx := &VirtualMemoryExStat{} + + for _, line := range lines { + fields := strings.Split(line, ":") + if len(fields) != 2 { + continue + } + key := strings.TrimSpace(fields[0]) + value := strings.TrimSpace(fields[1]) + value = strings.Replace(value, " kB", "", -1) + + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, retEx,err + } + switch key { + case "MemTotal": + ret.Total = t * 1024 + case "MemFree": + ret.Free = t * 1024 + case "MemAvailable": + memavail = true + ret.Available = t * 1024 + case "Buffers": + ret.Buffers = t * 1024 + case "Cached": + ret.Cached = t * 1024 + case "Active": + ret.Active = t * 1024 + case "Inactive": + ret.Inactive = t * 1024 + case "Active(anon)": + retEx.ActiveAnon = t * 1024 + case "Inactive(anon)": + retEx.InactiveAnon = t * 1024 + case "Active(file)": + activeFile = true + retEx.ActiveFile = t * 1024 + case "Inactive(file)": + inactiveFile = true + retEx.InactiveFile = t * 1024 + case "Unevictable": + retEx.Unevictable = t * 1024 + case "Writeback": + ret.Writeback = t * 1024 + case "WritebackTmp": + ret.WritebackTmp = t * 1024 + case "Dirty": + ret.Dirty = t * 1024 + case "Shmem": + ret.Shared = t * 1024 + case "Slab": + ret.Slab = t * 1024 + case "SReclaimable": + sReclaimable = true + ret.SReclaimable = t * 1024 + case "SUnreclaim": + ret.SUnreclaim = t * 1024 + case "PageTables": + ret.PageTables = t * 1024 + case "SwapCached": + ret.SwapCached = t * 1024 + case "CommitLimit": + ret.CommitLimit = t * 1024 + case "Committed_AS": + ret.CommittedAS = t * 1024 + case "HighTotal": + ret.HighTotal = t * 1024 + case "HighFree": + ret.HighFree = t * 1024 + case "LowTotal": + ret.LowTotal = t * 1024 + case "LowFree": + ret.LowFree = t * 1024 + case "SwapTotal": + ret.SwapTotal = t * 1024 + case "SwapFree": + ret.SwapFree = t * 1024 + case "Mapped": + ret.Mapped = t * 1024 + case "VmallocTotal": + ret.VMallocTotal = t * 1024 + case "VmallocUsed": + ret.VMallocUsed = t * 1024 + case "VmallocChunk": + ret.VMallocChunk = t * 1024 + case "HugePages_Total": + ret.HugePagesTotal = t + case "HugePages_Free": + ret.HugePagesFree = t + case "Hugepagesize": + ret.HugePageSize = t * 1024 + } + } + + ret.Cached += ret.SReclaimable + + if !memavail { + if activeFile && inactiveFile && sReclaimable { + ret.Available = calcuateAvailVmem(ret, retEx) + } else { + ret.Available = ret.Cached + ret.Free + } + } + + ret.Used = ret.Total - ret.Free - ret.Buffers - ret.Cached + ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0 + + return ret, retEx, nil +} + +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + sysinfo := &unix.Sysinfo_t{} + + if err := unix.Sysinfo(sysinfo); err != nil { + return nil, err + } + ret := &SwapMemoryStat{ + Total: uint64(sysinfo.Totalswap) * uint64(sysinfo.Unit), + Free: uint64(sysinfo.Freeswap) * uint64(sysinfo.Unit), + } + ret.Used = ret.Total - ret.Free + //check Infinity + if ret.Total != 0 { + ret.UsedPercent = float64(ret.Total-ret.Free) / float64(ret.Total) * 100.0 + } else { + ret.UsedPercent = 0 + } + filename := common.HostProc("vmstat") + lines, _ := common.ReadLines(filename) + for _, l := range lines { + fields := strings.Fields(l) + if len(fields) < 2 { + continue + } + switch fields[0] { + case "pswpin": + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + ret.Sin = value * 4 * 1024 + case "pswpout": + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + ret.Sout = value * 4 * 1024 + case "pgpgin": + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + ret.PgIn = value * 4 * 1024 + case "pgpgout": + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + ret.PgOut = value * 4 * 1024 + case "pgfault": + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + ret.PgFault = value * 4 * 1024 + case "pgmajfault": + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + ret.PgMajFault = value * 4 * 1024 + } + } + return ret, nil +} + +// calcuateAvailVmem is a fallback under kernel 3.14 where /proc/meminfo does not provide +// "MemAvailable:" column. It reimplements an algorithm from the link below +// https://github.com/giampaolo/psutil/pull/890 +func calcuateAvailVmem(ret *VirtualMemoryStat, retEx *VirtualMemoryExStat) uint64 { + var watermarkLow uint64 + + fn := common.HostProc("zoneinfo") + lines, err := common.ReadLines(fn) + + if err != nil { + return ret.Free + ret.Cached // fallback under kernel 2.6.13 + } + + pagesize := uint64(os.Getpagesize()) + watermarkLow = 0 + + for _, line := range lines { + fields := strings.Fields(line) + + if strings.HasPrefix(fields[0], "low") { + lowValue, err := strconv.ParseUint(fields[1], 10, 64) + + if err != nil { + lowValue = 0 + } + watermarkLow += lowValue + } + } + + watermarkLow *= pagesize + + availMemory := ret.Free - watermarkLow + pageCache := retEx.ActiveFile + retEx.InactiveFile + pageCache -= uint64(math.Min(float64(pageCache/2), float64(watermarkLow))) + availMemory += pageCache + availMemory += ret.SReclaimable - uint64(math.Min(float64(ret.SReclaimable/2.0), float64(watermarkLow))) + + if availMemory < 0 { + availMemory = 0 + } + + return availMemory +} diff --git a/vendor/github.com/shirou/gopsutil/mem/mem_openbsd.go b/vendor/github.com/shirou/gopsutil/mem/mem_openbsd.go new file mode 100644 index 00000000000..35472a32600 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/mem/mem_openbsd.go @@ -0,0 +1,124 @@ +// +build openbsd + +package mem + +import ( + "bytes" + "context" + "encoding/binary" + "errors" + "fmt" + "os/exec" + + "github.com/shirou/gopsutil/internal/common" +) + +func GetPageSize() (uint64, error) { + return GetPageSizeWithContext(context.Background()) +} + +func GetPageSizeWithContext(ctx context.Context) (uint64, error) { + mib := []int32{CTLVm, VmUvmexp} + buf, length, err := common.CallSyscall(mib) + if err != nil { + return 0, err + } + if length < sizeOfUvmexp { + return 0, fmt.Errorf("short syscall ret %d bytes", length) + } + var uvmexp Uvmexp + br := bytes.NewReader(buf) + err = common.Read(br, binary.LittleEndian, &uvmexp) + if err != nil { + return 0, err + } + return uint64(uvmexp.Pagesize), nil +} + +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + mib := []int32{CTLVm, VmUvmexp} + buf, length, err := common.CallSyscall(mib) + if err != nil { + return nil, err + } + if length < sizeOfUvmexp { + return nil, fmt.Errorf("short syscall ret %d bytes", length) + } + var uvmexp Uvmexp + br := bytes.NewReader(buf) + err = common.Read(br, binary.LittleEndian, &uvmexp) + if err != nil { + return nil, err + } + p := uint64(uvmexp.Pagesize) + + ret := &VirtualMemoryStat{ + Total: uint64(uvmexp.Npages) * p, + Free: uint64(uvmexp.Free) * p, + Active: uint64(uvmexp.Active) * p, + Inactive: uint64(uvmexp.Inactive) * p, + Cached: 0, // not available + Wired: uint64(uvmexp.Wired) * p, + } + + ret.Available = ret.Inactive + ret.Cached + ret.Free + ret.Used = ret.Total - ret.Available + ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0 + + mib = []int32{CTLVfs, VfsGeneric, VfsBcacheStat} + buf, length, err = common.CallSyscall(mib) + if err != nil { + return nil, err + } + if length < sizeOfBcachestats { + return nil, fmt.Errorf("short syscall ret %d bytes", length) + } + var bcs Bcachestats + br = bytes.NewReader(buf) + err = common.Read(br, binary.LittleEndian, &bcs) + if err != nil { + return nil, err + } + ret.Buffers = uint64(bcs.Numbufpages) * p + + return ret, nil +} + +// Return swapctl summary info +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + swapctl, err := exec.LookPath("swapctl") + if err != nil { + return nil, err + } + + out, err := invoke.CommandWithContext(ctx, swapctl, "-sk") + if err != nil { + return &SwapMemoryStat{}, nil + } + + line := string(out) + var total, used, free uint64 + + _, err = fmt.Sscanf(line, + "total: %d 1K-blocks allocated, %d used, %d available", + &total, &used, &free) + if err != nil { + return nil, errors.New("failed to parse swapctl output") + } + + percent := float64(used) / float64(total) * 100 + return &SwapMemoryStat{ + Total: total * 1024, + Used: used * 1024, + Free: free * 1024, + UsedPercent: percent, + }, nil +} diff --git a/vendor/github.com/shirou/gopsutil/mem/mem_openbsd_amd64.go b/vendor/github.com/shirou/gopsutil/mem/mem_openbsd_amd64.go new file mode 100644 index 00000000000..e09b908e466 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/mem/mem_openbsd_amd64.go @@ -0,0 +1,122 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_openbsd.go + +package mem + +const ( + CTLVm = 2 + CTLVfs = 10 + VmUvmexp = 4 + VfsGeneric = 0 + VfsBcacheStat = 3 +) + +const ( + sizeOfUvmexp = 0x154 + sizeOfBcachestats = 0x78 +) + +type Uvmexp struct { + Pagesize int32 + Pagemask int32 + Pageshift int32 + Npages int32 + Free int32 + Active int32 + Inactive int32 + Paging int32 + Wired int32 + Zeropages int32 + Reserve_pagedaemon int32 + Reserve_kernel int32 + Anonpages int32 + Vnodepages int32 + Vtextpages int32 + Freemin int32 + Freetarg int32 + Inactarg int32 + Wiredmax int32 + Anonmin int32 + Vtextmin int32 + Vnodemin int32 + Anonminpct int32 + Vtextminpct int32 + Vnodeminpct int32 + Nswapdev int32 + Swpages int32 + Swpginuse int32 + Swpgonly int32 + Nswget int32 + Nanon int32 + Nanonneeded int32 + Nfreeanon int32 + Faults int32 + Traps int32 + Intrs int32 + Swtch int32 + Softs int32 + Syscalls int32 + Pageins int32 + Obsolete_swapins int32 + Obsolete_swapouts int32 + Pgswapin int32 + Pgswapout int32 + Forks int32 + Forks_ppwait int32 + Forks_sharevm int32 + Pga_zerohit int32 + Pga_zeromiss int32 + Zeroaborts int32 + Fltnoram int32 + Fltnoanon int32 + Fltpgwait int32 + Fltpgrele int32 + Fltrelck int32 + Fltrelckok int32 + Fltanget int32 + Fltanretry int32 + Fltamcopy int32 + Fltnamap int32 + Fltnomap int32 + Fltlget int32 + Fltget int32 + Flt_anon int32 + Flt_acow int32 + Flt_obj int32 + Flt_prcopy int32 + Flt_przero int32 + Pdwoke int32 + Pdrevs int32 + Pdswout int32 + Pdfreed int32 + Pdscans int32 + Pdanscan int32 + Pdobscan int32 + Pdreact int32 + Pdbusy int32 + Pdpageouts int32 + Pdpending int32 + Pddeact int32 + Pdreanon int32 + Pdrevnode int32 + Pdrevtext int32 + Fpswtch int32 + Kmapent int32 +} +type Bcachestats struct { + Numbufs int64 + Numbufpages int64 + Numdirtypages int64 + Numcleanpages int64 + Pendingwrites int64 + Pendingreads int64 + Numwrites int64 + Numreads int64 + Cachehits int64 + Busymapped int64 + Dmapages int64 + Highpages int64 + Delwribufs int64 + Kvaslots int64 + Avail int64 +} diff --git a/vendor/github.com/shirou/gopsutil/mem/mem_solaris.go b/vendor/github.com/shirou/gopsutil/mem/mem_solaris.go new file mode 100644 index 00000000000..08512733c98 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/mem/mem_solaris.go @@ -0,0 +1,121 @@ +package mem + +import ( + "context" + "errors" + "fmt" + "os/exec" + "regexp" + "strconv" + "strings" + + "github.com/shirou/gopsutil/internal/common" +) + +// VirtualMemory for Solaris is a minimal implementation which only returns +// what Nomad needs. It does take into account global vs zone, however. +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + result := &VirtualMemoryStat{} + + zoneName, err := zoneName() + if err != nil { + return nil, err + } + + if zoneName == "global" { + cap, err := globalZoneMemoryCapacity() + if err != nil { + return nil, err + } + result.Total = cap + } else { + cap, err := nonGlobalZoneMemoryCapacity() + if err != nil { + return nil, err + } + result.Total = cap + } + + return result, nil +} + +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + return nil, common.ErrNotImplementedError +} + +func zoneName() (string, error) { + zonename, err := exec.LookPath("zonename") + if err != nil { + return "", err + } + + ctx := context.Background() + out, err := invoke.CommandWithContext(ctx, zonename) + if err != nil { + return "", err + } + + return strings.TrimSpace(string(out)), nil +} + +var globalZoneMemoryCapacityMatch = regexp.MustCompile(`memory size: ([\d]+) Megabytes`) + +func globalZoneMemoryCapacity() (uint64, error) { + prtconf, err := exec.LookPath("prtconf") + if err != nil { + return 0, err + } + + ctx := context.Background() + out, err := invoke.CommandWithContext(ctx, prtconf) + if err != nil { + return 0, err + } + + match := globalZoneMemoryCapacityMatch.FindAllStringSubmatch(string(out), -1) + if len(match) != 1 { + return 0, errors.New("memory size not contained in output of /usr/sbin/prtconf") + } + + totalMB, err := strconv.ParseUint(match[0][1], 10, 64) + if err != nil { + return 0, err + } + + return totalMB * 1024 * 1024, nil +} + +var kstatMatch = regexp.MustCompile(`([^\s]+)[\s]+([^\s]*)`) + +func nonGlobalZoneMemoryCapacity() (uint64, error) { + kstat, err := exec.LookPath("kstat") + if err != nil { + return 0, err + } + + ctx := context.Background() + out, err := invoke.CommandWithContext(ctx, kstat, "-p", "-c", "zone_memory_cap", "memory_cap:*:*:physcap") + if err != nil { + return 0, err + } + + kstats := kstatMatch.FindAllStringSubmatch(string(out), -1) + if len(kstats) != 1 { + return 0, fmt.Errorf("expected 1 kstat, found %d", len(kstats)) + } + + memSizeBytes, err := strconv.ParseUint(kstats[0][2], 10, 64) + if err != nil { + return 0, err + } + + return memSizeBytes, nil +} diff --git a/vendor/github.com/shirou/gopsutil/mem/mem_windows.go b/vendor/github.com/shirou/gopsutil/mem/mem_windows.go new file mode 100644 index 00000000000..a925faa2526 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/mem/mem_windows.go @@ -0,0 +1,98 @@ +// +build windows + +package mem + +import ( + "context" + "unsafe" + + "github.com/shirou/gopsutil/internal/common" + "golang.org/x/sys/windows" +) + +var ( + procGlobalMemoryStatusEx = common.Modkernel32.NewProc("GlobalMemoryStatusEx") + procGetPerformanceInfo = common.ModPsapi.NewProc("GetPerformanceInfo") +) + +type memoryStatusEx struct { + cbSize uint32 + dwMemoryLoad uint32 + ullTotalPhys uint64 // in bytes + ullAvailPhys uint64 + ullTotalPageFile uint64 + ullAvailPageFile uint64 + ullTotalVirtual uint64 + ullAvailVirtual uint64 + ullAvailExtendedVirtual uint64 +} + +func VirtualMemory() (*VirtualMemoryStat, error) { + return VirtualMemoryWithContext(context.Background()) +} + +func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { + var memInfo memoryStatusEx + memInfo.cbSize = uint32(unsafe.Sizeof(memInfo)) + mem, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo))) + if mem == 0 { + return nil, windows.GetLastError() + } + + ret := &VirtualMemoryStat{ + Total: memInfo.ullTotalPhys, + Available: memInfo.ullAvailPhys, + Free: memInfo.ullAvailPhys, + UsedPercent: float64(memInfo.dwMemoryLoad), + } + + ret.Used = ret.Total - ret.Available + return ret, nil +} + +type performanceInformation struct { + cb uint32 + commitTotal uint64 + commitLimit uint64 + commitPeak uint64 + physicalTotal uint64 + physicalAvailable uint64 + systemCache uint64 + kernelTotal uint64 + kernelPaged uint64 + kernelNonpaged uint64 + pageSize uint64 + handleCount uint32 + processCount uint32 + threadCount uint32 +} + +func SwapMemory() (*SwapMemoryStat, error) { + return SwapMemoryWithContext(context.Background()) +} + +func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { + var perfInfo performanceInformation + perfInfo.cb = uint32(unsafe.Sizeof(perfInfo)) + mem, _, _ := procGetPerformanceInfo.Call(uintptr(unsafe.Pointer(&perfInfo)), uintptr(perfInfo.cb)) + if mem == 0 { + return nil, windows.GetLastError() + } + tot := perfInfo.commitLimit * perfInfo.pageSize + used := perfInfo.commitTotal * perfInfo.pageSize + free := tot - used + var usedPercent float64 + if tot == 0 { + usedPercent = 0 + } else { + usedPercent = float64(used) / float64(tot) * 100 + } + ret := &SwapMemoryStat{ + Total: tot, + Used: used, + Free: free, + UsedPercent: usedPercent, + } + + return ret, nil +} diff --git a/vendor/github.com/shirou/gopsutil/mem/types_openbsd.go b/vendor/github.com/shirou/gopsutil/mem/types_openbsd.go new file mode 100644 index 00000000000..83cb91a1964 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/mem/types_openbsd.go @@ -0,0 +1,34 @@ +// +build ignore + +/* +Input to cgo -godefs. +*/ + +package mem + +/* +#include +#include +#include +#include + +*/ +import "C" + +// Machine characteristics; for internal use. + +const ( + CTLVm = 2 + CTLVfs = 10 + VmUvmexp = 4 // get uvmexp + VfsGeneric = 0 + VfsBcacheStat = 3 +) + +const ( + sizeOfUvmexp = C.sizeof_struct_uvmexp + sizeOfBcachestats = C.sizeof_struct_bcachestats +) + +type Uvmexp C.struct_uvmexp +type Bcachestats C.struct_bcachestats diff --git a/vendor/github.com/shirou/gopsutil/net/net.go b/vendor/github.com/shirou/gopsutil/net/net.go new file mode 100644 index 00000000000..f1f99dc3a64 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/net/net.go @@ -0,0 +1,263 @@ +package net + +import ( + "context" + "encoding/json" + "net" + + "github.com/shirou/gopsutil/internal/common" +) + +var invoke common.Invoker = common.Invoke{} + +type IOCountersStat struct { + Name string `json:"name"` // interface name + BytesSent uint64 `json:"bytesSent"` // number of bytes sent + BytesRecv uint64 `json:"bytesRecv"` // number of bytes received + PacketsSent uint64 `json:"packetsSent"` // number of packets sent + PacketsRecv uint64 `json:"packetsRecv"` // number of packets received + Errin uint64 `json:"errin"` // total number of errors while receiving + Errout uint64 `json:"errout"` // total number of errors while sending + Dropin uint64 `json:"dropin"` // total number of incoming packets which were dropped + Dropout uint64 `json:"dropout"` // total number of outgoing packets which were dropped (always 0 on OSX and BSD) + Fifoin uint64 `json:"fifoin"` // total number of FIFO buffers errors while receiving + Fifoout uint64 `json:"fifoout"` // total number of FIFO buffers errors while sending + +} + +// Addr is implemented compatibility to psutil +type Addr struct { + IP string `json:"ip"` + Port uint32 `json:"port"` +} + +type ConnectionStat struct { + Fd uint32 `json:"fd"` + Family uint32 `json:"family"` + Type uint32 `json:"type"` + Laddr Addr `json:"localaddr"` + Raddr Addr `json:"remoteaddr"` + Status string `json:"status"` + Uids []int32 `json:"uids"` + Pid int32 `json:"pid"` +} + +// System wide stats about different network protocols +type ProtoCountersStat struct { + Protocol string `json:"protocol"` + Stats map[string]int64 `json:"stats"` +} + +// NetInterfaceAddr is designed for represent interface addresses +type InterfaceAddr struct { + Addr string `json:"addr"` +} + +type InterfaceStat struct { + Index int `json:"index"` + MTU int `json:"mtu"` // maximum transmission unit + Name string `json:"name"` // e.g., "en0", "lo0", "eth0.100" + HardwareAddr string `json:"hardwareaddr"` // IEEE MAC-48, EUI-48 and EUI-64 form + Flags []string `json:"flags"` // e.g., FlagUp, FlagLoopback, FlagMulticast + Addrs []InterfaceAddr `json:"addrs"` +} + +type FilterStat struct { + ConnTrackCount int64 `json:"conntrackCount"` + ConnTrackMax int64 `json:"conntrackMax"` +} + +// ConntrackStat has conntrack summary info +type ConntrackStat struct { + Entries uint32 `json:"entries"` // Number of entries in the conntrack table + Searched uint32 `json:"searched"` // Number of conntrack table lookups performed + Found uint32 `json:"found"` // Number of searched entries which were successful + New uint32 `json:"new"` // Number of entries added which were not expected before + Invalid uint32 `json:"invalid"` // Number of packets seen which can not be tracked + Ignore uint32 `json:"ignore"` // Packets seen which are already connected to an entry + Delete uint32 `json:"delete"` // Number of entries which were removed + DeleteList uint32 `json:"delete_list"` // Number of entries which were put to dying list + Insert uint32 `json:"insert"` // Number of entries inserted into the list + InsertFailed uint32 `json:"insert_failed"` // # insertion attempted but failed (same entry exists) + Drop uint32 `json:"drop"` // Number of packets dropped due to conntrack failure. + EarlyDrop uint32 `json:"early_drop"` // Dropped entries to make room for new ones, if maxsize reached + IcmpError uint32 `json:"icmp_error"` // Subset of invalid. Packets that can't be tracked d/t error + ExpectNew uint32 `json:"expect_new"` // Entries added after an expectation was already present + ExpectCreate uint32 `json:"expect_create"` // Expectations added + ExpectDelete uint32 `json:"expect_delete"` // Expectations deleted + SearchRestart uint32 `json:"search_restart"` // Conntrack table lookups restarted due to hashtable resizes +} + +func NewConntrackStat(e uint32, s uint32, f uint32, n uint32, inv uint32, ign uint32, del uint32, dlst uint32, ins uint32, insfail uint32, drop uint32, edrop uint32, ie uint32, en uint32, ec uint32, ed uint32, sr uint32) *ConntrackStat { + return &ConntrackStat{ + Entries: e, + Searched: s, + Found: f, + New: n, + Invalid: inv, + Ignore: ign, + Delete: del, + DeleteList: dlst, + Insert: ins, + InsertFailed: insfail, + Drop: drop, + EarlyDrop: edrop, + IcmpError: ie, + ExpectNew: en, + ExpectCreate: ec, + ExpectDelete: ed, + SearchRestart: sr, + } +} + +type ConntrackStatList struct { + items []*ConntrackStat +} + +func NewConntrackStatList() *ConntrackStatList { + return &ConntrackStatList{ + items: []*ConntrackStat{}, + } +} + +func (l *ConntrackStatList) Append(c *ConntrackStat) { + l.items = append(l.items, c) +} + +func (l *ConntrackStatList) Items() []ConntrackStat { + items := make([]ConntrackStat, len(l.items), len(l.items)) + for i, el := range l.items { + items[i] = *el + } + return items +} + +// Summary returns a single-element list with totals from all list items. +func (l *ConntrackStatList) Summary() []ConntrackStat { + summary := NewConntrackStat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + for _, cs := range l.items { + summary.Entries += cs.Entries + summary.Searched += cs.Searched + summary.Found += cs.Found + summary.New += cs.New + summary.Invalid += cs.Invalid + summary.Ignore += cs.Ignore + summary.Delete += cs.Delete + summary.DeleteList += cs.DeleteList + summary.Insert += cs.Insert + summary.InsertFailed += cs.InsertFailed + summary.Drop += cs.Drop + summary.EarlyDrop += cs.EarlyDrop + summary.IcmpError += cs.IcmpError + summary.ExpectNew += cs.ExpectNew + summary.ExpectCreate += cs.ExpectCreate + summary.ExpectDelete += cs.ExpectDelete + summary.SearchRestart += cs.SearchRestart + } + return []ConntrackStat{*summary} +} + +func (n IOCountersStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func (n ConnectionStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func (n ProtoCountersStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func (a Addr) String() string { + s, _ := json.Marshal(a) + return string(s) +} + +func (n InterfaceStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func (n InterfaceAddr) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func (n ConntrackStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func Interfaces() ([]InterfaceStat, error) { + return InterfacesWithContext(context.Background()) +} + +func InterfacesWithContext(ctx context.Context) ([]InterfaceStat, error) { + is, err := net.Interfaces() + if err != nil { + return nil, err + } + ret := make([]InterfaceStat, 0, len(is)) + for _, ifi := range is { + + var flags []string + if ifi.Flags&net.FlagUp != 0 { + flags = append(flags, "up") + } + if ifi.Flags&net.FlagBroadcast != 0 { + flags = append(flags, "broadcast") + } + if ifi.Flags&net.FlagLoopback != 0 { + flags = append(flags, "loopback") + } + if ifi.Flags&net.FlagPointToPoint != 0 { + flags = append(flags, "pointtopoint") + } + if ifi.Flags&net.FlagMulticast != 0 { + flags = append(flags, "multicast") + } + + r := InterfaceStat{ + Index: ifi.Index, + Name: ifi.Name, + MTU: ifi.MTU, + HardwareAddr: ifi.HardwareAddr.String(), + Flags: flags, + } + addrs, err := ifi.Addrs() + if err == nil { + r.Addrs = make([]InterfaceAddr, 0, len(addrs)) + for _, addr := range addrs { + r.Addrs = append(r.Addrs, InterfaceAddr{ + Addr: addr.String(), + }) + } + + } + ret = append(ret, r) + } + + return ret, nil +} + +func getIOCountersAll(n []IOCountersStat) ([]IOCountersStat, error) { + r := IOCountersStat{ + Name: "all", + } + for _, nic := range n { + r.BytesRecv += nic.BytesRecv + r.PacketsRecv += nic.PacketsRecv + r.Errin += nic.Errin + r.Dropin += nic.Dropin + r.BytesSent += nic.BytesSent + r.PacketsSent += nic.PacketsSent + r.Errout += nic.Errout + r.Dropout += nic.Dropout + } + + return []IOCountersStat{r}, nil +} diff --git a/vendor/github.com/shirou/gopsutil/net/net_aix.go b/vendor/github.com/shirou/gopsutil/net/net_aix.go new file mode 100644 index 00000000000..4ac8497015e --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/net/net_aix.go @@ -0,0 +1,425 @@ +// +build aix + +package net + +import ( + "context" + "fmt" + "os/exec" + "regexp" + "strconv" + "strings" + "syscall" + + "github.com/shirou/gopsutil/internal/common" +) + +func parseNetstatI(output string) ([]IOCountersStat, error) { + lines := strings.Split(string(output), "\n") + ret := make([]IOCountersStat, 0, len(lines)-1) + exists := make([]string, 0, len(ret)) + + // Check first line is header + if len(lines) > 0 && strings.Fields(lines[0])[0] != "Name" { + return nil, fmt.Errorf("not a 'netstat -i' output") + } + + for _, line := range lines[1:] { + values := strings.Fields(line) + if len(values) < 1 || values[0] == "Name" { + continue + } + if common.StringsHas(exists, values[0]) { + // skip if already get + continue + } + exists = append(exists, values[0]) + + if len(values) < 9 { + continue + } + + base := 1 + // sometimes Address is omitted + if len(values) < 10 { + base = 0 + } + + parsed := make([]uint64, 0, 5) + vv := []string{ + values[base+3], // Ipkts == PacketsRecv + values[base+4], // Ierrs == Errin + values[base+5], // Opkts == PacketsSent + values[base+6], // Oerrs == Errout + values[base+8], // Drops == Dropout + } + + for _, target := range vv { + if target == "-" { + parsed = append(parsed, 0) + continue + } + + t, err := strconv.ParseUint(target, 10, 64) + if err != nil { + return nil, err + } + parsed = append(parsed, t) + } + + n := IOCountersStat{ + Name: values[0], + PacketsRecv: parsed[0], + Errin: parsed[1], + PacketsSent: parsed[2], + Errout: parsed[3], + Dropout: parsed[4], + } + ret = append(ret, n) + } + return ret, nil +} + +func IOCounters(pernic bool) ([]IOCountersStat, error) { + return IOCountersWithContext(context.Background(), pernic) +} + +func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { + netstat, err := exec.LookPath("netstat") + if err != nil { + return nil, err + } + out, err := invoke.CommandWithContext(ctx, netstat, "-idn") + if err != nil { + return nil, err + } + + iocounters, err := parseNetstatI(string(out)) + if err != nil { + return nil, err + } + if pernic == false { + return getIOCountersAll(iocounters) + } + return iocounters, nil + +} + +// NetIOCountersByFile is an method which is added just a compatibility for linux. +func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { + return IOCountersByFileWithContext(context.Background(), pernic, filename) +} + +func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) { + return IOCounters(pernic) +} + +func FilterCounters() ([]FilterStat, error) { + return FilterCountersWithContext(context.Background()) +} + +func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { + return nil, common.ErrNotImplementedError +} + +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, common.ErrNotImplementedError +} + +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { + return ProtoCountersWithContext(context.Background(), protocols) +} + +func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) { + return nil, common.ErrNotImplementedError +} + +func parseNetstatNetLine(line string) (ConnectionStat, error) { + f := strings.Fields(line) + if len(f) < 5 { + return ConnectionStat{}, fmt.Errorf("wrong line,%s", line) + } + + var netType, netFamily uint32 + switch f[0] { + case "tcp", "tcp4": + netType = syscall.SOCK_STREAM + netFamily = syscall.AF_INET + case "udp", "udp4": + netType = syscall.SOCK_DGRAM + netFamily = syscall.AF_INET + case "tcp6": + netType = syscall.SOCK_STREAM + netFamily = syscall.AF_INET6 + case "udp6": + netType = syscall.SOCK_DGRAM + netFamily = syscall.AF_INET6 + default: + return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[0]) + } + + laddr, raddr, err := parseNetstatAddr(f[3], f[4], netFamily) + if err != nil { + return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s %s", f[3], f[4]) + } + + n := ConnectionStat{ + Fd: uint32(0), // not supported + Family: uint32(netFamily), + Type: uint32(netType), + Laddr: laddr, + Raddr: raddr, + Pid: int32(0), // not supported + } + if len(f) == 6 { + n.Status = f[5] + } + + return n, nil +} + +var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`) + +// This function only works for netstat returning addresses with a "." +// before the port (0.0.0.0.22 instead of 0.0.0.0:22). +func parseNetstatAddr(local string, remote string, family uint32) (laddr Addr, raddr Addr, err error) { + parse := func(l string) (Addr, error) { + matches := portMatch.FindStringSubmatch(l) + if matches == nil { + return Addr{}, fmt.Errorf("wrong addr, %s", l) + } + host := matches[1] + port := matches[2] + if host == "*" { + switch family { + case syscall.AF_INET: + host = "0.0.0.0" + case syscall.AF_INET6: + host = "::" + default: + return Addr{}, fmt.Errorf("unknown family, %d", family) + } + } + lport, err := strconv.Atoi(port) + if err != nil { + return Addr{}, err + } + return Addr{IP: host, Port: uint32(lport)}, nil + } + + laddr, err = parse(local) + if remote != "*.*" { // remote addr exists + raddr, err = parse(remote) + if err != nil { + return laddr, raddr, err + } + } + + return laddr, raddr, err +} + +func parseNetstatUnixLine(f []string) (ConnectionStat, error) { + if len(f) < 8 { + return ConnectionStat{}, fmt.Errorf("wrong number of fields: expected >=8 got %d", len(f)) + } + + var netType uint32 + + switch f[1] { + case "dgram": + netType = syscall.SOCK_DGRAM + case "stream": + netType = syscall.SOCK_STREAM + default: + return ConnectionStat{}, fmt.Errorf("unknown type: %s", f[1]) + } + + // Some Unix Socket don't have any address associated + addr := "" + if len(f) == 9 { + addr = f[8] + } + + c := ConnectionStat{ + Fd: uint32(0), // not supported + Family: uint32(syscall.AF_UNIX), + Type: uint32(netType), + Laddr: Addr{ + IP: addr, + }, + Status: "NONE", + Pid: int32(0), // not supported + } + + return c, nil +} + +// Return true if proto is the corresponding to the kind parameter +// Only for Inet lines +func hasCorrectInetProto(kind, proto string) bool { + switch kind { + case "all", "inet": + return true + case "unix": + return false + case "inet4": + return !strings.HasSuffix(proto, "6") + case "inet6": + return strings.HasSuffix(proto, "6") + case "tcp": + return proto == "tcp" || proto == "tcp4" || proto == "tcp6" + case "tcp4": + return proto == "tcp" || proto == "tcp4" + case "tcp6": + return proto == "tcp6" + case "udp": + return proto == "udp" || proto == "udp4" || proto == "udp6" + case "udp4": + return proto == "udp" || proto == "udp4" + case "udp6": + return proto == "udp6" + } + return false +} + +func parseNetstatA(output string, kind string) ([]ConnectionStat, error) { + var ret []ConnectionStat + lines := strings.Split(string(output), "\n") + + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) < 1 { + continue + } + + if strings.HasPrefix(fields[0], "f1") { + // Unix lines + if len(fields) < 2 { + // every unix connections have two lines + continue + } + + c, err := parseNetstatUnixLine(fields) + if err != nil { + return nil, fmt.Errorf("failed to parse Unix Address (%s): %s", line, err) + } + + ret = append(ret, c) + + } else if strings.HasPrefix(fields[0], "tcp") || strings.HasPrefix(fields[0], "udp") { + // Inet lines + if !hasCorrectInetProto(kind, fields[0]) { + continue + } + + // On AIX, netstat display some connections with "*.*" as local addresses + // Skip them as they aren't real connections. + if fields[3] == "*.*" { + continue + } + + c, err := parseNetstatNetLine(line) + if err != nil { + return nil, fmt.Errorf("failed to parse Inet Address (%s): %s", line, err) + } + + ret = append(ret, c) + } else { + // Header lines + continue + } + } + + return ret, nil + +} + +func Connections(kind string) ([]ConnectionStat, error) { + return ConnectionsWithContext(context.Background(), kind) +} + +func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + + args := []string{"-na"} + switch strings.ToLower(kind) { + default: + fallthrough + case "": + kind = "all" + case "all": + // nothing to add + case "inet", "inet4", "inet6": + args = append(args, "-finet") + case "tcp", "tcp4", "tcp6": + args = append(args, "-finet") + case "udp", "udp4", "udp6": + args = append(args, "-finet") + case "unix": + args = append(args, "-funix") + } + + netstat, err := exec.LookPath("netstat") + if err != nil { + return nil, err + } + out, err := invoke.CommandWithContext(ctx, netstat, args...) + + if err != nil { + return nil, err + } + + ret, err := parseNetstatA(string(out), kind) + if err != nil { + return nil, err + } + + return ret, nil + +} + +func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) { + return ConnectionsMaxWithContext(context.Background(), kind, max) +} + +func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} + +// Return a list of network connections opened, omitting `Uids`. +// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be +// removed from the API in the future. +func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) { + return ConnectionsWithoutUidsWithContext(context.Background(), kind) +} + +func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0) +} + +func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max) +} + +func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid) +} + +func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0) +} + +func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max) +} + +func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max) +} + +func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/net/net_darwin.go b/vendor/github.com/shirou/gopsutil/net/net_darwin.go new file mode 100644 index 00000000000..1daed86982e --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/net/net_darwin.go @@ -0,0 +1,293 @@ +// +build darwin + +package net + +import ( + "context" + "errors" + "fmt" + "github.com/shirou/gopsutil/internal/common" + "os/exec" + "regexp" + "strconv" + "strings" +) + +var ( + errNetstatHeader = errors.New("Can't parse header of netstat output") + netstatLinkRegexp = regexp.MustCompile(`^$`) +) + +const endOfLine = "\n" + +func parseNetstatLine(line string) (stat *IOCountersStat, linkID *uint, err error) { + var ( + numericValue uint64 + columns = strings.Fields(line) + ) + + if columns[0] == "Name" { + err = errNetstatHeader + return + } + + // try to extract the numeric value from + if subMatch := netstatLinkRegexp.FindStringSubmatch(columns[2]); len(subMatch) == 2 { + numericValue, err = strconv.ParseUint(subMatch[1], 10, 64) + if err != nil { + return + } + linkIDUint := uint(numericValue) + linkID = &linkIDUint + } + + base := 1 + numberColumns := len(columns) + // sometimes Address is omitted + if numberColumns < 12 { + base = 0 + } + if numberColumns < 11 || numberColumns > 13 { + err = fmt.Errorf("Line %q do have an invalid number of columns %d", line, numberColumns) + return + } + + parsed := make([]uint64, 0, 7) + vv := []string{ + columns[base+3], // Ipkts == PacketsRecv + columns[base+4], // Ierrs == Errin + columns[base+5], // Ibytes == BytesRecv + columns[base+6], // Opkts == PacketsSent + columns[base+7], // Oerrs == Errout + columns[base+8], // Obytes == BytesSent + } + if len(columns) == 12 { + vv = append(vv, columns[base+10]) + } + + for _, target := range vv { + if target == "-" { + parsed = append(parsed, 0) + continue + } + + if numericValue, err = strconv.ParseUint(target, 10, 64); err != nil { + return + } + parsed = append(parsed, numericValue) + } + + stat = &IOCountersStat{ + Name: strings.Trim(columns[0], "*"), // remove the * that sometimes is on right on interface + PacketsRecv: parsed[0], + Errin: parsed[1], + BytesRecv: parsed[2], + PacketsSent: parsed[3], + Errout: parsed[4], + BytesSent: parsed[5], + } + if len(parsed) == 7 { + stat.Dropout = parsed[6] + } + return +} + +type netstatInterface struct { + linkID *uint + stat *IOCountersStat +} + +func parseNetstatOutput(output string) ([]netstatInterface, error) { + var ( + err error + lines = strings.Split(strings.Trim(output, endOfLine), endOfLine) + ) + + // number of interfaces is number of lines less one for the header + numberInterfaces := len(lines) - 1 + + interfaces := make([]netstatInterface, numberInterfaces) + // no output beside header + if numberInterfaces == 0 { + return interfaces, nil + } + + for index := 0; index < numberInterfaces; index++ { + nsIface := netstatInterface{} + if nsIface.stat, nsIface.linkID, err = parseNetstatLine(lines[index+1]); err != nil { + return nil, err + } + interfaces[index] = nsIface + } + return interfaces, nil +} + +// map that hold the name of a network interface and the number of usage +type mapInterfaceNameUsage map[string]uint + +func newMapInterfaceNameUsage(ifaces []netstatInterface) mapInterfaceNameUsage { + output := make(mapInterfaceNameUsage) + for index := range ifaces { + if ifaces[index].linkID != nil { + ifaceName := ifaces[index].stat.Name + usage, ok := output[ifaceName] + if ok { + output[ifaceName] = usage + 1 + } else { + output[ifaceName] = 1 + } + } + } + return output +} + +func (min mapInterfaceNameUsage) isTruncated() bool { + for _, usage := range min { + if usage > 1 { + return true + } + } + return false +} + +func (min mapInterfaceNameUsage) notTruncated() []string { + output := make([]string, 0) + for ifaceName, usage := range min { + if usage == 1 { + output = append(output, ifaceName) + } + } + return output +} + +// example of `netstat -ibdnW` output on yosemite +// Name Mtu Network Address Ipkts Ierrs Ibytes Opkts Oerrs Obytes Coll Drop +// lo0 16384 869107 0 169411755 869107 0 169411755 0 0 +// lo0 16384 ::1/128 ::1 869107 - 169411755 869107 - 169411755 - - +// lo0 16384 127 127.0.0.1 869107 - 169411755 869107 - 169411755 - - +func IOCounters(pernic bool) ([]IOCountersStat, error) { + return IOCountersWithContext(context.Background(), pernic) +} + +func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { + var ( + ret []IOCountersStat + retIndex int + ) + + netstat, err := exec.LookPath("netstat") + if err != nil { + return nil, err + } + + // try to get all interface metrics, and hope there won't be any truncated + out, err := invoke.CommandWithContext(ctx, netstat, "-ibdnW") + if err != nil { + return nil, err + } + + nsInterfaces, err := parseNetstatOutput(string(out)) + if err != nil { + return nil, err + } + + ifaceUsage := newMapInterfaceNameUsage(nsInterfaces) + notTruncated := ifaceUsage.notTruncated() + ret = make([]IOCountersStat, len(notTruncated)) + + if !ifaceUsage.isTruncated() { + // no truncated interface name, return stats of all interface with + for index := range nsInterfaces { + if nsInterfaces[index].linkID != nil { + ret[retIndex] = *nsInterfaces[index].stat + retIndex++ + } + } + } else { + // duplicated interface, list all interfaces + ifconfig, err := exec.LookPath("ifconfig") + if err != nil { + return nil, err + } + if out, err = invoke.CommandWithContext(ctx, ifconfig, "-l"); err != nil { + return nil, err + } + interfaceNames := strings.Fields(strings.TrimRight(string(out), endOfLine)) + + // for each of the interface name, run netstat if we don't have any stats yet + for _, interfaceName := range interfaceNames { + truncated := true + for index := range nsInterfaces { + if nsInterfaces[index].linkID != nil && nsInterfaces[index].stat.Name == interfaceName { + // handle the non truncated name to avoid execute netstat for them again + ret[retIndex] = *nsInterfaces[index].stat + retIndex++ + truncated = false + break + } + } + if truncated { + // run netstat with -I$ifacename + if out, err = invoke.CommandWithContext(ctx, netstat, "-ibdnWI"+interfaceName); err != nil { + return nil, err + } + parsedIfaces, err := parseNetstatOutput(string(out)) + if err != nil { + return nil, err + } + if len(parsedIfaces) == 0 { + // interface had been removed since `ifconfig -l` had been executed + continue + } + for index := range parsedIfaces { + if parsedIfaces[index].linkID != nil { + ret = append(ret, *parsedIfaces[index].stat) + break + } + } + } + } + } + + if pernic == false { + return getIOCountersAll(ret) + } + return ret, nil +} + +// NetIOCountersByFile is an method which is added just a compatibility for linux. +func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { + return IOCountersByFileWithContext(context.Background(), pernic, filename) +} + +func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) { + return IOCounters(pernic) +} + +func FilterCounters() ([]FilterStat, error) { + return FilterCountersWithContext(context.Background()) +} + +func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { + return nil, errors.New("NetFilterCounters not implemented for darwin") +} + +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, common.ErrNotImplementedError +} + +// NetProtoCounters returns network statistics for the entire system +// If protocols is empty then all protocols are returned, otherwise +// just the protocols in the list are returned. +// Not Implemented for Darwin +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { + return ProtoCountersWithContext(context.Background(), protocols) +} + +func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) { + return nil, errors.New("NetProtoCounters not implemented for darwin") +} diff --git a/vendor/github.com/shirou/gopsutil/net/net_fallback.go b/vendor/github.com/shirou/gopsutil/net/net_fallback.go new file mode 100644 index 00000000000..7d9a2659182 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/net/net_fallback.go @@ -0,0 +1,92 @@ +// +build !aix,!darwin,!linux,!freebsd,!openbsd,!windows + +package net + +import ( + "context" + + "github.com/shirou/gopsutil/internal/common" +) + +func IOCounters(pernic bool) ([]IOCountersStat, error) { + return IOCountersWithContext(context.Background(), pernic) +} + +func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { + return []IOCountersStat{}, common.ErrNotImplementedError +} + +func FilterCounters() ([]FilterStat, error) { + return FilterCountersWithContext(context.Background()) +} + +func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { + return []FilterStat{}, common.ErrNotImplementedError +} + +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, common.ErrNotImplementedError +} + +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { + return ProtoCountersWithContext(context.Background(), protocols) +} + +func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) { + return []ProtoCountersStat{}, common.ErrNotImplementedError +} + +func Connections(kind string) ([]ConnectionStat, error) { + return ConnectionsWithContext(context.Background(), kind) +} + +func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} + +func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) { + return ConnectionsMaxWithContext(context.Background(), kind, max) +} + +func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} + +// Return a list of network connections opened, omitting `Uids`. +// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be +// removed from the API in the future. +func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) { + return ConnectionsWithoutUidsWithContext(context.Background(), kind) +} + +func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0) +} + +func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max) +} + +func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid) +} + +func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0) +} + +func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max) +} + +func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max) +} + +func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/net/net_freebsd.go b/vendor/github.com/shirou/gopsutil/net/net_freebsd.go new file mode 100644 index 00000000000..2284d982c82 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/net/net_freebsd.go @@ -0,0 +1,133 @@ +// +build freebsd + +package net + +import ( + "context" + "errors" + "os/exec" + "strconv" + "strings" + + "github.com/shirou/gopsutil/internal/common" +) + +func IOCounters(pernic bool) ([]IOCountersStat, error) { + return IOCountersWithContext(context.Background(), pernic) +} + +func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { + netstat, err := exec.LookPath("netstat") + if err != nil { + return nil, err + } + out, err := invoke.CommandWithContext(ctx, netstat, "-ibdnW") + if err != nil { + return nil, err + } + + lines := strings.Split(string(out), "\n") + ret := make([]IOCountersStat, 0, len(lines)-1) + exists := make([]string, 0, len(ret)) + + for _, line := range lines { + values := strings.Fields(line) + if len(values) < 1 || values[0] == "Name" { + continue + } + if common.StringsHas(exists, values[0]) { + // skip if already get + continue + } + exists = append(exists, values[0]) + + if len(values) < 12 { + continue + } + base := 1 + // sometimes Address is omitted + if len(values) < 13 { + base = 0 + } + + parsed := make([]uint64, 0, 8) + vv := []string{ + values[base+3], // PacketsRecv + values[base+4], // Errin + values[base+5], // Dropin + values[base+6], // BytesRecvn + values[base+7], // PacketSent + values[base+8], // Errout + values[base+9], // BytesSent + values[base+11], // Dropout + } + for _, target := range vv { + if target == "-" { + parsed = append(parsed, 0) + continue + } + + t, err := strconv.ParseUint(target, 10, 64) + if err != nil { + return nil, err + } + parsed = append(parsed, t) + } + + n := IOCountersStat{ + Name: values[0], + PacketsRecv: parsed[0], + Errin: parsed[1], + Dropin: parsed[2], + BytesRecv: parsed[3], + PacketsSent: parsed[4], + Errout: parsed[5], + BytesSent: parsed[6], + Dropout: parsed[7], + } + ret = append(ret, n) + } + + if pernic == false { + return getIOCountersAll(ret) + } + + return ret, nil +} + +// NetIOCountersByFile is an method which is added just a compatibility for linux. +func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { + return IOCountersByFileWithContext(context.Background(), pernic, filename) +} + +func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) { + return IOCounters(pernic) +} + +func FilterCounters() ([]FilterStat, error) { + return FilterCountersWithContext(context.Background()) +} + +func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { + return nil, errors.New("NetFilterCounters not implemented for freebsd") +} + +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, errors.New("ConntrackStats not implemented for freebsd") +} + +// NetProtoCounters returns network statistics for the entire system +// If protocols is empty then all protocols are returned, otherwise +// just the protocols in the list are returned. +// Not Implemented for FreeBSD +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { + return ProtoCountersWithContext(context.Background(), protocols) +} + +func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) { + return nil, errors.New("NetProtoCounters not implemented for freebsd") +} diff --git a/vendor/github.com/shirou/gopsutil/net/net_linux.go b/vendor/github.com/shirou/gopsutil/net/net_linux.go new file mode 100644 index 00000000000..f289a5dcc19 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/net/net_linux.go @@ -0,0 +1,884 @@ +// +build linux + +package net + +import ( + "bytes" + "context" + "encoding/hex" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "os" + "strconv" + "strings" + "syscall" + + "github.com/shirou/gopsutil/internal/common" +) + +const ( // Conntrack Column numbers + CT_ENTRIES = iota + CT_SEARCHED + CT_FOUND + CT_NEW + CT_INVALID + CT_IGNORE + CT_DELETE + CT_DELETE_LIST + CT_INSERT + CT_INSERT_FAILED + CT_DROP + CT_EARLY_DROP + CT_ICMP_ERROR + CT_EXPECT_NEW + CT_EXPECT_CREATE + CT_EXPECT_DELETE + CT_SEARCH_RESTART +) + +// NetIOCounters returnes network I/O statistics for every network +// interface installed on the system. If pernic argument is false, +// return only sum of all information (which name is 'all'). If true, +// every network interface installed on the system is returned +// separately. +func IOCounters(pernic bool) ([]IOCountersStat, error) { + return IOCountersWithContext(context.Background(), pernic) +} + +func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { + filename := common.HostProc("net/dev") + return IOCountersByFileWithContext(ctx, pernic, filename) +} + +func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { + return IOCountersByFileWithContext(context.Background(), pernic, filename) +} + +func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) { + lines, err := common.ReadLines(filename) + if err != nil { + return nil, err + } + + parts := make([]string, 2) + + statlen := len(lines) - 1 + + ret := make([]IOCountersStat, 0, statlen) + + for _, line := range lines[2:] { + separatorPos := strings.LastIndex(line, ":") + if separatorPos == -1 { + continue + } + parts[0] = line[0:separatorPos] + parts[1] = line[separatorPos+1:] + + interfaceName := strings.TrimSpace(parts[0]) + if interfaceName == "" { + continue + } + + fields := strings.Fields(strings.TrimSpace(parts[1])) + bytesRecv, err := strconv.ParseUint(fields[0], 10, 64) + if err != nil { + return ret, err + } + packetsRecv, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + return ret, err + } + errIn, err := strconv.ParseUint(fields[2], 10, 64) + if err != nil { + return ret, err + } + dropIn, err := strconv.ParseUint(fields[3], 10, 64) + if err != nil { + return ret, err + } + fifoIn, err := strconv.ParseUint(fields[4], 10, 64) + if err != nil { + return ret, err + } + bytesSent, err := strconv.ParseUint(fields[8], 10, 64) + if err != nil { + return ret, err + } + packetsSent, err := strconv.ParseUint(fields[9], 10, 64) + if err != nil { + return ret, err + } + errOut, err := strconv.ParseUint(fields[10], 10, 64) + if err != nil { + return ret, err + } + dropOut, err := strconv.ParseUint(fields[11], 10, 64) + if err != nil { + return ret, err + } + fifoOut, err := strconv.ParseUint(fields[12], 10, 64) + if err != nil { + return ret, err + } + + nic := IOCountersStat{ + Name: interfaceName, + BytesRecv: bytesRecv, + PacketsRecv: packetsRecv, + Errin: errIn, + Dropin: dropIn, + Fifoin: fifoIn, + BytesSent: bytesSent, + PacketsSent: packetsSent, + Errout: errOut, + Dropout: dropOut, + Fifoout: fifoOut, + } + ret = append(ret, nic) + } + + if pernic == false { + return getIOCountersAll(ret) + } + + return ret, nil +} + +var netProtocols = []string{ + "ip", + "icmp", + "icmpmsg", + "tcp", + "udp", + "udplite", +} + +// NetProtoCounters returns network statistics for the entire system +// If protocols is empty then all protocols are returned, otherwise +// just the protocols in the list are returned. +// Available protocols: +// ip,icmp,icmpmsg,tcp,udp,udplite +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { + return ProtoCountersWithContext(context.Background(), protocols) +} + +func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) { + if len(protocols) == 0 { + protocols = netProtocols + } + + stats := make([]ProtoCountersStat, 0, len(protocols)) + protos := make(map[string]bool, len(protocols)) + for _, p := range protocols { + protos[p] = true + } + + filename := common.HostProc("net/snmp") + lines, err := common.ReadLines(filename) + if err != nil { + return nil, err + } + + linecount := len(lines) + for i := 0; i < linecount; i++ { + line := lines[i] + r := strings.IndexRune(line, ':') + if r == -1 { + return nil, errors.New(filename + " is not fomatted correctly, expected ':'.") + } + proto := strings.ToLower(line[:r]) + if !protos[proto] { + // skip protocol and data line + i++ + continue + } + + // Read header line + statNames := strings.Split(line[r+2:], " ") + + // Read data line + i++ + statValues := strings.Split(lines[i][r+2:], " ") + if len(statNames) != len(statValues) { + return nil, errors.New(filename + " is not fomatted correctly, expected same number of columns.") + } + stat := ProtoCountersStat{ + Protocol: proto, + Stats: make(map[string]int64, len(statNames)), + } + for j := range statNames { + value, err := strconv.ParseInt(statValues[j], 10, 64) + if err != nil { + return nil, err + } + stat.Stats[statNames[j]] = value + } + stats = append(stats, stat) + } + return stats, nil +} + +// NetFilterCounters returns iptables conntrack statistics +// the currently in use conntrack count and the max. +// If the file does not exist or is invalid it will return nil. +func FilterCounters() ([]FilterStat, error) { + return FilterCountersWithContext(context.Background()) +} + +func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { + countfile := common.HostProc("sys/net/netfilter/nf_conntrack_count") + maxfile := common.HostProc("sys/net/netfilter/nf_conntrack_max") + + count, err := common.ReadInts(countfile) + + if err != nil { + return nil, err + } + stats := make([]FilterStat, 0, 1) + + max, err := common.ReadInts(maxfile) + if err != nil { + return nil, err + } + + payload := FilterStat{ + ConnTrackCount: count[0], + ConnTrackMax: max[0], + } + + stats = append(stats, payload) + return stats, nil +} + +// ConntrackStats returns more detailed info about the conntrack table +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +// ConntrackStatsWithContext returns more detailed info about the conntrack table +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return conntrackStatsFromFile(common.HostProc("net/stat/nf_conntrack"), percpu) +} + +// conntrackStatsFromFile returns more detailed info about the conntrack table +// from `filename` +// If 'percpu' is false, the result will contain exactly one item with totals/summary +func conntrackStatsFromFile(filename string, percpu bool) ([]ConntrackStat, error) { + lines, err := common.ReadLines(filename) + if err != nil { + return nil, err + } + + statlist := NewConntrackStatList() + + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) == 17 && fields[0] != "entries" { + statlist.Append(NewConntrackStat( + common.HexToUint32(fields[CT_ENTRIES]), + common.HexToUint32(fields[CT_SEARCHED]), + common.HexToUint32(fields[CT_FOUND]), + common.HexToUint32(fields[CT_NEW]), + common.HexToUint32(fields[CT_INVALID]), + common.HexToUint32(fields[CT_IGNORE]), + common.HexToUint32(fields[CT_DELETE]), + common.HexToUint32(fields[CT_DELETE_LIST]), + common.HexToUint32(fields[CT_INSERT]), + common.HexToUint32(fields[CT_INSERT_FAILED]), + common.HexToUint32(fields[CT_DROP]), + common.HexToUint32(fields[CT_EARLY_DROP]), + common.HexToUint32(fields[CT_ICMP_ERROR]), + common.HexToUint32(fields[CT_EXPECT_NEW]), + common.HexToUint32(fields[CT_EXPECT_CREATE]), + common.HexToUint32(fields[CT_EXPECT_DELETE]), + common.HexToUint32(fields[CT_SEARCH_RESTART]), + )) + } + } + + if percpu { + return statlist.Items(), nil + } + return statlist.Summary(), nil +} + +// http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h +var TCPStatuses = map[string]string{ + "01": "ESTABLISHED", + "02": "SYN_SENT", + "03": "SYN_RECV", + "04": "FIN_WAIT1", + "05": "FIN_WAIT2", + "06": "TIME_WAIT", + "07": "CLOSE", + "08": "CLOSE_WAIT", + "09": "LAST_ACK", + "0A": "LISTEN", + "0B": "CLOSING", +} + +type netConnectionKindType struct { + family uint32 + sockType uint32 + filename string +} + +var kindTCP4 = netConnectionKindType{ + family: syscall.AF_INET, + sockType: syscall.SOCK_STREAM, + filename: "tcp", +} +var kindTCP6 = netConnectionKindType{ + family: syscall.AF_INET6, + sockType: syscall.SOCK_STREAM, + filename: "tcp6", +} +var kindUDP4 = netConnectionKindType{ + family: syscall.AF_INET, + sockType: syscall.SOCK_DGRAM, + filename: "udp", +} +var kindUDP6 = netConnectionKindType{ + family: syscall.AF_INET6, + sockType: syscall.SOCK_DGRAM, + filename: "udp6", +} +var kindUNIX = netConnectionKindType{ + family: syscall.AF_UNIX, + filename: "unix", +} + +var netConnectionKindMap = map[string][]netConnectionKindType{ + "all": {kindTCP4, kindTCP6, kindUDP4, kindUDP6, kindUNIX}, + "tcp": {kindTCP4, kindTCP6}, + "tcp4": {kindTCP4}, + "tcp6": {kindTCP6}, + "udp": {kindUDP4, kindUDP6}, + "udp4": {kindUDP4}, + "udp6": {kindUDP6}, + "unix": {kindUNIX}, + "inet": {kindTCP4, kindTCP6, kindUDP4, kindUDP6}, + "inet4": {kindTCP4, kindUDP4}, + "inet6": {kindTCP6, kindUDP6}, +} + +type inodeMap struct { + pid int32 + fd uint32 +} + +type connTmp struct { + fd uint32 + family uint32 + sockType uint32 + laddr Addr + raddr Addr + status string + pid int32 + boundPid int32 + path string +} + +// Return a list of network connections opened. +func Connections(kind string) ([]ConnectionStat, error) { + return ConnectionsWithContext(context.Background(), kind) +} + +func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return ConnectionsPid(kind, 0) +} + +// Return a list of network connections opened returning at most `max` +// connections for each running process. +func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) { + return ConnectionsMaxWithContext(context.Background(), kind, max) +} + +func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return ConnectionsPidMax(kind, 0, max) +} + +// Return a list of network connections opened, omitting `Uids`. +// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be +// removed from the API in the future. +func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) { + return ConnectionsWithoutUidsWithContext(context.Background(), kind) +} + +func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0) +} + +func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max) +} + +// Return a list of network connections opened by a process. +func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidWithContext(context.Background(), kind, pid) +} + +func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid) +} + +func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithContext(ctx, kind, pid, 0) +} + +func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0) +} + +// Return up to `max` network connections opened by a process. +func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithContext(context.Background(), kind, pid, max) +} + +func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max) +} + +func ConnectionsPidMaxWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max, false) +} + +func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max, true) +} + +func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int, skipUids bool) ([]ConnectionStat, error) { + tmap, ok := netConnectionKindMap[kind] + if !ok { + return nil, fmt.Errorf("invalid kind, %s", kind) + } + root := common.HostProc() + var err error + var inodes map[string][]inodeMap + if pid == 0 { + inodes, err = getProcInodesAll(root, max) + } else { + inodes, err = getProcInodes(root, pid, max) + if len(inodes) == 0 { + // no connection for the pid + return []ConnectionStat{}, nil + } + } + if err != nil { + return nil, fmt.Errorf("cound not get pid(s), %d: %s", pid, err) + } + return statsFromInodes(root, pid, tmap, inodes, skipUids) +} + +func statsFromInodes(root string, pid int32, tmap []netConnectionKindType, inodes map[string][]inodeMap, skipUids bool) ([]ConnectionStat, error) { + dupCheckMap := make(map[string]struct{}) + var ret []ConnectionStat + + var err error + for _, t := range tmap { + var path string + var connKey string + var ls []connTmp + if pid == 0 { + path = fmt.Sprintf("%s/net/%s", root, t.filename) + } else { + path = fmt.Sprintf("%s/%d/net/%s", root, pid, t.filename) + } + switch t.family { + case syscall.AF_INET, syscall.AF_INET6: + ls, err = processInet(path, t, inodes, pid) + case syscall.AF_UNIX: + ls, err = processUnix(path, t, inodes, pid) + } + if err != nil { + return nil, err + } + for _, c := range ls { + // Build TCP key to id the connection uniquely + // socket type, src ip, src port, dst ip, dst port and state should be enough + // to prevent duplications. + connKey = fmt.Sprintf("%d-%s:%d-%s:%d-%s", c.sockType, c.laddr.IP, c.laddr.Port, c.raddr.IP, c.raddr.Port, c.status) + if _, ok := dupCheckMap[connKey]; ok { + continue + } + + conn := ConnectionStat{ + Fd: c.fd, + Family: c.family, + Type: c.sockType, + Laddr: c.laddr, + Raddr: c.raddr, + Status: c.status, + Pid: c.pid, + } + if c.pid == 0 { + conn.Pid = c.boundPid + } else { + conn.Pid = c.pid + } + + if !skipUids { + // fetch process owner Real, effective, saved set, and filesystem UIDs + proc := process{Pid: conn.Pid} + conn.Uids, _ = proc.getUids() + } + + ret = append(ret, conn) + dupCheckMap[connKey] = struct{}{} + } + + } + + return ret, nil +} + +// getProcInodes returnes fd of the pid. +func getProcInodes(root string, pid int32, max int) (map[string][]inodeMap, error) { + ret := make(map[string][]inodeMap) + + dir := fmt.Sprintf("%s/%d/fd", root, pid) + f, err := os.Open(dir) + if err != nil { + return ret, err + } + defer f.Close() + files, err := f.Readdir(max) + if err != nil { + return ret, err + } + for _, fd := range files { + inodePath := fmt.Sprintf("%s/%d/fd/%s", root, pid, fd.Name()) + + inode, err := os.Readlink(inodePath) + if err != nil { + continue + } + if !strings.HasPrefix(inode, "socket:[") { + continue + } + // the process is using a socket + l := len(inode) + inode = inode[8 : l-1] + _, ok := ret[inode] + if !ok { + ret[inode] = make([]inodeMap, 0) + } + fd, err := strconv.Atoi(fd.Name()) + if err != nil { + continue + } + + i := inodeMap{ + pid: pid, + fd: uint32(fd), + } + ret[inode] = append(ret[inode], i) + } + return ret, nil +} + +// Pids retunres all pids. +// Note: this is a copy of process_linux.Pids() +// FIXME: Import process occures import cycle. +// move to common made other platform breaking. Need consider. +func Pids() ([]int32, error) { + return PidsWithContext(context.Background()) +} + +func PidsWithContext(ctx context.Context) ([]int32, error) { + var ret []int32 + + d, err := os.Open(common.HostProc()) + if err != nil { + return nil, err + } + defer d.Close() + + fnames, err := d.Readdirnames(-1) + if err != nil { + return nil, err + } + for _, fname := range fnames { + pid, err := strconv.ParseInt(fname, 10, 32) + if err != nil { + // if not numeric name, just skip + continue + } + ret = append(ret, int32(pid)) + } + + return ret, nil +} + +// Note: the following is based off process_linux structs and methods +// we need these to fetch the owner of a process ID +// FIXME: Import process occures import cycle. +// see remarks on pids() +type process struct { + Pid int32 `json:"pid"` + uids []int32 +} + +// Uids returns user ids of the process as a slice of the int +func (p *process) getUids() ([]int32, error) { + err := p.fillFromStatus() + if err != nil { + return []int32{}, err + } + return p.uids, nil +} + +// Get status from /proc/(pid)/status +func (p *process) fillFromStatus() error { + pid := p.Pid + statPath := common.HostProc(strconv.Itoa(int(pid)), "status") + contents, err := ioutil.ReadFile(statPath) + if err != nil { + return err + } + lines := strings.Split(string(contents), "\n") + for _, line := range lines { + tabParts := strings.SplitN(line, "\t", 2) + if len(tabParts) < 2 { + continue + } + value := tabParts[1] + switch strings.TrimRight(tabParts[0], ":") { + case "Uid": + p.uids = make([]int32, 0, 4) + for _, i := range strings.Split(value, "\t") { + v, err := strconv.ParseInt(i, 10, 32) + if err != nil { + return err + } + p.uids = append(p.uids, int32(v)) + } + } + } + return nil +} + +func getProcInodesAll(root string, max int) (map[string][]inodeMap, error) { + pids, err := Pids() + if err != nil { + return nil, err + } + ret := make(map[string][]inodeMap) + + for _, pid := range pids { + t, err := getProcInodes(root, pid, max) + if err != nil { + // skip if permission error or no longer exists + if os.IsPermission(err) || os.IsNotExist(err) || err == io.EOF { + continue + } + return ret, err + } + if len(t) == 0 { + continue + } + // TODO: update ret. + ret = updateMap(ret, t) + } + return ret, nil +} + +// decodeAddress decode addresse represents addr in proc/net/* +// ex: +// "0500000A:0016" -> "10.0.0.5", 22 +// "0085002452100113070057A13F025401:0035" -> "2400:8500:1301:1052:a157:7:154:23f", 53 +func decodeAddress(family uint32, src string) (Addr, error) { + t := strings.Split(src, ":") + if len(t) != 2 { + return Addr{}, fmt.Errorf("does not contain port, %s", src) + } + addr := t[0] + port, err := strconv.ParseInt("0x"+t[1], 0, 64) + if err != nil { + return Addr{}, fmt.Errorf("invalid port, %s", src) + } + decoded, err := hex.DecodeString(addr) + if err != nil { + return Addr{}, fmt.Errorf("decode error, %s", err) + } + var ip net.IP + // Assumes this is little_endian + if family == syscall.AF_INET { + ip = net.IP(Reverse(decoded)) + } else { // IPv6 + ip, err = parseIPv6HexString(decoded) + if err != nil { + return Addr{}, err + } + } + return Addr{ + IP: ip.String(), + Port: uint32(port), + }, nil +} + +// Reverse reverses array of bytes. +func Reverse(s []byte) []byte { + return ReverseWithContext(context.Background(), s) +} + +func ReverseWithContext(ctx context.Context, s []byte) []byte { + for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// parseIPv6HexString parse array of bytes to IPv6 string +func parseIPv6HexString(src []byte) (net.IP, error) { + if len(src) != 16 { + return nil, fmt.Errorf("invalid IPv6 string") + } + + buf := make([]byte, 0, 16) + for i := 0; i < len(src); i += 4 { + r := Reverse(src[i : i+4]) + buf = append(buf, r...) + } + return net.IP(buf), nil +} + +func processInet(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) { + + if strings.HasSuffix(file, "6") && !common.PathExists(file) { + // IPv6 not supported, return empty. + return []connTmp{}, nil + } + + // Read the contents of the /proc file with a single read sys call. + // This minimizes duplicates in the returned connections + // For more info: + // https://github.com/shirou/gopsutil/pull/361 + contents, err := ioutil.ReadFile(file) + if err != nil { + return nil, err + } + + lines := bytes.Split(contents, []byte("\n")) + + var ret []connTmp + // skip first line + for _, line := range lines[1:] { + l := strings.Fields(string(line)) + if len(l) < 10 { + continue + } + laddr := l[1] + raddr := l[2] + status := l[3] + inode := l[9] + pid := int32(0) + fd := uint32(0) + i, exists := inodes[inode] + if exists { + pid = i[0].pid + fd = i[0].fd + } + if filterPid > 0 && filterPid != pid { + continue + } + if kind.sockType == syscall.SOCK_STREAM { + status = TCPStatuses[status] + } else { + status = "NONE" + } + la, err := decodeAddress(kind.family, laddr) + if err != nil { + continue + } + ra, err := decodeAddress(kind.family, raddr) + if err != nil { + continue + } + + ret = append(ret, connTmp{ + fd: fd, + family: kind.family, + sockType: kind.sockType, + laddr: la, + raddr: ra, + status: status, + pid: pid, + }) + } + + return ret, nil +} + +func processUnix(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) { + // Read the contents of the /proc file with a single read sys call. + // This minimizes duplicates in the returned connections + // For more info: + // https://github.com/shirou/gopsutil/pull/361 + contents, err := ioutil.ReadFile(file) + if err != nil { + return nil, err + } + + lines := bytes.Split(contents, []byte("\n")) + + var ret []connTmp + // skip first line + for _, line := range lines[1:] { + tokens := strings.Fields(string(line)) + if len(tokens) < 6 { + continue + } + st, err := strconv.Atoi(tokens[4]) + if err != nil { + return nil, err + } + + inode := tokens[6] + + var pairs []inodeMap + pairs, exists := inodes[inode] + if !exists { + pairs = []inodeMap{ + {}, + } + } + for _, pair := range pairs { + if filterPid > 0 && filterPid != pair.pid { + continue + } + var path string + if len(tokens) == 8 { + path = tokens[len(tokens)-1] + } + ret = append(ret, connTmp{ + fd: pair.fd, + family: kind.family, + sockType: uint32(st), + laddr: Addr{ + IP: path, + }, + pid: pair.pid, + status: "NONE", + path: path, + }) + } + } + + return ret, nil +} + +func updateMap(src map[string][]inodeMap, add map[string][]inodeMap) map[string][]inodeMap { + for key, value := range add { + a, exists := src[key] + if !exists { + src[key] = value + continue + } + src[key] = append(a, value...) + } + return src +} diff --git a/vendor/github.com/shirou/gopsutil/net/net_openbsd.go b/vendor/github.com/shirou/gopsutil/net/net_openbsd.go new file mode 100644 index 00000000000..3cf0a89d479 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/net/net_openbsd.go @@ -0,0 +1,320 @@ +// +build openbsd + +package net + +import ( + "context" + "errors" + "fmt" + "os/exec" + "regexp" + "strconv" + "strings" + "syscall" + + "github.com/shirou/gopsutil/internal/common" +) + +var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`) + +func ParseNetstat(output string, mode string, + iocs map[string]IOCountersStat) error { + lines := strings.Split(output, "\n") + + exists := make([]string, 0, len(lines)-1) + + columns := 6 + if mode == "ind" { + columns = 10 + } + for _, line := range lines { + values := strings.Fields(line) + if len(values) < 1 || values[0] == "Name" { + continue + } + if common.StringsHas(exists, values[0]) { + // skip if already get + continue + } + + if len(values) < columns { + continue + } + base := 1 + // sometimes Address is omitted + if len(values) < columns { + base = 0 + } + + parsed := make([]uint64, 0, 8) + var vv []string + if mode == "inb" { + vv = []string{ + values[base+3], // BytesRecv + values[base+4], // BytesSent + } + } else { + vv = []string{ + values[base+3], // Ipkts + values[base+4], // Ierrs + values[base+5], // Opkts + values[base+6], // Oerrs + values[base+8], // Drops + } + } + for _, target := range vv { + if target == "-" { + parsed = append(parsed, 0) + continue + } + + t, err := strconv.ParseUint(target, 10, 64) + if err != nil { + return err + } + parsed = append(parsed, t) + } + exists = append(exists, values[0]) + + n, present := iocs[values[0]] + if !present { + n = IOCountersStat{Name: values[0]} + } + if mode == "inb" { + n.BytesRecv = parsed[0] + n.BytesSent = parsed[1] + } else { + n.PacketsRecv = parsed[0] + n.Errin = parsed[1] + n.PacketsSent = parsed[2] + n.Errout = parsed[3] + n.Dropin = parsed[4] + n.Dropout = parsed[4] + } + + iocs[n.Name] = n + } + return nil +} + +func IOCounters(pernic bool) ([]IOCountersStat, error) { + return IOCountersWithContext(context.Background(), pernic) +} + +func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { + netstat, err := exec.LookPath("netstat") + if err != nil { + return nil, err + } + out, err := invoke.CommandWithContext(ctx, netstat, "-inb") + if err != nil { + return nil, err + } + out2, err := invoke.CommandWithContext(ctx, netstat, "-ind") + if err != nil { + return nil, err + } + iocs := make(map[string]IOCountersStat) + + lines := strings.Split(string(out), "\n") + ret := make([]IOCountersStat, 0, len(lines)-1) + + err = ParseNetstat(string(out), "inb", iocs) + if err != nil { + return nil, err + } + err = ParseNetstat(string(out2), "ind", iocs) + if err != nil { + return nil, err + } + + for _, ioc := range iocs { + ret = append(ret, ioc) + } + + if pernic == false { + return getIOCountersAll(ret) + } + + return ret, nil +} + +// NetIOCountersByFile is an method which is added just a compatibility for linux. +func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { + return IOCountersByFileWithContext(context.Background(), pernic, filename) +} + +func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) { + return IOCounters(pernic) +} + +func FilterCounters() ([]FilterStat, error) { + return FilterCountersWithContext(context.Background()) +} + +func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { + return nil, errors.New("NetFilterCounters not implemented for openbsd") +} + +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, common.ErrNotImplementedError +} + +// NetProtoCounters returns network statistics for the entire system +// If protocols is empty then all protocols are returned, otherwise +// just the protocols in the list are returned. +// Not Implemented for OpenBSD +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { + return ProtoCountersWithContext(context.Background(), protocols) +} + +func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) { + return nil, errors.New("NetProtoCounters not implemented for openbsd") +} + +func parseNetstatLine(line string) (ConnectionStat, error) { + f := strings.Fields(line) + if len(f) < 5 { + return ConnectionStat{}, fmt.Errorf("wrong line,%s", line) + } + + var netType, netFamily uint32 + switch f[0] { + case "tcp": + netType = syscall.SOCK_STREAM + netFamily = syscall.AF_INET + case "udp": + netType = syscall.SOCK_DGRAM + netFamily = syscall.AF_INET + case "tcp6": + netType = syscall.SOCK_STREAM + netFamily = syscall.AF_INET6 + case "udp6": + netType = syscall.SOCK_DGRAM + netFamily = syscall.AF_INET6 + default: + return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[0]) + } + + laddr, raddr, err := parseNetstatAddr(f[3], f[4], netFamily) + if err != nil { + return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s %s", f[3], f[4]) + } + + n := ConnectionStat{ + Fd: uint32(0), // not supported + Family: uint32(netFamily), + Type: uint32(netType), + Laddr: laddr, + Raddr: raddr, + Pid: int32(0), // not supported + } + if len(f) == 6 { + n.Status = f[5] + } + + return n, nil +} + +func parseNetstatAddr(local string, remote string, family uint32) (laddr Addr, raddr Addr, err error) { + parse := func(l string) (Addr, error) { + matches := portMatch.FindStringSubmatch(l) + if matches == nil { + return Addr{}, fmt.Errorf("wrong addr, %s", l) + } + host := matches[1] + port := matches[2] + if host == "*" { + switch family { + case syscall.AF_INET: + host = "0.0.0.0" + case syscall.AF_INET6: + host = "::" + default: + return Addr{}, fmt.Errorf("unknown family, %d", family) + } + } + lport, err := strconv.Atoi(port) + if err != nil { + return Addr{}, err + } + return Addr{IP: host, Port: uint32(lport)}, nil + } + + laddr, err = parse(local) + if remote != "*.*" { // remote addr exists + raddr, err = parse(remote) + if err != nil { + return laddr, raddr, err + } + } + + return laddr, raddr, err +} + +// Return a list of network connections opened. +func Connections(kind string) ([]ConnectionStat, error) { + return ConnectionsWithContext(context.Background(), kind) +} + +func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + var ret []ConnectionStat + + args := []string{"-na"} + switch strings.ToLower(kind) { + default: + fallthrough + case "": + fallthrough + case "all": + fallthrough + case "inet": + // nothing to add + case "inet4": + args = append(args, "-finet") + case "inet6": + args = append(args, "-finet6") + case "tcp": + args = append(args, "-ptcp") + case "tcp4": + args = append(args, "-ptcp", "-finet") + case "tcp6": + args = append(args, "-ptcp", "-finet6") + case "udp": + args = append(args, "-pudp") + case "udp4": + args = append(args, "-pudp", "-finet") + case "udp6": + args = append(args, "-pudp", "-finet6") + case "unix": + return ret, common.ErrNotImplementedError + } + + netstat, err := exec.LookPath("netstat") + if err != nil { + return nil, err + } + out, err := invoke.CommandWithContext(ctx, netstat, args...) + + if err != nil { + return nil, err + } + lines := strings.Split(string(out), "\n") + for _, line := range lines { + if !(strings.HasPrefix(line, "tcp") || strings.HasPrefix(line, "udp")) { + continue + } + n, err := parseNetstatLine(line) + if err != nil { + continue + } + + ret = append(ret, n) + } + + return ret, nil +} diff --git a/vendor/github.com/shirou/gopsutil/net/net_unix.go b/vendor/github.com/shirou/gopsutil/net/net_unix.go new file mode 100644 index 00000000000..d6e4303fdba --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/net/net_unix.go @@ -0,0 +1,224 @@ +// +build freebsd darwin + +package net + +import ( + "context" + "fmt" + "net" + "strconv" + "strings" + "syscall" + + "github.com/shirou/gopsutil/internal/common" +) + +// Return a list of network connections opened. +func Connections(kind string) ([]ConnectionStat, error) { + return ConnectionsWithContext(context.Background(), kind) +} + +func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return ConnectionsPid(kind, 0) +} + +// Return a list of network connections opened returning at most `max` +// connections for each running process. +func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) { + return ConnectionsMaxWithContext(context.Background(), kind, max) +} + +func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} + +// Return a list of network connections opened by a process. +func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidWithContext(context.Background(), kind, pid) +} + +func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { + var ret []ConnectionStat + + args := []string{"-i"} + switch strings.ToLower(kind) { + default: + fallthrough + case "": + fallthrough + case "all": + fallthrough + case "inet": + args = append(args, "tcp", "-i", "udp") + case "inet4": + args = append(args, "4") + case "inet6": + args = append(args, "6") + case "tcp": + args = append(args, "tcp") + case "tcp4": + args = append(args, "4tcp") + case "tcp6": + args = append(args, "6tcp") + case "udp": + args = append(args, "udp") + case "udp4": + args = append(args, "6udp") + case "udp6": + args = append(args, "6udp") + case "unix": + args = []string{"-U"} + } + + r, err := common.CallLsofWithContext(ctx, invoke, pid, args...) + if err != nil { + return nil, err + } + for _, rr := range r { + if strings.HasPrefix(rr, "COMMAND") { + continue + } + n, err := parseNetLine(rr) + if err != nil { + + continue + } + + ret = append(ret, n) + } + + return ret, nil +} + +var constMap = map[string]int{ + "unix": syscall.AF_UNIX, + "TCP": syscall.SOCK_STREAM, + "UDP": syscall.SOCK_DGRAM, + "IPv4": syscall.AF_INET, + "IPv6": syscall.AF_INET6, +} + +func parseNetLine(line string) (ConnectionStat, error) { + f := strings.Fields(line) + if len(f) < 8 { + return ConnectionStat{}, fmt.Errorf("wrong line,%s", line) + } + + if len(f) == 8 { + f = append(f, f[7]) + f[7] = "unix" + } + + pid, err := strconv.Atoi(f[1]) + if err != nil { + return ConnectionStat{}, err + } + fd, err := strconv.Atoi(strings.Trim(f[3], "u")) + if err != nil { + return ConnectionStat{}, fmt.Errorf("unknown fd, %s", f[3]) + } + netFamily, ok := constMap[f[4]] + if !ok { + return ConnectionStat{}, fmt.Errorf("unknown family, %s", f[4]) + } + netType, ok := constMap[f[7]] + if !ok { + return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[7]) + } + + var laddr, raddr Addr + if f[7] == "unix" { + laddr.IP = f[8] + } else { + laddr, raddr, err = parseNetAddr(f[8]) + if err != nil { + return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s", f[8]) + } + } + + n := ConnectionStat{ + Fd: uint32(fd), + Family: uint32(netFamily), + Type: uint32(netType), + Laddr: laddr, + Raddr: raddr, + Pid: int32(pid), + } + if len(f) == 10 { + n.Status = strings.Trim(f[9], "()") + } + + return n, nil +} + +func parseNetAddr(line string) (laddr Addr, raddr Addr, err error) { + parse := func(l string) (Addr, error) { + host, port, err := net.SplitHostPort(l) + if err != nil { + return Addr{}, fmt.Errorf("wrong addr, %s", l) + } + lport, err := strconv.Atoi(port) + if err != nil { + return Addr{}, err + } + return Addr{IP: host, Port: uint32(lport)}, nil + } + + addrs := strings.Split(line, "->") + if len(addrs) == 0 { + return laddr, raddr, fmt.Errorf("wrong netaddr, %s", line) + } + laddr, err = parse(addrs[0]) + if len(addrs) == 2 { // remote addr exists + raddr, err = parse(addrs[1]) + if err != nil { + return laddr, raddr, err + } + } + + return laddr, raddr, err +} + +// Return up to `max` network connections opened by a process. +func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithContext(context.Background(), kind, pid, max) +} + +func ConnectionsPidMaxWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} + +// Return a list of network connections opened, omitting `Uids`. +// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be +// removed from the API in the future. +func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) { + return ConnectionsWithoutUidsWithContext(context.Background(), kind) +} + +func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0) +} + +func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max) +} + +func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid) +} + +func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0) +} + +func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max) +} + +func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max) +} + +func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/net/net_windows.go b/vendor/github.com/shirou/gopsutil/net/net_windows.go new file mode 100644 index 00000000000..6ab45ab3a76 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/net/net_windows.go @@ -0,0 +1,774 @@ +// +build windows + +package net + +import ( + "context" + "errors" + "fmt" + "net" + "os" + "syscall" + "unsafe" + + "github.com/shirou/gopsutil/internal/common" + "golang.org/x/sys/windows" +) + +var ( + modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") + procGetExtendedTCPTable = modiphlpapi.NewProc("GetExtendedTcpTable") + procGetExtendedUDPTable = modiphlpapi.NewProc("GetExtendedUdpTable") + procGetIfEntry2 = modiphlpapi.NewProc("GetIfEntry2") +) + +const ( + TCPTableBasicListener = iota + TCPTableBasicConnections + TCPTableBasicAll + TCPTableOwnerPIDListener + TCPTableOwnerPIDConnections + TCPTableOwnerPIDAll + TCPTableOwnerModuleListener + TCPTableOwnerModuleConnections + TCPTableOwnerModuleAll +) + +type netConnectionKindType struct { + family uint32 + sockType uint32 + filename string +} + +var kindTCP4 = netConnectionKindType{ + family: syscall.AF_INET, + sockType: syscall.SOCK_STREAM, + filename: "tcp", +} +var kindTCP6 = netConnectionKindType{ + family: syscall.AF_INET6, + sockType: syscall.SOCK_STREAM, + filename: "tcp6", +} +var kindUDP4 = netConnectionKindType{ + family: syscall.AF_INET, + sockType: syscall.SOCK_DGRAM, + filename: "udp", +} +var kindUDP6 = netConnectionKindType{ + family: syscall.AF_INET6, + sockType: syscall.SOCK_DGRAM, + filename: "udp6", +} + +var netConnectionKindMap = map[string][]netConnectionKindType{ + "all": {kindTCP4, kindTCP6, kindUDP4, kindUDP6}, + "tcp": {kindTCP4, kindTCP6}, + "tcp4": {kindTCP4}, + "tcp6": {kindTCP6}, + "udp": {kindUDP4, kindUDP6}, + "udp4": {kindUDP4}, + "udp6": {kindUDP6}, + "inet": {kindTCP4, kindTCP6, kindUDP4, kindUDP6}, + "inet4": {kindTCP4, kindUDP4}, + "inet6": {kindTCP6, kindUDP6}, +} + +// https://github.com/microsoft/ethr/blob/aecdaf923970e5a9b4c461b4e2e3963d781ad2cc/plt_windows.go#L114-L170 +type guid struct { + Data1 uint32 + Data2 uint16 + Data3 uint16 + Data4 [8]byte +} + +const ( + maxStringSize = 256 + maxPhysAddressLength = 32 + pad0for64_4for32 = 0 +) + +type mibIfRow2 struct { + InterfaceLuid uint64 + InterfaceIndex uint32 + InterfaceGuid guid + Alias [maxStringSize + 1]uint16 + Description [maxStringSize + 1]uint16 + PhysicalAddressLength uint32 + PhysicalAddress [maxPhysAddressLength]uint8 + PermanentPhysicalAddress [maxPhysAddressLength]uint8 + Mtu uint32 + Type uint32 + TunnelType uint32 + MediaType uint32 + PhysicalMediumType uint32 + AccessType uint32 + DirectionType uint32 + InterfaceAndOperStatusFlags uint32 + OperStatus uint32 + AdminStatus uint32 + MediaConnectState uint32 + NetworkGuid guid + ConnectionType uint32 + padding1 [pad0for64_4for32]byte + TransmitLinkSpeed uint64 + ReceiveLinkSpeed uint64 + InOctets uint64 + InUcastPkts uint64 + InNUcastPkts uint64 + InDiscards uint64 + InErrors uint64 + InUnknownProtos uint64 + InUcastOctets uint64 + InMulticastOctets uint64 + InBroadcastOctets uint64 + OutOctets uint64 + OutUcastPkts uint64 + OutNUcastPkts uint64 + OutDiscards uint64 + OutErrors uint64 + OutUcastOctets uint64 + OutMulticastOctets uint64 + OutBroadcastOctets uint64 + OutQLen uint64 +} + +func IOCounters(pernic bool) ([]IOCountersStat, error) { + return IOCountersWithContext(context.Background(), pernic) +} + +func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { + ifs, err := net.Interfaces() + if err != nil { + return nil, err + } + var counters []IOCountersStat + + err = procGetIfEntry2.Find() + if err == nil { // Vista+, uint64 values (issue#693) + for _, ifi := range ifs { + c := IOCountersStat{ + Name: ifi.Name, + } + + row := mibIfRow2{InterfaceIndex: uint32(ifi.Index)} + ret, _, err := procGetIfEntry2.Call(uintptr(unsafe.Pointer(&row))) + if ret != 0 { + return nil, os.NewSyscallError("GetIfEntry2", err) + } + c.BytesSent = uint64(row.OutOctets) + c.BytesRecv = uint64(row.InOctets) + c.PacketsSent = uint64(row.OutUcastPkts) + c.PacketsRecv = uint64(row.InUcastPkts) + c.Errin = uint64(row.InErrors) + c.Errout = uint64(row.OutErrors) + c.Dropin = uint64(row.InDiscards) + c.Dropout = uint64(row.OutDiscards) + + counters = append(counters, c) + } + } else { // WinXP fallback, uint32 values + for _, ifi := range ifs { + c := IOCountersStat{ + Name: ifi.Name, + } + + row := windows.MibIfRow{Index: uint32(ifi.Index)} + err = windows.GetIfEntry(&row) + if err != nil { + return nil, os.NewSyscallError("GetIfEntry", err) + } + c.BytesSent = uint64(row.OutOctets) + c.BytesRecv = uint64(row.InOctets) + c.PacketsSent = uint64(row.OutUcastPkts) + c.PacketsRecv = uint64(row.InUcastPkts) + c.Errin = uint64(row.InErrors) + c.Errout = uint64(row.OutErrors) + c.Dropin = uint64(row.InDiscards) + c.Dropout = uint64(row.OutDiscards) + + counters = append(counters, c) + } + } + + if !pernic { + return getIOCountersAll(counters) + } + return counters, nil +} + +// NetIOCountersByFile is an method which is added just a compatibility for linux. +func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { + return IOCountersByFileWithContext(context.Background(), pernic, filename) +} + +func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) { + return IOCounters(pernic) +} + +// Return a list of network connections +// Available kind: +// reference to netConnectionKindMap +func Connections(kind string) ([]ConnectionStat, error) { + return ConnectionsWithContext(context.Background(), kind) +} + +func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return ConnectionsPidWithContext(ctx, kind, 0) +} + +// ConnectionsPid Return a list of network connections opened by a process +func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidWithContext(context.Background(), kind, pid) +} + +func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { + tmap, ok := netConnectionKindMap[kind] + if !ok { + return nil, fmt.Errorf("invalid kind, %s", kind) + } + return getProcInet(tmap, pid) +} + +func getProcInet(kinds []netConnectionKindType, pid int32) ([]ConnectionStat, error) { + stats := make([]ConnectionStat, 0) + + for _, kind := range kinds { + s, err := getNetStatWithKind(kind) + if err != nil { + continue + } + + if pid == 0 { + stats = append(stats, s...) + } else { + for _, ns := range s { + if ns.Pid != pid { + continue + } + stats = append(stats, ns) + } + } + } + + return stats, nil +} + +func getNetStatWithKind(kindType netConnectionKindType) ([]ConnectionStat, error) { + if kindType.filename == "" { + return nil, fmt.Errorf("kind filename must be required") + } + + switch kindType.filename { + case kindTCP4.filename: + return getTCPConnections(kindTCP4.family) + case kindTCP6.filename: + return getTCPConnections(kindTCP6.family) + case kindUDP4.filename: + return getUDPConnections(kindUDP4.family) + case kindUDP6.filename: + return getUDPConnections(kindUDP6.family) + } + + return nil, fmt.Errorf("invalid kind filename, %s", kindType.filename) +} + +// Return a list of network connections opened returning at most `max` +// connections for each running process. +func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) { + return ConnectionsMaxWithContext(context.Background(), kind, max) +} + +func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} + +// Return a list of network connections opened, omitting `Uids`. +// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be +// removed from the API in the future. +func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) { + return ConnectionsWithoutUidsWithContext(context.Background(), kind) +} + +func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { + return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0) +} + +func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max) +} + +func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid) +} + +func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0) +} + +func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) { + return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max) +} + +func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max) +} + +func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { + return []ConnectionStat{}, common.ErrNotImplementedError +} + +func FilterCounters() ([]FilterStat, error) { + return FilterCountersWithContext(context.Background()) +} + +func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { + return nil, errors.New("NetFilterCounters not implemented for windows") +} + +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, common.ErrNotImplementedError +} + + +// NetProtoCounters returns network statistics for the entire system +// If protocols is empty then all protocols are returned, otherwise +// just the protocols in the list are returned. +// Not Implemented for Windows +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { + return ProtoCountersWithContext(context.Background(), protocols) +} + +func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) { + return nil, errors.New("NetProtoCounters not implemented for windows") +} + +func getTableUintptr(family uint32, buf []byte) uintptr { + var ( + pmibTCPTable pmibTCPTableOwnerPidAll + pmibTCP6Table pmibTCP6TableOwnerPidAll + + p uintptr + ) + switch family { + case kindTCP4.family: + if len(buf) > 0 { + pmibTCPTable = (*mibTCPTableOwnerPid)(unsafe.Pointer(&buf[0])) + p = uintptr(unsafe.Pointer(pmibTCPTable)) + } else { + p = uintptr(unsafe.Pointer(pmibTCPTable)) + } + case kindTCP6.family: + if len(buf) > 0 { + pmibTCP6Table = (*mibTCP6TableOwnerPid)(unsafe.Pointer(&buf[0])) + p = uintptr(unsafe.Pointer(pmibTCP6Table)) + } else { + p = uintptr(unsafe.Pointer(pmibTCP6Table)) + } + } + return p +} + +func getTableInfo(filename string, table interface{}) (index, step, length int) { + switch filename { + case kindTCP4.filename: + index = int(unsafe.Sizeof(table.(pmibTCPTableOwnerPidAll).DwNumEntries)) + step = int(unsafe.Sizeof(table.(pmibTCPTableOwnerPidAll).Table)) + length = int(table.(pmibTCPTableOwnerPidAll).DwNumEntries) + case kindTCP6.filename: + index = int(unsafe.Sizeof(table.(pmibTCP6TableOwnerPidAll).DwNumEntries)) + step = int(unsafe.Sizeof(table.(pmibTCP6TableOwnerPidAll).Table)) + length = int(table.(pmibTCP6TableOwnerPidAll).DwNumEntries) + case kindUDP4.filename: + index = int(unsafe.Sizeof(table.(pmibUDPTableOwnerPid).DwNumEntries)) + step = int(unsafe.Sizeof(table.(pmibUDPTableOwnerPid).Table)) + length = int(table.(pmibUDPTableOwnerPid).DwNumEntries) + case kindUDP6.filename: + index = int(unsafe.Sizeof(table.(pmibUDP6TableOwnerPid).DwNumEntries)) + step = int(unsafe.Sizeof(table.(pmibUDP6TableOwnerPid).Table)) + length = int(table.(pmibUDP6TableOwnerPid).DwNumEntries) + } + + return +} + +func getTCPConnections(family uint32) ([]ConnectionStat, error) { + var ( + p uintptr + buf []byte + size uint32 + + pmibTCPTable pmibTCPTableOwnerPidAll + pmibTCP6Table pmibTCP6TableOwnerPidAll + ) + + if family == 0 { + return nil, fmt.Errorf("faimly must be required") + } + + for { + switch family { + case kindTCP4.family: + if len(buf) > 0 { + pmibTCPTable = (*mibTCPTableOwnerPid)(unsafe.Pointer(&buf[0])) + p = uintptr(unsafe.Pointer(pmibTCPTable)) + } else { + p = uintptr(unsafe.Pointer(pmibTCPTable)) + } + case kindTCP6.family: + if len(buf) > 0 { + pmibTCP6Table = (*mibTCP6TableOwnerPid)(unsafe.Pointer(&buf[0])) + p = uintptr(unsafe.Pointer(pmibTCP6Table)) + } else { + p = uintptr(unsafe.Pointer(pmibTCP6Table)) + } + } + + err := getExtendedTcpTable(p, + &size, + true, + family, + tcpTableOwnerPidAll, + 0) + if err == nil { + break + } + if err != windows.ERROR_INSUFFICIENT_BUFFER { + return nil, err + } + buf = make([]byte, size) + } + + var ( + index, step int + length int + ) + + stats := make([]ConnectionStat, 0) + switch family { + case kindTCP4.family: + index, step, length = getTableInfo(kindTCP4.filename, pmibTCPTable) + case kindTCP6.family: + index, step, length = getTableInfo(kindTCP6.filename, pmibTCP6Table) + } + + if length == 0 { + return nil, nil + } + + for i := 0; i < length; i++ { + switch family { + case kindTCP4.family: + mibs := (*mibTCPRowOwnerPid)(unsafe.Pointer(&buf[index])) + ns := mibs.convertToConnectionStat() + stats = append(stats, ns) + case kindTCP6.family: + mibs := (*mibTCP6RowOwnerPid)(unsafe.Pointer(&buf[index])) + ns := mibs.convertToConnectionStat() + stats = append(stats, ns) + } + + index += step + } + return stats, nil +} + +func getUDPConnections(family uint32) ([]ConnectionStat, error) { + var ( + p uintptr + buf []byte + size uint32 + + pmibUDPTable pmibUDPTableOwnerPid + pmibUDP6Table pmibUDP6TableOwnerPid + ) + + if family == 0 { + return nil, fmt.Errorf("faimly must be required") + } + + for { + switch family { + case kindUDP4.family: + if len(buf) > 0 { + pmibUDPTable = (*mibUDPTableOwnerPid)(unsafe.Pointer(&buf[0])) + p = uintptr(unsafe.Pointer(pmibUDPTable)) + } else { + p = uintptr(unsafe.Pointer(pmibUDPTable)) + } + case kindUDP6.family: + if len(buf) > 0 { + pmibUDP6Table = (*mibUDP6TableOwnerPid)(unsafe.Pointer(&buf[0])) + p = uintptr(unsafe.Pointer(pmibUDP6Table)) + } else { + p = uintptr(unsafe.Pointer(pmibUDP6Table)) + } + } + + err := getExtendedUdpTable( + p, + &size, + true, + family, + udpTableOwnerPid, + 0, + ) + if err == nil { + break + } + if err != windows.ERROR_INSUFFICIENT_BUFFER { + return nil, err + } + buf = make([]byte, size) + } + + var ( + index, step, length int + ) + + stats := make([]ConnectionStat, 0) + switch family { + case kindUDP4.family: + index, step, length = getTableInfo(kindUDP4.filename, pmibUDPTable) + case kindUDP6.family: + index, step, length = getTableInfo(kindUDP6.filename, pmibUDP6Table) + } + + if length == 0 { + return nil, nil + } + + for i := 0; i < length; i++ { + switch family { + case kindUDP4.family: + mibs := (*mibUDPRowOwnerPid)(unsafe.Pointer(&buf[index])) + ns := mibs.convertToConnectionStat() + stats = append(stats, ns) + case kindUDP4.family: + mibs := (*mibUDP6RowOwnerPid)(unsafe.Pointer(&buf[index])) + ns := mibs.convertToConnectionStat() + stats = append(stats, ns) + } + + index += step + } + return stats, nil +} + +// tcpStatuses https://msdn.microsoft.com/en-us/library/windows/desktop/bb485761(v=vs.85).aspx +var tcpStatuses = map[mibTCPState]string{ + 1: "CLOSED", + 2: "LISTEN", + 3: "SYN_SENT", + 4: "SYN_RECEIVED", + 5: "ESTABLISHED", + 6: "FIN_WAIT_1", + 7: "FIN_WAIT_2", + 8: "CLOSE_WAIT", + 9: "CLOSING", + 10: "LAST_ACK", + 11: "TIME_WAIT", + 12: "DELETE", +} + +func getExtendedTcpTable(pTcpTable uintptr, pdwSize *uint32, bOrder bool, ulAf uint32, tableClass tcpTableClass, reserved uint32) (errcode error) { + r1, _, _ := syscall.Syscall6(procGetExtendedTCPTable.Addr(), 6, pTcpTable, uintptr(unsafe.Pointer(pdwSize)), getUintptrFromBool(bOrder), uintptr(ulAf), uintptr(tableClass), uintptr(reserved)) + if r1 != 0 { + errcode = syscall.Errno(r1) + } + return +} + +func getExtendedUdpTable(pUdpTable uintptr, pdwSize *uint32, bOrder bool, ulAf uint32, tableClass udpTableClass, reserved uint32) (errcode error) { + r1, _, _ := syscall.Syscall6(procGetExtendedUDPTable.Addr(), 6, pUdpTable, uintptr(unsafe.Pointer(pdwSize)), getUintptrFromBool(bOrder), uintptr(ulAf), uintptr(tableClass), uintptr(reserved)) + if r1 != 0 { + errcode = syscall.Errno(r1) + } + return +} + +func getUintptrFromBool(b bool) uintptr { + if b { + return 1 + } + return 0 +} + +const anySize = 1 + +// type MIB_TCP_STATE int32 +type mibTCPState int32 + +type tcpTableClass int32 + +const ( + tcpTableBasicListener tcpTableClass = iota + tcpTableBasicConnections + tcpTableBasicAll + tcpTableOwnerPidListener + tcpTableOwnerPidConnections + tcpTableOwnerPidAll + tcpTableOwnerModuleListener + tcpTableOwnerModuleConnections + tcpTableOwnerModuleAll +) + +type udpTableClass int32 + +const ( + udpTableBasic udpTableClass = iota + udpTableOwnerPid + udpTableOwnerModule +) + +// TCP + +type mibTCPRowOwnerPid struct { + DwState uint32 + DwLocalAddr uint32 + DwLocalPort uint32 + DwRemoteAddr uint32 + DwRemotePort uint32 + DwOwningPid uint32 +} + +func (m *mibTCPRowOwnerPid) convertToConnectionStat() ConnectionStat { + ns := ConnectionStat{ + Family: kindTCP4.family, + Type: kindTCP4.sockType, + Laddr: Addr{ + IP: parseIPv4HexString(m.DwLocalAddr), + Port: uint32(decodePort(m.DwLocalPort)), + }, + Raddr: Addr{ + IP: parseIPv4HexString(m.DwRemoteAddr), + Port: uint32(decodePort(m.DwRemotePort)), + }, + Pid: int32(m.DwOwningPid), + Status: tcpStatuses[mibTCPState(m.DwState)], + } + + return ns +} + +type mibTCPTableOwnerPid struct { + DwNumEntries uint32 + Table [anySize]mibTCPRowOwnerPid +} + +type mibTCP6RowOwnerPid struct { + UcLocalAddr [16]byte + DwLocalScopeId uint32 + DwLocalPort uint32 + UcRemoteAddr [16]byte + DwRemoteScopeId uint32 + DwRemotePort uint32 + DwState uint32 + DwOwningPid uint32 +} + +func (m *mibTCP6RowOwnerPid) convertToConnectionStat() ConnectionStat { + ns := ConnectionStat{ + Family: kindTCP6.family, + Type: kindTCP6.sockType, + Laddr: Addr{ + IP: parseIPv6HexString(m.UcLocalAddr), + Port: uint32(decodePort(m.DwLocalPort)), + }, + Raddr: Addr{ + IP: parseIPv6HexString(m.UcRemoteAddr), + Port: uint32(decodePort(m.DwRemotePort)), + }, + Pid: int32(m.DwOwningPid), + Status: tcpStatuses[mibTCPState(m.DwState)], + } + + return ns +} + +type mibTCP6TableOwnerPid struct { + DwNumEntries uint32 + Table [anySize]mibTCP6RowOwnerPid +} + +type pmibTCPTableOwnerPidAll *mibTCPTableOwnerPid +type pmibTCP6TableOwnerPidAll *mibTCP6TableOwnerPid + +// UDP + +type mibUDPRowOwnerPid struct { + DwLocalAddr uint32 + DwLocalPort uint32 + DwOwningPid uint32 +} + +func (m *mibUDPRowOwnerPid) convertToConnectionStat() ConnectionStat { + ns := ConnectionStat{ + Family: kindUDP4.family, + Type: kindUDP4.sockType, + Laddr: Addr{ + IP: parseIPv4HexString(m.DwLocalAddr), + Port: uint32(decodePort(m.DwLocalPort)), + }, + Pid: int32(m.DwOwningPid), + } + + return ns +} + +type mibUDPTableOwnerPid struct { + DwNumEntries uint32 + Table [anySize]mibUDPRowOwnerPid +} + +type mibUDP6RowOwnerPid struct { + UcLocalAddr [16]byte + DwLocalScopeId uint32 + DwLocalPort uint32 + DwOwningPid uint32 +} + +func (m *mibUDP6RowOwnerPid) convertToConnectionStat() ConnectionStat { + ns := ConnectionStat{ + Family: kindUDP6.family, + Type: kindUDP6.sockType, + Laddr: Addr{ + IP: parseIPv6HexString(m.UcLocalAddr), + Port: uint32(decodePort(m.DwLocalPort)), + }, + Pid: int32(m.DwOwningPid), + } + + return ns +} + +type mibUDP6TableOwnerPid struct { + DwNumEntries uint32 + Table [anySize]mibUDP6RowOwnerPid +} + +type pmibUDPTableOwnerPid *mibUDPTableOwnerPid +type pmibUDP6TableOwnerPid *mibUDP6TableOwnerPid + +func decodePort(port uint32) uint16 { + return syscall.Ntohs(uint16(port)) +} + +func parseIPv4HexString(addr uint32) string { + return fmt.Sprintf("%d.%d.%d.%d", addr&255, addr>>8&255, addr>>16&255, addr>>24&255) +} + +func parseIPv6HexString(addr [16]byte) string { + var ret [16]byte + for i := 0; i < 16; i++ { + ret[i] = uint8(addr[i]) + } + + // convert []byte to net.IP + ip := net.IP(ret[:]) + return ip.String() +} diff --git a/vendor/github.com/shirou/gopsutil/process/process.go b/vendor/github.com/shirou/gopsutil/process/process.go new file mode 100644 index 00000000000..482537d55ab --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process.go @@ -0,0 +1,320 @@ +package process + +import ( + "context" + "encoding/json" + "errors" + "runtime" + "sort" + "time" + + "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/internal/common" + "github.com/shirou/gopsutil/mem" +) + +var ( + invoke common.Invoker = common.Invoke{} + ErrorNoChildren = errors.New("process does not have children") + ErrorProcessNotRunning = errors.New("process does not exist") +) + +type Process struct { + Pid int32 `json:"pid"` + name string + status string + parent int32 + numCtxSwitches *NumCtxSwitchesStat + uids []int32 + gids []int32 + groups []int32 + numThreads int32 + memInfo *MemoryInfoStat + sigInfo *SignalInfoStat + createTime int64 + + lastCPUTimes *cpu.TimesStat + lastCPUTime time.Time + + tgid int32 +} + +type OpenFilesStat struct { + Path string `json:"path"` + Fd uint64 `json:"fd"` +} + +type MemoryInfoStat struct { + RSS uint64 `json:"rss"` // bytes + VMS uint64 `json:"vms"` // bytes + HWM uint64 `json:"hwm"` // bytes + Data uint64 `json:"data"` // bytes + Stack uint64 `json:"stack"` // bytes + Locked uint64 `json:"locked"` // bytes + Swap uint64 `json:"swap"` // bytes +} + +type SignalInfoStat struct { + PendingProcess uint64 `json:"pending_process"` + PendingThread uint64 `json:"pending_thread"` + Blocked uint64 `json:"blocked"` + Ignored uint64 `json:"ignored"` + Caught uint64 `json:"caught"` +} + +type RlimitStat struct { + Resource int32 `json:"resource"` + Soft int32 `json:"soft"` //TODO too small. needs to be uint64 + Hard int32 `json:"hard"` //TODO too small. needs to be uint64 + Used uint64 `json:"used"` +} + +type IOCountersStat struct { + ReadCount uint64 `json:"readCount"` + WriteCount uint64 `json:"writeCount"` + ReadBytes uint64 `json:"readBytes"` + WriteBytes uint64 `json:"writeBytes"` +} + +type NumCtxSwitchesStat struct { + Voluntary int64 `json:"voluntary"` + Involuntary int64 `json:"involuntary"` +} + +type PageFaultsStat struct { + MinorFaults uint64 `json:"minorFaults"` + MajorFaults uint64 `json:"majorFaults"` + ChildMinorFaults uint64 `json:"childMinorFaults"` + ChildMajorFaults uint64 `json:"childMajorFaults"` +} + +// Resource limit constants are from /usr/include/x86_64-linux-gnu/bits/resource.h +// from libc6-dev package in Ubuntu 16.10 +const ( + RLIMIT_CPU int32 = 0 + RLIMIT_FSIZE int32 = 1 + RLIMIT_DATA int32 = 2 + RLIMIT_STACK int32 = 3 + RLIMIT_CORE int32 = 4 + RLIMIT_RSS int32 = 5 + RLIMIT_NPROC int32 = 6 + RLIMIT_NOFILE int32 = 7 + RLIMIT_MEMLOCK int32 = 8 + RLIMIT_AS int32 = 9 + RLIMIT_LOCKS int32 = 10 + RLIMIT_SIGPENDING int32 = 11 + RLIMIT_MSGQUEUE int32 = 12 + RLIMIT_NICE int32 = 13 + RLIMIT_RTPRIO int32 = 14 + RLIMIT_RTTIME int32 = 15 +) + +func (p Process) String() string { + s, _ := json.Marshal(p) + return string(s) +} + +func (o OpenFilesStat) String() string { + s, _ := json.Marshal(o) + return string(s) +} + +func (m MemoryInfoStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +func (r RlimitStat) String() string { + s, _ := json.Marshal(r) + return string(s) +} + +func (i IOCountersStat) String() string { + s, _ := json.Marshal(i) + return string(s) +} + +func (p NumCtxSwitchesStat) String() string { + s, _ := json.Marshal(p) + return string(s) +} + +// Pids returns a slice of process ID list which are running now. +func Pids() ([]int32, error) { + return PidsWithContext(context.Background()) +} + +func PidsWithContext(ctx context.Context) ([]int32, error) { + pids, err := pidsWithContext(ctx) + sort.Slice(pids, func(i, j int) bool { return pids[i] < pids[j] }) + return pids, err +} + +// NewProcess creates a new Process instance, it only stores the pid and +// checks that the process exists. Other method on Process can be used +// to get more information about the process. An error will be returned +// if the process does not exist. +func NewProcess(pid int32) (*Process, error) { + p := &Process{Pid: pid} + + exists, err := PidExists(pid) + if err != nil { + return p, err + } + if !exists { + return p, ErrorProcessNotRunning + } + p.CreateTime() + return p, nil +} + +func PidExists(pid int32) (bool, error) { + return PidExistsWithContext(context.Background(), pid) +} + +// Background returns true if the process is in background, false otherwise. +func (p *Process) Background() (bool, error) { + return p.BackgroundWithContext(context.Background()) +} + +func (p *Process) BackgroundWithContext(ctx context.Context) (bool, error) { + fg, err := p.ForegroundWithContext(ctx) + if err != nil { + return false, err + } + return !fg, err +} + +// If interval is 0, return difference from last call(non-blocking). +// If interval > 0, wait interval sec and return diffrence between start and end. +func (p *Process) Percent(interval time.Duration) (float64, error) { + return p.PercentWithContext(context.Background(), interval) +} + +func (p *Process) PercentWithContext(ctx context.Context, interval time.Duration) (float64, error) { + cpuTimes, err := p.Times() + if err != nil { + return 0, err + } + now := time.Now() + + if interval > 0 { + p.lastCPUTimes = cpuTimes + p.lastCPUTime = now + time.Sleep(interval) + cpuTimes, err = p.Times() + now = time.Now() + if err != nil { + return 0, err + } + } else { + if p.lastCPUTimes == nil { + // invoked first time + p.lastCPUTimes = cpuTimes + p.lastCPUTime = now + return 0, nil + } + } + + numcpu := runtime.NumCPU() + delta := (now.Sub(p.lastCPUTime).Seconds()) * float64(numcpu) + ret := calculatePercent(p.lastCPUTimes, cpuTimes, delta, numcpu) + p.lastCPUTimes = cpuTimes + p.lastCPUTime = now + return ret, nil +} + +// IsRunning returns whether the process is still running or not. +func (p *Process) IsRunning() (bool, error) { + return p.IsRunningWithContext(context.Background()) +} + +func (p *Process) IsRunningWithContext(ctx context.Context) (bool, error) { + createTime, err := p.CreateTimeWithContext(ctx) + if err != nil { + return false, err + } + p2, err := NewProcess(p.Pid) + if err == ErrorProcessNotRunning { + return false, nil + } + createTime2, err := p2.CreateTimeWithContext(ctx) + if err != nil { + return false, err + } + return createTime == createTime2, nil +} + +// CreateTime returns created time of the process in milliseconds since the epoch, in UTC. +func (p *Process) CreateTime() (int64, error) { + return p.CreateTimeWithContext(context.Background()) +} + +func (p *Process) CreateTimeWithContext(ctx context.Context) (int64, error) { + if p.createTime != 0 { + return p.createTime, nil + } + createTime, err := p.createTimeWithContext(ctx) + p.createTime = createTime + return p.createTime, err +} + +func calculatePercent(t1, t2 *cpu.TimesStat, delta float64, numcpu int) float64 { + if delta == 0 { + return 0 + } + delta_proc := t2.Total() - t1.Total() + overall_percent := ((delta_proc / delta) * 100) * float64(numcpu) + return overall_percent +} + +// MemoryPercent returns how many percent of the total RAM this process uses +func (p *Process) MemoryPercent() (float32, error) { + return p.MemoryPercentWithContext(context.Background()) +} + +func (p *Process) MemoryPercentWithContext(ctx context.Context) (float32, error) { + machineMemory, err := mem.VirtualMemory() + if err != nil { + return 0, err + } + total := machineMemory.Total + + processMemory, err := p.MemoryInfo() + if err != nil { + return 0, err + } + used := processMemory.RSS + + return (100 * float32(used) / float32(total)), nil +} + +// CPU_Percent returns how many percent of the CPU time this process uses +func (p *Process) CPUPercent() (float64, error) { + return p.CPUPercentWithContext(context.Background()) +} + +func (p *Process) CPUPercentWithContext(ctx context.Context) (float64, error) { + crt_time, err := p.CreateTime() + if err != nil { + return 0, err + } + + cput, err := p.Times() + if err != nil { + return 0, err + } + + created := time.Unix(0, crt_time*int64(time.Millisecond)) + totalTime := time.Since(created).Seconds() + if totalTime <= 0 { + return 0, nil + } + + return 100 * cput.Total() / totalTime, nil +} + +// Groups returns all group IDs(include supplementary groups) of the process as a slice of the int +func (p *Process) Groups() ([]int32, error) { + return p.GroupsWithContext(context.Background()) +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_darwin.go b/vendor/github.com/shirou/gopsutil/process/process_darwin.go new file mode 100644 index 00000000000..c5ec0c6fbe8 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_darwin.go @@ -0,0 +1,671 @@ +// +build darwin + +package process + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "os/exec" + "path/filepath" + "strconv" + "strings" + "time" + "unsafe" + + "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/internal/common" + "github.com/shirou/gopsutil/net" + "golang.org/x/sys/unix" +) + +// copied from sys/sysctl.h +const ( + CTLKern = 1 // "high kernel": proc, limits + KernProc = 14 // struct: process entries + KernProcPID = 1 // by process id + KernProcProc = 8 // only return procs + KernProcAll = 0 // everything + KernProcPathname = 12 // path to executable +) + +const ( + ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK) +) + +type _Ctype_struct___0 struct { + Pad uint64 +} + +// MemoryInfoExStat is different between OSes +type MemoryInfoExStat struct { +} + +type MemoryMapsStat struct { +} + +func pidsWithContext(ctx context.Context) ([]int32, error) { + var ret []int32 + + pids, err := callPsWithContext(ctx, "pid", 0, false) + if err != nil { + return ret, err + } + + for _, pid := range pids { + v, err := strconv.Atoi(pid[0]) + if err != nil { + return ret, err + } + ret = append(ret, int32(v)) + } + + return ret, nil +} + +func (p *Process) Ppid() (int32, error) { + return p.PpidWithContext(context.Background()) +} + +func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { + r, err := callPsWithContext(ctx, "ppid", p.Pid, false) + if err != nil { + return 0, err + } + + v, err := strconv.Atoi(r[0][0]) + if err != nil { + return 0, err + } + + return int32(v), err +} +func (p *Process) Name() (string, error) { + return p.NameWithContext(context.Background()) +} + +func (p *Process) NameWithContext(ctx context.Context) (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + name := common.IntToString(k.Proc.P_comm[:]) + + if len(name) >= 15 { + cmdlineSlice, err := p.CmdlineSliceWithContext(ctx) + if err != nil { + return "", err + } + if len(cmdlineSlice) > 0 { + extendedName := filepath.Base(cmdlineSlice[0]) + if strings.HasPrefix(extendedName, p.name) { + name = extendedName + } else { + name = cmdlineSlice[0] + } + } + } + + return name, nil +} +func (p *Process) Tgid() (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) Exe() (string, error) { + return p.ExeWithContext(context.Background()) +} + +// Cmdline returns the command line arguments of the process as a string with +// each argument separated by 0x20 ascii character. +func (p *Process) Cmdline() (string, error) { + return p.CmdlineWithContext(context.Background()) +} + +func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) { + r, err := callPsWithContext(ctx, "command", p.Pid, false) + if err != nil { + return "", err + } + return strings.Join(r[0], " "), err +} + +// CmdlineSlice returns the command line arguments of the process as a slice with each +// element being an argument. Because of current deficiencies in the way that the command +// line arguments are found, single arguments that have spaces in the will actually be +// reported as two separate items. In order to do something better CGO would be needed +// to use the native darwin functions. +func (p *Process) CmdlineSlice() ([]string, error) { + return p.CmdlineSliceWithContext(context.Background()) +} + +func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { + r, err := callPsWithContext(ctx, "command", p.Pid, false) + if err != nil { + return nil, err + } + return r[0], err +} + +func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) { + r, err := callPsWithContext(ctx, "etime", p.Pid, false) + if err != nil { + return 0, err + } + + elapsedSegments := strings.Split(strings.Replace(r[0][0], "-", ":", 1), ":") + var elapsedDurations []time.Duration + for i := len(elapsedSegments) - 1; i >= 0; i-- { + p, err := strconv.ParseInt(elapsedSegments[i], 10, 0) + if err != nil { + return 0, err + } + elapsedDurations = append(elapsedDurations, time.Duration(p)) + } + + var elapsed = time.Duration(elapsedDurations[0]) * time.Second + if len(elapsedDurations) > 1 { + elapsed += time.Duration(elapsedDurations[1]) * time.Minute + } + if len(elapsedDurations) > 2 { + elapsed += time.Duration(elapsedDurations[2]) * time.Hour + } + if len(elapsedDurations) > 3 { + elapsed += time.Duration(elapsedDurations[3]) * time.Hour * 24 + } + + start := time.Now().Add(-elapsed) + return start.Unix() * 1000, nil +} +func (p *Process) Cwd() (string, error) { + return p.CwdWithContext(context.Background()) +} + +func (p *Process) CwdWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} +func (p *Process) Parent() (*Process, error) { + return p.ParentWithContext(context.Background()) +} + +func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) { + rr, err := common.CallLsofWithContext(ctx, invoke, p.Pid, "-FR") + if err != nil { + return nil, err + } + for _, r := range rr { + if strings.HasPrefix(r, "p") { // skip if process + continue + } + l := string(r) + v, err := strconv.Atoi(strings.Replace(l, "R", "", 1)) + if err != nil { + return nil, err + } + return NewProcess(int32(v)) + } + return nil, fmt.Errorf("could not find parent line") +} +func (p *Process) Status() (string, error) { + return p.StatusWithContext(context.Background()) +} + +func (p *Process) StatusWithContext(ctx context.Context) (string, error) { + r, err := callPsWithContext(ctx, "state", p.Pid, false) + if err != nil { + return "", err + } + + return r[0][0][0:1], err +} + +func (p *Process) Foreground() (bool, error) { + return p.ForegroundWithContext(context.Background()) +} + +func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { + // see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details + pid := p.Pid + ps, err := exec.LookPath("ps") + if err != nil { + return false, err + } + out, err := invoke.CommandWithContext(ctx, ps, "-o", "stat=", "-p", strconv.Itoa(int(pid))) + if err != nil { + return false, err + } + return strings.IndexByte(string(out), '+') != -1, nil +} + +func (p *Process) Uids() ([]int32, error) { + return p.UidsWithContext(context.Background()) +} + +func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + // See: http://unix.superglobalmegacorp.com/Net2/newsrc/sys/ucred.h.html + userEffectiveUID := int32(k.Eproc.Ucred.UID) + + return []int32{userEffectiveUID}, nil +} +func (p *Process) Gids() ([]int32, error) { + return p.GidsWithContext(context.Background()) +} + +func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + gids := make([]int32, 0, 3) + gids = append(gids, int32(k.Eproc.Pcred.P_rgid), int32(k.Eproc.Ucred.Ngroups), int32(k.Eproc.Pcred.P_svgid)) + + return gids, nil +} + +func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + groups := make([]int32, k.Eproc.Ucred.Ngroups) + for i := int16(0); i < k.Eproc.Ucred.Ngroups; i++ { + groups[i] = int32(k.Eproc.Ucred.Groups[i]) + } + + return groups, nil +} +func (p *Process) Terminal() (string, error) { + return p.TerminalWithContext(context.Background()) +} + +func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError + /* + k, err := p.getKProc() + if err != nil { + return "", err + } + + ttyNr := uint64(k.Eproc.Tdev) + termmap, err := getTerminalMap() + if err != nil { + return "", err + } + + return termmap[ttyNr], nil + */ +} +func (p *Process) Nice() (int32, error) { + return p.NiceWithContext(context.Background()) +} + +func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + return int32(k.Proc.P_nice), nil +} +func (p *Process) IOnice() (int32, error) { + return p.IOniceWithContext(context.Background()) +} + +func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) Rlimit() ([]RlimitStat, error) { + return p.RlimitWithContext(context.Background()) +} + +func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) { + var rlimit []RlimitStat + return rlimit, common.ErrNotImplementedError +} +func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) { + return p.RlimitUsageWithContext(context.Background(), gatherUsed) +} + +func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) { + var rlimit []RlimitStat + return rlimit, common.ErrNotImplementedError +} +func (p *Process) IOCounters() (*IOCountersStat, error) { + return p.IOCountersWithContext(context.Background()) +} + +func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { + return p.NumCtxSwitchesWithContext(context.Background()) +} + +func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) NumFDs() (int32, error) { + return p.NumFDsWithContext(context.Background()) +} + +func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) NumThreads() (int32, error) { + return p.NumThreadsWithContext(context.Background()) +} + +func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { + r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true) + if err != nil { + return 0, err + } + return int32(len(r)), nil +} +func (p *Process) Threads() (map[int32]*cpu.TimesStat, error) { + return p.ThreadsWithContext(context.Background()) +} + +func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) { + ret := make(map[int32]*cpu.TimesStat) + return ret, common.ErrNotImplementedError +} + +func convertCPUTimes(s string) (ret float64, err error) { + var t int + var _tmp string + if strings.Contains(s, ":") { + _t := strings.Split(s, ":") + switch len(_t) { + case 3: + hour, err := strconv.Atoi(_t[0]) + if err != nil { + return ret, err + } + t += hour * 60 * 60 * ClockTicks + + mins, err := strconv.Atoi(_t[1]) + if err != nil { + return ret, err + } + t += mins * 60 * ClockTicks + _tmp = _t[2] + case 2: + mins, err := strconv.Atoi(_t[0]) + if err != nil { + return ret, err + } + t += mins * 60 * ClockTicks + _tmp = _t[1] + case 1, 0: + _tmp = s + default: + return ret, fmt.Errorf("wrong cpu time string") + } + } else { + _tmp = s + } + + _t := strings.Split(_tmp, ".") + if err != nil { + return ret, err + } + h, err := strconv.Atoi(_t[0]) + t += h * ClockTicks + h, err = strconv.Atoi(_t[1]) + t += h + return float64(t) / ClockTicks, nil +} +func (p *Process) Times() (*cpu.TimesStat, error) { + return p.TimesWithContext(context.Background()) +} + +func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { + r, err := callPsWithContext(ctx, "utime,stime", p.Pid, false) + + if err != nil { + return nil, err + } + + utime, err := convertCPUTimes(r[0][0]) + if err != nil { + return nil, err + } + stime, err := convertCPUTimes(r[0][1]) + if err != nil { + return nil, err + } + + ret := &cpu.TimesStat{ + CPU: "cpu", + User: utime, + System: stime, + } + return ret, nil +} +func (p *Process) CPUAffinity() ([]int32, error) { + return p.CPUAffinityWithContext(context.Background()) +} + +func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { + return p.MemoryInfoWithContext(context.Background()) +} + +func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { + r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false) + if err != nil { + return nil, err + } + rss, err := strconv.Atoi(r[0][0]) + if err != nil { + return nil, err + } + vms, err := strconv.Atoi(r[0][1]) + if err != nil { + return nil, err + } + pagein, err := strconv.Atoi(r[0][2]) + if err != nil { + return nil, err + } + + ret := &MemoryInfoStat{ + RSS: uint64(rss) * 1024, + VMS: uint64(vms) * 1024, + Swap: uint64(pagein), + } + + return ret, nil +} +func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { + return p.MemoryInfoExWithContext(context.Background()) +} + +func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) PageFaults() (*PageFaultsStat, error) { + return p.PageFaultsWithContext(context.Background()) +} + +func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) Children() ([]*Process, error) { + return p.ChildrenWithContext(context.Background()) +} + +func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { + pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) + if err != nil { + return nil, err + } + ret := make([]*Process, 0, len(pids)) + for _, pid := range pids { + np, err := NewProcess(pid) + if err != nil { + return nil, err + } + ret = append(ret, np) + } + return ret, nil +} + +func (p *Process) OpenFiles() ([]OpenFilesStat, error) { + return p.OpenFilesWithContext(context.Background()) +} + +func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) Connections() ([]net.ConnectionStat, error) { + return p.ConnectionsWithContext(context.Background()) +} + +func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) { + return net.ConnectionsPid("all", p.Pid) +} + +// Connections returns a slice of net.ConnectionStat used by the process at most `max` +func (p *Process) ConnectionsMax(max int) ([]net.ConnectionStat, error) { + return p.ConnectionsMaxWithContext(context.Background(), max) +} + +func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) { + return net.ConnectionsPidMax("all", p.Pid, max) +} + +func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { + return p.NetIOCountersWithContext(context.Background(), pernic) +} + +func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { + return p.MemoryMapsWithContext(context.Background(), grouped) +} + +func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) { + var ret []MemoryMapsStat + return &ret, common.ErrNotImplementedError +} + +func Processes() ([]*Process, error) { + return ProcessesWithContext(context.Background()) +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { + out := []*Process{} + + pids, err := PidsWithContext(ctx) + if err != nil { + return out, err + } + + for _, pid := range pids { + p, err := NewProcess(pid) + if err != nil { + continue + } + out = append(out, p) + } + + return out, nil +} + +func parseKinfoProc(buf []byte) (KinfoProc, error) { + var k KinfoProc + br := bytes.NewReader(buf) + + err := common.Read(br, binary.LittleEndian, &k) + if err != nil { + return k, err + } + + return k, nil +} + +// Returns a proc as defined here: +// http://unix.superglobalmegacorp.com/Net2/newsrc/sys/kinfo_proc.h.html +func (p *Process) getKProc() (*KinfoProc, error) { + return p.getKProcWithContext(context.Background()) +} + +func (p *Process) getKProcWithContext(ctx context.Context) (*KinfoProc, error) { + mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid} + procK := KinfoProc{} + length := uint64(unsafe.Sizeof(procK)) + buf := make([]byte, length) + _, _, syserr := unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(len(mib)), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if syserr != 0 { + return nil, syserr + } + k, err := parseKinfoProc(buf) + if err != nil { + return nil, err + } + + return &k, nil +} + +// call ps command. +// Return value deletes Header line(you must not input wrong arg). +// And splited by Space. Caller have responsibility to manage. +// If passed arg pid is 0, get information from all process. +func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption bool) ([][]string, error) { + bin, err := exec.LookPath("ps") + if err != nil { + return [][]string{}, err + } + + var cmd []string + if pid == 0 { // will get from all processes. + cmd = []string{"-ax", "-o", arg} + } else if threadOption { + cmd = []string{"-x", "-o", arg, "-M", "-p", strconv.Itoa(int(pid))} + } else { + cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))} + } + out, err := invoke.CommandWithContext(ctx, bin, cmd...) + if err != nil { + return [][]string{}, err + } + lines := strings.Split(string(out), "\n") + + var ret [][]string + for _, l := range lines[1:] { + var lr []string + for _, r := range strings.Split(l, " ") { + if r == "" { + continue + } + lr = append(lr, strings.TrimSpace(r)) + } + if len(lr) != 0 { + ret = append(ret, lr) + } + } + + return ret, nil +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_darwin_386.go b/vendor/github.com/shirou/gopsutil/process/process_darwin_386.go new file mode 100644 index 00000000000..f8e922385b9 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_darwin_386.go @@ -0,0 +1,234 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_darwin.go + +package process + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int32 + Pad_cgo_0 [4]byte +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Rlimit struct { + Cur uint64 + Max uint64 +} + +type UGid_t uint32 + +type KinfoProc struct { + Proc ExternProc + Eproc Eproc +} + +type Eproc struct { + Paddr *uint64 + Sess *Session + Pcred Upcred + Ucred Uucred + Pad_cgo_0 [4]byte + Vm Vmspace + Ppid int32 + Pgid int32 + Jobc int16 + Pad_cgo_1 [2]byte + Tdev int32 + Tpgid int32 + Pad_cgo_2 [4]byte + Tsess *Session + Wmesg [8]int8 + Xsize int32 + Xrssize int16 + Xccount int16 + Xswrss int16 + Pad_cgo_3 [2]byte + Flag int32 + Login [12]int8 + Spare [4]int32 + Pad_cgo_4 [4]byte +} + +type Proc struct{} + +type Session struct{} + +type ucred struct { + Link _Ctype_struct___0 + Ref uint64 + Posix Posix_cred + Label *Label + Audit Au_session +} + +type Uucred struct { + Ref int32 + UID uint32 + Ngroups int16 + Pad_cgo_0 [2]byte + Groups [16]uint32 +} + +type Upcred struct { + Pc_lock [72]int8 + Pc_ucred *ucred + P_ruid uint32 + P_svuid uint32 + P_rgid uint32 + P_svgid uint32 + P_refcnt int32 + Pad_cgo_0 [4]byte +} + +type Vmspace struct { + Dummy int32 + Pad_cgo_0 [4]byte + Dummy2 *int8 + Dummy3 [5]int32 + Pad_cgo_1 [4]byte + Dummy4 [3]*int8 +} + +type Sigacts struct{} + +type ExternProc struct { + P_un [16]byte + P_vmspace uint64 + P_sigacts uint64 + Pad_cgo_0 [3]byte + P_flag int32 + P_stat int8 + P_pid int32 + P_oppid int32 + P_dupfd int32 + Pad_cgo_1 [4]byte + User_stack uint64 + Exit_thread uint64 + P_debugger int32 + Sigwait int32 + P_estcpu uint32 + P_cpticks int32 + P_pctcpu uint32 + Pad_cgo_2 [4]byte + P_wchan uint64 + P_wmesg uint64 + P_swtime uint32 + P_slptime uint32 + P_realtimer Itimerval + P_rtime Timeval + P_uticks uint64 + P_sticks uint64 + P_iticks uint64 + P_traceflag int32 + Pad_cgo_3 [4]byte + P_tracep uint64 + P_siglist int32 + Pad_cgo_4 [4]byte + P_textvp uint64 + P_holdcnt int32 + P_sigmask uint32 + P_sigignore uint32 + P_sigcatch uint32 + P_priority uint8 + P_usrpri uint8 + P_nice int8 + P_comm [17]int8 + Pad_cgo_5 [4]byte + P_pgrp uint64 + P_addr uint64 + P_xstat uint16 + P_acflag uint16 + Pad_cgo_6 [4]byte + P_ru uint64 +} + +type Itimerval struct { + Interval Timeval + Value Timeval +} + +type Vnode struct{} + +type Pgrp struct{} + +type UserStruct struct{} + +type Au_session struct { + Aia_p *AuditinfoAddr + Mask AuMask +} + +type Posix_cred struct { + UID uint32 + Ruid uint32 + Svuid uint32 + Ngroups int16 + Pad_cgo_0 [2]byte + Groups [16]uint32 + Rgid uint32 + Svgid uint32 + Gmuid uint32 + Flags int32 +} + +type Label struct{} + +type AuditinfoAddr struct { + Auid uint32 + Mask AuMask + Termid AuTidAddr + Asid int32 + Flags uint64 +} +type AuMask struct { + Success uint32 + Failure uint32 +} +type AuTidAddr struct { + Port int32 + Type uint32 + Addr [4]uint32 +} + +type UcredQueue struct { + Next *ucred + Prev **ucred +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_darwin_amd64.go b/vendor/github.com/shirou/gopsutil/process/process_darwin_amd64.go new file mode 100644 index 00000000000..f8e922385b9 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_darwin_amd64.go @@ -0,0 +1,234 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_darwin.go + +package process + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int32 + Pad_cgo_0 [4]byte +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Rlimit struct { + Cur uint64 + Max uint64 +} + +type UGid_t uint32 + +type KinfoProc struct { + Proc ExternProc + Eproc Eproc +} + +type Eproc struct { + Paddr *uint64 + Sess *Session + Pcred Upcred + Ucred Uucred + Pad_cgo_0 [4]byte + Vm Vmspace + Ppid int32 + Pgid int32 + Jobc int16 + Pad_cgo_1 [2]byte + Tdev int32 + Tpgid int32 + Pad_cgo_2 [4]byte + Tsess *Session + Wmesg [8]int8 + Xsize int32 + Xrssize int16 + Xccount int16 + Xswrss int16 + Pad_cgo_3 [2]byte + Flag int32 + Login [12]int8 + Spare [4]int32 + Pad_cgo_4 [4]byte +} + +type Proc struct{} + +type Session struct{} + +type ucred struct { + Link _Ctype_struct___0 + Ref uint64 + Posix Posix_cred + Label *Label + Audit Au_session +} + +type Uucred struct { + Ref int32 + UID uint32 + Ngroups int16 + Pad_cgo_0 [2]byte + Groups [16]uint32 +} + +type Upcred struct { + Pc_lock [72]int8 + Pc_ucred *ucred + P_ruid uint32 + P_svuid uint32 + P_rgid uint32 + P_svgid uint32 + P_refcnt int32 + Pad_cgo_0 [4]byte +} + +type Vmspace struct { + Dummy int32 + Pad_cgo_0 [4]byte + Dummy2 *int8 + Dummy3 [5]int32 + Pad_cgo_1 [4]byte + Dummy4 [3]*int8 +} + +type Sigacts struct{} + +type ExternProc struct { + P_un [16]byte + P_vmspace uint64 + P_sigacts uint64 + Pad_cgo_0 [3]byte + P_flag int32 + P_stat int8 + P_pid int32 + P_oppid int32 + P_dupfd int32 + Pad_cgo_1 [4]byte + User_stack uint64 + Exit_thread uint64 + P_debugger int32 + Sigwait int32 + P_estcpu uint32 + P_cpticks int32 + P_pctcpu uint32 + Pad_cgo_2 [4]byte + P_wchan uint64 + P_wmesg uint64 + P_swtime uint32 + P_slptime uint32 + P_realtimer Itimerval + P_rtime Timeval + P_uticks uint64 + P_sticks uint64 + P_iticks uint64 + P_traceflag int32 + Pad_cgo_3 [4]byte + P_tracep uint64 + P_siglist int32 + Pad_cgo_4 [4]byte + P_textvp uint64 + P_holdcnt int32 + P_sigmask uint32 + P_sigignore uint32 + P_sigcatch uint32 + P_priority uint8 + P_usrpri uint8 + P_nice int8 + P_comm [17]int8 + Pad_cgo_5 [4]byte + P_pgrp uint64 + P_addr uint64 + P_xstat uint16 + P_acflag uint16 + Pad_cgo_6 [4]byte + P_ru uint64 +} + +type Itimerval struct { + Interval Timeval + Value Timeval +} + +type Vnode struct{} + +type Pgrp struct{} + +type UserStruct struct{} + +type Au_session struct { + Aia_p *AuditinfoAddr + Mask AuMask +} + +type Posix_cred struct { + UID uint32 + Ruid uint32 + Svuid uint32 + Ngroups int16 + Pad_cgo_0 [2]byte + Groups [16]uint32 + Rgid uint32 + Svgid uint32 + Gmuid uint32 + Flags int32 +} + +type Label struct{} + +type AuditinfoAddr struct { + Auid uint32 + Mask AuMask + Termid AuTidAddr + Asid int32 + Flags uint64 +} +type AuMask struct { + Success uint32 + Failure uint32 +} +type AuTidAddr struct { + Port int32 + Type uint32 + Addr [4]uint32 +} + +type UcredQueue struct { + Next *ucred + Prev **ucred +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_darwin_cgo.go b/vendor/github.com/shirou/gopsutil/process/process_darwin_cgo.go new file mode 100644 index 00000000000..a80817755c3 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_darwin_cgo.go @@ -0,0 +1,30 @@ +// +build darwin +// +build cgo + +package process + +// #include +// #include +import "C" +import ( + "context" + "fmt" + "unsafe" +) + +func (p *Process) ExeWithContext(ctx context.Context) (string, error) { + var c C.char // need a var for unsafe.Sizeof need a var + const bufsize = C.PROC_PIDPATHINFO_MAXSIZE * unsafe.Sizeof(c) + buffer := (*C.char)(C.malloc(C.size_t(bufsize))) + defer C.free(unsafe.Pointer(buffer)) + + ret, err := C.proc_pidpath(C.int(p.Pid), unsafe.Pointer(buffer), C.uint32_t(bufsize)) + if err != nil { + return "", err + } + if ret <= 0 { + return "", fmt.Errorf("unknown error: proc_pidpath returned %d", ret) + } + + return C.GoString(buffer), nil +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_darwin_nocgo.go b/vendor/github.com/shirou/gopsutil/process/process_darwin_nocgo.go new file mode 100644 index 00000000000..3583e19877e --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_darwin_nocgo.go @@ -0,0 +1,34 @@ +// +build darwin +// +build !cgo + +package process + +import ( + "context" + "fmt" + "os/exec" + "strconv" + "strings" +) + +func (p *Process) ExeWithContext(ctx context.Context) (string, error) { + lsof_bin, err := exec.LookPath("lsof") + if err != nil { + return "", err + } + out, err := invoke.CommandWithContext(ctx, lsof_bin, "-p", strconv.Itoa(int(p.Pid)), "-Fpfn") + if err != nil { + return "", fmt.Errorf("bad call to lsof: %s", err) + } + txtFound := 0 + lines := strings.Split(string(out), "\n") + for i := 1; i < len(lines); i++ { + if lines[i] == "ftxt" { + txtFound++ + if txtFound == 2 { + return lines[i-1][1:], nil + } + } + } + return "", fmt.Errorf("missing txt data returned by lsof") +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_fallback.go b/vendor/github.com/shirou/gopsutil/process/process_fallback.go new file mode 100644 index 00000000000..43803e785f9 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_fallback.go @@ -0,0 +1,336 @@ +// +build !darwin,!linux,!freebsd,!openbsd,!windows + +package process + +import ( + "context" + "syscall" + + "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/internal/common" + "github.com/shirou/gopsutil/net" +) + +type MemoryMapsStat struct { + Path string `json:"path"` + Rss uint64 `json:"rss"` + Size uint64 `json:"size"` + Pss uint64 `json:"pss"` + SharedClean uint64 `json:"sharedClean"` + SharedDirty uint64 `json:"sharedDirty"` + PrivateClean uint64 `json:"privateClean"` + PrivateDirty uint64 `json:"privateDirty"` + Referenced uint64 `json:"referenced"` + Anonymous uint64 `json:"anonymous"` + Swap uint64 `json:"swap"` +} + +type MemoryInfoExStat struct { +} + +func pidsWithContext(ctx context.Context) ([]int32, error) { + return []int32{}, common.ErrNotImplementedError +} + +func Processes() ([]*Process, error) { + return nil, common.ErrNotImplementedError +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { + return nil, common.ErrNotImplementedError +} + +func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { + pids, err := PidsWithContext(ctx) + if err != nil { + return false, err + } + + for _, i := range pids { + if i == pid { + return true, err + } + } + + return false, err +} + +func (p *Process) Ppid() (int32, error) { + return p.PpidWithContext(context.Background()) +} + +func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) Name() (string, error) { + return p.NameWithContext(context.Background()) +} + +func (p *Process) NameWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} +func (p *Process) Tgid() (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) Exe() (string, error) { + return p.ExeWithContext(context.Background()) +} + +func (p *Process) ExeWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} +func (p *Process) Cmdline() (string, error) { + return p.CmdlineWithContext(context.Background()) +} + +func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} +func (p *Process) CmdlineSlice() ([]string, error) { + return p.CmdlineSliceWithContext(context.Background()) +} + +func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { + return []string{}, common.ErrNotImplementedError +} + +func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) Cwd() (string, error) { + return p.CwdWithContext(context.Background()) +} + +func (p *Process) CwdWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} +func (p *Process) Parent() (*Process, error) { + return p.ParentWithContext(context.Background()) +} + +func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) Status() (string, error) { + return p.StatusWithContext(context.Background()) +} + +func (p *Process) StatusWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} +func (p *Process) Foreground() (bool, error) { + return p.ForegroundWithContext(context.Background()) +} + +func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { + return false, common.ErrNotImplementedError +} +func (p *Process) Uids() ([]int32, error) { + return p.UidsWithContext(context.Background()) +} + +func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) { + return []int32{}, common.ErrNotImplementedError +} +func (p *Process) Gids() ([]int32, error) { + return p.GidsWithContext(context.Background()) +} + +func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) { + return []int32{}, common.ErrNotImplementedError +} + +func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) { + return []int32{}, common.ErrNotImplementedError +} +func (p *Process) Terminal() (string, error) { + return p.TerminalWithContext(context.Background()) +} + +func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} +func (p *Process) Nice() (int32, error) { + return p.NiceWithContext(context.Background()) +} + +func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) IOnice() (int32, error) { + return p.IOniceWithContext(context.Background()) +} + +func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) Rlimit() ([]RlimitStat, error) { + return p.RlimitWithContext(context.Background()) +} + +func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) { + return p.RlimitUsageWithContext(context.Background(), gatherUsed) +} + +func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) IOCounters() (*IOCountersStat, error) { + return p.IOCountersWithContext(context.Background()) +} + +func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { + return p.NumCtxSwitchesWithContext(context.Background()) +} + +func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) NumFDs() (int32, error) { + return p.NumFDsWithContext(context.Background()) +} + +func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) NumThreads() (int32, error) { + return p.NumThreadsWithContext(context.Background()) +} + +func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) Threads() (map[int32]*cpu.TimesStat, error) { + return p.ThreadsWithContext(context.Background()) +} + +func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) Times() (*cpu.TimesStat, error) { + return p.TimesWithContext(context.Background()) +} + +func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) CPUAffinity() ([]int32, error) { + return p.CPUAffinityWithContext(context.Background()) +} + +func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { + return p.MemoryInfoWithContext(context.Background()) +} + +func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { + return p.MemoryInfoExWithContext(context.Background()) +} + +func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) PageFaults() (*PageFaultsStat, error) { + return p.PageFaultsWithContext(context.Background()) +} +func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) Children() ([]*Process, error) { + return p.ChildrenWithContext(context.Background()) +} + +func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) OpenFiles() ([]OpenFilesStat, error) { + return p.OpenFilesWithContext(context.Background()) +} + +func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) { + return []OpenFilesStat{}, common.ErrNotImplementedError +} +func (p *Process) Connections() ([]net.ConnectionStat, error) { + return p.ConnectionsWithContext(context.Background()) +} + +func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) { + return []net.ConnectionStat{}, common.ErrNotImplementedError +} + +func (p *Process) ConnectionsMax(max int) ([]net.ConnectionStat, error) { + return p.ConnectionsMaxWithContext(context.Background(), max) +} + +func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) { + return []net.ConnectionStat{}, common.ErrNotImplementedError +} + +func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { + return p.NetIOCountersWithContext(context.Background(), pernic) +} + +func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) { + return []net.IOCountersStat{}, common.ErrNotImplementedError +} + +func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { + return p.MemoryMapsWithContext(context.Background(), grouped) +} + +func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) SendSignal(sig syscall.Signal) error { + return p.SendSignalWithContext(context.Background(), sig) +} + +func (p *Process) SendSignalWithContext(ctx context.Context, sig syscall.Signal) error { + return common.ErrNotImplementedError +} +func (p *Process) Suspend() error { + return p.SuspendWithContext(context.Background()) +} + +func (p *Process) SuspendWithContext(ctx context.Context) error { + return common.ErrNotImplementedError +} +func (p *Process) Resume() error { + return p.ResumeWithContext(context.Background()) +} + +func (p *Process) ResumeWithContext(ctx context.Context) error { + return common.ErrNotImplementedError +} +func (p *Process) Terminate() error { + return p.TerminateWithContext(context.Background()) +} + +func (p *Process) TerminateWithContext(ctx context.Context) error { + return common.ErrNotImplementedError +} +func (p *Process) Kill() error { + return p.KillWithContext(context.Background()) +} + +func (p *Process) KillWithContext(ctx context.Context) error { + return common.ErrNotImplementedError +} +func (p *Process) Username() (string, error) { + return p.UsernameWithContext(context.Background()) +} + +func (p *Process) UsernameWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_freebsd.go b/vendor/github.com/shirou/gopsutil/process/process_freebsd.go new file mode 100644 index 00000000000..84dbead96c7 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_freebsd.go @@ -0,0 +1,529 @@ +// +build freebsd + +package process + +import ( + "bytes" + "context" + "encoding/binary" + "os/exec" + "path/filepath" + "strconv" + "strings" + + cpu "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/internal/common" + net "github.com/shirou/gopsutil/net" + "golang.org/x/sys/unix" +) + +// MemoryInfoExStat is different between OSes +type MemoryInfoExStat struct { +} + +type MemoryMapsStat struct { +} + +func pidsWithContext(ctx context.Context) ([]int32, error) { + var ret []int32 + procs, err := Processes() + if err != nil { + return ret, nil + } + + for _, p := range procs { + ret = append(ret, p.Pid) + } + + return ret, nil +} + +func (p *Process) Ppid() (int32, error) { + return p.PpidWithContext(context.Background()) +} + +func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + + return k.Ppid, nil +} +func (p *Process) Name() (string, error) { + return p.NameWithContext(context.Background()) +} + +func (p *Process) NameWithContext(ctx context.Context) (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + name := common.IntToString(k.Comm[:]) + + if len(name) >= 15 { + cmdlineSlice, err := p.CmdlineSliceWithContext(ctx) + if err != nil { + return "", err + } + if len(cmdlineSlice) > 0 { + extendedName := filepath.Base(cmdlineSlice[0]) + if strings.HasPrefix(extendedName, p.name) { + name = extendedName + } else { + name = cmdlineSlice[0] + } + } + } + + return name, nil +} +func (p *Process) Tgid() (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) Exe() (string, error) { + return p.ExeWithContext(context.Background()) +} + +func (p *Process) ExeWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) Cmdline() (string, error) { + return p.CmdlineWithContext(context.Background()) +} + +func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) { + mib := []int32{CTLKern, KernProc, KernProcArgs, p.Pid} + buf, _, err := common.CallSyscall(mib) + if err != nil { + return "", err + } + ret := strings.FieldsFunc(string(buf), func(r rune) bool { + if r == '\u0000' { + return true + } + return false + }) + + return strings.Join(ret, " "), nil +} + +func (p *Process) CmdlineSlice() ([]string, error) { + return p.CmdlineSliceWithContext(context.Background()) +} + +func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { + mib := []int32{CTLKern, KernProc, KernProcArgs, p.Pid} + buf, _, err := common.CallSyscall(mib) + if err != nil { + return nil, err + } + if len(buf) == 0 { + return nil, nil + } + if buf[len(buf)-1] == 0 { + buf = buf[:len(buf)-1] + } + parts := bytes.Split(buf, []byte{0}) + var strParts []string + for _, p := range parts { + strParts = append(strParts, string(p)) + } + + return strParts, nil +} + +func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) Cwd() (string, error) { + return p.CwdWithContext(context.Background()) +} + +func (p *Process) CwdWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} +func (p *Process) Parent() (*Process, error) { + return p.ParentWithContext(context.Background()) +} + +func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) { + return p, common.ErrNotImplementedError +} +func (p *Process) Status() (string, error) { + return p.StatusWithContext(context.Background()) +} + +func (p *Process) StatusWithContext(ctx context.Context) (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + var s string + switch k.Stat { + case SIDL: + s = "I" + case SRUN: + s = "R" + case SSLEEP: + s = "S" + case SSTOP: + s = "T" + case SZOMB: + s = "Z" + case SWAIT: + s = "W" + case SLOCK: + s = "L" + } + + return s, nil +} + +func (p *Process) Foreground() (bool, error) { + return p.ForegroundWithContext(context.Background()) +} + +func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { + // see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details + pid := p.Pid + ps, err := exec.LookPath("ps") + if err != nil { + return false, err + } + out, err := invoke.CommandWithContext(ctx, ps, "-o", "stat=", "-p", strconv.Itoa(int(pid))) + if err != nil { + return false, err + } + return strings.IndexByte(string(out), '+') != -1, nil +} + +func (p *Process) Uids() ([]int32, error) { + return p.UidsWithContext(context.Background()) +} + +func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + uids := make([]int32, 0, 3) + + uids = append(uids, int32(k.Ruid), int32(k.Uid), int32(k.Svuid)) + + return uids, nil +} +func (p *Process) Gids() ([]int32, error) { + return p.GidsWithContext(context.Background()) +} + +func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + gids := make([]int32, 0, 3) + gids = append(gids, int32(k.Rgid), int32(k.Ngroups), int32(k.Svgid)) + + return gids, nil +} + +func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + groups := make([]int32, k.Ngroups) + for i := int16(0); i < k.Ngroups; i++ { + groups[i] = int32(k.Groups[i]) + } + + return groups, nil +} +func (p *Process) Terminal() (string, error) { + return p.TerminalWithContext(context.Background()) +} + +func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + + ttyNr := uint64(k.Tdev) + + termmap, err := getTerminalMap() + if err != nil { + return "", err + } + + return termmap[ttyNr], nil +} +func (p *Process) Nice() (int32, error) { + return p.NiceWithContext(context.Background()) +} + +func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + return int32(k.Nice), nil +} +func (p *Process) IOnice() (int32, error) { + return p.IOniceWithContext(context.Background()) +} + +func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) Rlimit() ([]RlimitStat, error) { + return p.RlimitWithContext(context.Background()) +} + +func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) { + var rlimit []RlimitStat + return rlimit, common.ErrNotImplementedError +} +func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) { + return p.RlimitUsageWithContext(context.Background(), gatherUsed) +} + +func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) { + var rlimit []RlimitStat + return rlimit, common.ErrNotImplementedError +} +func (p *Process) IOCounters() (*IOCountersStat, error) { + return p.IOCountersWithContext(context.Background()) +} + +func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + return &IOCountersStat{ + ReadCount: uint64(k.Rusage.Inblock), + WriteCount: uint64(k.Rusage.Oublock), + }, nil +} +func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { + return p.NumCtxSwitchesWithContext(context.Background()) +} + +func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) NumFDs() (int32, error) { + return p.NumFDsWithContext(context.Background()) +} + +func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) NumThreads() (int32, error) { + return p.NumThreadsWithContext(context.Background()) +} + +func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + + return k.Numthreads, nil +} +func (p *Process) Threads() (map[int32]*cpu.TimesStat, error) { + return p.ThreadsWithContext(context.Background()) +} + +func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) { + ret := make(map[int32]*cpu.TimesStat) + return ret, common.ErrNotImplementedError +} +func (p *Process) Times() (*cpu.TimesStat, error) { + return p.TimesWithContext(context.Background()) +} + +func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + return &cpu.TimesStat{ + CPU: "cpu", + User: float64(k.Rusage.Utime.Sec) + float64(k.Rusage.Utime.Usec)/1000000, + System: float64(k.Rusage.Stime.Sec) + float64(k.Rusage.Stime.Usec)/1000000, + }, nil +} +func (p *Process) CPUAffinity() ([]int32, error) { + return p.CPUAffinityWithContext(context.Background()) +} + +func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { + return p.MemoryInfoWithContext(context.Background()) +} + +func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + v, err := unix.Sysctl("vm.stats.vm.v_page_size") + if err != nil { + return nil, err + } + pageSize := common.LittleEndian.Uint16([]byte(v)) + + return &MemoryInfoStat{ + RSS: uint64(k.Rssize) * uint64(pageSize), + VMS: uint64(k.Size), + }, nil +} +func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { + return p.MemoryInfoExWithContext(context.Background()) +} + +func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) PageFaults() (*PageFaultsStat, error) { + return p.PageFaultsWithContext(context.Background()) +} + +func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) Children() ([]*Process, error) { + return p.ChildrenWithContext(context.Background()) +} + +func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { + pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) + if err != nil { + return nil, err + } + ret := make([]*Process, 0, len(pids)) + for _, pid := range pids { + np, err := NewProcess(pid) + if err != nil { + return nil, err + } + ret = append(ret, np) + } + return ret, nil +} + +func (p *Process) OpenFiles() ([]OpenFilesStat, error) { + return p.OpenFilesWithContext(context.Background()) +} + +func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) Connections() ([]net.ConnectionStat, error) { + return p.ConnectionsWithContext(context.Background()) +} + +func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) { + return nil, common.ErrNotImplementedError +} + +// Connections returns a slice of net.ConnectionStat used by the process at most `max` +func (p *Process) ConnectionsMax(max int) ([]net.ConnectionStat, error) { + return p.ConnectionsMaxWithContext(context.Background(), max) +} + +func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) { + return []net.ConnectionStat{}, common.ErrNotImplementedError +} + +func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { + return p.NetIOCountersWithContext(context.Background(), pernic) +} + +func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { + return p.MemoryMapsWithContext(context.Background(), grouped) +} + +func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) { + var ret []MemoryMapsStat + return &ret, common.ErrNotImplementedError +} + +func Processes() ([]*Process, error) { + return ProcessesWithContext(context.Background()) +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { + results := []*Process{} + + mib := []int32{CTLKern, KernProc, KernProcProc, 0} + buf, length, err := common.CallSyscall(mib) + if err != nil { + return results, err + } + + // get kinfo_proc size + count := int(length / uint64(sizeOfKinfoProc)) + + // parse buf to procs + for i := 0; i < count; i++ { + b := buf[i*sizeOfKinfoProc : (i+1)*sizeOfKinfoProc] + k, err := parseKinfoProc(b) + if err != nil { + continue + } + p, err := NewProcess(int32(k.Pid)) + if err != nil { + continue + } + + results = append(results, p) + } + + return results, nil +} + +func parseKinfoProc(buf []byte) (KinfoProc, error) { + var k KinfoProc + br := bytes.NewReader(buf) + err := common.Read(br, binary.LittleEndian, &k) + return k, err +} + +func (p *Process) getKProc() (*KinfoProc, error) { + return p.getKProcWithContext(context.Background()) +} + +func (p *Process) getKProcWithContext(ctx context.Context) (*KinfoProc, error) { + mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid} + + buf, length, err := common.CallSyscall(mib) + if err != nil { + return nil, err + } + if length != sizeOfKinfoProc { + return nil, err + } + + k, err := parseKinfoProc(buf) + if err != nil { + return nil, err + } + return &k, nil +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_freebsd_386.go b/vendor/github.com/shirou/gopsutil/process/process_freebsd_386.go new file mode 100644 index 00000000000..08ab333b435 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_freebsd_386.go @@ -0,0 +1,192 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package process + +const ( + CTLKern = 1 + KernProc = 14 + KernProcPID = 1 + KernProcProc = 8 + KernProcPathname = 12 + KernProcArgs = 7 +) + +const ( + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 +) + +const ( + sizeOfKinfoVmentry = 0x488 + sizeOfKinfoProc = 0x300 +) + +const ( + SIDL = 1 + SRUN = 2 + SSLEEP = 3 + SSTOP = 4 + SZOMB = 5 + SWAIT = 6 + SLOCK = 7 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int32 + _C_long_long int64 +) + +type Timespec struct { + Sec int32 + Nsec int32 +} + +type Timeval struct { + Sec int32 + Usec int32 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int32 + Ixrss int32 + Idrss int32 + Isrss int32 + Minflt int32 + Majflt int32 + Nswap int32 + Inblock int32 + Oublock int32 + Msgsnd int32 + Msgrcv int32 + Nsignals int32 + Nvcsw int32 + Nivcsw int32 +} + +type Rlimit struct { + Cur int64 + Max int64 +} + +type KinfoProc struct { + Structsize int32 + Layout int32 + Args int32 /* pargs */ + Paddr int32 /* proc */ + Addr int32 /* user */ + Tracep int32 /* vnode */ + Textvp int32 /* vnode */ + Fd int32 /* filedesc */ + Vmspace int32 /* vmspace */ + Wchan int32 + Pid int32 + Ppid int32 + Pgid int32 + Tpgid int32 + Sid int32 + Tsid int32 + Jobc int16 + Spare_short1 int16 + Tdev uint32 + Siglist [16]byte /* sigset */ + Sigmask [16]byte /* sigset */ + Sigignore [16]byte /* sigset */ + Sigcatch [16]byte /* sigset */ + Uid uint32 + Ruid uint32 + Svuid uint32 + Rgid uint32 + Svgid uint32 + Ngroups int16 + Spare_short2 int16 + Groups [16]uint32 + Size uint32 + Rssize int32 + Swrss int32 + Tsize int32 + Dsize int32 + Ssize int32 + Xstat uint16 + Acflag uint16 + Pctcpu uint32 + Estcpu uint32 + Slptime uint32 + Swtime uint32 + Cow uint32 + Runtime uint64 + Start Timeval + Childtime Timeval + Flag int32 + Kiflag int32 + Traceflag int32 + Stat int8 + Nice int8 + Lock int8 + Rqindex int8 + Oncpu uint8 + Lastcpu uint8 + Tdname [17]int8 + Wmesg [9]int8 + Login [18]int8 + Lockname [9]int8 + Comm [20]int8 + Emul [17]int8 + Loginclass [18]int8 + Sparestrings [50]int8 + Spareints [7]int32 + Flag2 int32 + Fibnum int32 + Cr_flags uint32 + Jid int32 + Numthreads int32 + Tid int32 + Pri Priority + Rusage Rusage + Rusage_ch Rusage + Pcb int32 /* pcb */ + Kstack int32 + Udata int32 + Tdaddr int32 /* thread */ + Spareptrs [6]int32 + Sparelongs [12]int32 + Sflag int32 + Tdflags int32 +} + +type Priority struct { + Class uint8 + Level uint8 + Native uint8 + User uint8 +} + +type KinfoVmentry struct { + Structsize int32 + Type int32 + Start uint64 + End uint64 + Offset uint64 + Vn_fileid uint64 + Vn_fsid uint32 + Flags int32 + Resident int32 + Private_resident int32 + Protection int32 + Ref_count int32 + Shadow_count int32 + Vn_type int32 + Vn_size uint64 + Vn_rdev uint32 + Vn_mode uint16 + Status uint16 + X_kve_ispare [12]int32 + Path [1024]int8 +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_freebsd_amd64.go b/vendor/github.com/shirou/gopsutil/process/process_freebsd_amd64.go new file mode 100644 index 00000000000..560e627d249 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_freebsd_amd64.go @@ -0,0 +1,192 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package process + +const ( + CTLKern = 1 + KernProc = 14 + KernProcPID = 1 + KernProcProc = 8 + KernProcPathname = 12 + KernProcArgs = 7 +) + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 +) + +const ( + sizeOfKinfoVmentry = 0x488 + sizeOfKinfoProc = 0x440 +) + +const ( + SIDL = 1 + SRUN = 2 + SSLEEP = 3 + SSTOP = 4 + SZOMB = 5 + SWAIT = 6 + SLOCK = 7 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int64 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Rlimit struct { + Cur int64 + Max int64 +} + +type KinfoProc struct { + Structsize int32 + Layout int32 + Args int64 /* pargs */ + Paddr int64 /* proc */ + Addr int64 /* user */ + Tracep int64 /* vnode */ + Textvp int64 /* vnode */ + Fd int64 /* filedesc */ + Vmspace int64 /* vmspace */ + Wchan int64 + Pid int32 + Ppid int32 + Pgid int32 + Tpgid int32 + Sid int32 + Tsid int32 + Jobc int16 + Spare_short1 int16 + Tdev uint32 + Siglist [16]byte /* sigset */ + Sigmask [16]byte /* sigset */ + Sigignore [16]byte /* sigset */ + Sigcatch [16]byte /* sigset */ + Uid uint32 + Ruid uint32 + Svuid uint32 + Rgid uint32 + Svgid uint32 + Ngroups int16 + Spare_short2 int16 + Groups [16]uint32 + Size uint64 + Rssize int64 + Swrss int64 + Tsize int64 + Dsize int64 + Ssize int64 + Xstat uint16 + Acflag uint16 + Pctcpu uint32 + Estcpu uint32 + Slptime uint32 + Swtime uint32 + Cow uint32 + Runtime uint64 + Start Timeval + Childtime Timeval + Flag int64 + Kiflag int64 + Traceflag int32 + Stat int8 + Nice int8 + Lock int8 + Rqindex int8 + Oncpu uint8 + Lastcpu uint8 + Tdname [17]int8 + Wmesg [9]int8 + Login [18]int8 + Lockname [9]int8 + Comm [20]int8 + Emul [17]int8 + Loginclass [18]int8 + Sparestrings [50]int8 + Spareints [7]int32 + Flag2 int32 + Fibnum int32 + Cr_flags uint32 + Jid int32 + Numthreads int32 + Tid int32 + Pri Priority + Rusage Rusage + Rusage_ch Rusage + Pcb int64 /* pcb */ + Kstack int64 + Udata int64 + Tdaddr int64 /* thread */ + Spareptrs [6]int64 + Sparelongs [12]int64 + Sflag int64 + Tdflags int64 +} + +type Priority struct { + Class uint8 + Level uint8 + Native uint8 + User uint8 +} + +type KinfoVmentry struct { + Structsize int32 + Type int32 + Start uint64 + End uint64 + Offset uint64 + Vn_fileid uint64 + Vn_fsid uint32 + Flags int32 + Resident int32 + Private_resident int32 + Protection int32 + Ref_count int32 + Shadow_count int32 + Vn_type int32 + Vn_size uint64 + Vn_rdev uint32 + Vn_mode uint16 + Status uint16 + X_kve_ispare [12]int32 + Path [1024]int8 +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_freebsd_arm.go b/vendor/github.com/shirou/gopsutil/process/process_freebsd_arm.go new file mode 100644 index 00000000000..81ae0b9a8d4 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_freebsd_arm.go @@ -0,0 +1,192 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package process + +const ( + CTLKern = 1 + KernProc = 14 + KernProcPID = 1 + KernProcProc = 8 + KernProcPathname = 12 + KernProcArgs = 7 +) + +const ( + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 +) + +const ( + sizeOfKinfoVmentry = 0x488 + sizeOfKinfoProc = 0x440 +) + +const ( + SIDL = 1 + SRUN = 2 + SSLEEP = 3 + SSTOP = 4 + SZOMB = 5 + SWAIT = 6 + SLOCK = 7 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int32 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int64 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int32 + Ixrss int32 + Idrss int32 + Isrss int32 + Minflt int32 + Majflt int32 + Nswap int32 + Inblock int32 + Oublock int32 + Msgsnd int32 + Msgrcv int32 + Nsignals int32 + Nvcsw int32 + Nivcsw int32 +} + +type Rlimit struct { + Cur int32 + Max int32 +} + +type KinfoProc struct { + Structsize int32 + Layout int32 + Args int32 /* pargs */ + Paddr int32 /* proc */ + Addr int32 /* user */ + Tracep int32 /* vnode */ + Textvp int32 /* vnode */ + Fd int32 /* filedesc */ + Vmspace int32 /* vmspace */ + Wchan int32 + Pid int32 + Ppid int32 + Pgid int32 + Tpgid int32 + Sid int32 + Tsid int32 + Jobc int16 + Spare_short1 int16 + Tdev uint32 + Siglist [16]byte /* sigset */ + Sigmask [16]byte /* sigset */ + Sigignore [16]byte /* sigset */ + Sigcatch [16]byte /* sigset */ + Uid uint32 + Ruid uint32 + Svuid uint32 + Rgid uint32 + Svgid uint32 + Ngroups int16 + Spare_short2 int16 + Groups [16]uint32 + Size uint32 + Rssize int32 + Swrss int32 + Tsize int32 + Dsize int32 + Ssize int32 + Xstat uint16 + Acflag uint16 + Pctcpu uint32 + Estcpu uint32 + Slptime uint32 + Swtime uint32 + Cow uint32 + Runtime uint64 + Start Timeval + Childtime Timeval + Flag int32 + Kiflag int32 + Traceflag int32 + Stat int8 + Nice int8 + Lock int8 + Rqindex int8 + Oncpu uint8 + Lastcpu uint8 + Tdname [17]int8 + Wmesg [9]int8 + Login [18]int8 + Lockname [9]int8 + Comm [20]int8 + Emul [17]int8 + Loginclass [18]int8 + Sparestrings [50]int8 + Spareints [4]int32 + Flag2 int32 + Fibnum int32 + Cr_flags uint32 + Jid int32 + Numthreads int32 + Tid int32 + Pri Priority + Rusage Rusage + Rusage_ch Rusage + Pcb int32 /* pcb */ + Kstack int32 + Udata int32 + Tdaddr int32 /* thread */ + Spareptrs [6]int64 + Sparelongs [12]int64 + Sflag int64 + Tdflags int64 +} + +type Priority struct { + Class uint8 + Level uint8 + Native uint8 + User uint8 +} + +type KinfoVmentry struct { + Structsize int32 + Type int32 + Start uint64 + End uint64 + Offset uint64 + Vn_fileid uint64 + Vn_fsid uint32 + Flags int32 + Resident int32 + Private_resident int32 + Protection int32 + Ref_count int32 + Shadow_count int32 + Vn_type int32 + Vn_size uint64 + Vn_rdev uint32 + Vn_mode uint16 + Status uint16 + X_kve_ispare [12]int32 + Path [1024]int8 +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_freebsd_arm64.go b/vendor/github.com/shirou/gopsutil/process/process_freebsd_arm64.go new file mode 100644 index 00000000000..99781d1a2fc --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_freebsd_arm64.go @@ -0,0 +1,201 @@ +// +build freebsd +// +build arm64 +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs process/types_freebsd.go + +package process + +const ( + CTLKern = 1 + KernProc = 14 + KernProcPID = 1 + KernProcProc = 8 + KernProcPathname = 12 + KernProcArgs = 7 +) + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 +) + +const ( + sizeOfKinfoVmentry = 0x488 + sizeOfKinfoProc = 0x440 +) + +const ( + SIDL = 1 + SRUN = 2 + SSLEEP = 3 + SSTOP = 4 + SZOMB = 5 + SWAIT = 6 + SLOCK = 7 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int64 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Rlimit struct { + Cur int64 + Max int64 +} + +type KinfoProc struct { + Structsize int32 + Layout int32 + Args *int64 /* pargs */ + Paddr *int64 /* proc */ + Addr *int64 /* user */ + Tracep *int64 /* vnode */ + Textvp *int64 /* vnode */ + Fd *int64 /* filedesc */ + Vmspace *int64 /* vmspace */ + Wchan *byte + Pid int32 + Ppid int32 + Pgid int32 + Tpgid int32 + Sid int32 + Tsid int32 + Jobc int16 + Spare_short1 int16 + Tdev_freebsd11 uint32 + Siglist [16]byte /* sigset */ + Sigmask [16]byte /* sigset */ + Sigignore [16]byte /* sigset */ + Sigcatch [16]byte /* sigset */ + Uid uint32 + Ruid uint32 + Svuid uint32 + Rgid uint32 + Svgid uint32 + Ngroups int16 + Spare_short2 int16 + Groups [16]uint32 + Size uint64 + Rssize int64 + Swrss int64 + Tsize int64 + Dsize int64 + Ssize int64 + Xstat uint16 + Acflag uint16 + Pctcpu uint32 + Estcpu uint32 + Slptime uint32 + Swtime uint32 + Cow uint32 + Runtime uint64 + Start Timeval + Childtime Timeval + Flag int64 + Kiflag int64 + Traceflag int32 + Stat uint8 + Nice int8 + Lock uint8 + Rqindex uint8 + Oncpu_old uint8 + Lastcpu_old uint8 + Tdname [17]uint8 + Wmesg [9]uint8 + Login [18]uint8 + Lockname [9]uint8 + Comm [20]int8 + Emul [17]uint8 + Loginclass [18]uint8 + Moretdname [4]uint8 + Sparestrings [46]uint8 + Spareints [2]int32 + Tdev uint64 + Oncpu int32 + Lastcpu int32 + Tracer int32 + Flag2 int32 + Fibnum int32 + Cr_flags uint32 + Jid int32 + Numthreads int32 + Tid int32 + Pri Priority + Rusage Rusage + Rusage_ch Rusage + Pcb *int64 /* pcb */ + Kstack *byte + Udata *byte + Tdaddr *int64 /* thread */ + Spareptrs [6]*byte + Sparelongs [12]int64 + Sflag int64 + Tdflags int64 +} + +type Priority struct { + Class uint8 + Level uint8 + Native uint8 + User uint8 +} + +type KinfoVmentry struct { + Structsize int32 + Type int32 + Start uint64 + End uint64 + Offset uint64 + Vn_fileid uint64 + Vn_fsid_freebsd11 uint32 + Flags int32 + Resident int32 + Private_resident int32 + Protection int32 + Ref_count int32 + Shadow_count int32 + Vn_type int32 + Vn_size uint64 + Vn_rdev_freebsd11 uint32 + Vn_mode uint16 + Status uint16 + Vn_fsid uint64 + Vn_rdev uint64 + X_kve_ispare [8]int32 + Path [1024]uint8 +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_linux.go b/vendor/github.com/shirou/gopsutil/process/process_linux.go new file mode 100644 index 00000000000..6a509099c1c --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_linux.go @@ -0,0 +1,1300 @@ +// +build linux + +package process + +import ( + "bufio" + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "math" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/internal/common" + "github.com/shirou/gopsutil/net" + "golang.org/x/sys/unix" +) + +var PageSize = uint64(os.Getpagesize()) + +const ( + PrioProcess = 0 // linux/resource.h + ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK) +) + +// MemoryInfoExStat is different between OSes +type MemoryInfoExStat struct { + RSS uint64 `json:"rss"` // bytes + VMS uint64 `json:"vms"` // bytes + Shared uint64 `json:"shared"` // bytes + Text uint64 `json:"text"` // bytes + Lib uint64 `json:"lib"` // bytes + Data uint64 `json:"data"` // bytes + Dirty uint64 `json:"dirty"` // bytes +} + +func (m MemoryInfoExStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +type MemoryMapsStat struct { + Path string `json:"path"` + Rss uint64 `json:"rss"` + Size uint64 `json:"size"` + Pss uint64 `json:"pss"` + SharedClean uint64 `json:"sharedClean"` + SharedDirty uint64 `json:"sharedDirty"` + PrivateClean uint64 `json:"privateClean"` + PrivateDirty uint64 `json:"privateDirty"` + Referenced uint64 `json:"referenced"` + Anonymous uint64 `json:"anonymous"` + Swap uint64 `json:"swap"` +} + +// String returns JSON value of the process. +func (m MemoryMapsStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +// Ppid returns Parent Process ID of the process. +func (p *Process) Ppid() (int32, error) { + return p.PpidWithContext(context.Background()) +} + +func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { + _, ppid, _, _, _, _, _, err := p.fillFromStatWithContext(ctx) + if err != nil { + return -1, err + } + return ppid, nil +} + +// Name returns name of the process. +func (p *Process) Name() (string, error) { + return p.NameWithContext(context.Background()) +} + +func (p *Process) NameWithContext(ctx context.Context) (string, error) { + if p.name == "" { + if err := p.fillFromStatusWithContext(ctx); err != nil { + return "", err + } + } + return p.name, nil +} + +// Tgid returns tgid, a Linux-synonym for user-space Pid +func (p *Process) Tgid() (int32, error) { + if p.tgid == 0 { + if err := p.fillFromStatusWithContext(context.Background()); err != nil { + return 0, err + } + } + return p.tgid, nil +} + +// Exe returns executable path of the process. +func (p *Process) Exe() (string, error) { + return p.ExeWithContext(context.Background()) +} + +func (p *Process) ExeWithContext(ctx context.Context) (string, error) { + return p.fillFromExeWithContext(ctx) +} + +// Cmdline returns the command line arguments of the process as a string with +// each argument separated by 0x20 ascii character. +func (p *Process) Cmdline() (string, error) { + return p.CmdlineWithContext(context.Background()) +} + +func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) { + return p.fillFromCmdlineWithContext(ctx) +} + +// CmdlineSlice returns the command line arguments of the process as a slice with each +// element being an argument. +func (p *Process) CmdlineSlice() ([]string, error) { + return p.CmdlineSliceWithContext(context.Background()) +} + +func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { + return p.fillSliceFromCmdlineWithContext(ctx) +} + +func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) { + _, _, _, createTime, _, _, _, err := p.fillFromStatWithContext(ctx) + if err != nil { + return 0, err + } + return createTime, nil +} + +// Cwd returns current working directory of the process. +func (p *Process) Cwd() (string, error) { + return p.CwdWithContext(context.Background()) +} + +func (p *Process) CwdWithContext(ctx context.Context) (string, error) { + return p.fillFromCwdWithContext(ctx) +} + +// Parent returns parent Process of the process. +func (p *Process) Parent() (*Process, error) { + return p.ParentWithContext(context.Background()) +} + +func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) { + err := p.fillFromStatusWithContext(ctx) + if err != nil { + return nil, err + } + if p.parent == 0 { + return nil, fmt.Errorf("wrong number of parents") + } + return NewProcess(p.parent) +} + +// Status returns the process status. +// Return value could be one of these. +// R: Running S: Sleep T: Stop I: Idle +// Z: Zombie W: Wait L: Lock +// The character is same within all supported platforms. +func (p *Process) Status() (string, error) { + return p.StatusWithContext(context.Background()) +} + +func (p *Process) StatusWithContext(ctx context.Context) (string, error) { + err := p.fillFromStatusWithContext(ctx) + if err != nil { + return "", err + } + return p.status, nil +} + +// Foreground returns true if the process is in foreground, false otherwise. +func (p *Process) Foreground() (bool, error) { + return p.ForegroundWithContext(context.Background()) +} + +func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { + // see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details + pid := p.Pid + statPath := common.HostProc(strconv.Itoa(int(pid)), "stat") + contents, err := ioutil.ReadFile(statPath) + if err != nil { + return false, err + } + fields := strings.Fields(string(contents)) + if len(fields) < 8 { + return false, fmt.Errorf("insufficient data in %s", statPath) + } + pgid := fields[4] + tpgid := fields[7] + return pgid == tpgid, nil +} + +// Uids returns user ids of the process as a slice of the int +func (p *Process) Uids() ([]int32, error) { + return p.UidsWithContext(context.Background()) +} + +func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) { + err := p.fillFromStatusWithContext(ctx) + if err != nil { + return []int32{}, err + } + return p.uids, nil +} + +// Gids returns group ids of the process as a slice of the int +func (p *Process) Gids() ([]int32, error) { + return p.GidsWithContext(context.Background()) +} + +func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) { + err := p.fillFromStatusWithContext(ctx) + if err != nil { + return []int32{}, err + } + return p.gids, nil +} + +func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) { + err := p.fillFromStatusWithContext(ctx) + if err != nil { + return []int32{}, err + } + return p.groups, nil +} + +// Terminal returns a terminal which is associated with the process. +func (p *Process) Terminal() (string, error) { + return p.TerminalWithContext(context.Background()) +} + +func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { + t, _, _, _, _, _, _, err := p.fillFromStatWithContext(ctx) + if err != nil { + return "", err + } + termmap, err := getTerminalMap() + if err != nil { + return "", err + } + terminal := termmap[t] + return terminal, nil +} + +// Nice returns a nice value (priority). +// Notice: gopsutil can not set nice value. +func (p *Process) Nice() (int32, error) { + return p.NiceWithContext(context.Background()) +} + +func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { + _, _, _, _, _, nice, _, err := p.fillFromStatWithContext(ctx) + if err != nil { + return 0, err + } + return nice, nil +} + +// IOnice returns process I/O nice value (priority). +func (p *Process) IOnice() (int32, error) { + return p.IOniceWithContext(context.Background()) +} + +func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} + +// Rlimit returns Resource Limits. +func (p *Process) Rlimit() ([]RlimitStat, error) { + return p.RlimitWithContext(context.Background()) +} + +func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) { + return p.RlimitUsage(false) +} + +// RlimitUsage returns Resource Limits. +// If gatherUsed is true, the currently used value will be gathered and added +// to the resulting RlimitStat. +func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) { + return p.RlimitUsageWithContext(context.Background(), gatherUsed) +} + +func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) { + rlimits, err := p.fillFromLimitsWithContext(ctx) + if !gatherUsed || err != nil { + return rlimits, err + } + + _, _, _, _, rtprio, nice, _, err := p.fillFromStatWithContext(ctx) + if err != nil { + return nil, err + } + if err := p.fillFromStatusWithContext(ctx); err != nil { + return nil, err + } + + for i := range rlimits { + rs := &rlimits[i] + switch rs.Resource { + case RLIMIT_CPU: + times, err := p.Times() + if err != nil { + return nil, err + } + rs.Used = uint64(times.User + times.System) + case RLIMIT_DATA: + rs.Used = uint64(p.memInfo.Data) + case RLIMIT_STACK: + rs.Used = uint64(p.memInfo.Stack) + case RLIMIT_RSS: + rs.Used = uint64(p.memInfo.RSS) + case RLIMIT_NOFILE: + n, err := p.NumFDs() + if err != nil { + return nil, err + } + rs.Used = uint64(n) + case RLIMIT_MEMLOCK: + rs.Used = uint64(p.memInfo.Locked) + case RLIMIT_AS: + rs.Used = uint64(p.memInfo.VMS) + case RLIMIT_LOCKS: + //TODO we can get the used value from /proc/$pid/locks. But linux doesn't enforce it, so not a high priority. + case RLIMIT_SIGPENDING: + rs.Used = p.sigInfo.PendingProcess + case RLIMIT_NICE: + // The rlimit for nice is a little unusual, in that 0 means the niceness cannot be decreased beyond the current value, but it can be increased. + // So effectively: if rs.Soft == 0 { rs.Soft = rs.Used } + rs.Used = uint64(nice) + case RLIMIT_RTPRIO: + rs.Used = uint64(rtprio) + } + } + + return rlimits, err +} + +// IOCounters returns IO Counters. +func (p *Process) IOCounters() (*IOCountersStat, error) { + return p.IOCountersWithContext(context.Background()) +} + +func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) { + return p.fillFromIOWithContext(ctx) +} + +// NumCtxSwitches returns the number of the context switches of the process. +func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { + return p.NumCtxSwitchesWithContext(context.Background()) +} + +func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) { + err := p.fillFromStatusWithContext(ctx) + if err != nil { + return nil, err + } + return p.numCtxSwitches, nil +} + +// NumFDs returns the number of File Descriptors used by the process. +func (p *Process) NumFDs() (int32, error) { + return p.NumFDsWithContext(context.Background()) +} + +func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) { + _, fnames, err := p.fillFromfdListWithContext(ctx) + return int32(len(fnames)), err +} + +// NumThreads returns the number of threads used by the process. +func (p *Process) NumThreads() (int32, error) { + return p.NumThreadsWithContext(context.Background()) +} + +func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { + err := p.fillFromStatusWithContext(ctx) + if err != nil { + return 0, err + } + return p.numThreads, nil +} + +func (p *Process) Threads() (map[int32]*cpu.TimesStat, error) { + return p.ThreadsWithContext(context.Background()) +} + +func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) { + ret := make(map[int32]*cpu.TimesStat) + taskPath := common.HostProc(strconv.Itoa(int(p.Pid)), "task") + + tids, err := readPidsFromDir(taskPath) + if err != nil { + return nil, err + } + + for _, tid := range tids { + _, _, cpuTimes, _, _, _, _, err := p.fillFromTIDStatWithContext(ctx, tid) + if err != nil { + return nil, err + } + ret[tid] = cpuTimes + } + + return ret, nil +} + +// Times returns CPU times of the process. +func (p *Process) Times() (*cpu.TimesStat, error) { + return p.TimesWithContext(context.Background()) +} + +func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { + _, _, cpuTimes, _, _, _, _, err := p.fillFromStatWithContext(ctx) + if err != nil { + return nil, err + } + return cpuTimes, nil +} + +// CPUAffinity returns CPU affinity of the process. +// +// Notice: Not implemented yet. +func (p *Process) CPUAffinity() ([]int32, error) { + return p.CPUAffinityWithContext(context.Background()) +} + +func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} + +// MemoryInfo returns platform in-dependend memory information, such as RSS, VMS and Swap +func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { + return p.MemoryInfoWithContext(context.Background()) +} + +func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { + meminfo, _, err := p.fillFromStatmWithContext(ctx) + if err != nil { + return nil, err + } + return meminfo, nil +} + +// MemoryInfoEx returns platform dependend memory information. +func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { + return p.MemoryInfoExWithContext(context.Background()) +} + +func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) { + _, memInfoEx, err := p.fillFromStatmWithContext(ctx) + if err != nil { + return nil, err + } + return memInfoEx, nil +} + +// PageFaultsInfo returns the process's page fault counters +func (p *Process) PageFaults() (*PageFaultsStat, error) { + return p.PageFaultsWithContext(context.Background()) +} + +func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) { + _, _, _, _, _, _, pageFaults, err := p.fillFromStatWithContext(ctx) + if err != nil { + return nil, err + } + return pageFaults, nil + +} + +// Children returns a slice of Process of the process. +func (p *Process) Children() ([]*Process, error) { + return p.ChildrenWithContext(context.Background()) +} + +func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { + pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) + if err != nil { + if pids == nil || len(pids) == 0 { + return nil, ErrorNoChildren + } + return nil, err + } + ret := make([]*Process, 0, len(pids)) + for _, pid := range pids { + np, err := NewProcess(pid) + if err != nil { + return nil, err + } + ret = append(ret, np) + } + return ret, nil +} + +// OpenFiles returns a slice of OpenFilesStat opend by the process. +// OpenFilesStat includes a file path and file descriptor. +func (p *Process) OpenFiles() ([]OpenFilesStat, error) { + return p.OpenFilesWithContext(context.Background()) +} + +func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) { + _, ofs, err := p.fillFromfdWithContext(ctx) + if err != nil { + return nil, err + } + ret := make([]OpenFilesStat, len(ofs)) + for i, o := range ofs { + ret[i] = *o + } + + return ret, nil +} + +// Connections returns a slice of net.ConnectionStat used by the process. +// This returns all kind of the connection. This measn TCP, UDP or UNIX. +func (p *Process) Connections() ([]net.ConnectionStat, error) { + return p.ConnectionsWithContext(context.Background()) +} + +func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) { + return net.ConnectionsPid("all", p.Pid) +} + +// Connections returns a slice of net.ConnectionStat used by the process at most `max` +func (p *Process) ConnectionsMax(max int) ([]net.ConnectionStat, error) { + return p.ConnectionsMaxWithContext(context.Background(), max) +} + +func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) { + return net.ConnectionsPidMax("all", p.Pid, max) +} + +// NetIOCounters returns NetIOCounters of the process. +func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { + return p.NetIOCountersWithContext(context.Background(), pernic) +} + +func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) { + filename := common.HostProc(strconv.Itoa(int(p.Pid)), "net/dev") + return net.IOCountersByFile(pernic, filename) +} + +// MemoryMaps get memory maps from /proc/(pid)/smaps +func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { + return p.MemoryMapsWithContext(context.Background(), grouped) +} + +func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) { + pid := p.Pid + var ret []MemoryMapsStat + if grouped { + ret = make([]MemoryMapsStat, 1) + } + smapsPath := common.HostProc(strconv.Itoa(int(pid)), "smaps") + contents, err := ioutil.ReadFile(smapsPath) + if err != nil { + return nil, err + } + lines := strings.Split(string(contents), "\n") + + // function of parsing a block + getBlock := func(first_line []string, block []string) (MemoryMapsStat, error) { + m := MemoryMapsStat{} + m.Path = first_line[len(first_line)-1] + + for _, line := range block { + if strings.Contains(line, "VmFlags") { + continue + } + field := strings.Split(line, ":") + if len(field) < 2 { + continue + } + v := strings.Trim(field[1], "kB") // remove last "kB" + v = strings.TrimSpace(v) + t, err := strconv.ParseUint(v, 10, 64) + if err != nil { + return m, err + } + + switch field[0] { + case "Size": + m.Size = t + case "Rss": + m.Rss = t + case "Pss": + m.Pss = t + case "Shared_Clean": + m.SharedClean = t + case "Shared_Dirty": + m.SharedDirty = t + case "Private_Clean": + m.PrivateClean = t + case "Private_Dirty": + m.PrivateDirty = t + case "Referenced": + m.Referenced = t + case "Anonymous": + m.Anonymous = t + case "Swap": + m.Swap = t + } + } + return m, nil + } + + blocks := make([]string, 16) + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) > 0 && !strings.HasSuffix(fields[0], ":") { + // new block section + if len(blocks) > 0 { + g, err := getBlock(fields, blocks) + if err != nil { + return &ret, err + } + if grouped { + ret[0].Size += g.Size + ret[0].Rss += g.Rss + ret[0].Pss += g.Pss + ret[0].SharedClean += g.SharedClean + ret[0].SharedDirty += g.SharedDirty + ret[0].PrivateClean += g.PrivateClean + ret[0].PrivateDirty += g.PrivateDirty + ret[0].Referenced += g.Referenced + ret[0].Anonymous += g.Anonymous + ret[0].Swap += g.Swap + } else { + ret = append(ret, g) + } + } + // starts new block + blocks = make([]string, 16) + } else { + blocks = append(blocks, line) + } + } + + return &ret, nil +} + +/** +** Internal functions +**/ + +func limitToInt(val string) (int32, error) { + if val == "unlimited" { + return math.MaxInt32, nil + } else { + res, err := strconv.ParseInt(val, 10, 32) + if err != nil { + return 0, err + } + return int32(res), nil + } +} + +// Get num_fds from /proc/(pid)/limits +func (p *Process) fillFromLimitsWithContext(ctx context.Context) ([]RlimitStat, error) { + pid := p.Pid + limitsFile := common.HostProc(strconv.Itoa(int(pid)), "limits") + d, err := os.Open(limitsFile) + if err != nil { + return nil, err + } + defer d.Close() + + var limitStats []RlimitStat + + limitsScanner := bufio.NewScanner(d) + for limitsScanner.Scan() { + var statItem RlimitStat + + str := strings.Fields(limitsScanner.Text()) + + // Remove the header line + if strings.Contains(str[len(str)-1], "Units") { + continue + } + + // Assert that last item is a Hard limit + statItem.Hard, err = limitToInt(str[len(str)-1]) + if err != nil { + // On error remove last item an try once again since it can be unit or header line + str = str[:len(str)-1] + statItem.Hard, err = limitToInt(str[len(str)-1]) + if err != nil { + return nil, err + } + } + // Remove last item from string + str = str[:len(str)-1] + + //Now last item is a Soft limit + statItem.Soft, err = limitToInt(str[len(str)-1]) + if err != nil { + return nil, err + } + // Remove last item from string + str = str[:len(str)-1] + + //The rest is a stats name + resourceName := strings.Join(str, " ") + switch resourceName { + case "Max cpu time": + statItem.Resource = RLIMIT_CPU + case "Max file size": + statItem.Resource = RLIMIT_FSIZE + case "Max data size": + statItem.Resource = RLIMIT_DATA + case "Max stack size": + statItem.Resource = RLIMIT_STACK + case "Max core file size": + statItem.Resource = RLIMIT_CORE + case "Max resident set": + statItem.Resource = RLIMIT_RSS + case "Max processes": + statItem.Resource = RLIMIT_NPROC + case "Max open files": + statItem.Resource = RLIMIT_NOFILE + case "Max locked memory": + statItem.Resource = RLIMIT_MEMLOCK + case "Max address space": + statItem.Resource = RLIMIT_AS + case "Max file locks": + statItem.Resource = RLIMIT_LOCKS + case "Max pending signals": + statItem.Resource = RLIMIT_SIGPENDING + case "Max msgqueue size": + statItem.Resource = RLIMIT_MSGQUEUE + case "Max nice priority": + statItem.Resource = RLIMIT_NICE + case "Max realtime priority": + statItem.Resource = RLIMIT_RTPRIO + case "Max realtime timeout": + statItem.Resource = RLIMIT_RTTIME + default: + continue + } + + limitStats = append(limitStats, statItem) + } + + if err := limitsScanner.Err(); err != nil { + return nil, err + } + + return limitStats, nil +} + +// Get list of /proc/(pid)/fd files +func (p *Process) fillFromfdListWithContext(ctx context.Context) (string, []string, error) { + pid := p.Pid + statPath := common.HostProc(strconv.Itoa(int(pid)), "fd") + d, err := os.Open(statPath) + if err != nil { + return statPath, []string{}, err + } + defer d.Close() + fnames, err := d.Readdirnames(-1) + return statPath, fnames, err +} + +// Get num_fds from /proc/(pid)/fd +func (p *Process) fillFromfdWithContext(ctx context.Context) (int32, []*OpenFilesStat, error) { + statPath, fnames, err := p.fillFromfdListWithContext(ctx) + if err != nil { + return 0, nil, err + } + numFDs := int32(len(fnames)) + + var openfiles []*OpenFilesStat + for _, fd := range fnames { + fpath := filepath.Join(statPath, fd) + filepath, err := os.Readlink(fpath) + if err != nil { + continue + } + t, err := strconv.ParseUint(fd, 10, 64) + if err != nil { + return numFDs, openfiles, err + } + o := &OpenFilesStat{ + Path: filepath, + Fd: t, + } + openfiles = append(openfiles, o) + } + + return numFDs, openfiles, nil +} + +// Get cwd from /proc/(pid)/cwd +func (p *Process) fillFromCwdWithContext(ctx context.Context) (string, error) { + pid := p.Pid + cwdPath := common.HostProc(strconv.Itoa(int(pid)), "cwd") + cwd, err := os.Readlink(cwdPath) + if err != nil { + return "", err + } + return string(cwd), nil +} + +// Get exe from /proc/(pid)/exe +func (p *Process) fillFromExeWithContext(ctx context.Context) (string, error) { + pid := p.Pid + exePath := common.HostProc(strconv.Itoa(int(pid)), "exe") + exe, err := os.Readlink(exePath) + if err != nil { + return "", err + } + return string(exe), nil +} + +// Get cmdline from /proc/(pid)/cmdline +func (p *Process) fillFromCmdlineWithContext(ctx context.Context) (string, error) { + pid := p.Pid + cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline") + cmdline, err := ioutil.ReadFile(cmdPath) + if err != nil { + return "", err + } + ret := strings.FieldsFunc(string(cmdline), func(r rune) bool { + if r == '\u0000' { + return true + } + return false + }) + + return strings.Join(ret, " "), nil +} + +func (p *Process) fillSliceFromCmdlineWithContext(ctx context.Context) ([]string, error) { + pid := p.Pid + cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline") + cmdline, err := ioutil.ReadFile(cmdPath) + if err != nil { + return nil, err + } + if len(cmdline) == 0 { + return nil, nil + } + if cmdline[len(cmdline)-1] == 0 { + cmdline = cmdline[:len(cmdline)-1] + } + parts := bytes.Split(cmdline, []byte{0}) + var strParts []string + for _, p := range parts { + strParts = append(strParts, string(p)) + } + + return strParts, nil +} + +// Get IO status from /proc/(pid)/io +func (p *Process) fillFromIOWithContext(ctx context.Context) (*IOCountersStat, error) { + pid := p.Pid + ioPath := common.HostProc(strconv.Itoa(int(pid)), "io") + ioline, err := ioutil.ReadFile(ioPath) + if err != nil { + return nil, err + } + lines := strings.Split(string(ioline), "\n") + ret := &IOCountersStat{} + + for _, line := range lines { + field := strings.Fields(line) + if len(field) < 2 { + continue + } + t, err := strconv.ParseUint(field[1], 10, 64) + if err != nil { + return nil, err + } + param := field[0] + if strings.HasSuffix(param, ":") { + param = param[:len(param)-1] + } + switch param { + case "syscr": + ret.ReadCount = t + case "syscw": + ret.WriteCount = t + case "read_bytes": + ret.ReadBytes = t + case "write_bytes": + ret.WriteBytes = t + } + } + + return ret, nil +} + +// Get memory info from /proc/(pid)/statm +func (p *Process) fillFromStatmWithContext(ctx context.Context) (*MemoryInfoStat, *MemoryInfoExStat, error) { + pid := p.Pid + memPath := common.HostProc(strconv.Itoa(int(pid)), "statm") + contents, err := ioutil.ReadFile(memPath) + if err != nil { + return nil, nil, err + } + fields := strings.Split(string(contents), " ") + + vms, err := strconv.ParseUint(fields[0], 10, 64) + if err != nil { + return nil, nil, err + } + rss, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + return nil, nil, err + } + memInfo := &MemoryInfoStat{ + RSS: rss * PageSize, + VMS: vms * PageSize, + } + + shared, err := strconv.ParseUint(fields[2], 10, 64) + if err != nil { + return nil, nil, err + } + text, err := strconv.ParseUint(fields[3], 10, 64) + if err != nil { + return nil, nil, err + } + lib, err := strconv.ParseUint(fields[4], 10, 64) + if err != nil { + return nil, nil, err + } + dirty, err := strconv.ParseUint(fields[5], 10, 64) + if err != nil { + return nil, nil, err + } + + memInfoEx := &MemoryInfoExStat{ + RSS: rss * PageSize, + VMS: vms * PageSize, + Shared: shared * PageSize, + Text: text * PageSize, + Lib: lib * PageSize, + Dirty: dirty * PageSize, + } + + return memInfo, memInfoEx, nil +} + +// Get various status from /proc/(pid)/status +func (p *Process) fillFromStatusWithContext(ctx context.Context) error { + pid := p.Pid + statPath := common.HostProc(strconv.Itoa(int(pid)), "status") + contents, err := ioutil.ReadFile(statPath) + if err != nil { + return err + } + lines := strings.Split(string(contents), "\n") + p.numCtxSwitches = &NumCtxSwitchesStat{} + p.memInfo = &MemoryInfoStat{} + p.sigInfo = &SignalInfoStat{} + for _, line := range lines { + tabParts := strings.SplitN(line, "\t", 2) + if len(tabParts) < 2 { + continue + } + value := tabParts[1] + switch strings.TrimRight(tabParts[0], ":") { + case "Name": + p.name = strings.Trim(value, " \t") + if len(p.name) >= 15 { + cmdlineSlice, err := p.CmdlineSlice() + if err != nil { + return err + } + if len(cmdlineSlice) > 0 { + extendedName := filepath.Base(cmdlineSlice[0]) + if strings.HasPrefix(extendedName, p.name) { + p.name = extendedName + } else { + p.name = cmdlineSlice[0] + } + } + } + case "State": + p.status = value[0:1] + case "PPid", "Ppid": + pval, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return err + } + p.parent = int32(pval) + case "Tgid": + pval, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return err + } + p.tgid = int32(pval) + case "Uid": + p.uids = make([]int32, 0, 4) + for _, i := range strings.Split(value, "\t") { + v, err := strconv.ParseInt(i, 10, 32) + if err != nil { + return err + } + p.uids = append(p.uids, int32(v)) + } + case "Gid": + p.gids = make([]int32, 0, 4) + for _, i := range strings.Split(value, "\t") { + v, err := strconv.ParseInt(i, 10, 32) + if err != nil { + return err + } + p.gids = append(p.gids, int32(v)) + } + case "Groups": + groups := strings.Fields(value) + p.groups = make([]int32, 0, len(groups)) + for _, i := range groups { + v, err := strconv.ParseInt(i, 10, 32) + if err != nil { + return err + } + p.groups = append(p.groups, int32(v)) + } + case "Threads": + v, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return err + } + p.numThreads = int32(v) + case "voluntary_ctxt_switches": + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + p.numCtxSwitches.Voluntary = v + case "nonvoluntary_ctxt_switches": + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + p.numCtxSwitches.Involuntary = v + case "VmRSS": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.RSS = v * 1024 + case "VmSize": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.VMS = v * 1024 + case "VmSwap": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.Swap = v * 1024 + case "VmHWM": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.HWM = v * 1024 + case "VmData": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.Data = v * 1024 + case "VmStk": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.Stack = v * 1024 + case "VmLck": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.Locked = v * 1024 + case "SigPnd": + v, err := strconv.ParseUint(value, 16, 64) + if err != nil { + return err + } + p.sigInfo.PendingThread = v + case "ShdPnd": + v, err := strconv.ParseUint(value, 16, 64) + if err != nil { + return err + } + p.sigInfo.PendingProcess = v + case "SigBlk": + v, err := strconv.ParseUint(value, 16, 64) + if err != nil { + return err + } + p.sigInfo.Blocked = v + case "SigIgn": + v, err := strconv.ParseUint(value, 16, 64) + if err != nil { + return err + } + p.sigInfo.Ignored = v + case "SigCgt": + v, err := strconv.ParseUint(value, 16, 64) + if err != nil { + return err + } + p.sigInfo.Caught = v + } + + } + return nil +} + +func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, *PageFaultsStat, error) { + pid := p.Pid + var statPath string + + if tid == -1 { + statPath = common.HostProc(strconv.Itoa(int(pid)), "stat") + } else { + statPath = common.HostProc(strconv.Itoa(int(pid)), "task", strconv.Itoa(int(tid)), "stat") + } + + contents, err := ioutil.ReadFile(statPath) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + fields := strings.Fields(string(contents)) + + i := 1 + for !strings.HasSuffix(fields[i], ")") { + i++ + } + + terminal, err := strconv.ParseUint(fields[i+5], 10, 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + + ppid, err := strconv.ParseInt(fields[i+2], 10, 32) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + utime, err := strconv.ParseFloat(fields[i+12], 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + + stime, err := strconv.ParseFloat(fields[i+13], 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + + // There is no such thing as iotime in stat file. As an approximation, we + // will use delayacct_blkio_ticks (aggregated block I/O delays, as per Linux + // docs). Note: I am assuming at least Linux 2.6.18 + iotime, err := strconv.ParseFloat(fields[i+40], 64) + if err != nil { + iotime = 0 // Ancient linux version, most likely + } + + cpuTimes := &cpu.TimesStat{ + CPU: "cpu", + User: float64(utime / ClockTicks), + System: float64(stime / ClockTicks), + Iowait: float64(iotime / ClockTicks), + } + + bootTime, _ := common.BootTimeWithContext(ctx) + t, err := strconv.ParseUint(fields[i+20], 10, 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + ctime := (t / uint64(ClockTicks)) + uint64(bootTime) + createTime := int64(ctime * 1000) + + rtpriority, err := strconv.ParseInt(fields[i+16], 10, 32) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + if rtpriority < 0 { + rtpriority = rtpriority*-1 - 1 + } else { + rtpriority = 0 + } + + // p.Nice = mustParseInt32(fields[18]) + // use syscall instead of parse Stat file + snice, _ := unix.Getpriority(PrioProcess, int(pid)) + nice := int32(snice) // FIXME: is this true? + + minFault, err := strconv.ParseUint(fields[i+8], 10, 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + cMinFault, err := strconv.ParseUint(fields[i+9], 10, 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + majFault, err := strconv.ParseUint(fields[i+10], 10, 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + cMajFault, err := strconv.ParseUint(fields[i+11], 10, 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, nil, err + } + + faults := &PageFaultsStat{ + MinorFaults: minFault, + MajorFaults: majFault, + ChildMinorFaults: cMinFault, + ChildMajorFaults: cMajFault, + } + + return terminal, int32(ppid), cpuTimes, createTime, uint32(rtpriority), nice, faults, nil +} + +func (p *Process) fillFromStatWithContext(ctx context.Context) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, *PageFaultsStat, error) { + return p.fillFromTIDStatWithContext(ctx, -1) +} + +func pidsWithContext(ctx context.Context) ([]int32, error) { + return readPidsFromDir(common.HostProc()) +} + +// Process returns a slice of pointers to Process structs for all +// currently running processes. +func Processes() ([]*Process, error) { + return ProcessesWithContext(context.Background()) +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { + out := []*Process{} + + pids, err := PidsWithContext(ctx) + if err != nil { + return out, err + } + + for _, pid := range pids { + p, err := NewProcess(pid) + if err != nil { + continue + } + out = append(out, p) + } + + return out, nil +} + +func readPidsFromDir(path string) ([]int32, error) { + var ret []int32 + + d, err := os.Open(path) + if err != nil { + return nil, err + } + defer d.Close() + + fnames, err := d.Readdirnames(-1) + if err != nil { + return nil, err + } + for _, fname := range fnames { + pid, err := strconv.ParseInt(fname, 10, 32) + if err != nil { + // if not numeric name, just skip + continue + } + ret = append(ret, int32(pid)) + } + + return ret, nil +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_openbsd.go b/vendor/github.com/shirou/gopsutil/process/process_openbsd.go new file mode 100644 index 00000000000..93f9875ea34 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_openbsd.go @@ -0,0 +1,549 @@ +// +build openbsd + +package process + +import ( + "C" + "bytes" + "context" + "encoding/binary" + "os/exec" + "path/filepath" + "strconv" + "strings" + "unsafe" + + cpu "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/internal/common" + mem "github.com/shirou/gopsutil/mem" + net "github.com/shirou/gopsutil/net" + "golang.org/x/sys/unix" +) + +// MemoryInfoExStat is different between OSes +type MemoryInfoExStat struct { +} + +type MemoryMapsStat struct { +} + +func pidsWithContext(ctx context.Context) ([]int32, error) { + var ret []int32 + procs, err := Processes() + if err != nil { + return ret, nil + } + + for _, p := range procs { + ret = append(ret, p.Pid) + } + + return ret, nil +} + +func (p *Process) Ppid() (int32, error) { + return p.PpidWithContext(context.Background()) +} + +func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + + return k.Ppid, nil +} +func (p *Process) Name() (string, error) { + return p.NameWithContext(context.Background()) +} + +func (p *Process) NameWithContext(ctx context.Context) (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + name := common.IntToString(k.Comm[:]) + + if len(name) >= 15 { + cmdlineSlice, err := p.CmdlineSliceWithContext(ctx) + if err != nil { + return "", err + } + if len(cmdlineSlice) > 0 { + extendedName := filepath.Base(cmdlineSlice[0]) + if strings.HasPrefix(extendedName, p.name) { + name = extendedName + } else { + name = cmdlineSlice[0] + } + } + } + + return name, nil +} +func (p *Process) Tgid() (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) Exe() (string, error) { + return p.ExeWithContext(context.Background()) +} + +func (p *Process) ExeWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) CmdlineSlice() ([]string, error) { + return p.CmdlineSliceWithContext(context.Background()) +} + +func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { + mib := []int32{CTLKern, KernProcArgs, p.Pid, KernProcArgv} + buf, _, err := common.CallSyscall(mib) + + if err != nil { + return nil, err + } + + argc := 0 + argvp := unsafe.Pointer(&buf[0]) + argv := *(**C.char)(unsafe.Pointer(argvp)) + size := unsafe.Sizeof(argv) + var strParts []string + + for argv != nil { + strParts = append(strParts, C.GoString(argv)) + + argc++ + argv = *(**C.char)(unsafe.Pointer(uintptr(argvp) + uintptr(argc)*size)) + } + return strParts, nil +} + +func (p *Process) Cmdline() (string, error) { + return p.CmdlineWithContext(context.Background()) +} + +func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) { + argv, err := p.CmdlineSlice() + if err != nil { + return "", err + } + return strings.Join(argv, " "), nil +} + +func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) Cwd() (string, error) { + return p.CwdWithContext(context.Background()) +} + +func (p *Process) CwdWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} +func (p *Process) Parent() (*Process, error) { + return p.ParentWithContext(context.Background()) +} + +func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) { + return p, common.ErrNotImplementedError +} +func (p *Process) Status() (string, error) { + return p.StatusWithContext(context.Background()) +} + +func (p *Process) StatusWithContext(ctx context.Context) (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + var s string + switch k.Stat { + case SIDL: + case SRUN: + case SONPROC: + s = "R" + case SSLEEP: + s = "S" + case SSTOP: + s = "T" + case SDEAD: + s = "Z" + } + + return s, nil +} +func (p *Process) Foreground() (bool, error) { + return p.ForegroundWithContext(context.Background()) +} + +func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { + // see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details + pid := p.Pid + ps, err := exec.LookPath("ps") + if err != nil { + return false, err + } + out, err := invoke.CommandWithContext(ctx, ps, "-o", "stat=", "-p", strconv.Itoa(int(pid))) + if err != nil { + return false, err + } + return strings.IndexByte(string(out), '+') != -1, nil +} +func (p *Process) Uids() ([]int32, error) { + return p.UidsWithContext(context.Background()) +} + +func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + uids := make([]int32, 0, 3) + + uids = append(uids, int32(k.Ruid), int32(k.Uid), int32(k.Svuid)) + + return uids, nil +} +func (p *Process) Gids() ([]int32, error) { + return p.GidsWithContext(context.Background()) +} + +func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + gids := make([]int32, 0, 3) + gids = append(gids, int32(k.Rgid), int32(k.Ngroups), int32(k.Svgid)) + + return gids, nil +} +func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + return k.Groups, nil +} +func (p *Process) Terminal() (string, error) { + return p.TerminalWithContext(context.Background()) +} + +func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + + ttyNr := uint64(k.Tdev) + + termmap, err := getTerminalMap() + if err != nil { + return "", err + } + + return termmap[ttyNr], nil +} +func (p *Process) Nice() (int32, error) { + return p.NiceWithContext(context.Background()) +} + +func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + return int32(k.Nice), nil +} +func (p *Process) IOnice() (int32, error) { + return p.IOniceWithContext(context.Background()) +} + +func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) Rlimit() ([]RlimitStat, error) { + return p.RlimitWithContext(context.Background()) +} + +func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) { + var rlimit []RlimitStat + return rlimit, common.ErrNotImplementedError +} +func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) { + return p.RlimitUsageWithContext(context.Background(), gatherUsed) +} + +func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) { + var rlimit []RlimitStat + return rlimit, common.ErrNotImplementedError +} +func (p *Process) IOCounters() (*IOCountersStat, error) { + return p.IOCountersWithContext(context.Background()) +} + +func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + return &IOCountersStat{ + ReadCount: uint64(k.Uru_inblock), + WriteCount: uint64(k.Uru_oublock), + }, nil +} +func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { + return p.NumCtxSwitchesWithContext(context.Background()) +} + +func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) NumFDs() (int32, error) { + return p.NumFDsWithContext(context.Background()) +} + +func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) NumThreads() (int32, error) { + return p.NumThreadsWithContext(context.Background()) +} + +func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { + /* not supported, just return 1 */ + return 1, nil +} +func (p *Process) Threads() (map[int32]*cpu.TimesStat, error) { + return p.ThreadsWithContext(context.Background()) +} + +func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) { + ret := make(map[int32]*cpu.TimesStat) + return ret, common.ErrNotImplementedError +} +func (p *Process) Times() (*cpu.TimesStat, error) { + return p.TimesWithContext(context.Background()) +} + +func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + return &cpu.TimesStat{ + CPU: "cpu", + User: float64(k.Uutime_sec) + float64(k.Uutime_usec)/1000000, + System: float64(k.Ustime_sec) + float64(k.Ustime_usec)/1000000, + }, nil +} +func (p *Process) CPUAffinity() ([]int32, error) { + return p.CPUAffinityWithContext(context.Background()) +} + +func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { + return p.MemoryInfoWithContext(context.Background()) +} + +func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + pageSize, err := mem.GetPageSize() + if err != nil { + return nil, err + } + + return &MemoryInfoStat{ + RSS: uint64(k.Vm_rssize) * pageSize, + VMS: uint64(k.Vm_tsize) + uint64(k.Vm_dsize) + + uint64(k.Vm_ssize), + }, nil +} +func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { + return p.MemoryInfoExWithContext(context.Background()) +} + +func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) PageFaults() (*PageFaultsStat, error) { + return p.PageFaultsWithContext(context.Background()) +} + +func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) Children() ([]*Process, error) { + return p.ChildrenWithContext(context.Background()) +} + +func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { + pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) + if err != nil { + return nil, err + } + ret := make([]*Process, 0, len(pids)) + for _, pid := range pids { + np, err := NewProcess(pid) + if err != nil { + return nil, err + } + ret = append(ret, np) + } + return ret, nil +} + +func (p *Process) OpenFiles() ([]OpenFilesStat, error) { + return p.OpenFilesWithContext(context.Background()) +} + +func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) Connections() ([]net.ConnectionStat, error) { + return p.ConnectionsWithContext(context.Background()) +} + +func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) ConnectionsMax(max int) ([]net.ConnectionStat, error) { + return p.ConnectionsMaxWithContext(context.Background(), max) +} + +func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) { + return []net.ConnectionStat{}, common.ErrNotImplementedError +} + +func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { + return p.NetIOCountersWithContext(context.Background(), pernic) +} + +func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { + return p.MemoryMapsWithContext(context.Background(), grouped) +} + +func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) { + var ret []MemoryMapsStat + return &ret, common.ErrNotImplementedError +} + +func Processes() ([]*Process, error) { + return ProcessesWithContext(context.Background()) +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { + results := []*Process{} + + buf, length, err := CallKernProcSyscall(KernProcAll, 0) + + if err != nil { + return results, err + } + + // get kinfo_proc size + count := int(length / uint64(sizeOfKinfoProc)) + + // parse buf to procs + for i := 0; i < count; i++ { + b := buf[i*sizeOfKinfoProc : (i+1)*sizeOfKinfoProc] + k, err := parseKinfoProc(b) + if err != nil { + continue + } + p, err := NewProcess(int32(k.Pid)) + if err != nil { + continue + } + + results = append(results, p) + } + + return results, nil +} + +func parseKinfoProc(buf []byte) (KinfoProc, error) { + var k KinfoProc + br := bytes.NewReader(buf) + err := common.Read(br, binary.LittleEndian, &k) + return k, err +} + +func (p *Process) getKProc() (*KinfoProc, error) { + return p.getKProcWithContext(context.Background()) +} + +func (p *Process) getKProcWithContext(ctx context.Context) (*KinfoProc, error) { + buf, length, err := CallKernProcSyscall(KernProcPID, p.Pid) + if err != nil { + return nil, err + } + if length != sizeOfKinfoProc { + return nil, err + } + + k, err := parseKinfoProc(buf) + if err != nil { + return nil, err + } + return &k, nil +} + +func CallKernProcSyscall(op int32, arg int32) ([]byte, uint64, error) { + return CallKernProcSyscallWithContext(context.Background(), op, arg) +} + +func CallKernProcSyscallWithContext(ctx context.Context, op int32, arg int32) ([]byte, uint64, error) { + mib := []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, 0} + mibptr := unsafe.Pointer(&mib[0]) + miblen := uint64(len(mib)) + length := uint64(0) + _, _, err := unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(mibptr), + uintptr(miblen), + 0, + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + return nil, length, err + } + + count := int32(length / uint64(sizeOfKinfoProc)) + mib = []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, count} + mibptr = unsafe.Pointer(&mib[0]) + miblen = uint64(len(mib)) + // get proc info itself + buf := make([]byte, length) + _, _, err = unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(mibptr), + uintptr(miblen), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + return buf, length, err + } + + return buf, length, nil +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_openbsd_amd64.go b/vendor/github.com/shirou/gopsutil/process/process_openbsd_amd64.go new file mode 100644 index 00000000000..8607422b5f5 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_openbsd_amd64.go @@ -0,0 +1,200 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_openbsd.go + +package process + +const ( + CTLKern = 1 + KernProc = 66 + KernProcAll = 0 + KernProcPID = 1 + KernProcProc = 8 + KernProcPathname = 12 + KernProcArgs = 55 + KernProcArgv = 1 + KernProcEnv = 3 +) + +const ( + ArgMax = 256 * 1024 +) + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 +) + +const ( + sizeOfKinfoVmentry = 0x50 + sizeOfKinfoProc = 0x268 +) + +const ( + SIDL = 1 + SRUN = 2 + SSLEEP = 3 + SSTOP = 4 + SZOMB = 5 + SDEAD = 6 + SONPROC = 7 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int64 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Rlimit struct { + Cur uint64 + Max uint64 +} + +type KinfoProc struct { + Forw uint64 + Back uint64 + Paddr uint64 + Addr uint64 + Fd uint64 + Stats uint64 + Limit uint64 + Vmspace uint64 + Sigacts uint64 + Sess uint64 + Tsess uint64 + Ru uint64 + Eflag int32 + Exitsig int32 + Flag int32 + Pid int32 + Ppid int32 + Sid int32 + X_pgid int32 + Tpgid int32 + Uid uint32 + Ruid uint32 + Gid uint32 + Rgid uint32 + Groups [16]uint32 + Ngroups int16 + Jobc int16 + Tdev uint32 + Estcpu uint32 + Rtime_sec uint32 + Rtime_usec uint32 + Cpticks int32 + Pctcpu uint32 + Swtime uint32 + Slptime uint32 + Schedflags int32 + Uticks uint64 + Sticks uint64 + Iticks uint64 + Tracep uint64 + Traceflag int32 + Holdcnt int32 + Siglist int32 + Sigmask uint32 + Sigignore uint32 + Sigcatch uint32 + Stat int8 + Priority uint8 + Usrpri uint8 + Nice uint8 + Xstat uint16 + Acflag uint16 + Comm [24]int8 + Wmesg [8]int8 + Wchan uint64 + Login [32]int8 + Vm_rssize int32 + Vm_tsize int32 + Vm_dsize int32 + Vm_ssize int32 + Uvalid int64 + Ustart_sec uint64 + Ustart_usec uint32 + Uutime_sec uint32 + Uutime_usec uint32 + Ustime_sec uint32 + Ustime_usec uint32 + Pad_cgo_0 [4]byte + Uru_maxrss uint64 + Uru_ixrss uint64 + Uru_idrss uint64 + Uru_isrss uint64 + Uru_minflt uint64 + Uru_majflt uint64 + Uru_nswap uint64 + Uru_inblock uint64 + Uru_oublock uint64 + Uru_msgsnd uint64 + Uru_msgrcv uint64 + Uru_nsignals uint64 + Uru_nvcsw uint64 + Uru_nivcsw uint64 + Uctime_sec uint32 + Uctime_usec uint32 + Psflags int32 + Spare int32 + Svuid uint32 + Svgid uint32 + Emul [8]int8 + Rlim_rss_cur uint64 + Cpuid uint64 + Vm_map_size uint64 + Tid int32 + Rtableid uint32 +} + +type Priority struct{} + +type KinfoVmentry struct { + Start uint64 + End uint64 + Guard uint64 + Fspace uint64 + Fspace_augment uint64 + Offset uint64 + Wired_count int32 + Etype int32 + Protection int32 + Max_protection int32 + Advice int32 + Inheritance int32 + Flags uint8 + Pad_cgo_0 [7]byte +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_posix.go b/vendor/github.com/shirou/gopsutil/process/process_posix.go new file mode 100644 index 00000000000..109239a577c --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_posix.go @@ -0,0 +1,191 @@ +// +build linux freebsd openbsd darwin + +package process + +import ( + "context" + "fmt" + "os" + "os/user" + "path/filepath" + "strconv" + "strings" + "syscall" + + "github.com/shirou/gopsutil/internal/common" + "golang.org/x/sys/unix" +) + +// POSIX +func getTerminalMap() (map[uint64]string, error) { + ret := make(map[uint64]string) + var termfiles []string + + d, err := os.Open("/dev") + if err != nil { + return nil, err + } + defer d.Close() + + devnames, err := d.Readdirnames(-1) + if err != nil { + return nil, err + } + for _, devname := range devnames { + if strings.HasPrefix(devname, "/dev/tty") { + termfiles = append(termfiles, "/dev/tty/"+devname) + } + } + + var ptsnames []string + ptsd, err := os.Open("/dev/pts") + if err != nil { + ptsnames, _ = filepath.Glob("/dev/ttyp*") + if ptsnames == nil { + return nil, err + } + } + defer ptsd.Close() + + if ptsnames == nil { + defer ptsd.Close() + ptsnames, err = ptsd.Readdirnames(-1) + if err != nil { + return nil, err + } + for _, ptsname := range ptsnames { + termfiles = append(termfiles, "/dev/pts/"+ptsname) + } + } else { + termfiles = ptsnames + } + + for _, name := range termfiles { + stat := unix.Stat_t{} + if err = unix.Stat(name, &stat); err != nil { + return nil, err + } + rdev := uint64(stat.Rdev) + ret[rdev] = strings.Replace(name, "/dev", "", -1) + } + return ret, nil +} + +func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { + if pid <= 0 { + return false, fmt.Errorf("invalid pid %v", pid) + } + proc, err := os.FindProcess(int(pid)) + if err != nil { + return false, err + } + + if _, err := os.Stat(common.HostProc()); err == nil { //Means that proc filesystem exist + // Checking PID existence based on existence of //proc/ folder + // This covers the case when running inside container with a different process namespace (by default) + + _, err := os.Stat(common.HostProc(strconv.Itoa(int(pid)))) + if os.IsNotExist(err) { + return false, nil + } + return err == nil, err + } + + //'/proc' filesystem is not exist, checking of PID existence is done via signalling the process + //Make sense only if we run in the same process namespace + err = proc.Signal(syscall.Signal(0)) + if err == nil { + return true, nil + } + if err.Error() == "os: process already finished" { + return false, nil + } + errno, ok := err.(syscall.Errno) + if !ok { + return false, err + } + switch errno { + case syscall.ESRCH: + return false, nil + case syscall.EPERM: + return true, nil + } + + return false, err +} + +// SendSignal sends a unix.Signal to the process. +// Currently, SIGSTOP, SIGCONT, SIGTERM and SIGKILL are supported. +func (p *Process) SendSignal(sig syscall.Signal) error { + return p.SendSignalWithContext(context.Background(), sig) +} + +func (p *Process) SendSignalWithContext(ctx context.Context, sig syscall.Signal) error { + process, err := os.FindProcess(int(p.Pid)) + if err != nil { + return err + } + + err = process.Signal(sig) + if err != nil { + return err + } + + return nil +} + +// Suspend sends SIGSTOP to the process. +func (p *Process) Suspend() error { + return p.SuspendWithContext(context.Background()) +} + +func (p *Process) SuspendWithContext(ctx context.Context) error { + return p.SendSignal(unix.SIGSTOP) +} + +// Resume sends SIGCONT to the process. +func (p *Process) Resume() error { + return p.ResumeWithContext(context.Background()) +} + +func (p *Process) ResumeWithContext(ctx context.Context) error { + return p.SendSignal(unix.SIGCONT) +} + +// Terminate sends SIGTERM to the process. +func (p *Process) Terminate() error { + return p.TerminateWithContext(context.Background()) +} + +func (p *Process) TerminateWithContext(ctx context.Context) error { + return p.SendSignal(unix.SIGTERM) +} + +// Kill sends SIGKILL to the process. +func (p *Process) Kill() error { + return p.KillWithContext(context.Background()) +} + +func (p *Process) KillWithContext(ctx context.Context) error { + return p.SendSignal(unix.SIGKILL) +} + +// Username returns a username of the process. +func (p *Process) Username() (string, error) { + return p.UsernameWithContext(context.Background()) +} + +func (p *Process) UsernameWithContext(ctx context.Context) (string, error) { + uids, err := p.Uids() + if err != nil { + return "", err + } + if len(uids) > 0 { + u, err := user.LookupId(strconv.Itoa(int(uids[0]))) + if err != nil { + return "", err + } + return u.Username, nil + } + return "", nil +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_windows.go b/vendor/github.com/shirou/gopsutil/process/process_windows.go new file mode 100644 index 00000000000..efb4dc1e73e --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_windows.go @@ -0,0 +1,1009 @@ +// +build windows + +package process + +import ( + "context" + "errors" + "fmt" + "os" + "strings" + "syscall" + "unsafe" + + cpu "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/internal/common" + net "github.com/shirou/gopsutil/net" + "golang.org/x/sys/windows" +) + +var ( + modntdll = windows.NewLazySystemDLL("ntdll.dll") + procNtResumeProcess = modntdll.NewProc("NtResumeProcess") + procNtSuspendProcess = modntdll.NewProc("NtSuspendProcess") + + modpsapi = windows.NewLazySystemDLL("psapi.dll") + procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") + procGetProcessImageFileNameW = modpsapi.NewProc("GetProcessImageFileNameW") + + advapi32 = windows.NewLazySystemDLL("advapi32.dll") + procLookupPrivilegeValue = advapi32.NewProc("LookupPrivilegeValueW") + procAdjustTokenPrivileges = advapi32.NewProc("AdjustTokenPrivileges") + + procQueryFullProcessImageNameW = common.Modkernel32.NewProc("QueryFullProcessImageNameW") + procGetPriorityClass = common.Modkernel32.NewProc("GetPriorityClass") + procGetProcessIoCounters = common.Modkernel32.NewProc("GetProcessIoCounters") + procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo") + + processorArchitecture uint +) + +type SystemProcessInformation struct { + NextEntryOffset uint64 + NumberOfThreads uint64 + Reserved1 [48]byte + Reserved2 [3]byte + UniqueProcessID uintptr + Reserved3 uintptr + HandleCount uint64 + Reserved4 [4]byte + Reserved5 [11]byte + PeakPagefileUsage uint64 + PrivatePageCount uint64 + Reserved6 [6]uint64 +} + +type systemProcessorInformation struct { + ProcessorArchitecture uint16 + ProcessorLevel uint16 + ProcessorRevision uint16 + Reserved uint16 + ProcessorFeatureBits uint16 +} + +type systemInfo struct { + wProcessorArchitecture uint16 + wReserved uint16 + dwPageSize uint32 + lpMinimumApplicationAddress uintptr + lpMaximumApplicationAddress uintptr + dwActiveProcessorMask uintptr + dwNumberOfProcessors uint32 + dwProcessorType uint32 + dwAllocationGranularity uint32 + wProcessorLevel uint16 + wProcessorRevision uint16 +} + +// Memory_info_ex is different between OSes +type MemoryInfoExStat struct { +} + +type MemoryMapsStat struct { +} + +// ioCounters is an equivalent representation of IO_COUNTERS in the Windows API. +// https://docs.microsoft.com/windows/win32/api/winnt/ns-winnt-io_counters +type ioCounters struct { + ReadOperationCount uint64 + WriteOperationCount uint64 + OtherOperationCount uint64 + ReadTransferCount uint64 + WriteTransferCount uint64 + OtherTransferCount uint64 +} + +type processBasicInformation32 struct { + Reserved1 uint32 + PebBaseAddress uint32 + Reserved2 uint32 + Reserved3 uint32 + UniqueProcessId uint32 + Reserved4 uint32 +} + +type processBasicInformation64 struct { + Reserved1 uint64 + PebBaseAddress uint64 + Reserved2 uint64 + Reserved3 uint64 + UniqueProcessId uint64 + Reserved4 uint64 +} + +type winLUID struct { + LowPart winDWord + HighPart winLong +} + +// LUID_AND_ATTRIBUTES +type winLUIDAndAttributes struct { + Luid winLUID + Attributes winDWord +} + +// TOKEN_PRIVILEGES +type winTokenPriviledges struct { + PrivilegeCount winDWord + Privileges [1]winLUIDAndAttributes +} + +type winLong int32 +type winDWord uint32 + +func init() { + var systemInfo systemInfo + + procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo))) + processorArchitecture = uint(systemInfo.wProcessorArchitecture) + + // enable SeDebugPrivilege https://github.com/midstar/proci/blob/6ec79f57b90ba3d9efa2a7b16ef9c9369d4be875/proci_windows.go#L80-L119 + handle, err := syscall.GetCurrentProcess() + if err != nil { + return + } + + var token syscall.Token + err = syscall.OpenProcessToken(handle, 0x0028, &token) + if err != nil { + return + } + defer token.Close() + + tokenPriviledges := winTokenPriviledges{PrivilegeCount: 1} + lpName := syscall.StringToUTF16("SeDebugPrivilege") + ret, _, _ := procLookupPrivilegeValue.Call( + 0, + uintptr(unsafe.Pointer(&lpName[0])), + uintptr(unsafe.Pointer(&tokenPriviledges.Privileges[0].Luid))) + if ret == 0 { + return + } + + tokenPriviledges.Privileges[0].Attributes = 0x00000002 // SE_PRIVILEGE_ENABLED + + procAdjustTokenPrivileges.Call( + uintptr(token), + 0, + uintptr(unsafe.Pointer(&tokenPriviledges)), + uintptr(unsafe.Sizeof(tokenPriviledges)), + 0, + 0) +} + +func pidsWithContext(ctx context.Context) ([]int32, error) { + // inspired by https://gist.github.com/henkman/3083408 + // and https://github.com/giampaolo/psutil/blob/1c3a15f637521ba5c0031283da39c733fda53e4c/psutil/arch/windows/process_info.c#L315-L329 + var ret []int32 + var read uint32 = 0 + var psSize uint32 = 1024 + const dwordSize uint32 = 4 + + for { + ps := make([]uint32, psSize) + if err := windows.EnumProcesses(ps, &read); err != nil { + return nil, err + } + if uint32(len(ps)) == read { // ps buffer was too small to host every results, retry with a bigger one + psSize += 1024 + continue + } + for _, pid := range ps[:read/dwordSize] { + ret = append(ret, int32(pid)) + } + return ret, nil + + } + +} + +func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { + if pid == 0 { // special case for pid 0 System Idle Process + return true, nil + } + if pid < 0 { + return false, fmt.Errorf("invalid pid %v", pid) + } + if pid%4 != 0 { + // OpenProcess will succeed even on non-existing pid here https://devblogs.microsoft.com/oldnewthing/20080606-00/?p=22043 + // so we list every pid just to be sure and be future-proof + pids, err := PidsWithContext(ctx) + if err != nil { + return false, err + } + for _, i := range pids { + if i == pid { + return true, err + } + } + return false, err + } + const STILL_ACTIVE = 259 // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess + h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid)) + if err == windows.ERROR_ACCESS_DENIED { + return true, nil + } + if err == windows.ERROR_INVALID_PARAMETER { + return false, nil + } + if err != nil { + return false, err + } + defer syscall.CloseHandle(syscall.Handle(h)) + var exitCode uint32 + err = windows.GetExitCodeProcess(h, &exitCode) + return exitCode == STILL_ACTIVE, err +} + +func (p *Process) Ppid() (int32, error) { + return p.PpidWithContext(context.Background()) +} + +func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { + ppid, _, _, err := getFromSnapProcess(p.Pid) + if err != nil { + return 0, err + } + return ppid, nil +} + +func (p *Process) Name() (string, error) { + return p.NameWithContext(context.Background()) +} + +func (p *Process) NameWithContext(ctx context.Context) (string, error) { + _, _, name, err := getFromSnapProcess(p.Pid) + if err != nil { + return "", fmt.Errorf("could not get Name: %s", err) + } + return name, nil +} + +func (p *Process) Tgid() (int32, error) { + return 0, common.ErrNotImplementedError +} + +func (p *Process) Exe() (string, error) { + return p.ExeWithContext(context.Background()) +} + +func (p *Process) ExeWithContext(ctx context.Context) (string, error) { + c, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(p.Pid)) + if err != nil { + return "", err + } + defer windows.CloseHandle(c) + buf := make([]uint16, syscall.MAX_LONG_PATH) + size := uint32(syscall.MAX_LONG_PATH) + if err := procQueryFullProcessImageNameW.Find(); err == nil { // Vista+ + ret, _, err := procQueryFullProcessImageNameW.Call( + uintptr(c), + uintptr(0), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&size))) + if ret == 0 { + return "", err + } + return windows.UTF16ToString(buf[:]), nil + } + // XP fallback + ret, _, err := procGetProcessImageFileNameW.Call(uintptr(c), uintptr(unsafe.Pointer(&buf[0])), uintptr(size)) + if ret == 0 { + return "", err + } + return common.ConvertDOSPath(windows.UTF16ToString(buf[:])), nil +} + +func (p *Process) Cmdline() (string, error) { + return p.CmdlineWithContext(context.Background()) +} + +func (p *Process) CmdlineWithContext(_ context.Context) (string, error) { + cmdline, err := getProcessCommandLine(p.Pid) + if err != nil { + return "", fmt.Errorf("could not get CommandLine: %s", err) + } + return cmdline, nil +} + +// CmdlineSlice returns the command line arguments of the process as a slice with each +// element being an argument. This merely returns the CommandLine informations passed +// to the process split on the 0x20 ASCII character. +func (p *Process) CmdlineSlice() ([]string, error) { + return p.CmdlineSliceWithContext(context.Background()) +} + +func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { + cmdline, err := p.CmdlineWithContext(ctx) + if err != nil { + return nil, err + } + return strings.Split(cmdline, " "), nil +} + +func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) { + ru, err := getRusage(p.Pid) + if err != nil { + return 0, fmt.Errorf("could not get CreationDate: %s", err) + } + + return ru.CreationTime.Nanoseconds() / 1000000, nil +} + +func (p *Process) Cwd() (string, error) { + return p.CwdWithContext(context.Background()) +} + +func (p *Process) CwdWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} +func (p *Process) Parent() (*Process, error) { + return p.ParentWithContext(context.Background()) +} + +func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) { + ppid, err := p.PpidWithContext(ctx) + if err != nil { + return nil, fmt.Errorf("could not get ParentProcessID: %s", err) + } + + return NewProcess(ppid) +} +func (p *Process) Status() (string, error) { + return p.StatusWithContext(context.Background()) +} + +func (p *Process) StatusWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +func (p *Process) Foreground() (bool, error) { + return p.ForegroundWithContext(context.Background()) +} + +func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { + return false, common.ErrNotImplementedError +} + +func (p *Process) Username() (string, error) { + return p.UsernameWithContext(context.Background()) +} + +func (p *Process) UsernameWithContext(ctx context.Context) (string, error) { + pid := p.Pid + c, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid)) + if err != nil { + return "", err + } + defer windows.CloseHandle(c) + + var token syscall.Token + err = syscall.OpenProcessToken(syscall.Handle(c), syscall.TOKEN_QUERY, &token) + if err != nil { + return "", err + } + defer token.Close() + tokenUser, err := token.GetTokenUser() + if err != nil { + return "", err + } + + user, domain, _, err := tokenUser.User.Sid.LookupAccount("") + return domain + "\\" + user, err +} + +func (p *Process) Uids() ([]int32, error) { + return p.UidsWithContext(context.Background()) +} + +func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) { + var uids []int32 + + return uids, common.ErrNotImplementedError +} +func (p *Process) Gids() ([]int32, error) { + return p.GidsWithContext(context.Background()) +} + +func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) { + var gids []int32 + return gids, common.ErrNotImplementedError +} + +func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) { + var groups []int32 + return groups, common.ErrNotImplementedError +} +func (p *Process) Terminal() (string, error) { + return p.TerminalWithContext(context.Background()) +} + +func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { + return "", common.ErrNotImplementedError +} + +// priorityClasses maps a win32 priority class to its WMI equivalent Win32_Process.Priority +// https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getpriorityclass +// https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-process +var priorityClasses = map[int]int32{ + 0x00008000: 10, // ABOVE_NORMAL_PRIORITY_CLASS + 0x00004000: 6, // BELOW_NORMAL_PRIORITY_CLASS + 0x00000080: 13, // HIGH_PRIORITY_CLASS + 0x00000040: 4, // IDLE_PRIORITY_CLASS + 0x00000020: 8, // NORMAL_PRIORITY_CLASS + 0x00000100: 24, // REALTIME_PRIORITY_CLASS +} + +// Nice returns priority in Windows +func (p *Process) Nice() (int32, error) { + return p.NiceWithContext(context.Background()) +} + +func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { + c, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(p.Pid)) + if err != nil { + return 0, err + } + defer windows.CloseHandle(c) + ret, _, err := procGetPriorityClass.Call(uintptr(c)) + if ret == 0 { + return 0, err + } + priority, ok := priorityClasses[int(ret)] + if !ok { + return 0, fmt.Errorf("unknown priority class %v", ret) + } + return priority, nil +} +func (p *Process) IOnice() (int32, error) { + return p.IOniceWithContext(context.Background()) +} + +func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) Rlimit() ([]RlimitStat, error) { + return p.RlimitWithContext(context.Background()) +} + +func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) { + var rlimit []RlimitStat + + return rlimit, common.ErrNotImplementedError +} +func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) { + return p.RlimitUsageWithContext(context.Background(), gatherUsed) +} + +func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) { + var rlimit []RlimitStat + + return rlimit, common.ErrNotImplementedError +} + +func (p *Process) IOCounters() (*IOCountersStat, error) { + return p.IOCountersWithContext(context.Background()) +} + +func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) { + c, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(p.Pid)) + if err != nil { + return nil, err + } + defer windows.CloseHandle(c) + var ioCounters ioCounters + ret, _, err := procGetProcessIoCounters.Call(uintptr(c), uintptr(unsafe.Pointer(&ioCounters))) + if ret == 0 { + return nil, err + } + stats := &IOCountersStat{ + ReadCount: ioCounters.ReadOperationCount, + ReadBytes: ioCounters.ReadTransferCount, + WriteCount: ioCounters.WriteOperationCount, + WriteBytes: ioCounters.WriteTransferCount, + } + + return stats, nil +} +func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { + return p.NumCtxSwitchesWithContext(context.Background()) +} + +func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) NumFDs() (int32, error) { + return p.NumFDsWithContext(context.Background()) +} + +func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) { + return 0, common.ErrNotImplementedError +} +func (p *Process) NumThreads() (int32, error) { + return p.NumThreadsWithContext(context.Background()) +} + +func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { + _, ret, _, err := getFromSnapProcess(p.Pid) + if err != nil { + return 0, err + } + return ret, nil +} +func (p *Process) Threads() (map[int32]*cpu.TimesStat, error) { + return p.ThreadsWithContext(context.Background()) +} + +func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) { + ret := make(map[int32]*cpu.TimesStat) + return ret, common.ErrNotImplementedError +} +func (p *Process) Times() (*cpu.TimesStat, error) { + return p.TimesWithContext(context.Background()) +} + +func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { + sysTimes, err := getProcessCPUTimes(p.Pid) + if err != nil { + return nil, err + } + + // User and kernel times are represented as a FILETIME structure + // which contains a 64-bit value representing the number of + // 100-nanosecond intervals since January 1, 1601 (UTC): + // http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx + // To convert it into a float representing the seconds that the + // process has executed in user/kernel mode I borrowed the code + // below from psutil's _psutil_windows.c, and in turn from Python's + // Modules/posixmodule.c + + user := float64(sysTimes.UserTime.HighDateTime)*429.4967296 + float64(sysTimes.UserTime.LowDateTime)*1e-7 + kernel := float64(sysTimes.KernelTime.HighDateTime)*429.4967296 + float64(sysTimes.KernelTime.LowDateTime)*1e-7 + + return &cpu.TimesStat{ + User: user, + System: kernel, + }, nil +} +func (p *Process) CPUAffinity() ([]int32, error) { + return p.CPUAffinityWithContext(context.Background()) +} + +func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) { + return nil, common.ErrNotImplementedError +} +func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { + return p.MemoryInfoWithContext(context.Background()) +} + +func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { + mem, err := getMemoryInfo(p.Pid) + if err != nil { + return nil, err + } + + ret := &MemoryInfoStat{ + RSS: uint64(mem.WorkingSetSize), + VMS: uint64(mem.PagefileUsage), + } + + return ret, nil +} +func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { + return p.MemoryInfoExWithContext(context.Background()) +} + +func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) PageFaults() (*PageFaultsStat, error) { + return p.PageFaultsWithContext(context.Background()) +} + +func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) Children() ([]*Process, error) { + return p.ChildrenWithContext(context.Background()) +} + +func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { + out := []*Process{} + snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, uint32(0)) + if err != nil { + return out, err + } + defer windows.CloseHandle(snap) + var pe32 windows.ProcessEntry32 + pe32.Size = uint32(unsafe.Sizeof(pe32)) + if err := windows.Process32First(snap, &pe32); err != nil { + return out, err + } + for { + if pe32.ParentProcessID == uint32(p.Pid) { + p, err := NewProcess(int32(pe32.ProcessID)) + if err == nil { + out = append(out, p) + } + } + if err = windows.Process32Next(snap, &pe32); err != nil { + break + } + } + return out, nil +} + +func (p *Process) OpenFiles() ([]OpenFilesStat, error) { + return p.OpenFilesWithContext(context.Background()) +} + +func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) Connections() ([]net.ConnectionStat, error) { + return p.ConnectionsWithContext(context.Background()) +} + +func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) { + return net.ConnectionsPidWithContext(ctx, "all", p.Pid) +} + +func (p *Process) ConnectionsMax(max int) ([]net.ConnectionStat, error) { + return p.ConnectionsMaxWithContext(context.Background(), max) +} + +func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) { + return []net.ConnectionStat{}, common.ErrNotImplementedError +} + +func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { + return p.NetIOCountersWithContext(context.Background(), pernic) +} + +func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { + return p.MemoryMapsWithContext(context.Background(), grouped) +} + +func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) { + var ret []MemoryMapsStat + return &ret, common.ErrNotImplementedError +} + +func (p *Process) SendSignal(sig windows.Signal) error { + return p.SendSignalWithContext(context.Background(), sig) +} + +func (p *Process) SendSignalWithContext(ctx context.Context, sig windows.Signal) error { + return common.ErrNotImplementedError +} + +func (p *Process) Suspend() error { + return p.SuspendWithContext(context.Background()) +} + +func (p *Process) SuspendWithContext(ctx context.Context) error { + c, err := windows.OpenProcess(windows.PROCESS_SUSPEND_RESUME, false, uint32(p.Pid)) + if err != nil { + return err + } + defer windows.CloseHandle(c) + + r1, _, _ := procNtSuspendProcess.Call(uintptr(unsafe.Pointer(c))) + if r1 != 0 { + // See https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55 + return fmt.Errorf("NtStatus='0x%.8X'", r1) + } + + return nil +} + +func (p *Process) Resume() error { + return p.ResumeWithContext(context.Background()) +} + +func (p *Process) ResumeWithContext(ctx context.Context) error { + c, err := windows.OpenProcess(windows.PROCESS_SUSPEND_RESUME, false, uint32(p.Pid)) + if err != nil { + return err + } + defer windows.CloseHandle(c) + + r1, _, _ := procNtResumeProcess.Call(uintptr(unsafe.Pointer(c))) + if r1 != 0 { + // See https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55 + return fmt.Errorf("NtStatus='0x%.8X'", r1) + } + + return nil +} + +func (p *Process) Terminate() error { + return p.TerminateWithContext(context.Background()) +} + +func (p *Process) TerminateWithContext(ctx context.Context) error { + proc, err := windows.OpenProcess(windows.PROCESS_TERMINATE, false, uint32(p.Pid)) + if err != nil { + return err + } + err = windows.TerminateProcess(proc, 0) + windows.CloseHandle(proc) + return err +} + +func (p *Process) Kill() error { + return p.KillWithContext(context.Background()) +} + +func (p *Process) KillWithContext(ctx context.Context) error { + process := os.Process{Pid: int(p.Pid)} + return process.Kill() +} + +func getFromSnapProcess(pid int32) (int32, int32, string, error) { + snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, uint32(pid)) + if err != nil { + return 0, 0, "", err + } + defer windows.CloseHandle(snap) + var pe32 windows.ProcessEntry32 + pe32.Size = uint32(unsafe.Sizeof(pe32)) + if err = windows.Process32First(snap, &pe32); err != nil { + return 0, 0, "", err + } + for { + if pe32.ProcessID == uint32(pid) { + szexe := windows.UTF16ToString(pe32.ExeFile[:]) + return int32(pe32.ParentProcessID), int32(pe32.Threads), szexe, nil + } + if err = windows.Process32Next(snap, &pe32); err != nil { + break + } + } + return 0, 0, "", fmt.Errorf("couldn't find pid: %d", pid) +} + +// Get processes +func Processes() ([]*Process, error) { + return ProcessesWithContext(context.Background()) +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { + out := []*Process{} + + pids, err := PidsWithContext(ctx) + if err != nil { + return out, fmt.Errorf("could not get Processes %s", err) + } + + for _, pid := range pids { + p, err := NewProcess(pid) + if err != nil { + continue + } + out = append(out, p) + } + + return out, nil +} + +func getRusage(pid int32) (*windows.Rusage, error) { + var CPU windows.Rusage + + c, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid)) + if err != nil { + return nil, err + } + defer windows.CloseHandle(c) + + if err := windows.GetProcessTimes(c, &CPU.CreationTime, &CPU.ExitTime, &CPU.KernelTime, &CPU.UserTime); err != nil { + return nil, err + } + + return &CPU, nil +} + +func getMemoryInfo(pid int32) (PROCESS_MEMORY_COUNTERS, error) { + var mem PROCESS_MEMORY_COUNTERS + c, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid)) + if err != nil { + return mem, err + } + defer windows.CloseHandle(c) + if err := getProcessMemoryInfo(c, &mem); err != nil { + return mem, err + } + + return mem, err +} + +func getProcessMemoryInfo(h windows.Handle, mem *PROCESS_MEMORY_COUNTERS) (err error) { + r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(h), uintptr(unsafe.Pointer(mem)), uintptr(unsafe.Sizeof(*mem))) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +type SYSTEM_TIMES struct { + CreateTime syscall.Filetime + ExitTime syscall.Filetime + KernelTime syscall.Filetime + UserTime syscall.Filetime +} + +func getProcessCPUTimes(pid int32) (SYSTEM_TIMES, error) { + var times SYSTEM_TIMES + + h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid)) + if err != nil { + return times, err + } + defer windows.CloseHandle(h) + + err = syscall.GetProcessTimes( + syscall.Handle(h), + ×.CreateTime, + ×.ExitTime, + ×.KernelTime, + ×.UserTime, + ) + + return times, err +} + +func is32BitProcess(procHandle syscall.Handle) bool { + var wow64 uint + + ret, _, _ := common.ProcNtQueryInformationProcess.Call( + uintptr(procHandle), + uintptr(common.ProcessWow64Information), + uintptr(unsafe.Pointer(&wow64)), + uintptr(unsafe.Sizeof(wow64)), + uintptr(0), + ) + if int(ret) >= 0 { + if wow64 != 0 { + return true + } + } else { + //if the OS does not support the call, we fallback into the bitness of the app + if unsafe.Sizeof(wow64) == 4 { + return true + } + } + return false +} + +func getProcessCommandLine(pid int32) (string, error) { + h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION|windows.PROCESS_VM_READ, false, uint32(pid)) + if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER { + return "", nil + } + if err != nil { + return "", err + } + defer syscall.CloseHandle(syscall.Handle(h)) + + const ( + PROCESSOR_ARCHITECTURE_INTEL = 0 + PROCESSOR_ARCHITECTURE_ARM = 5 + PROCESSOR_ARCHITECTURE_ARM64 = 12 + PROCESSOR_ARCHITECTURE_IA64 = 6 + PROCESSOR_ARCHITECTURE_AMD64 = 9 + ) + + procIs32Bits := true + switch processorArchitecture { + case PROCESSOR_ARCHITECTURE_INTEL: + fallthrough + case PROCESSOR_ARCHITECTURE_ARM: + procIs32Bits = true + + case PROCESSOR_ARCHITECTURE_ARM64: + fallthrough + case PROCESSOR_ARCHITECTURE_IA64: + fallthrough + case PROCESSOR_ARCHITECTURE_AMD64: + procIs32Bits = is32BitProcess(syscall.Handle(h)) + + default: + //for other unknown platforms, we rely on process platform + if unsafe.Sizeof(processorArchitecture) == 8 { + procIs32Bits = false + } + } + + pebAddress := queryPebAddress(syscall.Handle(h), procIs32Bits) + if pebAddress == 0 { + return "", errors.New("cannot locate process PEB") + } + + if procIs32Bits { + buf := readProcessMemory(syscall.Handle(h), procIs32Bits, pebAddress+uint64(16), 4) + if len(buf) != 4 { + return "", errors.New("cannot locate process user parameters") + } + userProcParams := uint64(buf[0]) | (uint64(buf[1]) << 8) | (uint64(buf[2]) << 16) | (uint64(buf[3]) << 24) + + //read CommandLine field from PRTL_USER_PROCESS_PARAMETERS + remoteCmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams+uint64(64), 8) + if len(remoteCmdLine) != 8 { + return "", errors.New("cannot read cmdline field") + } + + //remoteCmdLine is actually a UNICODE_STRING32 + //the first two bytes has the length + cmdLineLength := uint(remoteCmdLine[0]) | (uint(remoteCmdLine[1]) << 8) + if cmdLineLength > 0 { + //and, at offset 4, is the pointer to the buffer + bufferAddress := uint32(remoteCmdLine[4]) | (uint32(remoteCmdLine[5]) << 8) | + (uint32(remoteCmdLine[6]) << 16) | (uint32(remoteCmdLine[7]) << 24) + + cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, uint64(bufferAddress), cmdLineLength) + if len(cmdLine) != int(cmdLineLength) { + return "", errors.New("cannot read cmdline") + } + + return convertUTF16ToString(cmdLine), nil + } + } else { + buf := readProcessMemory(syscall.Handle(h), procIs32Bits, pebAddress+uint64(32), 8) + if len(buf) != 8 { + return "", errors.New("cannot locate process user parameters") + } + userProcParams := uint64(buf[0]) | (uint64(buf[1]) << 8) | (uint64(buf[2]) << 16) | (uint64(buf[3]) << 24) | + (uint64(buf[4]) << 32) | (uint64(buf[5]) << 40) | (uint64(buf[6]) << 48) | (uint64(buf[7]) << 56) + + //read CommandLine field from PRTL_USER_PROCESS_PARAMETERS + remoteCmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams+uint64(112), 16) + if len(remoteCmdLine) != 16 { + return "", errors.New("cannot read cmdline field") + } + + //remoteCmdLine is actually a UNICODE_STRING64 + //the first two bytes has the length + cmdLineLength := uint(remoteCmdLine[0]) | (uint(remoteCmdLine[1]) << 8) + if cmdLineLength > 0 { + //and, at offset 8, is the pointer to the buffer + bufferAddress := uint64(remoteCmdLine[8]) | (uint64(remoteCmdLine[9]) << 8) | + (uint64(remoteCmdLine[10]) << 16) | (uint64(remoteCmdLine[11]) << 24) | + (uint64(remoteCmdLine[12]) << 32) | (uint64(remoteCmdLine[13]) << 40) | + (uint64(remoteCmdLine[14]) << 48) | (uint64(remoteCmdLine[15]) << 56) + + cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, bufferAddress, cmdLineLength) + if len(cmdLine) != int(cmdLineLength) { + return "", errors.New("cannot read cmdline") + } + + return convertUTF16ToString(cmdLine), nil + } + } + + //if we reach here, we have no command line + return "", nil +} + +func convertUTF16ToString(src []byte) string { + srcLen := len(src) / 2 + + codePoints := make([]uint16, srcLen) + + srcIdx := 0 + for i := 0; i < srcLen; i++ { + codePoints[i] = uint16(src[srcIdx]) | uint16(src[srcIdx+1]<<8) + srcIdx += 2 + } + return syscall.UTF16ToString(codePoints) +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_windows_386.go b/vendor/github.com/shirou/gopsutil/process/process_windows_386.go new file mode 100644 index 00000000000..cd884968efd --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_windows_386.go @@ -0,0 +1,102 @@ +// +build windows + +package process + +import ( + "syscall" + "unsafe" + + "github.com/shirou/gopsutil/internal/common" +) + +type PROCESS_MEMORY_COUNTERS struct { + CB uint32 + PageFaultCount uint32 + PeakWorkingSetSize uint32 + WorkingSetSize uint32 + QuotaPeakPagedPoolUsage uint32 + QuotaPagedPoolUsage uint32 + QuotaPeakNonPagedPoolUsage uint32 + QuotaNonPagedPoolUsage uint32 + PagefileUsage uint32 + PeakPagefileUsage uint32 +} + +func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) uint64 { + if is32BitProcess { + //we are on a 32-bit process reading an external 32-bit process + var info processBasicInformation32 + + ret, _, _ := common.ProcNtQueryInformationProcess.Call( + uintptr(procHandle), + uintptr(common.ProcessBasicInformation), + uintptr(unsafe.Pointer(&info)), + uintptr(unsafe.Sizeof(info)), + uintptr(0), + ) + if int(ret) >= 0 { + return uint64(info.PebBaseAddress) + } + } else { + //we are on a 32-bit process reading an external 64-bit process + if common.ProcNtWow64QueryInformationProcess64.Find() == nil { //avoid panic + var info processBasicInformation64 + + ret, _, _ := common.ProcNtWow64QueryInformationProcess64.Call( + uintptr(procHandle), + uintptr(common.ProcessBasicInformation), + uintptr(unsafe.Pointer(&info)), + uintptr(unsafe.Sizeof(info)), + uintptr(0), + ) + if int(ret) >= 0 { + return info.PebBaseAddress + } + } + } + + //return 0 on error + return 0 +} + +func readProcessMemory(h syscall.Handle, is32BitProcess bool, address uint64, size uint) []byte { + if is32BitProcess { + var read uint + + buffer := make([]byte, size) + + ret, _, _ := common.ProcNtReadVirtualMemory.Call( + uintptr(h), + uintptr(address), + uintptr(unsafe.Pointer(&buffer[0])), + uintptr(size), + uintptr(unsafe.Pointer(&read)), + ) + if int(ret) >= 0 && read > 0 { + return buffer[:read] + } + } else { + //reading a 64-bit process from a 32-bit one + if common.ProcNtWow64ReadVirtualMemory64.Find() == nil { //avoid panic + var read uint64 + + buffer := make([]byte, size) + + ret, _, _ := common.ProcNtWow64ReadVirtualMemory64.Call( + uintptr(h), + uintptr(address & 0xFFFFFFFF), //the call expects a 64-bit value + uintptr(address >> 32), + uintptr(unsafe.Pointer(&buffer[0])), + uintptr(size), //the call expects a 64-bit value + uintptr(0), //but size is 32-bit so pass zero as the high dword + uintptr(unsafe.Pointer(&read)), + ) + if int(ret) >= 0 && read > 0 { + return buffer[:uint(read)] + } + } + } + + //if we reach here, an error happened + return nil +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_windows_amd64.go b/vendor/github.com/shirou/gopsutil/process/process_windows_amd64.go new file mode 100644 index 00000000000..3ee5be449ce --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/process_windows_amd64.go @@ -0,0 +1,76 @@ +// +build windows + +package process + +import ( + "syscall" + "unsafe" + + "github.com/shirou/gopsutil/internal/common" +) + +type PROCESS_MEMORY_COUNTERS struct { + CB uint32 + PageFaultCount uint32 + PeakWorkingSetSize uint64 + WorkingSetSize uint64 + QuotaPeakPagedPoolUsage uint64 + QuotaPagedPoolUsage uint64 + QuotaPeakNonPagedPoolUsage uint64 + QuotaNonPagedPoolUsage uint64 + PagefileUsage uint64 + PeakPagefileUsage uint64 +} + +func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) uint64 { + if is32BitProcess { + //we are on a 64-bit process reading an external 32-bit process + var wow64 uint + + ret, _, _ := common.ProcNtQueryInformationProcess.Call( + uintptr(procHandle), + uintptr(common.ProcessWow64Information), + uintptr(unsafe.Pointer(&wow64)), + uintptr(unsafe.Sizeof(wow64)), + uintptr(0), + ) + if int(ret) >= 0 { + return uint64(wow64) + } + } else { + //we are on a 64-bit process reading an external 64-bit process + var info processBasicInformation64 + + ret, _, _ := common.ProcNtQueryInformationProcess.Call( + uintptr(procHandle), + uintptr(common.ProcessBasicInformation), + uintptr(unsafe.Pointer(&info)), + uintptr(unsafe.Sizeof(info)), + uintptr(0), + ) + if int(ret) >= 0 { + return info.PebBaseAddress + } + } + + //return 0 on error + return 0 +} + +func readProcessMemory(procHandle syscall.Handle, _ bool, address uint64, size uint) []byte { + var read uint + + buffer := make([]byte, size) + + ret, _, _ := common.ProcNtReadVirtualMemory.Call( + uintptr(procHandle), + uintptr(address), + uintptr(unsafe.Pointer(&buffer[0])), + uintptr(size), + uintptr(unsafe.Pointer(&read)), + ) + if int(ret) >= 0 && read > 0 { + return buffer[:read] + } + return nil +} diff --git a/vendor/github.com/shirou/gopsutil/process/types_darwin.go b/vendor/github.com/shirou/gopsutil/process/types_darwin.go new file mode 100644 index 00000000000..21216cd09a7 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/types_darwin.go @@ -0,0 +1,160 @@ +// Copyright 2009 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. + +// Hand Writing +// - all pointer in ExternProc to uint64 + +// +build ignore + +/* +Input to cgo -godefs. +*/ + +// +godefs map struct_in_addr [4]byte /* in_addr */ +// +godefs map struct_in6_addr [16]byte /* in6_addr */ +// +godefs map struct_ [16]byte /* in6_addr */ + +package process + +/* +#define __DARWIN_UNIX03 0 +#define KERNEL +#define _DARWIN_USE_64_BIT_INODE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + sizeofPtr = sizeof(void*), +}; + +union sockaddr_all { + struct sockaddr s1; // this one gets used for fields + struct sockaddr_in s2; // these pad it out + struct sockaddr_in6 s3; + struct sockaddr_un s4; + struct sockaddr_dl s5; +}; + +struct sockaddr_any { + struct sockaddr addr; + char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)]; +}; + +struct ucred_queue { + struct ucred *tqe_next; + struct ucred **tqe_prev; + TRACEBUF +}; + +*/ +import "C" + +// Machine characteristics; for internal use. + +const ( + sizeofPtr = C.sizeofPtr + sizeofShort = C.sizeof_short + sizeofInt = C.sizeof_int + sizeofLong = C.sizeof_long + sizeofLongLong = C.sizeof_longlong +) + +// Basic types + +type ( + _C_short C.short + _C_int C.int + _C_long C.long + _C_long_long C.longlong +) + +// Time + +type Timespec C.struct_timespec + +type Timeval C.struct_timeval + +// Processes + +type Rusage C.struct_rusage + +type Rlimit C.struct_rlimit + +type UGid_t C.gid_t + +type KinfoProc C.struct_kinfo_proc + +type Eproc C.struct_eproc + +type Proc C.struct_proc + +type Session C.struct_session + +type ucred C.struct_ucred + +type Uucred C.struct__ucred + +type Upcred C.struct__pcred + +type Vmspace C.struct_vmspace + +type Sigacts C.struct_sigacts + +type ExternProc C.struct_extern_proc + +type Itimerval C.struct_itimerval + +type Vnode C.struct_vnode + +type Pgrp C.struct_pgrp + +type UserStruct C.struct_user + +type Au_session C.struct_au_session + +type Posix_cred C.struct_posix_cred + +type Label C.struct_label + +type AuditinfoAddr C.struct_auditinfo_addr +type AuMask C.struct_au_mask +type AuTidAddr C.struct_au_tid_addr + +// TAILQ(ucred) +type UcredQueue C.struct_ucred_queue diff --git a/vendor/github.com/shirou/gopsutil/process/types_freebsd.go b/vendor/github.com/shirou/gopsutil/process/types_freebsd.go new file mode 100644 index 00000000000..aa7b3462de6 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/types_freebsd.go @@ -0,0 +1,95 @@ +// +build ignore + +// We still need editing by hands. +// go tool cgo -godefs types_freebsd.go | sed 's/\*int64/int64/' | sed 's/\*byte/int64/' > process_freebsd_amd64.go + +/* +Input to cgo -godefs. +*/ + +// +godefs map struct_pargs int64 /* pargs */ +// +godefs map struct_proc int64 /* proc */ +// +godefs map struct_user int64 /* user */ +// +godefs map struct_vnode int64 /* vnode */ +// +godefs map struct_vnode int64 /* vnode */ +// +godefs map struct_filedesc int64 /* filedesc */ +// +godefs map struct_vmspace int64 /* vmspace */ +// +godefs map struct_pcb int64 /* pcb */ +// +godefs map struct_thread int64 /* thread */ +// +godefs map struct___sigset [16]byte /* sigset */ + +package process + +/* +#include +#include + +enum { + sizeofPtr = sizeof(void*), +}; + + +*/ +import "C" + +// Machine characteristics; for internal use. + +const ( + CTLKern = 1 // "high kernel": proc, limits + KernProc = 14 // struct: process entries + KernProcPID = 1 // by process id + KernProcProc = 8 // only return procs + KernProcPathname = 12 // path to executable + KernProcArgs = 7 // get/set arguments/proctitle +) + +const ( + sizeofPtr = C.sizeofPtr + sizeofShort = C.sizeof_short + sizeofInt = C.sizeof_int + sizeofLong = C.sizeof_long + sizeofLongLong = C.sizeof_longlong +) + +const ( + sizeOfKinfoVmentry = C.sizeof_struct_kinfo_vmentry + sizeOfKinfoProc = C.sizeof_struct_kinfo_proc +) + +// from sys/proc.h +const ( + SIDL = 1 /* Process being created by fork. */ + SRUN = 2 /* Currently runnable. */ + SSLEEP = 3 /* Sleeping on an address. */ + SSTOP = 4 /* Process debugging or suspension. */ + SZOMB = 5 /* Awaiting collection by parent. */ + SWAIT = 6 /* Waiting for interrupt. */ + SLOCK = 7 /* Blocked on a lock. */ +) + +// Basic types + +type ( + _C_short C.short + _C_int C.int + _C_long C.long + _C_long_long C.longlong +) + +// Time + +type Timespec C.struct_timespec + +type Timeval C.struct_timeval + +// Processes + +type Rusage C.struct_rusage + +type Rlimit C.struct_rlimit + +type KinfoProc C.struct_kinfo_proc + +type Priority C.struct_priority + +type KinfoVmentry C.struct_kinfo_vmentry diff --git a/vendor/github.com/shirou/gopsutil/process/types_openbsd.go b/vendor/github.com/shirou/gopsutil/process/types_openbsd.go new file mode 100644 index 00000000000..09ac590288a --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/types_openbsd.go @@ -0,0 +1,103 @@ +// +build ignore + +// We still need editing by hands. +// go tool cgo -godefs types_openbsd.go | sed 's/\*int64/int64/' | sed 's/\*byte/int64/' > process_openbsd_amd64.go + +/* +Input to cgo -godefs. +*/ + +// +godefs map struct_pargs int64 /* pargs */ +// +godefs map struct_proc int64 /* proc */ +// +godefs map struct_user int64 /* user */ +// +godefs map struct_vnode int64 /* vnode */ +// +godefs map struct_vnode int64 /* vnode */ +// +godefs map struct_filedesc int64 /* filedesc */ +// +godefs map struct_vmspace int64 /* vmspace */ +// +godefs map struct_pcb int64 /* pcb */ +// +godefs map struct_thread int64 /* thread */ +// +godefs map struct___sigset [16]byte /* sigset */ + +package process + +/* +#include +#include +#include + +enum { + sizeofPtr = sizeof(void*), +}; + + +*/ +import "C" + +// Machine characteristics; for internal use. + +const ( + CTLKern = 1 // "high kernel": proc, limits + KernProc = 66 // struct: process entries + KernProcAll = 0 + KernProcPID = 1 // by process id + KernProcProc = 8 // only return procs + KernProcPathname = 12 // path to executable + KernProcArgs = 55 // get/set arguments/proctitle + KernProcArgv = 1 + KernProcEnv = 3 +) + +const ( + ArgMax = 256 * 1024 // sys/syslimits.h:#define ARG_MAX +) + +const ( + sizeofPtr = C.sizeofPtr + sizeofShort = C.sizeof_short + sizeofInt = C.sizeof_int + sizeofLong = C.sizeof_long + sizeofLongLong = C.sizeof_longlong +) + +const ( + sizeOfKinfoVmentry = C.sizeof_struct_kinfo_vmentry + sizeOfKinfoProc = C.sizeof_struct_kinfo_proc +) + +// from sys/proc.h +const ( + SIDL = 1 /* Process being created by fork. */ + SRUN = 2 /* Currently runnable. */ + SSLEEP = 3 /* Sleeping on an address. */ + SSTOP = 4 /* Process debugging or suspension. */ + SZOMB = 5 /* Awaiting collection by parent. */ + SDEAD = 6 /* Thread is almost gone */ + SONPROC = 7 /* Thread is currently on a CPU. */ +) + +// Basic types + +type ( + _C_short C.short + _C_int C.int + _C_long C.long + _C_long_long C.longlong +) + +// Time + +type Timespec C.struct_timespec + +type Timeval C.struct_timeval + +// Processes + +type Rusage C.struct_rusage + +type Rlimit C.struct_rlimit + +type KinfoProc C.struct_kinfo_proc + +type Priority C.struct_priority + +type KinfoVmentry C.struct_kinfo_vmentry