Merge branch 'dev' into iam

This commit is contained in:
Ingo Oppermann 2023-05-15 21:17:23 +02:00
commit ead6e64442
No known key found for this signature in database
GPG Key ID: 2AB32426E9DD229E
152 changed files with 2375 additions and 713 deletions

View File

@ -2,4 +2,4 @@
OS_NAME=alpine
OS_VERSION=3.16
GOLANG_IMAGE=golang:1.20-alpine3.16
CORE_VERSION=16.12.0
CORE_VERSION=16.13.0

View File

@ -1,3 +1,3 @@
# CORE NVIDIA CUDA BUNDLE
FFMPEG_VERSION=5.1.2
FFMPEG_VERSION=5.1.3
CUDA_VERSION=11.7.1

View File

@ -1,2 +1,2 @@
# CORE BUNDLE
FFMPEG_VERSION=5.1.2
FFMPEG_VERSION=5.1.3

View File

@ -1,2 +1,2 @@
# CORE RASPBERRY-PI BUNDLE
FFMPEG_VERSION=5.1.2
FFMPEG_VERSION=5.1.3

View File

@ -1,2 +1,2 @@
# CORE BUNDLE
FFMPEG_VERSION=5.1.2
FFMPEG_VERSION=5.1.3

View File

@ -2,4 +2,4 @@
OS_NAME=ubuntu
OS_VERSION=20.04
GOLANG_IMAGE=golang:1.20-alpine3.16
CORE_VERSION=16.12.0
CORE_VERSION=16.13.0

View File

@ -1,11 +1,23 @@
# Core
### Core v16.12.0 > v16.?.?
### Core v16.12.0 > v16.13.0
- Add updated_at field in process infos
- Add preserve process log history when updating a process
- Add support for input framerate data from jsonstats patch
- Add number of keyframes and extradata size to process progress data
- Mod bumps FFmpeg to v5.1.3 (datarhei/core:tag bundles)
- Fix better naming for storage endpoint documentation
- Fix freeing up S3 mounts
- Fix URL validation if the path contains FFmpeg specific placeholders
- Fix purging default file from HTTP cache
- Fix parsing S3 storage definition from environment variable
- Fix checking length of CPU time array ([#10](https://github.com/datarhei/core/issues/10))
- Fix possible infinite loop with HLS session rewriter
- Fix not propagating process limits
- Fix URL validation if the path contains FFmpeg specific placeholders
- Fix RTMP DoS attack (thx Johannes Frank)
- Deprecate ENV names that do not correspond to JSON name
### Core v16.11.0 > v16.12.0

View File

@ -15,11 +15,11 @@ init:
## build: Build core (default)
build:
CGO_ENABLED=${CGO_ENABLED} GOOS=${GOOS} GOARCH=${GOARCH} go build -o core${BINSUFFIX}
CGO_ENABLED=${CGO_ENABLED} GOOS=${GOOS} GOARCH=${GOARCH} go build -o core${BINSUFFIX} -trimpath
# github workflow workaround
build_linux:
CGO_ENABLED=0 GOOS=linux GOARCH=${OSARCH} go build -o core
CGO_ENABLED=0 GOOS=linux GOARCH=${OSARCH} go build -o core -trimpath
## swagger: Update swagger API documentation (requires github.com/swaggo/swag)
swagger:
@ -69,19 +69,19 @@ lint:
## import: Build import binary
import:
cd app/import && CGO_ENABLED=${CGO_ENABLED} GOOS=${GOOS} GOARCH=${GOARCH} go build -o ../../import -ldflags="-s -w"
cd app/import && CGO_ENABLED=${CGO_ENABLED} GOOS=${GOOS} GOARCH=${GOARCH} go build -o ../../import -trimpath -ldflags="-s -w"
# github workflow workaround
import_linux:
cd app/import && CGO_ENABLED=0 GOOS=linux GOARCH=${OSARCH} go build -o ../../import -ldflags="-s -w"
cd app/import && CGO_ENABLED=0 GOOS=linux GOARCH=${OSARCH} go build -o ../../import -trimpath -ldflags="-s -w"
## ffmigrate: Build ffmpeg migration binary
ffmigrate:
cd app/ffmigrate && CGO_ENABLED=${CGO_ENABLED} GOOS=${GOOS} GOARCH=${GOARCH} go build -o ../../ffmigrate -ldflags="-s -w"
cd app/ffmigrate && CGO_ENABLED=${CGO_ENABLED} GOOS=${GOOS} GOARCH=${GOARCH} go build -o ../../ffmigrate -trimpath -ldflags="-s -w"
# github workflow workaround
ffmigrate_linux:
cd app/ffmigrate && CGO_ENABLED=0 GOOS=linux GOARCH=${OSARCH} go build -o ../../ffmigrate -ldflags="-s -w"
cd app/ffmigrate && CGO_ENABLED=0 GOOS=linux GOARCH=${OSARCH} go build -o ../../ffmigrate -trimpath -ldflags="-s -w"
## coverage: Generate code coverage analysis
coverage:
@ -94,11 +94,11 @@ commit: vet fmt lint test build
## release: Build a release binary of core
release:
CGO_ENABLED=${CGO_ENABLED} GOOS=${GOOS} GOARCH=${GOARCH} go build -o core -ldflags="-s -w -X github.com/datarhei/core/v16/app.Commit=$(COMMIT) -X github.com/datarhei/core/v16/app.Branch=$(BRANCH) -X github.com/datarhei/core/v16/app.Build=$(BUILD)"
CGO_ENABLED=${CGO_ENABLED} GOOS=${GOOS} GOARCH=${GOARCH} go build -o core -trimpath -ldflags="-s -w -X github.com/datarhei/core/v16/app.Commit=$(COMMIT) -X github.com/datarhei/core/v16/app.Branch=$(BRANCH) -X github.com/datarhei/core/v16/app.Build=$(BUILD)"
# github workflow workaround
release_linux:
CGO_ENABLED=0 GOOS=linux GOARCH=${OSARCH} go build -o core -ldflags="-s -w -X github.com/datarhei/core/v16/app.Commit=$(COMMIT) -X github.com/datarhei/core/v16/app.Branch=$(BRANCH) -X github.com/datarhei/core/v16/app.Build=$(BUILD)"
CGO_ENABLED=0 GOOS=linux GOARCH=${OSARCH} go build -o core -trimpath -ldflags="-s -w -X github.com/datarhei/core/v16/app.Commit=$(COMMIT) -X github.com/datarhei/core/v16/app.Branch=$(BRANCH) -X github.com/datarhei/core/v16/app.Build=$(BUILD)"
## docker: Build standard Docker image
docker:

View File

@ -29,7 +29,7 @@ func (v versionInfo) MinorString() string {
// Version of the app
var Version = versionInfo{
Major: 16,
Minor: 12,
Minor: 13,
Patch: 0,
}

View File

@ -150,7 +150,7 @@ func (d *Config) init() {
// Log
d.vars.Register(value.NewString(&d.Log.Level, "info"), "log.level", "CORE_LOG_LEVEL", nil, "Loglevel: silent, error, warn, info, debug", false, false)
d.vars.Register(value.NewStringList(&d.Log.Topics, []string{}, ","), "log.topics", "CORE_LOG_TOPICS", nil, "Show only selected log topics", false, false)
d.vars.Register(value.NewInt(&d.Log.MaxLines, 1000), "log.max_lines", "CORE_LOG_MAXLINES", nil, "Number of latest log lines to keep in memory", false, false)
d.vars.Register(value.NewInt(&d.Log.MaxLines, 1000), "log.max_lines", "CORE_LOG_MAX_LINES", []string{"CORE_LOG_MAXLINES"}, "Number of latest log lines to keep in memory", false, false)
// DB
d.vars.Register(value.NewMustDir(&d.DB.Dir, "./config", d.fs), "db.dir", "CORE_DB_DIR", nil, "Directory for holding the operational data", false, false)
@ -182,19 +182,19 @@ func (d *Config) init() {
d.vars.Register(value.NewBool(&d.TLS.Enable, false), "tls.enable", "CORE_TLS_ENABLE", nil, "Enable HTTPS", false, false)
d.vars.Register(value.NewBool(&d.TLS.Auto, false), "tls.auto", "CORE_TLS_AUTO", nil, "Enable Let's Encrypt certificate", false, false)
d.vars.Register(value.NewEmail(&d.TLS.Email, "cert@datarhei.com"), "tls.email", "CORE_TLS_EMAIL", nil, "Email for Let's Encrypt registration", false, false)
d.vars.Register(value.NewFile(&d.TLS.CertFile, "", d.fs), "tls.cert_file", "CORE_TLS_CERTFILE", nil, "Path to certificate file in PEM format", false, false)
d.vars.Register(value.NewFile(&d.TLS.KeyFile, "", d.fs), "tls.key_file", "CORE_TLS_KEYFILE", nil, "Path to key file in PEM format", false, false)
d.vars.Register(value.NewFile(&d.TLS.CertFile, "", d.fs), "tls.cert_file", "CORE_TLS_CERT_FILE", []string{"CORE_TLS_CERTFILE"}, "Path to certificate file in PEM format", false, false)
d.vars.Register(value.NewFile(&d.TLS.KeyFile, "", d.fs), "tls.key_file", "CORE_TLS_KEY_FILE", []string{"CORE_TLS_KEYFILE"}, "Path to key file in PEM format", false, false)
// Storage
d.vars.Register(value.NewFile(&d.Storage.MimeTypes, "./mime.types", d.fs), "storage.mimetypes_file", "CORE_STORAGE_MIMETYPES_FILE", []string{"CORE_MIMETYPES_FILE"}, "Path to file with mime-types", false, false)
// Storage (Disk)
d.vars.Register(value.NewMustDir(&d.Storage.Disk.Dir, "./data", d.fs), "storage.disk.dir", "CORE_STORAGE_DISK_DIR", nil, "Directory on disk, exposed on /", false, false)
d.vars.Register(value.NewInt64(&d.Storage.Disk.Size, 0), "storage.disk.max_size_mbytes", "CORE_STORAGE_DISK_MAXSIZEMBYTES", nil, "Max. allowed megabytes for storage.disk.dir, 0 for unlimited", false, false)
d.vars.Register(value.NewInt64(&d.Storage.Disk.Size, 0), "storage.disk.max_size_mbytes", "CORE_STORAGE_DISK_MAX_SIZE_MBYTES", []string{"CORE_STORAGE_DISK_MAXSIZEMBYTES"}, "Max. allowed megabytes for storage.disk.dir, 0 for unlimited", false, false)
d.vars.Register(value.NewBool(&d.Storage.Disk.Cache.Enable, true), "storage.disk.cache.enable", "CORE_STORAGE_DISK_CACHE_ENABLE", nil, "Enable cache for /", false, false)
d.vars.Register(value.NewUint64(&d.Storage.Disk.Cache.Size, 0), "storage.disk.cache.max_size_mbytes", "CORE_STORAGE_DISK_CACHE_MAXSIZEMBYTES", nil, "Max. allowed cache size, 0 for unlimited", false, false)
d.vars.Register(value.NewInt64(&d.Storage.Disk.Cache.TTL, 300), "storage.disk.cache.ttl_seconds", "CORE_STORAGE_DISK_CACHE_TTLSECONDS", nil, "Seconds to keep files in cache", false, false)
d.vars.Register(value.NewUint64(&d.Storage.Disk.Cache.FileSize, 1), "storage.disk.cache.max_file_size_mbytes", "CORE_STORAGE_DISK_CACHE_MAXFILESIZEMBYTES", nil, "Max. file size to put in cache", false, false)
d.vars.Register(value.NewUint64(&d.Storage.Disk.Cache.Size, 0), "storage.disk.cache.max_size_mbytes", "CORE_STORAGE_DISK_CACHE_MAX_SIZE_MBYTES", []string{"CORE_STORAGE_DISK_CACHE_MAXSIZEMBYTES"}, "Max. allowed cache size, 0 for unlimited", false, false)
d.vars.Register(value.NewInt64(&d.Storage.Disk.Cache.TTL, 300), "storage.disk.cache.ttl_seconds", "CORE_STORAGE_DISK_CACHE_TTL_SECONDS", []string{"CORE_STORAGE_DISK_CACHE_TTLSECONDS"}, "Seconds to keep files in cache", false, false)
d.vars.Register(value.NewUint64(&d.Storage.Disk.Cache.FileSize, 1), "storage.disk.cache.max_file_size_mbytes", "CORE_STORAGE_DISK_CACHE_MAX_FILE_SIZE_MBYTES", []string{"CORE_STORAGE_DISK_CACHE_MAXFILESIZEMBYTES"}, "Max. file size to put in cache", false, false)
d.vars.Register(value.NewStringList(&d.Storage.Disk.Cache.Types.Allow, []string{}, " "), "storage.disk.cache.type.allow", "CORE_STORAGE_DISK_CACHE_TYPES_ALLOW", []string{"CORE_STORAGE_DISK_CACHE_TYPES"}, "File extensions to cache, empty for all", false, false)
d.vars.Register(value.NewStringList(&d.Storage.Disk.Cache.Types.Block, []string{".m3u8", ".mpd"}, " "), "storage.disk.cache.type.block", "CORE_STORAGE_DISK_CACHE_TYPES_BLOCK", nil, "File extensions not to cache, empty for none", false, false)
@ -202,7 +202,7 @@ func (d *Config) init() {
d.vars.Register(value.NewBool(&d.Storage.Memory.Auth.Enable, true), "storage.memory.auth.enable", "CORE_STORAGE_MEMORY_AUTH_ENABLE", nil, "Enable basic auth for PUT,POST, and DELETE on /memfs", false, false)
d.vars.Register(value.NewString(&d.Storage.Memory.Auth.Username, "admin"), "storage.memory.auth.username", "CORE_STORAGE_MEMORY_AUTH_USERNAME", nil, "Username for Basic-Auth of /memfs", false, false)
d.vars.Register(value.NewString(&d.Storage.Memory.Auth.Password, rand.StringAlphanumeric(18)), "storage.memory.auth.password", "CORE_STORAGE_MEMORY_AUTH_PASSWORD", nil, "Password for Basic-Auth of /memfs", false, true)
d.vars.Register(value.NewInt64(&d.Storage.Memory.Size, 0), "storage.memory.max_size_mbytes", "CORE_STORAGE_MEMORY_MAXSIZEMBYTES", nil, "Max. allowed megabytes for /memfs, 0 for unlimited", false, false)
d.vars.Register(value.NewInt64(&d.Storage.Memory.Size, 0), "storage.memory.max_size_mbytes", "CORE_STORAGE_MEMORY_MAX_SIZE_MBYTES", []string{"CORE_STORAGE_MEMORY_MAXSIZEMBYTES"}, "Max. allowed megabytes for /memfs, 0 for unlimited", false, false)
d.vars.Register(value.NewBool(&d.Storage.Memory.Purge, false), "storage.memory.purge", "CORE_STORAGE_MEMORY_PURGE", nil, "Automatically remove the oldest files if /memfs is full", false, false)
// Storage (S3)
@ -234,17 +234,17 @@ func (d *Config) init() {
d.vars.Register(value.NewStringList(&d.FFmpeg.Access.Input.Block, []string{}, " "), "ffmpeg.access.input.block", "CORE_FFMPEG_ACCESS_INPUT_BLOCK", nil, "List of blocked expression to match against the input addresses", false, false)
d.vars.Register(value.NewStringList(&d.FFmpeg.Access.Output.Allow, []string{}, " "), "ffmpeg.access.output.allow", "CORE_FFMPEG_ACCESS_OUTPUT_ALLOW", nil, "List of allowed expression to match against the output addresses", false, false)
d.vars.Register(value.NewStringList(&d.FFmpeg.Access.Output.Block, []string{}, " "), "ffmpeg.access.output.block", "CORE_FFMPEG_ACCESS_OUTPUT_BLOCK", nil, "List of blocked expression to match against the output addresses", false, false)
d.vars.Register(value.NewInt(&d.FFmpeg.Log.MaxLines, 50), "ffmpeg.log.max_lines", "CORE_FFMPEG_LOG_MAXLINES", nil, "Number of latest log lines to keep for each process", false, false)
d.vars.Register(value.NewInt(&d.FFmpeg.Log.MaxHistory, 3), "ffmpeg.log.max_history", "CORE_FFMPEG_LOG_MAXHISTORY", nil, "Number of latest logs to keep for each process", false, false)
d.vars.Register(value.NewInt(&d.FFmpeg.Log.MaxLines, 50), "ffmpeg.log.max_lines", "CORE_FFMPEG_LOG_MAX_LINES", []string{"CORE_FFMPEG_LOG_MAXLINES"}, "Number of latest log lines to keep for each process", false, false)
d.vars.Register(value.NewInt(&d.FFmpeg.Log.MaxHistory, 3), "ffmpeg.log.max_history", "CORE_FFMPEG_LOG_MAX_HISTORY", []string{"CORE_FFMPEG_LOG_MAXHISTORY"}, "Number of latest logs to keep for each process", false, false)
// Playout
d.vars.Register(value.NewBool(&d.Playout.Enable, false), "playout.enable", "CORE_PLAYOUT_ENABLE", nil, "Enable playout proxy where available", false, false)
d.vars.Register(value.NewPort(&d.Playout.MinPort, 0), "playout.min_port", "CORE_PLAYOUT_MINPORT", nil, "Min. playout server port", false, false)
d.vars.Register(value.NewPort(&d.Playout.MaxPort, 0), "playout.max_port", "CORE_PLAYOUT_MAXPORT", nil, "Max. playout server port", false, false)
d.vars.Register(value.NewPort(&d.Playout.MinPort, 0), "playout.min_port", "CORE_PLAYOUT_MIN_PORT", []string{"CORE_PLAYOUT_MINPORT"}, "Min. playout server port", false, false)
d.vars.Register(value.NewPort(&d.Playout.MaxPort, 0), "playout.max_port", "CORE_PLAYOUT_MAX_PORT", []string{"CORE_PLAYOUT_MAXPORT"}, "Max. playout server port", false, false)
// Debug
d.vars.Register(value.NewBool(&d.Debug.Profiling, false), "debug.profiling", "CORE_DEBUG_PROFILING", nil, "Enable profiling endpoint on /profiling", false, false)
d.vars.Register(value.NewInt(&d.Debug.ForceGC, 0), "debug.force_gc", "CORE_DEBUG_FORCEGC", nil, "Number of seconds between forcing GC to return memory to the OS", false, false)
d.vars.Register(value.NewInt(&d.Debug.ForceGC, 0), "debug.force_gc", "CORE_DEBUG_FORCE_GC", []string{"CORE_DEBUG_FORCEGC"}, "Number of seconds between forcing GC to return memory to the OS", false, false)
d.vars.Register(value.NewInt64(&d.Debug.MemoryLimit, 0), "debug.memory_limit_mbytes", "CORE_DEBUG_MEMORY_LIMIT_MBYTES", nil, "Impose a soft memory limit for the core, in megabytes", false, false)
// Metrics
@ -260,7 +260,7 @@ func (d *Config) init() {
d.vars.Register(value.NewBool(&d.Sessions.Persist, false), "sessions.persist", "CORE_SESSIONS_PERSIST", nil, "Whether to persist session history. Will be stored as sessions.json in db.dir", false, false)
d.vars.Register(value.NewInt(&d.Sessions.PersistInterval, 300), "sessions.persist_interval_sec", "CORE_SESSIONS_PERSIST_INTERVAL_SEC", nil, "Interval in seconds in which to persist the current session history", false, false)
d.vars.Register(value.NewUint64(&d.Sessions.MaxBitrate, 0), "sessions.max_bitrate_mbit", "CORE_SESSIONS_MAXBITRATE_MBIT", nil, "Max. allowed outgoing bitrate in mbit/s, 0 for unlimited", false, false)
d.vars.Register(value.NewUint64(&d.Sessions.MaxSessions, 0), "sessions.max_sessions", "CORE_SESSIONS_MAXSESSIONS", nil, "Max. allowed number of simultaneous sessions, 0 for unlimited", false, false)
d.vars.Register(value.NewUint64(&d.Sessions.MaxSessions, 0), "sessions.max_sessions", "CORE_SESSIONS_MAX_SESSIONS", []string{"CORE_SESSIONS_MAXSESSIONS"}, "Max. allowed number of simultaneous sessions, 0 for unlimited", false, false)
// Service
d.vars.Register(value.NewBool(&d.Service.Enable, false), "service.enable", "CORE_SERVICE_ENABLE", nil, "Enable connecting to the Restreamer Service", false, false)

View File

@ -3,28 +3,29 @@ package value
import (
"fmt"
"net/url"
"regexp"
"strings"
"golang.org/x/net/publicsuffix"
)
// array of s3 storages
// https://access_key_id:secret_access_id@region.endpoint/bucket?name=aaa&mount=/abc&username=xxx&password=yyy
type S3Storage struct {
Name string `json:"name"`
Mountpoint string `json:"mountpoint"`
Auth struct {
Enable bool `json:"enable"`
Username string `json:"username"`
Password string `json:"password"`
} `json:"auth"`
Endpoint string `json:"endpoint"`
AccessKeyID string `json:"access_key_id"`
SecretAccessKey string `json:"secret_access_key"`
Bucket string `json:"bucket"`
Region string `json:"region"`
UseSSL bool `json:"use_ssl"`
Name string `json:"name"`
Mountpoint string `json:"mountpoint"`
Auth S3StorageAuth `json:"auth"`
Endpoint string `json:"endpoint"`
AccessKeyID string `json:"access_key_id"`
SecretAccessKey string `json:"secret_access_key"`
Bucket string `json:"bucket"`
Region string `json:"region"`
UseSSL bool `json:"use_ssl"`
}
type S3StorageAuth struct {
Enable bool `json:"enable"`
Username string `json:"username"`
Password string `json:"password"`
}
func (t *S3Storage) String() string {
@ -50,7 +51,7 @@ func (t *S3Storage) String() string {
v := url.Values{}
v.Set("name", t.Name)
v.Set("mountpoint", t.Mountpoint)
v.Set("mount", t.Mountpoint)
if t.Auth.Enable {
if len(t.Auth.Username) != 0 {
@ -70,12 +71,14 @@ func (t *S3Storage) String() string {
type s3StorageListValue struct {
p *[]S3Storage
separator string
reName *regexp.Regexp
}
func NewS3StorageListValue(p *[]S3Storage, val []S3Storage, separator string) *s3StorageListValue {
v := &s3StorageListValue{
p: p,
separator: separator,
reName: regexp.MustCompile(`^[A-Za-z0-9_-]+$`),
}
*p = val
@ -93,27 +96,16 @@ func (s *s3StorageListValue) Set(val string) error {
t := S3Storage{
Name: u.Query().Get("name"),
Mountpoint: u.Query().Get("mountpoint"),
Mountpoint: u.Query().Get("mount"),
AccessKeyID: u.User.Username(),
}
hostname := u.Hostname()
port := u.Port()
password, _ := u.User.Password()
t.SecretAccessKey = password
domain, err := publicsuffix.EffectiveTLDPlusOne(hostname)
if err != nil {
return fmt.Errorf("invalid eTLD (%s): %w", hostname, err)
}
t.Endpoint = domain
if len(port) != 0 {
t.Endpoint += ":" + port
}
region := strings.TrimSuffix(hostname, domain)
if len(region) != 0 {
t.Region = strings.TrimSuffix(region, ".")
}
region, endpoint, _ := strings.Cut(u.Host, ".")
t.Endpoint = endpoint
t.Region = region
secret, ok := u.User.Password()
if ok {
@ -129,7 +121,7 @@ func (s *s3StorageListValue) Set(val string) error {
if u.Query().Has("username") || u.Query().Has("password") {
t.Auth.Enable = true
t.Auth.Username = u.Query().Get("username")
t.Auth.Username = u.Query().Get("password")
t.Auth.Password = u.Query().Get("password")
}
list = append(list, t)
@ -160,6 +152,10 @@ func (s *s3StorageListValue) Validate() error {
return fmt.Errorf("the name for s3 storage %d is missing", i)
}
if !s.reName.MatchString(t.Name) {
return fmt.Errorf("the name for s3 storage must match the pattern %s", s.reName.String())
}
if len(t.Mountpoint) == 0 {
return fmt.Errorf("the mountpoint for s3 storage %d is missing", i)
}

53
config/value/s3_test.go Normal file
View File

@ -0,0 +1,53 @@
package value
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestS3Value(t *testing.T) {
filesystems := []S3Storage{}
v := NewS3StorageListValue(&filesystems, nil, " ")
require.Equal(t, "(empty)", v.String())
v.Set("https://access_key_id1:secret_access_id1@region1.subdomain.endpoint1.com/bucket1?name=aaa1&mount=/abc1&username=xxx1&password=yyy1 http://access_key_id2:secret_access_id2@region2.endpoint2.com/bucket2?name=aaa2&mount=/abc2&username=xxx2&password=yyy2")
require.Equal(t, []S3Storage{
{
Name: "aaa1",
Mountpoint: "/abc1",
Auth: S3StorageAuth{
Enable: true,
Username: "xxx1",
Password: "yyy1",
},
Endpoint: "subdomain.endpoint1.com",
AccessKeyID: "access_key_id1",
SecretAccessKey: "secret_access_id1",
Bucket: "bucket1",
Region: "region1",
UseSSL: true,
},
{
Name: "aaa2",
Mountpoint: "/abc2",
Auth: S3StorageAuth{
Enable: true,
Username: "xxx2",
Password: "yyy2",
},
Endpoint: "endpoint2.com",
AccessKeyID: "access_key_id2",
SecretAccessKey: "secret_access_id2",
Bucket: "bucket2",
Region: "region2",
UseSSL: false,
},
}, filesystems)
require.Equal(t, "https://access_key_id1:---@region1.subdomain.endpoint1.com/bucket1?mount=%2Fabc1&name=aaa1&password=---&username=xxx1 http://access_key_id2:---@region2.endpoint2.com/bucket2?mount=%2Fabc2&name=aaa2&password=---&username=xxx2", v.String())
require.NoError(t, v.Validate())
v.Set("https://access_key_id1:secret_access_id1@region1.endpoint1.com/bucket1?name=djk*;..&mount=/abc1&username=xxx1&password=yyy1")
require.Error(t, v.Validate())
}

View File

@ -2633,6 +2633,10 @@ const docTemplate = `{
"looping": {
"type": "boolean"
},
"looping_runtime": {
"type": "integer",
"format": "uint64"
},
"output": {
"$ref": "#/definitions/api.AVstreamIO"
},
@ -3754,6 +3758,10 @@ const docTemplate = `{
},
"type": {
"type": "string"
},
"updated_at": {
"type": "integer",
"format": "int64"
}
}
},
@ -4055,6 +4063,11 @@ const docTemplate = `{
"coder": {
"type": "string"
},
"extradata_size_bytes": {
"description": "bytes",
"type": "integer",
"format": "uint64"
},
"format": {
"type": "string"
},
@ -4065,6 +4078,9 @@ const docTemplate = `{
"type": "integer",
"format": "uint64"
},
"framerate": {
"$ref": "#/definitions/api.ProgressIOFramerate"
},
"height": {
"type": "integer",
"format": "uint64"
@ -4077,6 +4093,10 @@ const docTemplate = `{
"type": "integer",
"format": "uint64"
},
"keyframe": {
"type": "integer",
"format": "uint64"
},
"layout": {
"type": "string"
},
@ -4117,6 +4137,20 @@ const docTemplate = `{
}
}
},
"api.ProgressIOFramerate": {
"type": "object",
"properties": {
"avg": {
"type": "number"
},
"max": {
"type": "number"
},
"min": {
"type": "number"
}
}
},
"api.RTMPChannel": {
"type": "object",
"properties": {
@ -5438,18 +5472,7 @@ const docTemplate = `{
"type": "string"
},
"auth": {
"type": "object",
"properties": {
"enable": {
"type": "boolean"
},
"password": {
"type": "string"
},
"username": {
"type": "string"
}
}
"$ref": "#/definitions/value.S3StorageAuth"
},
"bucket": {
"type": "string"
@ -5473,6 +5496,20 @@ const docTemplate = `{
"type": "boolean"
}
}
},
"value.S3StorageAuth": {
"type": "object",
"properties": {
"enable": {
"type": "boolean"
},
"password": {
"type": "string"
},
"username": {
"type": "string"
}
}
}
},
"securityDefinitions": {

View File

@ -2626,6 +2626,10 @@
"looping": {
"type": "boolean"
},
"looping_runtime": {
"type": "integer",
"format": "uint64"
},
"output": {
"$ref": "#/definitions/api.AVstreamIO"
},
@ -3747,6 +3751,10 @@
},
"type": {
"type": "string"
},
"updated_at": {
"type": "integer",
"format": "int64"
}
}
},
@ -4048,6 +4056,11 @@
"coder": {
"type": "string"
},
"extradata_size_bytes": {
"description": "bytes",
"type": "integer",
"format": "uint64"
},
"format": {
"type": "string"
},
@ -4058,6 +4071,9 @@
"type": "integer",
"format": "uint64"
},
"framerate": {
"$ref": "#/definitions/api.ProgressIOFramerate"
},
"height": {
"type": "integer",
"format": "uint64"
@ -4070,6 +4086,10 @@
"type": "integer",
"format": "uint64"
},
"keyframe": {
"type": "integer",
"format": "uint64"
},
"layout": {
"type": "string"
},
@ -4110,6 +4130,20 @@
}
}
},
"api.ProgressIOFramerate": {
"type": "object",
"properties": {
"avg": {
"type": "number"
},
"max": {
"type": "number"
},
"min": {
"type": "number"
}
}
},
"api.RTMPChannel": {
"type": "object",
"properties": {
@ -5431,18 +5465,7 @@
"type": "string"
},
"auth": {
"type": "object",
"properties": {
"enable": {
"type": "boolean"
},
"password": {
"type": "string"
},
"username": {
"type": "string"
}
}
"$ref": "#/definitions/value.S3StorageAuth"
},
"bucket": {
"type": "string"
@ -5466,6 +5489,20 @@
"type": "boolean"
}
}
},
"value.S3StorageAuth": {
"type": "object",
"properties": {
"enable": {
"type": "boolean"
},
"password": {
"type": "string"
},
"username": {
"type": "string"
}
}
}
},
"securityDefinitions": {

View File

@ -22,6 +22,9 @@ definitions:
$ref: '#/definitions/api.AVstreamIO'
looping:
type: boolean
looping_runtime:
format: uint64
type: integer
output:
$ref: '#/definitions/api.AVstreamIO'
queue:
@ -771,6 +774,9 @@ definitions:
$ref: '#/definitions/api.ProcessState'
type:
type: string
updated_at:
format: int64
type: integer
type: object
api.ProcessConfig:
properties:
@ -974,6 +980,10 @@ definitions:
type: string
coder:
type: string
extradata_size_bytes:
description: bytes
format: uint64
type: integer
format:
type: string
fps:
@ -981,6 +991,8 @@ definitions:
frame:
format: uint64
type: integer
framerate:
$ref: '#/definitions/api.ProgressIOFramerate'
height:
format: uint64
type: integer
@ -990,6 +1002,9 @@ definitions:
description: General
format: uint64
type: integer
keyframe:
format: uint64
type: integer
layout:
type: string
packet:
@ -1019,6 +1034,15 @@ definitions:
format: uint64
type: integer
type: object
api.ProgressIOFramerate:
properties:
avg:
type: number
max:
type: number
min:
type: number
type: object
api.RTMPChannel:
properties:
name:
@ -1963,14 +1987,7 @@ definitions:
access_key_id:
type: string
auth:
properties:
enable:
type: boolean
password:
type: string
username:
type: string
type: object
$ref: '#/definitions/value.S3StorageAuth'
bucket:
type: string
endpoint:
@ -1986,6 +2003,15 @@ definitions:
use_ssl:
type: boolean
type: object
value.S3StorageAuth:
properties:
enable:
type: boolean
password:
type: string
username:
type: string
type: object
info:
contact:
email: hello@datarhei.com

View File

@ -32,6 +32,9 @@ type ProcessConfig struct {
Reconnect bool
ReconnectDelay time.Duration
StaleTimeout time.Duration
LimitCPU float64
LimitMemory uint64
LimitDuration time.Duration
Command []string
Parser process.Parser
Logger log.Logger
@ -117,6 +120,9 @@ func (f *ffmpeg) New(config ProcessConfig) (process.Process, error) {
Reconnect: config.Reconnect,
ReconnectDelay: config.ReconnectDelay,
StaleTimeout: config.StaleTimeout,
LimitCPU: config.LimitCPU,
LimitMemory: config.LimitMemory,
LimitDuration: config.LimitDuration,
Parser: config.Parser,
Logger: config.Logger,
OnStart: config.OnStart,

View File

@ -33,6 +33,9 @@ type Parser interface {
// ReportHistory returns an array of previews logs
ReportHistory() []Report
// TransferReportHistory transfers the report history to another parser
TransferReportHistory(Parser) error
}
// Config is the config for the Parser implementation
@ -356,7 +359,7 @@ func (p *parser) Parse(line string) uint64 {
if p.collector.IsCollectableIP(p.process.input[i].IP) {
p.collector.Activate("")
p.collector.Ingress("", int64(p.stats.input[i].diff.size)*1024)
p.collector.Ingress("", int64(p.stats.input[i].diff.size))
}
}
}
@ -373,7 +376,7 @@ func (p *parser) Parse(line string) uint64 {
if p.collector.IsCollectableIP(p.process.output[i].IP) {
p.collector.Activate("")
p.collector.Egress("", int64(p.stats.output[i].diff.size)*1024)
p.collector.Egress("", int64(p.stats.output[i].diff.size))
}
}
}
@ -410,7 +413,7 @@ func (p *parser) parseDefaultProgress(line string) error {
if matches = p.re.size.FindStringSubmatch(line); matches != nil {
if x, err := strconv.ParseUint(matches[1], 10, 64); err == nil {
p.progress.ffmpeg.Size = x
p.progress.ffmpeg.Size = x * 1024
}
}
@ -485,6 +488,26 @@ func (p *parser) parseFFmpegProgress(line string) error {
return fmt.Errorf("output length mismatch (have: %d, want: %d)", len(progress.Output), len(p.process.output))
}
if progress.Size == 0 {
progress.Size = progress.SizeKB * 1024
}
for i, io := range progress.Input {
if io.Size == 0 {
io.Size = io.SizeKB * 1024
}
progress.Input[i].Size = io.Size
}
for i, io := range progress.Output {
if io.Size == 0 {
io.Size = io.SizeKB * 1024
}
progress.Output[i].Size = io.Size
}
p.progress.ffmpeg = progress
return nil
@ -747,3 +770,21 @@ func (p *parser) ReportHistory() []Report {
return history
}
func (p *parser) TransferReportHistory(dst Parser) error {
pp, ok := dst.(*parser)
if !ok {
return fmt.Errorf("the target parser is not of the required type")
}
p.logHistory.Do(func(l interface{}) {
if l == nil {
return
}
pp.logHistory.Value = l
pp.logHistory = pp.logHistory.Next()
})
return nil
}

View File

@ -1,11 +1,11 @@
package parse
type statsData struct {
frame uint64
packet uint64
size uint64 // kbytes
dup uint64
drop uint64
frame uint64 // counter
packet uint64 // counter
size uint64 // bytes
dup uint64 // counter
drop uint64 // counter
}
type stats struct {

View File

@ -44,9 +44,9 @@ func (d *Duration) UnmarshalJSON(b []byte) error {
type ffmpegAVstreamIO struct {
State string `json:"state"`
Packet uint64 `json:"packet"`
Packet uint64 `json:"packet"` // counter
Time uint64 `json:"time"`
Size uint64 `json:"size_kb"`
Size uint64 `json:"size_kb"` // kbytes
}
func (avio *ffmpegAVstreamIO) export() app.AVstreamIO {
@ -54,51 +54,61 @@ func (avio *ffmpegAVstreamIO) export() app.AVstreamIO {
State: avio.State,
Packet: avio.Packet,
Time: avio.Time,
Size: avio.Size,
Size: avio.Size * 1024,
}
}
type ffmpegAVstream struct {
Input ffmpegAVstreamIO `json:"input"`
Output ffmpegAVstreamIO `json:"output"`
Address string `json:"id"`
URL string `json:"url"`
Stream uint64 `json:"stream"`
Aqueue uint64 `json:"aqueue"`
Queue uint64 `json:"queue"`
Dup uint64 `json:"dup"`
Drop uint64 `json:"drop"`
Enc uint64 `json:"enc"`
Looping bool `json:"looping"`
Duplicating bool `json:"duplicating"`
GOP string `json:"gop"`
Input ffmpegAVstreamIO `json:"input"`
Output ffmpegAVstreamIO `json:"output"`
Address string `json:"id"`
URL string `json:"url"`
Stream uint64 `json:"stream"`
Aqueue uint64 `json:"aqueue"`
Queue uint64 `json:"queue"`
Dup uint64 `json:"dup"`
Drop uint64 `json:"drop"`
Enc uint64 `json:"enc"`
Looping bool `json:"looping"`
LoopingRuntime uint64 `json:"looping_runtime"`
Duplicating bool `json:"duplicating"`
GOP string `json:"gop"`
}
func (av *ffmpegAVstream) export() *app.AVstream {
return &app.AVstream{
Aqueue: av.Aqueue,
Queue: av.Queue,
Drop: av.Drop,
Dup: av.Dup,
Enc: av.Enc,
Looping: av.Looping,
Duplicating: av.Duplicating,
GOP: av.GOP,
Input: av.Input.export(),
Output: av.Output.export(),
Aqueue: av.Aqueue,
Queue: av.Queue,
Drop: av.Drop,
Dup: av.Dup,
Enc: av.Enc,
Looping: av.Looping,
LoopingRuntime: av.LoopingRuntime,
Duplicating: av.Duplicating,
GOP: av.GOP,
Input: av.Input.export(),
Output: av.Output.export(),
}
}
type ffmpegProgressIO struct {
// common
Index uint64 `json:"index"`
Stream uint64 `json:"stream"`
Size uint64 `json:"size_kb"` // kbytes
Bitrate float64 `json:"-"` // kbit/s
Frame uint64 `json:"frame"`
Packet uint64 `json:"packet"`
FPS float64 `json:"-"`
PPS float64 `json:"-"`
Index uint64 `json:"index"`
Stream uint64 `json:"stream"`
SizeKB uint64 `json:"size_kb"` // kbytes
Size uint64 `json:"size_bytes"` // bytes
Bitrate float64 `json:"-"` // bit/s
Frame uint64 `json:"frame"` // counter
Keyframe uint64 `json:"keyframe"` // counter
Framerate struct {
Min float64 `json:"min"`
Max float64 `json:"max"`
Average float64 `json:"avg"`
} `json:"framerate"`
Packet uint64 `json:"packet"` // counter
Extradata uint64 `json:"extradata_size_bytes"` // bytes
FPS float64 `json:"-"` // rate, frames per second
PPS float64 `json:"-"` // rate, packets per second
// video
Quantizer float64 `json:"q"`
@ -108,28 +118,39 @@ func (io *ffmpegProgressIO) exportTo(progress *app.ProgressIO) {
progress.Index = io.Index
progress.Stream = io.Stream
progress.Frame = io.Frame
progress.Keyframe = io.Keyframe
progress.Framerate.Min = io.Framerate.Min
progress.Framerate.Max = io.Framerate.Max
progress.Framerate.Average = io.Framerate.Average
progress.Packet = io.Packet
progress.FPS = io.FPS
progress.PPS = io.PPS
progress.Quantizer = io.Quantizer
progress.Size = io.Size * 1024
progress.Bitrate = io.Bitrate * 1024
progress.Bitrate = io.Bitrate
progress.Extradata = io.Extradata
if io.Size == 0 {
progress.Size = io.SizeKB * 1024
} else {
progress.Size = io.Size
}
}
type ffmpegProgress struct {
Input []ffmpegProgressIO `json:"inputs"`
Output []ffmpegProgressIO `json:"outputs"`
Frame uint64 `json:"frame"`
Packet uint64 `json:"packet"`
FPS float64 `json:"-"`
PPS float64 `json:"-"`
Frame uint64 `json:"frame"` // counter
Packet uint64 `json:"packet"` // counter
FPS float64 `json:"-"` // rate, frames per second
PPS float64 `json:"-"` // rate, packets per second
Quantizer float64 `json:"q"`
Size uint64 `json:"size_kb"` // kbytes
Bitrate float64 `json:"-"` // kbit/s
SizeKB uint64 `json:"size_kb"` // kbytes
Size uint64 `json:"size_bytes"` // bytes
Bitrate float64 `json:"-"` // bit/s
Time Duration `json:"time"`
Speed float64 `json:"speed"`
Drop uint64 `json:"drop"`
Dup uint64 `json:"dup"`
Drop uint64 `json:"drop"` // counter
Dup uint64 `json:"dup"` // counter
}
func (p *ffmpegProgress) exportTo(progress *app.Progress) {
@ -138,13 +159,18 @@ func (p *ffmpegProgress) exportTo(progress *app.Progress) {
progress.FPS = p.FPS
progress.PPS = p.PPS
progress.Quantizer = p.Quantizer
progress.Size = p.Size * 1024
progress.Time = p.Time.Seconds()
progress.Bitrate = p.Bitrate * 1024
progress.Bitrate = p.Bitrate
progress.Speed = p.Speed
progress.Drop = p.Drop
progress.Dup = p.Dup
if p.Size == 0 {
progress.Size = p.SizeKB * 1024
} else {
progress.Size = p.Size
}
for i := range p.Input {
if len(progress.Input) <= i {
break

11
go.mod
View File

@ -9,7 +9,7 @@ require (
github.com/caddyserver/certmagic v0.17.2
github.com/casbin/casbin/v2 v2.60.0
github.com/datarhei/gosrt v0.3.1
github.com/datarhei/joy4 v0.0.0-20220914170649-23c70d207759
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a
github.com/go-playground/validator/v10 v10.11.1
github.com/gobwas/glob v0.2.3
github.com/golang-jwt/jwt/v4 v4.4.3
@ -22,15 +22,14 @@ require (
github.com/minio/minio-go/v7 v7.0.47
github.com/prep/average v0.0.0-20200506183628-d26c465f48c3
github.com/prometheus/client_golang v1.14.0
github.com/shirou/gopsutil/v3 v3.22.11
github.com/stretchr/testify v1.8.1
github.com/shirou/gopsutil/v3 v3.23.3
github.com/stretchr/testify v1.8.2
github.com/swaggo/echo-swagger v1.3.5
github.com/swaggo/swag v1.8.7
github.com/vektah/gqlparser/v2 v2.5.1
github.com/xeipuuv/gojsonschema v1.2.0
go.uber.org/zap v1.24.0
golang.org/x/mod v0.7.0
golang.org/x/net v0.7.0
)
require (
@ -82,6 +81,7 @@ require (
github.com/rogpeppe/go-internal v1.8.1 // indirect
github.com/rs/xid v1.4.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shoenig/go-m1cpu v0.1.4 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a // indirect
github.com/tklauser/go-sysconf v0.3.11 // indirect
@ -97,7 +97,8 @@ require (
go.uber.org/goleak v1.1.12 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.5.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.4.0 // indirect

19
go.sum
View File

@ -38,8 +38,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/datarhei/gosrt v0.3.1 h1:9A75hIvnY74IUFyeguqYXh1lsGF8Qt8fjxJS2Ewr12Q=
github.com/datarhei/gosrt v0.3.1/go.mod h1:M2nl2WPrawncUc1FtUBK6gZX4tpZRC7FqL8NjOdBZV0=
github.com/datarhei/joy4 v0.0.0-20220914170649-23c70d207759 h1:h8NyekuQSDvLIsZVTV172m5/RVArXkEM/cnHaUzszQU=
github.com/datarhei/joy4 v0.0.0-20220914170649-23c70d207759/go.mod h1:Jcw/6jZDQQmPx8A7INEkXmuEF7E9jjBbSTfVSLwmiQw=
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a h1:Tf4DSHY1xruBglr+yYP5Wct7czM86GKMYgbXH8a7OFo=
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a/go.mod h1:Jcw/6jZDQQmPx8A7INEkXmuEF7E9jjBbSTfVSLwmiQw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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=
@ -210,8 +210,12 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shirou/gopsutil/v3 v3.22.11 h1:kxsPKS+Eeo+VnEQ2XCaGJepeP6KY53QoRTETx3+1ndM=
github.com/shirou/gopsutil/v3 v3.22.11/go.mod h1:xl0EeL4vXJ+hQMAGN8B9VFpxukEMA0XdevQOe5MZ1oY=
github.com/shirou/gopsutil/v3 v3.23.3 h1:Syt5vVZXUDXPEXpIBt5ziWsJ4LdSAAxF4l/xZeQgSEE=
github.com/shirou/gopsutil/v3 v3.23.3/go.mod h1:lSBNN6t3+D6W5e5nXTxc8KIMMVxAcS+6IJlffjRRlMU=
github.com/shoenig/go-m1cpu v0.1.4 h1:SZPIgRM2sEF9NJy50mRHu9PKGwxyyTTJIWvCtgVbozs=
github.com/shoenig/go-m1cpu v0.1.4/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c=
github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
@ -227,8 +231,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/swaggo/echo-swagger v1.3.5 h1:kCx1wvX5AKhjI6Ykt48l3PTsfL9UD40ZROOx/tYzWyY=
github.com/swaggo/echo-swagger v1.3.5/go.mod h1:3IMHd2Z8KftdWFEEjGmv6QpWj370LwMCOfovuh7vF34=
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a h1:kAe4YSu0O0UFn1DowNo2MY5p6xzqtJ/wQ7LZynSvGaY=
@ -338,8 +343,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=

View File

@ -23,16 +23,17 @@ func (i *AVstreamIO) Unmarshal(io *app.AVstreamIO) {
}
type AVstream struct {
Input AVstreamIO `json:"input"`
Output AVstreamIO `json:"output"`
Aqueue uint64 `json:"aqueue" format:"uint64"`
Queue uint64 `json:"queue" format:"uint64"`
Dup uint64 `json:"dup" format:"uint64"`
Drop uint64 `json:"drop" format:"uint64"`
Enc uint64 `json:"enc" format:"uint64"`
Looping bool `json:"looping"`
Duplicating bool `json:"duplicating"`
GOP string `json:"gop"`
Input AVstreamIO `json:"input"`
Output AVstreamIO `json:"output"`
Aqueue uint64 `json:"aqueue" format:"uint64"`
Queue uint64 `json:"queue" format:"uint64"`
Dup uint64 `json:"dup" format:"uint64"`
Drop uint64 `json:"drop" format:"uint64"`
Enc uint64 `json:"enc" format:"uint64"`
Looping bool `json:"looping"`
LoopingRuntime uint64 `json:"looping_runtime" format:"uint64"`
Duplicating bool `json:"duplicating"`
GOP string `json:"gop"`
}
func (a *AVstream) Unmarshal(av *app.AVstream) {
@ -46,6 +47,7 @@ func (a *AVstream) Unmarshal(av *app.AVstream) {
a.Drop = av.Drop
a.Enc = av.Enc
a.Looping = av.Looping
a.LoopingRuntime = av.LoopingRuntime
a.Duplicating = av.Duplicating
a.GOP = av.GOP

View File

@ -14,6 +14,7 @@ type Process struct {
Type string `json:"type" jsonschema:"enum=ffmpeg"`
Reference string `json:"reference"`
CreatedAt int64 `json:"created_at" jsonschema:"minimum=0" format:"int64"`
UpdatedAt int64 `json:"updated_at" jsonschema:"minimum=0" format:"int64"`
Config *ProcessConfig `json:"config,omitempty"`
State *ProcessState `json:"state,omitempty"`
Report *ProcessReport `json:"report,omitempty"`

View File

@ -7,24 +7,33 @@ import (
"github.com/datarhei/core/v16/restream/app"
)
type ProgressIOFramerate struct {
Min json.Number `json:"min" swaggertype:"number" jsonschema:"type=number"`
Max json.Number `json:"max" swaggertype:"number" jsonschema:"type=number"`
Average json.Number `json:"avg" swaggertype:"number" jsonschema:"type=number"`
}
// ProgressIO represents the progress of an ffmpeg input or output
type ProgressIO struct {
ID string `json:"id" jsonschema:"minLength=1"`
Address string `json:"address" jsonschema:"minLength=1"`
// General
Index uint64 `json:"index" format:"uint64"`
Stream uint64 `json:"stream" format:"uint64"`
Format string `json:"format"`
Type string `json:"type"`
Codec string `json:"codec"`
Coder string `json:"coder"`
Frame uint64 `json:"frame" format:"uint64"`
FPS json.Number `json:"fps" swaggertype:"number" jsonschema:"type=number"`
Packet uint64 `json:"packet" format:"uint64"`
PPS json.Number `json:"pps" swaggertype:"number" jsonschema:"type=number"`
Size uint64 `json:"size_kb" format:"uint64"` // kbytes
Bitrate json.Number `json:"bitrate_kbit" swaggertype:"number" jsonschema:"type=number"` // kbit/s
Index uint64 `json:"index" format:"uint64"`
Stream uint64 `json:"stream" format:"uint64"`
Format string `json:"format"`
Type string `json:"type"`
Codec string `json:"codec"`
Coder string `json:"coder"`
Frame uint64 `json:"frame" format:"uint64"`
Keyframe uint64 `json:"keyframe" format:"uint64"`
Framerate ProgressIOFramerate `json:"framerate"`
FPS json.Number `json:"fps" swaggertype:"number" jsonschema:"type=number"`
Packet uint64 `json:"packet" format:"uint64"`
PPS json.Number `json:"pps" swaggertype:"number" jsonschema:"type=number"`
Size uint64 `json:"size_kb" format:"uint64"` // kbytes
Bitrate json.Number `json:"bitrate_kbit" swaggertype:"number" jsonschema:"type=number"` // kbit/s
Extradata uint64 `json:"extradata_size_bytes" format:"uint64"` // bytes
// Video
Pixfmt string `json:"pix_fmt,omitempty"`
@ -56,11 +65,16 @@ func (i *ProgressIO) Unmarshal(io *app.ProgressIO) {
i.Codec = io.Codec
i.Coder = io.Coder
i.Frame = io.Frame
i.Keyframe = io.Keyframe
i.Framerate.Min = json.Number(fmt.Sprintf("%.3f", io.Framerate.Min))
i.Framerate.Max = json.Number(fmt.Sprintf("%.3f", io.Framerate.Max))
i.Framerate.Average = json.Number(fmt.Sprintf("%.3f", io.Framerate.Average))
i.FPS = json.Number(fmt.Sprintf("%.3f", io.FPS))
i.Packet = io.Packet
i.PPS = json.Number(fmt.Sprintf("%.3f", io.PPS))
i.Size = io.Size / 1024
i.Bitrate = json.Number(fmt.Sprintf("%.3f", io.Bitrate/1024))
i.Extradata = io.Extradata
i.Pixfmt = io.Pixfmt
i.Quantizer = json.Number(fmt.Sprintf("%.3f", io.Quantizer))
i.Width = io.Width

View File

@ -570,6 +570,7 @@ func (h *RestreamHandler) getProcess(id, user, group, filterString string) (api.
Reference: process.Reference,
Type: "ffmpeg",
CreatedAt: process.CreatedAt,
UpdatedAt: process.UpdatedAt,
}
if wants["config"] {

View File

@ -298,7 +298,7 @@ func (r *bodyReader) Close() error {
func (r *bodyReader) getSegments(dir string) []string {
segments := []string{}
// Find all segments URLS in the .m3u8
// Find all segment URLs in the .m3u8
scanner := bufio.NewScanner(&r.buffer)
for scanner.Scan() {
line := scanner.Text()
@ -405,9 +405,23 @@ func (g *sessionRewriter) rewriteHLS(sessionID string, requestURL *url.URL) {
q := u.Query()
loop := false
// If this is a master manifest (i.e. an m3u8 which contains references to other m3u8), then
// we give each substream an own session ID if they don't have already.
if strings.HasSuffix(u.Path, ".m3u8") {
// Check if we're referring to ourselves. This will cause an infinite loop
// and has to be stopped.
file := u.Path
if !strings.HasPrefix(file, "/") {
dir := urlpath.Dir(requestURL.Path)
file = filepath.Join(dir, file)
}
if requestURL.Path == file {
loop = true
}
q.Set("session", shortuuid.New())
isMaster = true
@ -417,7 +431,11 @@ func (g *sessionRewriter) rewriteHLS(sessionID string, requestURL *url.URL) {
u.RawQuery = q.Encode()
buffer.WriteString(u.String() + "\n")
if loop {
buffer.WriteString("# m3u8 is referencing itself: " + u.String() + "\n")
} else {
buffer.WriteString(u.String() + "\n")
}
}
if err := scanner.Err(); err != nil {

View File

@ -6,6 +6,7 @@ import (
"fmt"
"reflect"
"runtime"
"runtime/debug"
"strings"
"time"
)
@ -101,8 +102,9 @@ type Logger interface {
// logger is an implementation of the Logger interface.
type logger struct {
output Writer
component string
output Writer
component string
modulePath string
}
// New returns an implementation of the Logger interface.
@ -111,13 +113,18 @@ func New(component string) Logger {
component: component,
}
if info, ok := debug.ReadBuildInfo(); ok {
l.modulePath = info.Path
}
return l
}
func (l *logger) clone() *logger {
clone := &logger{
output: l.output,
component: l.component,
output: l.output,
component: l.component,
modulePath: l.modulePath,
}
return clone
@ -213,6 +220,7 @@ func (e *Event) WithComponent(component string) Logger {
func (e *Event) Log(format string, args ...interface{}) {
_, file, line, _ := runtime.Caller(1)
file = strings.TrimPrefix(file, e.logger.modulePath)
n := e.clone()

View File

@ -17,7 +17,7 @@ func NewFFmpegCollector(f ffmpeg.FFmpeg) metric.Collector {
ffmpeg: f,
}
c.processDescr = metric.NewDesc("ffmpeg_process", "State of the ffmpeg process", []string{"state"})
c.processDescr = metric.NewDesc("ffmpeg_process", "Accumulated state changes of all ffmpeg processes", []string{"state"})
return c
}

View File

@ -25,7 +25,7 @@ func NewRestreamCollector(r restream.Restreamer) metric.Collector {
c.restreamProcessDescr = metric.NewDesc("restream_process", "Current process values by name", []string{"processid", "state", "order", "name"})
c.restreamProcessStatesDescr = metric.NewDesc("restream_process_states", "Current process state", []string{"processid", "state"})
c.restreamProcessIODescr = metric.NewDesc("restream_io", "Current process IO values by name", []string{"processid", "type", "id", "address", "index", "stream", "media", "name"})
c.restreamStatesDescr = metric.NewDesc("restream_state", "Summarized process states", []string{"state"})
c.restreamStatesDescr = metric.NewDesc("restream_state", "Summarized current process states", []string{"state"})
return c
}

View File

@ -55,7 +55,7 @@ func TestScheme(t *testing.T) {
require.False(t, r)
}
func TestPars(t *testing.T) {
func TestParse(t *testing.T) {
u, err := Parse("http://localhost/foobar")
require.NoError(t, err)
require.Equal(t, &URL{

View File

@ -63,26 +63,19 @@ type Config struct {
// Status represents the current status of a process
type Status struct {
// State is the current state of the process. See stateType for the known states.
State string
// States is the cumulative history of states the process had.
States States
// Order is the wanted condition of process, either "start" or "stop"
Order string
// Duration is the time since the last change of the state
Duration time.Duration
// Time is the time of the last change of the state
Time time.Time
// Used CPU in percent
CPU float64
// Used memory in bytes
Memory uint64
State string // State is the current state of the process. See stateType for the known states.
States States // States is the cumulative history of states the process had.
Order string // Order is the wanted condition of process, either "start" or "stop"
Duration time.Duration // Duration is the time since the last change of the state
Time time.Time // Time is the time of the last change of the state
CPU struct {
Current float64 // Used CPU in percent
Limit float64 // Limit in percent
}
Memory struct {
Current uint64 // Used memory in bytes
Limit uint64 // Limit in bytes
}
}
// States
@ -390,6 +383,7 @@ func (p *process) getStateString() string {
// Status returns the current status of the process
func (p *process) Status() Status {
cpu, memory := p.limits.Current()
cpuLimit, memoryLimit := p.limits.Limits()
p.state.lock.Lock()
stateTime := p.state.time
@ -407,10 +401,14 @@ func (p *process) Status() Status {
Order: order,
Duration: time.Since(stateTime),
Time: stateTime,
CPU: cpu,
Memory: memory,
}
s.CPU.Current = cpu
s.CPU.Limit = cpuLimit
s.Memory.Current = memory
s.Memory.Limit = memoryLimit
return s
}
@ -593,6 +591,7 @@ func (p *process) stop(wait bool) error {
if p.callbacks.onExit == nil {
p.callbacks.onExit = func() {
wg.Done()
p.callbacks.onExit = nil
}
} else {
@ -600,6 +599,7 @@ func (p *process) stop(wait bool) error {
p.callbacks.onExit = func() {
cb()
wg.Done()
p.callbacks.onExit = cb
}
}

View File

@ -2,6 +2,7 @@ package psutil
import (
"context"
"errors"
"fmt"
"io"
"io/fs"
@ -284,6 +285,10 @@ func (u *util) cpuTimes() (*cpuTimesStat, error) {
return nil, err
}
if len(times) == 0 {
return nil, errors.New("cpu.Times() returned an empty slice")
}
s := &cpuTimesStat{
total: cpuTotal(&times[0]),
system: times[0].System,

View File

@ -2,20 +2,21 @@ package app
type AVstreamIO struct {
State string
Packet uint64
Time uint64
Size uint64
Packet uint64 // counter
Time uint64 // sec
Size uint64 // bytes
}
type AVstream struct {
Input AVstreamIO
Output AVstreamIO
Aqueue uint64
Queue uint64
Dup uint64
Drop uint64
Enc uint64
Looping bool
Duplicating bool
GOP string
Input AVstreamIO
Output AVstreamIO
Aqueue uint64 // gauge
Queue uint64 // gauge
Dup uint64 // counter
Drop uint64 // counter
Enc uint64 // counter
Looping bool
LoopingRuntime uint64 // sec
Duplicating bool
GOP string
}

View File

@ -110,6 +110,7 @@ type Process struct {
Reference string `json:"reference"`
Config *Config `json:"config"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
Order string `json:"order"`
}
@ -119,6 +120,7 @@ func (process *Process) Clone() *Process {
Reference: process.Reference,
Config: process.Config.Clone(),
CreatedAt: process.CreatedAt,
UpdatedAt: process.UpdatedAt,
Order: process.Order,
}

View File

@ -5,18 +5,25 @@ type ProgressIO struct {
Address string
// General
Index uint64
Stream uint64
Format string
Type string
Codec string
Coder string
Frame uint64
FPS float64
Packet uint64
PPS float64
Size uint64 // bytes
Bitrate float64 // bit/s
Index uint64
Stream uint64
Format string
Type string
Codec string
Coder string
Frame uint64 // counter
Keyframe uint64 // counter
Framerate struct {
Min float64
Max float64
Average float64
}
FPS float64 // rate, frames per second
Packet uint64 // counter
PPS float64 // rate, packets per second
Size uint64 // bytes
Bitrate float64 // bit/s
Extradata uint64 // bytes
// Video
Pixfmt string
@ -36,15 +43,15 @@ type ProgressIO struct {
type Progress struct {
Input []ProgressIO
Output []ProgressIO
Frame uint64
Packet uint64
FPS float64
PPS float64
Quantizer float64
Size uint64 // bytes
Time float64
Frame uint64 // counter
Packet uint64 // counter
FPS float64 // rate, frames per second
PPS float64 // rate, packets per second
Quantizer float64 // gauge
Size uint64 // bytes
Time float64 // seconds with fractions
Bitrate float64 // bit/s
Speed float64
Drop uint64
Dup uint64
Speed float64 // gauge
Drop uint64 // counter
Dup uint64 // counter
}

View File

@ -78,7 +78,7 @@ func TestMaxAge(t *testing.T) {
require.Eventually(t, func() bool {
return cleanfs.Files() == 0
}, 5*time.Second, time.Second)
}, 10*time.Second, time.Second)
cleanfs.WriteFileReader("/chunk_3.ts", strings.NewReader("chunk_3"))
@ -96,7 +96,7 @@ func TestMaxAge(t *testing.T) {
require.ElementsMatch(t, []string{"/chunk_3.ts"}, names)
return true
}, 2*time.Second, time.Second)
}, 5*time.Second, time.Second)
cleanfs.Stop()
}

View File

@ -390,6 +390,9 @@ func (r *restream) load() error {
Reconnect: t.config.Reconnect,
ReconnectDelay: time.Duration(t.config.ReconnectDelay) * time.Second,
StaleTimeout: time.Duration(t.config.StaleTimeout) * time.Second,
LimitCPU: t.config.LimitCPU,
LimitMemory: t.config.LimitMemory,
LimitDuration: time.Duration(t.config.LimitWaitFor) * time.Second,
Command: t.command,
Parser: t.parser,
Logger: t.logger,
@ -514,6 +517,8 @@ func (r *restream) createTask(config *app.Config) (*task, error) {
CreatedAt: time.Now().Unix(),
}
process.UpdatedAt = process.CreatedAt
if config.Autostart {
process.Order = "start"
}
@ -554,6 +559,9 @@ func (r *restream) createTask(config *app.Config) (*task, error) {
Reconnect: t.config.Reconnect,
ReconnectDelay: time.Duration(t.config.ReconnectDelay) * time.Second,
StaleTimeout: time.Duration(t.config.StaleTimeout) * time.Second,
LimitCPU: t.config.LimitCPU,
LimitMemory: t.config.LimitMemory,
LimitDuration: time.Duration(t.config.LimitWaitFor) * time.Second,
Command: t.command,
Parser: t.parser,
Logger: t.logger,
@ -1036,6 +1044,10 @@ func (r *restream) UpdateProcess(id, user, group string, config *app.Config) err
return ErrUnknownProcess
}
// This would require a major version jump
//t.process.CreatedAt = task.process.CreatedAt
t.process.UpdatedAt = time.Now().Unix()
task.parser.TransferReportHistory(t.parser)
t.process.Order = task.process.Order
if tid != t.String() {
@ -1402,6 +1414,9 @@ func (r *restream) reloadProcess(tid string) error {
Reconnect: t.config.Reconnect,
ReconnectDelay: time.Duration(t.config.ReconnectDelay) * time.Second,
StaleTimeout: time.Duration(t.config.StaleTimeout) * time.Second,
LimitCPU: t.config.LimitCPU,
LimitMemory: t.config.LimitMemory,
LimitDuration: time.Duration(t.config.LimitWaitFor) * time.Second,
Command: t.command,
Parser: t.parser,
Logger: t.logger,
@ -1447,8 +1462,8 @@ func (r *restream) GetProcessState(id, user, group string) (*app.State, error) {
state.State = status.State
state.States.Marshal(status.States)
state.Time = status.Time.Unix()
state.Memory = status.Memory
state.CPU = status.CPU
state.Memory = status.Memory.Current
state.CPU = status.CPU.Current
state.Duration = status.Duration.Round(10 * time.Millisecond).Seconds()
state.Reconnect = -1
state.Command = make([]string, len(task.command))

View File

@ -24,10 +24,11 @@ func getDummyRestreamer(portrange net.Portranger, validatorIn, validatorOut ffmp
}
ffmpeg, err := ffmpeg.New(ffmpeg.Config{
Binary: binary,
Portrange: portrange,
ValidatorInput: validatorIn,
ValidatorOutput: validatorOut,
Binary: binary,
LogHistoryLength: 3,
Portrange: portrange,
ValidatorInput: validatorIn,
ValidatorOutput: validatorOut,
})
if err != nil {
return nil, err
@ -245,6 +246,14 @@ func TestUpdateProcess(t *testing.T) {
err = rs.AddProcess(process2)
require.Equal(t, nil, err)
process, err := rs.GetProcess(process2.ID, "", "")
require.NoError(t, err)
createdAt := process.CreatedAt
updatedAt := process.UpdatedAt
time.Sleep(2 * time.Second)
process3 := getDummyProcess()
require.NotNil(t, process3)
process3.ID = "process2"
@ -259,8 +268,11 @@ func TestUpdateProcess(t *testing.T) {
_, err = rs.GetProcess(process1.ID, "", "")
require.Error(t, err)
_, err = rs.GetProcess(process3.ID, "", "")
process, err = rs.GetProcess(process3.ID, "", "")
require.NoError(t, err)
require.NotEqual(t, createdAt, process.CreatedAt) // this should be equal, but will require a major version jump
require.NotEqual(t, updatedAt, process.UpdatedAt)
}
func TestGetProcess(t *testing.T) {
@ -470,10 +482,10 @@ func TestLog(t *testing.T) {
rs.AddProcess(process)
_, err = rs.GetProcessLog("foobar", "", "")
require.NotEqual(t, nil, err, "shouldn't be able to get log from non-existing process")
require.Error(t, err)
log, err := rs.GetProcessLog(process.ID, "", "")
require.Equal(t, nil, err, "should be able to get log from existing process")
require.NoError(t, err)
require.Equal(t, 0, len(log.Prelude))
require.Equal(t, 0, len(log.Log))
@ -494,6 +506,34 @@ func TestLog(t *testing.T) {
require.NotEqual(t, 0, len(log.Log))
}
func TestLogTransfer(t *testing.T) {
rs, err := getDummyRestreamer(nil, nil, nil, nil)
require.NoError(t, err)
process := getDummyProcess()
err = rs.AddProcess(process)
require.NoError(t, err)
rs.StartProcess(process.ID, "", "")
time.Sleep(3 * time.Second)
rs.StopProcess(process.ID, "", "")
rs.StartProcess(process.ID, "", "")
rs.StopProcess(process.ID, "", "")
log, _ := rs.GetProcessLog(process.ID, "", "")
require.Equal(t, 1, len(log.History))
err = rs.UpdateProcess(process.ID, "", "", process)
require.NoError(t, err)
log, _ = rs.GetProcessLog(process.ID, "", "")
require.Equal(t, 1, len(log.History))
}
func TestPlayoutNoRange(t *testing.T) {
rs, err := getDummyRestreamer(nil, nil, nil, nil)
require.NoError(t, err)
@ -949,3 +989,26 @@ func TestReplacer(t *testing.T) {
require.Equal(t, process, task.config)
}
func TestProcessLimit(t *testing.T) {
rsi, err := getDummyRestreamer(nil, nil, nil, nil)
require.NoError(t, err)
process := getDummyProcess()
process.LimitCPU = 61
process.LimitMemory = 42
process.Autostart = false
err = rsi.AddProcess(process)
require.NoError(t, err)
rs := rsi.(*restream)
task, ok := rs.tasks[process.ID+"~"]
require.True(t, ok)
status := task.ffmpeg.Status()
require.Equal(t, float64(61), status.CPU.Limit)
require.Equal(t, uint64(42), status.Memory.Limit)
}

View File

@ -178,6 +178,7 @@ func (self *Server) Serve(listener net.Listener) error {
if Debug {
fmt.Println("rtmp: server: client closed err:", err)
}
conn.Close()
}()
}
}
@ -190,6 +191,7 @@ func (self *Server) Close() {
close(self.doneChan)
self.listener.Close()
self.listener = nil
}
const (
@ -398,7 +400,7 @@ func getTcUrl(u *url.URL) string {
return nu.String()
}
func createURL(tcurl, app, play string) (u *url.URL) {
func createURL(tcurl, app, play string) (*url.URL, error) {
ps := strings.Split(app+"/"+play, "/")
out := []string{""}
for _, s := range ps {
@ -410,7 +412,11 @@ func createURL(tcurl, app, play string) (u *url.URL) {
out = append(out, "")
}
path := strings.Join(out, "/")
u, _ = url.ParseRequestURI(path)
u, err := url.ParseRequestURI(path)
if err != nil {
return nil, err
}
if tcurl != "" {
tu, _ := url.Parse(tcurl)
@ -419,7 +425,8 @@ func createURL(tcurl, app, play string) (u *url.URL) {
u.Scheme = tu.Scheme
}
}
return
return u, nil
}
var CodecTypes = flv.CodecTypes
@ -553,7 +560,13 @@ func (self *Conn) readConnect() (err error) {
return
}
self.URL = createURL(tcurl, connectpath, publishpath)
u, uerr := createURL(tcurl, connectpath, publishpath)
if uerr != nil {
err = fmt.Errorf("invalid URL: %w", uerr)
return
}
self.URL = u
self.publishing = true
self.reading = true
self.stage++
@ -599,7 +612,13 @@ func (self *Conn) readConnect() (err error) {
return
}
self.URL = createURL(tcurl, connectpath, playpath)
u, uerr := createURL(tcurl, connectpath, playpath)
if uerr != nil {
err = fmt.Errorf("invalid URL: %w", uerr)
return
}
self.URL = u
self.playing = true
self.writing = true
self.stage++

View File

@ -6,8 +6,8 @@ package cpu
import (
"context"
"regexp"
"strings"
"strconv"
"strings"
"github.com/shirou/gopsutil/v3/internal/common"
)
@ -28,19 +28,19 @@ func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
}
ret := TimesStat{CPU: "cpu-total"}
h := whiteSpaces.Split(lines[len(lines)-3], -1) // headers
v := whiteSpaces.Split(lines[len(lines)-2], -1) // values
h := whiteSpaces.Split(lines[len(lines)-3], -1) // headers
v := whiteSpaces.Split(lines[len(lines)-2], -1) // values
for i, header := range h {
if t, err := strconv.ParseFloat(v[i], 64); err == nil {
switch header {
case `%usr`:
ret.User = t
case `%sys`:
ret.System = t
case `%wio`:
ret.Iowait = t
case `%idle`:
ret.Idle = t
case `%usr`:
ret.User = t
case `%sys`:
ret.System = t
case `%wio`:
ret.Iowait = t
case `%idle`:
ret.Idle = t
}
}
}

View File

@ -8,6 +8,7 @@ import (
"strconv"
"strings"
"github.com/shoenig/go-m1cpu"
"github.com/tklauser/go-sysconf"
"golang.org/x/sys/unix"
)
@ -85,11 +86,15 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
c.CacheSize = int32(cacheSize)
c.VendorID, _ = unix.Sysctl("machdep.cpu.vendor")
// Use the rated frequency of the CPU. This is a static value and does not
// account for low power or Turbo Boost modes.
cpuFrequency, err := unix.SysctlUint64("hw.cpufrequency")
if err == nil {
c.Mhz = float64(cpuFrequency) / 1000000.0
if m1cpu.IsAppleSilicon() {
c.Mhz = float64(m1cpu.PCoreHz() / 1_000_000)
} else {
// Use the rated frequency of the CPU. This is a static value and does not
// account for low power or Turbo Boost modes.
cpuFrequency, err := unix.SysctlUint64("hw.cpufrequency")
if err == nil {
c.Mhz = float64(cpuFrequency) / 1000000.0
}
}
return append(ret, c), nil

View File

@ -190,7 +190,7 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
switch key {
case "Processor":
processorName = value
case "processor":
case "processor", "cpu number":
if c.CPU >= 0 {
finishCPUInfo(&c)
ret = append(ret, c)
@ -203,6 +203,9 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
c.CPU = int32(t)
case "vendorId", "vendor_id":
c.VendorID = value
if strings.Contains(value, "S390") {
processorName = "S390"
}
case "CPU implementer":
if v, err := strconv.ParseUint(value, 0, 8); err == nil {
switch v {
@ -242,7 +245,18 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
c.Family = value
case "model", "CPU part":
c.Model = value
case "model name", "cpu":
// if CPU is arm based, model name is found via model number. refer to: arch/arm64/kernel/cpuinfo.c
if c.VendorID == "ARM" {
if v, err := strconv.ParseUint(c.Model, 0, 16); err == nil {
modelName, exist := armModelToModelName[v]
if exist {
c.ModelName = modelName
} else {
c.ModelName = "Undefined"
}
}
}
case "Model Name", "model name", "cpu":
c.ModelName = value
if strings.Contains(value, "POWER8") ||
strings.Contains(value, "POWER7") {
@ -262,7 +276,7 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
return ret, err
}
c.Stepping = int32(t)
case "cpu MHz", "clock":
case "cpu MHz", "clock", "cpu MHz dynamic":
// treat this as the fallback value, thus we ignore error
if t, err := strconv.ParseFloat(strings.Replace(value, "MHz", "", 1), 64); err == nil {
c.Mhz = t
@ -285,16 +299,6 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
c.Microcode = value
}
}
if c.VendorID == "ARM" && c.ModelName == "" {
if v, err := strconv.ParseUint(c.Model, 0, 16); err == nil {
modelName, exist := armModelToModelName[v]
if exist {
c.ModelName = modelName
} else {
c.ModelName = "Undefined"
}
}
}
if c.CPU >= 0 {
finishCPUInfo(&c)
ret = append(ret, c)

View File

@ -14,8 +14,7 @@ import (
)
var (
procGetActiveProcessorCount = common.Modkernel32.NewProc("GetActiveProcessorCount")
procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo")
procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo")
)
type win32_Processor struct {
@ -204,15 +203,12 @@ type systemInfo struct {
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
if logical {
// https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L97
err := procGetActiveProcessorCount.Find()
if err == nil { // Win7+
ret, _, _ := procGetActiveProcessorCount.Call(uintptr(0xffff)) // ALL_PROCESSOR_GROUPS is 0xffff according to Rust's winapi lib https://docs.rs/winapi/*/x86_64-pc-windows-msvc/src/winapi/shared/ntdef.rs.html#120
if ret != 0 {
return int(ret), nil
}
ret := windows.GetActiveProcessorCount(windows.ALL_PROCESSOR_GROUPS)
if ret != 0 {
return int(ret), nil
}
var systemInfo systemInfo
_, _, err = procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo)))
_, _, err := procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo)))
if systemInfo.dwNumberOfProcessors == 0 {
return 0, err
}

View File

@ -9,6 +9,8 @@ import (
var invoke common.Invoker = common.Invoke{}
type Warnings = common.Warnings
type UsageStat struct {
Path string `json:"path"`
Fstype string `json:"fstype"`

View File

@ -8,8 +8,8 @@ import (
"regexp"
"strings"
"golang.org/x/sys/unix"
"github.com/shirou/gopsutil/v3/internal/common"
"golang.org/x/sys/unix"
)
var whiteSpaces = regexp.MustCompile(`\s+`)
@ -17,11 +17,11 @@ var startBlank = regexp.MustCompile(`^\s+`)
var ignoreFSType = map[string]bool{"procfs": true}
var FSType = map[int]string{
0: "jfs2", 1: "namefs", 2: "nfs", 3: "jfs", 5: "cdrom", 6: "proc",
16: "special-fs", 17: "cache-fs", 18: "nfs3", 19: "automount-fs", 20: "pool-fs", 32: "vxfs",
33: "veritas-fs", 34: "udfs", 35: "nfs4", 36: "nfs4-pseudo", 37: "smbfs", 38: "mcr-pseudofs",
39: "ahafs", 40: "sterm-nfs", 41: "asmfs",
}
0: "jfs2", 1: "namefs", 2: "nfs", 3: "jfs", 5: "cdrom", 6: "proc",
16: "special-fs", 17: "cache-fs", 18: "nfs3", 19: "automount-fs", 20: "pool-fs", 32: "vxfs",
33: "veritas-fs", 34: "udfs", 35: "nfs4", 36: "nfs4-pseudo", 37: "smbfs", 38: "mcr-pseudofs",
39: "ahafs", 40: "sterm-nfs", 41: "asmfs",
}
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
var ret []PartitionStat
@ -42,7 +42,7 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro
start := 0
finished := false
for pos, ch := range lines[1] {
if ch == ' ' && ! finished {
if ch == ' ' && !finished {
name := strings.TrimSpace(lines[0][start:pos])
colidx[name] = idx
finished = true

View File

@ -20,9 +20,15 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro
return ret, err
}
fs := make([]unix.Statfs_t, count)
if _, err = unix.Getfsstat(fs, unix.MNT_WAIT); err != nil {
count, err = unix.Getfsstat(fs, unix.MNT_WAIT)
if err != nil {
return ret, err
}
// On 10.14, and possibly other OS versions, the actual count may
// be less than from the first call. Truncate to the returned count
// to prevent accessing uninitialized entries.
// https://github.com/shirou/gopsutil/issues/1390
fs = fs[:count]
for _, stat := range fs {
opts := []string{"rw"}
if stat.Flags&unix.MNT_RDONLY != 0 {

View File

@ -12,9 +12,8 @@ import (
"strconv"
"strings"
"golang.org/x/sys/unix"
"github.com/shirou/gopsutil/v3/internal/common"
"golang.org/x/sys/unix"
)
// PartitionsWithContext returns disk partition.

View File

@ -341,7 +341,7 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro
}
if strings.HasPrefix(d.Device, "/dev/mapper/") {
devpath, err := filepath.EvalSymlinks(common.HostDev(strings.Replace(d.Device, "/dev", "", -1)))
devpath, err := filepath.EvalSymlinks(common.HostDev(strings.Replace(d.Device, "/dev", "", 1)))
if err == nil {
d.Device = devpath
}

View File

@ -6,20 +6,16 @@ package disk
import (
"bytes"
"context"
"errors"
"fmt"
"strconv"
"sync"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
"github.com/shirou/gopsutil/v3/internal/common"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)
type Warnings = common.Warnings
var (
procGetDiskFreeSpaceExW = common.Modkernel32.NewProc("GetDiskFreeSpaceExW")
procGetLogicalDriveStringsW = common.Modkernel32.NewProc("GetLogicalDriveStringsW")
@ -84,44 +80,101 @@ func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
return ret, nil
}
// PartitionsWithContext returns disk partitions.
// Since GetVolumeInformation doesn't have a timeout, this method uses context to set deadline by users.
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
warnings := Warnings{
Verbose: true,
}
var ret []PartitionStat
retChan := make(chan []PartitionStat)
errChan := make(chan error)
lpBuffer := make([]byte, 254)
diskret, _, err := procGetLogicalDriveStringsW.Call(
uintptr(len(lpBuffer)),
uintptr(unsafe.Pointer(&lpBuffer[0])))
if diskret == 0 {
return ret, err
}
for _, v := range lpBuffer {
if v >= 65 && v <= 90 {
i, err := getVolumeInformation(string(v), &warnings)
if err != nil && !errors.Is(err, errDeviceNotReady) && !errors.Is(err, errInvalidDriveType) {
continue
}
opts := []string{"rw"}
if i.FileSystemFlags&fileReadOnlyVolume != 0 {
opts = []string{"ro"}
}
if i.FileSystemFlags&fileFileCompression != 0 {
opts = append(opts, "compress")
}
var waitgrp sync.WaitGroup
waitgrp.Add(1)
defer waitgrp.Done()
path := string(v) + ":"
d := PartitionStat{
Mountpoint: path,
Device: path,
Fstype: i.FileSystemName,
Opts: opts,
}
ret = append(ret, d)
f := func() {
defer func() {
waitgrp.Wait()
// fires when this func and the outside func finishes.
close(errChan)
close(retChan)
}()
diskret, _, err := procGetLogicalDriveStringsW.Call(
uintptr(len(lpBuffer)),
uintptr(unsafe.Pointer(&lpBuffer[0])))
if diskret == 0 {
errChan <- err
return
}
for _, v := range lpBuffer {
if v >= 65 && v <= 90 {
path := string(v) + ":"
typepath, _ := windows.UTF16PtrFromString(path)
typeret, _, _ := procGetDriveType.Call(uintptr(unsafe.Pointer(typepath)))
if typeret == 0 {
err := windows.GetLastError()
warnings.Add(err)
continue
}
// 2: DRIVE_REMOVABLE 3: DRIVE_FIXED 4: DRIVE_REMOTE 5: DRIVE_CDROM
if typeret == 2 || typeret == 3 || typeret == 4 || typeret == 5 {
lpVolumeNameBuffer := make([]byte, 256)
lpVolumeSerialNumber := int64(0)
lpMaximumComponentLength := int64(0)
lpFileSystemFlags := int64(0)
lpFileSystemNameBuffer := make([]byte, 256)
volpath, _ := windows.UTF16PtrFromString(string(v) + ":/")
driveret, _, err := procGetVolumeInformation.Call(
uintptr(unsafe.Pointer(volpath)),
uintptr(unsafe.Pointer(&lpVolumeNameBuffer[0])),
uintptr(len(lpVolumeNameBuffer)),
uintptr(unsafe.Pointer(&lpVolumeSerialNumber)),
uintptr(unsafe.Pointer(&lpMaximumComponentLength)),
uintptr(unsafe.Pointer(&lpFileSystemFlags)),
uintptr(unsafe.Pointer(&lpFileSystemNameBuffer[0])),
uintptr(len(lpFileSystemNameBuffer)))
if driveret == 0 {
if typeret == 5 || typeret == 2 {
continue // device is not ready will happen if there is no disk in the drive
}
warnings.Add(err)
continue
}
opts := []string{"rw"}
if lpFileSystemFlags&fileReadOnlyVolume != 0 {
opts = []string{"ro"}
}
if lpFileSystemFlags&fileFileCompression != 0 {
opts = append(opts, "compress")
}
d := PartitionStat{
Mountpoint: path,
Device: path,
Fstype: string(bytes.Replace(lpFileSystemNameBuffer, []byte("\x00"), []byte(""), -1)),
Opts: opts,
}
ret = append(ret, d)
}
}
}
retChan <- ret
}
go f()
select {
case err := <-errChan:
return ret, err
case ret := <-retChan:
return ret, warnings.Reference()
case <-ctx.Done():
return ret, ctx.Err()
}
return ret, warnings.Reference()
}
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
@ -136,8 +189,7 @@ func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOC
}
for _, v := range lpBuffer[:lpBufferLen] {
if 'A' <= v && v <= 'Z' {
vStr := string(rune(v))
path := vStr + ":"
path := string(rune(v)) + ":"
typepath, _ := windows.UTF16PtrFromString(path)
typeret := windows.GetDriveType(typepath)
if typeret == 0 {
@ -162,22 +214,14 @@ func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOC
if err != nil {
return drivemap, err
}
i, err := getVolumeInformation(vStr, nil)
if err != nil {
return nil, err
}
drivemap[path] = IOCountersStat{
ReadBytes: uint64(diskPerformance.BytesRead),
WriteBytes: uint64(diskPerformance.BytesWritten),
ReadCount: uint64(diskPerformance.ReadCount),
WriteCount: uint64(diskPerformance.WriteCount),
ReadTime: uint64(diskPerformance.ReadTime / 10000 / 1000), // convert to ms: https://github.com/giampaolo/psutil/issues/1012
WriteTime: uint64(diskPerformance.WriteTime / 10000 / 1000),
Name: path,
SerialNumber: strconv.FormatInt(i.SerialNumber, 10),
Label: i.Label,
ReadBytes: uint64(diskPerformance.BytesRead),
WriteBytes: uint64(diskPerformance.BytesWritten),
ReadCount: uint64(diskPerformance.ReadCount),
WriteCount: uint64(diskPerformance.WriteCount),
ReadTime: uint64(diskPerformance.ReadTime / 10000 / 1000), // convert to ms: https://github.com/giampaolo/psutil/issues/1012
WriteTime: uint64(diskPerformance.WriteTime / 10000 / 1000),
Name: path,
}
}
}
@ -185,83 +229,9 @@ func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOC
}
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
i, err := getVolumeInformation(name, nil)
if err != nil {
return "", err
}
return strconv.FormatInt(i.SerialNumber, 10), nil
return "", common.ErrNotImplementedError
}
func LabelWithContext(ctx context.Context, name string) (string, error) {
i, err := getVolumeInformation(name, nil)
if err != nil {
return "", err
}
return i.Label, nil
}
type volumeInformation struct {
Label string
SerialNumber int64
MaximumComponentLength int64
FileSystemFlags int64
FileSystemName string
}
var (
errDeviceNotReady = errors.New("device not ready")
errInvalidDriveType = errors.New("invalid drive type specified")
)
// getVolumeInformation returns all the information gathered from GetVolumeInformationW
func getVolumeInformation(name string, warnings *common.Warnings) (*volumeInformation, error) {
path := name + ":"
typepath, _ := windows.UTF16PtrFromString(path)
typeret, _, _ := procGetDriveType.Call(uintptr(unsafe.Pointer(typepath)))
if typeret == 0 {
err := windows.GetLastError()
if warnings != nil {
warnings.Add(err)
}
return nil, err
}
if typeret == windows.DRIVE_REMOVABLE || typeret == windows.DRIVE_FIXED || typeret == windows.DRIVE_REMOTE || typeret == windows.DRIVE_CDROM {
lpVolumeNameBuffer := make([]byte, 256)
lpVolumeSerialNumber := int64(0)
lpMaximumComponentLength := int64(0)
lpFileSystemFlags := int64(0)
lpFileSystemNameBuffer := make([]byte, 256)
volpath, _ := windows.UTF16PtrFromString(name + ":/")
driveret, _, err := procGetVolumeInformation.Call(
uintptr(unsafe.Pointer(volpath)),
uintptr(unsafe.Pointer(&lpVolumeNameBuffer[0])),
uintptr(len(lpVolumeNameBuffer)),
uintptr(unsafe.Pointer(&lpVolumeSerialNumber)),
uintptr(unsafe.Pointer(&lpMaximumComponentLength)),
uintptr(unsafe.Pointer(&lpFileSystemFlags)),
uintptr(unsafe.Pointer(&lpFileSystemNameBuffer[0])),
uintptr(len(lpFileSystemNameBuffer)))
if driveret == 0 {
if typeret == 5 || typeret == 2 {
return nil, errDeviceNotReady // device is not ready will happen if there is no disk in the drive
}
if warnings != nil {
warnings.Add(err)
}
return nil, err
}
return &volumeInformation{
Label: string(bytes.Replace(lpVolumeNameBuffer, []byte("\x00"), []byte(""), -1)),
SerialNumber: lpVolumeSerialNumber,
MaximumComponentLength: lpMaximumComponentLength,
FileSystemFlags: lpFileSystemFlags,
FileSystemName: string(bytes.Replace(lpFileSystemNameBuffer, []byte("\x00"), []byte(""), -1)),
}, nil
}
return nil, errInvalidDriveType
return "", common.ErrNotImplementedError
}

View File

@ -21,6 +21,7 @@ package common
// high-performance serialization, especially for large data structures,
// should look at more advanced solutions such as the encoding/gob
// package or protocol buffers.
import (
"errors"
"io"

View File

@ -6,6 +6,7 @@ package common
// - linux (amd64, arm)
// - freebsd (amd64)
// - windows (amd64)
import (
"bufio"
"bytes"
@ -364,14 +365,8 @@ func HostDev(combineWith ...string) string {
return GetEnv("HOST_DEV", "/dev", combineWith...)
}
// MockEnv set environment variable and return revert function.
// MockEnv should be used testing only.
func MockEnv(key string, value string) func() {
original := os.Getenv(key)
os.Setenv(key, value)
return func() {
os.Setenv(key, original)
}
func HostRoot(combineWith ...string) string {
return GetEnv("HOST_ROOT", "/", combineWith...)
}
// getSysctrlEnv sets LC_ALL=C in a list of env vars for use when running

View File

@ -259,6 +259,11 @@ func VirtualizationWithContext(ctx context.Context) (string, string, error) {
}
}
if PathExists(HostRoot(".dockerenv")) {
system = "docker"
role = "guest"
}
// before returning for the first time, cache the system and role
cachedVirtOnce.Do(func() {
cachedVirtMutex.Lock()

View File

@ -218,9 +218,12 @@ func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, con
}
// Convert paths using native DOS format like:
// "\Device\HarddiskVolume1\Windows\systemew\file.txt"
//
// "\Device\HarddiskVolume1\Windows\systemew\file.txt"
//
// into:
// "C:\Windows\systemew\file.txt"
//
// "C:\Windows\systemew\file.txt"
func ConvertDOSPath(p string) string {
rawDrive := strings.Join(strings.Split(p, `\`)[:3], `\`)

View File

@ -71,7 +71,7 @@ func callSVMon(ctx context.Context) (*VirtualMemoryStat, *SwapMemoryStat, error)
swap.Total = t * pagesize
}
if t, err := strconv.ParseUint(p[3], 10, 64); err == nil {
swap.Free = swap.Total - t * pagesize
swap.Free = swap.Total - t*pagesize
}
}
break

View File

@ -8,9 +8,8 @@ import (
"errors"
"unsafe"
"golang.org/x/sys/unix"
"github.com/shirou/gopsutil/v3/internal/common"
"golang.org/x/sys/unix"
)
func VirtualMemory() (*VirtualMemoryStat, error) {

View File

@ -9,6 +9,7 @@ import (
"encoding/binary"
"errors"
"fmt"
"github.com/shirou/gopsutil/v3/internal/common"
"golang.org/x/sys/unix"
)

View File

@ -18,14 +18,14 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat,
iocounters := make([]IOCountersStat, 0, len(ifs))
for _, netif := range ifs {
n := IOCountersStat{
Name: netif.Name,
BytesSent: uint64(netif.OBytes),
BytesRecv: uint64(netif.IBytes),
Name: netif.Name,
BytesSent: uint64(netif.OBytes),
BytesRecv: uint64(netif.IBytes),
PacketsSent: uint64(netif.OPackets),
PacketsRecv: uint64(netif.IPackets),
Errin: uint64(netif.OErrors),
Errout: uint64(netif.IErrors),
Dropout: uint64(netif.XmitDrops),
Errin: uint64(netif.OErrors),
Errout: uint64(netif.IErrors),
Dropout: uint64(netif.XmitDrops),
}
iocounters = append(iocounters, n)
}

View File

@ -211,7 +211,8 @@ func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename stri
// Return a list of network connections
// Available kind:
// reference to netConnectionKindMap
//
// reference to netConnectionKindMap
func Connections(kind string) ([]ConnectionStat, error) {
return ConnectionsWithContext(context.Background(), kind)
}

View File

@ -69,7 +69,13 @@ func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
}
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError
mib := []int32{CTLKern, KernProc, KernProcPathname, p.Pid}
buf, _, err := common.CallSyscall(mib)
if err != nil {
return "", err
}
return strings.Trim(string(buf), "\x00"), nil
}
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {

View File

@ -8,9 +8,8 @@ import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
"github.com/shirou/gopsutil/v3/internal/common"
"golang.org/x/sys/windows"
)
type PROCESS_MEMORY_COUNTERS struct {

12
vendor/github.com/shoenig/go-m1cpu/.golangci.yaml generated vendored Normal file
View File

@ -0,0 +1,12 @@
run:
timeout: 5m
linters:
enable:
- gofmt
- errcheck
- errname
- errorlint
- bodyclose
- durationcheck
- whitespace

363
vendor/github.com/shoenig/go-m1cpu/LICENSE generated vendored Normal file
View File

@ -0,0 +1,363 @@
Mozilla Public License, version 2.0
1. Definitions
1.1. "Contributor"
means each individual or legal entity that creates, contributes to the
creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached the
notice in Exhibit A, the Executable Form of such Source Code Form, and
Modifications of such Source Code Form, in each case including portions
thereof.
1.5. "Incompatible With Secondary Licenses"
means
a. that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
b. that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the terms of
a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in a
separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible, whether
at the time of the initial grant or subsequently, any and all of the
rights conveyed by this License.
1.10. "Modifications"
means any of the following:
a. any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered Software; or
b. any new file in Source Code Form that contains any Covered Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the License,
by the making, using, selling, offering for sale, having made, import,
or transfer of either its Contributions or its Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public
License, Version 3.0, or any later versions of those licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that controls, is
controlled by, or is under common control with You. For purposes of this
definition, "control" means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by contract or
otherwise, or (b) ownership of more than fifty percent (50%) of the
outstanding shares or beneficial ownership of such entity.
2. License Grants and Conditions
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
a. under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
b. under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
a. for any code that a Contributor has removed from Covered Software; or
b. for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
c. under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights to
grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
3. Responsibilities
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
a. such Covered Software must also be made available in Source Code Form,
as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost
of distribution to the recipient; and
b. You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter the
recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty, or
limitations of liability) contained within the Source Code Form of the
Covered Software, except that You may alter any license notices to the
extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute,
judicial order, or regulation then You must: (a) comply with the terms of
this License to the maximum extent possible; and (b) describe the
limitations and the code they affect. Such description must be placed in a
text file included with all distributions of the Covered Software under
this License. Except to the extent prohibited by statute or regulation,
such description must be sufficiently detailed for a recipient of ordinary
skill to be able to understand it.
5. Termination
5.1. The rights granted under this License will terminate automatically if You
fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor
are reinstated (a) provisionally, unless and until such Contributor
explicitly and finally terminates Your grants, and (b) on an ongoing
basis, if such Contributor fails to notify You of the non-compliance by
some reasonable means prior to 60 days after You have come back into
compliance. Moreover, Your grants from a particular Contributor are
reinstated on an ongoing basis if such Contributor notifies You of the
non-compliance by some reasonable means, this is the first time You have
received notice of non-compliance with this License from such
Contributor, and You become compliant prior to 30 days after Your receipt
of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
6. Disclaimer of Warranty
Covered Software is provided under this License on an "as is" basis,
without warranty of any kind, either expressed, implied, or statutory,
including, without limitation, warranties that the Covered Software is free
of defects, merchantable, fit for a particular purpose or non-infringing.
The entire risk as to the quality and performance of the Covered Software
is with You. Should any Covered Software prove defective in any respect,
You (not any Contributor) assume the cost of any necessary servicing,
repair, or correction. This disclaimer of warranty constitutes an essential
part of this License. No use of any Covered Software is authorized under
this License except under this disclaimer.
7. Limitation of Liability
Under no circumstances and under no legal theory, whether tort (including
negligence), contract, or otherwise, shall any Contributor, or anyone who
distributes Covered Software as permitted above, be liable to You for any
direct, indirect, special, incidental, or consequential damages of any
character including, without limitation, damages for lost profits, loss of
goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses, even if such party shall have been
informed of the possibility of such damages. This limitation of liability
shall not apply to liability for death or personal injury resulting from
such party's negligence to the extent applicable law prohibits such
limitation. Some jurisdictions do not allow the exclusion or limitation of
incidental or consequential damages, so this exclusion and limitation may
not apply to You.
8. Litigation
Any litigation relating to this License may be brought only in the courts
of a jurisdiction where the defendant maintains its principal place of
business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions. Nothing
in this Section shall prevent a party's ability to bring cross-claims or
counter-claims.
9. Miscellaneous
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides that
the language of a contract shall be construed against the drafter shall not
be used to construe this License against a Contributor.
10. Versions of the License
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses If You choose to distribute Source Code Form that is
Incompatible With Secondary Licenses under the terms of this version of
the License, the notice described in Exhibit B of this License must be
attached.
Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file,
then You may include the notice in a location (such as a LICENSE file in a
relevant directory) where a recipient would be likely to look for such a
notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
This Source Code Form is "Incompatible
With Secondary Licenses", as defined by
the Mozilla Public License, v. 2.0.

12
vendor/github.com/shoenig/go-m1cpu/Makefile generated vendored Normal file
View File

@ -0,0 +1,12 @@
SHELL = bash
default: test
.PHONY: test
test:
@echo "--> Running Tests ..."
@go test -v -race ./...
vet:
@echo "--> Vet Go sources ..."
@go vet ./...

66
vendor/github.com/shoenig/go-m1cpu/README.md generated vendored Normal file
View File

@ -0,0 +1,66 @@
# m1cpu
[![Go Reference](https://pkg.go.dev/badge/github.com/shoenig/go-m1cpu.svg)](https://pkg.go.dev/github.com/shoenig/go-m1cpu)
[![MPL License](https://img.shields.io/github/license/shoenig/go-m1cpu?color=g&style=flat-square)](https://github.com/shoenig/go-m1cpu/blob/main/LICENSE)
[![Run CI Tests](https://github.com/shoenig/go-m1cpu/actions/workflows/ci.yaml/badge.svg)](https://github.com/shoenig/go-m1cpu/actions/workflows/ci.yaml)
The `go-m1cpu` module is a library for inspecting Apple Silicon CPUs in Go.
Use the `m1cpu` Go package for looking up the CPU frequency for Apple M1 and M2 CPUs.
# Install
```shell
go get github.com/shoenig/go-m1cpu@latest
```
# CGO
This package requires the use of [CGO](https://go.dev/blog/cgo).
Extracting the CPU properties is done via Apple's [IOKit](https://developer.apple.com/documentation/iokit?language=objc)
framework, which is accessible only through system C libraries.
# Example
Simple Go program to print Apple Silicon M1/M2 CPU speeds.
```go
package main
import (
"fmt"
"github.com/shoenig/go-m1cpu"
)
func main() {
fmt.Println("Apple Silicon", m1cpu.IsAppleSilicon())
fmt.Println("pCore GHz", m1cpu.PCoreGHz())
fmt.Println("eCore GHz", m1cpu.ECoreGHz())
fmt.Println("pCore Hz", m1cpu.PCoreHz())
fmt.Println("eCore Hz", m1cpu.ECoreHz())
}
```
Using `go test` to print out available information.
```
➜ go test -v -run Show
=== RUN Test_Show
cpu_test.go:42: pCore Hz 3504000000
cpu_test.go:43: eCore Hz 2424000000
cpu_test.go:44: pCore GHz 3.504
cpu_test.go:45: eCore GHz 2.424
cpu_test.go:46: pCore count 8
cpu_test.go:47: eCoreCount 4
cpu_test.go:50: pCore Caches 196608 131072 16777216
cpu_test.go:53: eCore Caches 131072 65536 4194304
--- PASS: Test_Show (0.00s)
```
# License
Open source under the [MPL](LICENSE)

208
vendor/github.com/shoenig/go-m1cpu/cpu.go generated vendored Normal file
View File

@ -0,0 +1,208 @@
//go:build darwin && arm64 && cgo
package m1cpu
// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit
// #include <CoreFoundation/CoreFoundation.h>
// #include <IOKit/IOKitLib.h>
// #include <sys/sysctl.h>
//
// #define HzToGHz(hz) ((hz) / 1000000000.0)
//
// UInt64 global_pCoreHz;
// UInt64 global_eCoreHz;
// int global_pCoreCount;
// int global_eCoreCount;
// int global_pCoreL1InstCacheSize;
// int global_eCoreL1InstCacheSize;
// int global_pCoreL1DataCacheSize;
// int global_eCoreL1DataCacheSize;
// int global_pCoreL2CacheSize;
// int global_eCoreL2CacheSize;
// char global_brand[32];
//
// UInt64 getFrequency(CFTypeRef typeRef) {
// CFDataRef cfData = typeRef;
//
// CFIndex size = CFDataGetLength(cfData);
// UInt8 buf[size];
// CFDataGetBytes(cfData, CFRangeMake(0, size), buf);
//
// UInt8 b1 = buf[size-5];
// UInt8 b2 = buf[size-6];
// UInt8 b3 = buf[size-7];
// UInt8 b4 = buf[size-8];
//
// UInt64 pCoreHz = 0x00000000FFFFFFFF & ((b1<<24) | (b2 << 16) | (b3 << 8) | (b4));
// return pCoreHz;
// }
//
// int sysctl_int(const char * name) {
// int value = -1;
// size_t size = 8;
// sysctlbyname(name, &value, &size, NULL, 0);
// return value;
// }
//
// void sysctl_string(const char * name, char * dest) {
// size_t size = 32;
// sysctlbyname(name, dest, &size, NULL, 0);
// }
//
// void initialize() {
// global_pCoreCount = sysctl_int("hw.perflevel0.physicalcpu");
// global_eCoreCount = sysctl_int("hw.perflevel1.physicalcpu");
// global_pCoreL1InstCacheSize = sysctl_int("hw.perflevel0.l1icachesize");
// global_eCoreL1InstCacheSize = sysctl_int("hw.perflevel1.l1icachesize");
// global_pCoreL1DataCacheSize = sysctl_int("hw.perflevel0.l1dcachesize");
// global_eCoreL1DataCacheSize = sysctl_int("hw.perflevel1.l1dcachesize");
// global_pCoreL2CacheSize = sysctl_int("hw.perflevel0.l2cachesize");
// global_eCoreL2CacheSize = sysctl_int("hw.perflevel1.l2cachesize");
// sysctl_string("machdep.cpu.brand_string", global_brand);
//
// CFMutableDictionaryRef matching = IOServiceMatching("AppleARMIODevice");
// io_iterator_t iter;
// IOServiceGetMatchingServices(kIOMainPortDefault, matching, &iter);
//
// const size_t bufsize = 512;
// io_object_t obj;
// while ((obj = IOIteratorNext(iter))) {
// char class[bufsize];
// IOObjectGetClass(obj, class);
// char name[bufsize];
// IORegistryEntryGetName(obj, name);
//
// if (strncmp(name, "pmgr", bufsize) == 0) {
// CFTypeRef pCoreRef = IORegistryEntryCreateCFProperty(obj, CFSTR("voltage-states5-sram"), kCFAllocatorDefault, 0);
// CFTypeRef eCoreRef = IORegistryEntryCreateCFProperty(obj, CFSTR("voltage-states1-sram"), kCFAllocatorDefault, 0);
//
// long long pCoreHz = getFrequency(pCoreRef);
// long long eCoreHz = getFrequency(eCoreRef);
//
// global_pCoreHz = pCoreHz;
// global_eCoreHz = eCoreHz;
// return;
// }
// }
// }
//
// UInt64 eCoreHz() {
// return global_eCoreHz;
// }
//
// UInt64 pCoreHz() {
// return global_pCoreHz;
// }
//
// Float64 eCoreGHz() {
// return HzToGHz(global_eCoreHz);
// }
//
// Float64 pCoreGHz() {
// return HzToGHz(global_pCoreHz);
// }
//
// int pCoreCount() {
// return global_pCoreCount;
// }
//
// int eCoreCount() {
// return global_eCoreCount;
// }
//
// int pCoreL1InstCacheSize() {
// return global_pCoreL1InstCacheSize;
// }
//
// int pCoreL1DataCacheSize() {
// return global_pCoreL1DataCacheSize;
// }
//
// int pCoreL2CacheSize() {
// return global_pCoreL2CacheSize;
// }
//
// int eCoreL1InstCacheSize() {
// return global_eCoreL1InstCacheSize;
// }
//
// int eCoreL1DataCacheSize() {
// return global_eCoreL1DataCacheSize;
// }
//
// int eCoreL2CacheSize() {
// return global_eCoreL2CacheSize;
// }
//
// char * modelName() {
// return global_brand;
// }
import "C"
func init() {
C.initialize()
}
// IsAppleSilicon returns true on this platform.
func IsAppleSilicon() bool {
return true
}
// PCoreHZ returns the max frequency in Hertz of the P-Core of an Apple Silicon CPU.
func PCoreHz() uint64 {
return uint64(C.pCoreHz())
}
// ECoreHZ returns the max frequency in Hertz of the E-Core of an Apple Silicon CPU.
func ECoreHz() uint64 {
return uint64(C.eCoreHz())
}
// PCoreGHz returns the max frequency in Gigahertz of the P-Core of an Apple Silicon CPU.
func PCoreGHz() float64 {
return float64(C.pCoreGHz())
}
// ECoreGHz returns the max frequency in Gigahertz of the E-Core of an Apple Silicon CPU.
func ECoreGHz() float64 {
return float64(C.eCoreGHz())
}
// PCoreCount returns the number of physical P (performance) cores.
func PCoreCount() int {
return int(C.pCoreCount())
}
// ECoreCount returns the number of physical E (efficiency) cores.
func ECoreCount() int {
return int(C.eCoreCount())
}
// PCoreCacheSize returns the sizes of the P (performance) core cache sizes
// in the order of
//
// - L1 instruction cache
// - L1 data cache
// - L2 cache
func PCoreCache() (int, int, int) {
return int(C.pCoreL1InstCacheSize()),
int(C.pCoreL1DataCacheSize()),
int(C.pCoreL2CacheSize())
}
// ECoreCacheSize returns the sizes of the E (efficiency) core cache sizes
// in the order of
//
// - L1 instruction cache
// - L1 data cache
// - L2 cache
func ECoreCache() (int, int, int) {
return int(C.eCoreL1InstCacheSize()),
int(C.eCoreL1DataCacheSize()),
int(C.eCoreL2CacheSize())
}
// ModelName returns the model name of the CPU.
func ModelName() string {
return C.GoString(C.modelName())
}

53
vendor/github.com/shoenig/go-m1cpu/incompatible.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
//go:build !darwin || !arm64 || !cgo
package m1cpu
// IsAppleSilicon return false on this platform.
func IsAppleSilicon() bool {
return false
}
// PCoreHZ requires darwin/arm64
func PCoreHz() uint64 {
panic("m1cpu: not a darwin/arm64 system")
}
// ECoreHZ requires darwin/arm64
func ECoreHz() uint64 {
panic("m1cpu: not a darwin/arm64 system")
}
// PCoreGHz requires darwin/arm64
func PCoreGHz() float64 {
panic("m1cpu: not a darwin/arm64 system")
}
// ECoreGHz requires darwin/arm64
func ECoreGHz() float64 {
panic("m1cpu: not a darwin/arm64 system")
}
// PCoreCount requires darwin/arm64
func PCoreCount() int {
panic("m1cpu: not a darwin/arm64 system")
}
// ECoreCount requires darwin/arm64
func ECoreCount() int {
panic("m1cpu: not a darwin/arm64 system")
}
// PCoreCacheSize requires darwin/arm64
func PCoreCache() (int, int, int) {
panic("m1cpu: not a darwin/arm64 system")
}
// ECoreCacheSize requires darwin/arm64
func ECoreCache() (int, int, int) {
panic("m1cpu: not a darwin/arm64 system")
}
// ModelName requires darwin/arm64
func ModelName() string {
panic("m1cpu: not a darwin/arm64 system")
}

View File

@ -8,7 +8,6 @@ import (
"fmt"
"math"
"os"
"path/filepath"
"reflect"
"regexp"
"runtime"
@ -141,12 +140,11 @@ func CallerInfo() []string {
}
parts := strings.Split(file, "/")
file = parts[len(parts)-1]
if len(parts) > 1 {
filename := parts[len(parts)-1]
dir := parts[len(parts)-2]
if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" {
path, _ := filepath.Abs(file)
callers = append(callers, fmt.Sprintf("%s:%d", path, line))
if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" {
callers = append(callers, fmt.Sprintf("%s:%d", file, line))
}
}
@ -530,7 +528,7 @@ func isNil(object interface{}) bool {
[]reflect.Kind{
reflect.Chan, reflect.Func,
reflect.Interface, reflect.Map,
reflect.Ptr, reflect.Slice},
reflect.Ptr, reflect.Slice, reflect.UnsafePointer},
kind)
if isNilableKind && value.IsNil() {
@ -818,49 +816,44 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
return true // we consider nil to be equal to the nil set
}
defer func() {
if e := recover(); e != nil {
ok = false
}
}()
listKind := reflect.TypeOf(list).Kind()
subsetKind := reflect.TypeOf(subset).Kind()
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
}
subsetKind := reflect.TypeOf(subset).Kind()
if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
}
subsetValue := reflect.ValueOf(subset)
if subsetKind == reflect.Map && listKind == reflect.Map {
listValue := reflect.ValueOf(list)
subsetKeys := subsetValue.MapKeys()
subsetMap := reflect.ValueOf(subset)
actualMap := reflect.ValueOf(list)
for i := 0; i < len(subsetKeys); i++ {
subsetKey := subsetKeys[i]
subsetElement := subsetValue.MapIndex(subsetKey).Interface()
listElement := listValue.MapIndex(subsetKey).Interface()
for _, k := range subsetMap.MapKeys() {
ev := subsetMap.MapIndex(k)
av := actualMap.MapIndex(k)
if !ObjectsAreEqual(subsetElement, listElement) {
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, subsetElement), msgAndArgs...)
if !av.IsValid() {
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...)
}
if !ObjectsAreEqual(ev.Interface(), av.Interface()) {
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...)
}
}
return true
}
for i := 0; i < subsetValue.Len(); i++ {
element := subsetValue.Index(i).Interface()
subsetList := reflect.ValueOf(subset)
for i := 0; i < subsetList.Len(); i++ {
element := subsetList.Index(i).Interface()
ok, found := containsElement(list, element)
if !ok {
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...)
}
if !found {
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, element), msgAndArgs...)
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...)
}
}
@ -879,34 +872,28 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...)
}
defer func() {
if e := recover(); e != nil {
ok = false
}
}()
listKind := reflect.TypeOf(list).Kind()
subsetKind := reflect.TypeOf(subset).Kind()
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
}
subsetKind := reflect.TypeOf(subset).Kind()
if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
}
subsetValue := reflect.ValueOf(subset)
if subsetKind == reflect.Map && listKind == reflect.Map {
listValue := reflect.ValueOf(list)
subsetKeys := subsetValue.MapKeys()
subsetMap := reflect.ValueOf(subset)
actualMap := reflect.ValueOf(list)
for i := 0; i < len(subsetKeys); i++ {
subsetKey := subsetKeys[i]
subsetElement := subsetValue.MapIndex(subsetKey).Interface()
listElement := listValue.MapIndex(subsetKey).Interface()
for _, k := range subsetMap.MapKeys() {
ev := subsetMap.MapIndex(k)
av := actualMap.MapIndex(k)
if !ObjectsAreEqual(subsetElement, listElement) {
if !av.IsValid() {
return true
}
if !ObjectsAreEqual(ev.Interface(), av.Interface()) {
return true
}
}
@ -914,8 +901,9 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
}
for i := 0; i < subsetValue.Len(); i++ {
element := subsetValue.Index(i).Interface()
subsetList := reflect.ValueOf(subset)
for i := 0; i < subsetList.Len(); i++ {
element := subsetList.Index(i).Interface()
ok, found := containsElement(list, element)
if !ok {
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)

View File

@ -24,6 +24,21 @@ var hwCap uint
var hwCap2 uint
func readHWCAP() error {
// For Go 1.21+, get auxv from the Go runtime.
if a := getAuxv(); len(a) > 0 {
for len(a) >= 2 {
tag, val := a[0], uint(a[1])
a = a[2:]
switch tag {
case _AT_HWCAP:
hwCap = val
case _AT_HWCAP2:
hwCap2 = val
}
}
return nil
}
buf, err := ioutil.ReadFile(procAuxv)
if err != nil {
// e.g. on android /proc/self/auxv is not accessible, so silently

16
vendor/golang.org/x/sys/cpu/runtime_auxv.go generated vendored Normal file
View File

@ -0,0 +1,16 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cpu
// getAuxvFn is non-nil on Go 1.21+ (via runtime_auxv_go121.go init)
// on platforms that use auxv.
var getAuxvFn func() []uintptr
func getAuxv() []uintptr {
if getAuxvFn == nil {
return nil
}
return getAuxvFn()
}

19
vendor/golang.org/x/sys/cpu/runtime_auxv_go121.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
// Copyright 2023 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.
//go:build go1.21
// +build go1.21
package cpu
import (
_ "unsafe" // for linkname
)
//go:linkname runtime_getAuxv runtime.getAuxv
func runtime_getAuxv() []uintptr
func init() {
getAuxvFn = runtime_getAuxv
}

View File

@ -63,7 +63,7 @@ func LookPath(file string) (string, error) {
}
func fixCmd(name string, cmd *exec.Cmd) {
if filepath.Base(name) == name && !filepath.IsAbs(cmd.Path) {
if filepath.Base(name) == name && !filepath.IsAbs(cmd.Path) && !isGo119ErrFieldSet(cmd) {
// exec.Command was called with a bare binary name and
// exec.LookPath returned a path which is not absolute.
// Set cmd.lookPathErr and clear cmd.Path so that it

View File

@ -7,6 +7,12 @@
package execabs
import "os/exec"
func isGo119ErrDot(err error) bool {
return false
}
func isGo119ErrFieldSet(cmd *exec.Cmd) bool {
return false
}

View File

@ -15,3 +15,7 @@ import (
func isGo119ErrDot(err error) bool {
return errors.Is(err, exec.ErrDot)
}
func isGo119ErrFieldSet(cmd *exec.Cmd) bool {
return cmd.Err != nil
}

View File

@ -8,7 +8,6 @@
package unix
import (
"runtime"
"unsafe"
)
@ -27,7 +26,7 @@ func IoctlSetInt(fd int, req uint, value int) error {
// passing the integer value directly.
func IoctlSetPointerInt(fd int, req uint, value int) error {
v := int32(value)
return ioctl(fd, req, uintptr(unsafe.Pointer(&v)))
return ioctlPtr(fd, req, unsafe.Pointer(&v))
}
// IoctlSetWinsize performs an ioctl on fd with a *Winsize argument.
@ -36,9 +35,7 @@ func IoctlSetPointerInt(fd int, req uint, value int) error {
func IoctlSetWinsize(fd int, req uint, value *Winsize) error {
// TODO: if we get the chance, remove the req parameter and
// hardcode TIOCSWINSZ.
err := ioctl(fd, req, uintptr(unsafe.Pointer(value)))
runtime.KeepAlive(value)
return err
return ioctlPtr(fd, req, unsafe.Pointer(value))
}
// IoctlSetTermios performs an ioctl on fd with a *Termios.
@ -46,9 +43,7 @@ func IoctlSetWinsize(fd int, req uint, value *Winsize) error {
// The req value will usually be TCSETA or TIOCSETA.
func IoctlSetTermios(fd int, req uint, value *Termios) error {
// TODO: if we get the chance, remove the req parameter.
err := ioctl(fd, req, uintptr(unsafe.Pointer(value)))
runtime.KeepAlive(value)
return err
return ioctlPtr(fd, req, unsafe.Pointer(value))
}
// IoctlGetInt performs an ioctl operation which gets an integer value
@ -58,18 +53,18 @@ func IoctlSetTermios(fd int, req uint, value *Termios) error {
// for those, IoctlRetInt should be used instead of this function.
func IoctlGetInt(fd int, req uint) (int, error) {
var value int
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value)))
err := ioctlPtr(fd, req, unsafe.Pointer(&value))
return value, err
}
func IoctlGetWinsize(fd int, req uint) (*Winsize, error) {
var value Winsize
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value)))
err := ioctlPtr(fd, req, unsafe.Pointer(&value))
return &value, err
}
func IoctlGetTermios(fd int, req uint) (*Termios, error) {
var value Termios
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value)))
err := ioctlPtr(fd, req, unsafe.Pointer(&value))
return &value, err
}

View File

@ -27,9 +27,7 @@ func IoctlSetInt(fd int, req uint, value int) error {
func IoctlSetWinsize(fd int, req uint, value *Winsize) error {
// TODO: if we get the chance, remove the req parameter and
// hardcode TIOCSWINSZ.
err := ioctl(fd, req, uintptr(unsafe.Pointer(value)))
runtime.KeepAlive(value)
return err
return ioctlPtr(fd, req, unsafe.Pointer(value))
}
// IoctlSetTermios performs an ioctl on fd with a *Termios.
@ -51,13 +49,13 @@ func IoctlSetTermios(fd int, req uint, value *Termios) error {
// for those, IoctlRetInt should be used instead of this function.
func IoctlGetInt(fd int, req uint) (int, error) {
var value int
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value)))
err := ioctlPtr(fd, req, unsafe.Pointer(&value))
return value, err
}
func IoctlGetWinsize(fd int, req uint) (*Winsize, error) {
var value Winsize
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value)))
err := ioctlPtr(fd, req, unsafe.Pointer(&value))
return &value, err
}

View File

@ -7,6 +7,12 @@
package unix
import "unsafe"
func ptrace(request int, pid int, addr uintptr, data uintptr) error {
return ptrace1(request, pid, addr, data)
}
func ptracePtr(request int, pid int, addr uintptr, data unsafe.Pointer) error {
return ptrace1Ptr(request, pid, addr, data)
}

View File

@ -7,6 +7,12 @@
package unix
import "unsafe"
func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) {
return ENOTSUP
}
func ptracePtr(request int, pid int, addr uintptr, data unsafe.Pointer) (err error) {
return ENOTSUP
}

View File

@ -292,9 +292,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
break
}
}
bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
sa.Name = string(bytes)
sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n))
return sa, nil
case AF_INET:
@ -411,6 +409,7 @@ func (w WaitStatus) CoreDump() bool { return w&0x80 == 0x80 }
func (w WaitStatus) TrapCause() int { return -1 }
//sys ioctl(fd int, req uint, arg uintptr) (err error)
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = ioctl
// fcntl must never be called with cmd=F_DUP2FD because it doesn't work on AIX
// There is no way to create a custom fcntl and to keep //sys fcntl easily,

View File

@ -245,8 +245,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
break
}
}
bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
sa.Name = string(bytes)
sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n))
return sa, nil
case AF_INET:

View File

@ -14,7 +14,6 @@ package unix
import (
"fmt"
"runtime"
"syscall"
"unsafe"
)
@ -376,11 +375,10 @@ func Flistxattr(fd int, dest []byte) (sz int, err error) {
func Kill(pid int, signum syscall.Signal) (err error) { return kill(pid, int(signum), 1) }
//sys ioctl(fd int, req uint, arg uintptr) (err error)
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL
func IoctlCtlInfo(fd int, ctlInfo *CtlInfo) error {
err := ioctl(fd, CTLIOCGINFO, uintptr(unsafe.Pointer(ctlInfo)))
runtime.KeepAlive(ctlInfo)
return err
return ioctlPtr(fd, CTLIOCGINFO, unsafe.Pointer(ctlInfo))
}
// IfreqMTU is struct ifreq used to get or set a network device's MTU.
@ -394,16 +392,14 @@ type IfreqMTU struct {
func IoctlGetIfreqMTU(fd int, ifname string) (*IfreqMTU, error) {
var ifreq IfreqMTU
copy(ifreq.Name[:], ifname)
err := ioctl(fd, SIOCGIFMTU, uintptr(unsafe.Pointer(&ifreq)))
err := ioctlPtr(fd, SIOCGIFMTU, unsafe.Pointer(&ifreq))
return &ifreq, err
}
// IoctlSetIfreqMTU performs the SIOCSIFMTU ioctl operation on fd to set the MTU
// of the network device specified by ifreq.Name.
func IoctlSetIfreqMTU(fd int, ifreq *IfreqMTU) error {
err := ioctl(fd, SIOCSIFMTU, uintptr(unsafe.Pointer(ifreq)))
runtime.KeepAlive(ifreq)
return err
return ioctlPtr(fd, SIOCSIFMTU, unsafe.Pointer(ifreq))
}
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS_SYSCTL

View File

@ -47,5 +47,6 @@ func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr,
//sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT64
//sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64
//sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace
//sys ptrace1Ptr(request int, pid int, addr unsafe.Pointer, data uintptr) (err error) = SYS_ptrace
//sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64
//sys Statfs(path string, stat *Statfs_t) (err error) = SYS_STATFS64

View File

@ -47,5 +47,6 @@ func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr,
//sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT
//sys Lstat(path string, stat *Stat_t) (err error)
//sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace
//sys ptrace1Ptr(request int, pid int, addr unsafe.Pointer, data uintptr) (err error) = SYS_ptrace
//sys Stat(path string, stat *Stat_t) (err error)
//sys Statfs(path string, stat *Statfs_t) (err error)

View File

@ -172,6 +172,7 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
}
//sys ioctl(fd int, req uint, arg uintptr) (err error)
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL

View File

@ -161,7 +161,8 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
return
}
//sys ioctl(fd int, req uint, arg uintptr) (err error)
//sys ioctl(fd int, req uint, arg uintptr) (err error) = SYS_IOCTL
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
@ -253,6 +254,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
}
//sys ptrace(request int, pid int, addr uintptr, data int) (err error)
//sys ptracePtr(request int, pid int, addr unsafe.Pointer, data int) (err error) = SYS_PTRACE
func PtraceAttach(pid int) (err error) {
return ptrace(PT_ATTACH, pid, 0, 0)
@ -267,19 +269,36 @@ func PtraceDetach(pid int) (err error) {
}
func PtraceGetFpRegs(pid int, fpregsout *FpReg) (err error) {
return ptrace(PT_GETFPREGS, pid, uintptr(unsafe.Pointer(fpregsout)), 0)
return ptracePtr(PT_GETFPREGS, pid, unsafe.Pointer(fpregsout), 0)
}
func PtraceGetRegs(pid int, regsout *Reg) (err error) {
return ptrace(PT_GETREGS, pid, uintptr(unsafe.Pointer(regsout)), 0)
return ptracePtr(PT_GETREGS, pid, unsafe.Pointer(regsout), 0)
}
func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{
Op: int32(req),
Offs: offs,
}
if countin > 0 {
_ = out[:countin] // check bounds
ioDesc.Addr = &out[0]
} else if out != nil {
ioDesc.Addr = (*byte)(unsafe.Pointer(&_zero))
}
ioDesc.SetLen(countin)
err = ptracePtr(PT_IO, pid, unsafe.Pointer(&ioDesc), 0)
return int(ioDesc.Len), err
}
func PtraceLwpEvents(pid int, enable int) (err error) {
return ptrace(PT_LWP_EVENTS, pid, 0, enable)
}
func PtraceLwpInfo(pid int, info uintptr) (err error) {
return ptrace(PT_LWPINFO, pid, info, int(unsafe.Sizeof(PtraceLwpInfoStruct{})))
func PtraceLwpInfo(pid int, info *PtraceLwpInfoStruct) (err error) {
return ptracePtr(PT_LWPINFO, pid, unsafe.Pointer(info), int(unsafe.Sizeof(*info)))
}
func PtracePeekData(pid int, addr uintptr, out []byte) (count int, err error) {
@ -299,13 +318,25 @@ func PtracePokeText(pid int, addr uintptr, data []byte) (count int, err error) {
}
func PtraceSetRegs(pid int, regs *Reg) (err error) {
return ptrace(PT_SETREGS, pid, uintptr(unsafe.Pointer(regs)), 0)
return ptracePtr(PT_SETREGS, pid, unsafe.Pointer(regs), 0)
}
func PtraceSingleStep(pid int) (err error) {
return ptrace(PT_STEP, pid, 1, 0)
}
func Dup3(oldfd, newfd, flags int) error {
if oldfd == newfd || flags&^O_CLOEXEC != 0 {
return EINVAL
}
how := F_DUP2FD
if flags&O_CLOEXEC != 0 {
how = F_DUP2FD_CLOEXEC
}
_, err := fcntl(oldfd, how, newfd)
return err
}
/*
* Exposed directly
*/

View File

@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length)
}
func (d *PtraceIoDesc) SetLen(length int) {
d.Len = uint32(length)
}
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
var writtenOut uint64 = 0
_, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0)
@ -57,16 +61,5 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
func PtraceGetFsBase(pid int, fsbase *int64) (err error) {
return ptrace(PT_GETFSBASE, pid, uintptr(unsafe.Pointer(fsbase)), 0)
}
func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{
Op: int32(req),
Offs: offs,
Addr: uintptr(unsafe.Pointer(&out[0])), // TODO(#58351): this is not safe.
Len: uint32(countin),
}
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
return int(ioDesc.Len), err
return ptracePtr(PT_GETFSBASE, pid, unsafe.Pointer(fsbase), 0)
}

View File

@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length)
}
func (d *PtraceIoDesc) SetLen(length int) {
d.Len = uint64(length)
}
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
var writtenOut uint64 = 0
_, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0)
@ -57,16 +61,5 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
func PtraceGetFsBase(pid int, fsbase *int64) (err error) {
return ptrace(PT_GETFSBASE, pid, uintptr(unsafe.Pointer(fsbase)), 0)
}
func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{
Op: int32(req),
Offs: offs,
Addr: uintptr(unsafe.Pointer(&out[0])), // TODO(#58351): this is not safe.
Len: uint64(countin),
}
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
return int(ioDesc.Len), err
return ptracePtr(PT_GETFSBASE, pid, unsafe.Pointer(fsbase), 0)
}

View File

@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length)
}
func (d *PtraceIoDesc) SetLen(length int) {
d.Len = uint32(length)
}
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
var writtenOut uint64 = 0
_, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0)
@ -55,14 +59,3 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
}
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{
Op: int32(req),
Offs: offs,
Addr: uintptr(unsafe.Pointer(&out[0])), // TODO(#58351): this is not safe.
Len: uint32(countin),
}
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
return int(ioDesc.Len), err
}

View File

@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length)
}
func (d *PtraceIoDesc) SetLen(length int) {
d.Len = uint64(length)
}
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
var writtenOut uint64 = 0
_, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0)
@ -55,14 +59,3 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
}
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{
Op: int32(req),
Offs: offs,
Addr: uintptr(unsafe.Pointer(&out[0])), // TODO(#58351): this is not safe.
Len: uint64(countin),
}
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
return int(ioDesc.Len), err
}

View File

@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length)
}
func (d *PtraceIoDesc) SetLen(length int) {
d.Len = uint64(length)
}
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
var writtenOut uint64 = 0
_, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0)
@ -55,14 +59,3 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
}
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{
Op: int32(req),
Offs: offs,
Addr: uintptr(unsafe.Pointer(&out[0])), // TODO(#58351): this is not safe.
Len: uint64(countin),
}
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
return int(ioDesc.Len), err
}

View File

@ -20,3 +20,11 @@ func ioctl(fd int, req uint, arg uintptr) (err error) {
}
return
}
func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) {
r0, er := C.ioctl(C.int(fd), C.ulong(req), C.uintptr_t(uintptr(arg)))
if r0 == -1 && er != nil {
err = er
}
return
}

View File

@ -1015,8 +1015,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
for n < len(pp.Path) && pp.Path[n] != 0 {
n++
}
bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
sa.Name = string(bytes)
sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n))
return sa, nil
case AF_INET:
@ -1365,6 +1364,10 @@ func SetsockoptTCPRepairOpt(fd, level, opt int, o []TCPRepairOpt) (err error) {
return setsockopt(fd, level, opt, unsafe.Pointer(&o[0]), uintptr(SizeofTCPRepairOpt*len(o)))
}
func SetsockoptTCPMD5Sig(fd, level, opt int, s *TCPMD5Sig) error {
return setsockopt(fd, level, opt, unsafe.Pointer(s), unsafe.Sizeof(*s))
}
// Keyctl Commands (http://man7.org/linux/man-pages/man2/keyctl.2.html)
// KeyctlInt calls keyctl commands in which each argument is an int.
@ -1579,6 +1582,7 @@ func BindToDevice(fd int, device string) (err error) {
}
//sys ptrace(request int, pid int, addr uintptr, data uintptr) (err error)
//sys ptracePtr(request int, pid int, addr uintptr, data unsafe.Pointer) (err error) = SYS_PTRACE
func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err error) {
// The peek requests are machine-size oriented, so we wrap it
@ -1596,7 +1600,7 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err erro
// boundary.
n := 0
if addr%SizeofPtr != 0 {
err = ptrace(req, pid, addr-addr%SizeofPtr, uintptr(unsafe.Pointer(&buf[0])))
err = ptracePtr(req, pid, addr-addr%SizeofPtr, unsafe.Pointer(&buf[0]))
if err != nil {
return 0, err
}
@ -1608,7 +1612,7 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err erro
for len(out) > 0 {
// We use an internal buffer to guarantee alignment.
// It's not documented if this is necessary, but we're paranoid.
err = ptrace(req, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0])))
err = ptracePtr(req, pid, addr+uintptr(n), unsafe.Pointer(&buf[0]))
if err != nil {
return n, err
}
@ -1640,7 +1644,7 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c
n := 0
if addr%SizeofPtr != 0 {
var buf [SizeofPtr]byte
err = ptrace(peekReq, pid, addr-addr%SizeofPtr, uintptr(unsafe.Pointer(&buf[0])))
err = ptracePtr(peekReq, pid, addr-addr%SizeofPtr, unsafe.Pointer(&buf[0]))
if err != nil {
return 0, err
}
@ -1667,7 +1671,7 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c
// Trailing edge.
if len(data) > 0 {
var buf [SizeofPtr]byte
err = ptrace(peekReq, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0])))
err = ptracePtr(peekReq, pid, addr+uintptr(n), unsafe.Pointer(&buf[0]))
if err != nil {
return n, err
}
@ -1696,11 +1700,11 @@ func PtracePokeUser(pid int, addr uintptr, data []byte) (count int, err error) {
}
func PtraceGetRegs(pid int, regsout *PtraceRegs) (err error) {
return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout)))
return ptracePtr(PTRACE_GETREGS, pid, 0, unsafe.Pointer(regsout))
}
func PtraceSetRegs(pid int, regs *PtraceRegs) (err error) {
return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs)))
return ptracePtr(PTRACE_SETREGS, pid, 0, unsafe.Pointer(regs))
}
func PtraceSetOptions(pid int, options int) (err error) {
@ -1709,7 +1713,7 @@ func PtraceSetOptions(pid int, options int) (err error) {
func PtraceGetEventMsg(pid int) (msg uint, err error) {
var data _C_long
err = ptrace(PTRACE_GETEVENTMSG, pid, 0, uintptr(unsafe.Pointer(&data)))
err = ptracePtr(PTRACE_GETEVENTMSG, pid, 0, unsafe.Pointer(&data))
msg = uint(data)
return
}
@ -2154,6 +2158,14 @@ func isGroupMember(gid int) bool {
return false
}
func isCapDacOverrideSet() bool {
hdr := CapUserHeader{Version: LINUX_CAPABILITY_VERSION_3}
data := [2]CapUserData{}
err := Capget(&hdr, &data[0])
return err == nil && data[0].Effective&(1<<CAP_DAC_OVERRIDE) != 0
}
//sys faccessat(dirfd int, path string, mode uint32) (err error)
//sys Faccessat2(dirfd int, path string, mode uint32, flags int) (err error)
@ -2189,6 +2201,12 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
var uid int
if flags&AT_EACCESS != 0 {
uid = Geteuid()
if uid != 0 && isCapDacOverrideSet() {
// If CAP_DAC_OVERRIDE is set, file access check is
// done by the kernel in the same way as for root
// (see generic_permission() in the Linux sources).
uid = 0
}
} else {
uid = Getuid()
}

View File

@ -13,7 +13,6 @@
package unix
import (
"runtime"
"syscall"
"unsafe"
)
@ -178,13 +177,13 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
}
//sys ioctl(fd int, req uint, arg uintptr) (err error)
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
func IoctlGetPtmget(fd int, req uint) (*Ptmget, error) {
var value Ptmget
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value)))
runtime.KeepAlive(value)
err := ioctlPtr(fd, req, unsafe.Pointer(&value))
return &value, err
}

View File

@ -152,6 +152,7 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
}
//sys ioctl(fd int, req uint, arg uintptr) (err error)
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL

View File

@ -408,8 +408,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
for n < len(pp.Path) && pp.Path[n] != 0 {
n++
}
bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
sa.Name = string(bytes)
sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n))
return sa, nil
case AF_INET:
@ -547,21 +546,25 @@ func Minor(dev uint64) uint32 {
*/
//sys ioctlRet(fd int, req uint, arg uintptr) (ret int, err error) = libc.ioctl
//sys ioctlPtrRet(fd int, req uint, arg unsafe.Pointer) (ret int, err error) = libc.ioctl
func ioctl(fd int, req uint, arg uintptr) (err error) {
_, err = ioctlRet(fd, req, arg)
return err
}
func IoctlSetTermio(fd int, req uint, value *Termio) error {
err := ioctl(fd, req, uintptr(unsafe.Pointer(value)))
runtime.KeepAlive(value)
func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) {
_, err = ioctlPtrRet(fd, req, arg)
return err
}
func IoctlSetTermio(fd int, req uint, value *Termio) error {
return ioctlPtr(fd, req, unsafe.Pointer(value))
}
func IoctlGetTermio(fd int, req uint) (*Termio, error) {
var value Termio
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value)))
err := ioctlPtr(fd, req, unsafe.Pointer(&value))
return &value, err
}
@ -1084,7 +1087,7 @@ func IoctlSetIntRetInt(fd int, req uint, arg int) (int, error) {
func IoctlSetString(fd int, req uint, val string) error {
bs := make([]byte, len(val)+1)
copy(bs[:len(bs)-1], val)
err := ioctl(fd, req, uintptr(unsafe.Pointer(&bs[0])))
err := ioctlPtr(fd, req, unsafe.Pointer(&bs[0]))
runtime.KeepAlive(&bs[0])
return err
}
@ -1118,7 +1121,7 @@ func (l *Lifreq) GetLifruUint() uint {
}
func IoctlLifreq(fd int, req uint, l *Lifreq) error {
return ioctl(fd, req, uintptr(unsafe.Pointer(l)))
return ioctlPtr(fd, req, unsafe.Pointer(l))
}
// Strioctl Helpers
@ -1129,5 +1132,5 @@ func (s *Strioctl) SetInt(i int) {
}
func IoctlSetStrioctlRetInt(fd int, req uint, s *Strioctl) (int, error) {
return ioctlRet(fd, req, uintptr(unsafe.Pointer(s)))
return ioctlPtrRet(fd, req, unsafe.Pointer(s))
}

View File

@ -139,8 +139,7 @@ func anyToSockaddr(_ int, rsa *RawSockaddrAny) (Sockaddr, error) {
for n < int(pp.Len) && pp.Path[n] != 0 {
n++
}
bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
sa.Name = string(bytes)
sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n))
return sa, nil
case AF_INET:
@ -214,6 +213,7 @@ func (cmsg *Cmsghdr) SetLen(length int) {
//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) = SYS_MMAP
//sys munmap(addr uintptr, length uintptr) (err error) = SYS_MUNMAP
//sys ioctl(fd int, req uint, arg uintptr) (err error) = SYS_IOCTL
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL
//sys Access(path string, mode uint32) (err error) = SYS___ACCESS_A
//sys Chdir(path string) (err error) = SYS___CHDIR_A

View File

@ -70,6 +70,7 @@ const (
ALG_SET_DRBG_ENTROPY = 0x6
ALG_SET_IV = 0x2
ALG_SET_KEY = 0x1
ALG_SET_KEY_BY_KEY_SERIAL = 0x7
ALG_SET_OP = 0x3
ANON_INODE_FS_MAGIC = 0x9041934
ARPHRD_6LOWPAN = 0x339
@ -774,6 +775,8 @@ const (
DEVLINK_GENL_MCGRP_CONFIG_NAME = "config"
DEVLINK_GENL_NAME = "devlink"
DEVLINK_GENL_VERSION = 0x1
DEVLINK_PORT_FN_CAP_MIGRATABLE = 0x2
DEVLINK_PORT_FN_CAP_ROCE = 0x1
DEVLINK_SB_THRESHOLD_TO_ALPHA_MAX = 0x14
DEVLINK_SUPPORTED_FLASH_OVERWRITE_SECTIONS = 0x3
DEVMEM_MAGIC = 0x454d444d
@ -1262,6 +1265,8 @@ const (
FSCRYPT_MODE_AES_256_CTS = 0x4
FSCRYPT_MODE_AES_256_HCTR2 = 0xa
FSCRYPT_MODE_AES_256_XTS = 0x1
FSCRYPT_MODE_SM4_CTS = 0x8
FSCRYPT_MODE_SM4_XTS = 0x7
FSCRYPT_POLICY_FLAGS_PAD_16 = 0x2
FSCRYPT_POLICY_FLAGS_PAD_32 = 0x3
FSCRYPT_POLICY_FLAGS_PAD_4 = 0x0
@ -1280,8 +1285,6 @@ const (
FS_ENCRYPTION_MODE_AES_256_GCM = 0x2
FS_ENCRYPTION_MODE_AES_256_XTS = 0x1
FS_ENCRYPTION_MODE_INVALID = 0x0
FS_ENCRYPTION_MODE_SPECK128_256_CTS = 0x8
FS_ENCRYPTION_MODE_SPECK128_256_XTS = 0x7
FS_IOC_ADD_ENCRYPTION_KEY = 0xc0506617
FS_IOC_GET_ENCRYPTION_KEY_STATUS = 0xc080661a
FS_IOC_GET_ENCRYPTION_POLICY_EX = 0xc0096616
@ -1770,6 +1773,7 @@ const (
LANDLOCK_ACCESS_FS_REFER = 0x2000
LANDLOCK_ACCESS_FS_REMOVE_DIR = 0x10
LANDLOCK_ACCESS_FS_REMOVE_FILE = 0x20
LANDLOCK_ACCESS_FS_TRUNCATE = 0x4000
LANDLOCK_ACCESS_FS_WRITE_FILE = 0x2
LANDLOCK_CREATE_RULESET_VERSION = 0x1
LINUX_REBOOT_CMD_CAD_OFF = 0x0
@ -1809,6 +1813,7 @@ const (
LWTUNNEL_IP_OPT_GENEVE_MAX = 0x3
LWTUNNEL_IP_OPT_VXLAN_MAX = 0x1
MADV_COLD = 0x14
MADV_COLLAPSE = 0x19
MADV_DODUMP = 0x11
MADV_DOFORK = 0xb
MADV_DONTDUMP = 0x10
@ -2163,6 +2168,7 @@ const (
PACKET_FANOUT_DATA = 0x16
PACKET_FANOUT_EBPF = 0x7
PACKET_FANOUT_FLAG_DEFRAG = 0x8000
PACKET_FANOUT_FLAG_IGNORE_OUTGOING = 0x4000
PACKET_FANOUT_FLAG_ROLLOVER = 0x1000
PACKET_FANOUT_FLAG_UNIQUEID = 0x2000
PACKET_FANOUT_HASH = 0x0

View File

@ -15,12 +15,12 @@ type PtraceRegsArm struct {
// PtraceGetRegsArm fetches the registers used by arm binaries.
func PtraceGetRegsArm(pid int, regsout *PtraceRegsArm) error {
return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout)))
return ptracePtr(PTRACE_GETREGS, pid, 0, unsafe.Pointer(regsout))
}
// PtraceSetRegsArm sets the registers used by arm binaries.
func PtraceSetRegsArm(pid int, regs *PtraceRegsArm) error {
return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs)))
return ptracePtr(PTRACE_SETREGS, pid, 0, unsafe.Pointer(regs))
}
// PtraceRegsArm64 is the registers used by arm64 binaries.
@ -33,10 +33,10 @@ type PtraceRegsArm64 struct {
// PtraceGetRegsArm64 fetches the registers used by arm64 binaries.
func PtraceGetRegsArm64(pid int, regsout *PtraceRegsArm64) error {
return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout)))
return ptracePtr(PTRACE_GETREGS, pid, 0, unsafe.Pointer(regsout))
}
// PtraceSetRegsArm64 sets the registers used by arm64 binaries.
func PtraceSetRegsArm64(pid int, regs *PtraceRegsArm64) error {
return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs)))
return ptracePtr(PTRACE_SETREGS, pid, 0, unsafe.Pointer(regs))
}

View File

@ -7,11 +7,11 @@ import "unsafe"
// PtraceGetRegSetArm64 fetches the registers used by arm64 binaries.
func PtraceGetRegSetArm64(pid, addr int, regsout *PtraceRegsArm64) error {
iovec := Iovec{(*byte)(unsafe.Pointer(regsout)), uint64(unsafe.Sizeof(*regsout))}
return ptrace(PTRACE_GETREGSET, pid, uintptr(addr), uintptr(unsafe.Pointer(&iovec)))
return ptracePtr(PTRACE_GETREGSET, pid, uintptr(addr), unsafe.Pointer(&iovec))
}
// PtraceSetRegSetArm64 sets the registers used by arm64 binaries.
func PtraceSetRegSetArm64(pid, addr int, regs *PtraceRegsArm64) error {
iovec := Iovec{(*byte)(unsafe.Pointer(regs)), uint64(unsafe.Sizeof(*regs))}
return ptrace(PTRACE_SETREGSET, pid, uintptr(addr), uintptr(unsafe.Pointer(&iovec)))
return ptracePtr(PTRACE_SETREGSET, pid, uintptr(addr), unsafe.Pointer(&iovec))
}

View File

@ -21,12 +21,12 @@ type PtraceRegsMips struct {
// PtraceGetRegsMips fetches the registers used by mips binaries.
func PtraceGetRegsMips(pid int, regsout *PtraceRegsMips) error {
return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout)))
return ptracePtr(PTRACE_GETREGS, pid, 0, unsafe.Pointer(regsout))
}
// PtraceSetRegsMips sets the registers used by mips binaries.
func PtraceSetRegsMips(pid int, regs *PtraceRegsMips) error {
return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs)))
return ptracePtr(PTRACE_SETREGS, pid, 0, unsafe.Pointer(regs))
}
// PtraceRegsMips64 is the registers used by mips64 binaries.
@ -42,10 +42,10 @@ type PtraceRegsMips64 struct {
// PtraceGetRegsMips64 fetches the registers used by mips64 binaries.
func PtraceGetRegsMips64(pid int, regsout *PtraceRegsMips64) error {
return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout)))
return ptracePtr(PTRACE_GETREGS, pid, 0, unsafe.Pointer(regsout))
}
// PtraceSetRegsMips64 sets the registers used by mips64 binaries.
func PtraceSetRegsMips64(pid int, regs *PtraceRegsMips64) error {
return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs)))
return ptracePtr(PTRACE_SETREGS, pid, 0, unsafe.Pointer(regs))
}

View File

@ -21,12 +21,12 @@ type PtraceRegsMipsle struct {
// PtraceGetRegsMipsle fetches the registers used by mipsle binaries.
func PtraceGetRegsMipsle(pid int, regsout *PtraceRegsMipsle) error {
return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout)))
return ptracePtr(PTRACE_GETREGS, pid, 0, unsafe.Pointer(regsout))
}
// PtraceSetRegsMipsle sets the registers used by mipsle binaries.
func PtraceSetRegsMipsle(pid int, regs *PtraceRegsMipsle) error {
return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs)))
return ptracePtr(PTRACE_SETREGS, pid, 0, unsafe.Pointer(regs))
}
// PtraceRegsMips64le is the registers used by mips64le binaries.
@ -42,10 +42,10 @@ type PtraceRegsMips64le struct {
// PtraceGetRegsMips64le fetches the registers used by mips64le binaries.
func PtraceGetRegsMips64le(pid int, regsout *PtraceRegsMips64le) error {
return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout)))
return ptracePtr(PTRACE_GETREGS, pid, 0, unsafe.Pointer(regsout))
}
// PtraceSetRegsMips64le sets the registers used by mips64le binaries.
func PtraceSetRegsMips64le(pid int, regs *PtraceRegsMips64le) error {
return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs)))
return ptracePtr(PTRACE_SETREGS, pid, 0, unsafe.Pointer(regs))
}

Some files were not shown because too many files have changed in this diff Show More