Compare commits
22 Commits
74eaf3bd75
...
go
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81288389c6 | ||
|
|
5976d0b327 | ||
|
|
62bbf9d277 | ||
|
|
83970650b5 | ||
|
|
0b573aa169 | ||
|
|
7474028b82 | ||
|
|
120b7c995a | ||
|
|
301c22be02 | ||
|
|
1d76fd9920 | ||
|
|
d6f56dc569 | ||
|
|
863ca34383 | ||
|
|
d962e4fd46 | ||
|
|
3e532f46aa | ||
|
|
887643f62d | ||
|
|
b5bacc5afe | ||
|
|
b2788b9ef1 | ||
|
|
e569c6bfce | ||
|
|
227b9006dd | ||
|
|
cf9c8f0b29 | ||
|
|
d6f9966999 | ||
|
|
dc89d197d9 | ||
|
|
4d04731aeb |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/reniced
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
run:
|
run:
|
||||||
tests: false
|
tests: false
|
||||||
skip-dirs:
|
|
||||||
- vendor
|
|
||||||
modules-download-mode: readonly
|
modules-download-mode: readonly
|
||||||
allow-parallel-runners: true
|
allow-parallel-runners: true
|
||||||
go: '1.21'
|
go: '1.22'
|
||||||
output:
|
output:
|
||||||
sort-results: true
|
sort-results: true
|
||||||
linters:
|
linters:
|
||||||
@@ -26,15 +24,13 @@ linters:
|
|||||||
- errchkjson
|
- errchkjson
|
||||||
- errorlint
|
- errorlint
|
||||||
- exhaustive
|
- exhaustive
|
||||||
- exhaustruct
|
- copyloopvar
|
||||||
- exportloopref
|
|
||||||
- ginkgolinter
|
- ginkgolinter
|
||||||
- gci
|
- gci
|
||||||
- gocheckcompilerdirectives
|
- gocheckcompilerdirectives
|
||||||
- gocritic
|
- gocritic
|
||||||
- godot
|
- godot
|
||||||
- gofmt
|
- gofmt
|
||||||
- goimports
|
|
||||||
- gosec
|
- gosec
|
||||||
- gosmopolitan
|
- gosmopolitan
|
||||||
- grouper
|
- grouper
|
||||||
@@ -59,5 +55,11 @@ linters:
|
|||||||
- wastedassign
|
- wastedassign
|
||||||
- whitespace
|
- whitespace
|
||||||
- wsl
|
- wsl
|
||||||
|
linters-settings:
|
||||||
|
govet:
|
||||||
|
enable-all: true
|
||||||
|
disable:
|
||||||
|
- nilness
|
||||||
|
- fieldalignment
|
||||||
|
|
||||||
# vim: set ft=yaml noet ai ts=4 sw=4 sts=4:
|
# vim: set ft=yaml noet ai ts=4 sw=4 sts=4:
|
||||||
|
|||||||
@@ -1,57 +1,112 @@
|
|||||||
cmd_delay: 33
|
# Fork and run as real unix daemon.
|
||||||
loop_delay: 5000
|
daemon: true
|
||||||
|
|
||||||
|
# PID file for daemon.
|
||||||
|
pidfile: /var/run/reniced.pid
|
||||||
|
|
||||||
|
# Delay between process list update task consecutive runs.
|
||||||
|
process_list_update_period: 5000
|
||||||
|
|
||||||
|
# Delay between kill consecutive tasks.
|
||||||
|
process_kill_period: 5000
|
||||||
|
|
||||||
|
# Delay between changing niceness consecutive tasks.
|
||||||
|
process_nice_period: 5000
|
||||||
|
|
||||||
|
# Delay between changing io niceness consecutive tasks (linux-only).
|
||||||
|
process_ionice_period: 5000
|
||||||
|
|
||||||
|
# Period of time between consecutive periodic_process list tasks hammering.
|
||||||
|
periodic_process_invocation_period: 60000
|
||||||
|
|
||||||
|
# Period of time between process exec() invocations.
|
||||||
|
periodic_process_invocation_delay: 500
|
||||||
|
|
||||||
|
# Whether to print debug messages to stdout.
|
||||||
debug: true
|
debug: true
|
||||||
|
|
||||||
|
# Maximum worker in command pool.
|
||||||
|
max_workers: 3
|
||||||
|
|
||||||
|
# Capacity of non-blocking tasks. All tasks that exceeding this capacity will block submission of new tasks until queue
|
||||||
|
# has vacant place for new task.
|
||||||
|
nb_capacity: 3000
|
||||||
|
|
||||||
|
# Time to time run these commands.
|
||||||
|
periodic_process:
|
||||||
|
- ps xa
|
||||||
|
|
||||||
kill:
|
kill:
|
||||||
- sig: stop
|
- sig: stop
|
||||||
name:
|
name:
|
||||||
|
# Backup program. I don't really want it, but have to keep.
|
||||||
- commvault
|
- commvault
|
||||||
|
# Distributed notification service. Sometimes can be naughty.
|
||||||
- distnoted
|
- distnoted
|
||||||
|
# This service can be greedy and may make kernel_tak to hig cpu too
|
||||||
- coreduetd
|
- coreduetd
|
||||||
|
|
||||||
prio:
|
prio:
|
||||||
- nice: -11
|
- nice: -11
|
||||||
name:
|
name:
|
||||||
|
# Main audio service, to play sounds smoothly, must have high prio.
|
||||||
- coreaudiod
|
- coreaudiod
|
||||||
|
# Window manager service.
|
||||||
- WindowServer
|
- WindowServer
|
||||||
|
# Isolation helper.
|
||||||
- "com.apple.audio.SandboxHelper"
|
- "com.apple.audio.SandboxHelper"
|
||||||
|
# Audio driver helper.
|
||||||
- "com.apple.audio.DriverHelper"
|
- "com.apple.audio.DriverHelper"
|
||||||
|
# Used for syncing timers for different audio sources.
|
||||||
- audioclocksyncd
|
- audioclocksyncd
|
||||||
|
|
||||||
- nice: -5
|
- nice: -5
|
||||||
name:
|
name:
|
||||||
|
# Video playback helper.
|
||||||
- VTDecoderXPCService
|
- VTDecoderXPCService
|
||||||
|
# Corporate messenger.
|
||||||
- "VK Teams"
|
- "VK Teams"
|
||||||
|
# Responds to apple broadcast dns requests. In linux world it is avahi.
|
||||||
- mDNSResponder
|
- mDNSResponder
|
||||||
|
# Helper service for mDNSResponder.
|
||||||
- mDNSResponderHelper
|
- mDNSResponderHelper
|
||||||
|
# Part of AudioToolbox framework.
|
||||||
- AudioComponentRegistrar
|
- AudioComponentRegistrar
|
||||||
|
# Mac OS X Core Virtual Machine Server. Something related to isolation of system processes.
|
||||||
- CVMServer
|
- CVMServer
|
||||||
|
|
||||||
- nice: -3
|
- nice: -3
|
||||||
name:
|
name:
|
||||||
|
# 3-rd party tool to make tricks with keyboard. (remap/swap buttons)
|
||||||
- "Karabiner-Elements"
|
- "Karabiner-Elements"
|
||||||
|
# 3-rd party tool to automate routine actions.
|
||||||
- Hammerspoon
|
- Hammerspoon
|
||||||
|
# Compiler for metal shaders, that can pass information directly to the GPU.
|
||||||
- MTLCompilerService
|
- MTLCompilerService
|
||||||
- "com.apple.appkit.xpc.openAndSavePanelService"
|
|
||||||
|
|
||||||
- nice: 3
|
- nice: 3
|
||||||
name:
|
name:
|
||||||
|
# System logger.
|
||||||
- logd
|
- logd
|
||||||
|
# System logger.
|
||||||
- syslogd
|
- syslogd
|
||||||
|
# Something related to binary signature checks. To ensure system integrity. Part of DRM protection.
|
||||||
|
# It rubs berfore application starts, so we definitely don't want to low its prio heavily.
|
||||||
- syspolicyd
|
- syspolicyd
|
||||||
|
# Unix periodic task scheduler.
|
||||||
- cron
|
- cron
|
||||||
|
# Ditto.
|
||||||
- softwareupdated
|
- softwareupdated
|
||||||
|
# Handles the scheduling of the periodic command
|
||||||
- "periodic-wrapper"
|
- "periodic-wrapper"
|
||||||
- iconservicesagent
|
|
||||||
- "com.apple.quicklook.ThumbnailsAgent"
|
|
||||||
- appstored
|
- appstored
|
||||||
|
|
||||||
- nice: 5
|
- nice: 5
|
||||||
name:
|
name:
|
||||||
|
# Dynamic brightness tuning service.
|
||||||
- corebrightnessd
|
- corebrightnessd
|
||||||
- airportd
|
- airportd
|
||||||
- containermanagerd
|
- containermanagerd
|
||||||
- searchpartyd
|
|
||||||
- corespeechd
|
- corespeechd
|
||||||
- peopled
|
- peopled
|
||||||
- AssetCacheLocatorService
|
- AssetCacheLocatorService
|
||||||
@@ -121,7 +176,13 @@ prio:
|
|||||||
- biomesyncd
|
- biomesyncd
|
||||||
- siriknowledged
|
- siriknowledged
|
||||||
- triald
|
- triald
|
||||||
- syspolicyd
|
|
||||||
|
|
||||||
|
ioprio:
|
||||||
|
- name:
|
||||||
|
- htop
|
||||||
|
- top
|
||||||
|
- atop
|
||||||
|
class: 2
|
||||||
|
prio: 1
|
||||||
|
|
||||||
# vim: set ft=yaml et ai ts=2 sw=2 sts=2:
|
# vim: set ft=yaml et ai ts=2 sw=2 sts=2:
|
||||||
|
|||||||
9
Makefile
9
Makefile
@@ -1,17 +1,14 @@
|
|||||||
#!/usr/bin/env gmake -f
|
#!/usr/bin/env gmake -f
|
||||||
|
|
||||||
BUILDOPTS=-ldflags="-s -w" -a -gcflags=all=-l -trimpath
|
|
||||||
FILELIST=main.go types.go util.go
|
|
||||||
|
|
||||||
BINARY=reniced
|
BINARY=reniced
|
||||||
|
BUILDOPTS=-ldflags="-s -w" -a -gcflags=all="-l -B" -trimpath -buildvcs=false
|
||||||
|
|
||||||
all: clean build
|
all: clean build
|
||||||
|
|
||||||
build:
|
build:
|
||||||
go build ${BUILDOPTS} -o ${BINARY} ${FILELIST}
|
go build ${BUILDOPTS} -o ${BINARY} ./cmd/${BINARY}
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
go clean
|
$(RM) ${BINARY}
|
||||||
|
|
||||||
upgrade:
|
upgrade:
|
||||||
$(RM) -r vendor
|
$(RM) -r vendor
|
||||||
|
|||||||
67
README.md
67
README.md
@@ -1,16 +1,16 @@
|
|||||||
# Reniced
|
# Reniced
|
||||||
|
|
||||||
Helper utility intended to run as a service or as background process (say in tmux) that can make system more respositive
|
Helper utility intended to run as a service or as background process (say, in tmux) that can make system more
|
||||||
under certain load.
|
respositive under certain type of load.
|
||||||
|
|
||||||
## Why it was made
|
## Why it was made
|
||||||
|
|
||||||
In general case os can handle load well fairly share resources among processes. But in some cases it make it poorly.
|
In general case os can handle load well, fairly sharing resources among processes. But in some cases it makes it poorly.
|
||||||
As an exanple it can be ill-minded pseudo-security stuff like antiviruses or mediaanalysers that searches forbidden
|
As an example it can be ill-minded pseudo-security stuff like antiviruses or mediaanalysers that searches forbidden
|
||||||
content on your mac (due to gov.polices that apple have to accept). Or it can be backup that crawls in background.
|
content on your mac (due to gov.policies that apple have to accept). Or it can be backup that crawls fs in background.
|
||||||
|
|
||||||
To help resolve such situation this utility was made. Some malicious processes can be renice to lower nicelevels (say,
|
To help resolve such situation this utility was made. Some malicious processes can be reniced to lower nicelevels (say,
|
||||||
19) and for some good processes nicelevel can be rised to higher values(-19 if reniced run as superuser or 0 if as
|
19) and for some good processes nicelevel can be raisen to higher values (-19 if reniced run as superuser or 0 if as
|
||||||
normal).
|
normal).
|
||||||
|
|
||||||
In some critical cases process can be killed (if you want to know more about it see "man kill" in terminal).
|
In some critical cases process can be killed (if you want to know more about it see "man kill" in terminal).
|
||||||
@@ -19,7 +19,7 @@ In some critical cases process can be killed (if you want to know more about it
|
|||||||
|
|
||||||
You will need Go lang 1.21 or newer, gnu make, terminal and possibly internets to build this utility.
|
You will need Go lang 1.21 or newer, gnu make, terminal and possibly internets to build this utility.
|
||||||
|
|
||||||
To build reniced just type in src dir
|
To build reniced just type in project dir
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make
|
make
|
||||||
@@ -40,7 +40,56 @@ if you have no access to superuser or
|
|||||||
sudo ./reniced
|
sudo ./reniced
|
||||||
```
|
```
|
||||||
|
|
||||||
if you can sudo. Thats it.
|
if you can sudo. That's it.
|
||||||
|
|
||||||
|
Note: negative niceness values can be set only by root.
|
||||||
|
|
||||||
|
Note2: MacOS has too tight (or intrusive) security measures, so running something as system daemon is very difficult
|
||||||
|
task. Reniced can be run via sudo as daemon or backbround process (say, in tmux). On linux systems usually there is no
|
||||||
|
such obstacle and if reniced run from user with id=0, its config should be placed at **/etc/reniced.yaml**
|
||||||
|
|
||||||
|
## Install as systemd service
|
||||||
|
|
||||||
|
Supplied systemd unit is ready to install in system as-is and should work ok. It expect to see reniced at
|
||||||
|
**/usr/sbin/reniced** and assume that reniced runs as foreground task - in config file "daemon" setting either absent or
|
||||||
|
set to false.
|
||||||
|
|
||||||
|
## Install as System V Init service
|
||||||
|
|
||||||
|
Unlike supplpied systemd unit, supplied System V Init script assumes reniced runs as daemon (in config file "daemon"
|
||||||
|
setting must be set to true). Script also expects to find pid-file at **/var/run/reniced.pid** and executable itself at
|
||||||
|
**/usr/sbin/reniced**.
|
||||||
|
|
||||||
|
## Note on ionice operation
|
||||||
|
|
||||||
|
Currently implemented only linux io prio. On all other reniced-supported systems it is noop. Please note that io
|
||||||
|
priority can be set only by digits. Refer to *man ionice* for specific io classes and priorities inplemented there.
|
||||||
|
|
||||||
|
Here is table with numbers for defined class and priorities.
|
||||||
|
|
||||||
|
| class name | class number | list of possible priorities |
|
||||||
|
|:-----------|-------------:|----------------------------:|
|
||||||
|
| NONE |0 |0 |
|
||||||
|
| RealTime |1 |0, 1, 2, 3, 4, 5, 6, 7 |
|
||||||
|
| BestEffort |2 |0, 1, 2, 3, 4, 5, 6, 7 |
|
||||||
|
| Idle |3 |0 |
|
||||||
|
|
||||||
|
None is default io prio if process is not ioreniced. And it is the same value and behaviour as BestEffort with priority
|
||||||
|
of 4.
|
||||||
|
|
||||||
|
The lower priority value means higher priority and process will eat more io.
|
||||||
|
|
||||||
|
RealTime prio is not available for regular users but for root only.
|
||||||
|
|
||||||
|
## Note #2 on ionice operation on Mac OS / Darwin
|
||||||
|
|
||||||
|
The only working official method for Apple OS is via taskpolicy utility that comes with Mac OS itself. (Unlimited kernel
|
||||||
|
API calls requires that calling binary being signed by Apple Inc itself) In context of this program such method is
|
||||||
|
inefficient and beacuse of that mostly likely io task policy for Mac OS will not be implemented.
|
||||||
|
|
||||||
|
## *BSD porting help needed :)
|
||||||
|
|
||||||
|
Indeed. If you have some time and will to add this functionality PRs are welcome!
|
||||||
|
|
||||||
## Special "thanks"
|
## Special "thanks"
|
||||||
|
|
||||||
|
|||||||
92
cmd/reniced/main.go
Normal file
92
cmd/reniced/main.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
lib "reniced/internal/lib"
|
||||||
|
|
||||||
|
"github.com/alitto/pond"
|
||||||
|
"github.com/sevlyar/go-daemon"
|
||||||
|
)
|
||||||
|
|
||||||
|
// main основная функция программы.
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
cnf = lib.Config{
|
||||||
|
SigChan: make(chan os.Signal, 1),
|
||||||
|
KillSignal: make(map[string]syscall.Signal),
|
||||||
|
Renice: make(map[string]int),
|
||||||
|
IoreniceClass: make(map[string]uint32),
|
||||||
|
IoreniceClassdata: make(map[string]uint32),
|
||||||
|
NotImplementedError: fmt.Errorf("SYSCALL Not Implemented on current platform"),
|
||||||
|
ClassToString: map[uint32]string{
|
||||||
|
lib.IOPRIO_CLASS_NONE: "none",
|
||||||
|
lib.IOPRIO_CLASS_RT: "realtime",
|
||||||
|
lib.IOPRIO_CLASS_BE: "best-effort",
|
||||||
|
lib.IOPRIO_CLASS_IDLE: "idle",
|
||||||
|
},
|
||||||
|
StringToClass: map[string]uint32{
|
||||||
|
"none": lib.IOPRIO_CLASS_NONE,
|
||||||
|
"0": lib.IOPRIO_CLASS_NONE,
|
||||||
|
"realtime": lib.IOPRIO_CLASS_RT,
|
||||||
|
"1": lib.IOPRIO_CLASS_RT,
|
||||||
|
"best-effort": lib.IOPRIO_CLASS_BE,
|
||||||
|
"2": lib.IOPRIO_CLASS_BE,
|
||||||
|
"idle": lib.IOPRIO_CLASS_IDLE,
|
||||||
|
"3": lib.IOPRIO_CLASS_IDLE,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Самое время поставить траппер сигналов.
|
||||||
|
signal.Notify(cnf.SigChan,
|
||||||
|
syscall.SIGINT,
|
||||||
|
syscall.SIGTERM,
|
||||||
|
syscall.SIGQUIT)
|
||||||
|
|
||||||
|
go cnf.SigHandler()
|
||||||
|
|
||||||
|
err = cnf.ReadConf()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("unable to parse config: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаём пул со статическим количеством воркеров и длиной очереди неблокируемых задач, равной NbCapacity.
|
||||||
|
cnf.Pool = pond.New(
|
||||||
|
cnf.MaxWorkers,
|
||||||
|
cnf.NbCapacity,
|
||||||
|
pond.Strategy(pond.Lazy()),
|
||||||
|
)
|
||||||
|
|
||||||
|
defer cnf.Pool.StopAndWait()
|
||||||
|
|
||||||
|
if cnf.Daemon {
|
||||||
|
cntxt := &daemon.Context{
|
||||||
|
WorkDir: "/",
|
||||||
|
Args: []string{"reniced"},
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.Pidfile != "" {
|
||||||
|
cntxt.PidFileName = cnf.Pidfile
|
||||||
|
}
|
||||||
|
|
||||||
|
if d, err := cntxt.Reborn(); err != nil {
|
||||||
|
log.Fatalf("Unable to run: %s", err) //nolint: gocritic
|
||||||
|
} else if d != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer cntxt.Release() //nolint: errcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
// То, ради чего всё затевалось.
|
||||||
|
cnf.Reniced()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
64
contrib/reniced.init
Normal file
64
contrib/reniced.init
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Start/stop/restart reniced.
|
||||||
|
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: reniced
|
||||||
|
# Default-Start: 2 3 4 5
|
||||||
|
# Default-Stop: 0 1 6
|
||||||
|
# Short-Description: Start the reniced service.
|
||||||
|
# Description: Service that periodically checks and changes runtime niceness of given (by name) processes.
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
|
||||||
|
# Start reniced:
|
||||||
|
_start() {
|
||||||
|
echo -n "Starting Reniced daemon: /usr/sbin/reniced"
|
||||||
|
/usr/sbin/reniced
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
# Stop reniced:
|
||||||
|
_stop() {
|
||||||
|
echo -n "Stopping Reniced daemon..."
|
||||||
|
if [ -r /var/run/reniced.pid ]; then
|
||||||
|
kill -HUP "$(cat /var/run/reniced.pid)"
|
||||||
|
rm -f "/var/run/reniced.pid"
|
||||||
|
else
|
||||||
|
killall -HUP -q reniced
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
# Restart renicedd:
|
||||||
|
_restart() {
|
||||||
|
_stop
|
||||||
|
sleep 1
|
||||||
|
_start
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if reniced is running
|
||||||
|
_status() {
|
||||||
|
if [ -e /var/run/reniced.pid ]; then
|
||||||
|
echo "reniced is running as pid $(cat /run/reniced.pid)."
|
||||||
|
else
|
||||||
|
echo "reniced is stopped."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
'start')
|
||||||
|
_start
|
||||||
|
;;
|
||||||
|
'stop')
|
||||||
|
_stop
|
||||||
|
;;
|
||||||
|
'restart')
|
||||||
|
_restart
|
||||||
|
;;
|
||||||
|
'status')
|
||||||
|
_status
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "usage $0 start|stop|restart|status"
|
||||||
|
esac
|
||||||
10
contrib/reniced.service
Normal file
10
contrib/reniced.service
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Reniced Service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/usr/sbin/reniced
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
19
go.mod
19
go.mod
@@ -1,19 +1,22 @@
|
|||||||
module reniced
|
module reniced
|
||||||
|
|
||||||
go 1.21.4
|
go 1.22.6
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/shirou/gopsutil/v3 v3.24.2
|
github.com/alitto/pond v1.9.2
|
||||||
|
github.com/sevlyar/go-daemon v0.1.6
|
||||||
|
github.com/shirou/gopsutil/v3 v3.24.5
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a // indirect
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.13 // indirect
|
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||||
github.com/tklauser/numcpus v0.7.0 // indirect
|
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
golang.org/x/sys v0.18.0 // indirect
|
golang.org/x/sys v0.20.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
43
go.sum
43
go.sum
@@ -1,53 +1,44 @@
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/alitto/pond v1.9.2 h1:9Qb75z/scEZVCoSU+osVmQ0I0JOeLfdTDafrbcJ8CLs=
|
||||||
|
github.com/alitto/pond v1.9.2/go.mod h1:xQn3P/sHTYcU/1BR3i86IGIrilcrGC2LiS+E2+CJWsI=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
|
||||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
|
||||||
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a h1:3Bm7EwfUQUvhNeKIkUct/gl9eod1TcXuj8stxvi/GoI=
|
|
||||||
github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
github.com/sevlyar/go-daemon v0.1.6 h1:EUh1MDjEM4BI109Jign0EaknA2izkOyi0LV3ro3QQGs=
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
github.com/sevlyar/go-daemon v0.1.6/go.mod h1:6dJpPatBT9eUwM5VCw9Bt6CdX9Tk6UWvhW3MebLDRKE=
|
||||||
github.com/shirou/gopsutil/v3 v3.24.2 h1:kcR0erMbLg5/3LcInpw0X/rrPSqq4CDPyI6A6ZRC18Y=
|
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||||
github.com/shirou/gopsutil/v3 v3.24.2/go.mod h1:tSg/594BcA+8UdQU2XcW803GWYgdtauFFPgJCJKZlVk=
|
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||||
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||||
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
|
||||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||||
github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
|
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||||
github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
|
|
||||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||||
github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4=
|
|
||||||
github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY=
|
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
15
internal/lib/constants.go
Normal file
15
internal/lib/constants.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package lib
|
||||||
|
|
||||||
|
const IOPRIO_CLASS_NONE = uint32(0) //nolint:revive,stylecheck
|
||||||
|
const IOPRIO_CLASS_RT = uint32(1) //nolint:revive,stylecheck
|
||||||
|
const IOPRIO_CLASS_BE = uint32(2) //nolint:revive,stylecheck
|
||||||
|
const IOPRIO_CLASS_IDLE = uint32(3) //nolint:revive,stylecheck
|
||||||
|
|
||||||
|
const IOPRIO_WHO_PROCESS = 1 //nolint:revive,stylecheck
|
||||||
|
const IOPRIO_WHO_PGRP = 2 //nolint:revive,stylecheck
|
||||||
|
const IOPRIO_WHO_USER = 3 //nolint:revive,stylecheck
|
||||||
|
|
||||||
|
const IOPRIO_CLASS_SHIFT = uint32(13) //nolint:revive,stylecheck
|
||||||
|
const IOPRIO_PRIO_MASK = ((uint32(1) << IOPRIO_CLASS_SHIFT) - 1) //nolint:revive,stylecheck
|
||||||
|
|
||||||
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
17
internal/lib/ionice.go
Normal file
17
internal/lib/ionice.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package lib
|
||||||
|
|
||||||
|
func PrioToClass(prio uint32) uint32 {
|
||||||
|
return prio >> IOPRIO_CLASS_SHIFT
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrioToClassdata(prio uint32) uint32 {
|
||||||
|
return prio & IOPRIO_PRIO_MASK
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClassAndClassdataToPrio(class uint32, classdata uint32) uint32 {
|
||||||
|
return ((class) << IOPRIO_CLASS_SHIFT) | classdata
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrioToClassAndClassdata(prio uint32) (uint32, uint32) {
|
||||||
|
return PrioToClass(prio), PrioToClassdata(prio)
|
||||||
|
}
|
||||||
80
internal/lib/ionice_linux.go
Normal file
80
internal/lib/ionice_linux.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
//go:build linux
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
proc "github.com/shirou/gopsutil/v3/process"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetIOPriority достаёт приоритет вводв-вывода для укаанного процесса.
|
||||||
|
func GetIOPriority(which int, who int) (uint32, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
r0, _, e1 = syscall.Syscall(syscall.SYS_IOPRIO_GET, uintptr(which), uintptr(who), 0)
|
||||||
|
prio = uint32(r0)
|
||||||
|
)
|
||||||
|
|
||||||
|
if e1 != 0 {
|
||||||
|
err = fmt.Errorf("received error number %w", e1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return prio, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIOPriority ставит приоритет вводв-вывода для укаанного процесса.
|
||||||
|
func SetIOPriority(which int, who int, prio uint32) error {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
_, _, e1 = syscall.Syscall(syscall.SYS_IOPRIO_SET, uintptr(which), uintptr(who), uintptr(prio))
|
||||||
|
)
|
||||||
|
|
||||||
|
if e1 != 0 {
|
||||||
|
err = fmt.Errorf("received error number %w", e1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IORenice выставляет процессу приоритет для операций ввода-вывода.
|
||||||
|
func (cnf Config) IORenice(p *proc.Process, processName string) {
|
||||||
|
if currentIOPrioLevel, err := GetIOPriority(IOPRIO_WHO_PROCESS, int(p.Pid)); err == nil {
|
||||||
|
currentClass, currentClassdata := PrioToClassAndClassdata(currentIOPrioLevel)
|
||||||
|
class := cnf.IoreniceClass[processName]
|
||||||
|
classdata := cnf.IoreniceClassdata[processName]
|
||||||
|
|
||||||
|
if class == currentClass && classdata == currentClassdata {
|
||||||
|
if cnf.Debug && currentIOPrioLevel != 0 { // 0 - это дефолтный уровень io niceness.
|
||||||
|
log.Printf(
|
||||||
|
"IONiceness for %s(%d) already set to %d %d",
|
||||||
|
processName,
|
||||||
|
p.Pid,
|
||||||
|
currentClass,
|
||||||
|
currentClassdata,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
iopriority := ClassAndClassdataToPrio(class, classdata)
|
||||||
|
|
||||||
|
_ = SetIOPriority(IOPRIO_WHO_PROCESS, int(p.Pid), iopriority)
|
||||||
|
|
||||||
|
if cnf.Debug {
|
||||||
|
log.Printf(
|
||||||
|
"Set ioniceness for %s(%d) to %d %d (was %d %d)",
|
||||||
|
processName,
|
||||||
|
p.Pid,
|
||||||
|
class,
|
||||||
|
classdata,
|
||||||
|
currentClass,
|
||||||
|
currentClassdata,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("Unable to get ioprio for %s(%d)", processName, p.Pid)
|
||||||
|
}
|
||||||
|
}
|
||||||
16
internal/lib/ionice_other.go
Normal file
16
internal/lib/ionice_other.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
//go:build !linux
|
||||||
|
// +build !linux
|
||||||
|
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import proc "github.com/shirou/gopsutil/v3/process"
|
||||||
|
|
||||||
|
func (cnf Config) GetIOPriority(which int, who int) (prio uint32, err error) {
|
||||||
|
return 0, cnf.NotImplementedError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cnf Config) SetIOPriority(which int, who int, prio uint32) (err error) {
|
||||||
|
return cnf.NotImplementedError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cnf Config) IORenice(p *proc.Process, processName string) {}
|
||||||
166
internal/lib/reniced.go
Normal file
166
internal/lib/reniced.go
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
proc "github.com/shirou/gopsutil/v3/process"
|
||||||
|
)
|
||||||
|
|
||||||
|
// reniced основная логика.
|
||||||
|
func (cnf Config) Reniced() {
|
||||||
|
var (
|
||||||
|
processListUpdateTimer *time.Ticker
|
||||||
|
processKillTimer *time.Ticker
|
||||||
|
processReniceTimer *time.Ticker
|
||||||
|
processIONiceTimer *time.Ticker
|
||||||
|
processListTimer *time.Ticker
|
||||||
|
processList []*proc.Process
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
// Прежде всего настроим таймеры
|
||||||
|
processListUpdateTimer = time.NewTicker(time.Duration(cnf.ProcessListUpdatePeriod) * time.Millisecond)
|
||||||
|
processReniceTimer = time.NewTicker(time.Duration(cnf.ProcessNicePeriod) * time.Millisecond)
|
||||||
|
processKillTimer = time.NewTicker(time.Duration(cnf.ProcessKillPeriod) * time.Millisecond)
|
||||||
|
processIONiceTimer = time.NewTicker(time.Duration(cnf.ProcessIONicePeriod) * time.Millisecond)
|
||||||
|
processListTimer = time.NewTicker(time.Duration(cnf.PeriodicProcessInvocationPeriod) * time.Millisecond)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
// Обновляем список процессов.
|
||||||
|
case <-processListUpdateTimer.C:
|
||||||
|
processList, err = proc.Processes()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Unable to get process list: %s", err)
|
||||||
|
}
|
||||||
|
// Проходим по списку процессов и посылаем сигналы процессам.
|
||||||
|
case <-processKillTimer.C:
|
||||||
|
for _, p := range processList {
|
||||||
|
ok := cnf.Pool.TrySubmit(func() {
|
||||||
|
if processName, err := p.Name(); err == nil {
|
||||||
|
// Посылаем процессу сигналы, если таковые есть в конфиге.
|
||||||
|
if killSignal := cnf.KillSignal[processName]; killSignal != 0 {
|
||||||
|
switch killSignal { //nolint:exhaustive
|
||||||
|
case syscall.SIGSTOP:
|
||||||
|
_ = p.SendSignal(killSignal)
|
||||||
|
|
||||||
|
if cnf.Debug {
|
||||||
|
log.Printf(
|
||||||
|
"Matching processName with killProcessname %s(%d), SIG%s sent",
|
||||||
|
processName,
|
||||||
|
p.Pid,
|
||||||
|
strings.ToUpper(killSignal.String()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
_ = p.SendSignal(killSignal)
|
||||||
|
|
||||||
|
if cnf.Debug {
|
||||||
|
log.Printf(
|
||||||
|
"Matching processName with killProcessname %s(%d), SIG%s sent",
|
||||||
|
processName,
|
||||||
|
p.Pid,
|
||||||
|
strings.ToUpper(killSignal.String()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
log.Printf("Unable to add task to pool. Increase nb_capacity?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Проходим по списку процессов и меняем ionice процессам.
|
||||||
|
case <-processIONiceTimer.C:
|
||||||
|
for _, p := range processList {
|
||||||
|
ok := cnf.Pool.TrySubmit(func() {
|
||||||
|
if processName, err := p.Name(); err == nil {
|
||||||
|
cnf.IORenice(p, processName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
log.Printf("Unable to add task to pool. Increase nb_capacity?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Проходим по списку того, что надо запустить и запускаем.
|
||||||
|
case <-processListTimer.C:
|
||||||
|
for _, v := range cnf.PeriodicProcesses {
|
||||||
|
log.Printf("Invoking command: %s", v)
|
||||||
|
exec.Command(v)
|
||||||
|
time.Sleep(time.Duration(cnf.PeriodicProcessInvocationDelay) * time.Millisecond)
|
||||||
|
}
|
||||||
|
// Проходим по списку процессов и меняем ionice процессам.
|
||||||
|
case <-processReniceTimer.C:
|
||||||
|
// Не самый эффективный, зато работающий способ - простой перебор массивов.
|
||||||
|
for _, p := range processList {
|
||||||
|
ok := cnf.Pool.TrySubmit(func() {
|
||||||
|
if processName, err := p.Name(); err == nil {
|
||||||
|
// Для каждого процесса извлекаем его текущий priority.
|
||||||
|
if currentPrioLevel, err := syscall.Getpriority(syscall.PRIO_PROCESS, int(p.Pid)); err == nil {
|
||||||
|
if niceLevel := cnf.Renice[processName]; niceLevel != 0 {
|
||||||
|
prioLevel := niceLevel
|
||||||
|
currentNiceLevel := currentPrioLevel
|
||||||
|
|
||||||
|
if runtime.GOOS == "linux" {
|
||||||
|
// Value of PR = 20 + (-20 to +19) is 0 to 39
|
||||||
|
switch {
|
||||||
|
case niceLevel > 0:
|
||||||
|
prioLevel = 20 - niceLevel
|
||||||
|
case niceLevel < 0:
|
||||||
|
prioLevel = 20 + int(math.Abs(float64(niceLevel)))
|
||||||
|
default:
|
||||||
|
prioLevel = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
currentNiceLevel = 20 - currentPrioLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentPrioLevel != prioLevel {
|
||||||
|
// on Linux Setpriority actually operates niceness value.
|
||||||
|
_ = syscall.Setpriority(syscall.PRIO_PROCESS, int(p.Pid), niceLevel)
|
||||||
|
|
||||||
|
if cnf.Debug {
|
||||||
|
log.Printf(
|
||||||
|
"Set niceness for %s(%d) to %d (was %d)",
|
||||||
|
processName,
|
||||||
|
p.Pid,
|
||||||
|
niceLevel,
|
||||||
|
currentNiceLevel,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if cnf.Debug {
|
||||||
|
log.Printf(
|
||||||
|
"Niceness for %s(%d) already set to %d",
|
||||||
|
processName,
|
||||||
|
p.Pid,
|
||||||
|
niceLevel,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
log.Printf("Unable to add task to pool. Increase nb_capacity?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Мы сюда никогда не попадём.
|
||||||
|
// pool.StopAndWait()
|
||||||
|
} //nolint:wsl
|
||||||
|
|
||||||
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
30
internal/lib/sighandler.go
Normal file
30
internal/lib/sighandler.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// sigHandler хэндлер сигналов. Работает на выходе приложения. Держит INT, TERM, QUIT.
|
||||||
|
func (cnf Config) SigHandler() {
|
||||||
|
for {
|
||||||
|
var s = <-cnf.SigChan
|
||||||
|
switch s {
|
||||||
|
case syscall.SIGINT:
|
||||||
|
log.Print("Got SIGINT, quitting")
|
||||||
|
case syscall.SIGTERM:
|
||||||
|
log.Print("Got SIGTERM, quitting")
|
||||||
|
case syscall.SIGQUIT:
|
||||||
|
log.Print("Got SIGQUIT, quitting")
|
||||||
|
|
||||||
|
// Заходим на новую итерацию, если у нас "неинтересный" сигнал
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
89
internal/lib/types.go
Normal file
89
internal/lib/types.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/alitto/pond"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
// Пул воркеров.
|
||||||
|
Pool *pond.WorkerPool
|
||||||
|
|
||||||
|
KillSignal map[string]syscall.Signal
|
||||||
|
Renice map[string]int
|
||||||
|
IoreniceClass map[string]uint32
|
||||||
|
IoreniceClassdata map[string]uint32
|
||||||
|
ClassToString map[uint32]string
|
||||||
|
StringToClass map[string]uint32
|
||||||
|
|
||||||
|
// Канал, в который приходят уведомления для хэндлера сигналов от траппера сигналов.
|
||||||
|
SigChan chan os.Signal
|
||||||
|
|
||||||
|
NotImplementedError error
|
||||||
|
|
||||||
|
// Weather we fork to backgoround or stay as-is.
|
||||||
|
Daemon bool `yaml:"daemon,omitempty"`
|
||||||
|
|
||||||
|
// Where to put pid-file.
|
||||||
|
Pidfile string `yaml:"pidfile,omitempty"`
|
||||||
|
|
||||||
|
// ProcessListUpdatePeriod - delay between process list update task consecutive runs.
|
||||||
|
ProcessListUpdatePeriod int `yaml:"process_list_update_period,omitempty"`
|
||||||
|
|
||||||
|
// ProcessKillPeriod in milliseconds - delay between kill consecutive tasks.
|
||||||
|
ProcessKillPeriod int `yaml:"process_kill_period,omitempty"`
|
||||||
|
|
||||||
|
// ProcessNicePeriod in milliseconds - delay between changing niceness consecutive tasks.
|
||||||
|
ProcessNicePeriod int `yaml:"process_nice_period,omitempty"`
|
||||||
|
|
||||||
|
// ProcessNicePeriod in milliseconds - delay between changing io niceness consecutive tasks (linux-only).
|
||||||
|
ProcessIONicePeriod int `yaml:"process_ionice_period,omitempty"`
|
||||||
|
|
||||||
|
// PeriodicProcessInvocationPeriod in milliseconds - delay between PeriodicProcesses invocations.
|
||||||
|
PeriodicProcessInvocationPeriod int `yaml:"periodic_process_invocation_period,omitempty"`
|
||||||
|
|
||||||
|
// PriodicProcessInvocationDelay in milliseconds - delay between each process calling in PeriodicProcesses list.
|
||||||
|
PeriodicProcessInvocationDelay int `yaml:"periodic_process_invocation_delay,omitempty"`
|
||||||
|
|
||||||
|
// PeriodicProcesses list of processes that should be executed priodically.
|
||||||
|
PeriodicProcesses []string `yaml:"periodic_process,omitempty"`
|
||||||
|
|
||||||
|
// Whether to print debug info on stdout.
|
||||||
|
Debug bool `yaml:"debug,omitempty"`
|
||||||
|
|
||||||
|
// Maximum workers in pool.
|
||||||
|
MaxWorkers int `yaml:"max_workers,omitempty"`
|
||||||
|
|
||||||
|
// Capacity of non-blocking tasks. All tasks that exceeding this capacity will block submission of new tasks until
|
||||||
|
// queue has vacant place for new task.
|
||||||
|
NbCapacity int `yaml:"nb_capacity,omitempty"`
|
||||||
|
|
||||||
|
// В гошке yaml не позволяет сделать удобный парсер для конструкций, аналогичных json-овскому
|
||||||
|
// {kill:{"stop":["proc1","proc2"], "term":["proc3","proc4"]}}
|
||||||
|
// Поэтому мы просто возьмём массив и каждому элементу добавим тип сигнала.
|
||||||
|
Kill Kill `yaml:"kill,omitempty"`
|
||||||
|
|
||||||
|
Prio Prio `yaml:"prio,omitempty"`
|
||||||
|
|
||||||
|
IOPrio IOPrio `yaml:"ioprio,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Prio []struct {
|
||||||
|
Nice int `yaml:"nice,omitempty"`
|
||||||
|
Name []string `yaml:"name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Kill []struct {
|
||||||
|
Sig string `yaml:"sig,omitempty"`
|
||||||
|
Name []string `yaml:"name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IOPrio []struct {
|
||||||
|
Class uint32 `yaml:"class,omitempty"`
|
||||||
|
Prio uint32 `yaml:"prio,omitempty"`
|
||||||
|
Name []string `yaml:"name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
220
internal/lib/util.go
Normal file
220
internal/lib/util.go
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// readConf вычиляет, от какого пользователя запущен процесс и в зависимости от результата, скармливает readConfFile тот
|
||||||
|
// или иной путь, по которому предположительно лежит конфиг.
|
||||||
|
func (cnf *Config) ReadConf() error {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
u, err := user.Current()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Unable to get user info for current user: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Для рута и для других пользователей конфиги находятся в разных местах.
|
||||||
|
if u.Uid != "0" || runtime.GOOS == "darwin" {
|
||||||
|
if os.Getenv("HOME") == "" {
|
||||||
|
log.Fatalln("Unable to get HOME environment variable value")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cnf.readConfFile(os.Getenv("HOME") + "/.reniced.yaml")
|
||||||
|
} else {
|
||||||
|
if runtime.GOOS == "freebsd" {
|
||||||
|
err = cnf.readConfFile("/usr/local/etc/reniced.yaml")
|
||||||
|
} else {
|
||||||
|
err = cnf.readConfFile("/etc/reniced.yaml")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// readConfFile парсит даденный конфиг в глобальную структуру, с которой работает программа.
|
||||||
|
func (cnf *Config) readConfFile(path string) error {
|
||||||
|
fileInfo, err := os.Stat(path)
|
||||||
|
|
||||||
|
// Предполагаем, что файла либо нет, либо мы не можем его прочитать, второе надо бы логгировать, но пока забьём.
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конфиг-файл длинноват для конфига, попробуем следующего кандидата.
|
||||||
|
if fileInfo.Size() > 65535 {
|
||||||
|
err = fmt.Errorf("config file %s is too long for config, skipping", path)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := os.ReadFile(path)
|
||||||
|
|
||||||
|
// Не удалось прочитать.
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = yaml.Unmarshal(buf, &cnf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.ProcessListUpdatePeriod == 0 {
|
||||||
|
cnf.ProcessListUpdatePeriod = 5000
|
||||||
|
log.Printf("process_list_update_priod set to %d milliseconds", cnf.ProcessListUpdatePeriod)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.ProcessNicePeriod == 0 {
|
||||||
|
cnf.ProcessNicePeriod = 5000
|
||||||
|
log.Printf("process_nice_priod set to %d milliseconds", cnf.ProcessNicePeriod)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.ProcessIONicePeriod == 0 {
|
||||||
|
cnf.ProcessIONicePeriod = 5000
|
||||||
|
log.Printf("process_ionice_period set to %d milliseconds", cnf.ProcessIONicePeriod)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.ProcessKillPeriod == 0 {
|
||||||
|
cnf.ProcessKillPeriod = 5000
|
||||||
|
log.Printf("process_kill_period set to %d milliseconds", cnf.ProcessIONicePeriod)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.PeriodicProcessInvocationPeriod == 0 {
|
||||||
|
cnf.PeriodicProcessInvocationPeriod = 60000
|
||||||
|
log.Printf("periodic_process_invocation_period set to %d milliseconds", cnf.PeriodicProcessInvocationPeriod)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.PeriodicProcessInvocationDelay == 0 {
|
||||||
|
cnf.PeriodicProcessInvocationDelay = 500
|
||||||
|
log.Printf("periodic_process_invocation_delay set to %d milliseconds", cnf.PeriodicProcessInvocationDelay)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.Debug {
|
||||||
|
log.Println("debug set to true")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.MaxWorkers == 0 {
|
||||||
|
cnf.MaxWorkers = runtime.NumCPU()
|
||||||
|
log.Printf("max_workers set to %d", cnf.MaxWorkers)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.NbCapacity == 0 {
|
||||||
|
cnf.NbCapacity = 100
|
||||||
|
log.Printf("nb_capacity set to %d", cnf.NbCapacity)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.NbCapacity < cnf.MaxWorkers {
|
||||||
|
cnf.NbCapacity = cnf.MaxWorkers
|
||||||
|
log.Printf("nb_capacity set to same value as max_workers: %d", cnf.MaxWorkers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обычный юзер не может задрать nice больше 0, только руту такое можно.
|
||||||
|
var maxNice int
|
||||||
|
|
||||||
|
if os.Getuid() == 0 {
|
||||||
|
maxNice = -20
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range cnf.Prio {
|
||||||
|
switch {
|
||||||
|
case len(v.Name) != 0 && (v.Nice < 20 && v.Nice > maxNice):
|
||||||
|
for _, procName := range v.Name {
|
||||||
|
cnf.Renice[procName] = v.Nice
|
||||||
|
|
||||||
|
if cnf.Debug {
|
||||||
|
log.Printf("Add %s to list of nicelevel %d processes.", procName, v.Nice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case len(v.Name) == 0:
|
||||||
|
log.Println("Skipping empty entry in prio config block")
|
||||||
|
case v.Nice > 20 || v.Nice < maxNice:
|
||||||
|
log.Printf("Niceness value out of range %d < nice < 20 in prio config block", maxNice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range cnf.IOPrio {
|
||||||
|
switch {
|
||||||
|
case len(v.Name) == 0:
|
||||||
|
log.Println("Skipping empty name entry in ioprio config block")
|
||||||
|
case v.Class > 3:
|
||||||
|
log.Printf("Skipping entry %s with class > 3 in ioprio config block", v.Name)
|
||||||
|
case v.Class == 0 && v.Prio != 0:
|
||||||
|
log.Printf("Skipping entry %s with class 0 and prio not 0 in ioprio config block", v.Name)
|
||||||
|
case v.Class == 3 && v.Prio != 0:
|
||||||
|
log.Printf("Skipping entry %s with class 3 and prio not 0 in ioprio config block", v.Name)
|
||||||
|
case v.Class == 1 && v.Prio > 7:
|
||||||
|
log.Printf("Skipping entry %s with class 1 and prio > 7 in ioprio config block", v.Name)
|
||||||
|
case v.Class == 2 && v.Prio > 7:
|
||||||
|
log.Printf("Skipping entry %s with class 2 and prio > 7 in ioprio config block", v.Name)
|
||||||
|
default:
|
||||||
|
for _, processName := range v.Name {
|
||||||
|
cnf.IoreniceClass[processName] = v.Class
|
||||||
|
cnf.IoreniceClassdata[processName] = v.Prio
|
||||||
|
|
||||||
|
if cnf.Debug {
|
||||||
|
log.Printf("Add %s to list of ionice class %d processes.", processName, v.Class)
|
||||||
|
log.Printf("Add %s to list of ionice prio %d processes.", processName, v.Prio)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range cnf.Kill {
|
||||||
|
if len(v.Name) != 0 {
|
||||||
|
switch v.Sig {
|
||||||
|
case "kill", "stop", "term", "int", "quit", "abrt", "hup", "usr1", "usr2":
|
||||||
|
for _, procName := range v.Name {
|
||||||
|
switch v.Sig {
|
||||||
|
case "kill":
|
||||||
|
cnf.KillSignal[procName] = syscall.SIGKILL
|
||||||
|
case "stop":
|
||||||
|
cnf.KillSignal[procName] = syscall.SIGSTOP
|
||||||
|
case "term":
|
||||||
|
cnf.KillSignal[procName] = syscall.SIGTERM
|
||||||
|
case "int":
|
||||||
|
cnf.KillSignal[procName] = syscall.SIGINT
|
||||||
|
case "quit":
|
||||||
|
cnf.KillSignal[procName] = syscall.SIGQUIT
|
||||||
|
case "abrt":
|
||||||
|
cnf.KillSignal[procName] = syscall.SIGABRT
|
||||||
|
case "hup":
|
||||||
|
cnf.KillSignal[procName] = syscall.SIGHUP
|
||||||
|
case "usr1":
|
||||||
|
cnf.KillSignal[procName] = syscall.SIGUSR1
|
||||||
|
case "usr2":
|
||||||
|
cnf.KillSignal[procName] = syscall.SIGUSR2
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.Debug {
|
||||||
|
log.Printf(
|
||||||
|
"Add %s to list of processes that should be killed with SIG%s signal.",
|
||||||
|
procName,
|
||||||
|
strings.ToUpper(v.Sig),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Println("Skipping unsupported signal entry in kill config block")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Println("Skipping empty entry in kill config block")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
181
main.go
181
main.go
@@ -1,181 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
proc "github.com/shirou/gopsutil/v3/process"
|
|
||||||
)
|
|
||||||
|
|
||||||
// main основная функция программы.
|
|
||||||
func main() {
|
|
||||||
if os.Getenv("HOME") == "" {
|
|
||||||
log.Fatalln("Unable to get HOME environment variable value")
|
|
||||||
}
|
|
||||||
|
|
||||||
cnf, err := readConf(os.Getenv("HOME") + "/.reniced.yaml")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("unable to parse config: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Основной цикл.
|
|
||||||
for {
|
|
||||||
processList, err := proc.Processes()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Unable to get process list: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Не самый эффективный, зато работающий способ - простой перебор массивов.
|
|
||||||
for _, p := range processList {
|
|
||||||
if processName, err := p.Name(); err == nil {
|
|
||||||
for _, k := range cnf.Kill {
|
|
||||||
for _, killProcessName := range k.Name {
|
|
||||||
if processName == killProcessName {
|
|
||||||
switch k.Sig {
|
|
||||||
case "kill":
|
|
||||||
_ = p.SendSignal(syscall.SIGKILL)
|
|
||||||
|
|
||||||
if cnf.Debug {
|
|
||||||
log.Printf(
|
|
||||||
"Matching processName with killProcessname (%s), SIG%s sent",
|
|
||||||
processName,
|
|
||||||
strings.ToUpper(k.Sig),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case "stop":
|
|
||||||
if s, err := p.Status(); err == nil && s[0] != "stop" {
|
|
||||||
_ = p.SendSignal(syscall.SIGSTOP)
|
|
||||||
|
|
||||||
if cnf.Debug {
|
|
||||||
log.Printf(
|
|
||||||
"Matching processName with killProcessname (%s), SIG%s sent",
|
|
||||||
processName,
|
|
||||||
strings.ToUpper(k.Sig),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else if err == nil && s[0] == "stop" && cnf.Debug {
|
|
||||||
log.Printf(
|
|
||||||
"Matching processName with killProcessname (%s) and it is already stopped",
|
|
||||||
processName,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case "term":
|
|
||||||
_ = p.SendSignal(syscall.SIGTERM)
|
|
||||||
|
|
||||||
if cnf.Debug {
|
|
||||||
log.Printf(
|
|
||||||
"Matching processName with killProcessname (%s), SIG%s sent",
|
|
||||||
processName,
|
|
||||||
strings.ToUpper(k.Sig),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case "int":
|
|
||||||
_ = p.SendSignal(syscall.SIGINT)
|
|
||||||
|
|
||||||
if cnf.Debug {
|
|
||||||
log.Printf(
|
|
||||||
"Matching processName with killProcessname (%s), SIG%s sent",
|
|
||||||
processName,
|
|
||||||
strings.ToUpper(k.Sig),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case "quit":
|
|
||||||
_ = p.SendSignal(syscall.SIGQUIT)
|
|
||||||
|
|
||||||
if cnf.Debug {
|
|
||||||
log.Printf(
|
|
||||||
"Matching processName with killProcessname (%s), SIG%s sent",
|
|
||||||
processName,
|
|
||||||
strings.ToUpper(k.Sig),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case "abrt":
|
|
||||||
_ = p.SendSignal(syscall.SIGABRT)
|
|
||||||
|
|
||||||
if cnf.Debug {
|
|
||||||
log.Printf(
|
|
||||||
"Matching processName with killProcessname (%s), SIG%s sent",
|
|
||||||
processName,
|
|
||||||
strings.ToUpper(k.Sig),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case "hup":
|
|
||||||
_ = p.SendSignal(syscall.SIGHUP)
|
|
||||||
|
|
||||||
if cnf.Debug {
|
|
||||||
log.Printf(
|
|
||||||
"Matching processName with killProcessname (%s), SIG%s sent",
|
|
||||||
processName,
|
|
||||||
strings.ToUpper(k.Sig),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case "usr1":
|
|
||||||
_ = p.SendSignal(syscall.SIGUSR1)
|
|
||||||
|
|
||||||
if cnf.Debug {
|
|
||||||
log.Printf(
|
|
||||||
"Matching processName with killProcessname (%s), SIG%s sent",
|
|
||||||
processName,
|
|
||||||
strings.ToUpper(k.Sig),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case "usr2":
|
|
||||||
_ = p.SendSignal(syscall.SIGUSR2)
|
|
||||||
|
|
||||||
if cnf.Debug {
|
|
||||||
log.Printf(
|
|
||||||
"Matching processName with killProcessname (%s), SIG%s sent",
|
|
||||||
processName,
|
|
||||||
strings.ToUpper(k.Sig),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Для каждого процесса извлекаем его текущий nicelevel.
|
|
||||||
if currentNiceLevel, err := p.Nice(); err == nil {
|
|
||||||
// Вынимаем список приоритетов.
|
|
||||||
for _, n := range cnf.Prio {
|
|
||||||
// Для каждого приоритета, проходимся по списку имён процессов.
|
|
||||||
for _, name := range n.Name {
|
|
||||||
// Достаём имя текущего процесса.
|
|
||||||
if processName, err = p.Name(); err == nil {
|
|
||||||
// Если имя процесса совпадает с одним из списка в конфиге, то пробуем менять ему nice.
|
|
||||||
if processName == name {
|
|
||||||
if currentNiceLevel != n.Nice {
|
|
||||||
_ = syscall.Setpriority(syscall.PRIO_PROCESS, int(p.Pid), int(n.Nice))
|
|
||||||
|
|
||||||
if cnf.Debug {
|
|
||||||
log.Printf(
|
|
||||||
"Set niceness for %s(%d) to %d (was %d)",
|
|
||||||
name,
|
|
||||||
p.Pid,
|
|
||||||
n.Nice,
|
|
||||||
currentNiceLevel,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else if cnf.Debug {
|
|
||||||
log.Printf("Nicelevel for %d already set to %d", p.Pid, n.Nice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(time.Millisecond * time.Duration(cnf.CmdDelay))
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(time.Millisecond * time.Duration(cnf.LoopDelay))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
|
||||||
31
types.go
31
types.go
@@ -1,31 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
// LoopDelay in milliseconds - whole set of action delay period.
|
|
||||||
LoopDelay int `yaml:"loop_delay"`
|
|
||||||
|
|
||||||
// CmdDelay in milliseconds - delay between each change application.
|
|
||||||
CmdDelay int `yaml:"cmd_delay"`
|
|
||||||
|
|
||||||
// Whether to print debug info on stdout.
|
|
||||||
Debug bool `yaml:"debug,omitempty"`
|
|
||||||
|
|
||||||
// В гошке yaml не позволяет сделать удобный парсер для конструкций, аналогичных json-овскому
|
|
||||||
// {kill:{"stop":["proc1","proc2"], "term":["proc3","proc4"]}}
|
|
||||||
// Поэтому мы просто возьмём массив и каждому элементу добавим тип сигнала.
|
|
||||||
Kill Kill `yaml:"kill,omitempty"`
|
|
||||||
|
|
||||||
Prio Prio `yaml:"prio,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Prio []struct {
|
|
||||||
Nice int32 `yaml:"nice,omitempty"`
|
|
||||||
Name []string `yaml:"name,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Kill []struct {
|
|
||||||
Sig string `yaml:"sig,omitempty"`
|
|
||||||
Name []string `yaml:"name,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
|
||||||
117
util.go
117
util.go
@@ -1,117 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
// readConf парсит даденный конфиг в глобальную структуру, с которой работает программа.
|
|
||||||
func readConf(path string) (Config, error) {
|
|
||||||
var cfg Config
|
|
||||||
|
|
||||||
fileInfo, err := os.Stat(path)
|
|
||||||
|
|
||||||
// Предполагаем, что файла либо нет, либо мы не можем его прочитать, второе надо бы логгировать, но пока забьём.
|
|
||||||
if err != nil {
|
|
||||||
return Config{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Конфиг-файл длинноват для конфига, попробуем следующего кандидата.
|
|
||||||
if fileInfo.Size() > 65535 {
|
|
||||||
err := fmt.Errorf("Config file %s is too long for config, skipping", path)
|
|
||||||
|
|
||||||
return Config{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
buf, err := os.ReadFile(path)
|
|
||||||
|
|
||||||
// Не удалось прочитать.
|
|
||||||
if err != nil {
|
|
||||||
return Config{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = yaml.Unmarshal(buf, &cfg); err != nil {
|
|
||||||
return Config{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.CmdDelay == 0 {
|
|
||||||
log.Println("cmd_delay can not be 0, setting to 200 milliseconds")
|
|
||||||
|
|
||||||
cfg.CmdDelay = 200
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.LoopDelay == 0 {
|
|
||||||
log.Println("loop_delay can not be 0, setting to 2000 milliseconds")
|
|
||||||
|
|
||||||
cfg.LoopDelay = 2000
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Debug {
|
|
||||||
log.Println("debug set to true")
|
|
||||||
} else {
|
|
||||||
log.Println("debug set to false")
|
|
||||||
}
|
|
||||||
|
|
||||||
newPrio := make(Prio, 0)
|
|
||||||
|
|
||||||
// Обычный юзер не может задрать nice больше 0, только руту такое можно.
|
|
||||||
var maxNice int32
|
|
||||||
|
|
||||||
if os.Getuid() == 0 {
|
|
||||||
maxNice = -20
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range cfg.Prio {
|
|
||||||
switch {
|
|
||||||
case len(v.Name) != 0 && (v.Nice < 20 && v.Nice > maxNice):
|
|
||||||
newPrio = append(newPrio, v)
|
|
||||||
|
|
||||||
if cfg.Debug {
|
|
||||||
for _, procName := range v.Name {
|
|
||||||
log.Printf("Add %s to list of nicelevel %d processes.", procName, v.Nice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case len(v.Name) == 0:
|
|
||||||
log.Println("Skipping empty entry in prio config block")
|
|
||||||
case v.Nice > 20 || v.Nice < maxNice:
|
|
||||||
log.Printf("Niceness value out of range %d < nice < 20 in prio config block", maxNice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.Prio = newPrio
|
|
||||||
|
|
||||||
newKill := make(Kill, 0)
|
|
||||||
|
|
||||||
for _, v := range cfg.Kill {
|
|
||||||
if len(v.Name) != 0 {
|
|
||||||
switch v.Sig {
|
|
||||||
case "kill", "stop", "term", "int", "quit", "abrt", "hup", "usr1", "usr2":
|
|
||||||
newKill = append(newKill, v)
|
|
||||||
|
|
||||||
if cfg.Debug {
|
|
||||||
for _, procName := range v.Name {
|
|
||||||
log.Printf(
|
|
||||||
"Add %s to list of processes that should be killed with SIG%s signal.",
|
|
||||||
procName,
|
|
||||||
strings.ToUpper(v.Sig),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.Println("Skipping unsupported signal entry in kill config block")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Println("Skipping empty entry in kill config block")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.Kill = newKill
|
|
||||||
|
|
||||||
return cfg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
|
||||||
12
vendor/github.com/alitto/pond/.gitignore
generated
vendored
Normal file
12
vendor/github.com/alitto/pond/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
21
vendor/github.com/alitto/pond/LICENSE
generated
vendored
Normal file
21
vendor/github.com/alitto/pond/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Alejandro Durante
|
||||||
|
|
||||||
|
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.
|
||||||
5
vendor/github.com/alitto/pond/Makefile
generated
vendored
Normal file
5
vendor/github.com/alitto/pond/Makefile
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
test:
|
||||||
|
go test -race -v ./
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
go test -race -v -coverprofile=coverage.out -covermode=atomic ./
|
||||||
313
vendor/github.com/alitto/pond/README.md
generated
vendored
Normal file
313
vendor/github.com/alitto/pond/README.md
generated
vendored
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
<a title="Codecov" target="_blank" href="https://github.com/alitto/pond/actions"><img alt="Build status" src="https://github.com/alitto/pond/actions/workflows/main.yml/badge.svg?branch=master&event=push"/></a>
|
||||||
|
<a title="Codecov" target="_blank" href="https://codecov.io/gh/alitto/pond"><img src="https://codecov.io/gh/alitto/pond/branch/master/graph/badge.svg"/></a>
|
||||||
|
<a title="Release" target="_blank" href="https://github.com/alitto/pond/releases"><img src="https://img.shields.io/github/v/release/alitto/pond"/></a>
|
||||||
|
<a title="Go Report Card" target="_blank" href="https://goreportcard.com/report/github.com/alitto/pond"><img src="https://goreportcard.com/badge/github.com/alitto/pond"/></a>
|
||||||
|
|
||||||
|
# pond
|
||||||
|
Minimalistic and High-performance goroutine worker pool written in Go
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
|
||||||
|
This library is meant to provide a simple way to limit concurrency when executing some function over a limited resource or service.
|
||||||
|
|
||||||
|
Some common scenarios include:
|
||||||
|
|
||||||
|
- Executing queries against a Database with a limited no. of connections
|
||||||
|
- Sending HTTP requests to a a rate/concurrency limited API
|
||||||
|
|
||||||
|
## Features:
|
||||||
|
|
||||||
|
- Zero dependencies
|
||||||
|
- Create pools with fixed or dynamic size
|
||||||
|
- Worker goroutines are only created when needed (backpressure detection) and automatically purged after being idle for some time (configurable)
|
||||||
|
- Minimalistic APIs for:
|
||||||
|
- Creating worker pools with fixed or dynamic size
|
||||||
|
- Submitting tasks to a pool in a fire-and-forget fashion
|
||||||
|
- Submitting tasks to a pool and waiting for them to complete
|
||||||
|
- Submitting tasks to a pool with a deadline
|
||||||
|
- Submitting a group of tasks and waiting for them to complete
|
||||||
|
- Submitting a group of tasks associated to a Context
|
||||||
|
- Getting the number of running workers (goroutines)
|
||||||
|
- Stopping a worker pool
|
||||||
|
- Task panics are handled gracefully (configurable panic handler)
|
||||||
|
- Supports Non-blocking and Blocking task submission modes (buffered / unbuffered)
|
||||||
|
- Very high performance and efficient resource usage under heavy workloads, even outperforming unbounded goroutines in some scenarios (See [benchmarks](https://github.com/alitto/pond-benchmarks))
|
||||||
|
- Configurable pool resizing strategy, with 3 presets for common scenarios: Eager, Balanced and Lazy.
|
||||||
|
- Complete pool metrics such as number of running workers, tasks waiting in the queue [and more](#metrics--monitoring).
|
||||||
|
- **New (since v1.7.0)**: configurable parent context and graceful shutdown with deadline.
|
||||||
|
- [API reference](https://pkg.go.dev/github.com/alitto/pond)
|
||||||
|
|
||||||
|
## How to install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get -u github.com/alitto/pond
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
### Worker pool with dynamic size
|
||||||
|
|
||||||
|
``` go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/alitto/pond"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// Create a buffered (non-blocking) pool that can scale up to 100 workers
|
||||||
|
// and has a buffer capacity of 1000 tasks
|
||||||
|
pool := pond.New(100, 1000)
|
||||||
|
|
||||||
|
// Submit 1000 tasks
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
n := i
|
||||||
|
pool.Submit(func() {
|
||||||
|
fmt.Printf("Running task #%d\n", n)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the pool and wait for all submitted tasks to complete
|
||||||
|
pool.StopAndWait()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Worker pool with fixed size
|
||||||
|
|
||||||
|
``` go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/alitto/pond"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// Create an unbuffered (blocking) pool with a fixed
|
||||||
|
// number of workers
|
||||||
|
pool := pond.New(10, 0, pond.MinWorkers(10))
|
||||||
|
|
||||||
|
// Submit 1000 tasks
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
n := i
|
||||||
|
pool.Submit(func() {
|
||||||
|
fmt.Printf("Running task #%d\n", n)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the pool and wait for all submitted tasks to complete
|
||||||
|
pool.StopAndWait()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Submitting a group of tasks
|
||||||
|
|
||||||
|
``` go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/alitto/pond"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// Create a pool
|
||||||
|
pool := pond.New(10, 1000)
|
||||||
|
defer pool.StopAndWait()
|
||||||
|
|
||||||
|
// Create a task group
|
||||||
|
group := pool.Group()
|
||||||
|
|
||||||
|
// Submit a group of tasks
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
n := i
|
||||||
|
group.Submit(func() {
|
||||||
|
fmt.Printf("Running group task #%d\n", n)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all tasks in the group to complete
|
||||||
|
group.Wait()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Submitting a group of tasks associated to a context (**since v1.8.0**)
|
||||||
|
|
||||||
|
This feature provides synchronization, error propagation, and Context cancelation for subtasks of a common task. Similar to `errgroup.Group` from [`golang.org/x/sync/errgroup`](https://pkg.go.dev/golang.org/x/sync/errgroup) package with concurrency bounded by the worker pool.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/alitto/pond"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// Create a worker pool
|
||||||
|
pool := pond.New(10, 1000)
|
||||||
|
defer pool.StopAndWait()
|
||||||
|
|
||||||
|
// Create a task group associated to a context
|
||||||
|
group, ctx := pool.GroupContext(context.Background())
|
||||||
|
|
||||||
|
var urls = []string{
|
||||||
|
"https://www.golang.org/",
|
||||||
|
"https://www.google.com/",
|
||||||
|
"https://www.github.com/",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit tasks to fetch each URL
|
||||||
|
for _, url := range urls {
|
||||||
|
url := url
|
||||||
|
group.Submit(func() error {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err == nil {
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all HTTP requests to complete.
|
||||||
|
err := group.Wait()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to fetch URLs: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("Successfully fetched all URLs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pool Configuration Options
|
||||||
|
|
||||||
|
#### MinWorkers
|
||||||
|
|
||||||
|
Specifies the minimum number of worker goroutines that must be running at any given time. These goroutines are started when the pool is created. The default value is 0. Example:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
// This will create a pool with 5 running worker goroutines
|
||||||
|
pool := pond.New(10, 1000, pond.MinWorkers(5))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### IdleTimeout
|
||||||
|
|
||||||
|
Defines how long to wait before removing idle worker goroutines from the pool. The default value is 5 seconds. Example:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
// This will create a pool that will remove workers 100ms after they become idle
|
||||||
|
pool := pond.New(10, 1000, pond.IdleTimeout(100 * time.Millisecond))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### PanicHandler
|
||||||
|
|
||||||
|
Allows to configure a custom function to handle panics thrown by tasks submitted to the pool. The default handler just writes a message to standard output using `fmt.Printf` with the following contents: `Worker exits from a panic: [panic] \n Stack trace: [stack trace]`). Example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Custom panic handler function
|
||||||
|
panicHandler := func(p interface{}) {
|
||||||
|
fmt.Printf("Task panicked: %v", p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will create a pool that will handle panics using a custom panic handler
|
||||||
|
pool := pond.New(10, 1000, pond.PanicHandler(panicHandler)))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Strategy
|
||||||
|
|
||||||
|
Configures the strategy used to resize the pool when backpressure is detected. You can create a custom strategy by implementing the `pond.ResizingStrategy` interface or choose one of the 3 presets:
|
||||||
|
|
||||||
|
- **Eager**: maximizes responsiveness at the expense of higher resource usage, which can reduce throughput under certain conditions. This strategy is meant for worker pools that will operate at a small percentage of their capacity most of the time and may occasionally receive bursts of tasks. This is the default strategy.
|
||||||
|
- **Balanced**: tries to find a balance between responsiveness and throughput. It's suitable for general purpose worker pools or those that will operate close to 50% of their capacity most of the time.
|
||||||
|
- **Lazy**: maximizes throughput at the expense of responsiveness. This strategy is meant for worker pools that will operate close to their max. capacity most of the time.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
// Example: create pools with different resizing strategies
|
||||||
|
eagerPool := pond.New(10, 1000, pond.Strategy(pond.Eager()))
|
||||||
|
balancedPool := pond.New(10, 1000, pond.Strategy(pond.Balanced()))
|
||||||
|
lazyPool := pond.New(10, 1000, pond.Strategy(pond.Lazy()))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Context
|
||||||
|
|
||||||
|
Configures a parent context on this pool to stop all workers when it is cancelled. The default value `context.Background()`. Example:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
// This creates a pool that is stopped when myCtx is cancelled
|
||||||
|
pool := pond.New(10, 1000, pond.Context(myCtx))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Resizing strategies
|
||||||
|
|
||||||
|
The following chart illustrates the behaviour of the different pool resizing strategies as the number of submitted tasks increases. Each line represents the number of worker goroutines in the pool (pool size) and the x-axis reflects the number of submitted tasks (cumulative).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
As the name suggests, the "Eager" strategy always spawns an extra worker when there are no idles, which causes the pool to grow almost linearly with the number of submitted tasks. On the other end, the "Lazy" strategy creates one worker every N submitted tasks, where N is the maximum number of available CPUs ([GOMAXPROCS](https://golang.org/pkg/runtime/#GOMAXPROCS)). The "Balanced" strategy represents a middle ground between the previous two because it creates a worker every N/2 submitted tasks.
|
||||||
|
|
||||||
|
### Stopping a pool
|
||||||
|
|
||||||
|
There are 3 methods available to stop a pool and release associated resources:
|
||||||
|
- `pool.Stop()`: stop accepting new tasks and signal all workers to stop processing new tasks. Tasks being processed by workers will continue until completion unless the process is terminated.
|
||||||
|
- `pool.StopAndWait()`: stop accepting new tasks and wait until all running and queued tasks have completed before returning.
|
||||||
|
- `pool.StopAndWaitFor(deadline time.Duration)`: similar to `StopAndWait` but with a deadline to prevent waiting indefinitely.
|
||||||
|
|
||||||
|
### Metrics & monitoring
|
||||||
|
|
||||||
|
Each worker pool instance exposes useful metrics that can be queried through the following methods:
|
||||||
|
|
||||||
|
- `pool.RunningWorkers() int`: Current number of running workers
|
||||||
|
- `pool.IdleWorkers() int`: Current number of idle workers
|
||||||
|
- `pool.MinWorkers() int`: Minimum number of worker goroutines
|
||||||
|
- `pool.MaxWorkers() int`: Maxmimum number of worker goroutines
|
||||||
|
- `pool.MaxCapacity() int`: Maximum number of tasks that can be waiting in the queue at any given time (queue capacity)
|
||||||
|
- `pool.SubmittedTasks() uint64`: Total number of tasks submitted since the pool was created
|
||||||
|
- `pool.WaitingTasks() uint64`: Current number of tasks in the queue that are waiting to be executed
|
||||||
|
- `pool.SuccessfulTasks() uint64`: Total number of tasks that have successfully completed their exection since the pool was created
|
||||||
|
- `pool.FailedTasks() uint64`: Total number of tasks that completed with panic since the pool was created
|
||||||
|
- `pool.CompletedTasks() uint64`: Total number of tasks that have completed their exection either successfully or with panic since the pool was created
|
||||||
|
|
||||||
|
In our [Prometheus example](./examples/prometheus/prometheus.go) we showcase how to configure collectors for these metrics and expose them to Prometheus.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
- [Creating a worker pool with dynamic size](./examples/dynamic_size/dynamic_size.go)
|
||||||
|
- [Creating a worker pool with fixed size](./examples/fixed_size/fixed_size.go)
|
||||||
|
- [Creating a worker pool with a Context](./examples/pool_context/pool_context.go)
|
||||||
|
- [Exporting worker pool metrics to Prometheus](./examples/prometheus/prometheus.go)
|
||||||
|
- [Submitting a group of tasks](./examples/task_group/task_group.go)
|
||||||
|
- [Submitting a group of tasks associated to a context](./examples/group_context/group_context.go)
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
Full API reference is available at https://pkg.go.dev/github.com/alitto/pond
|
||||||
|
|
||||||
|
## Benchmarks
|
||||||
|
|
||||||
|
See [Benchmarks](https://github.com/alitto/pond-benchmarks).
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
Here are some of the resources which have served as inspiration when writing this library:
|
||||||
|
|
||||||
|
- http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang/
|
||||||
|
- https://brandur.org/go-worker-pool
|
||||||
|
- https://gobyexample.com/worker-pools
|
||||||
|
- https://github.com/panjf2000/ants
|
||||||
|
- https://github.com/gammazero/workerpool
|
||||||
|
|
||||||
|
## Contribution & Support
|
||||||
|
|
||||||
|
Feel free to send a pull request if you consider there's something which can be improved. Also, please open up an issue if you run into a problem when using this library or just have a question.
|
||||||
8
vendor/github.com/alitto/pond/codecov.yml
generated
vendored
Normal file
8
vendor/github.com/alitto/pond/codecov.yml
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
coverage:
|
||||||
|
status:
|
||||||
|
project:
|
||||||
|
default:
|
||||||
|
target: 90%
|
||||||
|
patch:
|
||||||
|
default:
|
||||||
|
target: 90%
|
||||||
106
vendor/github.com/alitto/pond/group.go
generated
vendored
Normal file
106
vendor/github.com/alitto/pond/group.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package pond
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TaskGroup represents a group of related tasks
|
||||||
|
type TaskGroup struct {
|
||||||
|
pool *WorkerPool
|
||||||
|
waitGroup sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit adds a task to this group and sends it to the worker pool to be executed
|
||||||
|
func (g *TaskGroup) Submit(task func()) {
|
||||||
|
g.waitGroup.Add(1)
|
||||||
|
|
||||||
|
g.pool.Submit(func() {
|
||||||
|
defer g.waitGroup.Done()
|
||||||
|
|
||||||
|
task()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait waits until all the tasks in this group have completed
|
||||||
|
func (g *TaskGroup) Wait() {
|
||||||
|
|
||||||
|
// Wait for all tasks to complete
|
||||||
|
g.waitGroup.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskGroupWithContext represents a group of related tasks associated to a context
|
||||||
|
type TaskGroupWithContext struct {
|
||||||
|
TaskGroup
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
|
||||||
|
errSync struct {
|
||||||
|
once sync.Once
|
||||||
|
guard sync.RWMutex
|
||||||
|
}
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit adds a task to this group and sends it to the worker pool to be executed
|
||||||
|
func (g *TaskGroupWithContext) Submit(task func() error) {
|
||||||
|
g.waitGroup.Add(1)
|
||||||
|
|
||||||
|
g.pool.Submit(func() {
|
||||||
|
defer g.waitGroup.Done()
|
||||||
|
|
||||||
|
// If context has already been cancelled, skip task execution
|
||||||
|
select {
|
||||||
|
case <-g.ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't actually ignore errors
|
||||||
|
err := task()
|
||||||
|
if err != nil {
|
||||||
|
g.setError(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait blocks until either all the tasks submitted to this group have completed,
|
||||||
|
// one of them returned a non-nil error or the context associated to this group
|
||||||
|
// was canceled.
|
||||||
|
func (g *TaskGroupWithContext) Wait() error {
|
||||||
|
|
||||||
|
// Wait for all tasks to complete
|
||||||
|
tasksCompleted := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
g.waitGroup.Wait()
|
||||||
|
tasksCompleted <- struct{}{}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-tasksCompleted:
|
||||||
|
// If context was provided, cancel it to signal all running tasks to stop
|
||||||
|
g.cancel()
|
||||||
|
case <-g.ctx.Done():
|
||||||
|
g.setError(g.ctx.Err())
|
||||||
|
}
|
||||||
|
|
||||||
|
return g.getError()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *TaskGroupWithContext) getError() error {
|
||||||
|
g.errSync.guard.RLock()
|
||||||
|
err := g.err
|
||||||
|
g.errSync.guard.RUnlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *TaskGroupWithContext) setError(err error) {
|
||||||
|
g.errSync.once.Do(func() {
|
||||||
|
g.errSync.guard.Lock()
|
||||||
|
g.err = err
|
||||||
|
g.errSync.guard.Unlock()
|
||||||
|
|
||||||
|
// Cancel execution of any pending task in this group
|
||||||
|
g.cancel()
|
||||||
|
})
|
||||||
|
}
|
||||||
556
vendor/github.com/alitto/pond/pond.go
generated
vendored
Normal file
556
vendor/github.com/alitto/pond/pond.go
generated
vendored
Normal file
@@ -0,0 +1,556 @@
|
|||||||
|
package pond
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"runtime/debug"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// defaultIdleTimeout defines the default idle timeout to use when not explicitly specified
|
||||||
|
// via the IdleTimeout() option
|
||||||
|
defaultIdleTimeout = 5 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrSubmitOnStoppedPool is thrown when attempting to submit a task to a pool that has been stopped
|
||||||
|
ErrSubmitOnStoppedPool = errors.New("worker pool has been stopped and is no longer accepting tasks")
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultPanicHandler is the default panic handler
|
||||||
|
func defaultPanicHandler(panic interface{}) {
|
||||||
|
fmt.Printf("Worker exits from a panic: %v\nStack trace: %s\n", panic, string(debug.Stack()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizingStrategy represents a pool resizing strategy
|
||||||
|
type ResizingStrategy interface {
|
||||||
|
Resize(runningWorkers, minWorkers, maxWorkers int) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option represents an option that can be passed when instantiating a worker pool to customize it
|
||||||
|
type Option func(*WorkerPool)
|
||||||
|
|
||||||
|
// IdleTimeout allows to change the idle timeout for a worker pool
|
||||||
|
func IdleTimeout(idleTimeout time.Duration) Option {
|
||||||
|
return func(pool *WorkerPool) {
|
||||||
|
pool.idleTimeout = idleTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinWorkers allows to change the minimum number of workers of a worker pool
|
||||||
|
func MinWorkers(minWorkers int) Option {
|
||||||
|
return func(pool *WorkerPool) {
|
||||||
|
pool.minWorkers = minWorkers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strategy allows to change the strategy used to resize the pool
|
||||||
|
func Strategy(strategy ResizingStrategy) Option {
|
||||||
|
return func(pool *WorkerPool) {
|
||||||
|
pool.strategy = strategy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PanicHandler allows to change the panic handler function of a worker pool
|
||||||
|
func PanicHandler(panicHandler func(interface{})) Option {
|
||||||
|
return func(pool *WorkerPool) {
|
||||||
|
pool.panicHandler = panicHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context configures a parent context on a worker pool to stop all workers when it is cancelled
|
||||||
|
func Context(parentCtx context.Context) Option {
|
||||||
|
return func(pool *WorkerPool) {
|
||||||
|
pool.context, pool.contextCancel = context.WithCancel(parentCtx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WorkerPool models a pool of workers
|
||||||
|
type WorkerPool struct {
|
||||||
|
// Atomic counters, should be placed first so alignment is guaranteed
|
||||||
|
// for atomic operations.
|
||||||
|
workerCount int32
|
||||||
|
idleWorkerCount int32
|
||||||
|
waitingTaskCount uint64
|
||||||
|
submittedTaskCount uint64
|
||||||
|
successfulTaskCount uint64
|
||||||
|
failedTaskCount uint64
|
||||||
|
// Configurable settings
|
||||||
|
maxWorkers int
|
||||||
|
maxCapacity int
|
||||||
|
minWorkers int
|
||||||
|
idleTimeout time.Duration
|
||||||
|
strategy ResizingStrategy
|
||||||
|
panicHandler func(interface{})
|
||||||
|
context context.Context
|
||||||
|
contextCancel context.CancelFunc
|
||||||
|
// Private properties
|
||||||
|
tasks chan func()
|
||||||
|
tasksCloseOnce sync.Once
|
||||||
|
workersWaitGroup sync.WaitGroup
|
||||||
|
tasksWaitGroup sync.WaitGroup
|
||||||
|
mutex sync.Mutex
|
||||||
|
stopped int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a worker pool with that can scale up to the given maximum number of workers (maxWorkers).
|
||||||
|
// The maxCapacity parameter determines the number of tasks that can be submitted to this pool without blocking,
|
||||||
|
// because it defines the size of the buffered channel used to receive tasks.
|
||||||
|
// The options parameter can take a list of functions to customize configuration values on this worker pool.
|
||||||
|
func New(maxWorkers, maxCapacity int, options ...Option) *WorkerPool {
|
||||||
|
|
||||||
|
// Instantiate the pool
|
||||||
|
pool := &WorkerPool{
|
||||||
|
maxWorkers: maxWorkers,
|
||||||
|
maxCapacity: maxCapacity,
|
||||||
|
idleTimeout: defaultIdleTimeout,
|
||||||
|
strategy: Eager(),
|
||||||
|
panicHandler: defaultPanicHandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply all options
|
||||||
|
for _, opt := range options {
|
||||||
|
opt(pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure options are consistent
|
||||||
|
if pool.maxWorkers <= 0 {
|
||||||
|
pool.maxWorkers = 1
|
||||||
|
}
|
||||||
|
if pool.minWorkers > pool.maxWorkers {
|
||||||
|
pool.minWorkers = pool.maxWorkers
|
||||||
|
}
|
||||||
|
if pool.maxCapacity < 0 {
|
||||||
|
pool.maxCapacity = 0
|
||||||
|
}
|
||||||
|
if pool.idleTimeout < 0 {
|
||||||
|
pool.idleTimeout = defaultIdleTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize base context (if not already set)
|
||||||
|
if pool.context == nil {
|
||||||
|
Context(context.Background())(pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create tasks channel
|
||||||
|
pool.tasks = make(chan func(), pool.maxCapacity)
|
||||||
|
|
||||||
|
// Start purger goroutine
|
||||||
|
pool.workersWaitGroup.Add(1)
|
||||||
|
go pool.purge()
|
||||||
|
|
||||||
|
// Start minWorkers workers
|
||||||
|
if pool.minWorkers > 0 {
|
||||||
|
for i := 0; i < pool.minWorkers; i++ {
|
||||||
|
pool.maybeStartWorker(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunningWorkers returns the current number of running workers
|
||||||
|
func (p *WorkerPool) RunningWorkers() int {
|
||||||
|
return int(atomic.LoadInt32(&p.workerCount))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IdleWorkers returns the current number of idle workers
|
||||||
|
func (p *WorkerPool) IdleWorkers() int {
|
||||||
|
return int(atomic.LoadInt32(&p.idleWorkerCount))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinWorkers returns the minimum number of worker goroutines
|
||||||
|
func (p *WorkerPool) MinWorkers() int {
|
||||||
|
return p.minWorkers
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxWorkers returns the maximum number of worker goroutines
|
||||||
|
func (p *WorkerPool) MaxWorkers() int {
|
||||||
|
return p.maxWorkers
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxCapacity returns the maximum number of tasks that can be waiting in the queue
|
||||||
|
// at any given time (queue size)
|
||||||
|
func (p *WorkerPool) MaxCapacity() int {
|
||||||
|
return p.maxCapacity
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strategy returns the configured pool resizing strategy
|
||||||
|
func (p *WorkerPool) Strategy() ResizingStrategy {
|
||||||
|
return p.strategy
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmittedTasks returns the total number of tasks submitted since the pool was created
|
||||||
|
func (p *WorkerPool) SubmittedTasks() uint64 {
|
||||||
|
return atomic.LoadUint64(&p.submittedTaskCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitingTasks returns the current number of tasks in the queue that are waiting to be executed
|
||||||
|
func (p *WorkerPool) WaitingTasks() uint64 {
|
||||||
|
return atomic.LoadUint64(&p.waitingTaskCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SuccessfulTasks returns the total number of tasks that have successfully completed their exection
|
||||||
|
// since the pool was created
|
||||||
|
func (p *WorkerPool) SuccessfulTasks() uint64 {
|
||||||
|
return atomic.LoadUint64(&p.successfulTaskCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FailedTasks returns the total number of tasks that completed with panic since the pool was created
|
||||||
|
func (p *WorkerPool) FailedTasks() uint64 {
|
||||||
|
return atomic.LoadUint64(&p.failedTaskCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompletedTasks returns the total number of tasks that have completed their exection either successfully
|
||||||
|
// or with panic since the pool was created
|
||||||
|
func (p *WorkerPool) CompletedTasks() uint64 {
|
||||||
|
return p.SuccessfulTasks() + p.FailedTasks()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stopped returns true if the pool has been stopped and is no longer accepting tasks, and false otherwise.
|
||||||
|
func (p *WorkerPool) Stopped() bool {
|
||||||
|
return atomic.LoadInt32(&p.stopped) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit sends a task to this worker pool for execution. If the queue is full,
|
||||||
|
// it will wait until the task is dispatched to a worker goroutine.
|
||||||
|
func (p *WorkerPool) Submit(task func()) {
|
||||||
|
p.submit(task, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrySubmit attempts to send a task to this worker pool for execution. If the queue is full,
|
||||||
|
// it will not wait for a worker to become idle. It returns true if it was able to dispatch
|
||||||
|
// the task and false otherwise.
|
||||||
|
func (p *WorkerPool) TrySubmit(task func()) bool {
|
||||||
|
return p.submit(task, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WorkerPool) submit(task func(), mustSubmit bool) (submitted bool) {
|
||||||
|
if task == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Stopped() {
|
||||||
|
// Pool is stopped and caller must submit the task
|
||||||
|
if mustSubmit {
|
||||||
|
panic(ErrSubmitOnStoppedPool)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment submitted and waiting task counters as soon as we receive a task
|
||||||
|
atomic.AddUint64(&p.submittedTaskCount, 1)
|
||||||
|
atomic.AddUint64(&p.waitingTaskCount, 1)
|
||||||
|
p.tasksWaitGroup.Add(1)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if !submitted {
|
||||||
|
// Task was not sumitted to the pool, decrement submitted and waiting task counters
|
||||||
|
atomic.AddUint64(&p.submittedTaskCount, ^uint64(0))
|
||||||
|
atomic.AddUint64(&p.waitingTaskCount, ^uint64(0))
|
||||||
|
p.tasksWaitGroup.Done()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Start a worker as long as we haven't reached the limit
|
||||||
|
if submitted = p.maybeStartWorker(task); submitted {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mustSubmit {
|
||||||
|
// Attempt to dispatch to an idle worker without blocking
|
||||||
|
select {
|
||||||
|
case p.tasks <- task:
|
||||||
|
submitted = true
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
// Channel is full and can't wait for an idle worker, so need to exit
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit the task to the tasks channel and wait for it to be picked up by a worker
|
||||||
|
p.tasks <- task
|
||||||
|
submitted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitAndWait sends a task to this worker pool for execution and waits for it to complete
|
||||||
|
// before returning
|
||||||
|
func (p *WorkerPool) SubmitAndWait(task func()) {
|
||||||
|
if task == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
p.Submit(func() {
|
||||||
|
defer close(done)
|
||||||
|
task()
|
||||||
|
})
|
||||||
|
<-done
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitBefore attempts to send a task for execution to this worker pool but aborts it
|
||||||
|
// if the task did not start before the given deadline.
|
||||||
|
func (p *WorkerPool) SubmitBefore(task func(), deadline time.Duration) {
|
||||||
|
if task == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
timer := time.NewTimer(deadline)
|
||||||
|
p.Submit(func() {
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
// Deadline was reached, abort the task
|
||||||
|
default:
|
||||||
|
// Deadline not reached, execute the task
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
|
task()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop causes this pool to stop accepting new tasks and signals all workers to exit.
|
||||||
|
// Tasks being executed by workers will continue until completion (unless the process is terminated).
|
||||||
|
// Tasks in the queue will not be executed.
|
||||||
|
// This method returns a context object that is cancelled when the pool has stopped completely.
|
||||||
|
func (p *WorkerPool) Stop() context.Context {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
go func() {
|
||||||
|
p.stop(false)
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopAndWait causes this pool to stop accepting new tasks and then waits for all tasks in the queue
|
||||||
|
// to complete before returning.
|
||||||
|
func (p *WorkerPool) StopAndWait() {
|
||||||
|
p.stop(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopAndWaitFor stops this pool and waits until either all tasks in the queue are completed
|
||||||
|
// or the given deadline is reached, whichever comes first.
|
||||||
|
func (p *WorkerPool) StopAndWaitFor(deadline time.Duration) {
|
||||||
|
|
||||||
|
// Launch goroutine to detect when worker pool has stopped gracefully
|
||||||
|
workersDone := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
p.stop(true)
|
||||||
|
workersDone <- struct{}{}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait until either all workers have exited or the deadline is reached
|
||||||
|
select {
|
||||||
|
case <-workersDone:
|
||||||
|
return
|
||||||
|
case <-time.After(deadline):
|
||||||
|
p.contextCancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WorkerPool) stop(waitForQueuedTasksToComplete bool) {
|
||||||
|
// Mark pool as stopped
|
||||||
|
atomic.StoreInt32(&p.stopped, 1)
|
||||||
|
|
||||||
|
if waitForQueuedTasksToComplete {
|
||||||
|
// Wait for all queued tasks to complete
|
||||||
|
p.tasksWaitGroup.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset worker count
|
||||||
|
p.resetWorkerCount()
|
||||||
|
|
||||||
|
// Terminate all workers & purger goroutine
|
||||||
|
p.contextCancel()
|
||||||
|
|
||||||
|
// Wait for all workers & purger goroutine to exit
|
||||||
|
p.workersWaitGroup.Wait()
|
||||||
|
|
||||||
|
// close tasks channel (only once, in case multiple concurrent calls to StopAndWait are made)
|
||||||
|
p.tasksCloseOnce.Do(func() {
|
||||||
|
close(p.tasks)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// purge represents the work done by the purger goroutine
|
||||||
|
func (p *WorkerPool) purge() {
|
||||||
|
defer p.workersWaitGroup.Done()
|
||||||
|
|
||||||
|
idleTicker := time.NewTicker(p.idleTimeout)
|
||||||
|
defer idleTicker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
// Timed out waiting for any activity to happen, attempt to stop an idle worker
|
||||||
|
case <-idleTicker.C:
|
||||||
|
p.maybeStopIdleWorker()
|
||||||
|
// Pool context was cancelled, exit
|
||||||
|
case <-p.context.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybeStopIdleWorker attempts to stop an idle worker by sending it a nil task
|
||||||
|
func (p *WorkerPool) maybeStopIdleWorker() bool {
|
||||||
|
|
||||||
|
if decremented := p.decrementWorkerCount(); !decremented {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a nil task to stop an idle worker
|
||||||
|
p.tasks <- nil
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybeStartWorker attempts to create a new worker goroutine to run the given task.
|
||||||
|
// If the worker pool has reached the maximum number of workers or there are idle workers,
|
||||||
|
// it will not create a new one.
|
||||||
|
func (p *WorkerPool) maybeStartWorker(firstTask func()) bool {
|
||||||
|
|
||||||
|
if incremented := p.incrementWorkerCount(); !incremented {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if firstTask == nil {
|
||||||
|
// Worker starts idle
|
||||||
|
atomic.AddInt32(&p.idleWorkerCount, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Launch worker goroutine
|
||||||
|
go worker(p.context, &p.workersWaitGroup, firstTask, p.tasks, p.executeTask, &p.tasksWaitGroup)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// executeTask executes the given task and updates task-related counters
|
||||||
|
func (p *WorkerPool) executeTask(task func(), isFirstTask bool) {
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if panic := recover(); panic != nil {
|
||||||
|
// Increment failed task count
|
||||||
|
atomic.AddUint64(&p.failedTaskCount, 1)
|
||||||
|
|
||||||
|
// Invoke panic handler
|
||||||
|
p.panicHandler(panic)
|
||||||
|
|
||||||
|
// Increment idle count
|
||||||
|
atomic.AddInt32(&p.idleWorkerCount, 1)
|
||||||
|
}
|
||||||
|
p.tasksWaitGroup.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Decrement idle count
|
||||||
|
if !isFirstTask {
|
||||||
|
atomic.AddInt32(&p.idleWorkerCount, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrement waiting task count
|
||||||
|
atomic.AddUint64(&p.waitingTaskCount, ^uint64(0))
|
||||||
|
|
||||||
|
// Execute task
|
||||||
|
task()
|
||||||
|
|
||||||
|
// Increment successful task count
|
||||||
|
atomic.AddUint64(&p.successfulTaskCount, 1)
|
||||||
|
|
||||||
|
// Increment idle count
|
||||||
|
atomic.AddInt32(&p.idleWorkerCount, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WorkerPool) incrementWorkerCount() bool {
|
||||||
|
|
||||||
|
p.mutex.Lock()
|
||||||
|
defer p.mutex.Unlock()
|
||||||
|
|
||||||
|
runningWorkerCount := p.RunningWorkers()
|
||||||
|
|
||||||
|
// Reached max workers, do not create a new one
|
||||||
|
if runningWorkerCount >= p.maxWorkers {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Idle workers available, do not create a new one
|
||||||
|
if runningWorkerCount >= p.minWorkers && runningWorkerCount > 0 && p.IdleWorkers() > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the resizing strategy to determine if we should create more workers
|
||||||
|
if resize := p.strategy.Resize(runningWorkerCount, p.minWorkers, p.maxWorkers); !resize {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment worker count
|
||||||
|
atomic.AddInt32(&p.workerCount, 1)
|
||||||
|
|
||||||
|
// Increment wait group
|
||||||
|
p.workersWaitGroup.Add(1)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WorkerPool) decrementWorkerCount() bool {
|
||||||
|
|
||||||
|
p.mutex.Lock()
|
||||||
|
defer p.mutex.Unlock()
|
||||||
|
|
||||||
|
if p.IdleWorkers() <= 0 || p.RunningWorkers() <= p.minWorkers || p.Stopped() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrement worker count
|
||||||
|
atomic.AddInt32(&p.workerCount, -1)
|
||||||
|
|
||||||
|
// Decrement idle count
|
||||||
|
atomic.AddInt32(&p.idleWorkerCount, -1)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WorkerPool) resetWorkerCount() {
|
||||||
|
|
||||||
|
p.mutex.Lock()
|
||||||
|
defer p.mutex.Unlock()
|
||||||
|
|
||||||
|
// Reset worker count
|
||||||
|
atomic.StoreInt32(&p.workerCount, 0)
|
||||||
|
|
||||||
|
// Reset idle count
|
||||||
|
atomic.StoreInt32(&p.idleWorkerCount, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group creates a new task group
|
||||||
|
func (p *WorkerPool) Group() *TaskGroup {
|
||||||
|
return &TaskGroup{
|
||||||
|
pool: p,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupContext creates a new task group and an associated Context derived from ctx.
|
||||||
|
//
|
||||||
|
// The derived Context is canceled the first time a function submitted to the group
|
||||||
|
// returns a non-nil error or the first time Wait returns, whichever occurs first.
|
||||||
|
func (p *WorkerPool) GroupContext(ctx context.Context) (*TaskGroupWithContext, context.Context) {
|
||||||
|
|
||||||
|
if ctx == nil {
|
||||||
|
panic("a non-nil context needs to be specified when using GroupContext")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
return &TaskGroupWithContext{
|
||||||
|
TaskGroup: TaskGroup{
|
||||||
|
pool: p,
|
||||||
|
},
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
}, ctx
|
||||||
|
}
|
||||||
55
vendor/github.com/alitto/pond/resizer.go
generated
vendored
Normal file
55
vendor/github.com/alitto/pond/resizer.go
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package pond
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var maxProcs = runtime.GOMAXPROCS(0)
|
||||||
|
|
||||||
|
// Preset pool resizing strategies
|
||||||
|
var (
|
||||||
|
// Eager maximizes responsiveness at the expense of higher resource usage,
|
||||||
|
// which can reduce throughput under certain conditions.
|
||||||
|
// This strategy is meant for worker pools that will operate at a small percentage of their capacity
|
||||||
|
// most of the time and may occasionally receive bursts of tasks. It's the default strategy.
|
||||||
|
Eager = func() ResizingStrategy { return RatedResizer(1) }
|
||||||
|
// Balanced tries to find a balance between responsiveness and throughput.
|
||||||
|
// It's suitable for general purpose worker pools or those
|
||||||
|
// that will operate close to 50% of their capacity most of the time.
|
||||||
|
Balanced = func() ResizingStrategy { return RatedResizer(maxProcs / 2) }
|
||||||
|
// Lazy maximizes throughput at the expense of responsiveness.
|
||||||
|
// This strategy is meant for worker pools that will operate close to their max. capacity most of the time.
|
||||||
|
Lazy = func() ResizingStrategy { return RatedResizer(maxProcs) }
|
||||||
|
)
|
||||||
|
|
||||||
|
// ratedResizer implements a rated resizing strategy
|
||||||
|
type ratedResizer struct {
|
||||||
|
rate uint64
|
||||||
|
hits uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// RatedResizer creates a resizing strategy which can be configured
|
||||||
|
// to create workers at a specific rate when the pool has no idle workers.
|
||||||
|
// rate: determines the number of tasks to receive before creating an extra worker.
|
||||||
|
// A value of 3 can be interpreted as: "Create a new worker every 3 tasks".
|
||||||
|
func RatedResizer(rate int) ResizingStrategy {
|
||||||
|
|
||||||
|
if rate < 1 {
|
||||||
|
rate = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ratedResizer{
|
||||||
|
rate: uint64(rate),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ratedResizer) Resize(runningWorkers, minWorkers, maxWorkers int) bool {
|
||||||
|
|
||||||
|
if r.rate == 1 || runningWorkers == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
r.hits++
|
||||||
|
|
||||||
|
return r.hits%r.rate == 1
|
||||||
|
}
|
||||||
62
vendor/github.com/alitto/pond/worker.go
generated
vendored
Normal file
62
vendor/github.com/alitto/pond/worker.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package pond
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// worker represents a worker goroutine
|
||||||
|
func worker(context context.Context, waitGroup *sync.WaitGroup, firstTask func(), tasks <-chan func(), taskExecutor func(func(), bool), taskWaitGroup *sync.WaitGroup) {
|
||||||
|
|
||||||
|
// If provided, execute the first task immediately, before listening to the tasks channel
|
||||||
|
if firstTask != nil {
|
||||||
|
taskExecutor(firstTask, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
waitGroup.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-context.Done():
|
||||||
|
// Pool context was cancelled, empty tasks channel and exit
|
||||||
|
drainTasks(tasks, taskWaitGroup)
|
||||||
|
return
|
||||||
|
case task, ok := <-tasks:
|
||||||
|
// Prioritize context.Done statement (https://stackoverflow.com/questions/46200343/force-priority-of-go-select-statement)
|
||||||
|
select {
|
||||||
|
case <-context.Done():
|
||||||
|
if task != nil && ok {
|
||||||
|
// We have received a task, ignore it
|
||||||
|
taskWaitGroup.Done()
|
||||||
|
}
|
||||||
|
// Pool context was cancelled, empty tasks channel and exit
|
||||||
|
drainTasks(tasks, taskWaitGroup)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
if task == nil || !ok {
|
||||||
|
// We have received a signal to exit
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have received a task, execute it
|
||||||
|
taskExecutor(task, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// drainPendingTasks discards queued tasks and decrements the corresponding wait group
|
||||||
|
func drainTasks(tasks <-chan func(), tasksWaitGroup *sync.WaitGroup) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case task, ok := <-tasks:
|
||||||
|
if task != nil && ok {
|
||||||
|
tasksWaitGroup.Done()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
vendor/github.com/go-ole/go-ole/SECURITY.md
generated
vendored
13
vendor/github.com/go-ole/go-ole/SECURITY.md
generated
vendored
@@ -1,13 +0,0 @@
|
|||||||
# Security Policy
|
|
||||||
|
|
||||||
## Supported Versions
|
|
||||||
|
|
||||||
Security updates are applied only to the latest release.
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
|
|
||||||
|
|
||||||
Please disclose it at [security advisory](https://github.com/go-ole/go-ole/security/advisories/new).
|
|
||||||
|
|
||||||
This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure.
|
|
||||||
56
vendor/github.com/go-ole/go-ole/appveyor.yml
generated
vendored
56
vendor/github.com/go-ole/go-ole/appveyor.yml
generated
vendored
@@ -6,29 +6,7 @@
|
|||||||
|
|
||||||
version: "1.3.0.{build}-alpha-{branch}"
|
version: "1.3.0.{build}-alpha-{branch}"
|
||||||
|
|
||||||
os: Visual Studio 2019
|
os: Windows Server 2012 R2
|
||||||
|
|
||||||
build: off
|
|
||||||
|
|
||||||
skip_tags: true
|
|
||||||
|
|
||||||
clone_folder: c:\gopath\src\github.com\go-ole\go-ole
|
|
||||||
|
|
||||||
environment:
|
|
||||||
GOPATH: c:\gopath
|
|
||||||
GOROOT: c:\go
|
|
||||||
DOWNLOADPLATFORM: "x64"
|
|
||||||
|
|
||||||
before_test:
|
|
||||||
# - Download COM Server
|
|
||||||
- ps: Start-FileDownload "https://github.com/go-ole/test-com-server/releases/download/v1.0.2/test-com-server-${env:DOWNLOADPLATFORM}.zip"
|
|
||||||
- 7z e test-com-server-%DOWNLOADPLATFORM%.zip -oc:\gopath\src\github.com\go-ole\go-ole > NUL
|
|
||||||
- c:\gopath\src\github.com\go-ole\go-ole\build\register-assembly.bat
|
|
||||||
|
|
||||||
test_script:
|
|
||||||
- go test -v -cover ./...
|
|
||||||
# go vet has false positives on unsafe.Pointer with windows/sys. Disabling since it is recommended to use go test instead.
|
|
||||||
# - go vet ./...
|
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
@@ -37,19 +15,26 @@ branches:
|
|||||||
- v1.1
|
- v1.1
|
||||||
- v1.0
|
- v1.0
|
||||||
|
|
||||||
matrix:
|
skip_tags: true
|
||||||
allow_failures:
|
|
||||||
- environment:
|
clone_folder: c:\gopath\src\github.com\go-ole\go-ole
|
||||||
GOROOT: C:\go-x86
|
|
||||||
DOWNLOADPLATFORM: "x86"
|
environment:
|
||||||
- environment:
|
GOPATH: c:\gopath
|
||||||
GOROOT: C:\go118
|
matrix:
|
||||||
DOWNLOADPLATFORM: "x64"
|
- GOARCH: amd64
|
||||||
- environment:
|
GOVERSION: 1.5
|
||||||
GOROOT: C:\go118-x86
|
GOROOT: c:\go
|
||||||
DOWNLOADPLATFORM: "x86"
|
DOWNLOADPLATFORM: "x64"
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
- choco install mingw
|
||||||
|
- SET PATH=c:\tools\mingw64\bin;%PATH%
|
||||||
|
# - Download COM Server
|
||||||
|
- ps: Start-FileDownload "https://github.com/go-ole/test-com-server/releases/download/v1.0.2/test-com-server-${env:DOWNLOADPLATFORM}.zip"
|
||||||
|
- 7z e test-com-server-%DOWNLOADPLATFORM%.zip -oc:\gopath\src\github.com\go-ole\go-ole > NUL
|
||||||
|
- c:\gopath\src\github.com\go-ole\go-ole\build\register-assembly.bat
|
||||||
|
# - set
|
||||||
- go version
|
- go version
|
||||||
- go env
|
- go env
|
||||||
- go get -u golang.org/x/tools/cmd/cover
|
- go get -u golang.org/x/tools/cmd/cover
|
||||||
@@ -60,9 +45,10 @@ build_script:
|
|||||||
- cd c:\gopath\src\github.com\go-ole\go-ole
|
- cd c:\gopath\src\github.com\go-ole\go-ole
|
||||||
- go get -v -t ./...
|
- go get -v -t ./...
|
||||||
- go build
|
- go build
|
||||||
|
- go test -v -cover ./...
|
||||||
|
|
||||||
# disable automatic tests
|
# disable automatic tests
|
||||||
test: on
|
test: off
|
||||||
|
|
||||||
# disable deployment
|
# disable deployment
|
||||||
deploy: off
|
deploy: off
|
||||||
|
|||||||
42
vendor/github.com/go-ole/go-ole/com.go
generated
vendored
42
vendor/github.com/go-ole/go-ole/com.go
generated
vendored
@@ -11,7 +11,6 @@ import (
|
|||||||
var (
|
var (
|
||||||
procCoInitialize = modole32.NewProc("CoInitialize")
|
procCoInitialize = modole32.NewProc("CoInitialize")
|
||||||
procCoInitializeEx = modole32.NewProc("CoInitializeEx")
|
procCoInitializeEx = modole32.NewProc("CoInitializeEx")
|
||||||
procCoInitializeSecurity = modole32.NewProc("CoInitializeSecurity")
|
|
||||||
procCoUninitialize = modole32.NewProc("CoUninitialize")
|
procCoUninitialize = modole32.NewProc("CoUninitialize")
|
||||||
procCoCreateInstance = modole32.NewProc("CoCreateInstance")
|
procCoCreateInstance = modole32.NewProc("CoCreateInstance")
|
||||||
procCoTaskMemFree = modole32.NewProc("CoTaskMemFree")
|
procCoTaskMemFree = modole32.NewProc("CoTaskMemFree")
|
||||||
@@ -38,9 +37,6 @@ var (
|
|||||||
procDispatchMessageW = moduser32.NewProc("DispatchMessageW")
|
procDispatchMessageW = moduser32.NewProc("DispatchMessageW")
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is to enable calling COM Security initialization multiple times
|
|
||||||
var bSecurityInit bool = false
|
|
||||||
|
|
||||||
// coInitialize initializes COM library on current thread.
|
// coInitialize initializes COM library on current thread.
|
||||||
//
|
//
|
||||||
// MSDN documentation suggests that this function should not be called. Call
|
// MSDN documentation suggests that this function should not be called. Call
|
||||||
@@ -72,35 +68,6 @@ func coInitializeEx(coinit uint32) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// coInitializeSecurity: Registers security and sets the default security values
|
|
||||||
// for the process.
|
|
||||||
func coInitializeSecurity(cAuthSvc int32,
|
|
||||||
dwAuthnLevel uint32,
|
|
||||||
dwImpLevel uint32,
|
|
||||||
dwCapabilities uint32) (err error) {
|
|
||||||
// Check COM Security initialization has done previously
|
|
||||||
if !bSecurityInit {
|
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializesecurity
|
|
||||||
hr, _, _ := procCoInitializeSecurity.Call(
|
|
||||||
uintptr(0), // Allow *all* VSS writers to communicate back!
|
|
||||||
uintptr(cAuthSvc), // Default COM authentication service
|
|
||||||
uintptr(0), // Default COM authorization service
|
|
||||||
uintptr(0), // Reserved parameter
|
|
||||||
uintptr(dwAuthnLevel), // Strongest COM authentication level
|
|
||||||
uintptr(dwImpLevel), // Minimal impersonation abilities
|
|
||||||
uintptr(0), // Default COM authentication settings
|
|
||||||
uintptr(dwCapabilities), // Cloaking
|
|
||||||
uintptr(0)) // eserved parameter
|
|
||||||
if hr != 0 {
|
|
||||||
err = NewError(hr)
|
|
||||||
} else {
|
|
||||||
// COM Security initialization done make global flag true.
|
|
||||||
bSecurityInit = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// CoInitialize initializes COM library on current thread.
|
// CoInitialize initializes COM library on current thread.
|
||||||
//
|
//
|
||||||
// MSDN documentation suggests that this function should not be called. Call
|
// MSDN documentation suggests that this function should not be called. Call
|
||||||
@@ -129,15 +96,6 @@ func CoUninitialize() {
|
|||||||
procCoUninitialize.Call()
|
procCoUninitialize.Call()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CoInitializeSecurity: Registers security and sets the default security values
|
|
||||||
// for the process.
|
|
||||||
func CoInitializeSecurity(cAuthSvc int32,
|
|
||||||
dwAuthnLevel uint32,
|
|
||||||
dwImpLevel uint32,
|
|
||||||
dwCapabilities uint32) (err error) {
|
|
||||||
return coInitializeSecurity(cAuthSvc, dwAuthnLevel, dwImpLevel, dwCapabilities)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CoTaskMemFree frees memory pointer.
|
// CoTaskMemFree frees memory pointer.
|
||||||
func CoTaskMemFree(memptr uintptr) {
|
func CoTaskMemFree(memptr uintptr) {
|
||||||
procCoTaskMemFree.Call(memptr)
|
procCoTaskMemFree.Call(memptr)
|
||||||
|
|||||||
3
vendor/github.com/go-ole/go-ole/idispatch_windows.go
generated
vendored
3
vendor/github.com/go-ole/go-ole/idispatch_windows.go
generated
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build windows
|
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package ole
|
package ole
|
||||||
@@ -93,7 +92,7 @@ func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}
|
|||||||
case int8:
|
case int8:
|
||||||
vargs[n] = NewVariant(VT_I1, int64(v.(int8)))
|
vargs[n] = NewVariant(VT_I1, int64(v.(int8)))
|
||||||
case *int8:
|
case *int8:
|
||||||
vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int8)))))
|
vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint8)))))
|
||||||
case int16:
|
case int16:
|
||||||
vargs[n] = NewVariant(VT_I2, int64(v.(int16)))
|
vargs[n] = NewVariant(VT_I2, int64(v.(int16)))
|
||||||
case *int16:
|
case *int16:
|
||||||
|
|||||||
2
vendor/github.com/go-ole/go-ole/variant.go
generated
vendored
2
vendor/github.com/go-ole/go-ole/variant.go
generated
vendored
@@ -99,7 +99,7 @@ func (v *VARIANT) Value() interface{} {
|
|||||||
case VT_DISPATCH:
|
case VT_DISPATCH:
|
||||||
return v.ToIDispatch()
|
return v.ToIDispatch()
|
||||||
case VT_BOOL:
|
case VT_BOOL:
|
||||||
return (v.Val & 0xffff) != 0
|
return v.Val != 0
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
27
vendor/github.com/kardianos/osext/LICENSE
generated
vendored
Normal file
27
vendor/github.com/kardianos/osext/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2012 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.
|
||||||
21
vendor/github.com/kardianos/osext/README.md
generated
vendored
Normal file
21
vendor/github.com/kardianos/osext/README.md
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
### Extensions to the "os" package.
|
||||||
|
|
||||||
|
[](https://godoc.org/github.com/kardianos/osext)
|
||||||
|
|
||||||
|
## Find the current Executable and ExecutableFolder.
|
||||||
|
|
||||||
|
As of go1.8 the Executable function may be found in `os`. The Executable function
|
||||||
|
in the std lib `os` package is used if available.
|
||||||
|
|
||||||
|
There is sometimes utility in finding the current executable file
|
||||||
|
that is running. This can be used for upgrading the current executable
|
||||||
|
or finding resources located relative to the executable file. Both
|
||||||
|
working directory and the os.Args[0] value are arbitrary and cannot
|
||||||
|
be relied on; os.Args[0] can be "faked".
|
||||||
|
|
||||||
|
Multi-platform and supports:
|
||||||
|
* Linux
|
||||||
|
* OS X
|
||||||
|
* Windows
|
||||||
|
* Plan 9
|
||||||
|
* BSDs.
|
||||||
33
vendor/github.com/kardianos/osext/osext.go
generated
vendored
Normal file
33
vendor/github.com/kardianos/osext/osext.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// Extensions to the standard "os" package.
|
||||||
|
package osext // import "github.com/kardianos/osext"
|
||||||
|
|
||||||
|
import "path/filepath"
|
||||||
|
|
||||||
|
var cx, ce = executableClean()
|
||||||
|
|
||||||
|
func executableClean() (string, error) {
|
||||||
|
p, err := executable()
|
||||||
|
return filepath.Clean(p), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Executable returns an absolute path that can be used to
|
||||||
|
// re-invoke the current program.
|
||||||
|
// It may not be valid after the current program exits.
|
||||||
|
func Executable() (string, error) {
|
||||||
|
return cx, ce
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns same path as Executable, returns just the folder
|
||||||
|
// path. Excludes the executable name and any trailing slash.
|
||||||
|
func ExecutableFolder() (string, error) {
|
||||||
|
p, err := Executable()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Dir(p), nil
|
||||||
|
}
|
||||||
9
vendor/github.com/kardianos/osext/osext_go18.go
generated
vendored
Normal file
9
vendor/github.com/kardianos/osext/osext_go18.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
//+build go1.8,!openbsd
|
||||||
|
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func executable() (string, error) {
|
||||||
|
return os.Executable()
|
||||||
|
}
|
||||||
22
vendor/github.com/kardianos/osext/osext_plan9.go
generated
vendored
Normal file
22
vendor/github.com/kardianos/osext/osext_plan9.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
//+build !go1.8
|
||||||
|
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func executable() (string, error) {
|
||||||
|
f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return syscall.Fd2path(int(f.Fd()))
|
||||||
|
}
|
||||||
36
vendor/github.com/kardianos/osext/osext_procfs.go
generated
vendored
Normal file
36
vendor/github.com/kardianos/osext/osext_procfs.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// +build !go1.8,android !go1.8,linux !go1.8,netbsd !go1.8,solaris !go1.8,dragonfly
|
||||||
|
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func executable() (string, error) {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "linux", "android":
|
||||||
|
const deletedTag = " (deleted)"
|
||||||
|
execpath, err := os.Readlink("/proc/self/exe")
|
||||||
|
if err != nil {
|
||||||
|
return execpath, err
|
||||||
|
}
|
||||||
|
execpath = strings.TrimSuffix(execpath, deletedTag)
|
||||||
|
execpath = strings.TrimPrefix(execpath, deletedTag)
|
||||||
|
return execpath, nil
|
||||||
|
case "netbsd":
|
||||||
|
return os.Readlink("/proc/curproc/exe")
|
||||||
|
case "dragonfly":
|
||||||
|
return os.Readlink("/proc/curproc/file")
|
||||||
|
case "solaris":
|
||||||
|
return os.Readlink(fmt.Sprintf("/proc/%d/path/a.out", os.Getpid()))
|
||||||
|
}
|
||||||
|
return "", errors.New("ExecPath not implemented for " + runtime.GOOS)
|
||||||
|
}
|
||||||
126
vendor/github.com/kardianos/osext/osext_sysctl.go
generated
vendored
Normal file
126
vendor/github.com/kardianos/osext/osext_sysctl.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// +build !go1.8,darwin !go1.8,freebsd openbsd
|
||||||
|
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var initCwd, initCwdErr = os.Getwd()
|
||||||
|
|
||||||
|
func executable() (string, error) {
|
||||||
|
var mib [4]int32
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "freebsd":
|
||||||
|
mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
|
||||||
|
case "darwin":
|
||||||
|
mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1}
|
||||||
|
case "openbsd":
|
||||||
|
mib = [4]int32{1 /* CTL_KERN */, 55 /* KERN_PROC_ARGS */, int32(os.Getpid()), 1 /* KERN_PROC_ARGV */}
|
||||||
|
}
|
||||||
|
|
||||||
|
n := uintptr(0)
|
||||||
|
// Get length.
|
||||||
|
_, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||||
|
if errNum != 0 {
|
||||||
|
return "", errNum
|
||||||
|
}
|
||||||
|
if n == 0 { // This shouldn't happen.
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
buf := make([]byte, n)
|
||||||
|
_, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||||
|
if errNum != 0 {
|
||||||
|
return "", errNum
|
||||||
|
}
|
||||||
|
if n == 0 { // This shouldn't happen.
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var execPath string
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "openbsd":
|
||||||
|
// buf now contains **argv, with pointers to each of the C-style
|
||||||
|
// NULL terminated arguments.
|
||||||
|
var args []string
|
||||||
|
argv := uintptr(unsafe.Pointer(&buf[0]))
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
argp := *(**[1 << 20]byte)(unsafe.Pointer(argv))
|
||||||
|
if argp == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for i := 0; uintptr(i) < n; i++ {
|
||||||
|
// we don't want the full arguments list
|
||||||
|
if string(argp[i]) == " " {
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
if argp[i] != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
args = append(args, string(argp[:i]))
|
||||||
|
n -= uintptr(i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if n < unsafe.Sizeof(argv) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
argv += unsafe.Sizeof(argv)
|
||||||
|
n -= unsafe.Sizeof(argv)
|
||||||
|
}
|
||||||
|
execPath = args[0]
|
||||||
|
// There is no canonical way to get an executable path on
|
||||||
|
// OpenBSD, so check PATH in case we are called directly
|
||||||
|
if execPath[0] != '/' && execPath[0] != '.' {
|
||||||
|
execIsInPath, err := exec.LookPath(execPath)
|
||||||
|
if err == nil {
|
||||||
|
execPath = execIsInPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
for i, v := range buf {
|
||||||
|
if v == 0 {
|
||||||
|
buf = buf[:i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
execPath = string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
// execPath will not be empty due to above checks.
|
||||||
|
// Try to get the absolute path if the execPath is not rooted.
|
||||||
|
if execPath[0] != '/' {
|
||||||
|
execPath, err = getAbs(execPath)
|
||||||
|
if err != nil {
|
||||||
|
return execPath, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// For darwin KERN_PROCARGS may return the path to a symlink rather than the
|
||||||
|
// actual executable.
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
if execPath, err = filepath.EvalSymlinks(execPath); err != nil {
|
||||||
|
return execPath, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return execPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAbs(execPath string) (string, error) {
|
||||||
|
if initCwdErr != nil {
|
||||||
|
return execPath, initCwdErr
|
||||||
|
}
|
||||||
|
// The execPath may begin with a "../" or a "./" so clean it first.
|
||||||
|
// Join the two paths, trailing and starting slashes undetermined, so use
|
||||||
|
// the generic Join function.
|
||||||
|
return filepath.Join(initCwd, filepath.Clean(execPath)), nil
|
||||||
|
}
|
||||||
36
vendor/github.com/kardianos/osext/osext_windows.go
generated
vendored
Normal file
36
vendor/github.com/kardianos/osext/osext_windows.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
//+build !go1.8
|
||||||
|
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
kernel = syscall.MustLoadDLL("kernel32.dll")
|
||||||
|
getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW")
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetModuleFileName() with hModule = NULL
|
||||||
|
func executable() (exePath string, err error) {
|
||||||
|
return getModuleFileName()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getModuleFileName() (string, error) {
|
||||||
|
var n uint32
|
||||||
|
b := make([]uint16, syscall.MAX_PATH)
|
||||||
|
size := uint32(len(b))
|
||||||
|
|
||||||
|
r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
|
||||||
|
n = uint32(r0)
|
||||||
|
if n == 0 {
|
||||||
|
return "", e1
|
||||||
|
}
|
||||||
|
return string(utf16.Decode(b[0:n])), nil
|
||||||
|
}
|
||||||
11
vendor/github.com/lufia/plan9stats/README.md
generated
vendored
11
vendor/github.com/lufia/plan9stats/README.md
generated
vendored
@@ -1,13 +1,2 @@
|
|||||||
# plan9stats
|
# plan9stats
|
||||||
A module for retrieving statistics of Plan 9
|
A module for retrieving statistics of Plan 9
|
||||||
|
|
||||||
[![GoDev][godev-image]][godev-url]
|
|
||||||
[![Actions Status][actions-image]][actions-url]
|
|
||||||
[![Coverage Status][coveralls-image]][coveralls-url]
|
|
||||||
|
|
||||||
[godev-image]: https://pkg.go.dev/badge/github.com/lufia/plan9stats
|
|
||||||
[godev-url]: https://pkg.go.dev/github.com/lufia/plan9stats
|
|
||||||
[actions-image]: https://github.com/lufia/plan9stats/workflows/Test/badge.svg?branch=main
|
|
||||||
[actions-url]: https://github.com/lufia/plan9stats/actions?workflow=Test
|
|
||||||
[coveralls-image]: https://coveralls.io/repos/github/lufia/plan9stats/badge.svg
|
|
||||||
[coveralls-url]: https://coveralls.io/github/lufia/plan9stats
|
|
||||||
|
|||||||
5
vendor/github.com/lufia/plan9stats/cpu.go
generated
vendored
5
vendor/github.com/lufia/plan9stats/cpu.go
generated
vendored
@@ -178,12 +178,9 @@ func ReadCPUStats(ctx context.Context, opts ...Option) (*CPUStats, error) {
|
|||||||
var up uint32parser
|
var up uint32parser
|
||||||
pids := make([]uint32, len(names))
|
pids := make([]uint32, len(names))
|
||||||
for i, s := range names {
|
for i, s := range names {
|
||||||
if s == "trace" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
pids[i] = up.Parse(s)
|
pids[i] = up.Parse(s)
|
||||||
}
|
}
|
||||||
if err := up.err; err != nil {
|
if up.err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sort.Slice(pids, func(i, j int) bool {
|
sort.Slice(pids, func(i, j int) bool {
|
||||||
|
|||||||
116
vendor/github.com/lufia/plan9stats/disk.go
generated
vendored
116
vendor/github.com/lufia/plan9stats/disk.go
generated
vendored
@@ -1,116 +0,0 @@
|
|||||||
package stats
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Storage represents /dev/sdXX/ctl.
|
|
||||||
type Storage struct {
|
|
||||||
Name string
|
|
||||||
Model string
|
|
||||||
Capacity int64
|
|
||||||
Partitions []*Partition
|
|
||||||
}
|
|
||||||
|
|
||||||
// Partition represents a part of /dev/sdXX/ctl.
|
|
||||||
type Partition struct {
|
|
||||||
Name string
|
|
||||||
Start uint64
|
|
||||||
End uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadStorages(ctx context.Context, opts ...Option) ([]*Storage, error) {
|
|
||||||
cfg := newConfig(opts...)
|
|
||||||
sdctl := filepath.Join(cfg.rootdir, "/dev/sdctl")
|
|
||||||
f, err := os.Open(sdctl)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
var a []*Storage
|
|
||||||
scanner := bufio.NewScanner(f)
|
|
||||||
for scanner.Scan() {
|
|
||||||
fields := bytes.Split(scanner.Bytes(), delim)
|
|
||||||
if len(fields) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
exp := string(fields[0]) + "*"
|
|
||||||
if !strings.HasPrefix(exp, "sd") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dir := filepath.Join(cfg.rootdir, "/dev", exp)
|
|
||||||
m, err := filepath.Glob(dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, dir := range m {
|
|
||||||
s, err := readStorage(dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
a = append(a, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return a, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readStorage(dir string) (*Storage, error) {
|
|
||||||
ctl := filepath.Join(dir, "ctl")
|
|
||||||
f, err := os.Open(ctl)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
var s Storage
|
|
||||||
s.Name = filepath.Base(dir)
|
|
||||||
scanner := bufio.NewScanner(f)
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Bytes()
|
|
||||||
switch {
|
|
||||||
case bytes.HasPrefix(line, []byte("inquiry ")):
|
|
||||||
s.Model = string(bytes.TrimSpace(line[7:]))
|
|
||||||
case bytes.HasPrefix(line, []byte("geometry ")):
|
|
||||||
fields := bytes.Split(line, delim)
|
|
||||||
if len(fields) < 3 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var p intParser
|
|
||||||
sec := p.ParseInt64(string(fields[1]), 10)
|
|
||||||
size := p.ParseInt64(string(fields[2]), 10)
|
|
||||||
if err := p.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
s.Capacity = sec * size
|
|
||||||
case bytes.HasPrefix(line, []byte("part ")):
|
|
||||||
fields := bytes.Split(line, delim)
|
|
||||||
if len(fields) < 4 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var p intParser
|
|
||||||
start := p.ParseUint64(string(fields[2]), 10)
|
|
||||||
end := p.ParseUint64(string(fields[3]), 10)
|
|
||||||
if err := p.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
s.Partitions = append(s.Partitions, &Partition{
|
|
||||||
Name: string(fields[1]),
|
|
||||||
Start: start,
|
|
||||||
End: end,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &s, nil
|
|
||||||
}
|
|
||||||
82
vendor/github.com/lufia/plan9stats/host.go
generated
vendored
82
vendor/github.com/lufia/plan9stats/host.go
generated
vendored
@@ -109,6 +109,12 @@ func parseGauge(s string, r *Gauge) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Storage struct {
|
||||||
|
Name string
|
||||||
|
Model string
|
||||||
|
Capacity int64
|
||||||
|
}
|
||||||
|
|
||||||
type Interface struct {
|
type Interface struct {
|
||||||
Name string
|
Name string
|
||||||
Addr string
|
Addr string
|
||||||
@@ -171,7 +177,7 @@ func ReadHost(ctx context.Context, opts ...Option) (*Host, error) {
|
|||||||
}
|
}
|
||||||
h.Sysname = name
|
h.Sysname = name
|
||||||
|
|
||||||
a, err := ReadStorages(ctx, opts...)
|
a, err := readStorages(cfg.rootdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -197,6 +203,80 @@ func readSysname(rootdir string) (string, error) {
|
|||||||
return string(bytes.TrimSpace(b)), nil
|
return string(bytes.TrimSpace(b)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readStorages(rootdir string) ([]*Storage, error) {
|
||||||
|
sdctl := filepath.Join(rootdir, "/dev/sdctl")
|
||||||
|
f, err := os.Open(sdctl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
var a []*Storage
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
for scanner.Scan() {
|
||||||
|
fields := bytes.Split(scanner.Bytes(), delim)
|
||||||
|
if len(fields) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
exp := string(fields[0]) + "*"
|
||||||
|
if !strings.HasPrefix(exp, "sd") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dir := filepath.Join(rootdir, "/dev", exp)
|
||||||
|
m, err := filepath.Glob(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, dir := range m {
|
||||||
|
s, err := readStorage(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
a = append(a, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readStorage(dir string) (*Storage, error) {
|
||||||
|
ctl := filepath.Join(dir, "ctl")
|
||||||
|
f, err := os.Open(ctl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
var s Storage
|
||||||
|
s.Name = filepath.Base(dir)
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Bytes()
|
||||||
|
switch {
|
||||||
|
case bytes.HasPrefix(line, []byte("inquiry")):
|
||||||
|
s.Model = string(bytes.TrimSpace(line[7:]))
|
||||||
|
case bytes.HasPrefix(line, []byte("geometry")):
|
||||||
|
fields := bytes.Split(line, delim)
|
||||||
|
if len(fields) < 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var p intParser
|
||||||
|
sec := p.ParseInt64(string(fields[1]), 10)
|
||||||
|
size := p.ParseInt64(string(fields[2]), 10)
|
||||||
|
if err := p.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.Capacity = sec * size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &s, nil
|
||||||
|
}
|
||||||
|
|
||||||
type IPStats struct {
|
type IPStats struct {
|
||||||
ID int // number of interface in ipifc dir
|
ID int // number of interface in ipifc dir
|
||||||
Device string // associated physical device
|
Device string // associated physical device
|
||||||
|
|||||||
9
vendor/github.com/lufia/plan9stats/int.go
generated
vendored
9
vendor/github.com/lufia/plan9stats/int.go
generated
vendored
@@ -26,15 +26,6 @@ func (p *intParser) ParseInt64(s string, base int) int64 {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *intParser) ParseUint64(s string, base int) uint64 {
|
|
||||||
if p.err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var n uint64
|
|
||||||
n, p.err = strconv.ParseUint(s, base, 64)
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *intParser) Err() error {
|
func (p *intParser) Err() error {
|
||||||
return p.err
|
return p.err
|
||||||
}
|
}
|
||||||
|
|||||||
1
vendor/github.com/power-devops/perfstat/config.go
generated
vendored
1
vendor/github.com/power-devops/perfstat/config.go
generated
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build aix
|
|
||||||
// +build aix
|
// +build aix
|
||||||
|
|
||||||
package perfstat
|
package perfstat
|
||||||
|
|||||||
40
vendor/github.com/power-devops/perfstat/cpustat.go
generated
vendored
40
vendor/github.com/power-devops/perfstat/cpustat.go
generated
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build aix
|
|
||||||
// +build aix
|
// +build aix
|
||||||
|
|
||||||
package perfstat
|
package perfstat
|
||||||
@@ -21,13 +20,6 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
var old_cpu_total_stat *C.perfstat_cpu_total_t
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
old_cpu_total_stat = (*C.perfstat_cpu_total_t)(C.malloc(C.sizeof_perfstat_cpu_total_t))
|
|
||||||
C.perfstat_cpu_total(nil, old_cpu_total_stat, C.sizeof_perfstat_cpu_total_t, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func CpuStat() ([]CPU, error) {
|
func CpuStat() ([]CPU, error) {
|
||||||
var cpustat *C.perfstat_cpu_t
|
var cpustat *C.perfstat_cpu_t
|
||||||
var cpu C.perfstat_id_t
|
var cpu C.perfstat_id_t
|
||||||
@@ -104,35 +96,3 @@ func CpuUtilStat(intvl time.Duration) (*CPUUtil, error) {
|
|||||||
u := perfstatcpuutil2cpuutil(cpuutil)
|
u := perfstatcpuutil2cpuutil(cpuutil)
|
||||||
return &u, nil
|
return &u, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CpuUtilTotalStat() (*CPUUtil, error) {
|
|
||||||
var cpuutil *C.perfstat_cpu_util_t
|
|
||||||
var new_cpu_total_stat *C.perfstat_cpu_total_t
|
|
||||||
var data C.perfstat_rawdata_t
|
|
||||||
|
|
||||||
new_cpu_total_stat = (*C.perfstat_cpu_total_t)(C.malloc(C.sizeof_perfstat_cpu_total_t))
|
|
||||||
cpuutil = (*C.perfstat_cpu_util_t)(C.malloc(C.sizeof_perfstat_cpu_util_t))
|
|
||||||
defer C.free(unsafe.Pointer(cpuutil))
|
|
||||||
|
|
||||||
r := C.perfstat_cpu_total(nil, new_cpu_total_stat, C.sizeof_perfstat_cpu_total_t, 1)
|
|
||||||
if r <= 0 {
|
|
||||||
C.free(unsafe.Pointer(new_cpu_total_stat))
|
|
||||||
return nil, fmt.Errorf("error perfstat_cpu_total()")
|
|
||||||
}
|
|
||||||
|
|
||||||
data._type = C.UTIL_CPU_TOTAL
|
|
||||||
data.curstat = unsafe.Pointer(new_cpu_total_stat)
|
|
||||||
data.prevstat = unsafe.Pointer(old_cpu_total_stat)
|
|
||||||
data.sizeof_data = C.sizeof_perfstat_cpu_total_t
|
|
||||||
data.cur_elems = 1
|
|
||||||
data.prev_elems = 1
|
|
||||||
|
|
||||||
r = C.perfstat_cpu_util(&data, cpuutil, C.sizeof_perfstat_cpu_util_t, 1)
|
|
||||||
C.free(unsafe.Pointer(old_cpu_total_stat))
|
|
||||||
old_cpu_total_stat = new_cpu_total_stat
|
|
||||||
if r <= 0 {
|
|
||||||
return nil, fmt.Errorf("error perfstat_cpu_util()")
|
|
||||||
}
|
|
||||||
u := perfstatcpuutil2cpuutil(cpuutil)
|
|
||||||
return &u, nil
|
|
||||||
}
|
|
||||||
|
|||||||
1
vendor/github.com/power-devops/perfstat/diskstat.go
generated
vendored
1
vendor/github.com/power-devops/perfstat/diskstat.go
generated
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build aix
|
|
||||||
// +build aix
|
// +build aix
|
||||||
|
|
||||||
package perfstat
|
package perfstat
|
||||||
|
|||||||
13
vendor/github.com/power-devops/perfstat/doc.go
generated
vendored
13
vendor/github.com/power-devops/perfstat/doc.go
generated
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build !aix
|
|
||||||
// +build !aix
|
// +build !aix
|
||||||
|
|
||||||
// Copyright 2020 Power-Devops.com. All rights reserved.
|
// Copyright 2020 Power-Devops.com. All rights reserved.
|
||||||
@@ -37,24 +36,24 @@ func DisableLVMStat() {}
|
|||||||
// CpuStat() returns array of CPU structures with information about
|
// CpuStat() returns array of CPU structures with information about
|
||||||
// logical CPUs on the system.
|
// logical CPUs on the system.
|
||||||
// IBM documentation:
|
// IBM documentation:
|
||||||
// - https://www.ibm.com/support/knowledgecenter/ssw_aix_72/performancetools/idprftools_perfstat_int_cpu.html
|
// * https://www.ibm.com/support/knowledgecenter/ssw_aix_72/performancetools/idprftools_perfstat_int_cpu.html
|
||||||
// - https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/p_bostechref/perfstat_cpu.html
|
// * https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/p_bostechref/perfstat_cpu.html
|
||||||
func CpuStat() ([]CPU, error) {
|
func CpuStat() ([]CPU, error) {
|
||||||
return nil, fmt.Errorf("not implemented")
|
return nil, fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// CpuTotalStat() returns general information about CPUs on the system.
|
// CpuTotalStat() returns general information about CPUs on the system.
|
||||||
// IBM documentation:
|
// IBM documentation:
|
||||||
// - https://www.ibm.com/support/knowledgecenter/ssw_aix_72/performancetools/idprftools_perfstat_glob_cpu.html
|
// * https://www.ibm.com/support/knowledgecenter/ssw_aix_72/performancetools/idprftools_perfstat_glob_cpu.html
|
||||||
// - https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/p_bostechref/perfstat_cputot.html
|
// * https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/p_bostechref/perfstat_cputot.html
|
||||||
func CpuTotalStat() (*CPUTotal, error) {
|
func CpuTotalStat() (*CPUTotal, error) {
|
||||||
return nil, fmt.Errorf("not implemented")
|
return nil, fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// CpuUtilStat() calculates CPU utilization.
|
// CpuUtilStat() calculates CPU utilization.
|
||||||
// IBM documentation:
|
// IBM documentation:
|
||||||
// - https://www.ibm.com/support/knowledgecenter/ssw_aix_72/performancetools/idprftools_perfstat_cpu_util.html
|
// * https://www.ibm.com/support/knowledgecenter/ssw_aix_72/performancetools/idprftools_perfstat_cpu_util.html
|
||||||
// - https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/p_bostechref/perfstat_cpu_util.html
|
// * https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/p_bostechref/perfstat_cpu_util.html
|
||||||
func CpuUtilStat(intvl time.Duration) (*CPUUtil, error) {
|
func CpuUtilStat(intvl time.Duration) (*CPUUtil, error) {
|
||||||
return nil, fmt.Errorf("not implemented")
|
return nil, fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|||||||
1
vendor/github.com/power-devops/perfstat/fsstat.go
generated
vendored
1
vendor/github.com/power-devops/perfstat/fsstat.go
generated
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build aix
|
|
||||||
// +build aix
|
// +build aix
|
||||||
|
|
||||||
package perfstat
|
package perfstat
|
||||||
|
|||||||
57
vendor/github.com/power-devops/perfstat/helpers.go
generated
vendored
57
vendor/github.com/power-devops/perfstat/helpers.go
generated
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build aix
|
|
||||||
// +build aix
|
// +build aix
|
||||||
|
|
||||||
package perfstat
|
package perfstat
|
||||||
@@ -8,7 +7,6 @@ package perfstat
|
|||||||
|
|
||||||
#include <libperfstat.h>
|
#include <libperfstat.h>
|
||||||
#include <sys/proc.h>
|
#include <sys/proc.h>
|
||||||
#include <sys/dr.h>
|
|
||||||
|
|
||||||
#include "c_helpers.h"
|
#include "c_helpers.h"
|
||||||
*/
|
*/
|
||||||
@@ -756,7 +754,7 @@ func fsinfo2filesystem(n *C.struct_fsinfo) FileSystem {
|
|||||||
i.Device = C.GoString(n.devname)
|
i.Device = C.GoString(n.devname)
|
||||||
i.MountPoint = C.GoString(n.fsname)
|
i.MountPoint = C.GoString(n.fsname)
|
||||||
i.FSType = int(n.fstype)
|
i.FSType = int(n.fstype)
|
||||||
i.Flags = uint(n.flags)
|
i.Flags = int(n.flags)
|
||||||
i.TotalBlocks = int64(n.totalblks)
|
i.TotalBlocks = int64(n.totalblks)
|
||||||
i.FreeBlocks = int64(n.freeblks)
|
i.FreeBlocks = int64(n.freeblks)
|
||||||
i.TotalInodes = int64(n.totalinodes)
|
i.TotalInodes = int64(n.totalinodes)
|
||||||
@@ -764,56 +762,3 @@ func fsinfo2filesystem(n *C.struct_fsinfo) FileSystem {
|
|||||||
|
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
func lparinfo2partinfo(n C.lpar_info_format2_t) PartitionInfo {
|
|
||||||
var i PartitionInfo
|
|
||||||
|
|
||||||
i.Version = int(n.version)
|
|
||||||
i.OnlineMemory = uint64(n.online_memory)
|
|
||||||
i.TotalDispatchTime = uint64(n.tot_dispatch_time)
|
|
||||||
i.PoolIdleTime = uint64(n.pool_idle_time)
|
|
||||||
i.DispatchLatency = uint64(n.dispatch_latency)
|
|
||||||
i.LparFlags = uint(n.lpar_flags)
|
|
||||||
i.PCpusInSys = uint(n.pcpus_in_sys)
|
|
||||||
i.OnlineVCpus = uint(n.online_vcpus)
|
|
||||||
i.OnlineLCpus = uint(n.online_lcpus)
|
|
||||||
i.PCpusInPool = uint(n.pcpus_in_pool)
|
|
||||||
i.UnallocCapacity = uint(n.unalloc_capacity)
|
|
||||||
i.EntitledCapacity = uint(n.entitled_capacity)
|
|
||||||
i.VariableWeight = uint(n.variable_weight)
|
|
||||||
i.UnallocWeight = uint(n.unalloc_weight)
|
|
||||||
i.MinReqVCpuCapacity = uint(n.min_req_vcpu_capacity)
|
|
||||||
i.GroupId = uint8(n.group_id)
|
|
||||||
i.PoolId = uint8(n.pool_id)
|
|
||||||
i.ShCpusInSys = uint(n.shcpus_in_sys)
|
|
||||||
i.MaxPoolCapacity = uint(n.max_pool_capacity)
|
|
||||||
i.EntitledPoolCapacity = uint(n.entitled_pool_capacity)
|
|
||||||
i.PoolMaxTime = uint64(n.pool_max_time)
|
|
||||||
i.PoolBusyTime = uint64(n.pool_busy_time)
|
|
||||||
i.PoolScaledBusyTime = uint64(n.pool_scaled_busy_time)
|
|
||||||
i.ShCpuTotalTime = uint64(n.shcpu_tot_time)
|
|
||||||
i.ShCpuBusyTime = uint64(n.shcpu_busy_time)
|
|
||||||
i.ShCpuScaledBusyTime = uint64(n.shcpu_scaled_busy_time)
|
|
||||||
i.EntMemCapacity = uint64(n.ent_mem_capacity)
|
|
||||||
i.PhysMem = uint64(n.phys_mem)
|
|
||||||
i.VrmPoolPhysMem = uint64(n.vrm_pool_physmem)
|
|
||||||
i.HypPageSize = uint(n.hyp_pagesize)
|
|
||||||
i.VrmPoolId = int(n.vrm_pool_id)
|
|
||||||
i.VrmGroupId = int(n.vrm_group_id)
|
|
||||||
i.VarMemWeight = int(n.var_mem_weight)
|
|
||||||
i.UnallocVarMemWeight = int(n.unalloc_var_mem_weight)
|
|
||||||
i.UnallocEntMemCapacity = uint64(n.unalloc_ent_mem_capacity)
|
|
||||||
i.TrueOnlineMemory = uint64(n.true_online_memory)
|
|
||||||
i.AmeOnlineMemory = uint64(n.ame_online_memory)
|
|
||||||
i.AmeType = uint8(n.ame_type)
|
|
||||||
i.SpecExecMode = uint8(n.spec_exec_mode)
|
|
||||||
i.AmeFactor = uint(n.ame_factor)
|
|
||||||
i.EmPartMajorCode = uint(n.em_part_major_code)
|
|
||||||
i.EmPartMinorCode = uint(n.em_part_minor_code)
|
|
||||||
i.BytesCoalesced = uint64(n.bytes_coalesced)
|
|
||||||
i.BytesCoalescedMemPool = uint64(n.bytes_coalesced_mempool)
|
|
||||||
i.PurrCoalescing = uint64(n.purr_coalescing)
|
|
||||||
i.SpurrCoalescing = uint64(n.spurr_coalescing)
|
|
||||||
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|||||||
14
vendor/github.com/power-devops/perfstat/lparstat.go
generated
vendored
14
vendor/github.com/power-devops/perfstat/lparstat.go
generated
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build aix
|
|
||||||
// +build aix
|
// +build aix
|
||||||
|
|
||||||
package perfstat
|
package perfstat
|
||||||
@@ -7,13 +6,11 @@ package perfstat
|
|||||||
#cgo LDFLAGS: -lperfstat
|
#cgo LDFLAGS: -lperfstat
|
||||||
|
|
||||||
#include <libperfstat.h>
|
#include <libperfstat.h>
|
||||||
#include <sys/dr.h>
|
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"unsafe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func PartitionStat() (*PartitionConfig, error) {
|
func PartitionStat() (*PartitionConfig, error) {
|
||||||
@@ -27,14 +24,3 @@ func PartitionStat() (*PartitionConfig, error) {
|
|||||||
return &p, nil
|
return &p, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func LparInfo() (*PartitionInfo, error) {
|
|
||||||
var pinfo C.lpar_info_format2_t
|
|
||||||
|
|
||||||
rc := C.lpar_get_info(C.LPAR_INFO_FORMAT2, unsafe.Pointer(&pinfo), C.sizeof_lpar_info_format2_t)
|
|
||||||
if rc != 0 {
|
|
||||||
return nil, fmt.Errorf("lpar_get_info() error")
|
|
||||||
}
|
|
||||||
p := lparinfo2partinfo(pinfo)
|
|
||||||
return &p, nil
|
|
||||||
}
|
|
||||||
|
|||||||
1
vendor/github.com/power-devops/perfstat/lvmstat.go
generated
vendored
1
vendor/github.com/power-devops/perfstat/lvmstat.go
generated
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build aix
|
|
||||||
// +build aix
|
// +build aix
|
||||||
|
|
||||||
package perfstat
|
package perfstat
|
||||||
|
|||||||
1
vendor/github.com/power-devops/perfstat/memstat.go
generated
vendored
1
vendor/github.com/power-devops/perfstat/memstat.go
generated
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build aix
|
|
||||||
// +build aix
|
// +build aix
|
||||||
|
|
||||||
package perfstat
|
package perfstat
|
||||||
|
|||||||
1
vendor/github.com/power-devops/perfstat/netstat.go
generated
vendored
1
vendor/github.com/power-devops/perfstat/netstat.go
generated
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build aix
|
|
||||||
// +build aix
|
// +build aix
|
||||||
|
|
||||||
package perfstat
|
package perfstat
|
||||||
|
|||||||
1
vendor/github.com/power-devops/perfstat/procstat.go
generated
vendored
1
vendor/github.com/power-devops/perfstat/procstat.go
generated
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build aix
|
|
||||||
// +build aix
|
// +build aix
|
||||||
|
|
||||||
package perfstat
|
package perfstat
|
||||||
|
|||||||
1
vendor/github.com/power-devops/perfstat/sysconf.go
generated
vendored
1
vendor/github.com/power-devops/perfstat/sysconf.go
generated
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build aix
|
|
||||||
// +build aix
|
// +build aix
|
||||||
|
|
||||||
package perfstat
|
package perfstat
|
||||||
|
|||||||
59
vendor/github.com/power-devops/perfstat/systemcfg.go
generated
vendored
59
vendor/github.com/power-devops/perfstat/systemcfg.go
generated
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build aix
|
|
||||||
// +build aix
|
// +build aix
|
||||||
|
|
||||||
package perfstat
|
package perfstat
|
||||||
@@ -71,7 +70,6 @@ const (
|
|||||||
SC_TM_VER = 59 /* Transaction Memory version, 0 - not capable */
|
SC_TM_VER = 59 /* Transaction Memory version, 0 - not capable */
|
||||||
SC_NX_CAP = 60 /* NX GZIP capable */
|
SC_NX_CAP = 60 /* NX GZIP capable */
|
||||||
SC_PKS_STATE = 61 /* Platform KeyStore */
|
SC_PKS_STATE = 61 /* Platform KeyStore */
|
||||||
SC_MMA_VER = 62
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/* kernel attributes */
|
/* kernel attributes */
|
||||||
@@ -121,7 +119,6 @@ const (
|
|||||||
IMPL_POWER7 = 0x8000 /* 7 class CPU */
|
IMPL_POWER7 = 0x8000 /* 7 class CPU */
|
||||||
IMPL_POWER8 = 0x10000 /* 8 class CPU */
|
IMPL_POWER8 = 0x10000 /* 8 class CPU */
|
||||||
IMPL_POWER9 = 0x20000 /* 9 class CPU */
|
IMPL_POWER9 = 0x20000 /* 9 class CPU */
|
||||||
IMPL_POWER10 = 0x20000 /* 10 class CPU */
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Values for implementation field for IA64 Architectures
|
// Values for implementation field for IA64 Architectures
|
||||||
@@ -154,13 +151,11 @@ const (
|
|||||||
PV_7 = 0x200000 /* Power PC 7 */
|
PV_7 = 0x200000 /* Power PC 7 */
|
||||||
PV_8 = 0x300000 /* Power PC 8 */
|
PV_8 = 0x300000 /* Power PC 8 */
|
||||||
PV_9 = 0x400000 /* Power PC 9 */
|
PV_9 = 0x400000 /* Power PC 9 */
|
||||||
PV_10 = 0x500000 /* Power PC 10 */
|
|
||||||
PV_5_Compat = 0x0F8000 /* Power PC 5 */
|
PV_5_Compat = 0x0F8000 /* Power PC 5 */
|
||||||
PV_6_Compat = 0x108000 /* Power PC 6 */
|
PV_6_Compat = 0x108000 /* Power PC 6 */
|
||||||
PV_7_Compat = 0x208000 /* Power PC 7 */
|
PV_7_Compat = 0x208000 /* Power PC 7 */
|
||||||
PV_8_Compat = 0x308000 /* Power PC 8 */
|
PV_8_Compat = 0x308000 /* Power PC 8 */
|
||||||
PV_9_Compat = 0x408000 /* Power PC 9 */
|
PV_9_Compat = 0x408000 /* Power PC 9 */
|
||||||
PV_10_Compat = 0x508000 /* Power PC 10 */
|
|
||||||
PV_RESERVED_2 = 0x0A0000 /* source compatability */
|
PV_RESERVED_2 = 0x0A0000 /* source compatability */
|
||||||
PV_RESERVED_3 = 0x0B0000 /* source compatability */
|
PV_RESERVED_3 = 0x0B0000 /* source compatability */
|
||||||
PV_RS2 = 0x040000 /* Power RS2 */
|
PV_RS2 = 0x040000 /* Power RS2 */
|
||||||
@@ -186,21 +181,19 @@ const (
|
|||||||
|
|
||||||
// Macros for identifying physical processor
|
// Macros for identifying physical processor
|
||||||
const (
|
const (
|
||||||
PPI4_1 = 0x35
|
PPI4_1 = 0x35
|
||||||
PPI4_2 = 0x38
|
PPI4_2 = 0x38
|
||||||
PPI4_3 = 0x39
|
PPI4_3 = 0x39
|
||||||
PPI4_4 = 0x3C
|
PPI4_4 = 0x3C
|
||||||
PPI4_5 = 0x44
|
PPI4_5 = 0x44
|
||||||
PPI5_1 = 0x3A
|
PPI5_1 = 0x3A
|
||||||
PPI5_2 = 0x3B
|
PPI5_2 = 0x3B
|
||||||
PPI6_1 = 0x3E
|
PPI6_1 = 0x3E
|
||||||
PPI7_1 = 0x3F
|
PPI7_1 = 0x3F
|
||||||
PPI7_2 = 0x4A
|
PPI7_2 = 0x4A
|
||||||
PPI8_1 = 0x4B
|
PPI8_1 = 0x4B
|
||||||
PPI8_2 = 0x4D
|
PPI8_2 = 0x4D
|
||||||
PPI9 = 0x4E
|
PPI9 = 0x4E
|
||||||
PPI9_1 = 0x4E
|
|
||||||
PPI10_1 = 0x80
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Macros for kernel attributes
|
// Macros for kernel attributes
|
||||||
@@ -298,32 +291,14 @@ func GetCPUImplementation() string {
|
|||||||
return "POWER8"
|
return "POWER8"
|
||||||
case impl&IMPL_POWER9 != 0:
|
case impl&IMPL_POWER9 != 0:
|
||||||
return "POWER9"
|
return "POWER9"
|
||||||
case impl&IMPL_POWER10 != 0:
|
|
||||||
return "Power10"
|
|
||||||
default:
|
default:
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func POWER10OrNewer() bool {
|
|
||||||
impl := unix.Getsystemcfg(SC_IMPL)
|
|
||||||
if impl&IMPL_POWER10 != 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func POWER10() bool {
|
|
||||||
impl := unix.Getsystemcfg(SC_IMPL)
|
|
||||||
if impl&IMPL_POWER10 != 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func POWER9OrNewer() bool {
|
func POWER9OrNewer() bool {
|
||||||
impl := unix.Getsystemcfg(SC_IMPL)
|
impl := unix.Getsystemcfg(SC_IMPL)
|
||||||
if impl&IMPL_POWER10 != 0 || impl&IMPL_POWER9 != 0 {
|
if impl&IMPL_POWER9 != 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -339,7 +314,7 @@ func POWER9() bool {
|
|||||||
|
|
||||||
func POWER8OrNewer() bool {
|
func POWER8OrNewer() bool {
|
||||||
impl := unix.Getsystemcfg(SC_IMPL)
|
impl := unix.Getsystemcfg(SC_IMPL)
|
||||||
if impl&IMPL_POWER10 != 0 || impl&IMPL_POWER9 != 0 || impl&IMPL_POWER8 != 0 {
|
if impl&IMPL_POWER9 != 0 || impl&IMPL_POWER8 != 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -355,7 +330,7 @@ func POWER8() bool {
|
|||||||
|
|
||||||
func POWER7OrNewer() bool {
|
func POWER7OrNewer() bool {
|
||||||
impl := unix.Getsystemcfg(SC_IMPL)
|
impl := unix.Getsystemcfg(SC_IMPL)
|
||||||
if impl&IMPL_POWER10 != 0 || impl&IMPL_POWER9 != 0 || impl&IMPL_POWER8 != 0 || impl&IMPL_POWER7 != 0 {
|
if impl&IMPL_POWER9 != 0 || impl&IMPL_POWER8 != 0 || impl&IMPL_POWER7 != 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -444,8 +419,6 @@ func PksEnabled() bool {
|
|||||||
func CPUMode() string {
|
func CPUMode() string {
|
||||||
impl := unix.Getsystemcfg(SC_VERS)
|
impl := unix.Getsystemcfg(SC_VERS)
|
||||||
switch impl {
|
switch impl {
|
||||||
case PV_10, PV_10_Compat:
|
|
||||||
return "Power10"
|
|
||||||
case PV_9, PV_9_Compat:
|
case PV_9, PV_9_Compat:
|
||||||
return "POWER9"
|
return "POWER9"
|
||||||
case PV_8, PV_8_Compat:
|
case PV_8, PV_8_Compat:
|
||||||
|
|||||||
4
vendor/github.com/power-devops/perfstat/types_disk.go
generated
vendored
4
vendor/github.com/power-devops/perfstat/types_disk.go
generated
vendored
@@ -29,8 +29,8 @@ type DiskTotal struct {
|
|||||||
// Disk Adapter Types
|
// Disk Adapter Types
|
||||||
const (
|
const (
|
||||||
DA_SCSI = 0 /* 0 ==> SCSI, SAS, other legacy adapter types */
|
DA_SCSI = 0 /* 0 ==> SCSI, SAS, other legacy adapter types */
|
||||||
DA_VSCSI = 1 /* 1 ==> Virtual SCSI/SAS Adapter */
|
DA_VSCSI /* 1 ==> Virtual SCSI/SAS Adapter */
|
||||||
DA_FCA = 2 /* 2 ==> Fiber Channel Adapter */
|
DA_FCA /* 2 ==> Fiber Channel Adapter */
|
||||||
)
|
)
|
||||||
|
|
||||||
type DiskAdapter struct {
|
type DiskAdapter struct {
|
||||||
|
|||||||
2
vendor/github.com/power-devops/perfstat/types_fs.go
generated
vendored
2
vendor/github.com/power-devops/perfstat/types_fs.go
generated
vendored
@@ -8,7 +8,7 @@ type FileSystem struct {
|
|||||||
Device string /* name of the mounted device */
|
Device string /* name of the mounted device */
|
||||||
MountPoint string /* where the device is mounted */
|
MountPoint string /* where the device is mounted */
|
||||||
FSType int /* File system type, see the constants below */
|
FSType int /* File system type, see the constants below */
|
||||||
Flags uint /* Flags of the file system */
|
Flags int /* Flags of the file system */
|
||||||
TotalBlocks int64 /* number of 512 bytes blocks in the filesystem */
|
TotalBlocks int64 /* number of 512 bytes blocks in the filesystem */
|
||||||
FreeBlocks int64 /* number of free 512 bytes block in the filesystem */
|
FreeBlocks int64 /* number of free 512 bytes block in the filesystem */
|
||||||
TotalInodes int64 /* total number of inodes in the filesystem */
|
TotalInodes int64 /* total number of inodes in the filesystem */
|
||||||
|
|||||||
61
vendor/github.com/power-devops/perfstat/types_lpar.go
generated
vendored
61
vendor/github.com/power-devops/perfstat/types_lpar.go
generated
vendored
@@ -66,64 +66,3 @@ type PartitionConfig struct {
|
|||||||
TargetMemExpSize int64 /* Expanded Memory Size in MB */
|
TargetMemExpSize int64 /* Expanded Memory Size in MB */
|
||||||
SubProcessorMode int32 /* Split core mode, its value can be 0,1,2 or 4. 0 for unsupported, 1 for capable but not enabled, 2 or 4 for enabled*/
|
SubProcessorMode int32 /* Split core mode, its value can be 0,1,2 or 4. 0 for unsupported, 1 for capable but not enabled, 2 or 4 for enabled*/
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
AME_TYPE_V1 = 0x1
|
|
||||||
AME_TYPE_V2 = 0x2
|
|
||||||
LPAR_INFO_CAPPED = 0x01 /* Parition Capped */
|
|
||||||
LPAR_INFO_AUTH_PIC = 0x02 /* Authority granted for poolidle*/
|
|
||||||
LPAR_INFO_SMT_ENABLED = 0x04 /* SMT Enabled */
|
|
||||||
LPAR_INFO_WPAR_ACTIVE = 0x08 /* Process Running Within a WPAR */
|
|
||||||
LPAR_INFO_EXTENDED = 0x10 /* Extended shared processor pool information */
|
|
||||||
LPAR_INFO_AME_ENABLED = 0x20 /* Active Mem. Expansion (AME) enabled*/
|
|
||||||
LPAR_INFO_SEM_ENABLED = 0x40 /* Speculative Execution Mode enabled */
|
|
||||||
)
|
|
||||||
|
|
||||||
type PartitionInfo struct {
|
|
||||||
Version int /* version for this structure */
|
|
||||||
OnlineMemory uint64 /* MB of currently online memory */
|
|
||||||
TotalDispatchTime uint64 /* Total lpar dispatch time in nsecs */
|
|
||||||
PoolIdleTime uint64 /* Idle time of shared CPU pool nsecs*/
|
|
||||||
DispatchLatency uint64 /* Max latency inbetween dispatches of this LPAR on physCPUS in nsecs */
|
|
||||||
LparFlags uint /* LPAR flags */
|
|
||||||
PCpusInSys uint /* # of active licensed physical CPUs in system */
|
|
||||||
OnlineVCpus uint /* # of current online virtual CPUs */
|
|
||||||
OnlineLCpus uint /* # of current online logical CPUs */
|
|
||||||
PCpusInPool uint /* # physical CPUs in shared pool */
|
|
||||||
UnallocCapacity uint /* Unallocated Capacity available in shared pool */
|
|
||||||
EntitledCapacity uint /* Entitled Processor Capacity for this partition */
|
|
||||||
VariableWeight uint /* Variable Processor Capacity Weight */
|
|
||||||
UnallocWeight uint /* Unallocated Variable Weight available for this partition */
|
|
||||||
MinReqVCpuCapacity uint /* OS minimum required virtual processor capacity. */
|
|
||||||
GroupId uint8 /* ID of a LPAR group/aggregation */
|
|
||||||
PoolId uint8 /* ID of a shared pool */
|
|
||||||
ShCpusInSys uint /* # of physical processors allocated for shared processor use */
|
|
||||||
MaxPoolCapacity uint /* Maximum processor capacity of partition's pool */
|
|
||||||
EntitledPoolCapacity uint /* Entitled processor capacity of partition's pool */
|
|
||||||
PoolMaxTime uint64 /* Summation of maximum time that could be consumed by the pool, in nanoseconds */
|
|
||||||
PoolBusyTime uint64 /* Summation of busy time accumulated across all partitions in the pool, in nanoseconds */
|
|
||||||
PoolScaledBusyTime uint64 /* Scaled summation of busy time accumulated across all partitions in the pool, in nanoseconds */
|
|
||||||
ShCpuTotalTime uint64 /* Summation of total time across all physical processors allocated for shared processor use, in nanoseconds */
|
|
||||||
ShCpuBusyTime uint64 /* Summation of busy time accumulated across all shared processor partitions, in nanoseconds */
|
|
||||||
ShCpuScaledBusyTime uint64 /* Scaled summation of busy time accumulated across all shared processor partitions, in nanoseconds */
|
|
||||||
EntMemCapacity uint64 /* Partition's current entitlement memory capacity setting */
|
|
||||||
PhysMem uint64 /* Amount of physical memory, in bytes, currently backing the partition's logical memory */
|
|
||||||
VrmPoolPhysMem uint64 /* Total amount of physical memory in the VRM pool */
|
|
||||||
HypPageSize uint /* Page size hypervisor is using to virtualize partition's memory */
|
|
||||||
VrmPoolId int /* ID of VRM pool */
|
|
||||||
VrmGroupId int /* eWLM VRM group to which partition belongs */
|
|
||||||
VarMemWeight int /* Partition's current variable memory capacity weighting setting */
|
|
||||||
UnallocVarMemWeight int /* Amount of unallocated variable memory capacity weight available to LPAR's group */
|
|
||||||
UnallocEntMemCapacity uint64 /* Amount of unallocated I/O memory entitlement available to LPAR's group */
|
|
||||||
TrueOnlineMemory uint64 /* true MB of currently online memory */
|
|
||||||
AmeOnlineMemory uint64 /* AME MB of currently online memory */
|
|
||||||
AmeType uint8
|
|
||||||
SpecExecMode uint8 /* Speculative Execution Mode */
|
|
||||||
AmeFactor uint /* memory expansion factor for LPAR */
|
|
||||||
EmPartMajorCode uint /* Major and minor codes for our */
|
|
||||||
EmPartMinorCode uint /* current energy management mode */
|
|
||||||
BytesCoalesced uint64 /* The number of bytes of the calling partition.s logical real memory coalesced because they contained duplicated data */
|
|
||||||
BytesCoalescedMemPool uint64 /* If the calling partition is authorized to see pool wide statistics then the number of bytes of logical real memory coalesced because they contained duplicated data in the calling partition.s memory pool else set to zero.*/
|
|
||||||
PurrCoalescing uint64 /* If the calling partition is authorized to see pool wide statistics then PURR cycles consumed to coalesce data else set to zero.*/
|
|
||||||
SpurrCoalescing uint64 /* If the calling partition is authorized to see pool wide statistics then SPURR cycles consumed to coalesce data else set to zero.*/
|
|
||||||
}
|
|
||||||
|
|||||||
1
vendor/github.com/power-devops/perfstat/uptime.go
generated
vendored
1
vendor/github.com/power-devops/perfstat/uptime.go
generated
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build aix
|
|
||||||
// +build aix
|
// +build aix
|
||||||
|
|
||||||
package perfstat
|
package perfstat
|
||||||
|
|||||||
15
vendor/github.com/sevlyar/go-daemon/.travis.yml
generated
vendored
Normal file
15
vendor/github.com/sevlyar/go-daemon/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.3
|
||||||
|
- 1.5
|
||||||
|
- tip
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go get -t -v ./...
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go test -v -coverprofile=coverage.txt -covermode=atomic
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
7
vendor/github.com/sevlyar/go-daemon/LICENSE
generated
vendored
Normal file
7
vendor/github.com/sevlyar/go-daemon/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Copyright (C) 2013 Sergey Yarmonov
|
||||||
|
|
||||||
|
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.
|
||||||
70
vendor/github.com/sevlyar/go-daemon/README.md
generated
vendored
Normal file
70
vendor/github.com/sevlyar/go-daemon/README.md
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# go-daemon [](https://travis-ci.org/sevlyar/go-daemon) [](https://godoc.org/github.com/sevlyar/go-daemon)
|
||||||
|
|
||||||
|
Library for writing system daemons in Go.
|
||||||
|
|
||||||
|
Now supported only UNIX-based OS (Windows is not supported). But the library was tested only on Linux
|
||||||
|
and OSX, so that if you have an ability to test the library on other platforms, give me feedback, please (#26).
|
||||||
|
|
||||||
|
*Please, feel free to send me bug reports and fixes. Many thanks to all contributors.*
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* Goroutine-safe daemonization;
|
||||||
|
* Out of box work with pid-files;
|
||||||
|
* Easy handling of system signals;
|
||||||
|
* The control of a daemon.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
go get github.com/sevlyar/go-daemon
|
||||||
|
|
||||||
|
You can use [gopkg.in](http://labix.org/gopkg.in):
|
||||||
|
|
||||||
|
go get gopkg.in/sevlyar/go-daemon.v0
|
||||||
|
|
||||||
|
If you want to use the library in production project, please use vendoring,
|
||||||
|
because i can not ensure backward compatibility before release v1.0.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
* [Simple](examples/cmd/gd-simple/)
|
||||||
|
* [Log rotation](examples/cmd/gd-log-rotation/)
|
||||||
|
* [Signal handling](examples/cmd/gd-signal-handling/)
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
[godoc.org/github.com/sevlyar/go-daemon](https://godoc.org/github.com/sevlyar/go-daemon)
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
We can not use `fork` syscall in Golang's runtime, because child process doesn't inherit
|
||||||
|
threads and goroutines in that case. The library uses a simple trick: it runs its own copy with
|
||||||
|
a mark - a predefined environment variable. Availability of the variable for the process means
|
||||||
|
an execution in the child's copy. So that if the mark is not setted - the library executes
|
||||||
|
parent's operations and runs its own copy with mark, and if the mark is setted - the library
|
||||||
|
executes child's operations:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "log"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
Pre()
|
||||||
|
|
||||||
|
context := new(Context)
|
||||||
|
child, _ := context.Reborn()
|
||||||
|
|
||||||
|
if child != nil {
|
||||||
|
PostParent()
|
||||||
|
} else {
|
||||||
|
defer func() {
|
||||||
|
if err := context.Release(); err != nil {
|
||||||
|
log.Printf("Unable to release pid-file: %s", err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
PostChild()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
99
vendor/github.com/sevlyar/go-daemon/command.go
generated
vendored
Normal file
99
vendor/github.com/sevlyar/go-daemon/command.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddCommand is wrapper on AddFlag and SetSigHandler functions.
|
||||||
|
func AddCommand(f Flag, sig os.Signal, handler SignalHandlerFunc) {
|
||||||
|
if f != nil {
|
||||||
|
AddFlag(f, sig)
|
||||||
|
}
|
||||||
|
if handler != nil {
|
||||||
|
SetSigHandler(handler, sig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag is the interface implemented by an object that has two state:
|
||||||
|
// 'set' and 'unset'.
|
||||||
|
type Flag interface {
|
||||||
|
IsSet() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolFlag returns new object that implements interface Flag and
|
||||||
|
// has state 'set' when var with the given address is true.
|
||||||
|
func BoolFlag(f *bool) Flag {
|
||||||
|
return &boolFlag{f}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringFlag returns new object that implements interface Flag and
|
||||||
|
// has state 'set' when var with the given address equals given value of v.
|
||||||
|
func StringFlag(f *string, v string) Flag {
|
||||||
|
return &stringFlag{f, v}
|
||||||
|
}
|
||||||
|
|
||||||
|
type boolFlag struct {
|
||||||
|
b *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *boolFlag) IsSet() bool {
|
||||||
|
if f == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return *f.b
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringFlag struct {
|
||||||
|
s *string
|
||||||
|
v string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *stringFlag) IsSet() bool {
|
||||||
|
if f == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return *f.s == f.v
|
||||||
|
}
|
||||||
|
|
||||||
|
var flags = make(map[Flag]os.Signal)
|
||||||
|
|
||||||
|
// Flags returns flags that was added by the function AddFlag.
|
||||||
|
func Flags() map[Flag]os.Signal {
|
||||||
|
return flags
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFlag adds the flag and signal to the internal map.
|
||||||
|
func AddFlag(f Flag, sig os.Signal) {
|
||||||
|
flags[f] = sig
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendCommands sends active signals to the given process.
|
||||||
|
func SendCommands(p *os.Process) (err error) {
|
||||||
|
for _, sig := range signals() {
|
||||||
|
if err = p.Signal(sig); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActiveFlags returns flags that has the state 'set'.
|
||||||
|
func ActiveFlags() (ret []Flag) {
|
||||||
|
ret = make([]Flag, 0, 1)
|
||||||
|
for f := range flags {
|
||||||
|
if f.IsSet() {
|
||||||
|
ret = append(ret, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func signals() (ret []os.Signal) {
|
||||||
|
ret = make([]os.Signal, 0, 1)
|
||||||
|
for f, sig := range flags {
|
||||||
|
if f.IsSet() {
|
||||||
|
ret = append(ret, sig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
44
vendor/github.com/sevlyar/go-daemon/daemon.go
generated
vendored
Normal file
44
vendor/github.com/sevlyar/go-daemon/daemon.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errNotSupported = errors.New("daemon: Non-POSIX OS is not supported")
|
||||||
|
|
||||||
|
// Mark of daemon process - system environment variable _GO_DAEMON=1
|
||||||
|
const (
|
||||||
|
MARK_NAME = "_GO_DAEMON"
|
||||||
|
MARK_VALUE = "1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Default file permissions for log and pid files.
|
||||||
|
const FILE_PERM = os.FileMode(0640)
|
||||||
|
|
||||||
|
// WasReborn returns true in child process (daemon) and false in parent process.
|
||||||
|
func WasReborn() bool {
|
||||||
|
return os.Getenv(MARK_NAME) == MARK_VALUE
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reborn runs second copy of current process in the given context.
|
||||||
|
// function executes separate parts of code in child process and parent process
|
||||||
|
// and provides demonization of child process. It look similar as the
|
||||||
|
// fork-daemonization, but goroutine-safe.
|
||||||
|
// In success returns *os.Process in parent process and nil in child process.
|
||||||
|
// Otherwise returns error.
|
||||||
|
func (d *Context) Reborn() (child *os.Process, err error) {
|
||||||
|
return d.reborn()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search searches daemons process by given in context pid file name.
|
||||||
|
// If success returns pointer on daemons os.Process structure,
|
||||||
|
// else returns error. Returns nil if filename is empty.
|
||||||
|
func (d *Context) Search() (daemon *os.Process, err error) {
|
||||||
|
return d.search()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release provides correct pid-file release in daemon.
|
||||||
|
func (d *Context) Release() error {
|
||||||
|
return d.release()
|
||||||
|
}
|
||||||
53
vendor/github.com/sevlyar/go-daemon/daemon_stub.go
generated
vendored
Normal file
53
vendor/github.com/sevlyar/go-daemon/daemon_stub.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
//go:build !darwin && !dragonfly && !freebsd && !linux &&!netbsd && !openbsd && !plan9 && !solaris
|
||||||
|
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!plan9,!solaris
|
||||||
|
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Context describes daemon context.
|
||||||
|
type Context struct {
|
||||||
|
// If PidFileName is non-empty, parent process will try to create and lock
|
||||||
|
// pid file with given name. Child process writes process id to file.
|
||||||
|
PidFileName string
|
||||||
|
// Permissions for new pid file.
|
||||||
|
PidFilePerm os.FileMode
|
||||||
|
|
||||||
|
// If LogFileName is non-empty, parent process will create file with given name
|
||||||
|
// and will link to fd 2 (stderr) for child process.
|
||||||
|
LogFileName string
|
||||||
|
// Permissions for new log file.
|
||||||
|
LogFilePerm os.FileMode
|
||||||
|
|
||||||
|
// If WorkDir is non-empty, the child changes into the directory before
|
||||||
|
// creating the process.
|
||||||
|
WorkDir string
|
||||||
|
// If Chroot is non-empty, the child changes root directory
|
||||||
|
Chroot string
|
||||||
|
|
||||||
|
// If Env is non-nil, it gives the environment variables for the
|
||||||
|
// daemon-process in the form returned by os.Environ.
|
||||||
|
// If it is nil, the result of os.Environ will be used.
|
||||||
|
Env []string
|
||||||
|
// If Args is non-nil, it gives the command-line args for the
|
||||||
|
// daemon-process. If it is nil, the result of os.Args will be used
|
||||||
|
// (without program name).
|
||||||
|
Args []string
|
||||||
|
|
||||||
|
// If Umask is non-zero, the daemon-process call Umask() func with given value.
|
||||||
|
Umask int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Context) reborn() (child *os.Process, err error) {
|
||||||
|
return nil, errNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Context) search() (daemon *os.Process, err error) {
|
||||||
|
return nil, errNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Context) release() (err error) {
|
||||||
|
return errNotSupported
|
||||||
|
}
|
||||||
270
vendor/github.com/sevlyar/go-daemon/daemon_unix.go
generated
vendored
Normal file
270
vendor/github.com/sevlyar/go-daemon/daemon_unix.go
generated
vendored
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || plan9 || solaris
|
||||||
|
// +build darwin dragonfly freebsd linux netbsd openbsd plan9 solaris
|
||||||
|
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Context describes daemon context.
|
||||||
|
type Context struct {
|
||||||
|
// If PidFileName is non-empty, parent process will try to create and lock
|
||||||
|
// pid file with given name. Child process writes process id to file.
|
||||||
|
PidFileName string
|
||||||
|
// Permissions for new pid file.
|
||||||
|
PidFilePerm os.FileMode
|
||||||
|
|
||||||
|
// If LogFileName is non-empty, parent process will create file with given name
|
||||||
|
// and will link to fd 2 (stderr) for child process.
|
||||||
|
LogFileName string
|
||||||
|
// Permissions for new log file.
|
||||||
|
LogFilePerm os.FileMode
|
||||||
|
|
||||||
|
// If WorkDir is non-empty, the child changes into the directory before
|
||||||
|
// creating the process.
|
||||||
|
WorkDir string
|
||||||
|
// If Chroot is non-empty, the child changes root directory
|
||||||
|
Chroot string
|
||||||
|
|
||||||
|
// If Env is non-nil, it gives the environment variables for the
|
||||||
|
// daemon-process in the form returned by os.Environ.
|
||||||
|
// If it is nil, the result of os.Environ will be used.
|
||||||
|
Env []string
|
||||||
|
// If Args is non-nil, it gives the command-line args for the
|
||||||
|
// daemon-process. If it is nil, the result of os.Args will be used.
|
||||||
|
Args []string
|
||||||
|
|
||||||
|
// Credential holds user and group identities to be assumed by a daemon-process.
|
||||||
|
Credential *syscall.Credential
|
||||||
|
// If Umask is non-zero, the daemon-process call Umask() func with given value.
|
||||||
|
Umask int
|
||||||
|
|
||||||
|
// Struct contains only serializable public fields (!!!)
|
||||||
|
abspath string
|
||||||
|
pidFile *LockFile
|
||||||
|
logFile *os.File
|
||||||
|
nullFile *os.File
|
||||||
|
|
||||||
|
rpipe, wpipe *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Context) reborn() (child *os.Process, err error) {
|
||||||
|
if !WasReborn() {
|
||||||
|
child, err = d.parent()
|
||||||
|
} else {
|
||||||
|
err = d.child()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Context) search() (daemon *os.Process, err error) {
|
||||||
|
if len(d.PidFileName) > 0 {
|
||||||
|
var pid int
|
||||||
|
if pid, err = ReadPidFile(d.PidFileName); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
daemon, err = os.FindProcess(pid)
|
||||||
|
if err == nil && daemon != nil {
|
||||||
|
// Send a test signal to test if this daemon is actually alive or dead
|
||||||
|
// An error means it is dead
|
||||||
|
if daemon.Signal(syscall.Signal(0)) != nil {
|
||||||
|
daemon = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Context) parent() (child *os.Process, err error) {
|
||||||
|
if err = d.prepareEnv(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer d.closeFiles()
|
||||||
|
if err = d.openFiles(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
attr := &os.ProcAttr{
|
||||||
|
Dir: d.WorkDir,
|
||||||
|
Env: d.Env,
|
||||||
|
Files: d.files(),
|
||||||
|
Sys: &syscall.SysProcAttr{
|
||||||
|
//Chroot: d.Chroot,
|
||||||
|
Credential: d.Credential,
|
||||||
|
Setsid: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if child, err = os.StartProcess(d.abspath, d.Args, attr); err != nil {
|
||||||
|
if d.pidFile != nil {
|
||||||
|
d.pidFile.Remove()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
d.rpipe.Close()
|
||||||
|
encoder := json.NewEncoder(d.wpipe)
|
||||||
|
if err = encoder.Encode(d); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = fmt.Fprint(d.wpipe, "\n\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Context) openFiles() (err error) {
|
||||||
|
if d.PidFilePerm == 0 {
|
||||||
|
d.PidFilePerm = FILE_PERM
|
||||||
|
}
|
||||||
|
if d.LogFilePerm == 0 {
|
||||||
|
d.LogFilePerm = FILE_PERM
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.nullFile, err = os.Open(os.DevNull); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.PidFileName) > 0 {
|
||||||
|
if d.PidFileName, err = filepath.Abs(d.PidFileName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if d.pidFile, err = OpenLockFile(d.PidFileName, d.PidFilePerm); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = d.pidFile.Lock(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(d.Chroot) > 0 {
|
||||||
|
// Calculate PID-file absolute path in child's environment
|
||||||
|
if d.PidFileName, err = filepath.Rel(d.Chroot, d.PidFileName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.PidFileName = "/" + d.PidFileName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.LogFileName) > 0 {
|
||||||
|
if d.LogFileName == "/dev/stdout" {
|
||||||
|
d.logFile = os.Stdout
|
||||||
|
} else if d.LogFileName == "/dev/stderr" {
|
||||||
|
d.logFile = os.Stderr
|
||||||
|
} else if d.logFile, err = os.OpenFile(d.LogFileName,
|
||||||
|
os.O_WRONLY|os.O_CREATE|os.O_APPEND, d.LogFilePerm); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.rpipe, d.wpipe, err = os.Pipe()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Context) closeFiles() (err error) {
|
||||||
|
cl := func(file **os.File) {
|
||||||
|
if *file != nil {
|
||||||
|
(*file).Close()
|
||||||
|
*file = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cl(&d.rpipe)
|
||||||
|
cl(&d.wpipe)
|
||||||
|
cl(&d.logFile)
|
||||||
|
cl(&d.nullFile)
|
||||||
|
if d.pidFile != nil {
|
||||||
|
d.pidFile.Close()
|
||||||
|
d.pidFile = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Context) prepareEnv() (err error) {
|
||||||
|
if d.abspath, err = osExecutable(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.Args) == 0 {
|
||||||
|
d.Args = os.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
mark := fmt.Sprintf("%s=%s", MARK_NAME, MARK_VALUE)
|
||||||
|
if len(d.Env) == 0 {
|
||||||
|
d.Env = os.Environ()
|
||||||
|
}
|
||||||
|
d.Env = append(d.Env, mark)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Context) files() (f []*os.File) {
|
||||||
|
log := d.nullFile
|
||||||
|
if d.logFile != nil {
|
||||||
|
log = d.logFile
|
||||||
|
}
|
||||||
|
|
||||||
|
f = []*os.File{
|
||||||
|
d.rpipe, // (0) stdin
|
||||||
|
log, // (1) stdout
|
||||||
|
log, // (2) stderr
|
||||||
|
d.nullFile, // (3) dup on fd 0 after initialization
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.pidFile != nil {
|
||||||
|
f = append(f, d.pidFile.File) // (4) pid file
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var initialized = false
|
||||||
|
|
||||||
|
func (d *Context) child() (err error) {
|
||||||
|
if initialized {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
initialized = true
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(os.Stdin)
|
||||||
|
if err = decoder.Decode(d); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// create PID file after context decoding to know PID file full path.
|
||||||
|
if len(d.PidFileName) > 0 {
|
||||||
|
d.pidFile = NewLockFile(os.NewFile(4, d.PidFileName))
|
||||||
|
if err = d.pidFile.WritePid(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
d.pidFile.Remove()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = syscallDup(3, 0); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.Umask != 0 {
|
||||||
|
syscall.Umask(int(d.Umask))
|
||||||
|
}
|
||||||
|
if len(d.Chroot) > 0 {
|
||||||
|
err = syscall.Chroot(d.Chroot)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Context) release() error {
|
||||||
|
if !initialized || d.pidFile == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.pidFile.Remove()
|
||||||
|
}
|
||||||
109
vendor/github.com/sevlyar/go-daemon/lock_file.go
generated
vendored
Normal file
109
vendor/github.com/sevlyar/go-daemon/lock_file.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrWouldBlock indicates on locking pid-file by another process.
|
||||||
|
ErrWouldBlock = errors.New("daemon: Resource temporarily unavailable")
|
||||||
|
)
|
||||||
|
|
||||||
|
// LockFile wraps *os.File and provide functions for locking of files.
|
||||||
|
type LockFile struct {
|
||||||
|
*os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLockFile returns a new LockFile with the given File.
|
||||||
|
func NewLockFile(file *os.File) *LockFile {
|
||||||
|
return &LockFile{file}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePidFile opens the named file, applies exclusive lock and writes
|
||||||
|
// current process id to file.
|
||||||
|
func CreatePidFile(name string, perm os.FileMode) (lock *LockFile, err error) {
|
||||||
|
if lock, err = OpenLockFile(name, perm); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = lock.Lock(); err != nil {
|
||||||
|
lock.Remove()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = lock.WritePid(); err != nil {
|
||||||
|
lock.Remove()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenLockFile opens the named file with flags os.O_RDWR|os.O_CREATE and specified perm.
|
||||||
|
// If successful, function returns LockFile for opened file.
|
||||||
|
func OpenLockFile(name string, perm os.FileMode) (lock *LockFile, err error) {
|
||||||
|
var file *os.File
|
||||||
|
if file, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE, perm); err == nil {
|
||||||
|
lock = &LockFile{file}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock apply exclusive lock on an open file. If file already locked, returns error.
|
||||||
|
func (file *LockFile) Lock() error {
|
||||||
|
return lockFile(file.Fd())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock remove exclusive lock on an open file.
|
||||||
|
func (file *LockFile) Unlock() error {
|
||||||
|
return unlockFile(file.Fd())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadPidFile reads process id from file with give name and returns pid.
|
||||||
|
// If unable read from a file, returns error.
|
||||||
|
func ReadPidFile(name string) (pid int, err error) {
|
||||||
|
var file *os.File
|
||||||
|
if file, err = os.OpenFile(name, os.O_RDONLY, 0640); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
lock := &LockFile{file}
|
||||||
|
pid, err = lock.ReadPid()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// WritePid writes current process id to an open file.
|
||||||
|
func (file *LockFile) WritePid() (err error) {
|
||||||
|
if _, err = file.Seek(0, os.SEEK_SET); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var fileLen int
|
||||||
|
if fileLen, err = fmt.Fprint(file, os.Getpid()); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = file.Truncate(int64(fileLen)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = file.Sync()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadPid reads process id from file and returns pid.
|
||||||
|
// If unable read from a file, returns error.
|
||||||
|
func (file *LockFile) ReadPid() (pid int, err error) {
|
||||||
|
if _, err = file.Seek(0, os.SEEK_SET); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = fmt.Fscan(file, &pid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes lock, closes and removes an open file.
|
||||||
|
func (file *LockFile) Remove() error {
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
if err := file.Unlock(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.Remove(file.Name())
|
||||||
|
}
|
||||||
41
vendor/github.com/sevlyar/go-daemon/lock_file_solaris.go
generated
vendored
Normal file
41
vendor/github.com/sevlyar/go-daemon/lock_file_solaris.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
//go:build solaris
|
||||||
|
// +build solaris
|
||||||
|
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func lockFile(fd uintptr) error {
|
||||||
|
lockInfo := syscall.Flock_t{
|
||||||
|
Type: syscall.F_WRLCK,
|
||||||
|
Whence: io.SeekStart,
|
||||||
|
Start: 0,
|
||||||
|
Len: 0,
|
||||||
|
}
|
||||||
|
if err := syscall.FcntlFlock(fd, syscall.F_SETLK, &lockInfo); err != nil {
|
||||||
|
if err == syscall.EAGAIN {
|
||||||
|
err = ErrWouldBlock
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unlockFile(fd uintptr) error {
|
||||||
|
lockInfo := syscall.Flock_t{
|
||||||
|
Type: syscall.F_UNLCK,
|
||||||
|
Whence: io.SeekStart,
|
||||||
|
Start: 0,
|
||||||
|
Len: 0,
|
||||||
|
}
|
||||||
|
if err := syscall.FcntlFlock(fd, syscall.F_GETLK, &lockInfo); err != nil {
|
||||||
|
if err == syscall.EAGAIN {
|
||||||
|
err = ErrWouldBlock
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
12
vendor/github.com/sevlyar/go-daemon/lock_file_stub.go
generated
vendored
Normal file
12
vendor/github.com/sevlyar/go-daemon/lock_file_stub.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
//go:build !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !plan9 && !solaris
|
||||||
|
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!plan9,!solaris
|
||||||
|
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
func lockFile(fd uintptr) error {
|
||||||
|
return errNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func unlockFile(fd uintptr) error {
|
||||||
|
return errNotSupported
|
||||||
|
}
|
||||||
24
vendor/github.com/sevlyar/go-daemon/lock_file_unix.go
generated
vendored
Normal file
24
vendor/github.com/sevlyar/go-daemon/lock_file_unix.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || plan9
|
||||||
|
// +build darwin dragonfly freebsd linux netbsd openbsd plan9
|
||||||
|
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func lockFile(fd uintptr) error {
|
||||||
|
err := syscall.Flock(int(fd), syscall.LOCK_EX|syscall.LOCK_NB)
|
||||||
|
if err == syscall.EWOULDBLOCK {
|
||||||
|
err = ErrWouldBlock
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func unlockFile(fd uintptr) error {
|
||||||
|
err := syscall.Flock(int(fd), syscall.LOCK_UN)
|
||||||
|
if err == syscall.EWOULDBLOCK {
|
||||||
|
err = ErrWouldBlock
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
12
vendor/github.com/sevlyar/go-daemon/os_executable.go
generated
vendored
Normal file
12
vendor/github.com/sevlyar/go-daemon/os_executable.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
//go:build go1.8
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func osExecutable() (string, error) {
|
||||||
|
return os.Executable()
|
||||||
|
}
|
||||||
12
vendor/github.com/sevlyar/go-daemon/os_executable_pre18.go
generated
vendored
Normal file
12
vendor/github.com/sevlyar/go-daemon/os_executable_pre18.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
//go:build !go1.8
|
||||||
|
// +build !go1.8
|
||||||
|
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kardianos/osext"
|
||||||
|
)
|
||||||
|
|
||||||
|
func osExecutable() (string, error) {
|
||||||
|
return osext.Executable()
|
||||||
|
}
|
||||||
59
vendor/github.com/sevlyar/go-daemon/signal.go
generated
vendored
Normal file
59
vendor/github.com/sevlyar/go-daemon/signal.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrStop should be returned signal handler function
|
||||||
|
// for termination of handling signals.
|
||||||
|
var ErrStop = errors.New("stop serve signals")
|
||||||
|
|
||||||
|
// SignalHandlerFunc is the interface for signal handler functions.
|
||||||
|
type SignalHandlerFunc func(sig os.Signal) (err error)
|
||||||
|
|
||||||
|
// SetSigHandler sets handler for the given signals.
|
||||||
|
// SIGTERM has the default handler, he returns ErrStop.
|
||||||
|
func SetSigHandler(handler SignalHandlerFunc, signals ...os.Signal) {
|
||||||
|
for _, sig := range signals {
|
||||||
|
handlers[sig] = handler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeSignals calls handlers for system signals.
|
||||||
|
func ServeSignals() (err error) {
|
||||||
|
signals := make([]os.Signal, 0, len(handlers))
|
||||||
|
for sig := range handlers {
|
||||||
|
signals = append(signals, sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := make(chan os.Signal, 8)
|
||||||
|
signal.Notify(ch, signals...)
|
||||||
|
|
||||||
|
for sig := range ch {
|
||||||
|
err = handlers[sig](sig)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signal.Stop(ch)
|
||||||
|
|
||||||
|
if err == ErrStop {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var handlers = make(map[os.Signal]SignalHandlerFunc)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
handlers[syscall.SIGTERM] = sigtermDefaultHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func sigtermDefaultHandler(sig os.Signal) error {
|
||||||
|
return ErrStop
|
||||||
|
}
|
||||||
13
vendor/github.com/sevlyar/go-daemon/syscall_dup.go
generated
vendored
Normal file
13
vendor/github.com/sevlyar/go-daemon/syscall_dup.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
//go:build (!linux || !arm64) && (!linux || !riscv64) && !windows && go1.7
|
||||||
|
// +build !linux !arm64
|
||||||
|
// +build !linux !riscv64
|
||||||
|
// +build !windows
|
||||||
|
// +build go1.7
|
||||||
|
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
func syscallDup(oldfd int, newfd int) (err error) {
|
||||||
|
return unix.Dup2(oldfd, newfd)
|
||||||
|
}
|
||||||
13
vendor/github.com/sevlyar/go-daemon/syscall_dup3.go
generated
vendored
Normal file
13
vendor/github.com/sevlyar/go-daemon/syscall_dup3.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
//go:build linux && (riscv64 || arm64)
|
||||||
|
// +build linux
|
||||||
|
// +build riscv64 arm64
|
||||||
|
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
func syscallDup(oldfd int, newfd int) (err error) {
|
||||||
|
// linux_arm64 platform doesn't have syscall.Dup2
|
||||||
|
// so use the nearly identical syscall.Dup3 instead.
|
||||||
|
return syscall.Dup3(oldfd, newfd, 0)
|
||||||
|
}
|
||||||
14
vendor/github.com/sevlyar/go-daemon/syscall_dup_pre17.go
generated
vendored
Normal file
14
vendor/github.com/sevlyar/go-daemon/syscall_dup_pre17.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
//go:build (!linux || !arm64) && !windows && !go1.7
|
||||||
|
// +build !linux !arm64
|
||||||
|
// +build !windows
|
||||||
|
// +build !go1.7
|
||||||
|
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func syscallDup(oldfd int, newfd int) (err error) {
|
||||||
|
return syscall.Dup2(oldfd, newfd)
|
||||||
|
}
|
||||||
79
vendor/github.com/shirou/gopsutil/v3/cpu/cpu_aix_nocgo.go
generated
vendored
79
vendor/github.com/shirou/gopsutil/v3/cpu/cpu_aix_nocgo.go
generated
vendored
@@ -12,8 +12,57 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
|
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
|
||||||
|
var ret []TimesStat
|
||||||
if percpu {
|
if percpu {
|
||||||
return []TimesStat{}, common.ErrNotImplementedError
|
per_out, err := invoke.CommandWithContext(ctx, "sar", "-u", "-P", "ALL", "10", "1")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
lines := strings.Split(string(per_out), "\n")
|
||||||
|
if len(lines) < 6 {
|
||||||
|
return []TimesStat{}, common.ErrNotImplementedError
|
||||||
|
}
|
||||||
|
|
||||||
|
hp := strings.Fields(lines[5]) // headers
|
||||||
|
for l := 6; l < len(lines)-1; l++ {
|
||||||
|
ct := &TimesStat{}
|
||||||
|
v := strings.Fields(lines[l]) // values
|
||||||
|
for i, header := range hp {
|
||||||
|
// We're done in any of these use cases
|
||||||
|
if i >= len(v) || v[0] == "-" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position variable for v
|
||||||
|
pos := i
|
||||||
|
// There is a missing field at the beginning of all but the first line
|
||||||
|
// so adjust the position
|
||||||
|
if l > 6 {
|
||||||
|
pos = i - 1
|
||||||
|
}
|
||||||
|
// We don't want invalid positions
|
||||||
|
if pos < 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, err := strconv.ParseFloat(v[pos], 64); err == nil {
|
||||||
|
switch header {
|
||||||
|
case `cpu`:
|
||||||
|
ct.CPU = strconv.FormatFloat(t, 'f', -1, 64)
|
||||||
|
case `%usr`:
|
||||||
|
ct.User = t
|
||||||
|
case `%sys`:
|
||||||
|
ct.System = t
|
||||||
|
case `%wio`:
|
||||||
|
ct.Iowait = t
|
||||||
|
case `%idle`:
|
||||||
|
ct.Idle = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Valid CPU data, so append it
|
||||||
|
ret = append(ret, *ct)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
out, err := invoke.CommandWithContext(ctx, "sar", "-u", "10", "1")
|
out, err := invoke.CommandWithContext(ctx, "sar", "-u", "10", "1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -24,26 +73,28 @@ func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
|
|||||||
return []TimesStat{}, common.ErrNotImplementedError
|
return []TimesStat{}, common.ErrNotImplementedError
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := TimesStat{CPU: "cpu-total"}
|
ct := &TimesStat{CPU: "cpu-total"}
|
||||||
h := strings.Fields(lines[len(lines)-3]) // headers
|
h := strings.Fields(lines[len(lines)-3]) // headers
|
||||||
v := strings.Fields(lines[len(lines)-2]) // values
|
v := strings.Fields(lines[len(lines)-2]) // values
|
||||||
for i, header := range h {
|
for i, header := range h {
|
||||||
if t, err := strconv.ParseFloat(v[i], 64); err == nil {
|
if t, err := strconv.ParseFloat(v[i], 64); err == nil {
|
||||||
switch header {
|
switch header {
|
||||||
case `%usr`:
|
case `%usr`:
|
||||||
ret.User = t
|
ct.User = t
|
||||||
case `%sys`:
|
case `%sys`:
|
||||||
ret.System = t
|
ct.System = t
|
||||||
case `%wio`:
|
case `%wio`:
|
||||||
ret.Iowait = t
|
ct.Iowait = t
|
||||||
case `%idle`:
|
case `%idle`:
|
||||||
ret.Idle = t
|
ct.Idle = t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return []TimesStat{ret}, nil
|
ret = append(ret, *ct)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||||
@@ -78,6 +129,20 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
} else if strings.HasPrefix(line, "System Model:") {
|
||||||
|
p := strings.Split(string(line), ":")
|
||||||
|
if p != nil {
|
||||||
|
ret.VendorID = strings.TrimSpace(p[1])
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(line, "Processor Type:") {
|
||||||
|
p := strings.Split(string(line), ":")
|
||||||
|
if p != nil {
|
||||||
|
c := strings.Split(string(p[1]), "_")
|
||||||
|
if c != nil {
|
||||||
|
ret.Family = strings.TrimSpace(c[0])
|
||||||
|
ret.Model = strings.TrimSpace(c[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return []InfoStat{ret}, nil
|
return []InfoStat{ret}, nil
|
||||||
|
|||||||
2
vendor/github.com/shirou/gopsutil/v3/internal/common/sleep.go
generated
vendored
2
vendor/github.com/shirou/gopsutil/v3/internal/common/sleep.go
generated
vendored
@@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Sleep awaits for provided interval.
|
// Sleep awaits for provided interval.
|
||||||
// Can be interrupted by context cancelation.
|
// Can be interrupted by context cancellation.
|
||||||
func Sleep(ctx context.Context, interval time.Duration) error {
|
func Sleep(ctx context.Context, interval time.Duration) error {
|
||||||
timer := time.NewTimer(interval)
|
timer := time.NewTimer(interval)
|
||||||
select {
|
select {
|
||||||
|
|||||||
8
vendor/github.com/shirou/gopsutil/v3/mem/mem_aix_nocgo.go
generated
vendored
8
vendor/github.com/shirou/gopsutil/v3/mem/mem_aix_nocgo.go
generated
vendored
@@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||||
vmem, swap, err := callSVMon(ctx)
|
vmem, swap, err := callSVMon(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||||
_, swap, err := callSVMon(ctx)
|
_, swap, err := callSVMon(ctx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
|||||||
return swap, nil
|
return swap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func callSVMon(ctx context.Context) (*VirtualMemoryStat, *SwapMemoryStat, error) {
|
func callSVMon(ctx context.Context, virt bool) (*VirtualMemoryStat, *SwapMemoryStat, error) {
|
||||||
out, err := invoke.CommandWithContext(ctx, "svmon", "-G")
|
out, err := invoke.CommandWithContext(ctx, "svmon", "-G")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -45,7 +45,7 @@ func callSVMon(ctx context.Context) (*VirtualMemoryStat, *SwapMemoryStat, error)
|
|||||||
vmem := &VirtualMemoryStat{}
|
vmem := &VirtualMemoryStat{}
|
||||||
swap := &SwapMemoryStat{}
|
swap := &SwapMemoryStat{}
|
||||||
for _, line := range strings.Split(string(out), "\n") {
|
for _, line := range strings.Split(string(out), "\n") {
|
||||||
if strings.HasPrefix(line, "memory") {
|
if virt && strings.HasPrefix(line, "memory") {
|
||||||
p := strings.Fields(line)
|
p := strings.Fields(line)
|
||||||
if len(p) > 2 {
|
if len(p) > 2 {
|
||||||
if t, err := strconv.ParseUint(p[1], 10, 64); err == nil {
|
if t, err := strconv.ParseUint(p[1], 10, 64); err == nil {
|
||||||
|
|||||||
2
vendor/github.com/shirou/gopsutil/v3/net/net_linux.go
generated
vendored
2
vendor/github.com/shirou/gopsutil/v3/net/net_linux.go
generated
vendored
@@ -471,7 +471,7 @@ func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cound not get pid(s), %d: %w", pid, err)
|
return nil, fmt.Errorf("could not get pid(s), %d: %w", pid, err)
|
||||||
}
|
}
|
||||||
return statsFromInodesWithContext(ctx, root, pid, tmap, inodes, skipUids)
|
return statsFromInodesWithContext(ctx, root, pid, tmap, inodes, skipUids)
|
||||||
}
|
}
|
||||||
|
|||||||
42
vendor/github.com/shirou/gopsutil/v3/net/net_openbsd.go
generated
vendored
42
vendor/github.com/shirou/gopsutil/v3/net/net_openbsd.go
generated
vendored
@@ -23,9 +23,9 @@ func ParseNetstat(output string, mode string,
|
|||||||
|
|
||||||
exists := make([]string, 0, len(lines)-1)
|
exists := make([]string, 0, len(lines)-1)
|
||||||
|
|
||||||
columns := 6
|
columns := 9
|
||||||
if mode == "ind" {
|
if mode == "inb" {
|
||||||
columns = 10
|
columns = 6
|
||||||
}
|
}
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
values := strings.Fields(line)
|
values := strings.Fields(line)
|
||||||
@@ -48,18 +48,23 @@ func ParseNetstat(output string, mode string,
|
|||||||
|
|
||||||
parsed := make([]uint64, 0, 8)
|
parsed := make([]uint64, 0, 8)
|
||||||
var vv []string
|
var vv []string
|
||||||
if mode == "inb" {
|
switch mode {
|
||||||
|
case "inb":
|
||||||
vv = []string{
|
vv = []string{
|
||||||
values[base+3], // BytesRecv
|
values[base+3], // BytesRecv
|
||||||
values[base+4], // BytesSent
|
values[base+4], // BytesSent
|
||||||
}
|
}
|
||||||
} else {
|
case "ind":
|
||||||
vv = []string{
|
vv = []string{
|
||||||
values[base+3], // Ipkts
|
values[base+3], // Ipkts
|
||||||
values[base+4], // Ierrs
|
values[base+4], // Idrop
|
||||||
values[base+5], // Opkts
|
values[base+5], // Opkts
|
||||||
|
values[base+6], // Odrops
|
||||||
|
}
|
||||||
|
case "ine":
|
||||||
|
vv = []string{
|
||||||
|
values[base+4], // Ierrs
|
||||||
values[base+6], // Oerrs
|
values[base+6], // Oerrs
|
||||||
values[base+8], // Drops
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, target := range vv {
|
for _, target := range vv {
|
||||||
@@ -80,16 +85,19 @@ func ParseNetstat(output string, mode string,
|
|||||||
if !present {
|
if !present {
|
||||||
n = IOCountersStat{Name: values[0]}
|
n = IOCountersStat{Name: values[0]}
|
||||||
}
|
}
|
||||||
if mode == "inb" {
|
|
||||||
|
switch mode {
|
||||||
|
case "inb":
|
||||||
n.BytesRecv = parsed[0]
|
n.BytesRecv = parsed[0]
|
||||||
n.BytesSent = parsed[1]
|
n.BytesSent = parsed[1]
|
||||||
} else {
|
case "ind":
|
||||||
n.PacketsRecv = parsed[0]
|
n.PacketsRecv = parsed[0]
|
||||||
n.Errin = parsed[1]
|
n.Dropin = parsed[1]
|
||||||
n.PacketsSent = parsed[2]
|
n.PacketsSent = parsed[2]
|
||||||
n.Errout = parsed[3]
|
n.Dropout = parsed[3]
|
||||||
n.Dropin = parsed[4]
|
case "ine":
|
||||||
n.Dropout = parsed[4]
|
n.Errin = parsed[0]
|
||||||
|
n.Errout = parsed[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
iocs[n.Name] = n
|
iocs[n.Name] = n
|
||||||
@@ -114,6 +122,10 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
out3, err := invoke.CommandWithContext(ctx, netstat, "-ine")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
iocs := make(map[string]IOCountersStat)
|
iocs := make(map[string]IOCountersStat)
|
||||||
|
|
||||||
lines := strings.Split(string(out), "\n")
|
lines := strings.Split(string(out), "\n")
|
||||||
@@ -127,6 +139,10 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
err = ParseNetstat(string(out3), "ine", iocs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
for _, ioc := range iocs {
|
for _, ioc := range iocs {
|
||||||
ret = append(ret, ioc)
|
ret = append(ret, ioc)
|
||||||
|
|||||||
7
vendor/github.com/shirou/gopsutil/v3/process/process_darwin_nocgo.go
generated
vendored
7
vendor/github.com/shirou/gopsutil/v3/process/process_darwin_nocgo.go
generated
vendored
@@ -24,14 +24,21 @@ func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
|||||||
}
|
}
|
||||||
txtFound := 0
|
txtFound := 0
|
||||||
lines := strings.Split(string(out), "\n")
|
lines := strings.Split(string(out), "\n")
|
||||||
|
fallback := ""
|
||||||
for i := 1; i < len(lines); i++ {
|
for i := 1; i < len(lines); i++ {
|
||||||
if lines[i] == "ftxt" {
|
if lines[i] == "ftxt" {
|
||||||
txtFound++
|
txtFound++
|
||||||
|
if txtFound == 1 {
|
||||||
|
fallback = lines[i-1][1:]
|
||||||
|
}
|
||||||
if txtFound == 2 {
|
if txtFound == 2 {
|
||||||
return lines[i-1][1:], nil
|
return lines[i-1][1:], nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if fallback != "" {
|
||||||
|
return fallback, nil
|
||||||
|
}
|
||||||
return "", fmt.Errorf("missing txt data returned by lsof")
|
return "", fmt.Errorf("missing txt data returned by lsof")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
34
vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_arm64.go
generated
vendored
34
vendor/github.com/shirou/gopsutil/v3/process/process_freebsd_arm64.go
generated
vendored
@@ -1,8 +1,7 @@
|
|||||||
//go:build freebsd && arm64
|
//go:build freebsd && arm64
|
||||||
// +build freebsd,arm64
|
|
||||||
|
|
||||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||||
// cgo -godefs process/types_freebsd.go
|
// cgo -godefs types_freebsd.go
|
||||||
|
|
||||||
package process
|
package process
|
||||||
|
|
||||||
@@ -82,14 +81,14 @@ type Rlimit struct {
|
|||||||
type KinfoProc struct {
|
type KinfoProc struct {
|
||||||
Structsize int32
|
Structsize int32
|
||||||
Layout int32
|
Layout int32
|
||||||
Args *int64 /* pargs */
|
Args int64 /* pargs */
|
||||||
Paddr *int64 /* proc */
|
Paddr int64 /* proc */
|
||||||
Addr *int64 /* user */
|
Addr int64 /* user */
|
||||||
Tracep *int64 /* vnode */
|
Tracep int64 /* vnode */
|
||||||
Textvp *int64 /* vnode */
|
Textvp int64 /* vnode */
|
||||||
Fd *int64 /* filedesc */
|
Fd int64 /* filedesc */
|
||||||
Vmspace *int64 /* vmspace */
|
Vmspace int64 /* vmspace */
|
||||||
Wchan *byte
|
Wchan int64
|
||||||
Pid int32
|
Pid int32
|
||||||
Ppid int32
|
Ppid int32
|
||||||
Pgid int32
|
Pgid int32
|
||||||
@@ -140,7 +139,7 @@ type KinfoProc struct {
|
|||||||
Wmesg [9]uint8
|
Wmesg [9]uint8
|
||||||
Login [18]uint8
|
Login [18]uint8
|
||||||
Lockname [9]uint8
|
Lockname [9]uint8
|
||||||
Comm [20]int8
|
Comm [20]int8 // changed from uint8 by hand
|
||||||
Emul [17]uint8
|
Emul [17]uint8
|
||||||
Loginclass [18]uint8
|
Loginclass [18]uint8
|
||||||
Moretdname [4]uint8
|
Moretdname [4]uint8
|
||||||
@@ -159,11 +158,12 @@ type KinfoProc struct {
|
|||||||
Pri Priority
|
Pri Priority
|
||||||
Rusage Rusage
|
Rusage Rusage
|
||||||
Rusage_ch Rusage
|
Rusage_ch Rusage
|
||||||
Pcb *int64 /* pcb */
|
Pcb int64 /* pcb */
|
||||||
Kstack *byte
|
Kstack int64
|
||||||
Udata *byte
|
Udata int64
|
||||||
Tdaddr *int64 /* thread */
|
Tdaddr int64 /* thread */
|
||||||
Spareptrs [6]*byte
|
Pd int64 /* pwddesc, not accurate */
|
||||||
|
Spareptrs [5]int64
|
||||||
Sparelongs [12]int64
|
Sparelongs [12]int64
|
||||||
Sflag int64
|
Sflag int64
|
||||||
Tdflags int64
|
Tdflags int64
|
||||||
@@ -195,7 +195,7 @@ type KinfoVmentry struct {
|
|||||||
Vn_rdev_freebsd11 uint32
|
Vn_rdev_freebsd11 uint32
|
||||||
Vn_mode uint16
|
Vn_mode uint16
|
||||||
Status uint16
|
Status uint16
|
||||||
Vn_fsid uint64
|
Type_spec [8]byte
|
||||||
Vn_rdev uint64
|
Vn_rdev uint64
|
||||||
X_kve_ispare [8]int32
|
X_kve_ispare [8]int32
|
||||||
Path [1024]uint8
|
Path [1024]uint8
|
||||||
|
|||||||
7
vendor/github.com/shirou/gopsutil/v3/process/process_openbsd.go
generated
vendored
7
vendor/github.com/shirou/gopsutil/v3/process/process_openbsd.go
generated
vendored
@@ -68,7 +68,12 @@ func (p *Process) NameWithContext(ctx context.Context) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
|
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
|
||||||
return "", common.ErrNotImplementedError
|
mib := []int32{CTLKern, KernProcCwd, p.Pid}
|
||||||
|
buf, _, err := common.CallSyscall(mib)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return common.ByteToString(buf), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
||||||
|
|||||||
1
vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_386.go
generated
vendored
1
vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_386.go
generated
vendored
@@ -14,6 +14,7 @@ const (
|
|||||||
KernProcProc = 8
|
KernProcProc = 8
|
||||||
KernProcPathname = 12
|
KernProcPathname = 12
|
||||||
KernProcArgs = 55
|
KernProcArgs = 55
|
||||||
|
KernProcCwd = 78
|
||||||
KernProcArgv = 1
|
KernProcArgv = 1
|
||||||
KernProcEnv = 3
|
KernProcEnv = 3
|
||||||
)
|
)
|
||||||
|
|||||||
1
vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_amd64.go
generated
vendored
1
vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_amd64.go
generated
vendored
@@ -11,6 +11,7 @@ const (
|
|||||||
KernProcProc = 8
|
KernProcProc = 8
|
||||||
KernProcPathname = 12
|
KernProcPathname = 12
|
||||||
KernProcArgs = 55
|
KernProcArgs = 55
|
||||||
|
KernProcCwd = 78
|
||||||
KernProcArgv = 1
|
KernProcArgv = 1
|
||||||
KernProcEnv = 3
|
KernProcEnv = 3
|
||||||
)
|
)
|
||||||
|
|||||||
1
vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_arm.go
generated
vendored
1
vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_arm.go
generated
vendored
@@ -14,6 +14,7 @@ const (
|
|||||||
KernProcProc = 8
|
KernProcProc = 8
|
||||||
KernProcPathname = 12
|
KernProcPathname = 12
|
||||||
KernProcArgs = 55
|
KernProcArgs = 55
|
||||||
|
KernProcCwd = 78
|
||||||
KernProcArgv = 1
|
KernProcArgv = 1
|
||||||
KernProcEnv = 3
|
KernProcEnv = 3
|
||||||
)
|
)
|
||||||
|
|||||||
1
vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_arm64.go
generated
vendored
1
vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_arm64.go
generated
vendored
@@ -14,6 +14,7 @@ const (
|
|||||||
KernProcProc = 8
|
KernProcProc = 8
|
||||||
KernProcPathname = 12
|
KernProcPathname = 12
|
||||||
KernProcArgs = 55
|
KernProcArgs = 55
|
||||||
|
KernProcCwd = 78
|
||||||
KernProcArgv = 1
|
KernProcArgv = 1
|
||||||
KernProcEnv = 3
|
KernProcEnv = 3
|
||||||
)
|
)
|
||||||
|
|||||||
1
vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_riscv64.go
generated
vendored
1
vendor/github.com/shirou/gopsutil/v3/process/process_openbsd_riscv64.go
generated
vendored
@@ -14,6 +14,7 @@ const (
|
|||||||
KernProcProc = 8
|
KernProcProc = 8
|
||||||
KernProcPathname = 12
|
KernProcPathname = 12
|
||||||
KernProcArgs = 55
|
KernProcArgs = 55
|
||||||
|
KernProcCwd = 78
|
||||||
KernProcArgv = 1
|
KernProcArgv = 1
|
||||||
KernProcEnv = 3
|
KernProcEnv = 3
|
||||||
)
|
)
|
||||||
|
|||||||
2
vendor/github.com/tklauser/go-sysconf/.cirrus.yml
generated
vendored
2
vendor/github.com/tklauser/go-sysconf/.cirrus.yml
generated
vendored
@@ -1,6 +1,6 @@
|
|||||||
env:
|
env:
|
||||||
CIRRUS_CLONE_DEPTH: 1
|
CIRRUS_CLONE_DEPTH: 1
|
||||||
GO_VERSION: go1.21.3
|
GO_VERSION: go1.20
|
||||||
|
|
||||||
freebsd_12_task:
|
freebsd_12_task:
|
||||||
freebsd_instance:
|
freebsd_instance:
|
||||||
|
|||||||
20
vendor/github.com/tklauser/go-sysconf/sysconf_linux.go
generated
vendored
20
vendor/github.com/tklauser/go-sysconf/sysconf_linux.go
generated
vendored
@@ -6,6 +6,7 @@ package sysconf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -25,7 +26,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func readProcFsInt64(path string, fallback int64) int64 {
|
func readProcFsInt64(path string, fallback int64) int64 {
|
||||||
data, err := os.ReadFile(path)
|
data, err := ioutil.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fallback
|
return fallback
|
||||||
}
|
}
|
||||||
@@ -85,16 +86,10 @@ func getNprocsProcStat() (int64, error) {
|
|||||||
s := bufio.NewScanner(f)
|
s := bufio.NewScanner(f)
|
||||||
for s.Scan() {
|
for s.Scan() {
|
||||||
if line := strings.TrimSpace(s.Text()); strings.HasPrefix(line, "cpu") {
|
if line := strings.TrimSpace(s.Text()); strings.HasPrefix(line, "cpu") {
|
||||||
cpu, _, found := strings.Cut(line, " ")
|
l := strings.SplitN(line, " ", 2)
|
||||||
if found {
|
_, err := strconv.ParseInt(l[0][3:], 10, 64)
|
||||||
// skip first line with accumulated values
|
if err == nil {
|
||||||
if cpu == "cpu" {
|
count++
|
||||||
continue
|
|
||||||
}
|
|
||||||
_, err := strconv.ParseInt(cpu[len("cpu"):], 10, 64)
|
|
||||||
if err == nil {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The current format of /proc/stat has all the
|
// The current format of /proc/stat has all the
|
||||||
@@ -103,9 +98,6 @@ func getNprocsProcStat() (int64, error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := s.Err(); err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
return count, nil
|
return count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
vendor/github.com/tklauser/go-sysconf/sysconf_netbsd.go
generated
vendored
18
vendor/github.com/tklauser/go-sysconf/sysconf_netbsd.go
generated
vendored
@@ -25,10 +25,10 @@ const (
|
|||||||
_POSIX2_UPE = -1
|
_POSIX2_UPE = -1
|
||||||
)
|
)
|
||||||
|
|
||||||
var clktck struct {
|
var (
|
||||||
sync.Once
|
clktck int64
|
||||||
v int64
|
clktckOnce sync.Once
|
||||||
}
|
)
|
||||||
|
|
||||||
func sysconfPOSIX(name int) (int64, error) {
|
func sysconfPOSIX(name int) (int64, error) {
|
||||||
// NetBSD does not define all _POSIX_* values used in sysconf_posix.go
|
// NetBSD does not define all _POSIX_* values used in sysconf_posix.go
|
||||||
@@ -42,6 +42,7 @@ func sysconf(name int) (int64, error) {
|
|||||||
// Duplicate the relevant values here.
|
// Duplicate the relevant values here.
|
||||||
|
|
||||||
switch name {
|
switch name {
|
||||||
|
|
||||||
// 1003.1
|
// 1003.1
|
||||||
case SC_ARG_MAX:
|
case SC_ARG_MAX:
|
||||||
return sysctl32("kern.argmax"), nil
|
return sysctl32("kern.argmax"), nil
|
||||||
@@ -54,14 +55,13 @@ func sysconf(name int) (int64, error) {
|
|||||||
}
|
}
|
||||||
return -1, nil
|
return -1, nil
|
||||||
case SC_CLK_TCK:
|
case SC_CLK_TCK:
|
||||||
// TODO: use sync.OnceValue once Go 1.21 is the minimal supported version
|
clktckOnce.Do(func() {
|
||||||
clktck.Do(func() {
|
clktck = -1
|
||||||
clktck.v = -1
|
|
||||||
if ci, err := unix.SysctlClockinfo("kern.clockrate"); err == nil {
|
if ci, err := unix.SysctlClockinfo("kern.clockrate"); err == nil {
|
||||||
clktck.v = int64(ci.Hz)
|
clktck = int64(ci.Hz)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return clktck.v, nil
|
return clktck, nil
|
||||||
case SC_NGROUPS_MAX:
|
case SC_NGROUPS_MAX:
|
||||||
return sysctl32("kern.ngroups"), nil
|
return sysctl32("kern.ngroups"), nil
|
||||||
case SC_JOB_CONTROL:
|
case SC_JOB_CONTROL:
|
||||||
|
|||||||
2
vendor/github.com/tklauser/numcpus/.cirrus.yml
generated
vendored
2
vendor/github.com/tklauser/numcpus/.cirrus.yml
generated
vendored
@@ -1,6 +1,6 @@
|
|||||||
env:
|
env:
|
||||||
CIRRUS_CLONE_DEPTH: 1
|
CIRRUS_CLONE_DEPTH: 1
|
||||||
GO_VERSION: go1.21.3
|
GO_VERSION: go1.20
|
||||||
|
|
||||||
freebsd_12_task:
|
freebsd_12_task:
|
||||||
freebsd_instance:
|
freebsd_instance:
|
||||||
|
|||||||
13
vendor/github.com/tklauser/numcpus/numcpus_linux.go
generated
vendored
13
vendor/github.com/tklauser/numcpus/numcpus_linux.go
generated
vendored
@@ -15,6 +15,7 @@
|
|||||||
package numcpus
|
package numcpus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -34,7 +35,7 @@ func getFromCPUAffinity() (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func readCPURange(file string) (int, error) {
|
func readCPURange(file string) (int, error) {
|
||||||
buf, err := os.ReadFile(filepath.Join(sysfsCPUBasePath, file))
|
buf, err := ioutil.ReadFile(filepath.Join(sysfsCPUBasePath, file))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -47,16 +48,16 @@ func parseCPURange(cpus string) (int, error) {
|
|||||||
if len(cpuRange) == 0 {
|
if len(cpuRange) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
from, to, found := strings.Cut(cpuRange, "-")
|
rangeOp := strings.SplitN(cpuRange, "-", 2)
|
||||||
first, err := strconv.ParseUint(from, 10, 32)
|
first, err := strconv.ParseUint(rangeOp[0], 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if !found {
|
if len(rangeOp) == 1 {
|
||||||
n++
|
n++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
last, err := strconv.ParseUint(to, 10, 32)
|
last, err := strconv.ParseUint(rangeOp[1], 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -88,7 +89,7 @@ func getConfigured() (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getKernelMax() (int, error) {
|
func getKernelMax() (int, error) {
|
||||||
buf, err := os.ReadFile(filepath.Join(sysfsCPUBasePath, "kernel_max"))
|
buf, err := ioutil.ReadFile(filepath.Join(sysfsCPUBasePath, "kernel_max"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user