mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
Merge pull request #13539 from ioito/hotfix/qx-cli-help
fix(climc): add --help for other cli
This commit is contained in:
+29
-41
@@ -32,7 +32,6 @@ import (
|
||||
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
Help bool `help:"Show help"`
|
||||
CloudEnv string `help:"Cloud environment" default:"$ALIYUN_CLOUD_ENV" choices:"InternationalCloud|FinanceCloud" metavar:"ALIYUN_CLOUD_ENV"`
|
||||
AccessKey string `help:"Access key" default:"$ALIYUN_ACCESS_KEY" metavar:"ALIYUN_ACCESS_KEY"`
|
||||
Secret string `help:"Secret" default:"$ALIYUN_SECRET" metavar:"ALIYUN_SECRET"`
|
||||
@@ -41,10 +40,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"aliyuncli",
|
||||
"Command-line interface to aliyun API.",
|
||||
`See "aliyuncli help COMMAND" for help on a specific command.`)
|
||||
`See "aliyuncli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -54,20 +53,9 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -132,33 +120,33 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *aliyun.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *aliyun.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
+15
-27
@@ -32,7 +32,6 @@ import (
|
||||
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
Help bool `help:"Show help"`
|
||||
AccessKey string `help:"Access key" default:"$APSARA_ACCESS_KEY" metavar:"APSARA_ACCESS_KEY"`
|
||||
Secret string `help:"Secret" default:"$APSARA_SECRET" metavar:"APSARA_SECRET"`
|
||||
cloudprovider.SApsaraEndpoints `help:"Endpoints for Apsara"`
|
||||
@@ -43,10 +42,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"apsaracli",
|
||||
"Command-line interface to apsara API.",
|
||||
`See "apsaracli help COMMAND" for help on a specific command.`)
|
||||
`See "apsaracli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -56,20 +55,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -137,31 +124,32 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *apsara.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *apsara.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
|
||||
+28
-41
@@ -32,7 +32,6 @@ import (
|
||||
)
|
||||
|
||||
type BaseOptions struct {
|
||||
Help bool `help:"Show help"`
|
||||
Debug bool `help:"debug mode"`
|
||||
AccessUrl string `help:"Access key" default:"$AWS_ACCESS_URL" choices:"ChinaCloud|InternationalCloud" metavar:"AWS_ACCESS_URL"`
|
||||
AccessKey string `help:"Access key" default:"$AWS_ACCESS_KEY" metavar:"AWS_ACCESS_KEY"`
|
||||
@@ -43,10 +42,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"awscli",
|
||||
"Command-line interface to aws API.",
|
||||
`See "awscli help COMMAND" for help on a specific command.`)
|
||||
`See "awscli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -56,20 +55,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -135,33 +122,33 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *aws.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *aws.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
+27
-41
@@ -32,7 +32,6 @@ import (
|
||||
)
|
||||
|
||||
type BaseOptions struct {
|
||||
Help bool `help:"Show help"`
|
||||
Debug bool `help:"debug mode"`
|
||||
DirectoryID string `help:"Azure account Directory ID/Tenant ID" default:"$AZURE_DIRECTORY_ID" metavar:"AZURE_DIRECTORY_ID"`
|
||||
SubscriptionID string `help:"Azure account subscription ID" default:"$AZURE_SUBSCRIPTION_ID" metavar:"AZURE_SUBSCRIPTION_ID"`
|
||||
@@ -44,10 +43,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"azurecli",
|
||||
"Command-line interface to azure API.",
|
||||
`See "azurecli help COMMAND" for help on a specific command.`)
|
||||
`See "azurecli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -57,20 +56,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -153,33 +140,32 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *azure.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *azure.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
+14
-27
@@ -32,7 +32,6 @@ import (
|
||||
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
Help bool `help:"Show help"`
|
||||
RegionId string
|
||||
Endpoint string `help:"Endpoint" default:"$BINGO_CLOUD_ENDPOINT" metavar:"BINGO_CLOUD_ENDPOINT"`
|
||||
AccessKey string `help:"Access Key" default:"$BINGO_CLOUD_ACCESS_KEY" metavar:"BINGO_CLOUD_ACCESS_KEY"`
|
||||
@@ -41,10 +40,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"bingocli",
|
||||
"Command-line interface to bingo cloud API.",
|
||||
`See "bingocli help COMMAND" for help on a specific command.`)
|
||||
`See "bingocli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -54,20 +53,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -131,13 +118,13 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
@@ -147,16 +134,16 @@ func main() {
|
||||
return
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *bingocloud.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *bingocloud.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
|
||||
+14
-26
@@ -32,7 +32,6 @@ import (
|
||||
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
Help bool `help:"Show help"`
|
||||
AuthURL string `help:"Auth URL" default:"$CLOUDPODS_AUTH_URL" metavar:"CLOUDPODS_AUTH_URL"`
|
||||
AccessKey string `help:"AccessKey" default:"$CLOUDPODS_ACCESS_KEY" metavar:"CLOUDPODS_ACCESS_KEY"`
|
||||
AccessSecret string `help:"AccessSecret" default:"$CLOUDPODS_ACCESS_SECRET" metavar:"CLOUDPODS_ACCESS_SECRET"`
|
||||
@@ -41,10 +40,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, err := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, err := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"cloudpodscli",
|
||||
"Command-line interface to cloudpods API.",
|
||||
`See "cloudpodscli help COMMAND" for help on a specific command.`)
|
||||
`See "cloudpodscli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -54,19 +53,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, err := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, err = subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, err = subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -133,13 +121,13 @@ func main() {
|
||||
err = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if err != nil {
|
||||
if err != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
@@ -149,16 +137,16 @@ func main() {
|
||||
return
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
err = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *cloudpods.SRegion
|
||||
region, err = newClient(options)
|
||||
if err != nil {
|
||||
showErrorAndExit(err)
|
||||
}
|
||||
err = subcmd.Invoke(region, suboptions)
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *cloudpods.SRegion
|
||||
region, err = newClient(options)
|
||||
if err != nil {
|
||||
showErrorAndExit(err)
|
||||
}
|
||||
err = subcmd.Invoke(region, suboptions)
|
||||
if err != nil {
|
||||
showErrorAndExit(err)
|
||||
}
|
||||
|
||||
+18
-33
@@ -26,15 +26,14 @@ import (
|
||||
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
Help bool `help:"Show help"`
|
||||
SUBCOMMAND string `help:"aliyuncli subcommand" subcommand:"true"`
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"cryptool",
|
||||
"Command-line cryptography tools.",
|
||||
`See "cryptool help COMMAND" for help on a specific command.`)
|
||||
`See "cryptool COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -44,20 +43,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -77,26 +64,24 @@ func main() {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
e = subcmd.Invoke(suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
e = subcmd.Invoke(suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
+27
-41
@@ -33,7 +33,6 @@ import (
|
||||
type BaseOptions struct {
|
||||
cloudprovider.SCtyunExtraOptions
|
||||
|
||||
Help bool `help:"Show help" default:"false"`
|
||||
Debug bool `help:"Show debug" default:"false"`
|
||||
AccessKey string `help:"Access key" default:"$CTYUN_ACCESS_KEY"`
|
||||
Secret string `help:"Secret" default:"$CTYUN_SECRET"`
|
||||
@@ -42,10 +41,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"ctyuncli",
|
||||
"Command-line interface to ctyun API.",
|
||||
`See "ctyuncli help COMMAND" for help on a specific command.`)
|
||||
`See "ctyuncli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -55,20 +54,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -131,33 +118,32 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *ctyun.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *ctyun.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
+27
-41
@@ -31,7 +31,6 @@ import (
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Help bool `help:"Show help" default:"false"`
|
||||
Debug bool `help:"Show debug" default:"false"`
|
||||
AccessKey string `help:"Access key" default:"$ECLOUD_ACCESS_KEY"`
|
||||
AccessSecret string `help:"Secret" default:"$ECLOUD_ACCESS_SECRET"`
|
||||
@@ -40,10 +39,10 @@ type Options struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&Options{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&Options{},
|
||||
"ecloudcli",
|
||||
"Command-line interface to ecloud API.",
|
||||
`See "ecloud help COMMAND" for help on a specific command.`)
|
||||
`See "ecloudcli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -53,20 +52,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -129,33 +116,32 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*Options)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *ecloud.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *ecloud.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
+28
-42
@@ -31,7 +31,6 @@ import (
|
||||
)
|
||||
|
||||
type BaseOptions struct {
|
||||
Help bool `help:"Show help"`
|
||||
Host string `help:"Host IP or NAME" default:"$VMWARE_HOST" metavar:"VMWARE_HOST"`
|
||||
Port int `help:"Service port" default:"$VMWARE_PORT" metavar:"VMWARE_PORT"`
|
||||
Account string `help:"VCenter or ESXi Account" default:"$VMWARE_ACCOUNT" metavar:"VMWARE_ACCOUNT"`
|
||||
@@ -40,10 +39,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
"govmcli",
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"esxicli",
|
||||
"Command-line interface to VMware VSphere Webservice API.",
|
||||
`See "govmcli help COMMAND" for help on a specific command.`)
|
||||
`See "esxicli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -53,20 +52,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -126,33 +113,32 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var esxicli *esxi.SESXiClient
|
||||
esxicli, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(esxicli, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var esxicli *esxi.SESXiClient
|
||||
esxicli, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(esxicli, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
+14
-27
@@ -36,7 +36,6 @@ import (
|
||||
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
Help bool `help:"Show help"`
|
||||
AuthFile string `help:"google cloud auth json file path" default:"$GOOGLE_AUTH_FILE" metavar:"GOOGLE_AUTH_FILE"`
|
||||
ClientEmail string `help:"Client email" default:"$GOOGLE_CLIENT_EMAIL" metavar:"GOOGLE_CLIENT_EMAIL"`
|
||||
ProjectID string `help:"Project ID" default:"$GOOGLE_PROJECT_ID" metavar:"GOOGLE_PROJECT_ID"`
|
||||
@@ -47,10 +46,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"googlecli",
|
||||
"Command-line interface to google API.",
|
||||
`See "googlecli help COMMAND" for help on a specific command.`)
|
||||
`See "googlecli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -60,20 +59,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -160,13 +147,13 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
@@ -176,16 +163,16 @@ func main() {
|
||||
return
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *google.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *google.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
|
||||
+28
-42
@@ -32,7 +32,6 @@ import (
|
||||
|
||||
type BaseOptions struct {
|
||||
cloudprovider.SHCSOEndpoints
|
||||
Help bool `help:"Show help" default:"false"`
|
||||
Debug bool `help:"Show debug" default:"false"`
|
||||
AccessKey string `help:"Access key" default:"$HUAWEI_ACCESS_KEY" metavar:"HUAWEI_ACCESS_KEY"`
|
||||
Secret string `help:"Secret" default:"$HUAWEI_SECRET" metavar:"HUAWEI_SECRET"`
|
||||
@@ -43,10 +42,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
"huaweicli",
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"hcsocli",
|
||||
"Command-line interface to huawei API.",
|
||||
`See "huaweicli help COMMAND" for help on a specific command.`)
|
||||
`See "hcsocli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -56,20 +55,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -136,33 +123,32 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *huawei.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *huawei.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
+27
-41
@@ -31,7 +31,6 @@ import (
|
||||
)
|
||||
|
||||
type BaseOptions struct {
|
||||
Help bool `help:"Show help" default:"false"`
|
||||
Debug bool `help:"Show debug" default:"false"`
|
||||
CloudEnv string `help:"Cloud environment" default:"$HUAWEI_CLOUD_ENV" choices:"ChinaCloud|InternationalCloud" metavar:"HUAWEI_CLOUD_ENV"`
|
||||
AccessKey string `help:"Access key" default:"$HUAWEI_ACCESS_KEY" metavar:"HUAWEI_ACCESS_KEY"`
|
||||
@@ -42,10 +41,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"huaweicli",
|
||||
"Command-line interface to huawei API.",
|
||||
`See "huaweicli help COMMAND" for help on a specific command.`)
|
||||
`See "huaweicli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -55,20 +54,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -134,33 +121,32 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *huawei.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *huawei.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
+13
-26
@@ -27,7 +27,6 @@ import (
|
||||
)
|
||||
|
||||
type BaseOptions struct {
|
||||
Help bool `help:"Show help" short-token:"h"`
|
||||
MODE string `help:"Execute command mode" choices:"ssh|rmcp"`
|
||||
HOST string `help:"IP address of remote host"`
|
||||
PASSWD string `help:"Password"`
|
||||
@@ -43,11 +42,11 @@ func showErrorAndExit(e error) {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parser, err := structarg.NewArgumentParser(
|
||||
parser, err := structarg.NewArgumentParserWithHelp(
|
||||
&BaseOptions{},
|
||||
"ipmicli",
|
||||
"Command-line interface to ipmitool",
|
||||
`See "ipmicli help COMMAND" for help on a specific command.`,
|
||||
`See "ipmicli COMMAND --help" for help on a specific command.`,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -56,20 +55,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -108,14 +95,14 @@ func main() {
|
||||
err = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
return
|
||||
}
|
||||
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if err != nil {
|
||||
if err != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
@@ -127,15 +114,15 @@ func main() {
|
||||
|
||||
suboptions := subparser.Options()
|
||||
var args []interface{}
|
||||
if options.SUBCOMMAND == "help" {
|
||||
args = append(args, suboptions)
|
||||
} else {
|
||||
executor, err := newExecutor(options)
|
||||
if err != nil {
|
||||
showErrorAndExit(err)
|
||||
}
|
||||
args = append(args, executor, suboptions)
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
executor, err := newExecutor(options)
|
||||
if err != nil {
|
||||
showErrorAndExit(err)
|
||||
}
|
||||
args = append(args, executor, suboptions)
|
||||
err = subcmd.Invoke(args...)
|
||||
if err != nil {
|
||||
showErrorAndExit(err)
|
||||
|
||||
+31
-45
@@ -33,7 +33,6 @@ import (
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Help bool `help:"Show help" default:"false"`
|
||||
Debug bool `help:"Show debug" default:"false"`
|
||||
AccessKey string `help:"Access key" default:"$JDCLOUD_ACCESS_KEY"`
|
||||
AccessSecret string `help:"Secret" default:"$JDCLOUD_ACCESS_SECRET"`
|
||||
@@ -42,10 +41,10 @@ type Options struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, err := structarg.NewArgumentParser(&Options{},
|
||||
parse, err := structarg.NewArgumentParserWithHelp(&Options{},
|
||||
"jdcloudcli",
|
||||
"Command-line interface to ecloud API.",
|
||||
`See "ecloud help COMMAND" for help on a specific command.`,
|
||||
`See "jdcloudcli COMMAND --help" for help on a specific command.`,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -55,20 +54,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, err := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -123,38 +110,37 @@ func main() {
|
||||
err = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*Options)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
return
|
||||
}
|
||||
if options.Debug {
|
||||
log.SetLogLevel(log.Logger(), logrus.DebugLevel)
|
||||
} else {
|
||||
if options.Debug {
|
||||
log.SetLogLevel(log.Logger(), logrus.DebugLevel)
|
||||
log.SetLogLevel(log.Logger(), logrus.InfoLevel)
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if err != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
log.SetLogLevel(log.Logger(), logrus.InfoLevel)
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if err != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(err)
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
err = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *jdcloud.SRegion
|
||||
region, err = newClient(options)
|
||||
if err != nil {
|
||||
showErrorAndExit(err)
|
||||
}
|
||||
err = subcmd.Invoke(region, suboptions)
|
||||
}
|
||||
if err != nil {
|
||||
showErrorAndExit(err)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(err)
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *jdcloud.SRegion
|
||||
region, err = newClient(options)
|
||||
if err != nil {
|
||||
showErrorAndExit(err)
|
||||
}
|
||||
err = subcmd.Invoke(region, suboptions)
|
||||
if err != nil {
|
||||
showErrorAndExit(err)
|
||||
}
|
||||
}
|
||||
|
||||
+28
-42
@@ -27,8 +27,7 @@ import (
|
||||
)
|
||||
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
Help bool `help:"Show help"`
|
||||
Debug bool `help:"Show debug" default:"false"`
|
||||
Url string `help:"ldap url, like ldap://10.168.222.23:389 or ldaps://10.168.222.23:389" default:"$LDAP_URL"`
|
||||
Account string `help:"LDAP Account" default:"$LDAP_ACCOUNT"`
|
||||
Password string `help:"LDAP password" default:"$LDAP_PASSWORD"`
|
||||
@@ -38,10 +37,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"ldapcli",
|
||||
"Command-line tool for LDAP API.",
|
||||
`See "ldapcli help COMMAND" for help on a specific command.`)
|
||||
`See "ldapcli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -51,20 +50,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -104,33 +91,32 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
var cli *ldaputils.SLDAPClient
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
cli, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(cli, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
var cli *ldaputils.SLDAPClient
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
cli, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(cli, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
+14
-27
@@ -32,7 +32,6 @@ import (
|
||||
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
Help bool `help:"Show help"`
|
||||
Host string `help:"Host" default:"$NUTANIX_HOST" metavar:"NUTANIX_HOST"`
|
||||
Username string `help:"Username" default:"$NUTANIX_USERNAME" metavar:"NUTANIX_USERNAME"`
|
||||
Password string `help:"Password" default:"$NUTANIX_PASSWORD" metavar:"NUTANIX_PASSWORD"`
|
||||
@@ -41,10 +40,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"ncli",
|
||||
"Command-line interface to nutanix API.",
|
||||
`See "ncli help COMMAND" for help on a specific command.`)
|
||||
`See "ncli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -54,20 +53,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -132,13 +119,13 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
@@ -148,16 +135,16 @@ func main() {
|
||||
return
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *nutanix.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *nutanix.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
|
||||
+30
-44
@@ -32,7 +32,6 @@ import (
|
||||
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
Help bool `help:"Show help"`
|
||||
AuthURL string `help:"Auth URL" default:"$OPENSTACK_AUTH_URL" metavar:"OPENSTACK_AUTH_URL"`
|
||||
Username string `help:"Username" default:"$OPENSTACK_USERNAME" metavar:"OPENSTACK_USERNAME"`
|
||||
Password string `help:"Password" default:"$OPENSTACK_PASSWORD" metavar:"OPENSTACK_PASSWORD"`
|
||||
@@ -45,10 +44,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"openstackcli",
|
||||
"Command-line interface to openstack API.",
|
||||
`See "openstackcli help COMMAND" for help on a specific command.`)
|
||||
`See "openstackcli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -58,20 +57,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -143,36 +130,35 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *openstack.SRegion
|
||||
if len(options.RegionID) == 0 {
|
||||
options.RegionID = openstack.OPENSTACK_DEFAULT_REGION
|
||||
}
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *openstack.SRegion
|
||||
if len(options.RegionID) == 0 {
|
||||
options.RegionID = openstack.OPENSTACK_DEFAULT_REGION
|
||||
}
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
+30
-44
@@ -32,7 +32,6 @@ import (
|
||||
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
Help bool `help:"Show help"`
|
||||
AppID string `help:"AppID" default:"$QCLOUD_APPID" metavar:"QCLOUD_APPID"`
|
||||
SecretID string `help:"Secret" default:"$QCLOUD_SECRET_ID" metavar:"QCLOUD_SECRET_ID"`
|
||||
SecretKey string `help:"Access key" default:"$QCLOUD_SECRET_KEY" metavar:"QCLOUD_SECRET_KEY"`
|
||||
@@ -41,10 +40,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"qcloudcli",
|
||||
"Command-line interface to tencentcloud API.",
|
||||
`See "tencentcli help COMMAND" for help on a specific command.`)
|
||||
`See "qcloudcli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -54,20 +53,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -128,36 +115,35 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *qcloud.SRegion
|
||||
if len(options.RegionId) == 0 {
|
||||
options.RegionId = qcloud.QCLOUD_DEFAULT_REGION
|
||||
}
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *qcloud.SRegion
|
||||
if len(options.RegionId) == 0 {
|
||||
options.RegionId = qcloud.QCLOUD_DEFAULT_REGION
|
||||
}
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
+27
-41
@@ -29,7 +29,6 @@ import (
|
||||
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
Help bool `help:"Show help"`
|
||||
Host string `help:"SSH Host IP" default:"$RAID_HOST" metavar:"RAID_HOST"`
|
||||
Username string `help:"Username, usually root" default:"$RAID_USERNAME" metavar:"RAID_USERNAME"`
|
||||
Password string `help:"Password" default:"$RAID_PASSWORD" metavar:"RAID_PASSWORD"`
|
||||
@@ -43,10 +42,10 @@ var (
|
||||
)
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(options,
|
||||
parse, e := structarg.NewArgumentParserWithHelp(options,
|
||||
"raidcli",
|
||||
"Command-line interface to test RAID drivers.",
|
||||
`See "raidcli help COMMAND" for help on a specific command.`)
|
||||
`See "raidcli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -56,20 +55,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -141,33 +128,32 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
// options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var client raid.IRaidDriver
|
||||
client, e = newClient()
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(client, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var client raid.IRaidDriver
|
||||
client, e = newClient()
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(client, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
+30
-43
@@ -33,7 +33,6 @@ import (
|
||||
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
Help bool `help:"Show help"`
|
||||
Endpoint string `help:"Endpoint, usually https://<host_ipmi_ip>" default:"$REDFISH_ENDPOINT" metavar:"REDFISH_ENDPOINT"`
|
||||
Username string `help:"Username, usually root" default:"$REDFISH_USERNAME" metavar:"REDFISH_USERNAME"`
|
||||
Password string `help:"Password" default:"$REDFISH_PASSWORD" metavar:"REDFISH_PASSWORD"`
|
||||
@@ -45,10 +44,10 @@ var (
|
||||
)
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(options,
|
||||
parse, e := structarg.NewArgumentParserWithHelp(options,
|
||||
"redfishcli",
|
||||
"Command-line interface to redfish API.",
|
||||
`See "redfishcli help COMMAND" for help on a specific command.`)
|
||||
`See "redfishcli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -58,21 +57,9 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
bmcJnlp()
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -159,35 +146,35 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
// options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else if options.SUBCOMMAND == "bmc-jnlp" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var client redfish.IRedfishDriver
|
||||
client, e = newClient()
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(client, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
} else if options.SUBCOMMAND == "bmc-jnlp" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var client redfish.IRedfishDriver
|
||||
client, e = newClient()
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(client, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
+27
-41
@@ -31,7 +31,6 @@ import (
|
||||
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
Help bool `help:"Show help"`
|
||||
AccessUrl string `help:"Access url" default:"$S3_ACCESS_URL" metavar:"S3_ACCESS_URL"`
|
||||
AccessKey string `help:"Access key" default:"$S3_ACCESS_KEY" metavar:"S3_ACCESS_KEY"`
|
||||
Secret string `help:"Secret" default:"$S3_SECRET" metavar:"S3_SECRET"`
|
||||
@@ -40,10 +39,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"s3cli",
|
||||
"Command-line interface to standard S3 API.",
|
||||
`See "s3cli help COMMAND" for help on a specific command.`)
|
||||
`See "s3cli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -53,20 +52,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -121,33 +108,32 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var client cloudprovider.ICloudRegion
|
||||
client, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(client, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var client cloudprovider.ICloudRegion
|
||||
client, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(client, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
+27
-41
@@ -31,7 +31,6 @@ import (
|
||||
)
|
||||
|
||||
type BaseOptions struct {
|
||||
Help bool `help:"Show help" default:"false"`
|
||||
Debug bool `help:"Show debug" default:"false"`
|
||||
|
||||
AccessKey string `help:"Access key" default:"$UCLOUD_ACCESS_KEY" metavar:"UCLOUD_ACCESS_KEY"`
|
||||
@@ -42,10 +41,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"ucloudcli",
|
||||
"Command-line interface to ucloud API.",
|
||||
`See "ucloudcli help COMMAND" for help on a specific command.`)
|
||||
`See "ucloudcli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -55,20 +54,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -132,33 +119,32 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *ucloud.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *ucloud.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
+30
-44
@@ -32,7 +32,6 @@ import (
|
||||
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
Help bool `help:"Show help"`
|
||||
AuthURL string `help:"Auth URL" default:"$ZSTACK_AUTH_URL" metavar:"ZSTACK_AUTH_URL"`
|
||||
Username string `help:"Username" default:"$ZSTACK_USERNAME" metavar:"ZSTACK_USERNAME"`
|
||||
Password string `help:"Password" default:"$ZSTACK_PASSWORD" metavar:"ZSTACK_PASSWORD"`
|
||||
@@ -41,10 +40,10 @@ type BaseOptions struct {
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"zstackcli",
|
||||
"Command-line interface to zstack API.",
|
||||
`See "zstackcli help COMMAND" for help on a specific command.`)
|
||||
`See "zstackcli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -54,20 +53,8 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -134,36 +121,35 @@ func main() {
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *zstack.SRegion
|
||||
if len(options.RegionID) == 0 {
|
||||
options.RegionID = zstack.ZSTACK_DEFAULT_REGION
|
||||
}
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *zstack.SRegion
|
||||
if len(options.RegionID) == 0 {
|
||||
options.RegionID = zstack.ZSTACK_DEFAULT_REGION
|
||||
}
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,8 +109,7 @@ require (
|
||||
github.com/sergi/go-diff v1.2.0
|
||||
github.com/serialx/hashring v0.0.0-20180504054112-49a4782e9908
|
||||
github.com/sevlyar/go-daemon v0.1.5
|
||||
github.com/shirou/gopsutil v2.18.10+incompatible
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect
|
||||
github.com/shirou/gopsutil v2.21.11+incompatible
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9
|
||||
github.com/smartystreets/goconvey v1.6.4
|
||||
@@ -120,12 +119,14 @@ require (
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.135+incompatible
|
||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.24
|
||||
github.com/tinylib/msgp v1.1.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.9 // indirect
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
|
||||
github.com/tredoe/osutil v0.0.0-20161130133508-7d3ee1afa71c
|
||||
github.com/vishvananda/netlink v1.0.0
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible
|
||||
github.com/vmware/govmomi v0.20.1
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5
|
||||
go.uber.org/atomic v1.4.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
|
||||
|
||||
@@ -255,8 +255,9 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY=
|
||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||
@@ -578,10 +579,8 @@ github.com/serialx/hashring v0.0.0-20180504054112-49a4782e9908 h1:RRpyb4kheanCQV
|
||||
github.com/serialx/hashring v0.0.0-20180504054112-49a4782e9908/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc=
|
||||
github.com/sevlyar/go-daemon v0.1.5 h1:Zy/6jLbM8CfqJ4x4RPr7MJlSKt90f00kNM1D401C+Qk=
|
||||
github.com/sevlyar/go-daemon v0.1.5/go.mod h1:6dJpPatBT9eUwM5VCw9Bt6CdX9Tk6UWvhW3MebLDRKE=
|
||||
github.com/shirou/gopsutil v2.18.10+incompatible h1:cy84jW6EVRPa5g9HAHrlbxMSIjBhDSX0OFYyMYminYs=
|
||||
github.com/shirou/gopsutil v2.18.10+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/shirou/gopsutil v2.21.11+incompatible h1:lOGOyCG67a5dv2hq5Z1BLDUqqKp3HkbjPcz5j6XMS0U=
|
||||
github.com/shirou/gopsutil v2.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
@@ -628,6 +627,10 @@ github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e/go.
|
||||
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU=
|
||||
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo=
|
||||
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
|
||||
github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
|
||||
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
@@ -655,6 +658,8 @@ github.com/willf/bloom v2.0.3+incompatible/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0s
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5 h1:Gqga3zA9tdAcfqobUGjSoCob5L3f8Dt5EuOp3ihNZko=
|
||||
@@ -776,6 +781,7 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190830023255-19e00faab6ad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -793,6 +799,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 h1:xrCZDmdtoloIiooiA9q0OQb9r8HejIHYoHGhGCe1pGg=
|
||||
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
||||
+25
-25
@@ -9,32 +9,32 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
procCoInitialize, _ = modole32.FindProc("CoInitialize")
|
||||
procCoInitializeEx, _ = modole32.FindProc("CoInitializeEx")
|
||||
procCoUninitialize, _ = modole32.FindProc("CoUninitialize")
|
||||
procCoCreateInstance, _ = modole32.FindProc("CoCreateInstance")
|
||||
procCoTaskMemFree, _ = modole32.FindProc("CoTaskMemFree")
|
||||
procCLSIDFromProgID, _ = modole32.FindProc("CLSIDFromProgID")
|
||||
procCLSIDFromString, _ = modole32.FindProc("CLSIDFromString")
|
||||
procStringFromCLSID, _ = modole32.FindProc("StringFromCLSID")
|
||||
procStringFromIID, _ = modole32.FindProc("StringFromIID")
|
||||
procIIDFromString, _ = modole32.FindProc("IIDFromString")
|
||||
procCoGetObject, _ = modole32.FindProc("CoGetObject")
|
||||
procGetUserDefaultLCID, _ = modkernel32.FindProc("GetUserDefaultLCID")
|
||||
procCopyMemory, _ = modkernel32.FindProc("RtlMoveMemory")
|
||||
procVariantInit, _ = modoleaut32.FindProc("VariantInit")
|
||||
procVariantClear, _ = modoleaut32.FindProc("VariantClear")
|
||||
procVariantTimeToSystemTime, _ = modoleaut32.FindProc("VariantTimeToSystemTime")
|
||||
procSysAllocString, _ = modoleaut32.FindProc("SysAllocString")
|
||||
procSysAllocStringLen, _ = modoleaut32.FindProc("SysAllocStringLen")
|
||||
procSysFreeString, _ = modoleaut32.FindProc("SysFreeString")
|
||||
procSysStringLen, _ = modoleaut32.FindProc("SysStringLen")
|
||||
procCreateDispTypeInfo, _ = modoleaut32.FindProc("CreateDispTypeInfo")
|
||||
procCreateStdDispatch, _ = modoleaut32.FindProc("CreateStdDispatch")
|
||||
procGetActiveObject, _ = modoleaut32.FindProc("GetActiveObject")
|
||||
procCoInitialize = modole32.NewProc("CoInitialize")
|
||||
procCoInitializeEx = modole32.NewProc("CoInitializeEx")
|
||||
procCoUninitialize = modole32.NewProc("CoUninitialize")
|
||||
procCoCreateInstance = modole32.NewProc("CoCreateInstance")
|
||||
procCoTaskMemFree = modole32.NewProc("CoTaskMemFree")
|
||||
procCLSIDFromProgID = modole32.NewProc("CLSIDFromProgID")
|
||||
procCLSIDFromString = modole32.NewProc("CLSIDFromString")
|
||||
procStringFromCLSID = modole32.NewProc("StringFromCLSID")
|
||||
procStringFromIID = modole32.NewProc("StringFromIID")
|
||||
procIIDFromString = modole32.NewProc("IIDFromString")
|
||||
procCoGetObject = modole32.NewProc("CoGetObject")
|
||||
procGetUserDefaultLCID = modkernel32.NewProc("GetUserDefaultLCID")
|
||||
procCopyMemory = modkernel32.NewProc("RtlMoveMemory")
|
||||
procVariantInit = modoleaut32.NewProc("VariantInit")
|
||||
procVariantClear = modoleaut32.NewProc("VariantClear")
|
||||
procVariantTimeToSystemTime = modoleaut32.NewProc("VariantTimeToSystemTime")
|
||||
procSysAllocString = modoleaut32.NewProc("SysAllocString")
|
||||
procSysAllocStringLen = modoleaut32.NewProc("SysAllocStringLen")
|
||||
procSysFreeString = modoleaut32.NewProc("SysFreeString")
|
||||
procSysStringLen = modoleaut32.NewProc("SysStringLen")
|
||||
procCreateDispTypeInfo = modoleaut32.NewProc("CreateDispTypeInfo")
|
||||
procCreateStdDispatch = modoleaut32.NewProc("CreateStdDispatch")
|
||||
procGetActiveObject = modoleaut32.NewProc("GetActiveObject")
|
||||
|
||||
procGetMessageW, _ = moduser32.FindProc("GetMessageW")
|
||||
procDispatchMessageW, _ = moduser32.FindProc("DispatchMessageW")
|
||||
procGetMessageW = moduser32.NewProc("GetMessageW")
|
||||
procDispatchMessageW = moduser32.NewProc("DispatchMessageW")
|
||||
)
|
||||
|
||||
// coInitialize initializes COM library on current thread.
|
||||
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
module github.com/go-ole/go-ole
|
||||
|
||||
go 1.12
|
||||
|
||||
require golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
+3
-1
@@ -185,7 +185,9 @@ func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}
|
||||
uintptr(unsafe.Pointer(&excepInfo)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewErrorWithSubError(hr, BstrToString(excepInfo.bstrDescription), excepInfo)
|
||||
excepInfo.renderStrings()
|
||||
excepInfo.Clear()
|
||||
err = NewErrorWithSubError(hr, excepInfo.description, excepInfo)
|
||||
}
|
||||
for i, varg := range vargs {
|
||||
n := len(params) - i - 1
|
||||
|
||||
+59
-26
@@ -3,6 +3,7 @@ package ole
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// DISPPARAMS are the arguments that passed to methods or property.
|
||||
@@ -24,6 +25,56 @@ type EXCEPINFO struct {
|
||||
pvReserved uintptr
|
||||
pfnDeferredFillIn uintptr
|
||||
scode uint32
|
||||
|
||||
// Go-specific part. Don't move upper cos it'll break structure layout for native code.
|
||||
rendered bool
|
||||
source string
|
||||
description string
|
||||
helpFile string
|
||||
}
|
||||
|
||||
// renderStrings translates BSTR strings to Go ones so `.Error` and `.String`
|
||||
// could be safely called after `.Clear`. We need this when we can't rely on
|
||||
// a caller to call `.Clear`.
|
||||
func (e *EXCEPINFO) renderStrings() {
|
||||
e.rendered = true
|
||||
if e.bstrSource == nil {
|
||||
e.source = "<nil>"
|
||||
} else {
|
||||
e.source = BstrToString(e.bstrSource)
|
||||
}
|
||||
if e.bstrDescription == nil {
|
||||
e.description = "<nil>"
|
||||
} else {
|
||||
e.description = BstrToString(e.bstrDescription)
|
||||
}
|
||||
if e.bstrHelpFile == nil {
|
||||
e.helpFile = "<nil>"
|
||||
} else {
|
||||
e.helpFile = BstrToString(e.bstrHelpFile)
|
||||
}
|
||||
}
|
||||
|
||||
// Clear frees BSTR strings inside an EXCEPINFO and set it to NULL.
|
||||
func (e *EXCEPINFO) Clear() {
|
||||
freeBSTR := func(s *uint16) {
|
||||
// SysFreeString don't return errors and is safe for call's on NULL.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-sysfreestring
|
||||
_ = SysFreeString((*int16)(unsafe.Pointer(s)))
|
||||
}
|
||||
|
||||
if e.bstrSource != nil {
|
||||
freeBSTR(e.bstrSource)
|
||||
e.bstrSource = nil
|
||||
}
|
||||
if e.bstrDescription != nil {
|
||||
freeBSTR(e.bstrDescription)
|
||||
e.bstrDescription = nil
|
||||
}
|
||||
if e.bstrHelpFile != nil {
|
||||
freeBSTR(e.bstrHelpFile)
|
||||
e.bstrHelpFile = nil
|
||||
}
|
||||
}
|
||||
|
||||
// WCode return wCode in EXCEPINFO.
|
||||
@@ -38,48 +89,30 @@ func (e EXCEPINFO) SCODE() uint32 {
|
||||
|
||||
// String convert EXCEPINFO to string.
|
||||
func (e EXCEPINFO) String() string {
|
||||
var src, desc, hlp string
|
||||
if e.bstrSource == nil {
|
||||
src = "<nil>"
|
||||
} else {
|
||||
src = BstrToString(e.bstrSource)
|
||||
if !e.rendered {
|
||||
e.renderStrings()
|
||||
}
|
||||
|
||||
if e.bstrDescription == nil {
|
||||
desc = "<nil>"
|
||||
} else {
|
||||
desc = BstrToString(e.bstrDescription)
|
||||
}
|
||||
|
||||
if e.bstrHelpFile == nil {
|
||||
hlp = "<nil>"
|
||||
} else {
|
||||
hlp = BstrToString(e.bstrHelpFile)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"wCode: %#x, bstrSource: %v, bstrDescription: %v, bstrHelpFile: %v, dwHelpContext: %#x, scode: %#x",
|
||||
e.wCode, src, desc, hlp, e.dwHelpContext, e.scode,
|
||||
e.wCode, e.source, e.description, e.helpFile, e.dwHelpContext, e.scode,
|
||||
)
|
||||
}
|
||||
|
||||
// Error implements error interface and returns error string.
|
||||
func (e EXCEPINFO) Error() string {
|
||||
if e.bstrDescription != nil {
|
||||
return strings.TrimSpace(BstrToString(e.bstrDescription))
|
||||
if !e.rendered {
|
||||
e.renderStrings()
|
||||
}
|
||||
|
||||
src := "Unknown"
|
||||
if e.bstrSource != nil {
|
||||
src = BstrToString(e.bstrSource)
|
||||
if e.description != "<nil>" {
|
||||
return strings.TrimSpace(e.description)
|
||||
}
|
||||
|
||||
code := e.scode
|
||||
if e.wCode != 0 {
|
||||
code = uint32(e.wCode)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v: %#x", src, code)
|
||||
return fmt.Sprintf("%v: %#x", e.source, code)
|
||||
}
|
||||
|
||||
// PARAMDATA defines parameter data type.
|
||||
|
||||
+29
-29
@@ -7,35 +7,35 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
procSafeArrayAccessData, _ = modoleaut32.FindProc("SafeArrayAccessData")
|
||||
procSafeArrayAllocData, _ = modoleaut32.FindProc("SafeArrayAllocData")
|
||||
procSafeArrayAllocDescriptor, _ = modoleaut32.FindProc("SafeArrayAllocDescriptor")
|
||||
procSafeArrayAllocDescriptorEx, _ = modoleaut32.FindProc("SafeArrayAllocDescriptorEx")
|
||||
procSafeArrayCopy, _ = modoleaut32.FindProc("SafeArrayCopy")
|
||||
procSafeArrayCopyData, _ = modoleaut32.FindProc("SafeArrayCopyData")
|
||||
procSafeArrayCreate, _ = modoleaut32.FindProc("SafeArrayCreate")
|
||||
procSafeArrayCreateEx, _ = modoleaut32.FindProc("SafeArrayCreateEx")
|
||||
procSafeArrayCreateVector, _ = modoleaut32.FindProc("SafeArrayCreateVector")
|
||||
procSafeArrayCreateVectorEx, _ = modoleaut32.FindProc("SafeArrayCreateVectorEx")
|
||||
procSafeArrayDestroy, _ = modoleaut32.FindProc("SafeArrayDestroy")
|
||||
procSafeArrayDestroyData, _ = modoleaut32.FindProc("SafeArrayDestroyData")
|
||||
procSafeArrayDestroyDescriptor, _ = modoleaut32.FindProc("SafeArrayDestroyDescriptor")
|
||||
procSafeArrayGetDim, _ = modoleaut32.FindProc("SafeArrayGetDim")
|
||||
procSafeArrayGetElement, _ = modoleaut32.FindProc("SafeArrayGetElement")
|
||||
procSafeArrayGetElemsize, _ = modoleaut32.FindProc("SafeArrayGetElemsize")
|
||||
procSafeArrayGetIID, _ = modoleaut32.FindProc("SafeArrayGetIID")
|
||||
procSafeArrayGetLBound, _ = modoleaut32.FindProc("SafeArrayGetLBound")
|
||||
procSafeArrayGetUBound, _ = modoleaut32.FindProc("SafeArrayGetUBound")
|
||||
procSafeArrayGetVartype, _ = modoleaut32.FindProc("SafeArrayGetVartype")
|
||||
procSafeArrayLock, _ = modoleaut32.FindProc("SafeArrayLock")
|
||||
procSafeArrayPtrOfIndex, _ = modoleaut32.FindProc("SafeArrayPtrOfIndex")
|
||||
procSafeArrayUnaccessData, _ = modoleaut32.FindProc("SafeArrayUnaccessData")
|
||||
procSafeArrayUnlock, _ = modoleaut32.FindProc("SafeArrayUnlock")
|
||||
procSafeArrayPutElement, _ = modoleaut32.FindProc("SafeArrayPutElement")
|
||||
//procSafeArrayRedim, _ = modoleaut32.FindProc("SafeArrayRedim") // TODO
|
||||
//procSafeArraySetIID, _ = modoleaut32.FindProc("SafeArraySetIID") // TODO
|
||||
procSafeArrayGetRecordInfo, _ = modoleaut32.FindProc("SafeArrayGetRecordInfo")
|
||||
procSafeArraySetRecordInfo, _ = modoleaut32.FindProc("SafeArraySetRecordInfo")
|
||||
procSafeArrayAccessData = modoleaut32.NewProc("SafeArrayAccessData")
|
||||
procSafeArrayAllocData = modoleaut32.NewProc("SafeArrayAllocData")
|
||||
procSafeArrayAllocDescriptor = modoleaut32.NewProc("SafeArrayAllocDescriptor")
|
||||
procSafeArrayAllocDescriptorEx = modoleaut32.NewProc("SafeArrayAllocDescriptorEx")
|
||||
procSafeArrayCopy = modoleaut32.NewProc("SafeArrayCopy")
|
||||
procSafeArrayCopyData = modoleaut32.NewProc("SafeArrayCopyData")
|
||||
procSafeArrayCreate = modoleaut32.NewProc("SafeArrayCreate")
|
||||
procSafeArrayCreateEx = modoleaut32.NewProc("SafeArrayCreateEx")
|
||||
procSafeArrayCreateVector = modoleaut32.NewProc("SafeArrayCreateVector")
|
||||
procSafeArrayCreateVectorEx = modoleaut32.NewProc("SafeArrayCreateVectorEx")
|
||||
procSafeArrayDestroy = modoleaut32.NewProc("SafeArrayDestroy")
|
||||
procSafeArrayDestroyData = modoleaut32.NewProc("SafeArrayDestroyData")
|
||||
procSafeArrayDestroyDescriptor = modoleaut32.NewProc("SafeArrayDestroyDescriptor")
|
||||
procSafeArrayGetDim = modoleaut32.NewProc("SafeArrayGetDim")
|
||||
procSafeArrayGetElement = modoleaut32.NewProc("SafeArrayGetElement")
|
||||
procSafeArrayGetElemsize = modoleaut32.NewProc("SafeArrayGetElemsize")
|
||||
procSafeArrayGetIID = modoleaut32.NewProc("SafeArrayGetIID")
|
||||
procSafeArrayGetLBound = modoleaut32.NewProc("SafeArrayGetLBound")
|
||||
procSafeArrayGetUBound = modoleaut32.NewProc("SafeArrayGetUBound")
|
||||
procSafeArrayGetVartype = modoleaut32.NewProc("SafeArrayGetVartype")
|
||||
procSafeArrayLock = modoleaut32.NewProc("SafeArrayLock")
|
||||
procSafeArrayPtrOfIndex = modoleaut32.NewProc("SafeArrayPtrOfIndex")
|
||||
procSafeArrayUnaccessData = modoleaut32.NewProc("SafeArrayUnaccessData")
|
||||
procSafeArrayUnlock = modoleaut32.NewProc("SafeArrayUnlock")
|
||||
procSafeArrayPutElement = modoleaut32.NewProc("SafeArrayPutElement")
|
||||
//procSafeArrayRedim = modoleaut32.NewProc("SafeArrayRedim") // TODO
|
||||
//procSafeArraySetIID = modoleaut32.NewProc("SafeArraySetIID") // TODO
|
||||
procSafeArrayGetRecordInfo = modoleaut32.NewProc("SafeArrayGetRecordInfo")
|
||||
procSafeArraySetRecordInfo = modoleaut32.NewProc("SafeArraySetRecordInfo")
|
||||
)
|
||||
|
||||
// safeArrayAccessData returns raw array pointer.
|
||||
|
||||
+2
-2
@@ -84,13 +84,13 @@ func (sac *SafeArrayConversion) ToValueArray() (values []interface{}) {
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_BSTR:
|
||||
var v string
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
v , _ := safeArrayGetElementString(sac.Array, i)
|
||||
values[i] = v
|
||||
case VT_VARIANT:
|
||||
var v VARIANT
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v.Value()
|
||||
v.Clear()
|
||||
default:
|
||||
// TODO
|
||||
}
|
||||
|
||||
+6
-7
@@ -3,14 +3,13 @@
|
||||
package ole
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
modcombase = syscall.NewLazyDLL("combase.dll")
|
||||
modkernel32, _ = syscall.LoadDLL("kernel32.dll")
|
||||
modole32, _ = syscall.LoadDLL("ole32.dll")
|
||||
modoleaut32, _ = syscall.LoadDLL("oleaut32.dll")
|
||||
modmsvcrt, _ = syscall.LoadDLL("msvcrt.dll")
|
||||
moduser32, _ = syscall.LoadDLL("user32.dll")
|
||||
modcombase = windows.NewLazySystemDLL("combase.dll")
|
||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
modole32 = windows.NewLazySystemDLL("ole32.dll")
|
||||
modoleaut32 = windows.NewLazySystemDLL("oleaut32.dll")
|
||||
moduser32 = windows.NewLazySystemDLL("user32.dll")
|
||||
)
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// +build arm
|
||||
|
||||
package ole
|
||||
|
||||
type VARIANT struct {
|
||||
VT VT // 2
|
||||
wReserved1 uint16 // 4
|
||||
wReserved2 uint16 // 6
|
||||
wReserved3 uint16 // 8
|
||||
Val int64 // 16
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
//go:build arm64
|
||||
// +build arm64
|
||||
|
||||
package ole
|
||||
|
||||
type VARIANT struct {
|
||||
VT VT // 2
|
||||
wReserved1 uint16 // 4
|
||||
wReserved2 uint16 // 6
|
||||
wReserved3 uint16 // 8
|
||||
Val int64 // 16
|
||||
_ [8]byte // 24
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// +build windows,arm
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// GetVariantDate converts COM Variant Time value to Go time.Time.
|
||||
func GetVariantDate(value uint64) (time.Time, error) {
|
||||
var st syscall.Systemtime
|
||||
v1 := uint32(value)
|
||||
v2 := uint32(value >> 32)
|
||||
r, _, _ := procVariantTimeToSystemTime.Call(uintptr(v1), uintptr(v2), uintptr(unsafe.Pointer(&st)))
|
||||
if r != 0 {
|
||||
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil
|
||||
}
|
||||
return time.Now(), errors.New("Could not convert to time, passing current time.")
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
//go:build windows && arm64
|
||||
// +build windows,arm64
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// GetVariantDate converts COM Variant Time value to Go time.Time.
|
||||
func GetVariantDate(value uint64) (time.Time, error) {
|
||||
var st syscall.Systemtime
|
||||
v1 := uint32(value)
|
||||
v2 := uint32(value >> 32)
|
||||
r, _, _ := procVariantTimeToSystemTime.Call(uintptr(v1), uintptr(v2), uintptr(unsafe.Pointer(&st)))
|
||||
if r != 0 {
|
||||
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil
|
||||
}
|
||||
return time.Now(), errors.New("Could not convert to time, passing current time.")
|
||||
}
|
||||
+11
-15
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -14,8 +14,7 @@ import (
|
||||
)
|
||||
|
||||
// TimesStat contains the amounts of time the CPU has spent performing different
|
||||
// kinds of work. Time units are in USER_HZ or Jiffies (typically hundredths of
|
||||
// a second). It is based on linux /proc/stat file.
|
||||
// kinds of work. Time units are in seconds. It is based on linux /proc/stat file.
|
||||
type TimesStat struct {
|
||||
CPU string `json:"cpu"`
|
||||
User float64 `json:"user"`
|
||||
@@ -28,7 +27,6 @@ type TimesStat struct {
|
||||
Steal float64 `json:"steal"`
|
||||
Guest float64 `json:"guest"`
|
||||
GuestNice float64 `json:"guestNice"`
|
||||
Stolen float64 `json:"stolen"`
|
||||
}
|
||||
|
||||
type InfoStat struct {
|
||||
@@ -63,14 +61,11 @@ func init() {
|
||||
lastCPUPercent.Unlock()
|
||||
}
|
||||
|
||||
// Counts returns the number of physical or logical cores in the system
|
||||
func Counts(logical bool) (int, error) {
|
||||
return CountsWithContext(context.Background(), logical)
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
return runtime.NumCPU(), nil
|
||||
}
|
||||
|
||||
func (c TimesStat) String() string {
|
||||
v := []string{
|
||||
`"cpu":"` + c.CPU + `"`,
|
||||
@@ -84,7 +79,6 @@ func (c TimesStat) String() string {
|
||||
`"steal":` + strconv.FormatFloat(c.Steal, 'f', 1, 64),
|
||||
`"guest":` + strconv.FormatFloat(c.Guest, 'f', 1, 64),
|
||||
`"guestNice":` + strconv.FormatFloat(c.GuestNice, 'f', 1, 64),
|
||||
`"stolen":` + strconv.FormatFloat(c.Stolen, 'f', 1, 64),
|
||||
}
|
||||
|
||||
return `{` + strings.Join(v, ",") + `}`
|
||||
@@ -92,8 +86,8 @@ func (c TimesStat) String() string {
|
||||
|
||||
// Total returns the total number of seconds in a CPUTimesStat
|
||||
func (c TimesStat) Total() float64 {
|
||||
total := c.User + c.System + c.Nice + c.Iowait + c.Irq + c.Softirq + c.Steal +
|
||||
c.Guest + c.GuestNice + c.Idle + c.Stolen
|
||||
total := c.User + c.System + c.Nice + c.Iowait + c.Irq + c.Softirq +
|
||||
c.Steal + c.Idle
|
||||
return total
|
||||
}
|
||||
|
||||
@@ -104,7 +98,7 @@ func (c InfoStat) String() string {
|
||||
|
||||
func getAllBusy(t TimesStat) (float64, float64) {
|
||||
busy := t.User + t.System + t.Nice + t.Iowait + t.Irq +
|
||||
t.Softirq + t.Steal + t.Guest + t.GuestNice + t.Stolen
|
||||
t.Softirq + t.Steal
|
||||
return busy + t.Idle, busy
|
||||
}
|
||||
|
||||
@@ -116,9 +110,9 @@ func calculateBusy(t1, t2 TimesStat) float64 {
|
||||
return 0
|
||||
}
|
||||
if t2All <= t1All {
|
||||
return 1
|
||||
return 100
|
||||
}
|
||||
return (t2Busy - t1Busy) / (t2All - t1All) * 100
|
||||
return math.Min(100, math.Max(0, (t2Busy-t1Busy)/(t2All-t1All)*100))
|
||||
}
|
||||
|
||||
func calculateAllBusy(t1, t2 []TimesStat) ([]float64, error) {
|
||||
@@ -155,7 +149,9 @@ func PercentWithContext(ctx context.Context, interval time.Duration, percpu bool
|
||||
return nil, err
|
||||
}
|
||||
|
||||
time.Sleep(interval)
|
||||
if err := common.Sleep(ctx, interval); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// And at the end of the interval.
|
||||
cpuTimes2, err := Times(percpu)
|
||||
|
||||
+57
-60
@@ -4,9 +4,11 @@ package cpu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tklauser/go-sysconf"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// sys/resource.h
|
||||
@@ -22,6 +24,14 @@ const (
|
||||
// default value. from time.h
|
||||
var ClocksPerSec = float64(128)
|
||||
|
||||
func init() {
|
||||
clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK)
|
||||
// ignore errors
|
||||
if err == nil {
|
||||
ClocksPerSec = float64(clkTck)
|
||||
}
|
||||
}
|
||||
|
||||
func Times(percpu bool) ([]TimesStat, error) {
|
||||
return TimesWithContext(context.Background(), percpu)
|
||||
}
|
||||
@@ -41,75 +51,62 @@ func Info() ([]InfoStat, error) {
|
||||
|
||||
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
var ret []InfoStat
|
||||
sysctl, err := exec.LookPath("/usr/sbin/sysctl")
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, sysctl, "machdep.cpu")
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
c := InfoStat{}
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
values := strings.Fields(line)
|
||||
if len(values) < 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
t, err := strconv.ParseInt(values[1], 10, 64)
|
||||
// err is not checked here because some value is string.
|
||||
if strings.HasPrefix(line, "machdep.cpu.brand_string") {
|
||||
c.ModelName = strings.Join(values[1:], " ")
|
||||
} else if strings.HasPrefix(line, "machdep.cpu.family") {
|
||||
c.Family = values[1]
|
||||
} else if strings.HasPrefix(line, "machdep.cpu.model") {
|
||||
c.Model = values[1]
|
||||
} else if strings.HasPrefix(line, "machdep.cpu.stepping") {
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
c.Stepping = int32(t)
|
||||
} else if strings.HasPrefix(line, "machdep.cpu.features") {
|
||||
for _, v := range values[1:] {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
} else if strings.HasPrefix(line, "machdep.cpu.leaf7_features") {
|
||||
for _, v := range values[1:] {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
} else if strings.HasPrefix(line, "machdep.cpu.extfeatures") {
|
||||
for _, v := range values[1:] {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
} else if strings.HasPrefix(line, "machdep.cpu.core_count") {
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
c.Cores = int32(t)
|
||||
} else if strings.HasPrefix(line, "machdep.cpu.cache.size") {
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
c.CacheSize = int32(t)
|
||||
} else if strings.HasPrefix(line, "machdep.cpu.vendor") {
|
||||
c.VendorID = values[1]
|
||||
c.ModelName, _ = unix.Sysctl("machdep.cpu.brand_string")
|
||||
family, _ := unix.SysctlUint32("machdep.cpu.family")
|
||||
c.Family = strconv.FormatUint(uint64(family), 10)
|
||||
model, _ := unix.SysctlUint32("machdep.cpu.model")
|
||||
c.Model = strconv.FormatUint(uint64(model), 10)
|
||||
stepping, _ := unix.SysctlUint32("machdep.cpu.stepping")
|
||||
c.Stepping = int32(stepping)
|
||||
features, err := unix.Sysctl("machdep.cpu.features")
|
||||
if err == nil {
|
||||
for _, v := range strings.Fields(features) {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
}
|
||||
leaf7Features, err := unix.Sysctl("machdep.cpu.leaf7_features")
|
||||
if err == nil {
|
||||
for _, v := range strings.Fields(leaf7Features) {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
}
|
||||
extfeatures, err := unix.Sysctl("machdep.cpu.extfeatures")
|
||||
if err == nil {
|
||||
for _, v := range strings.Fields(extfeatures) {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
}
|
||||
cores, _ := unix.SysctlUint32("machdep.cpu.core_count")
|
||||
c.Cores = int32(cores)
|
||||
cacheSize, _ := unix.SysctlUint32("machdep.cpu.cache.size")
|
||||
c.CacheSize = int32(cacheSize)
|
||||
c.VendorID, _ = unix.Sysctl("machdep.cpu.vendor")
|
||||
|
||||
// Use the rated frequency of the CPU. This is a static value and does not
|
||||
// account for low power or Turbo Boost modes.
|
||||
out, err = invoke.CommandWithContext(ctx, sysctl, "hw.cpufrequency")
|
||||
cpuFrequency, err := unix.SysctlUint64("hw.cpufrequency")
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
values := strings.Fields(string(out))
|
||||
hz, err := strconv.ParseFloat(values[1], 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
c.Mhz = hz / 1000000.0
|
||||
c.Mhz = float64(cpuFrequency) / 1000000.0
|
||||
|
||||
return append(ret, c), nil
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
var cpuArgument string
|
||||
if logical {
|
||||
cpuArgument = "hw.logicalcpu"
|
||||
} else {
|
||||
cpuArgument = "hw.physicalcpu"
|
||||
}
|
||||
|
||||
count, err := unix.SysctlUint32(cpuArgument)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int(count), nil
|
||||
}
|
||||
|
||||
+1
@@ -10,6 +10,7 @@ package cpu
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_host.h>
|
||||
#include <mach/host_info.h>
|
||||
#include <TargetConditionals.h>
|
||||
#if TARGET_OS_MAC
|
||||
#include <libproc.h>
|
||||
#endif
|
||||
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
"github.com/tklauser/go-sysconf"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var ClocksPerSec = float64(128)
|
||||
var cpuMatch = regexp.MustCompile(`^CPU:`)
|
||||
var originMatch = regexp.MustCompile(`Origin\s*=\s*"(.+)"\s+Id\s*=\s*(.+)\s+Stepping\s*=\s*(.+)`)
|
||||
var featuresMatch = regexp.MustCompile(`Features=.+<(.+)>`)
|
||||
var featuresMatch2 = regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`)
|
||||
var cpuEnd = regexp.MustCompile(`^Trying to mount root`)
|
||||
var cpuTimesSize int
|
||||
var emptyTimes cpuTimes
|
||||
|
||||
func init() {
|
||||
clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK)
|
||||
// ignore errors
|
||||
if err == nil {
|
||||
ClocksPerSec = float64(clkTck)
|
||||
}
|
||||
}
|
||||
|
||||
func timeStat(name string, t *cpuTimes) *TimesStat {
|
||||
return &TimesStat{
|
||||
User: float64(t.User) / ClocksPerSec,
|
||||
Nice: float64(t.Nice) / ClocksPerSec,
|
||||
System: float64(t.Sys) / ClocksPerSec,
|
||||
Idle: float64(t.Idle) / ClocksPerSec,
|
||||
Irq: float64(t.Intr) / ClocksPerSec,
|
||||
CPU: name,
|
||||
}
|
||||
}
|
||||
|
||||
func Times(percpu bool) ([]TimesStat, error) {
|
||||
return TimesWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
|
||||
if percpu {
|
||||
buf, err := unix.SysctlRaw("kern.cp_times")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We can't do this in init due to the conflict with cpu.init()
|
||||
if cpuTimesSize == 0 {
|
||||
cpuTimesSize = int(reflect.TypeOf(cpuTimes{}).Size())
|
||||
}
|
||||
|
||||
ncpus := len(buf) / cpuTimesSize
|
||||
ret := make([]TimesStat, 0, ncpus)
|
||||
for i := 0; i < ncpus; i++ {
|
||||
times := (*cpuTimes)(unsafe.Pointer(&buf[i*cpuTimesSize]))
|
||||
if *times == emptyTimes {
|
||||
// CPU not present
|
||||
continue
|
||||
}
|
||||
ret = append(ret, *timeStat(fmt.Sprintf("cpu%d", len(ret)), times))
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
buf, err := unix.SysctlRaw("kern.cp_time")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
times := (*cpuTimes)(unsafe.Pointer(&buf[0]))
|
||||
return []TimesStat{*timeStat("cpu-total", times)}, nil
|
||||
}
|
||||
|
||||
// Returns only one InfoStat on DragonflyBSD. The information regarding core
|
||||
// count, however is accurate and it is assumed that all InfoStat attributes
|
||||
// are the same across CPUs.
|
||||
func Info() ([]InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
const dmesgBoot = "/var/run/dmesg.boot"
|
||||
|
||||
c, err := parseDmesgBoot(dmesgBoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var u32 uint32
|
||||
if u32, err = unix.SysctlUint32("hw.clockrate"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Mhz = float64(u32)
|
||||
|
||||
var num int
|
||||
var buf string
|
||||
if buf, err = unix.Sysctl("hw.cpu_topology.tree"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
num = strings.Count(buf, "CHIP")
|
||||
c.Cores = int32(strings.Count(string(buf), "CORE") / num)
|
||||
|
||||
if c.ModelName, err = unix.Sysctl("hw.model"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]InfoStat, num)
|
||||
for i := 0; i < num; i++ {
|
||||
ret[i] = c
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func parseDmesgBoot(fileName string) (InfoStat, error) {
|
||||
c := InfoStat{}
|
||||
lines, _ := common.ReadLines(fileName)
|
||||
for _, line := range lines {
|
||||
if matches := cpuEnd.FindStringSubmatch(line); matches != nil {
|
||||
break
|
||||
} else if matches := originMatch.FindStringSubmatch(line); matches != nil {
|
||||
c.VendorID = matches[1]
|
||||
t, err := strconv.ParseInt(matches[2], 10, 32)
|
||||
if err != nil {
|
||||
return c, fmt.Errorf("unable to parse DragonflyBSD CPU stepping information from %q: %v", line, err)
|
||||
}
|
||||
c.Stepping = int32(t)
|
||||
} else if matches := featuresMatch.FindStringSubmatch(line); matches != nil {
|
||||
for _, v := range strings.Split(matches[1], ",") {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
} else if matches := featuresMatch2.FindStringSubmatch(line); matches != nil {
|
||||
for _, v := range strings.Split(matches[1], ",") {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
return runtime.NumCPU(), nil
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package cpu
|
||||
|
||||
type cpuTimes struct {
|
||||
User uint64
|
||||
Nice uint64
|
||||
Sys uint64
|
||||
Intr uint64
|
||||
Idle uint64
|
||||
}
|
||||
+6
-1
@@ -1,9 +1,10 @@
|
||||
// +build !darwin,!linux,!freebsd,!openbsd,!solaris,!windows
|
||||
// +build !darwin,!linux,!freebsd,!openbsd,!solaris,!windows,!dragonfly
|
||||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
)
|
||||
@@ -23,3 +24,7 @@ func Info() ([]InfoStat, error) {
|
||||
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
return []InfoStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
return runtime.NumCPU(), nil
|
||||
}
|
||||
|
||||
+8
-10
@@ -3,14 +3,15 @@ package cpu
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
"github.com/tklauser/go-sysconf"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
@@ -25,17 +26,10 @@ var cpuTimesSize int
|
||||
var emptyTimes cpuTimes
|
||||
|
||||
func init() {
|
||||
getconf, err := exec.LookPath("/usr/bin/getconf")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
out, err := invoke.Command(getconf, "CLK_TCK")
|
||||
clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK)
|
||||
// ignore errors
|
||||
if err == nil {
|
||||
i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
|
||||
if err == nil {
|
||||
ClocksPerSec = float64(i)
|
||||
}
|
||||
ClocksPerSec = float64(clkTck)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,3 +160,7 @@ func parseDmesgBoot(fileName string) (InfoStat, int, error) {
|
||||
|
||||
return c, cpuNum, nil
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
return runtime.NumCPU(), nil
|
||||
}
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package cpu
|
||||
|
||||
type cpuTimes struct {
|
||||
User uint32
|
||||
Nice uint32
|
||||
Sys uint32
|
||||
Intr uint32
|
||||
Idle uint32
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package cpu
|
||||
|
||||
type cpuTimes struct {
|
||||
User uint64
|
||||
Nice uint64
|
||||
Sys uint64
|
||||
Intr uint64
|
||||
Idle uint64
|
||||
}
|
||||
+146
-24
@@ -6,27 +6,21 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
"github.com/tklauser/go-sysconf"
|
||||
)
|
||||
|
||||
var CPUTick = float64(100)
|
||||
var ClocksPerSec = float64(100)
|
||||
|
||||
func init() {
|
||||
getconf, err := exec.LookPath("/usr/bin/getconf")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
out, err := invoke.CommandWithContext(context.Background(), getconf, "CLK_TCK")
|
||||
clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK)
|
||||
// ignore errors
|
||||
if err == nil {
|
||||
i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
|
||||
if err == nil {
|
||||
CPUTick = i
|
||||
}
|
||||
ClocksPerSec = float64(clkTck)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +81,7 @@ func finishCPUInfo(c *InfoStat) error {
|
||||
lines, err = common.ReadLines(sysCPUPath(c.CPU, "cpufreq/cpuinfo_max_freq"))
|
||||
// if we encounter errors below such as there are no cpuinfo_max_freq file,
|
||||
// we just ignore. so let Mhz is 0.
|
||||
if err != nil {
|
||||
if err != nil || len(lines) == 0 {
|
||||
return nil
|
||||
}
|
||||
value, err = strconv.ParseFloat(lines[0], 64)
|
||||
@@ -147,9 +141,44 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
c.CPU = int32(t)
|
||||
case "vendorId", "vendor_id":
|
||||
c.VendorID = value
|
||||
case "CPU implementer":
|
||||
if v, err := strconv.ParseUint(value, 0, 8); err == nil {
|
||||
switch v {
|
||||
case 0x41:
|
||||
c.VendorID = "ARM"
|
||||
case 0x42:
|
||||
c.VendorID = "Broadcom"
|
||||
case 0x43:
|
||||
c.VendorID = "Cavium"
|
||||
case 0x44:
|
||||
c.VendorID = "DEC"
|
||||
case 0x46:
|
||||
c.VendorID = "Fujitsu"
|
||||
case 0x48:
|
||||
c.VendorID = "HiSilicon"
|
||||
case 0x49:
|
||||
c.VendorID = "Infineon"
|
||||
case 0x4d:
|
||||
c.VendorID = "Motorola/Freescale"
|
||||
case 0x4e:
|
||||
c.VendorID = "NVIDIA"
|
||||
case 0x50:
|
||||
c.VendorID = "APM"
|
||||
case 0x51:
|
||||
c.VendorID = "Qualcomm"
|
||||
case 0x56:
|
||||
c.VendorID = "Marvell"
|
||||
case 0x61:
|
||||
c.VendorID = "Apple"
|
||||
case 0x69:
|
||||
c.VendorID = "Intel"
|
||||
case 0xc0:
|
||||
c.VendorID = "Ampere"
|
||||
}
|
||||
}
|
||||
case "cpu family":
|
||||
c.Family = value
|
||||
case "model":
|
||||
case "model", "CPU part":
|
||||
c.Model = value
|
||||
case "model name", "cpu":
|
||||
c.ModelName = value
|
||||
@@ -159,7 +188,7 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
c.Family = "POWER"
|
||||
c.VendorID = "IBM"
|
||||
}
|
||||
case "stepping", "revision":
|
||||
case "stepping", "revision", "CPU revision":
|
||||
val := value
|
||||
|
||||
if key == "revision" {
|
||||
@@ -250,35 +279,128 @@ func parseStatLine(line string) (*TimesStat, error) {
|
||||
|
||||
ct := &TimesStat{
|
||||
CPU: cpu,
|
||||
User: user / CPUTick,
|
||||
Nice: nice / CPUTick,
|
||||
System: system / CPUTick,
|
||||
Idle: idle / CPUTick,
|
||||
Iowait: iowait / CPUTick,
|
||||
Irq: irq / CPUTick,
|
||||
Softirq: softirq / CPUTick,
|
||||
User: user / ClocksPerSec,
|
||||
Nice: nice / ClocksPerSec,
|
||||
System: system / ClocksPerSec,
|
||||
Idle: idle / ClocksPerSec,
|
||||
Iowait: iowait / ClocksPerSec,
|
||||
Irq: irq / ClocksPerSec,
|
||||
Softirq: softirq / ClocksPerSec,
|
||||
}
|
||||
if len(fields) > 8 { // Linux >= 2.6.11
|
||||
steal, err := strconv.ParseFloat(fields[8], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ct.Steal = steal / CPUTick
|
||||
ct.Steal = steal / ClocksPerSec
|
||||
}
|
||||
if len(fields) > 9 { // Linux >= 2.6.24
|
||||
guest, err := strconv.ParseFloat(fields[9], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ct.Guest = guest / CPUTick
|
||||
ct.Guest = guest / ClocksPerSec
|
||||
}
|
||||
if len(fields) > 10 { // Linux >= 3.2.0
|
||||
guestNice, err := strconv.ParseFloat(fields[10], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ct.GuestNice = guestNice / CPUTick
|
||||
ct.GuestNice = guestNice / ClocksPerSec
|
||||
}
|
||||
|
||||
return ct, nil
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
if logical {
|
||||
ret := 0
|
||||
// https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_pslinux.py#L599
|
||||
procCpuinfo := common.HostProc("cpuinfo")
|
||||
lines, err := common.ReadLines(procCpuinfo)
|
||||
if err == nil {
|
||||
for _, line := range lines {
|
||||
line = strings.ToLower(line)
|
||||
if strings.HasPrefix(line, "processor") {
|
||||
_, err = strconv.Atoi(strings.TrimSpace(line[strings.IndexByte(line, ':')+1:]))
|
||||
if err == nil {
|
||||
ret++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ret == 0 {
|
||||
procStat := common.HostProc("stat")
|
||||
lines, err = common.ReadLines(procStat)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, line := range lines {
|
||||
if len(line) >= 4 && strings.HasPrefix(line, "cpu") && '0' <= line[3] && line[3] <= '9' { // `^cpu\d` regexp matching
|
||||
ret++
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
// physical cores
|
||||
// https://github.com/giampaolo/psutil/blob/8415355c8badc9c94418b19bdf26e622f06f0cce/psutil/_pslinux.py#L615-L628
|
||||
var threadSiblingsLists = make(map[string]bool)
|
||||
// These 2 files are the same but */core_cpus_list is newer while */thread_siblings_list is deprecated and may disappear in the future.
|
||||
// https://www.kernel.org/doc/Documentation/admin-guide/cputopology.rst
|
||||
// https://github.com/giampaolo/psutil/pull/1727#issuecomment-707624964
|
||||
// https://lkml.org/lkml/2019/2/26/41
|
||||
for _, glob := range []string{"devices/system/cpu/cpu[0-9]*/topology/core_cpus_list", "devices/system/cpu/cpu[0-9]*/topology/thread_siblings_list"} {
|
||||
if files, err := filepath.Glob(common.HostSys(glob)); err == nil {
|
||||
for _, file := range files {
|
||||
lines, err := common.ReadLines(file)
|
||||
if err != nil || len(lines) != 1 {
|
||||
continue
|
||||
}
|
||||
threadSiblingsLists[lines[0]] = true
|
||||
}
|
||||
ret := len(threadSiblingsLists)
|
||||
if ret != 0 {
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
// https://github.com/giampaolo/psutil/blob/122174a10b75c9beebe15f6c07dcf3afbe3b120d/psutil/_pslinux.py#L631-L652
|
||||
filename := common.HostProc("cpuinfo")
|
||||
lines, err := common.ReadLines(filename)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
mapping := make(map[int]int)
|
||||
currentInfo := make(map[string]int)
|
||||
for _, line := range lines {
|
||||
line = strings.ToLower(strings.TrimSpace(line))
|
||||
if line == "" {
|
||||
// new section
|
||||
id, okID := currentInfo["physical id"]
|
||||
cores, okCores := currentInfo["cpu cores"]
|
||||
if okID && okCores {
|
||||
mapping[id] = cores
|
||||
}
|
||||
currentInfo = make(map[string]int)
|
||||
continue
|
||||
}
|
||||
fields := strings.Split(line, ":")
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
fields[0] = strings.TrimSpace(fields[0])
|
||||
if fields[0] == "physical id" || fields[0] == "cpu cores" {
|
||||
val, err := strconv.Atoi(strings.TrimSpace(fields[1]))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
currentInfo[fields[0]] = val
|
||||
}
|
||||
}
|
||||
ret := 0
|
||||
for _, v := range mapping {
|
||||
ret += v
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
+78
-19
@@ -7,16 +7,18 @@ import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
"github.com/tklauser/go-sysconf"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// sys/sched.h
|
||||
const (
|
||||
var (
|
||||
CPUser = 0
|
||||
CPNice = 1
|
||||
CPSys = 2
|
||||
@@ -28,6 +30,8 @@ const (
|
||||
// sys/sysctl.h
|
||||
const (
|
||||
CTLKern = 1 // "high kernel": proc, limits
|
||||
CTLHw = 6 // CTL_HW
|
||||
SMT = 24 // HW_SMT
|
||||
KernCptime = 40 // KERN_CPTIME
|
||||
KernCptime2 = 71 // KERN_CPTIME2
|
||||
)
|
||||
@@ -35,18 +39,44 @@ const (
|
||||
var ClocksPerSec = float64(128)
|
||||
|
||||
func init() {
|
||||
getconf, err := exec.LookPath("/usr/bin/getconf")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
out, err := invoke.Command(getconf, "CLK_TCK")
|
||||
clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK)
|
||||
// ignore errors
|
||||
if err == nil {
|
||||
i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
|
||||
if err == nil {
|
||||
ClocksPerSec = float64(i)
|
||||
}
|
||||
ClocksPerSec = float64(clkTck)
|
||||
}
|
||||
|
||||
func() {
|
||||
v, err := unix.Sysctl("kern.osrelease") // can't reuse host.PlatformInformation because of circular import
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
v = strings.ToLower(v)
|
||||
version, err := strconv.ParseFloat(v, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if version >= 6.4 {
|
||||
CPIntr = 4
|
||||
CPIdle = 5
|
||||
CPUStates = 6
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func smt() (bool, error) {
|
||||
mib := []int32{CTLHw, SMT}
|
||||
buf, _, err := common.CallSyscall(mib)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var ret bool
|
||||
br := bytes.NewReader(buf)
|
||||
if err := binary.Read(br, binary.LittleEndian, &ret); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func Times(percpu bool) ([]TimesStat, error) {
|
||||
@@ -63,13 +93,27 @@ func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
|
||||
ncpu = 1
|
||||
}
|
||||
|
||||
smt, err := smt()
|
||||
if err == syscall.EOPNOTSUPP {
|
||||
// if hw.smt is not applicable for this platform (e.g. i386),
|
||||
// pretend it's enabled
|
||||
smt = true
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < ncpu; i++ {
|
||||
var cpuTimes [CPUStates]int64
|
||||
j := i
|
||||
if !smt {
|
||||
j *= 2
|
||||
}
|
||||
|
||||
var cpuTimes = make([]int32, CPUStates)
|
||||
var mib []int32
|
||||
if percpu {
|
||||
mib = []int32{CTLKern, KernCptime}
|
||||
mib = []int32{CTLKern, KernCptime2, int32(j)}
|
||||
} else {
|
||||
mib = []int32{CTLKern, KernCptime2, int32(i)}
|
||||
mib = []int32{CTLKern, KernCptime}
|
||||
}
|
||||
buf, _, err := common.CallSyscall(mib)
|
||||
if err != nil {
|
||||
@@ -88,10 +132,10 @@ func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
|
||||
Idle: float64(cpuTimes[CPIdle]) / ClocksPerSec,
|
||||
Irq: float64(cpuTimes[CPIntr]) / ClocksPerSec,
|
||||
}
|
||||
if !percpu {
|
||||
c.CPU = "cpu-total"
|
||||
if percpu {
|
||||
c.CPU = fmt.Sprintf("cpu%d", j)
|
||||
} else {
|
||||
c.CPU = fmt.Sprintf("cpu%d", i)
|
||||
c.CPU = "cpu-total"
|
||||
}
|
||||
ret = append(ret, c)
|
||||
}
|
||||
@@ -106,14 +150,29 @@ func Info() ([]InfoStat, error) {
|
||||
|
||||
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
var ret []InfoStat
|
||||
var err error
|
||||
|
||||
c := InfoStat{}
|
||||
|
||||
v, err := unix.Sysctl("hw.model")
|
||||
mhz, err := unix.SysctlUint32("hw.cpuspeed")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.ModelName = v
|
||||
c.Mhz = float64(mhz)
|
||||
|
||||
ncpu, err := unix.SysctlUint32("hw.ncpuonline")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Cores = int32(ncpu)
|
||||
|
||||
if c.ModelName, err = unix.Sysctl("hw.model"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return append(ret, c), nil
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
return runtime.NumCPU(), nil
|
||||
}
|
||||
|
||||
+97
-14
@@ -6,36 +6,115 @@ import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
"github.com/tklauser/go-sysconf"
|
||||
)
|
||||
|
||||
var ClocksPerSec = float64(128)
|
||||
|
||||
func init() {
|
||||
getconf, err := exec.LookPath("/usr/bin/getconf")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
out, err := invoke.Command(getconf, "CLK_TCK")
|
||||
clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK)
|
||||
// ignore errors
|
||||
if err == nil {
|
||||
i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
|
||||
if err == nil {
|
||||
ClocksPerSec = float64(i)
|
||||
}
|
||||
ClocksPerSec = float64(clkTck)
|
||||
}
|
||||
}
|
||||
|
||||
//sum all values in a float64 map with float64 keys
|
||||
func msum(x map[float64]float64) float64 {
|
||||
total := 0.0
|
||||
for _, y := range x {
|
||||
total += y
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func Times(percpu bool) ([]TimesStat, error) {
|
||||
return TimesWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
|
||||
return []TimesStat{}, common.ErrNotImplementedError
|
||||
kstatSys, err := exec.LookPath("kstat")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot find kstat: %s", err)
|
||||
}
|
||||
cpu := make(map[float64]float64)
|
||||
idle := make(map[float64]float64)
|
||||
user := make(map[float64]float64)
|
||||
kern := make(map[float64]float64)
|
||||
iowt := make(map[float64]float64)
|
||||
//swap := make(map[float64]float64)
|
||||
kstatSysOut, err := invoke.CommandWithContext(ctx, kstatSys, "-p", "cpu_stat:*:*:/^idle$|^user$|^kernel$|^iowait$|^swap$/")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot execute kstat: %s", err)
|
||||
}
|
||||
re := regexp.MustCompile(`[:\s]+`)
|
||||
for _, line := range strings.Split(string(kstatSysOut), "\n") {
|
||||
fields := re.Split(line, -1)
|
||||
if fields[0] != "cpu_stat" {
|
||||
continue
|
||||
}
|
||||
cpuNumber, err := strconv.ParseFloat(fields[1], 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse cpu number: %s", err)
|
||||
}
|
||||
cpu[cpuNumber] = cpuNumber
|
||||
switch fields[3] {
|
||||
case "idle":
|
||||
idle[cpuNumber], err = strconv.ParseFloat(fields[4], 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse idle: %s", err)
|
||||
}
|
||||
case "user":
|
||||
user[cpuNumber], err = strconv.ParseFloat(fields[4], 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse user: %s", err)
|
||||
}
|
||||
case "kernel":
|
||||
kern[cpuNumber], err = strconv.ParseFloat(fields[4], 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse kernel: %s", err)
|
||||
}
|
||||
case "iowait":
|
||||
iowt[cpuNumber], err = strconv.ParseFloat(fields[4], 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse iowait: %s", err)
|
||||
}
|
||||
//not sure how this translates, don't report, add to kernel, something else?
|
||||
/*case "swap":
|
||||
swap[cpuNumber], err = strconv.ParseFloat(fields[4], 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse swap: %s", err)
|
||||
} */
|
||||
}
|
||||
}
|
||||
ret := make([]TimesStat, 0, len(cpu))
|
||||
if percpu {
|
||||
for _, c := range cpu {
|
||||
ct := &TimesStat{
|
||||
CPU: fmt.Sprintf("cpu%d", int(cpu[c])),
|
||||
Idle: idle[c] / ClocksPerSec,
|
||||
User: user[c] / ClocksPerSec,
|
||||
System: kern[c] / ClocksPerSec,
|
||||
Iowait: iowt[c] / ClocksPerSec,
|
||||
}
|
||||
ret = append(ret, *ct)
|
||||
}
|
||||
} else {
|
||||
ct := &TimesStat{
|
||||
CPU: "cpu-total",
|
||||
Idle: msum(idle) / ClocksPerSec,
|
||||
User: msum(user) / ClocksPerSec,
|
||||
System: msum(kern) / ClocksPerSec,
|
||||
Iowait: msum(iowt) / ClocksPerSec,
|
||||
}
|
||||
ret = append(ret, *ct)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func Info() ([]InfoStat, error) {
|
||||
@@ -43,7 +122,7 @@ func Info() ([]InfoStat, error) {
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
psrInfo, err := exec.LookPath("/usr/sbin/psrinfo")
|
||||
psrInfo, err := exec.LookPath("psrinfo")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot find psrinfo: %s", err)
|
||||
}
|
||||
@@ -52,7 +131,7 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
return nil, fmt.Errorf("cannot execute psrinfo: %s", err)
|
||||
}
|
||||
|
||||
isaInfo, err := exec.LookPath("/usr/bin/isainfo")
|
||||
isaInfo, err := exec.LookPath("isainfo")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot find isainfo: %s", err)
|
||||
}
|
||||
@@ -100,7 +179,7 @@ func parseISAInfo(cmdOutput string) ([]string, error) {
|
||||
return flags, nil
|
||||
}
|
||||
|
||||
var psrInfoMatch = regexp.MustCompile(`The physical processor has (?:([\d]+) virtual processor \(([\d]+)\)|([\d]+) cores and ([\d]+) virtual processors[^\n]+)\n(?:\s+ The core has.+\n)*\s+.+ \((\w+) ([\S]+) family (.+) model (.+) step (.+) clock (.+) MHz\)\n[\s]*(.*)`)
|
||||
var psrInfoMatch = regexp.MustCompile(`The physical processor has (?:([\d]+) virtual processors? \(([\d-]+)\)|([\d]+) cores and ([\d]+) virtual processors[^\n]+)\n(?:\s+ The core has.+\n)*\s+.+ \((\w+) ([\S]+) family (.+) model (.+) step (.+) clock (.+) MHz\)\n[\s]*(.*)`)
|
||||
|
||||
const (
|
||||
psrNumCoresOffset = 1
|
||||
@@ -196,3 +275,7 @@ func parseProcessorInfo(cmdOutput string) ([]InfoStat, error) {
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
return runtime.NumCPU(), nil
|
||||
}
|
||||
|
||||
+134
-41
@@ -5,37 +5,50 @@ package cpu
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/StackExchange/wmi"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
procGetActiveProcessorCount = common.Modkernel32.NewProc("GetActiveProcessorCount")
|
||||
procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo")
|
||||
)
|
||||
|
||||
type Win32_Processor struct {
|
||||
LoadPercentage *uint16
|
||||
Win32_ProcessorWithoutLoadPct
|
||||
LoadPercentage *uint16
|
||||
}
|
||||
|
||||
// LoadPercentage takes a linearly more time as the number of sockets increases.
|
||||
// For vSphere by default corespersocket = 1, meaning for a 40 vCPU VM Get Processor Info
|
||||
// could take more than half a minute.
|
||||
type Win32_ProcessorWithoutLoadPct struct {
|
||||
Family uint16
|
||||
Manufacturer string
|
||||
Name string
|
||||
NumberOfLogicalProcessors uint32
|
||||
NumberOfCores uint32
|
||||
ProcessorID *string
|
||||
Stepping *string
|
||||
MaxClockSpeed uint32
|
||||
}
|
||||
|
||||
// Win32_PerfFormattedData_Counters_ProcessorInformation stores instance value of the perf counters
|
||||
type Win32_PerfFormattedData_Counters_ProcessorInformation struct {
|
||||
Name string
|
||||
PercentDPCTime uint64
|
||||
PercentIdleTime uint64
|
||||
PercentUserTime uint64
|
||||
PercentProcessorTime uint64
|
||||
PercentInterruptTime uint64
|
||||
PercentPriorityTime uint64
|
||||
PercentPrivilegedTime uint64
|
||||
InterruptsPerSec uint32
|
||||
ProcessorFrequency uint32
|
||||
DPCRate uint32
|
||||
// SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
|
||||
// defined in windows api doc with the following
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/winternl/nf-winternl-ntquerysysteminformation#system_processor_performance_information
|
||||
// additional fields documented here
|
||||
// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/processor_performance.htm
|
||||
type win32_SystemProcessorPerformanceInformation struct {
|
||||
IdleTime int64 // idle time in 100ns (this is not a filetime).
|
||||
KernelTime int64 // kernel time in 100ns. kernel time includes idle time. (this is not a filetime).
|
||||
UserTime int64 // usertime in 100ns (this is not a filetime).
|
||||
DpcTime int64 // dpc time in 100ns (this is not a filetime).
|
||||
InterruptTime int64 // interrupt time in 100ns
|
||||
InterruptCount uint32
|
||||
}
|
||||
|
||||
// Win32_PerfFormattedData_PerfOS_System struct to have count of processes and processor queue length
|
||||
@@ -44,6 +57,17 @@ type Win32_PerfFormattedData_PerfOS_System struct {
|
||||
ProcessorQueueLength uint32
|
||||
}
|
||||
|
||||
const (
|
||||
ClocksPerSec = 10000000.0
|
||||
|
||||
// systemProcessorPerformanceInformationClass information class to query with NTQuerySystemInformation
|
||||
// https://processhacker.sourceforge.io/doc/ntexapi_8h.html#ad5d815b48e8f4da1ef2eb7a2f18a54e0
|
||||
win32_SystemProcessorPerformanceInformationClass = 8
|
||||
|
||||
// size of systemProcessorPerformanceInfoSize in memory
|
||||
win32_SystemProcessorPerformanceInfoSize = uint32(unsafe.Sizeof(win32_SystemProcessorPerformanceInformation{}))
|
||||
)
|
||||
|
||||
// Times returns times stat per cpu and combined for all CPUs
|
||||
func Times(percpu bool) ([]TimesStat, error) {
|
||||
return TimesWithContext(context.Background(), percpu)
|
||||
@@ -88,8 +112,9 @@ func Info() ([]InfoStat, error) {
|
||||
|
||||
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
var ret []InfoStat
|
||||
var dst []Win32_Processor
|
||||
var dst []Win32_ProcessorWithoutLoadPct
|
||||
q := wmi.CreateQuery(&dst, "")
|
||||
q = strings.ReplaceAll(q, "Win32_ProcessorWithoutLoadPct", "Win32_Processor")
|
||||
if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
@@ -117,24 +142,6 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// PerfInfo returns the performance counter's instance value for ProcessorInformation.
|
||||
// Name property is the key by which overall, per cpu and per core metric is known.
|
||||
func PerfInfo() ([]Win32_PerfFormattedData_Counters_ProcessorInformation, error) {
|
||||
return PerfInfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func PerfInfoWithContext(ctx context.Context) ([]Win32_PerfFormattedData_Counters_ProcessorInformation, error) {
|
||||
var ret []Win32_PerfFormattedData_Counters_ProcessorInformation
|
||||
|
||||
q := wmi.CreateQuery(&ret, "")
|
||||
err := common.WMIQueryWithContext(ctx, q, &ret)
|
||||
if err != nil {
|
||||
return []Win32_PerfFormattedData_Counters_ProcessorInformation{}, err
|
||||
}
|
||||
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// ProcInfo returns processes count and processor queue length in the system.
|
||||
// There is a single queue for processor even on multiprocessors systems.
|
||||
func ProcInfo() ([]Win32_PerfFormattedData_PerfOS_System, error) {
|
||||
@@ -154,19 +161,105 @@ func ProcInfoWithContext(ctx context.Context) ([]Win32_PerfFormattedData_PerfOS_
|
||||
// perCPUTimes returns times stat per cpu, per core and overall for all CPUs
|
||||
func perCPUTimes() ([]TimesStat, error) {
|
||||
var ret []TimesStat
|
||||
stats, err := PerfInfo()
|
||||
stats, err := perfInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range stats {
|
||||
for core, v := range stats {
|
||||
c := TimesStat{
|
||||
CPU: v.Name,
|
||||
User: float64(v.PercentUserTime),
|
||||
System: float64(v.PercentPrivilegedTime),
|
||||
Idle: float64(v.PercentIdleTime),
|
||||
Irq: float64(v.PercentInterruptTime),
|
||||
CPU: fmt.Sprintf("cpu%d", core),
|
||||
User: float64(v.UserTime) / ClocksPerSec,
|
||||
System: float64(v.KernelTime-v.IdleTime) / ClocksPerSec,
|
||||
Idle: float64(v.IdleTime) / ClocksPerSec,
|
||||
Irq: float64(v.InterruptTime) / ClocksPerSec,
|
||||
}
|
||||
ret = append(ret, c)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// makes call to Windows API function to retrieve performance information for each core
|
||||
func perfInfo() ([]win32_SystemProcessorPerformanceInformation, error) {
|
||||
// Make maxResults large for safety.
|
||||
// We can't invoke the api call with a results array that's too small.
|
||||
// If we have more than 2056 cores on a single host, then it's probably the future.
|
||||
maxBuffer := 2056
|
||||
// buffer for results from the windows proc
|
||||
resultBuffer := make([]win32_SystemProcessorPerformanceInformation, maxBuffer)
|
||||
// size of the buffer in memory
|
||||
bufferSize := uintptr(win32_SystemProcessorPerformanceInfoSize) * uintptr(maxBuffer)
|
||||
// size of the returned response
|
||||
var retSize uint32
|
||||
|
||||
// Invoke windows api proc.
|
||||
// The returned err from the windows dll proc will always be non-nil even when successful.
|
||||
// See https://godoc.org/golang.org/x/sys/windows#LazyProc.Call for more information
|
||||
retCode, _, err := common.ProcNtQuerySystemInformation.Call(
|
||||
win32_SystemProcessorPerformanceInformationClass, // System Information Class -> SystemProcessorPerformanceInformation
|
||||
uintptr(unsafe.Pointer(&resultBuffer[0])), // pointer to first element in result buffer
|
||||
bufferSize, // size of the buffer in memory
|
||||
uintptr(unsafe.Pointer(&retSize)), // pointer to the size of the returned results the windows proc will set this
|
||||
)
|
||||
|
||||
// check return code for errors
|
||||
if retCode != 0 {
|
||||
return nil, fmt.Errorf("call to NtQuerySystemInformation returned %d. err: %s", retCode, err.Error())
|
||||
}
|
||||
|
||||
// calculate the number of returned elements based on the returned size
|
||||
numReturnedElements := retSize / win32_SystemProcessorPerformanceInfoSize
|
||||
|
||||
// trim results to the number of returned elements
|
||||
resultBuffer = resultBuffer[:numReturnedElements]
|
||||
|
||||
return resultBuffer, nil
|
||||
}
|
||||
|
||||
// SystemInfo is an equivalent representation of SYSTEM_INFO in the Windows API.
|
||||
// https://msdn.microsoft.com/en-us/library/ms724958%28VS.85%29.aspx?f=255&MSPPError=-2147217396
|
||||
// https://github.com/elastic/go-windows/blob/bb1581babc04d5cb29a2bfa7a9ac6781c730c8dd/kernel32.go#L43
|
||||
type systemInfo struct {
|
||||
wProcessorArchitecture uint16
|
||||
wReserved uint16
|
||||
dwPageSize uint32
|
||||
lpMinimumApplicationAddress uintptr
|
||||
lpMaximumApplicationAddress uintptr
|
||||
dwActiveProcessorMask uintptr
|
||||
dwNumberOfProcessors uint32
|
||||
dwProcessorType uint32
|
||||
dwAllocationGranularity uint32
|
||||
wProcessorLevel uint16
|
||||
wProcessorRevision uint16
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
if logical {
|
||||
// https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L97
|
||||
err := procGetActiveProcessorCount.Find()
|
||||
if err == nil { // Win7+
|
||||
ret, _, _ := procGetActiveProcessorCount.Call(uintptr(0xffff)) // ALL_PROCESSOR_GROUPS is 0xffff according to Rust's winapi lib https://docs.rs/winapi/*/x86_64-pc-windows-msvc/src/winapi/shared/ntdef.rs.html#120
|
||||
if ret != 0 {
|
||||
return int(ret), nil
|
||||
}
|
||||
}
|
||||
var systemInfo systemInfo
|
||||
_, _, err = procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo)))
|
||||
if systemInfo.dwNumberOfProcessors == 0 {
|
||||
return 0, err
|
||||
}
|
||||
return int(systemInfo.dwNumberOfProcessors), nil
|
||||
}
|
||||
// physical cores https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L499
|
||||
// for the time being, try with unreliable and slow WMI call…
|
||||
var dst []Win32_ProcessorWithoutLoadPct
|
||||
q := wmi.CreateQuery(&dst, "")
|
||||
q = strings.ReplaceAll(q, "Win32_ProcessorWithoutLoadPct", "Win32_Processor")
|
||||
if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var count uint32
|
||||
for _, d := range dst {
|
||||
count += d.NumberOfCores
|
||||
}
|
||||
return int(count), nil
|
||||
}
|
||||
|
||||
+101
@@ -1,7 +1,11 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
)
|
||||
@@ -20,6 +24,7 @@ type InfoStat struct {
|
||||
PlatformFamily string `json:"platformFamily"` // ex: debian, rhel
|
||||
PlatformVersion string `json:"platformVersion"` // version of the complete OS
|
||||
KernelVersion string `json:"kernelVersion"` // version of the OS kernel (if available)
|
||||
KernelArch string `json:"kernelArch"` // native cpu architecture queried at runtime, as returned by `uname -m` or empty string in case of error
|
||||
VirtualizationSystem string `json:"virtualizationSystem"`
|
||||
VirtualizationRole string `json:"virtualizationRole"` // guest or host
|
||||
HostID string `json:"hostid"` // ex: uuid
|
||||
@@ -51,3 +56,99 @@ func (t TemperatureStat) String() string {
|
||||
s, _ := json.Marshal(t)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func Info() (*InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) (*InfoStat, error) {
|
||||
var err error
|
||||
ret := &InfoStat{
|
||||
OS: runtime.GOOS,
|
||||
}
|
||||
|
||||
ret.Hostname, err = os.Hostname()
|
||||
if err != nil && err != common.ErrNotImplementedError {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.Platform, ret.PlatformFamily, ret.PlatformVersion, err = PlatformInformationWithContext(ctx)
|
||||
if err != nil && err != common.ErrNotImplementedError {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.KernelVersion, err = KernelVersionWithContext(ctx)
|
||||
if err != nil && err != common.ErrNotImplementedError {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.KernelArch, err = KernelArch()
|
||||
if err != nil && err != common.ErrNotImplementedError {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.VirtualizationSystem, ret.VirtualizationRole, err = VirtualizationWithContext(ctx)
|
||||
if err != nil && err != common.ErrNotImplementedError {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.BootTime, err = BootTimeWithContext(ctx)
|
||||
if err != nil && err != common.ErrNotImplementedError {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.Uptime, err = UptimeWithContext(ctx)
|
||||
if err != nil && err != common.ErrNotImplementedError {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.Procs, err = numProcs(ctx)
|
||||
if err != nil && err != common.ErrNotImplementedError {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.HostID, err = HostIDWithContext(ctx)
|
||||
if err != nil && err != common.ErrNotImplementedError {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// BootTime returns the system boot time expressed in seconds since the epoch.
|
||||
func BootTime() (uint64, error) {
|
||||
return BootTimeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func Uptime() (uint64, error) {
|
||||
return UptimeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func Users() ([]UserStat, error) {
|
||||
return UsersWithContext(context.Background())
|
||||
}
|
||||
|
||||
func PlatformInformation() (string, string, string, error) {
|
||||
return PlatformInformationWithContext(context.Background())
|
||||
}
|
||||
|
||||
// HostID returns the unique host ID provided by the OS.
|
||||
func HostID() (string, error) {
|
||||
return HostIDWithContext(context.Background())
|
||||
}
|
||||
|
||||
func Virtualization() (string, string, error) {
|
||||
return VirtualizationWithContext(context.Background())
|
||||
}
|
||||
|
||||
func KernelVersion() (string, error) {
|
||||
return KernelVersionWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SensorsTemperatures() ([]TemperatureStat, error) {
|
||||
return SensorsTemperaturesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func timeSince(ts uint64) uint64 {
|
||||
return uint64(time.Now().Unix()) - ts
|
||||
}
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// +build darwin freebsd openbsd
|
||||
|
||||
package host
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// cachedBootTime must be accessed via atomic.Load/StoreUint64
|
||||
var cachedBootTime uint64
|
||||
|
||||
func BootTimeWithContext(ctx context.Context) (uint64, error) {
|
||||
t := atomic.LoadUint64(&cachedBootTime)
|
||||
if t != 0 {
|
||||
return t, nil
|
||||
}
|
||||
tv, err := unix.SysctlTimeval("kern.boottime")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
atomic.StoreUint64(&cachedBootTime, uint64(tv.Sec))
|
||||
|
||||
return uint64(tv.Sec), nil
|
||||
}
|
||||
|
||||
func UptimeWithContext(ctx context.Context) (uint64, error) {
|
||||
boot, err := BootTimeWithContext(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return timeSince(boot), nil
|
||||
}
|
||||
+42
-134
@@ -6,121 +6,51 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
"github.com/shirou/gopsutil/process"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// from utmpx.h
|
||||
const USER_PROCESS = 7
|
||||
|
||||
func Info() (*InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
func HostIDWithContext(ctx context.Context) (string, error) {
|
||||
ioreg, err := exec.LookPath("ioreg")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
out, err := invoke.CommandWithContext(ctx, ioreg, "-rd1", "-c", "IOPlatformExpertDevice")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
if strings.Contains(line, "IOPlatformUUID") {
|
||||
parts := strings.SplitAfter(line, `" = "`)
|
||||
if len(parts) == 2 {
|
||||
uuid := strings.TrimRight(parts[1], `"`)
|
||||
return strings.ToLower(uuid), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("cannot find host id")
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) (*InfoStat, error) {
|
||||
ret := &InfoStat{
|
||||
OS: runtime.GOOS,
|
||||
PlatformFamily: "darwin",
|
||||
}
|
||||
|
||||
hostname, err := os.Hostname()
|
||||
if err == nil {
|
||||
ret.Hostname = hostname
|
||||
}
|
||||
|
||||
kernelVersion, err := KernelVersionWithContext(ctx)
|
||||
if err == nil {
|
||||
ret.KernelVersion = kernelVersion
|
||||
}
|
||||
|
||||
platform, family, pver, err := PlatformInformation()
|
||||
if err == nil {
|
||||
ret.Platform = platform
|
||||
ret.PlatformFamily = family
|
||||
ret.PlatformVersion = pver
|
||||
}
|
||||
|
||||
system, role, err := Virtualization()
|
||||
if err == nil {
|
||||
ret.VirtualizationSystem = system
|
||||
ret.VirtualizationRole = role
|
||||
}
|
||||
|
||||
boot, err := BootTime()
|
||||
if err == nil {
|
||||
ret.BootTime = boot
|
||||
ret.Uptime = uptime(boot)
|
||||
}
|
||||
|
||||
procs, err := process.Pids()
|
||||
if err == nil {
|
||||
ret.Procs = uint64(len(procs))
|
||||
}
|
||||
|
||||
values, err := common.DoSysctrlWithContext(ctx, "kern.uuid")
|
||||
if err == nil && len(values) == 1 && values[0] != "" {
|
||||
ret.HostID = strings.ToLower(values[0])
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// cachedBootTime must be accessed via atomic.Load/StoreUint64
|
||||
var cachedBootTime uint64
|
||||
|
||||
func BootTime() (uint64, error) {
|
||||
return BootTimeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func BootTimeWithContext(ctx context.Context) (uint64, error) {
|
||||
t := atomic.LoadUint64(&cachedBootTime)
|
||||
if t != 0 {
|
||||
return t, nil
|
||||
}
|
||||
values, err := common.DoSysctrlWithContext(ctx, "kern.boottime")
|
||||
func numProcs(ctx context.Context) (uint64, error) {
|
||||
procs, err := process.PidsWithContext(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014
|
||||
v := strings.Replace(values[2], ",", "", 1)
|
||||
boottime, err := strconv.ParseInt(v, 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
t = uint64(boottime)
|
||||
atomic.StoreUint64(&cachedBootTime, t)
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func uptime(boot uint64) uint64 {
|
||||
return uint64(time.Now().Unix()) - boot
|
||||
}
|
||||
|
||||
func Uptime() (uint64, error) {
|
||||
return UptimeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func UptimeWithContext(ctx context.Context) (uint64, error) {
|
||||
boot, err := BootTime()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uptime(boot), nil
|
||||
}
|
||||
|
||||
func Users() ([]UserStat, error) {
|
||||
return UsersWithContext(context.Background())
|
||||
return uint64(len(procs)), nil
|
||||
}
|
||||
|
||||
func UsersWithContext(ctx context.Context) ([]UserStat, error) {
|
||||
@@ -167,10 +97,6 @@ func UsersWithContext(ctx context.Context) ([]UserStat, error) {
|
||||
|
||||
}
|
||||
|
||||
func PlatformInformation() (string, string, string, error) {
|
||||
return PlatformInformationWithContext(context.Background())
|
||||
}
|
||||
|
||||
func PlatformInformationWithContext(ctx context.Context) (string, string, string, error) {
|
||||
platform := ""
|
||||
family := ""
|
||||
@@ -180,53 +106,35 @@ func PlatformInformationWithContext(ctx context.Context) (string, string, string
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
uname, err := exec.LookPath("uname")
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
out, err := invoke.CommandWithContext(ctx, uname, "-s")
|
||||
p, err := unix.Sysctl("kern.ostype")
|
||||
if err == nil {
|
||||
platform = strings.ToLower(strings.TrimSpace(string(out)))
|
||||
platform = strings.ToLower(p)
|
||||
}
|
||||
|
||||
out, err = invoke.CommandWithContext(ctx, sw_vers, "-productVersion")
|
||||
out, err := invoke.CommandWithContext(ctx, sw_vers, "-productVersion")
|
||||
if err == nil {
|
||||
pver = strings.ToLower(strings.TrimSpace(string(out)))
|
||||
}
|
||||
|
||||
return platform, family, pver, nil
|
||||
}
|
||||
// check if the macos server version file exists
|
||||
_, err = os.Stat("/System/Library/CoreServices/ServerVersion.plist")
|
||||
|
||||
func Virtualization() (string, string, error) {
|
||||
return VirtualizationWithContext(context.Background())
|
||||
// server file doesn't exist
|
||||
if os.IsNotExist(err) {
|
||||
family = "Standalone Workstation"
|
||||
} else {
|
||||
family = "Server"
|
||||
}
|
||||
|
||||
return platform, family, pver, nil
|
||||
}
|
||||
|
||||
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
|
||||
return "", "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func KernelVersion() (string, error) {
|
||||
return KernelVersionWithContext(context.Background())
|
||||
}
|
||||
|
||||
func KernelVersionWithContext(ctx context.Context) (string, error) {
|
||||
uname, err := exec.LookPath("uname")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, uname, "-r")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
version := strings.ToLower(strings.TrimSpace(string(out)))
|
||||
return version, err
|
||||
}
|
||||
|
||||
func SensorsTemperatures() ([]TemperatureStat, error) {
|
||||
return SensorsTemperaturesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
|
||||
return []TemperatureStat{}, common.ErrNotImplementedError
|
||||
version, err := unix.Sysctl("kern.osrelease")
|
||||
return strings.ToLower(version), err
|
||||
}
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// +build darwin
|
||||
// +build arm64
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs host/types_darwin.go
|
||||
|
||||
package host
|
||||
|
||||
type Utmpx struct {
|
||||
User [256]int8
|
||||
Id [4]int8
|
||||
Line [32]int8
|
||||
Pid int32
|
||||
Type int16
|
||||
Tv Timeval
|
||||
Host [256]int8
|
||||
Pad [16]uint32
|
||||
}
|
||||
type Timeval struct {
|
||||
Sec int64
|
||||
Usec int32
|
||||
Pad_cgo_0 [4]byte
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
// +build darwin
|
||||
// +build cgo
|
||||
|
||||
package host
|
||||
|
||||
// #cgo LDFLAGS: -framework IOKit
|
||||
// #include "smc_darwin.h"
|
||||
import "C"
|
||||
import "context"
|
||||
|
||||
func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
|
||||
temperatureKeys := []string{
|
||||
C.AMBIENT_AIR_0,
|
||||
C.AMBIENT_AIR_1,
|
||||
C.CPU_0_DIODE,
|
||||
C.CPU_0_HEATSINK,
|
||||
C.CPU_0_PROXIMITY,
|
||||
C.ENCLOSURE_BASE_0,
|
||||
C.ENCLOSURE_BASE_1,
|
||||
C.ENCLOSURE_BASE_2,
|
||||
C.ENCLOSURE_BASE_3,
|
||||
C.GPU_0_DIODE,
|
||||
C.GPU_0_HEATSINK,
|
||||
C.GPU_0_PROXIMITY,
|
||||
C.HARD_DRIVE_BAY,
|
||||
C.MEMORY_SLOT_0,
|
||||
C.MEMORY_SLOTS_PROXIMITY,
|
||||
C.NORTHBRIDGE,
|
||||
C.NORTHBRIDGE_DIODE,
|
||||
C.NORTHBRIDGE_PROXIMITY,
|
||||
C.THUNDERBOLT_0,
|
||||
C.THUNDERBOLT_1,
|
||||
C.WIRELESS_MODULE,
|
||||
}
|
||||
var temperatures []TemperatureStat
|
||||
|
||||
C.open_smc()
|
||||
defer C.close_smc()
|
||||
|
||||
for _, key := range temperatureKeys {
|
||||
temperatures = append(temperatures, TemperatureStat{
|
||||
SensorKey: key,
|
||||
Temperature: float64(C.get_temperature(C.CString(key))),
|
||||
})
|
||||
}
|
||||
return temperatures, nil
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// +build darwin
|
||||
// +build !cgo
|
||||
|
||||
package host
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
)
|
||||
|
||||
func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
|
||||
return []TemperatureStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
+12
-28
@@ -8,58 +8,42 @@ import (
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
)
|
||||
|
||||
func Info() (*InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
func HostIDWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) (*InfoStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func BootTime() (uint64, error) {
|
||||
return BootTimeWithContext(context.Background())
|
||||
func numProcs(ctx context.Context) (uint64, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func BootTimeWithContext(ctx context.Context) (uint64, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func Uptime() (uint64, error) {
|
||||
return UptimeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func UptimeWithContext(ctx context.Context) (uint64, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func Users() ([]UserStat, error) {
|
||||
return UsersWithContext(context.Background())
|
||||
}
|
||||
|
||||
func UsersWithContext(ctx context.Context) ([]UserStat, error) {
|
||||
return []UserStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func Virtualization() (string, string, error) {
|
||||
return VirtualizationWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
|
||||
return "", "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func KernelVersion() (string, error) {
|
||||
return KernelVersionWithContext(context.Background())
|
||||
}
|
||||
|
||||
func KernelVersionWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func PlatformInformation() (string, string, string, error) {
|
||||
return PlatformInformationWithContext(context.Background())
|
||||
}
|
||||
|
||||
func PlatformInformationWithContext(ctx context.Context) (string, string, string, error) {
|
||||
return "", "", "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
|
||||
return []TemperatureStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func KernelArch() (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
+13
-107
@@ -7,12 +7,9 @@ import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
@@ -26,95 +23,20 @@ const (
|
||||
UTHostSize = 16
|
||||
)
|
||||
|
||||
func Info() (*InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
func HostIDWithContext(ctx context.Context) (string, error) {
|
||||
uuid, err := unix.Sysctl("kern.hostuuid")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.ToLower(uuid), err
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) (*InfoStat, error) {
|
||||
ret := &InfoStat{
|
||||
OS: runtime.GOOS,
|
||||
PlatformFamily: "freebsd",
|
||||
}
|
||||
|
||||
hostname, err := os.Hostname()
|
||||
if err == nil {
|
||||
ret.Hostname = hostname
|
||||
}
|
||||
|
||||
platform, family, version, err := PlatformInformation()
|
||||
if err == nil {
|
||||
ret.Platform = platform
|
||||
ret.PlatformFamily = family
|
||||
ret.PlatformVersion = version
|
||||
ret.KernelVersion = version
|
||||
}
|
||||
|
||||
system, role, err := Virtualization()
|
||||
if err == nil {
|
||||
ret.VirtualizationSystem = system
|
||||
ret.VirtualizationRole = role
|
||||
}
|
||||
|
||||
boot, err := BootTime()
|
||||
if err == nil {
|
||||
ret.BootTime = boot
|
||||
ret.Uptime = uptime(boot)
|
||||
}
|
||||
|
||||
procs, err := process.Pids()
|
||||
if err == nil {
|
||||
ret.Procs = uint64(len(procs))
|
||||
}
|
||||
|
||||
hostid, err := unix.Sysctl("kern.hostuuid")
|
||||
if err == nil && hostid != "" {
|
||||
ret.HostID = strings.ToLower(hostid)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// cachedBootTime must be accessed via atomic.Load/StoreUint64
|
||||
var cachedBootTime uint64
|
||||
|
||||
func BootTime() (uint64, error) {
|
||||
return BootTimeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func BootTimeWithContext(ctx context.Context) (uint64, error) {
|
||||
t := atomic.LoadUint64(&cachedBootTime)
|
||||
if t != 0 {
|
||||
return t, nil
|
||||
}
|
||||
buf, err := unix.SysctlRaw("kern.boottime")
|
||||
func numProcs(ctx context.Context) (uint64, error) {
|
||||
procs, err := process.PidsWithContext(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
tv := *(*syscall.Timeval)(unsafe.Pointer((&buf[0])))
|
||||
atomic.StoreUint64(&cachedBootTime, uint64(tv.Sec))
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func uptime(boot uint64) uint64 {
|
||||
return uint64(time.Now().Unix()) - boot
|
||||
}
|
||||
|
||||
func Uptime() (uint64, error) {
|
||||
return UptimeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func UptimeWithContext(ctx context.Context) (uint64, error) {
|
||||
boot, err := BootTime()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uptime(boot), nil
|
||||
}
|
||||
|
||||
func Users() ([]UserStat, error) {
|
||||
return UsersWithContext(context.Background())
|
||||
return uint64(len(procs)), nil
|
||||
}
|
||||
|
||||
func UsersWithContext(ctx context.Context) ([]UserStat, error) {
|
||||
@@ -143,11 +65,11 @@ func UsersWithContext(ctx context.Context) ([]UserStat, error) {
|
||||
b := buf[i*sizeOfUtmpx : (i+1)*sizeOfUtmpx]
|
||||
var u Utmpx
|
||||
br := bytes.NewReader(b)
|
||||
err := binary.Read(br, binary.LittleEndian, &u)
|
||||
err := binary.Read(br, binary.BigEndian, &u)
|
||||
if err != nil || u.Type != 4 {
|
||||
continue
|
||||
}
|
||||
sec := (binary.LittleEndian.Uint32(u.Tv.Sec[:])) / 2 // TODO:
|
||||
sec := math.Floor(float64(u.Tv) / 1000000)
|
||||
user := UserStat{
|
||||
User: common.IntToString(u.User[:]),
|
||||
Terminal: common.IntToString(u.Line[:]),
|
||||
@@ -162,10 +84,6 @@ func UsersWithContext(ctx context.Context) ([]UserStat, error) {
|
||||
|
||||
}
|
||||
|
||||
func PlatformInformation() (string, string, string, error) {
|
||||
return PlatformInformationWithContext(context.Background())
|
||||
}
|
||||
|
||||
func PlatformInformationWithContext(ctx context.Context) (string, string, string, error) {
|
||||
platform, err := unix.Sysctl("kern.ostype")
|
||||
if err != nil {
|
||||
@@ -180,10 +98,6 @@ func PlatformInformationWithContext(ctx context.Context) (string, string, string
|
||||
return strings.ToLower(platform), "", strings.ToLower(version), nil
|
||||
}
|
||||
|
||||
func Virtualization() (string, string, error) {
|
||||
return VirtualizationWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
|
||||
return "", "", common.ErrNotImplementedError
|
||||
}
|
||||
@@ -227,19 +141,11 @@ func getUsersFromUtmp(utmpfile string) ([]UserStat, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func SensorsTemperatures() ([]TemperatureStat, error) {
|
||||
return SensorsTemperaturesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
|
||||
return []TemperatureStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func KernelVersion() (string, error) {
|
||||
return KernelVersionWithContext(context.Background())
|
||||
}
|
||||
|
||||
func KernelVersionWithContext(ctx context.Context) (string, error) {
|
||||
_, _, version, err := PlatformInformation()
|
||||
_, _, version, err := PlatformInformationWithContext(ctx)
|
||||
return version, err
|
||||
}
|
||||
|
||||
+6
-12
@@ -1,4 +1,4 @@
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs types_freebsd.go
|
||||
|
||||
package host
|
||||
@@ -9,7 +9,7 @@ const (
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x4
|
||||
sizeofLongLong = 0x8
|
||||
sizeOfUtmpx = 197 // TODO why should 197
|
||||
sizeOfUtmpx = 0xc5
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -27,17 +27,11 @@ type Utmp struct {
|
||||
}
|
||||
|
||||
type Utmpx struct {
|
||||
Type int16
|
||||
Tv Timeval
|
||||
Type uint8
|
||||
Tv uint64
|
||||
Id [8]int8
|
||||
Pid int32
|
||||
Pid uint32
|
||||
User [32]int8
|
||||
Line [16]int8
|
||||
Host [125]int8
|
||||
// X__ut_spare [64]int8
|
||||
}
|
||||
|
||||
type Timeval struct {
|
||||
Sec [4]byte
|
||||
Usec [3]byte
|
||||
Host [128]int8
|
||||
}
|
||||
|
||||
+6
-13
@@ -1,4 +1,4 @@
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs types_freebsd.go
|
||||
|
||||
package host
|
||||
@@ -9,7 +9,7 @@ const (
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x8
|
||||
sizeofLongLong = 0x8
|
||||
sizeOfUtmpx = 197 // TODO: why should 197, not 0x118
|
||||
sizeOfUtmpx = 0xc5
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -27,18 +27,11 @@ type Utmp struct {
|
||||
}
|
||||
|
||||
type Utmpx struct {
|
||||
Type int16
|
||||
Tv Timeval
|
||||
Type uint8
|
||||
Tv uint64
|
||||
Id [8]int8
|
||||
Pid int32
|
||||
Pid uint32
|
||||
User [32]int8
|
||||
Line [16]int8
|
||||
Host [125]int8
|
||||
// Host [128]int8
|
||||
// X__ut_spare [64]int8
|
||||
}
|
||||
|
||||
type Timeval struct {
|
||||
Sec [4]byte
|
||||
Usec [3]byte
|
||||
Host [128]int8
|
||||
}
|
||||
|
||||
+6
-13
@@ -1,4 +1,4 @@
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs types_freebsd.go
|
||||
|
||||
package host
|
||||
@@ -9,7 +9,7 @@ const (
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x8
|
||||
sizeofLongLong = 0x8
|
||||
sizeOfUtmpx = 197 // TODO: why should 197, not 0x118
|
||||
sizeOfUtmpx = 0xc5
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -27,18 +27,11 @@ type Utmp struct {
|
||||
}
|
||||
|
||||
type Utmpx struct {
|
||||
Type int16
|
||||
Tv Timeval
|
||||
Type uint8
|
||||
Tv uint64
|
||||
Id [8]int8
|
||||
Pid int32
|
||||
Pid uint32
|
||||
User [32]int8
|
||||
Line [16]int8
|
||||
Host [125]int8
|
||||
// Host [128]int8
|
||||
// X__ut_spare [64]int8
|
||||
}
|
||||
|
||||
type Timeval struct {
|
||||
Sec [4]byte
|
||||
Usec [3]byte
|
||||
Host [128]int8
|
||||
}
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// +build freebsd
|
||||
// +build arm64
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs host/types_freebsd.go
|
||||
|
||||
package host
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x8
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x8
|
||||
sizeofLongLong = 0x8
|
||||
sizeOfUtmpx = 0xc5
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int64
|
||||
_C_long_long int64
|
||||
)
|
||||
|
||||
type Utmp struct {
|
||||
Line [8]int8
|
||||
Name [16]int8
|
||||
Host [16]int8
|
||||
Time int32
|
||||
}
|
||||
|
||||
type Utmpx struct {
|
||||
Type uint8
|
||||
Tv uint64
|
||||
Id [8]int8
|
||||
Pid uint32
|
||||
User [32]int8
|
||||
Line [16]int8
|
||||
Host [128]int8
|
||||
}
|
||||
+80
-286
@@ -12,13 +12,11 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type LSB struct {
|
||||
@@ -31,151 +29,52 @@ type LSB struct {
|
||||
// from utmp.h
|
||||
const USER_PROCESS = 7
|
||||
|
||||
func Info() (*InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) (*InfoStat, error) {
|
||||
ret := &InfoStat{
|
||||
OS: runtime.GOOS,
|
||||
}
|
||||
|
||||
hostname, err := os.Hostname()
|
||||
if err == nil {
|
||||
ret.Hostname = hostname
|
||||
}
|
||||
|
||||
platform, family, version, err := PlatformInformation()
|
||||
if err == nil {
|
||||
ret.Platform = platform
|
||||
ret.PlatformFamily = family
|
||||
ret.PlatformVersion = version
|
||||
}
|
||||
kernelVersion, err := KernelVersion()
|
||||
if err == nil {
|
||||
ret.KernelVersion = kernelVersion
|
||||
}
|
||||
|
||||
system, role, err := Virtualization()
|
||||
if err == nil {
|
||||
ret.VirtualizationSystem = system
|
||||
ret.VirtualizationRole = role
|
||||
}
|
||||
|
||||
boot, err := BootTime()
|
||||
if err == nil {
|
||||
ret.BootTime = boot
|
||||
ret.Uptime = uptime(boot)
|
||||
}
|
||||
|
||||
if numProcs, err := common.NumProcs(); err == nil {
|
||||
ret.Procs = numProcs
|
||||
}
|
||||
|
||||
func HostIDWithContext(ctx context.Context) (string, error) {
|
||||
sysProductUUID := common.HostSys("class/dmi/id/product_uuid")
|
||||
machineID := common.HostEtc("machine-id")
|
||||
procSysKernelRandomBootID := common.HostProc("sys/kernel/random/boot_id")
|
||||
switch {
|
||||
// In order to read this file, needs to be supported by kernel/arch and run as root
|
||||
// so having fallback is important
|
||||
case common.PathExists(sysProductUUID):
|
||||
lines, err := common.ReadLines(sysProductUUID)
|
||||
if err == nil && len(lines) > 0 && lines[0] != "" {
|
||||
ret.HostID = strings.ToLower(lines[0])
|
||||
break
|
||||
return strings.ToLower(lines[0]), nil
|
||||
}
|
||||
fallthrough
|
||||
// Fallback on GNU Linux systems with systemd, readable by everyone
|
||||
case common.PathExists(machineID):
|
||||
lines, err := common.ReadLines(machineID)
|
||||
if err == nil && len(lines) > 0 && len(lines[0]) == 32 {
|
||||
st := lines[0]
|
||||
return fmt.Sprintf("%s-%s-%s-%s-%s", st[0:8], st[8:12], st[12:16], st[16:20], st[20:32]), nil
|
||||
}
|
||||
fallthrough
|
||||
// Not stable between reboot, but better than nothing
|
||||
default:
|
||||
values, err := common.DoSysctrl("kernel.random.boot_id")
|
||||
if err == nil && len(values) == 1 && values[0] != "" {
|
||||
ret.HostID = strings.ToLower(values[0])
|
||||
lines, err := common.ReadLines(procSysKernelRandomBootID)
|
||||
if err == nil && len(lines) > 0 && lines[0] != "" {
|
||||
return strings.ToLower(lines[0]), nil
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// cachedBootTime must be accessed via atomic.Load/StoreUint64
|
||||
var cachedBootTime uint64
|
||||
|
||||
// BootTime returns the system boot time expressed in seconds since the epoch.
|
||||
func BootTime() (uint64, error) {
|
||||
return BootTimeWithContext(context.Background())
|
||||
func numProcs(ctx context.Context) (uint64, error) {
|
||||
return common.NumProcs()
|
||||
}
|
||||
|
||||
func BootTimeWithContext(ctx context.Context) (uint64, error) {
|
||||
t := atomic.LoadUint64(&cachedBootTime)
|
||||
if t != 0 {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
system, role, err := Virtualization()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
statFile := "stat"
|
||||
if system == "lxc" && role == "guest" {
|
||||
// if lxc, /proc/uptime is used.
|
||||
statFile = "uptime"
|
||||
} else if system == "docker" && role == "guest" {
|
||||
// also docker, guest
|
||||
statFile = "uptime"
|
||||
}
|
||||
|
||||
filename := common.HostProc(statFile)
|
||||
lines, err := common.ReadLines(filename)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if statFile == "stat" {
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "btime") {
|
||||
f := strings.Fields(line)
|
||||
if len(f) != 2 {
|
||||
return 0, fmt.Errorf("wrong btime format")
|
||||
}
|
||||
b, err := strconv.ParseInt(f[1], 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
t = uint64(b)
|
||||
atomic.StoreUint64(&cachedBootTime, t)
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
} else if statFile == "uptime" {
|
||||
if len(lines) != 1 {
|
||||
return 0, fmt.Errorf("wrong uptime format")
|
||||
}
|
||||
f := strings.Fields(lines[0])
|
||||
b, err := strconv.ParseFloat(f[0], 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
t = uint64(time.Now().Unix()) - uint64(b)
|
||||
atomic.StoreUint64(&cachedBootTime, t)
|
||||
return t, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("could not find btime")
|
||||
}
|
||||
|
||||
func uptime(boot uint64) uint64 {
|
||||
return uint64(time.Now().Unix()) - boot
|
||||
}
|
||||
|
||||
func Uptime() (uint64, error) {
|
||||
return UptimeWithContext(context.Background())
|
||||
return common.BootTimeWithContext(ctx)
|
||||
}
|
||||
|
||||
func UptimeWithContext(ctx context.Context) (uint64, error) {
|
||||
boot, err := BootTime()
|
||||
if err != nil {
|
||||
sysinfo := &unix.Sysinfo_t{}
|
||||
if err := unix.Sysinfo(sysinfo); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uptime(boot), nil
|
||||
}
|
||||
|
||||
func Users() ([]UserStat, error) {
|
||||
return UsersWithContext(context.Background())
|
||||
return uint64(sysinfo.Uptime), nil
|
||||
}
|
||||
|
||||
func UsersWithContext(ctx context.Context) ([]UserStat, error) {
|
||||
@@ -221,26 +120,6 @@ func UsersWithContext(ctx context.Context) ([]UserStat, error) {
|
||||
|
||||
}
|
||||
|
||||
func getOSRelease() (platform string, version string, err error) {
|
||||
contents, err := common.ReadLines(common.HostEtc("os-release"))
|
||||
if err != nil {
|
||||
return "", "", nil // return empty
|
||||
}
|
||||
for _, line := range contents {
|
||||
field := strings.Split(line, "=")
|
||||
if len(field) < 2 {
|
||||
continue
|
||||
}
|
||||
switch field[0] {
|
||||
case "ID": // use ID for lowercase
|
||||
platform = field[1]
|
||||
case "VERSION":
|
||||
version = field[1]
|
||||
}
|
||||
}
|
||||
return platform, version, nil
|
||||
}
|
||||
|
||||
func getLSB() (*LSB, error) {
|
||||
ret := &LSB{}
|
||||
if common.PathExists(common.HostEtc("lsb-release")) {
|
||||
@@ -265,7 +144,7 @@ func getLSB() (*LSB, error) {
|
||||
}
|
||||
}
|
||||
} else if common.PathExists("/usr/bin/lsb_release") {
|
||||
lsb_release, err := exec.LookPath("/usr/bin/lsb_release")
|
||||
lsb_release, err := exec.LookPath("lsb_release")
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
@@ -295,12 +174,7 @@ func getLSB() (*LSB, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func PlatformInformation() (platform string, family string, version string, err error) {
|
||||
return PlatformInformationWithContext(context.Background())
|
||||
}
|
||||
|
||||
func PlatformInformationWithContext(ctx context.Context) (platform string, family string, version string, err error) {
|
||||
|
||||
lsb, err := getLSB()
|
||||
if err != nil {
|
||||
lsb = &LSB{}
|
||||
@@ -339,7 +213,7 @@ func PlatformInformationWithContext(ctx context.Context) (platform string, famil
|
||||
platform = "debian"
|
||||
}
|
||||
contents, err := common.ReadLines(common.HostEtc("debian_version"))
|
||||
if err == nil {
|
||||
if err == nil && len(contents) > 0 && contents[0] != "" {
|
||||
version = contents[0]
|
||||
}
|
||||
}
|
||||
@@ -374,11 +248,11 @@ func PlatformInformationWithContext(ctx context.Context) (platform string, famil
|
||||
} else if common.PathExists(common.HostEtc("alpine-release")) {
|
||||
platform = "alpine"
|
||||
contents, err := common.ReadLines(common.HostEtc("alpine-release"))
|
||||
if err == nil && len(contents) > 0 {
|
||||
if err == nil && len(contents) > 0 && contents[0] != "" {
|
||||
version = contents[0]
|
||||
}
|
||||
} else if common.PathExists(common.HostEtc("os-release")) {
|
||||
p, v, err := getOSRelease()
|
||||
p, v, err := common.GetOSRelease()
|
||||
if err == nil {
|
||||
platform = p
|
||||
version = v
|
||||
@@ -405,9 +279,9 @@ func PlatformInformationWithContext(ctx context.Context) (platform string, famil
|
||||
family = "debian"
|
||||
case "fedora":
|
||||
family = "fedora"
|
||||
case "oracle", "centos", "redhat", "scientific", "enterpriseenterprise", "amazon", "xenserver", "cloudlinux", "ibm_powerkvm":
|
||||
case "oracle", "centos", "redhat", "scientific", "enterpriseenterprise", "amazon", "xenserver", "cloudlinux", "ibm_powerkvm", "rocky":
|
||||
family = "rhel"
|
||||
case "suse", "opensuse":
|
||||
case "suse", "opensuse", "opensuse-leap", "opensuse-tumbleweed", "opensuse-tumbleweed-kubic", "sles", "sled", "caasp":
|
||||
family = "suse"
|
||||
case "gentoo":
|
||||
family = "gentoo"
|
||||
@@ -421,30 +295,21 @@ func PlatformInformationWithContext(ctx context.Context) (platform string, famil
|
||||
family = "alpine"
|
||||
case "coreos":
|
||||
family = "coreos"
|
||||
case "solus":
|
||||
family = "solus"
|
||||
}
|
||||
|
||||
return platform, family, version, nil
|
||||
|
||||
}
|
||||
|
||||
func KernelVersion() (version string, err error) {
|
||||
return KernelVersionWithContext(context.Background())
|
||||
}
|
||||
|
||||
func KernelVersionWithContext(ctx context.Context) (version string, err error) {
|
||||
filename := common.HostProc("sys/kernel/osrelease")
|
||||
if common.PathExists(filename) {
|
||||
contents, err := common.ReadLines(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(contents) > 0 {
|
||||
version = contents[0]
|
||||
}
|
||||
var utsname unix.Utsname
|
||||
err = unix.Uname(&utsname)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return version, nil
|
||||
return string(utsname.Release[:bytes.IndexByte(utsname.Release[:], 0)]), nil
|
||||
}
|
||||
|
||||
func getSlackwareVersion(contents []string) string {
|
||||
@@ -496,115 +361,8 @@ func getSusePlatform(contents []string) string {
|
||||
return "suse"
|
||||
}
|
||||
|
||||
func Virtualization() (string, string, error) {
|
||||
return VirtualizationWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
|
||||
var system string
|
||||
var role string
|
||||
|
||||
filename := common.HostProc("xen")
|
||||
if common.PathExists(filename) {
|
||||
system = "xen"
|
||||
role = "guest" // assume guest
|
||||
|
||||
if common.PathExists(filepath.Join(filename, "capabilities")) {
|
||||
contents, err := common.ReadLines(filepath.Join(filename, "capabilities"))
|
||||
if err == nil {
|
||||
if common.StringsContains(contents, "control_d") {
|
||||
role = "host"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filename = common.HostProc("modules")
|
||||
if common.PathExists(filename) {
|
||||
contents, err := common.ReadLines(filename)
|
||||
if err == nil {
|
||||
if common.StringsContains(contents, "kvm") {
|
||||
system = "kvm"
|
||||
role = "host"
|
||||
} else if common.StringsContains(contents, "vboxdrv") {
|
||||
system = "vbox"
|
||||
role = "host"
|
||||
} else if common.StringsContains(contents, "vboxguest") {
|
||||
system = "vbox"
|
||||
role = "guest"
|
||||
} else if common.StringsContains(contents, "vmware") {
|
||||
system = "vmware"
|
||||
role = "guest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filename = common.HostProc("cpuinfo")
|
||||
if common.PathExists(filename) {
|
||||
contents, err := common.ReadLines(filename)
|
||||
if err == nil {
|
||||
if common.StringsContains(contents, "QEMU Virtual CPU") ||
|
||||
common.StringsContains(contents, "Common KVM processor") ||
|
||||
common.StringsContains(contents, "Common 32-bit KVM processor") {
|
||||
system = "kvm"
|
||||
role = "guest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filename = common.HostProc()
|
||||
if common.PathExists(filepath.Join(filename, "bc", "0")) {
|
||||
system = "openvz"
|
||||
role = "host"
|
||||
} else if common.PathExists(filepath.Join(filename, "vz")) {
|
||||
system = "openvz"
|
||||
role = "guest"
|
||||
}
|
||||
|
||||
// not use dmidecode because it requires root
|
||||
if common.PathExists(filepath.Join(filename, "self", "status")) {
|
||||
contents, err := common.ReadLines(filepath.Join(filename, "self", "status"))
|
||||
if err == nil {
|
||||
|
||||
if common.StringsContains(contents, "s_context:") ||
|
||||
common.StringsContains(contents, "VxID:") {
|
||||
system = "linux-vserver"
|
||||
}
|
||||
// TODO: guest or host
|
||||
}
|
||||
}
|
||||
|
||||
if common.PathExists(filepath.Join(filename, "self", "cgroup")) {
|
||||
contents, err := common.ReadLines(filepath.Join(filename, "self", "cgroup"))
|
||||
if err == nil {
|
||||
if common.StringsContains(contents, "lxc") {
|
||||
system = "lxc"
|
||||
role = "guest"
|
||||
} else if common.StringsContains(contents, "docker") {
|
||||
system = "docker"
|
||||
role = "guest"
|
||||
} else if common.StringsContains(contents, "machine-rkt") {
|
||||
system = "rkt"
|
||||
role = "guest"
|
||||
} else if common.PathExists("/usr/bin/lxc-version") {
|
||||
system = "lxc"
|
||||
role = "host"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if common.PathExists(common.HostEtc("os-release")) {
|
||||
p, _, err := getOSRelease()
|
||||
if err == nil && p == "coreos" {
|
||||
system = "rkt" // Is it true?
|
||||
role = "host"
|
||||
}
|
||||
}
|
||||
return system, role, nil
|
||||
}
|
||||
|
||||
func SensorsTemperatures() ([]TemperatureStat, error) {
|
||||
return SensorsTemperaturesWithContext(context.Background())
|
||||
return common.VirtualizationWithContext(ctx)
|
||||
}
|
||||
|
||||
func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
|
||||
@@ -621,6 +379,39 @@ func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, err
|
||||
return temperatures, err
|
||||
}
|
||||
}
|
||||
var warns Warnings
|
||||
|
||||
if len(files) == 0 { // handle distributions without hwmon, like raspbian #391, parse legacy thermal_zone files
|
||||
files, err = filepath.Glob(common.HostSys("/class/thermal/thermal_zone*/"))
|
||||
if err != nil {
|
||||
return temperatures, err
|
||||
}
|
||||
for _, file := range files {
|
||||
// Get the name of the temperature you are reading
|
||||
name, err := ioutil.ReadFile(filepath.Join(file, "type"))
|
||||
if err != nil {
|
||||
warns.Add(err)
|
||||
continue
|
||||
}
|
||||
// Get the temperature reading
|
||||
current, err := ioutil.ReadFile(filepath.Join(file, "temp"))
|
||||
if err != nil {
|
||||
warns.Add(err)
|
||||
continue
|
||||
}
|
||||
temperature, err := strconv.ParseInt(strings.TrimSpace(string(current)), 10, 64)
|
||||
if err != nil {
|
||||
warns.Add(err)
|
||||
continue
|
||||
}
|
||||
|
||||
temperatures = append(temperatures, TemperatureStat{
|
||||
SensorKey: strings.TrimSpace(string(name)),
|
||||
Temperature: float64(temperature) / 1000.0,
|
||||
})
|
||||
}
|
||||
return temperatures, warns.Reference()
|
||||
}
|
||||
|
||||
// example directory
|
||||
// device/ temp1_crit_alarm temp2_crit_alarm temp3_crit_alarm temp4_crit_alarm temp5_crit_alarm temp6_crit_alarm temp7_crit_alarm
|
||||
@@ -643,19 +434,22 @@ func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, err
|
||||
label = fmt.Sprintf("%s_", strings.Join(strings.Split(strings.TrimSpace(strings.ToLower(string(c))), " "), ""))
|
||||
}
|
||||
|
||||
// Get the name of the tempearture you are reading
|
||||
// Get the name of the temperature you are reading
|
||||
name, err := ioutil.ReadFile(filepath.Join(filepath.Dir(file), "name"))
|
||||
if err != nil {
|
||||
return temperatures, err
|
||||
warns.Add(err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the temperature reading
|
||||
current, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return temperatures, err
|
||||
warns.Add(err)
|
||||
continue
|
||||
}
|
||||
temperature, err := strconv.ParseFloat(strings.TrimSpace(string(current)), 64)
|
||||
if err != nil {
|
||||
warns.Add(err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -665,5 +459,5 @@ func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, err
|
||||
Temperature: temperature / 1000.0,
|
||||
})
|
||||
}
|
||||
return temperatures, nil
|
||||
return temperatures, warns.Reference()
|
||||
}
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types_linux.go
|
||||
|
||||
package host
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x8
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x8
|
||||
sizeofLongLong = 0x8
|
||||
sizeOfUtmp = 0x180
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int64
|
||||
_C_long_long int64
|
||||
)
|
||||
|
||||
type utmp struct {
|
||||
Type int16
|
||||
Pid int32
|
||||
Line [32]int8
|
||||
Id [4]int8
|
||||
User [32]int8
|
||||
Host [256]int8
|
||||
Exit exit_status
|
||||
Session int32
|
||||
Tv _Ctype_struct___0
|
||||
Addr_v6 [4]int32
|
||||
X__glibc_reserved [20]uint8
|
||||
}
|
||||
type exit_status struct {
|
||||
Termination int16
|
||||
Exit int16
|
||||
}
|
||||
type timeval struct {
|
||||
Sec int64
|
||||
Usec int64
|
||||
}
|
||||
|
||||
type _Ctype_struct___0 struct {
|
||||
Sec int32
|
||||
Usec int32
|
||||
}
|
||||
+12
-103
@@ -8,15 +8,12 @@ import (
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
"github.com/shirou/gopsutil/process"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -25,119 +22,39 @@ const (
|
||||
UTHostSize = 16
|
||||
)
|
||||
|
||||
func Info() (*InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
func HostIDWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) (*InfoStat, error) {
|
||||
ret := &InfoStat{
|
||||
OS: runtime.GOOS,
|
||||
PlatformFamily: "openbsd",
|
||||
}
|
||||
|
||||
hostname, err := os.Hostname()
|
||||
if err == nil {
|
||||
ret.Hostname = hostname
|
||||
}
|
||||
|
||||
platform, family, version, err := PlatformInformation()
|
||||
if err == nil {
|
||||
ret.Platform = platform
|
||||
ret.PlatformFamily = family
|
||||
ret.PlatformVersion = version
|
||||
}
|
||||
system, role, err := Virtualization()
|
||||
if err == nil {
|
||||
ret.VirtualizationSystem = system
|
||||
ret.VirtualizationRole = role
|
||||
}
|
||||
|
||||
procs, err := process.Pids()
|
||||
if err == nil {
|
||||
ret.Procs = uint64(len(procs))
|
||||
}
|
||||
|
||||
boot, err := BootTime()
|
||||
if err == nil {
|
||||
ret.BootTime = boot
|
||||
ret.Uptime = uptime(boot)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func BootTime() (uint64, error) {
|
||||
return BootTimeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func BootTimeWithContext(ctx context.Context) (uint64, error) {
|
||||
val, err := common.DoSysctrl("kern.boottime")
|
||||
func numProcs(ctx context.Context) (uint64, error) {
|
||||
procs, err := process.PidsWithContext(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
boottime, err := strconv.ParseUint(val[0], 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return boottime, nil
|
||||
}
|
||||
|
||||
func uptime(boot uint64) uint64 {
|
||||
return uint64(time.Now().Unix()) - boot
|
||||
}
|
||||
|
||||
func Uptime() (uint64, error) {
|
||||
return UptimeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func UptimeWithContext(ctx context.Context) (uint64, error) {
|
||||
boot, err := BootTime()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uptime(boot), nil
|
||||
}
|
||||
|
||||
func PlatformInformation() (string, string, string, error) {
|
||||
return PlatformInformationWithContext(context.Background())
|
||||
return uint64(len(procs)), nil
|
||||
}
|
||||
|
||||
func PlatformInformationWithContext(ctx context.Context) (string, string, string, error) {
|
||||
platform := ""
|
||||
family := ""
|
||||
version := ""
|
||||
uname, err := exec.LookPath("uname")
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
out, err := invoke.CommandWithContext(ctx, uname, "-s")
|
||||
p, err := unix.Sysctl("kern.ostype")
|
||||
if err == nil {
|
||||
platform = strings.ToLower(strings.TrimSpace(string(out)))
|
||||
platform = strings.ToLower(p)
|
||||
}
|
||||
|
||||
out, err = invoke.CommandWithContext(ctx, uname, "-r")
|
||||
v, err := unix.Sysctl("kern.osrelease")
|
||||
if err == nil {
|
||||
version = strings.ToLower(strings.TrimSpace(string(out)))
|
||||
version = strings.ToLower(v)
|
||||
}
|
||||
|
||||
return platform, family, version, nil
|
||||
}
|
||||
|
||||
func Virtualization() (string, string, error) {
|
||||
return VirtualizationWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
|
||||
return "", "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func Users() ([]UserStat, error) {
|
||||
return UsersWithContext(context.Background())
|
||||
}
|
||||
|
||||
func UsersWithContext(ctx context.Context) ([]UserStat, error) {
|
||||
var ret []UserStat
|
||||
utmpfile := "/var/run/utmp"
|
||||
@@ -161,7 +78,7 @@ func UsersWithContext(ctx context.Context) ([]UserStat, error) {
|
||||
var u Utmp
|
||||
br := bytes.NewReader(b)
|
||||
err := binary.Read(br, binary.LittleEndian, &u)
|
||||
if err != nil || u.Time == 0 {
|
||||
if err != nil || u.Time == 0 || u.Name[0] == 0 {
|
||||
continue
|
||||
}
|
||||
user := UserStat{
|
||||
@@ -177,19 +94,11 @@ func UsersWithContext(ctx context.Context) ([]UserStat, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func SensorsTemperatures() ([]TemperatureStat, error) {
|
||||
return SensorsTemperaturesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
|
||||
return []TemperatureStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func KernelVersion() (string, error) {
|
||||
return KernelVersionWithContext(context.Background())
|
||||
}
|
||||
|
||||
func KernelVersionWithContext(ctx context.Context) (string, error) {
|
||||
_, _, version, err := PlatformInformation()
|
||||
_, _, version, err := PlatformInformationWithContext(ctx)
|
||||
return version, err
|
||||
}
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// +build openbsd
|
||||
// +build 386
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs host/types_openbsd.go
|
||||
|
||||
package host
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x4
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x4
|
||||
sizeofLongLong = 0x8
|
||||
sizeOfUtmp = 0x130
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int32
|
||||
_C_long_long int64
|
||||
)
|
||||
|
||||
type Utmp struct {
|
||||
Line [8]int8
|
||||
Name [32]int8
|
||||
Host [256]int8
|
||||
Time int64
|
||||
}
|
||||
type Timeval struct {
|
||||
Sec int64
|
||||
Usec int32
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// +build openbsd
|
||||
// +build arm64
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs host/types_openbsd.go
|
||||
|
||||
package host
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x8
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x8
|
||||
sizeofLongLong = 0x8
|
||||
sizeOfUtmp = 0x130
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int64
|
||||
_C_long_long int64
|
||||
)
|
||||
|
||||
type Utmp struct {
|
||||
Line [8]int8
|
||||
Name [32]int8
|
||||
Host [256]int8
|
||||
Time int64
|
||||
}
|
||||
type Timeval struct {
|
||||
Sec int64
|
||||
Usec int64
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// +build linux freebsd openbsd darwin solaris
|
||||
|
||||
package host
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func KernelArch() (string, error) {
|
||||
var utsname unix.Utsname
|
||||
err := unix.Uname(&utsname)
|
||||
return string(utsname.Machine[:bytes.IndexByte(utsname.Machine[:], 0)]), err
|
||||
}
|
||||
+79
-143
@@ -9,83 +9,21 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
)
|
||||
|
||||
func Info() (*InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) (*InfoStat, error) {
|
||||
result := &InfoStat{
|
||||
OS: runtime.GOOS,
|
||||
}
|
||||
|
||||
hostname, err := os.Hostname()
|
||||
func HostIDWithContext(ctx context.Context) (string, error) {
|
||||
platform, err := parseReleaseFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Hostname = hostname
|
||||
|
||||
// Parse versions from output of `uname(1)`
|
||||
uname, err := exec.LookPath("/usr/bin/uname")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
out, err := invoke.CommandWithContext(ctx, uname, "-srv")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fields := strings.Fields(string(out))
|
||||
if len(fields) >= 1 {
|
||||
result.PlatformFamily = fields[0]
|
||||
}
|
||||
if len(fields) >= 2 {
|
||||
result.KernelVersion = fields[1]
|
||||
}
|
||||
if len(fields) == 3 {
|
||||
result.PlatformVersion = fields[2]
|
||||
}
|
||||
|
||||
// Find distribution name from /etc/release
|
||||
fh, err := os.Open("/etc/release")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
sc := bufio.NewScanner(fh)
|
||||
if sc.Scan() {
|
||||
line := strings.TrimSpace(sc.Text())
|
||||
switch {
|
||||
case strings.HasPrefix(line, "SmartOS"):
|
||||
result.Platform = "SmartOS"
|
||||
case strings.HasPrefix(line, "OpenIndiana"):
|
||||
result.Platform = "OpenIndiana"
|
||||
case strings.HasPrefix(line, "OmniOS"):
|
||||
result.Platform = "OmniOS"
|
||||
case strings.HasPrefix(line, "Open Storage"):
|
||||
result.Platform = "NexentaStor"
|
||||
case strings.HasPrefix(line, "Solaris"):
|
||||
result.Platform = "Solaris"
|
||||
case strings.HasPrefix(line, "Oracle Solaris"):
|
||||
result.Platform = "Solaris"
|
||||
default:
|
||||
result.Platform = strings.Fields(line)[0]
|
||||
}
|
||||
}
|
||||
|
||||
switch result.Platform {
|
||||
case "SmartOS":
|
||||
if platform == "SmartOS" {
|
||||
// If everything works, use the current zone ID as the HostID if present.
|
||||
zonename, err := exec.LookPath("/usr/bin/zonename")
|
||||
zonename, err := exec.LookPath("zonename")
|
||||
if err == nil {
|
||||
out, err := invoke.CommandWithContext(ctx, zonename)
|
||||
if err == nil {
|
||||
@@ -97,61 +35,47 @@ func InfoWithContext(ctx context.Context) (*InfoStat, error) {
|
||||
if line == "global" {
|
||||
hostname, err := os.Hostname()
|
||||
if err == nil {
|
||||
result.HostID = hostname
|
||||
return hostname, nil
|
||||
}
|
||||
} else {
|
||||
result.HostID = strings.TrimSpace(line)
|
||||
break
|
||||
return strings.TrimSpace(line), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If HostID is still empty, use hostid(1), which can lie to callers but at
|
||||
// If HostID is still unknown, use hostid(1), which can lie to callers but at
|
||||
// this point there are no hardware facilities available. This behavior
|
||||
// matches that of other supported OSes.
|
||||
if result.HostID == "" {
|
||||
hostID, err := exec.LookPath("/usr/bin/hostid")
|
||||
hostID, err := exec.LookPath("hostid")
|
||||
if err == nil {
|
||||
out, err := invoke.CommandWithContext(ctx, hostID)
|
||||
if err == nil {
|
||||
out, err := invoke.CommandWithContext(ctx, hostID)
|
||||
if err == nil {
|
||||
sc := bufio.NewScanner(bytes.NewReader(out))
|
||||
for sc.Scan() {
|
||||
line := sc.Text()
|
||||
result.HostID = strings.TrimSpace(line)
|
||||
break
|
||||
}
|
||||
sc := bufio.NewScanner(bytes.NewReader(out))
|
||||
for sc.Scan() {
|
||||
line := sc.Text()
|
||||
return strings.TrimSpace(line), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the boot time and calculate uptime relative to it
|
||||
bootTime, err := BootTime()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.BootTime = bootTime
|
||||
result.Uptime = uptimeSince(bootTime)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Count number of processes based on the number of entries in /proc
|
||||
// Count number of processes based on the number of entries in /proc
|
||||
func numProcs(ctx context.Context) (uint64, error) {
|
||||
dirs, err := ioutil.ReadDir("/proc")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return 0, err
|
||||
}
|
||||
result.Procs = uint64(len(dirs))
|
||||
|
||||
return result, nil
|
||||
return uint64(len(dirs)), nil
|
||||
}
|
||||
|
||||
var kstatMatch = regexp.MustCompile(`([^\s]+)[\s]+([^\s]*)`)
|
||||
|
||||
func BootTime() (uint64, error) {
|
||||
return BootTimeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func BootTimeWithContext(ctx context.Context) (uint64, error) {
|
||||
kstat, err := exec.LookPath("/usr/bin/kstat")
|
||||
kstat, err := exec.LookPath("kstat")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -169,80 +93,92 @@ func BootTimeWithContext(ctx context.Context) (uint64, error) {
|
||||
return strconv.ParseUint(kstats[0][2], 10, 64)
|
||||
}
|
||||
|
||||
func Uptime() (uint64, error) {
|
||||
return UptimeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func UptimeWithContext(ctx context.Context) (uint64, error) {
|
||||
bootTime, err := BootTime()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uptimeSince(bootTime), nil
|
||||
}
|
||||
|
||||
func uptimeSince(since uint64) uint64 {
|
||||
return uint64(time.Now().Unix()) - since
|
||||
}
|
||||
|
||||
func Users() ([]UserStat, error) {
|
||||
return UsersWithContext(context.Background())
|
||||
return timeSince(bootTime), nil
|
||||
}
|
||||
|
||||
func UsersWithContext(ctx context.Context) ([]UserStat, error) {
|
||||
return []UserStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func SensorsTemperatures() ([]TemperatureStat, error) {
|
||||
return SensorsTemperaturesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
|
||||
return []TemperatureStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func Virtualization() (string, string, error) {
|
||||
return VirtualizationWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
|
||||
return "", "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func KernelVersion() (string, error) {
|
||||
return KernelVersionWithContext(context.Background())
|
||||
}
|
||||
|
||||
func KernelVersionWithContext(ctx context.Context) (string, error) {
|
||||
// Parse versions from output of `uname(1)`
|
||||
uname, err := exec.LookPath("/usr/bin/uname")
|
||||
// Find distribution name from /etc/release
|
||||
func parseReleaseFile() (string, error) {
|
||||
b, err := ioutil.ReadFile("/etc/release")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
s := string(b)
|
||||
s = strings.TrimSpace(s)
|
||||
|
||||
out, err := invoke.CommandWithContext(ctx, uname, "-srv")
|
||||
if err != nil {
|
||||
return "", err
|
||||
var platform string
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(s, "SmartOS"):
|
||||
platform = "SmartOS"
|
||||
case strings.HasPrefix(s, "OpenIndiana"):
|
||||
platform = "OpenIndiana"
|
||||
case strings.HasPrefix(s, "OmniOS"):
|
||||
platform = "OmniOS"
|
||||
case strings.HasPrefix(s, "Open Storage"):
|
||||
platform = "NexentaStor"
|
||||
case strings.HasPrefix(s, "Solaris"):
|
||||
platform = "Solaris"
|
||||
case strings.HasPrefix(s, "Oracle Solaris"):
|
||||
platform = "Solaris"
|
||||
default:
|
||||
platform = strings.Fields(s)[0]
|
||||
}
|
||||
|
||||
fields := strings.Fields(string(out))
|
||||
if len(fields) >= 2 {
|
||||
return fields[1], nil
|
||||
}
|
||||
return "", fmt.Errorf("could not get kernel version")
|
||||
return platform, nil
|
||||
}
|
||||
|
||||
func PlatformInformation() (platform string, family string, version string, err error) {
|
||||
return PlatformInformationWithContext(context.Background())
|
||||
}
|
||||
|
||||
func PlatformInformationWithContext(ctx context.Context) (platform string, family string, version string, err error) {
|
||||
/* This is not finished yet at all. Please contribute! */
|
||||
|
||||
version, err = KernelVersion()
|
||||
// parseUnameOutput returns platformFamily, kernelVersion and platformVersion
|
||||
func parseUnameOutput(ctx context.Context) (string, string, string, error) {
|
||||
uname, err := exec.LookPath("uname")
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
return "solaris", "solaris", version, nil
|
||||
out, err := invoke.CommandWithContext(ctx, uname, "-srv")
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
fields := strings.Fields(string(out))
|
||||
if len(fields) < 3 {
|
||||
return "", "", "", fmt.Errorf("malformed `uname` output")
|
||||
}
|
||||
|
||||
return fields[0], fields[1], fields[2], nil
|
||||
}
|
||||
|
||||
func KernelVersionWithContext(ctx context.Context) (string, error) {
|
||||
_, kernelVersion, _, err := parseUnameOutput(ctx)
|
||||
return kernelVersion, err
|
||||
}
|
||||
|
||||
func PlatformInformationWithContext(ctx context.Context) (string, string, string, error) {
|
||||
platform, err := parseReleaseFile()
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
platformFamily, _, platformVersion, err := parseUnameOutput(ctx)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
return platform, platformFamily, platformVersion, nil
|
||||
}
|
||||
|
||||
+75
-92
@@ -6,17 +6,16 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/StackExchange/wmi"
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
process "github.com/shirou/gopsutil/process"
|
||||
"github.com/shirou/gopsutil/process"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
@@ -24,6 +23,7 @@ var (
|
||||
procGetSystemTimeAsFileTime = common.Modkernel32.NewProc("GetSystemTimeAsFileTime")
|
||||
procGetTickCount32 = common.Modkernel32.NewProc("GetTickCount")
|
||||
procGetTickCount64 = common.Modkernel32.NewProc("GetTickCount64")
|
||||
procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo")
|
||||
procRtlGetVersion = common.ModNt.NewProc("RtlGetVersion")
|
||||
)
|
||||
|
||||
@@ -42,6 +42,20 @@ type osVersionInfoExW struct {
|
||||
wReserved uint8
|
||||
}
|
||||
|
||||
type systemInfo struct {
|
||||
wProcessorArchitecture uint16
|
||||
wReserved uint16
|
||||
dwPageSize uint32
|
||||
lpMinimumApplicationAddress uintptr
|
||||
lpMaximumApplicationAddress uintptr
|
||||
dwActiveProcessorMask uintptr
|
||||
dwNumberOfProcessors uint32
|
||||
dwProcessorType uint32
|
||||
dwAllocationGranularity uint32
|
||||
wProcessorLevel uint16
|
||||
wProcessorRevision uint16
|
||||
}
|
||||
|
||||
type msAcpi_ThermalZoneTemperature struct {
|
||||
Active bool
|
||||
CriticalTripPoint uint32
|
||||
@@ -49,59 +63,7 @@ type msAcpi_ThermalZoneTemperature struct {
|
||||
InstanceName string
|
||||
}
|
||||
|
||||
func Info() (*InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) (*InfoStat, error) {
|
||||
ret := &InfoStat{
|
||||
OS: runtime.GOOS,
|
||||
}
|
||||
|
||||
{
|
||||
hostname, err := os.Hostname()
|
||||
if err == nil {
|
||||
ret.Hostname = hostname
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
platform, family, version, err := PlatformInformationWithContext(ctx)
|
||||
if err == nil {
|
||||
ret.Platform = platform
|
||||
ret.PlatformFamily = family
|
||||
ret.PlatformVersion = version
|
||||
} else {
|
||||
return ret, err
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
boot, err := BootTime()
|
||||
if err == nil {
|
||||
ret.BootTime = boot
|
||||
ret.Uptime, _ = Uptime()
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
hostID, err := getMachineGuid()
|
||||
if err == nil {
|
||||
ret.HostID = strings.ToLower(hostID)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
procs, err := process.Pids()
|
||||
if err == nil {
|
||||
ret.Procs = uint64(len(procs))
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getMachineGuid() (string, error) {
|
||||
func HostIDWithContext(ctx context.Context) (string, error) {
|
||||
// there has been reports of issues on 32bit using golang.org/x/sys/windows/registry, see https://github.com/shirou/gopsutil/pull/312#issuecomment-277422612
|
||||
// for rationale of using windows.RegOpenKeyEx/RegQueryValueEx instead of registry.OpenKey/GetStringValue
|
||||
var h windows.Handle
|
||||
@@ -128,11 +90,15 @@ func getMachineGuid() (string, error) {
|
||||
return "", fmt.Errorf("HostID incorrect: %q\n", hostID)
|
||||
}
|
||||
|
||||
return hostID, nil
|
||||
return strings.ToLower(hostID), nil
|
||||
}
|
||||
|
||||
func Uptime() (uint64, error) {
|
||||
return UptimeWithContext(context.Background())
|
||||
func numProcs(ctx context.Context) (uint64, error) {
|
||||
procs, err := process.PidsWithContext(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint64(len(procs)), nil
|
||||
}
|
||||
|
||||
func UptimeWithContext(ctx context.Context) (uint64, error) {
|
||||
@@ -148,17 +114,9 @@ func UptimeWithContext(ctx context.Context) (uint64, error) {
|
||||
return uint64((time.Duration(r1) * time.Millisecond).Seconds()), nil
|
||||
}
|
||||
|
||||
func bootTimeFromUptime(up uint64) uint64 {
|
||||
return uint64(time.Now().Unix()) - up
|
||||
}
|
||||
|
||||
// cachedBootTime must be accessed via atomic.Load/StoreUint64
|
||||
var cachedBootTime uint64
|
||||
|
||||
func BootTime() (uint64, error) {
|
||||
return BootTimeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func BootTimeWithContext(ctx context.Context) (uint64, error) {
|
||||
t := atomic.LoadUint64(&cachedBootTime)
|
||||
if t != 0 {
|
||||
@@ -168,15 +126,11 @@ func BootTimeWithContext(ctx context.Context) (uint64, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
t = bootTimeFromUptime(up)
|
||||
t = timeSince(up)
|
||||
atomic.StoreUint64(&cachedBootTime, t)
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func PlatformInformation() (platform string, family string, version string, err error) {
|
||||
return PlatformInformationWithContext(context.Background())
|
||||
}
|
||||
|
||||
func PlatformInformationWithContext(ctx context.Context) (platform string, family string, version string, err error) {
|
||||
// GetVersionEx lies on Windows 8.1 and returns as Windows 8 if we don't declare compatibility in manifest
|
||||
// RtlGetVersion bypasses this lying layer and returns the true Windows version
|
||||
@@ -190,7 +144,7 @@ func PlatformInformationWithContext(ctx context.Context) (platform string, famil
|
||||
}
|
||||
|
||||
// Platform
|
||||
var h windows.Handle // like getMachineGuid(), we query the registry using the raw windows.RegOpenKeyEx/RegQueryValueEx
|
||||
var h windows.Handle // like HostIDWithContext(), we query the registry using the raw windows.RegOpenKeyEx/RegQueryValueEx
|
||||
err = windows.RegOpenKeyEx(windows.HKEY_LOCAL_MACHINE, windows.StringToUTF16Ptr(`SOFTWARE\Microsoft\Windows NT\CurrentVersion`), 0, windows.KEY_READ|windows.KEY_WOW64_64KEY, &h)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -208,6 +162,19 @@ func PlatformInformationWithContext(ctx context.Context) (platform string, famil
|
||||
return
|
||||
}
|
||||
platform = windows.UTF16ToString(regBuf[:])
|
||||
if strings.Contains(platform, "Windows 10") { // check build number to determine whether it's actually Windows 11
|
||||
err = windows.RegQueryValueEx(h, windows.StringToUTF16Ptr(`CurrentBuildNumber`), nil, &valType, nil, &bufLen)
|
||||
if err == nil {
|
||||
regBuf = make([]uint16, bufLen/2+1)
|
||||
err = windows.RegQueryValueEx(h, windows.StringToUTF16Ptr(`CurrentBuildNumber`), nil, &valType, (*byte)(unsafe.Pointer(®Buf[0])), &bufLen)
|
||||
if err == nil {
|
||||
buildNumberStr := windows.UTF16ToString(regBuf[:])
|
||||
if buildNumber, err := strconv.Atoi(buildNumberStr); err == nil && buildNumber >= 22000 {
|
||||
platform = strings.Replace(platform, "Windows 10", "Windows 11", 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !strings.HasPrefix(platform, "Microsoft") {
|
||||
platform = "Microsoft " + platform
|
||||
}
|
||||
@@ -236,18 +203,10 @@ func PlatformInformationWithContext(ctx context.Context) (platform string, famil
|
||||
return platform, family, version, nil
|
||||
}
|
||||
|
||||
func Users() ([]UserStat, error) {
|
||||
return UsersWithContext(context.Background())
|
||||
}
|
||||
|
||||
func UsersWithContext(ctx context.Context) ([]UserStat, error) {
|
||||
var ret []UserStat
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func SensorsTemperatures() ([]TemperatureStat, error) {
|
||||
return SensorsTemperaturesWithContext(context.Background())
|
||||
return ret, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
|
||||
@@ -277,19 +236,43 @@ func kelvinToCelsius(temp uint32, n int) float64 {
|
||||
return math.Trunc((t+0.5/n10)*n10) / n10
|
||||
}
|
||||
|
||||
func Virtualization() (string, string, error) {
|
||||
return VirtualizationWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
|
||||
return "", "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func KernelVersion() (string, error) {
|
||||
return KernelVersionWithContext(context.Background())
|
||||
}
|
||||
|
||||
func KernelVersionWithContext(ctx context.Context) (string, error) {
|
||||
_, _, version, err := PlatformInformation()
|
||||
_, _, version, err := PlatformInformationWithContext(ctx)
|
||||
return version, err
|
||||
}
|
||||
|
||||
func KernelArch() (string, error) {
|
||||
var systemInfo systemInfo
|
||||
procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo)))
|
||||
|
||||
const (
|
||||
PROCESSOR_ARCHITECTURE_INTEL = 0
|
||||
PROCESSOR_ARCHITECTURE_ARM = 5
|
||||
PROCESSOR_ARCHITECTURE_ARM64 = 12
|
||||
PROCESSOR_ARCHITECTURE_IA64 = 6
|
||||
PROCESSOR_ARCHITECTURE_AMD64 = 9
|
||||
)
|
||||
switch systemInfo.wProcessorArchitecture {
|
||||
case PROCESSOR_ARCHITECTURE_INTEL:
|
||||
if systemInfo.wProcessorLevel < 3 {
|
||||
return "i386", nil
|
||||
}
|
||||
if systemInfo.wProcessorLevel > 6 {
|
||||
return "i686", nil
|
||||
}
|
||||
return fmt.Sprintf("i%d86", systemInfo.wProcessorLevel), nil
|
||||
case PROCESSOR_ARCHITECTURE_ARM:
|
||||
return "arm", nil
|
||||
case PROCESSOR_ARCHITECTURE_ARM64:
|
||||
return "aarch64", nil
|
||||
case PROCESSOR_ARCHITECTURE_IA64:
|
||||
return "ia64", nil
|
||||
case PROCESSOR_ARCHITECTURE_AMD64:
|
||||
return "x86_64", nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "smc_darwin.h"
|
||||
|
||||
#define IOSERVICE_SMC "AppleSMC"
|
||||
#define IOSERVICE_MODEL "IOPlatformExpertDevice"
|
||||
|
||||
#define DATA_TYPE_SP78 "sp78"
|
||||
|
||||
typedef enum {
|
||||
kSMCUserClientOpen = 0,
|
||||
kSMCUserClientClose = 1,
|
||||
kSMCHandleYPCEvent = 2,
|
||||
kSMCReadKey = 5,
|
||||
kSMCWriteKey = 6,
|
||||
kSMCGetKeyCount = 7,
|
||||
kSMCGetKeyFromIndex = 8,
|
||||
kSMCGetKeyInfo = 9,
|
||||
} selector_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned char major;
|
||||
unsigned char minor;
|
||||
unsigned char build;
|
||||
unsigned char reserved;
|
||||
unsigned short release;
|
||||
} SMCVersion;
|
||||
|
||||
typedef struct {
|
||||
uint16_t version;
|
||||
uint16_t length;
|
||||
uint32_t cpuPLimit;
|
||||
uint32_t gpuPLimit;
|
||||
uint32_t memPLimit;
|
||||
} SMCPLimitData;
|
||||
|
||||
typedef struct {
|
||||
IOByteCount data_size;
|
||||
uint32_t data_type;
|
||||
uint8_t data_attributes;
|
||||
} SMCKeyInfoData;
|
||||
|
||||
typedef struct {
|
||||
uint32_t key;
|
||||
SMCVersion vers;
|
||||
SMCPLimitData p_limit_data;
|
||||
SMCKeyInfoData key_info;
|
||||
uint8_t result;
|
||||
uint8_t status;
|
||||
uint8_t data8;
|
||||
uint32_t data32;
|
||||
uint8_t bytes[32];
|
||||
} SMCParamStruct;
|
||||
|
||||
typedef enum {
|
||||
kSMCSuccess = 0,
|
||||
kSMCError = 1,
|
||||
kSMCKeyNotFound = 0x84,
|
||||
} kSMC_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t data[32];
|
||||
uint32_t data_type;
|
||||
uint32_t data_size;
|
||||
kSMC_t kSMC;
|
||||
} smc_return_t;
|
||||
|
||||
static const int SMC_KEY_SIZE = 4; // number of characters in an SMC key.
|
||||
static io_connect_t conn; // our connection to the SMC.
|
||||
|
||||
kern_return_t open_smc(void) {
|
||||
kern_return_t result;
|
||||
io_service_t service;
|
||||
|
||||
service = IOServiceGetMatchingService(kIOMasterPortDefault,
|
||||
IOServiceMatching(IOSERVICE_SMC));
|
||||
if (service == 0) {
|
||||
// Note: IOServiceMatching documents 0 on failure
|
||||
printf("ERROR: %s NOT FOUND\n", IOSERVICE_SMC);
|
||||
return kIOReturnError;
|
||||
}
|
||||
|
||||
result = IOServiceOpen(service, mach_task_self(), 0, &conn);
|
||||
IOObjectRelease(service);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
kern_return_t close_smc(void) { return IOServiceClose(conn); }
|
||||
|
||||
static uint32_t to_uint32(char *key) {
|
||||
uint32_t ans = 0;
|
||||
uint32_t shift = 24;
|
||||
|
||||
if (strlen(key) != SMC_KEY_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < SMC_KEY_SIZE; i++) {
|
||||
ans += key[i] << shift;
|
||||
shift -= 8;
|
||||
}
|
||||
|
||||
return ans;
|
||||
}
|
||||
|
||||
static kern_return_t call_smc(SMCParamStruct *input, SMCParamStruct *output) {
|
||||
kern_return_t result;
|
||||
size_t input_cnt = sizeof(SMCParamStruct);
|
||||
size_t output_cnt = sizeof(SMCParamStruct);
|
||||
|
||||
result = IOConnectCallStructMethod(conn, kSMCHandleYPCEvent, input, input_cnt,
|
||||
output, &output_cnt);
|
||||
|
||||
if (result != kIOReturnSuccess) {
|
||||
result = err_get_code(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static kern_return_t read_smc(char *key, smc_return_t *result_smc) {
|
||||
kern_return_t result;
|
||||
SMCParamStruct input;
|
||||
SMCParamStruct output;
|
||||
|
||||
memset(&input, 0, sizeof(SMCParamStruct));
|
||||
memset(&output, 0, sizeof(SMCParamStruct));
|
||||
memset(result_smc, 0, sizeof(smc_return_t));
|
||||
|
||||
input.key = to_uint32(key);
|
||||
input.data8 = kSMCGetKeyInfo;
|
||||
|
||||
result = call_smc(&input, &output);
|
||||
result_smc->kSMC = output.result;
|
||||
|
||||
if (result != kIOReturnSuccess || output.result != kSMCSuccess) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result_smc->data_size = output.key_info.data_size;
|
||||
result_smc->data_type = output.key_info.data_type;
|
||||
|
||||
input.key_info.data_size = output.key_info.data_size;
|
||||
input.data8 = kSMCReadKey;
|
||||
|
||||
result = call_smc(&input, &output);
|
||||
result_smc->kSMC = output.result;
|
||||
|
||||
if (result != kIOReturnSuccess || output.result != kSMCSuccess) {
|
||||
return result;
|
||||
}
|
||||
|
||||
memcpy(result_smc->data, output.bytes, sizeof(output.bytes));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
double get_temperature(char *key) {
|
||||
kern_return_t result;
|
||||
smc_return_t result_smc;
|
||||
|
||||
result = read_smc(key, &result_smc);
|
||||
|
||||
if (!(result == kIOReturnSuccess) && result_smc.data_size == 2 &&
|
||||
result_smc.data_type == to_uint32(DATA_TYPE_SP78)) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return (double)result_smc.data[0];
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#ifndef __SMC_H__
|
||||
#define __SMC_H__ 1
|
||||
|
||||
#include <IOKit/IOKitLib.h>
|
||||
|
||||
#define AMBIENT_AIR_0 "TA0P"
|
||||
#define AMBIENT_AIR_1 "TA1P"
|
||||
#define CPU_0_DIODE "TC0D"
|
||||
#define CPU_0_HEATSINK "TC0H"
|
||||
#define CPU_0_PROXIMITY "TC0P"
|
||||
#define ENCLOSURE_BASE_0 "TB0T"
|
||||
#define ENCLOSURE_BASE_1 "TB1T"
|
||||
#define ENCLOSURE_BASE_2 "TB2T"
|
||||
#define ENCLOSURE_BASE_3 "TB3T"
|
||||
#define GPU_0_DIODE "TG0D"
|
||||
#define GPU_0_HEATSINK "TG0H"
|
||||
#define GPU_0_PROXIMITY "TG0P"
|
||||
#define HARD_DRIVE_BAY "TH0P"
|
||||
#define MEMORY_SLOT_0 "TM0S"
|
||||
#define MEMORY_SLOTS_PROXIMITY "TM0P"
|
||||
#define NORTHBRIDGE "TN0H"
|
||||
#define NORTHBRIDGE_DIODE "TN0D"
|
||||
#define NORTHBRIDGE_PROXIMITY "TN0P"
|
||||
#define THUNDERBOLT_0 "TI0P"
|
||||
#define THUNDERBOLT_1 "TI1P"
|
||||
#define WIRELESS_MODULE "TW0P"
|
||||
|
||||
kern_return_t open_smc(void);
|
||||
kern_return_t close_smc(void);
|
||||
double get_temperature(char *);
|
||||
|
||||
#endif // __SMC_H__
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Warnings struct {
|
||||
List []error
|
||||
}
|
||||
|
||||
func (w *Warnings) Add(err error) {
|
||||
w.List = append(w.List, err)
|
||||
}
|
||||
|
||||
func (w *Warnings) Reference() error {
|
||||
if len(w.List) > 0 {
|
||||
return w
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Warnings) Error() string {
|
||||
return fmt.Sprintf("Number of warnings: %v", len(w.List))
|
||||
}
|
||||
+1
-1
@@ -253,7 +253,7 @@ func Write(w io.Writer, order ByteOrder, data interface{}) error {
|
||||
b[0] = *v
|
||||
case uint8:
|
||||
bs = b[:1]
|
||||
b[0] = byte(v)
|
||||
b[0] = v
|
||||
case []uint8:
|
||||
bs = v
|
||||
case *int16:
|
||||
|
||||
+36
-49
@@ -94,13 +94,24 @@ func (i FakeInvoke) CommandWithContext(ctx context.Context, name string, arg ...
|
||||
|
||||
var ErrNotImplementedError = errors.New("not implemented yet")
|
||||
|
||||
// ReadFile reads contents from a file.
|
||||
func ReadFile(filename string) (string, error) {
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
// ReadLines reads contents from a file and splits them by new lines.
|
||||
// A convenience wrapper to ReadLinesOffsetN(filename, 0, -1).
|
||||
func ReadLines(filename string) ([]string, error) {
|
||||
return ReadLinesOffsetN(filename, 0, -1)
|
||||
}
|
||||
|
||||
// ReadLines reads contents from file and splits them by new line.
|
||||
// ReadLinesOffsetN reads contents from file and splits them by new line.
|
||||
// The offset tells at which line number to start.
|
||||
// The count determines the number of lines to read (starting from offset):
|
||||
// n >= 0: at most n lines
|
||||
@@ -154,7 +165,7 @@ func UintToString(orig []uint8) string {
|
||||
size = i
|
||||
break
|
||||
}
|
||||
ret[i] = byte(o)
|
||||
ret[i] = o
|
||||
}
|
||||
if size == -1 {
|
||||
size = len(orig)
|
||||
@@ -213,25 +224,31 @@ func ReadInts(filename string) ([]int64, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Parse to int32 without error
|
||||
// HexToUint32 parses Hex to uint32 without error.
|
||||
func HexToUint32(hex string) uint32 {
|
||||
vv, _ := strconv.ParseUint(hex, 16, 32)
|
||||
return uint32(vv)
|
||||
}
|
||||
|
||||
// mustParseInt32 parses to int32 without error.
|
||||
func mustParseInt32(val string) int32 {
|
||||
vv, _ := strconv.ParseInt(val, 10, 32)
|
||||
return int32(vv)
|
||||
}
|
||||
|
||||
// Parse to uint64 without error
|
||||
// mustParseUint64 parses to uint64 without error.
|
||||
func mustParseUint64(val string) uint64 {
|
||||
vv, _ := strconv.ParseInt(val, 10, 64)
|
||||
return uint64(vv)
|
||||
}
|
||||
|
||||
// Parse to Float64 without error
|
||||
// mustParseFloat64 parses to Float64 without error.
|
||||
func mustParseFloat64(val string) float64 {
|
||||
vv, _ := strconv.ParseFloat(val, 64)
|
||||
return vv
|
||||
}
|
||||
|
||||
// StringsHas checks the target string slice contains src or not
|
||||
// StringsHas checks the target string slice contains src or not.
|
||||
func StringsHas(target []string, src string) bool {
|
||||
for _, t := range target {
|
||||
if strings.TrimSpace(t) == src {
|
||||
@@ -241,7 +258,7 @@ func StringsHas(target []string, src string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// StringsContains checks the src in any string of the target string slice
|
||||
// StringsContains checks the src in any string of the target string slice.
|
||||
func StringsContains(target []string, src string) bool {
|
||||
for _, t := range target {
|
||||
if strings.Contains(t, src) {
|
||||
@@ -291,7 +308,7 @@ func PathExists(filename string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
//GetEnv retrieves the environment variable key. If it does not exist it returns the default.
|
||||
// GetEnv retrieves the environment variable key. If it does not exist it returns the default.
|
||||
func GetEnv(key string, dfault string, combineWith ...string) string {
|
||||
value := os.Getenv(key)
|
||||
if value == "" {
|
||||
@@ -309,7 +326,6 @@ func GetEnv(key string, dfault string, combineWith ...string) string {
|
||||
copy(all[1:], combineWith)
|
||||
return filepath.Join(all...)
|
||||
}
|
||||
panic("invalid switch case")
|
||||
}
|
||||
|
||||
func HostProc(combineWith ...string) string {
|
||||
@@ -332,47 +348,18 @@ func HostRun(combineWith ...string) string {
|
||||
return GetEnv("HOST_RUN", "/run", combineWith...)
|
||||
}
|
||||
|
||||
// https://gist.github.com/kylelemons/1525278
|
||||
func Pipeline(cmds ...*exec.Cmd) ([]byte, []byte, error) {
|
||||
// Require at least one command
|
||||
if len(cmds) < 1 {
|
||||
return nil, nil, nil
|
||||
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)
|
||||
}
|
||||
|
||||
// Collect the output from the command(s)
|
||||
var output bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
|
||||
last := len(cmds) - 1
|
||||
for i, cmd := range cmds[:last] {
|
||||
var err error
|
||||
// Connect each command's stdin to the previous command's stdout
|
||||
if cmds[i+1].Stdin, err = cmd.StdoutPipe(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Connect each command's stderr to a buffer
|
||||
cmd.Stderr = &stderr
|
||||
}
|
||||
|
||||
// Connect the output and error for the last command
|
||||
cmds[last].Stdout, cmds[last].Stderr = &output, &stderr
|
||||
|
||||
// Start each command
|
||||
for _, cmd := range cmds {
|
||||
if err := cmd.Start(); err != nil {
|
||||
return output.Bytes(), stderr.Bytes(), err
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for each command to complete
|
||||
for _, cmd := range cmds {
|
||||
if err := cmd.Wait(); err != nil {
|
||||
return output.Bytes(), stderr.Bytes(), err
|
||||
}
|
||||
}
|
||||
|
||||
// Return the pipeline output and the collected standard error
|
||||
return output.Bytes(), stderr.Bytes(), nil
|
||||
}
|
||||
|
||||
// getSysctrlEnv sets LC_ALL=C in a list of env vars for use when running
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
func DoSysctrlWithContext(ctx context.Context, mib string) ([]string, error) {
|
||||
sysctl, err := exec.LookPath("/usr/sbin/sysctl")
|
||||
sysctl, err := exec.LookPath("sysctl")
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
@@ -36,7 +36,7 @@ func CallSyscall(mib []int32) ([]byte, uint64, error) {
|
||||
// get required buffer size
|
||||
length := uint64(0)
|
||||
_, _, err := unix.Syscall6(
|
||||
unix.SYS___SYSCTL,
|
||||
202, // unix.SYS___SYSCTL https://github.com/golang/sys/blob/76b94024e4b621e672466e8db3d7f084e7ddcad2/unix/zsysnum_darwin_amd64.go#L146
|
||||
uintptr(unsafe.Pointer(&mib[0])),
|
||||
uintptr(miblen),
|
||||
0,
|
||||
@@ -54,7 +54,7 @@ func CallSyscall(mib []int32) ([]byte, uint64, error) {
|
||||
// get proc info itself
|
||||
buf := make([]byte, length)
|
||||
_, _, err = unix.Syscall6(
|
||||
unix.SYS___SYSCTL,
|
||||
202, // unix.SYS___SYSCTL https://github.com/golang/sys/blob/76b94024e4b621e672466e8db3d7f084e7ddcad2/unix/zsysnum_darwin_amd64.go#L146
|
||||
uintptr(unsafe.Pointer(&mib[0])),
|
||||
uintptr(miblen),
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
|
||||
+17
-1
@@ -3,6 +3,7 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
@@ -11,8 +12,23 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func SysctlUint(mib string) (uint64, error) {
|
||||
buf, err := unix.SysctlRaw(mib)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(buf) == 8 { // 64 bit
|
||||
return *(*uint64)(unsafe.Pointer(&buf[0])), nil
|
||||
}
|
||||
if len(buf) == 4 { // 32bit
|
||||
t := *(*uint32)(unsafe.Pointer(&buf[0]))
|
||||
return uint64(t), nil
|
||||
}
|
||||
return 0, fmt.Errorf("unexpected size: %s, %d", mib, len(buf))
|
||||
}
|
||||
|
||||
func DoSysctrl(mib string) ([]string, error) {
|
||||
sysctl, err := exec.LookPath("/sbin/sysctl")
|
||||
sysctl, err := exec.LookPath("sysctl")
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
+254
-4
@@ -3,13 +3,19 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func DoSysctrl(mib string) ([]string, error) {
|
||||
sysctl, err := exec.LookPath("/sbin/sysctl")
|
||||
sysctl, err := exec.LookPath("sysctl")
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
@@ -20,8 +26,8 @@ func DoSysctrl(mib string) ([]string, error) {
|
||||
return []string{}, err
|
||||
}
|
||||
v := strings.Replace(string(out), "{ ", "", 1)
|
||||
v = strings.Replace(string(v), " }", "", 1)
|
||||
values := strings.Fields(string(v))
|
||||
v = strings.Replace(v, " }", "", 1)
|
||||
values := strings.Fields(v)
|
||||
|
||||
return values, nil
|
||||
}
|
||||
@@ -37,5 +43,249 @@ func NumProcs() (uint64, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint64(len(list)), err
|
||||
var cnt uint64
|
||||
|
||||
for _, v := range list {
|
||||
if _, err = strconv.ParseUint(v, 10, 64); err == nil {
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
|
||||
return cnt, nil
|
||||
}
|
||||
|
||||
func BootTimeWithContext(ctx context.Context) (uint64, error) {
|
||||
system, role, err := Virtualization()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
statFile := "stat"
|
||||
if system == "lxc" && role == "guest" {
|
||||
// if lxc, /proc/uptime is used.
|
||||
statFile = "uptime"
|
||||
} else if system == "docker" && role == "guest" {
|
||||
// also docker, guest
|
||||
statFile = "uptime"
|
||||
}
|
||||
|
||||
filename := HostProc(statFile)
|
||||
lines, err := ReadLines(filename)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if statFile == "uptime" {
|
||||
if len(lines) != 1 {
|
||||
return 0, fmt.Errorf("wrong uptime format")
|
||||
}
|
||||
f := strings.Fields(lines[0])
|
||||
b, err := strconv.ParseFloat(f[0], 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
t := uint64(time.Now().Unix()) - uint64(b)
|
||||
return t, nil
|
||||
}
|
||||
if statFile == "stat" {
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "btime") {
|
||||
f := strings.Fields(line)
|
||||
if len(f) != 2 {
|
||||
return 0, fmt.Errorf("wrong btime format")
|
||||
}
|
||||
b, err := strconv.ParseInt(f[1], 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
t := uint64(b)
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("could not find btime")
|
||||
}
|
||||
|
||||
func Virtualization() (string, string, error) {
|
||||
return VirtualizationWithContext(context.Background())
|
||||
}
|
||||
|
||||
// required variables for concurrency safe virtualization caching.
|
||||
var (
|
||||
cachedVirtMap map[string]string
|
||||
cachedVirtMutex sync.RWMutex
|
||||
cachedVirtOnce sync.Once
|
||||
)
|
||||
|
||||
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
|
||||
var system, role string
|
||||
|
||||
// if cached already, return from cache
|
||||
cachedVirtMutex.RLock() // unlock won't be deferred so concurrent reads don't wait for long
|
||||
if cachedVirtMap != nil {
|
||||
cachedSystem, cachedRole := cachedVirtMap["system"], cachedVirtMap["role"]
|
||||
cachedVirtMutex.RUnlock()
|
||||
return cachedSystem, cachedRole, nil
|
||||
}
|
||||
cachedVirtMutex.RUnlock()
|
||||
|
||||
filename := HostProc("xen")
|
||||
if PathExists(filename) {
|
||||
system = "xen"
|
||||
role = "guest" // assume guest
|
||||
|
||||
if PathExists(filepath.Join(filename, "capabilities")) {
|
||||
contents, err := ReadLines(filepath.Join(filename, "capabilities"))
|
||||
if err == nil && StringsContains(contents, "control_d") {
|
||||
role = "host"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filename = HostProc("modules")
|
||||
if PathExists(filename) {
|
||||
contents, err := ReadLines(filename)
|
||||
if err == nil {
|
||||
switch {
|
||||
case StringsContains(contents, "kvm"):
|
||||
system = "kvm"
|
||||
role = "host"
|
||||
case StringsContains(contents, "vboxdrv"):
|
||||
system = "vbox"
|
||||
role = "host"
|
||||
case StringsContains(contents, "vboxguest"):
|
||||
system = "vbox"
|
||||
role = "guest"
|
||||
case StringsContains(contents, "vmware"):
|
||||
system = "vmware"
|
||||
role = "guest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filename = HostProc("cpuinfo")
|
||||
if PathExists(filename) {
|
||||
contents, err := ReadLines(filename)
|
||||
if err == nil {
|
||||
if StringsContains(contents, "QEMU Virtual CPU") ||
|
||||
StringsContains(contents, "Common KVM processor") ||
|
||||
StringsContains(contents, "Common 32-bit KVM processor") {
|
||||
system = "kvm"
|
||||
role = "guest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filename = HostProc("bus/pci/devices")
|
||||
if PathExists(filename) {
|
||||
contents, err := ReadLines(filename)
|
||||
if err == nil {
|
||||
if StringsContains(contents, "virtio-pci") {
|
||||
role = "guest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filename = HostProc()
|
||||
if PathExists(filepath.Join(filename, "bc", "0")) {
|
||||
system = "openvz"
|
||||
role = "host"
|
||||
} else if PathExists(filepath.Join(filename, "vz")) {
|
||||
system = "openvz"
|
||||
role = "guest"
|
||||
}
|
||||
|
||||
// not use dmidecode because it requires root
|
||||
if PathExists(filepath.Join(filename, "self", "status")) {
|
||||
contents, err := ReadLines(filepath.Join(filename, "self", "status"))
|
||||
if err == nil {
|
||||
if StringsContains(contents, "s_context:") ||
|
||||
StringsContains(contents, "VxID:") {
|
||||
system = "linux-vserver"
|
||||
}
|
||||
// TODO: guest or host
|
||||
}
|
||||
}
|
||||
|
||||
if PathExists(filepath.Join(filename, "1", "environ")) {
|
||||
contents, err := ReadFile(filepath.Join(filename, "1", "environ"))
|
||||
|
||||
if err == nil {
|
||||
if strings.Contains(contents, "container=lxc") {
|
||||
system = "lxc"
|
||||
role = "guest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if PathExists(filepath.Join(filename, "self", "cgroup")) {
|
||||
contents, err := ReadLines(filepath.Join(filename, "self", "cgroup"))
|
||||
if err == nil {
|
||||
switch {
|
||||
case StringsContains(contents, "lxc"):
|
||||
system = "lxc"
|
||||
role = "guest"
|
||||
case StringsContains(contents, "docker"):
|
||||
system = "docker"
|
||||
role = "guest"
|
||||
case StringsContains(contents, "machine-rkt"):
|
||||
system = "rkt"
|
||||
role = "guest"
|
||||
case PathExists("/usr/bin/lxc-version"):
|
||||
system = "lxc"
|
||||
role = "host"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if PathExists(HostEtc("os-release")) {
|
||||
p, _, err := GetOSRelease()
|
||||
if err == nil && p == "coreos" {
|
||||
system = "rkt" // Is it true?
|
||||
role = "host"
|
||||
}
|
||||
}
|
||||
|
||||
// before returning for the first time, cache the system and role
|
||||
cachedVirtOnce.Do(func() {
|
||||
cachedVirtMutex.Lock()
|
||||
defer cachedVirtMutex.Unlock()
|
||||
cachedVirtMap = map[string]string{
|
||||
"system": system,
|
||||
"role": role,
|
||||
}
|
||||
})
|
||||
|
||||
return system, role, nil
|
||||
}
|
||||
|
||||
func GetOSRelease() (platform string, version string, err error) {
|
||||
contents, err := ReadLines(HostEtc("os-release"))
|
||||
if err != nil {
|
||||
return "", "", nil // return empty
|
||||
}
|
||||
for _, line := range contents {
|
||||
field := strings.Split(line, "=")
|
||||
if len(field) < 2 {
|
||||
continue
|
||||
}
|
||||
switch field[0] {
|
||||
case "ID": // use ID for lowercase
|
||||
platform = trimQuotes(field[1])
|
||||
case "VERSION":
|
||||
version = trimQuotes(field[1])
|
||||
}
|
||||
}
|
||||
return platform, version, nil
|
||||
}
|
||||
|
||||
// trimQuotes removes quotes in the source string.
|
||||
func trimQuotes(s string) string {
|
||||
if len(s) >= 2 {
|
||||
if s[0] == '"' && s[len(s)-1] == '"' {
|
||||
return s[1 : len(s)-1]
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func DoSysctrl(mib string) ([]string, error) {
|
||||
sysctl, err := exec.LookPath("/sbin/sysctl")
|
||||
sysctl, err := exec.LookPath("sysctl")
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
+2
-3
@@ -23,7 +23,7 @@ func CallLsofWithContext(ctx context.Context, invoke Invoker, pid int32, args ..
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, lsof, cmd...)
|
||||
if err != nil {
|
||||
// if no pid found, lsof returnes code 1.
|
||||
// if no pid found, lsof returns code 1.
|
||||
if err.Error() == "exit status 1" && len(out) == 0 {
|
||||
return []string{}, nil
|
||||
}
|
||||
@@ -41,8 +41,7 @@ func CallLsofWithContext(ctx context.Context, invoke Invoker, pid int32, args ..
|
||||
}
|
||||
|
||||
func CallPgrepWithContext(ctx context.Context, invoke Invoker, pid int32) ([]int32, error) {
|
||||
var cmd []string
|
||||
cmd = []string{"-P", strconv.Itoa(int(pid))}
|
||||
cmd := []string{"-P", strconv.Itoa(int(pid))}
|
||||
pgrep, err := exec.LookPath("pgrep")
|
||||
if err != nil {
|
||||
return []int32{}, err
|
||||
|
||||
+177
-12
@@ -4,9 +4,14 @@ package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/StackExchange/wmi"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
@@ -44,6 +49,18 @@ const (
|
||||
PDH_INVALID_DATA = 0xc0000bc6
|
||||
PDH_INVALID_HANDLE = 0xC0000bbc
|
||||
PDH_NO_DATA = 0x800007d5
|
||||
|
||||
STATUS_BUFFER_OVERFLOW = 0x80000005
|
||||
STATUS_BUFFER_TOO_SMALL = 0xC0000023
|
||||
STATUS_INFO_LENGTH_MISMATCH = 0xC0000004
|
||||
)
|
||||
|
||||
const (
|
||||
ProcessBasicInformation = 0
|
||||
ProcessWow64Information = 26
|
||||
ProcessQueryInformation = windows.PROCESS_DUP_HANDLE | windows.PROCESS_QUERY_INFORMATION
|
||||
|
||||
SystemExtendedHandleInformationClass = 64
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -52,13 +69,22 @@ var (
|
||||
ModPdh = windows.NewLazySystemDLL("pdh.dll")
|
||||
ModPsapi = windows.NewLazySystemDLL("psapi.dll")
|
||||
|
||||
ProcGetSystemTimes = Modkernel32.NewProc("GetSystemTimes")
|
||||
ProcNtQuerySystemInformation = ModNt.NewProc("NtQuerySystemInformation")
|
||||
PdhOpenQuery = ModPdh.NewProc("PdhOpenQuery")
|
||||
PdhAddCounter = ModPdh.NewProc("PdhAddCounterW")
|
||||
PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData")
|
||||
PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue")
|
||||
PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery")
|
||||
ProcGetSystemTimes = Modkernel32.NewProc("GetSystemTimes")
|
||||
ProcNtQuerySystemInformation = ModNt.NewProc("NtQuerySystemInformation")
|
||||
ProcRtlGetNativeSystemInformation = ModNt.NewProc("RtlGetNativeSystemInformation")
|
||||
ProcRtlNtStatusToDosError = ModNt.NewProc("RtlNtStatusToDosError")
|
||||
ProcNtQueryInformationProcess = ModNt.NewProc("NtQueryInformationProcess")
|
||||
ProcNtReadVirtualMemory = ModNt.NewProc("NtReadVirtualMemory")
|
||||
ProcNtWow64QueryInformationProcess64 = ModNt.NewProc("NtWow64QueryInformationProcess64")
|
||||
ProcNtWow64ReadVirtualMemory64 = ModNt.NewProc("NtWow64ReadVirtualMemory64")
|
||||
|
||||
PdhOpenQuery = ModPdh.NewProc("PdhOpenQuery")
|
||||
PdhAddEnglishCounterW = ModPdh.NewProc("PdhAddEnglishCounterW")
|
||||
PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData")
|
||||
PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue")
|
||||
PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery")
|
||||
|
||||
procQueryDosDeviceW = Modkernel32.NewProc("QueryDosDeviceW")
|
||||
)
|
||||
|
||||
type FILETIME struct {
|
||||
@@ -76,7 +102,7 @@ func BytePtrToString(p *uint8) string {
|
||||
return string(a[:i])
|
||||
}
|
||||
|
||||
// CounterInfo
|
||||
// CounterInfo struct is used to track a windows performance counter
|
||||
// copied from https://github.com/mackerelio/mackerel-agent/
|
||||
type CounterInfo struct {
|
||||
PostName string
|
||||
@@ -84,7 +110,7 @@ type CounterInfo struct {
|
||||
Counter windows.Handle
|
||||
}
|
||||
|
||||
// CreateQuery XXX
|
||||
// CreateQuery with a PdhOpenQuery call
|
||||
// copied from https://github.com/mackerelio/mackerel-agent/
|
||||
func CreateQuery() (windows.Handle, error) {
|
||||
var query windows.Handle
|
||||
@@ -95,10 +121,10 @@ func CreateQuery() (windows.Handle, error) {
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// CreateCounter XXX
|
||||
// CreateCounter with a PdhAddEnglishCounterW call
|
||||
func CreateCounter(query windows.Handle, pname, cname string) (*CounterInfo, error) {
|
||||
var counter windows.Handle
|
||||
r, _, err := PdhAddCounter.Call(
|
||||
r, _, err := PdhAddEnglishCounterW.Call(
|
||||
uintptr(query),
|
||||
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(cname))),
|
||||
0,
|
||||
@@ -113,6 +139,62 @@ func CreateCounter(query windows.Handle, pname, cname string) (*CounterInfo, err
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetCounterValue get counter value from handle
|
||||
// adapted from https://github.com/mackerelio/mackerel-agent/
|
||||
func GetCounterValue(counter windows.Handle) (float64, error) {
|
||||
var value PDH_FMT_COUNTERVALUE_DOUBLE
|
||||
r, _, err := PdhGetFormattedCounterValue.Call(uintptr(counter), PDH_FMT_DOUBLE, uintptr(0), uintptr(unsafe.Pointer(&value)))
|
||||
if r != 0 && r != PDH_INVALID_DATA {
|
||||
return 0.0, err
|
||||
}
|
||||
return value.DoubleValue, nil
|
||||
}
|
||||
|
||||
type Win32PerformanceCounter struct {
|
||||
PostName string
|
||||
CounterName string
|
||||
Query windows.Handle
|
||||
Counter windows.Handle
|
||||
}
|
||||
|
||||
func NewWin32PerformanceCounter(postName, counterName string) (*Win32PerformanceCounter, error) {
|
||||
query, err := CreateQuery()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var counter = Win32PerformanceCounter{
|
||||
Query: query,
|
||||
PostName: postName,
|
||||
CounterName: counterName,
|
||||
}
|
||||
r, _, err := PdhAddEnglishCounterW.Call(
|
||||
uintptr(counter.Query),
|
||||
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(counter.CounterName))),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&counter.Counter)),
|
||||
)
|
||||
if r != 0 {
|
||||
return nil, err
|
||||
}
|
||||
return &counter, nil
|
||||
}
|
||||
|
||||
func (w *Win32PerformanceCounter) GetValue() (float64, error) {
|
||||
r, _, err := PdhCollectQueryData.Call(uintptr(w.Query))
|
||||
if r != 0 && err != nil {
|
||||
if r == PDH_NO_DATA {
|
||||
return 0.0, fmt.Errorf("%w: this counter has not data", err)
|
||||
}
|
||||
return 0.0, err
|
||||
}
|
||||
|
||||
return GetCounterValue(w.Counter)
|
||||
}
|
||||
|
||||
func ProcessorQueueLengthCounter() (*Win32PerformanceCounter, error) {
|
||||
return NewWin32PerformanceCounter("processor_queue_length", `\System\Processor Queue Length`)
|
||||
}
|
||||
|
||||
// WMIQueryWithContext - wraps wmi.Query with a timed-out context to avoid hanging
|
||||
func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, connectServerArgs ...interface{}) error {
|
||||
if _, ok := ctx.Deadline(); !ok {
|
||||
@@ -133,3 +215,86 @@ func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, con
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Convert paths using native DOS format like:
|
||||
// "\Device\HarddiskVolume1\Windows\systemew\file.txt"
|
||||
// into:
|
||||
// "C:\Windows\systemew\file.txt"
|
||||
func ConvertDOSPath(p string) string {
|
||||
rawDrive := strings.Join(strings.Split(p, `\`)[:3], `\`)
|
||||
|
||||
for d := 'A'; d <= 'Z'; d++ {
|
||||
szDeviceName := string(d) + ":"
|
||||
szTarget := make([]uint16, 512)
|
||||
ret, _, _ := procQueryDosDeviceW.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(szDeviceName))),
|
||||
uintptr(unsafe.Pointer(&szTarget[0])),
|
||||
uintptr(len(szTarget)))
|
||||
if ret != 0 && windows.UTF16ToString(szTarget[:]) == rawDrive {
|
||||
return filepath.Join(szDeviceName, p[len(rawDrive):])
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
type NtStatus uint32
|
||||
|
||||
func (s NtStatus) Error() error {
|
||||
if s == 0 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("NtStatus 0x%08x", uint32(s))
|
||||
}
|
||||
|
||||
func (s NtStatus) IsError() bool {
|
||||
return s>>30 == 3
|
||||
}
|
||||
|
||||
type SystemExtendedHandleTableEntryInformation struct {
|
||||
Object uintptr
|
||||
UniqueProcessId uintptr
|
||||
HandleValue uintptr
|
||||
GrantedAccess uint32
|
||||
CreatorBackTraceIndex uint16
|
||||
ObjectTypeIndex uint16
|
||||
HandleAttributes uint32
|
||||
Reserved uint32
|
||||
}
|
||||
|
||||
type SystemExtendedHandleInformation struct {
|
||||
NumberOfHandles uintptr
|
||||
Reserved uintptr
|
||||
Handles [1]SystemExtendedHandleTableEntryInformation
|
||||
}
|
||||
|
||||
// CallWithExpandingBuffer https://github.com/hillu/go-ntdll
|
||||
func CallWithExpandingBuffer(fn func() NtStatus, buf *[]byte, resultLength *uint32) NtStatus {
|
||||
for {
|
||||
if st := fn(); st == STATUS_BUFFER_OVERFLOW || st == STATUS_BUFFER_TOO_SMALL || st == STATUS_INFO_LENGTH_MISMATCH {
|
||||
if int(*resultLength) <= cap(*buf) {
|
||||
(*reflect.SliceHeader)(unsafe.Pointer(buf)).Len = int(*resultLength)
|
||||
} else {
|
||||
*buf = make([]byte, int(*resultLength))
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
if !st.IsError() {
|
||||
*buf = (*buf)[:int(*resultLength)]
|
||||
}
|
||||
return st
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NtQuerySystemInformation(
|
||||
SystemInformationClass uint32,
|
||||
SystemInformation *byte,
|
||||
SystemInformationLength uint32,
|
||||
ReturnLength *uint32,
|
||||
) NtStatus {
|
||||
r0, _, _ := ProcNtQuerySystemInformation.Call(
|
||||
uintptr(SystemInformationClass),
|
||||
uintptr(unsafe.Pointer(SystemInformation)),
|
||||
uintptr(SystemInformationLength),
|
||||
uintptr(unsafe.Pointer(ReturnLength)))
|
||||
return NtStatus(r0)
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Sleep awaits for provided interval.
|
||||
// Can be interrupted by context cancelation.
|
||||
func Sleep(ctx context.Context, interval time.Duration) error {
|
||||
var timer = time.NewTimer(interval)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-timer.C:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
+21
-1
@@ -44,7 +44,7 @@ type VirtualMemoryStat struct {
|
||||
|
||||
// FreeBSD specific numbers:
|
||||
// https://reviews.freebsd.org/D8467
|
||||
Laundry uint64 `json:"laundry"`
|
||||
Laundry uint64 `json:"laundry"`
|
||||
|
||||
// Linux specific numbers
|
||||
// https://www.centos.org/docs/5/html/5.1/Deployment_Guide/s2-proc-meminfo.html
|
||||
@@ -57,6 +57,8 @@ type VirtualMemoryStat struct {
|
||||
WritebackTmp uint64 `json:"writebacktmp"`
|
||||
Shared uint64 `json:"shared"`
|
||||
Slab uint64 `json:"slab"`
|
||||
SReclaimable uint64 `json:"sreclaimable"`
|
||||
SUnreclaim uint64 `json:"sunreclaim"`
|
||||
PageTables uint64 `json:"pagetables"`
|
||||
SwapCached uint64 `json:"swapcached"`
|
||||
CommitLimit uint64 `json:"commitlimit"`
|
||||
@@ -83,6 +85,13 @@ type SwapMemoryStat struct {
|
||||
UsedPercent float64 `json:"usedPercent"`
|
||||
Sin uint64 `json:"sin"`
|
||||
Sout uint64 `json:"sout"`
|
||||
PgIn uint64 `json:"pgin"`
|
||||
PgOut uint64 `json:"pgout"`
|
||||
PgFault uint64 `json:"pgfault"`
|
||||
|
||||
// Linux specific numbers
|
||||
// https://www.kernel.org/doc/Documentation/cgroup-v2.txt
|
||||
PgMajFault uint64 `json:"pgmajfault"`
|
||||
}
|
||||
|
||||
func (m VirtualMemoryStat) String() string {
|
||||
@@ -94,3 +103,14 @@ func (m SwapMemoryStat) String() string {
|
||||
s, _ := json.Marshal(m)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
type SwapDevice struct {
|
||||
Name string `json:"name"`
|
||||
UsedBytes uint64 `json:"usedBytes"`
|
||||
FreeBytes uint64 `json:"freeBytes"`
|
||||
}
|
||||
|
||||
func (m SwapDevice) String() string {
|
||||
s, _ := json.Marshal(m)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
// +build freebsd openbsd
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const swapCommand = "swapctl"
|
||||
|
||||
// swapctl column indexes
|
||||
const (
|
||||
nameCol = 0
|
||||
totalKiBCol = 1
|
||||
usedKiBCol = 2
|
||||
)
|
||||
|
||||
func SwapDevices() ([]*SwapDevice, error) {
|
||||
return SwapDevicesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
|
||||
swapCommandPath, err := exec.LookPath(swapCommand)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find command %q: %w", swapCommand, err)
|
||||
}
|
||||
output, err := invoke.CommandWithContext(ctx, swapCommandPath, "-lk")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not execute %q: %w", swapCommand, err)
|
||||
}
|
||||
|
||||
return parseSwapctlOutput(string(output))
|
||||
}
|
||||
|
||||
func parseSwapctlOutput(output string) ([]*SwapDevice, error) {
|
||||
lines := strings.Split(output, "\n")
|
||||
if len(lines) == 0 {
|
||||
return nil, fmt.Errorf("could not parse output of %q: no lines in %q", swapCommand, output)
|
||||
}
|
||||
|
||||
// Check header headerFields are as expected.
|
||||
header := lines[0]
|
||||
header = strings.ToLower(header)
|
||||
header = strings.ReplaceAll(header, ":", "")
|
||||
headerFields := strings.Fields(header)
|
||||
if len(headerFields) < usedKiBCol {
|
||||
return nil, fmt.Errorf("couldn't parse %q: too few fields in header %q", swapCommand, header)
|
||||
}
|
||||
if headerFields[nameCol] != "device" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[nameCol], "device")
|
||||
}
|
||||
if headerFields[totalKiBCol] != "1kb-blocks" && headerFields[totalKiBCol] != "1k-blocks" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[totalKiBCol], "1kb-blocks")
|
||||
}
|
||||
if headerFields[usedKiBCol] != "used" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[usedKiBCol], "used")
|
||||
}
|
||||
|
||||
var swapDevices []*SwapDevice
|
||||
for _, line := range lines[1:] {
|
||||
if line == "" {
|
||||
continue // the terminal line is typically empty
|
||||
}
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < usedKiBCol {
|
||||
return nil, fmt.Errorf("couldn't parse %q: too few fields", swapCommand)
|
||||
}
|
||||
|
||||
totalKiB, err := strconv.ParseUint(fields[totalKiBCol], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse 'Size' column in %q: %w", swapCommand, err)
|
||||
}
|
||||
|
||||
usedKiB, err := strconv.ParseUint(fields[usedKiBCol], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse 'Used' column in %q: %w", swapCommand, err)
|
||||
}
|
||||
|
||||
swapDevices = append(swapDevices, &SwapDevice{
|
||||
Name: fields[nameCol],
|
||||
UsedBytes: usedKiB * 1024,
|
||||
FreeBytes: (totalKiB - usedKiB) * 1024,
|
||||
})
|
||||
}
|
||||
|
||||
return swapDevices, nil
|
||||
}
|
||||
+30
-34
@@ -4,71 +4,67 @@ package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"strconv"
|
||||
"strings"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func getHwMemsize() (uint64, error) {
|
||||
totalString, err := unix.Sysctl("hw.memsize")
|
||||
total, err := unix.SysctlUint64("hw.memsize")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// unix.sysctl() helpfully assumes the result is a null-terminated string and
|
||||
// removes the last byte of the result if it's 0 :/
|
||||
totalString += "\x00"
|
||||
|
||||
total := uint64(binary.LittleEndian.Uint64([]byte(totalString)))
|
||||
|
||||
return total, nil
|
||||
}
|
||||
|
||||
// xsw_usage in sys/sysctl.h
|
||||
type swapUsage struct {
|
||||
Total uint64
|
||||
Avail uint64
|
||||
Used uint64
|
||||
Pagesize int32
|
||||
Encrypted bool
|
||||
}
|
||||
|
||||
// SwapMemory returns swapinfo.
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
return SwapMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
// https://github.com/yanllearnn/go-osstat/blob/ae8a279d26f52ec946a03698c7f50a26cfb427e3/memory/memory_darwin.go
|
||||
var ret *SwapMemoryStat
|
||||
|
||||
swapUsage, err := common.DoSysctrlWithContext(ctx, "vm.swapusage")
|
||||
value, err := unix.SysctlRaw("vm.swapusage")
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
total := strings.Replace(swapUsage[2], "M", "", 1)
|
||||
used := strings.Replace(swapUsage[5], "M", "", 1)
|
||||
free := strings.Replace(swapUsage[8], "M", "", 1)
|
||||
|
||||
total_v, err := strconv.ParseFloat(total, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
used_v, err := strconv.ParseFloat(used, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
free_v, err := strconv.ParseFloat(free, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if len(value) != 32 {
|
||||
return ret, fmt.Errorf("unexpected output of sysctl vm.swapusage: %v (len: %d)", value, len(value))
|
||||
}
|
||||
swap := (*swapUsage)(unsafe.Pointer(&value[0]))
|
||||
|
||||
u := float64(0)
|
||||
if total_v != 0 {
|
||||
u = ((total_v - free_v) / total_v) * 100.0
|
||||
if swap.Total != 0 {
|
||||
u = ((float64(swap.Total) - float64(swap.Avail)) / float64(swap.Total)) * 100.0
|
||||
}
|
||||
|
||||
// vm.swapusage shows "M", multiply 1024 * 1024 to convert bytes.
|
||||
ret = &SwapMemoryStat{
|
||||
Total: uint64(total_v * 1024 * 1024),
|
||||
Used: uint64(used_v * 1024 * 1024),
|
||||
Free: uint64(free_v * 1024 * 1024),
|
||||
Total: swap.Total,
|
||||
Used: swap.Used,
|
||||
Free: swap.Avail,
|
||||
UsedPercent: u,
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func SwapDevices() ([]*SwapDevice, error) {
|
||||
return SwapDevicesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,5 +1,4 @@
|
||||
// +build darwin
|
||||
// +build cgo
|
||||
// +build darwin,cgo
|
||||
|
||||
package mem
|
||||
|
||||
|
||||
+1
-2
@@ -1,5 +1,4 @@
|
||||
// +build darwin
|
||||
// +build !cgo
|
||||
// +build darwin,!cgo
|
||||
|
||||
package mem
|
||||
|
||||
|
||||
+8
@@ -23,3 +23,11 @@ func SwapMemory() (*SwapMemoryStat, error) {
|
||||
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func SwapDevices() ([]*SwapDevice, error) {
|
||||
return SwapDevicesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
+52
-27
@@ -8,6 +8,8 @@ import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
)
|
||||
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
@@ -15,58 +17,59 @@ func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
pageSize, err := unix.SysctlUint32("vm.stats.vm.v_page_size")
|
||||
pageSize, err := common.SysctlUint("vm.stats.vm.v_page_size")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
physmem, err := unix.SysctlUint64("hw.physmem")
|
||||
physmem, err := common.SysctlUint("hw.physmem")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
free, err := unix.SysctlUint32("vm.stats.vm.v_free_count")
|
||||
|
||||
free, err := common.SysctlUint("vm.stats.vm.v_free_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
active, err := unix.SysctlUint32("vm.stats.vm.v_active_count")
|
||||
active, err := common.SysctlUint("vm.stats.vm.v_active_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inactive, err := unix.SysctlUint32("vm.stats.vm.v_inactive_count")
|
||||
inactive, err := common.SysctlUint("vm.stats.vm.v_inactive_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buffers, err := unix.SysctlUint64("vfs.bufspace")
|
||||
buffers, err := common.SysctlUint("vfs.bufspace")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wired, err := unix.SysctlUint32("vm.stats.vm.v_wire_count")
|
||||
wired, err := common.SysctlUint("vm.stats.vm.v_wire_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cached, laundry uint32
|
||||
osreldate, _ := unix.SysctlUint32("kern.osreldate")
|
||||
var cached, laundry uint64
|
||||
osreldate, _ := common.SysctlUint("kern.osreldate")
|
||||
if osreldate < 1102000 {
|
||||
cached, err = unix.SysctlUint32("vm.stats.vm.v_cache_count")
|
||||
cached, err = common.SysctlUint("vm.stats.vm.v_cache_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
laundry, err = unix.SysctlUint32("vm.stats.vm.v_laundry_count")
|
||||
laundry, err = common.SysctlUint("vm.stats.vm.v_laundry_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
p := uint64(pageSize)
|
||||
p := pageSize
|
||||
ret := &VirtualMemoryStat{
|
||||
Total: uint64(physmem),
|
||||
Free: uint64(free) * p,
|
||||
Active: uint64(active) * p,
|
||||
Inactive: uint64(inactive) * p,
|
||||
Cached: uint64(cached) * p,
|
||||
Buffers: uint64(buffers),
|
||||
Wired: uint64(wired) * p,
|
||||
Laundry: uint64(laundry) * p,
|
||||
Total: physmem,
|
||||
Free: free * p,
|
||||
Active: active * p,
|
||||
Inactive: inactive * p,
|
||||
Cached: cached * p,
|
||||
Buffers: buffers,
|
||||
Wired: wired * p,
|
||||
Laundry: laundry * p,
|
||||
}
|
||||
|
||||
ret.Available = ret.Inactive + ret.Cached + ret.Free + ret.Laundry
|
||||
@@ -84,11 +87,22 @@ func SwapMemory() (*SwapMemoryStat, error) {
|
||||
// Constants from vm/vm_param.h
|
||||
// nolint: golint
|
||||
const (
|
||||
XSWDEV_VERSION = 1
|
||||
XSWDEV_VERSION11 = 1
|
||||
XSWDEV_VERSION = 2
|
||||
)
|
||||
|
||||
// Types from vm/vm_param.h
|
||||
type xswdev struct {
|
||||
Version uint32 // Version is the version
|
||||
Dev uint64 // Dev is the device identifier
|
||||
Flags int32 // Flags is the swap flags applied to the device
|
||||
NBlks int32 // NBlks is the total number of blocks
|
||||
Used int32 // Used is the number of blocks used
|
||||
}
|
||||
|
||||
// xswdev11 is a compatibility for under FreeBSD 11
|
||||
// sys/vm/swap_pager.c
|
||||
type xswdev11 struct {
|
||||
Version uint32 // Version is the version
|
||||
Dev uint32 // Dev is the device identifier
|
||||
Flags int32 // Flags is the swap flags applied to the device
|
||||
@@ -98,7 +112,7 @@ type xswdev struct {
|
||||
|
||||
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
// FreeBSD can have multiple swap devices so we total them up
|
||||
i, err := unix.SysctlUint32("vm.nswapdev")
|
||||
i, err := common.SysctlUint("vm.nswapdev")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -109,11 +123,11 @@ func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
|
||||
c := int(i)
|
||||
|
||||
i, err = unix.SysctlUint32("vm.stats.vm.v_page_size")
|
||||
i, err = common.SysctlUint("vm.stats.vm.v_page_size")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pageSize := uint64(i)
|
||||
pageSize := i
|
||||
|
||||
var buf []byte
|
||||
s := &SwapMemoryStat{}
|
||||
@@ -123,12 +137,23 @@ func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// first, try to parse with version 2
|
||||
xsw := (*xswdev)(unsafe.Pointer(&buf[0]))
|
||||
if xsw.Version != XSWDEV_VERSION {
|
||||
if xsw.Version == XSWDEV_VERSION11 {
|
||||
// this is version 1, so try to parse again
|
||||
xsw := (*xswdev11)(unsafe.Pointer(&buf[0]))
|
||||
if xsw.Version != XSWDEV_VERSION11 {
|
||||
return nil, errors.New("xswdev version mismatch(11)")
|
||||
}
|
||||
s.Total += uint64(xsw.NBlks)
|
||||
s.Used += uint64(xsw.Used)
|
||||
} else if xsw.Version != XSWDEV_VERSION {
|
||||
return nil, errors.New("xswdev version mismatch")
|
||||
} else {
|
||||
s.Total += uint64(xsw.NBlks)
|
||||
s.Used += uint64(xsw.Used)
|
||||
}
|
||||
s.Total += uint64(xsw.NBlks)
|
||||
s.Used += uint64(xsw.Used)
|
||||
|
||||
}
|
||||
|
||||
if s.Total != 0 {
|
||||
|
||||
+367
-6
@@ -3,7 +3,13 @@
|
||||
package mem
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -11,17 +17,56 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type VirtualMemoryExStat struct {
|
||||
ActiveFile uint64 `json:"activefile"`
|
||||
InactiveFile uint64 `json:"inactivefile"`
|
||||
ActiveAnon uint64 `json:"activeanon"`
|
||||
InactiveAnon uint64 `json:"inactiveanon"`
|
||||
Unevictable uint64 `json:"unevictable"`
|
||||
}
|
||||
|
||||
func (v VirtualMemoryExStat) String() string {
|
||||
s, _ := json.Marshal(v)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
vm, _, err := fillFromMeminfoWithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vm, nil
|
||||
}
|
||||
|
||||
func VirtualMemoryEx() (*VirtualMemoryExStat, error) {
|
||||
return VirtualMemoryExWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryExWithContext(ctx context.Context) (*VirtualMemoryExStat, error) {
|
||||
_, vmEx, err := fillFromMeminfoWithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vmEx, nil
|
||||
}
|
||||
|
||||
func fillFromMeminfoWithContext(ctx context.Context) (*VirtualMemoryStat, *VirtualMemoryExStat, error) {
|
||||
filename := common.HostProc("meminfo")
|
||||
lines, _ := common.ReadLines(filename)
|
||||
|
||||
// flag if MemAvailable is in /proc/meminfo (kernel 3.14+)
|
||||
memavail := false
|
||||
activeFile := false // "Active(file)" not available: 2.6.28 / Dec 2008
|
||||
inactiveFile := false // "Inactive(file)" not available: 2.6.28 / Dec 2008
|
||||
sReclaimable := false // "SReclaimable:" not available: 2.6.19 / Nov 2006
|
||||
|
||||
ret := &VirtualMemoryStat{}
|
||||
retEx := &VirtualMemoryExStat{}
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Split(line, ":")
|
||||
if len(fields) != 2 {
|
||||
@@ -31,79 +76,244 @@ func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
value := strings.TrimSpace(fields[1])
|
||||
value = strings.Replace(value, " kB", "", -1)
|
||||
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
switch key {
|
||||
case "MemTotal":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Total = t * 1024
|
||||
case "MemFree":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Free = t * 1024
|
||||
case "MemAvailable":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
memavail = true
|
||||
ret.Available = t * 1024
|
||||
case "Buffers":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Buffers = t * 1024
|
||||
case "Cached":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Cached = t * 1024
|
||||
case "Active":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Active = t * 1024
|
||||
case "Inactive":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Inactive = t * 1024
|
||||
case "Active(anon)":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
retEx.ActiveAnon = t * 1024
|
||||
case "Inactive(anon)":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
retEx.InactiveAnon = t * 1024
|
||||
case "Active(file)":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
activeFile = true
|
||||
retEx.ActiveFile = t * 1024
|
||||
case "Inactive(file)":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
inactiveFile = true
|
||||
retEx.InactiveFile = t * 1024
|
||||
case "Unevictable":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
retEx.Unevictable = t * 1024
|
||||
case "Writeback":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Writeback = t * 1024
|
||||
case "WritebackTmp":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.WritebackTmp = t * 1024
|
||||
case "Dirty":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Dirty = t * 1024
|
||||
case "Shmem":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Shared = t * 1024
|
||||
case "Slab":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Slab = t * 1024
|
||||
case "SReclaimable":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
sReclaimable = true
|
||||
ret.SReclaimable = t * 1024
|
||||
case "SUnreclaim":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.SUnreclaim = t * 1024
|
||||
case "PageTables":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.PageTables = t * 1024
|
||||
case "SwapCached":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.SwapCached = t * 1024
|
||||
case "CommitLimit":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.CommitLimit = t * 1024
|
||||
case "Committed_AS":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.CommittedAS = t * 1024
|
||||
case "HighTotal":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.HighTotal = t * 1024
|
||||
case "HighFree":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.HighFree = t * 1024
|
||||
case "LowTotal":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.LowTotal = t * 1024
|
||||
case "LowFree":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.LowFree = t * 1024
|
||||
case "SwapTotal":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.SwapTotal = t * 1024
|
||||
case "SwapFree":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.SwapFree = t * 1024
|
||||
case "Mapped":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Mapped = t * 1024
|
||||
case "VmallocTotal":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.VMallocTotal = t * 1024
|
||||
case "VmallocUsed":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.VMallocUsed = t * 1024
|
||||
case "VmallocChunk":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.VMallocChunk = t * 1024
|
||||
case "HugePages_Total":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.HugePagesTotal = t
|
||||
case "HugePages_Free":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.HugePagesFree = t
|
||||
case "Hugepagesize":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.HugePageSize = t * 1024
|
||||
}
|
||||
}
|
||||
|
||||
ret.Cached += ret.SReclaimable
|
||||
|
||||
if !memavail {
|
||||
ret.Available = ret.Free + ret.Buffers + ret.Cached
|
||||
if activeFile && inactiveFile && sReclaimable {
|
||||
ret.Available = calcuateAvailVmem(ret, retEx)
|
||||
} else {
|
||||
ret.Available = ret.Cached + ret.Free
|
||||
}
|
||||
}
|
||||
|
||||
ret.Used = ret.Total - ret.Free - ret.Buffers - ret.Cached
|
||||
ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0
|
||||
|
||||
return ret, nil
|
||||
return ret, retEx, nil
|
||||
}
|
||||
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
@@ -147,7 +357,158 @@ func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
continue
|
||||
}
|
||||
ret.Sout = value * 4 * 1024
|
||||
case "pgpgin":
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret.PgIn = value * 4 * 1024
|
||||
case "pgpgout":
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret.PgOut = value * 4 * 1024
|
||||
case "pgfault":
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret.PgFault = value * 4 * 1024
|
||||
case "pgmajfault":
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret.PgMajFault = value * 4 * 1024
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// calcuateAvailVmem is a fallback under kernel 3.14 where /proc/meminfo does not provide
|
||||
// "MemAvailable:" column. It reimplements an algorithm from the link below
|
||||
// https://github.com/giampaolo/psutil/pull/890
|
||||
func calcuateAvailVmem(ret *VirtualMemoryStat, retEx *VirtualMemoryExStat) uint64 {
|
||||
var watermarkLow uint64
|
||||
|
||||
fn := common.HostProc("zoneinfo")
|
||||
lines, err := common.ReadLines(fn)
|
||||
|
||||
if err != nil {
|
||||
return ret.Free + ret.Cached // fallback under kernel 2.6.13
|
||||
}
|
||||
|
||||
pagesize := uint64(os.Getpagesize())
|
||||
watermarkLow = 0
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
|
||||
if strings.HasPrefix(fields[0], "low") {
|
||||
lowValue, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
|
||||
if err != nil {
|
||||
lowValue = 0
|
||||
}
|
||||
watermarkLow += lowValue
|
||||
}
|
||||
}
|
||||
|
||||
watermarkLow *= pagesize
|
||||
|
||||
availMemory := ret.Free - watermarkLow
|
||||
pageCache := retEx.ActiveFile + retEx.InactiveFile
|
||||
pageCache -= uint64(math.Min(float64(pageCache/2), float64(watermarkLow)))
|
||||
availMemory += pageCache
|
||||
availMemory += ret.SReclaimable - uint64(math.Min(float64(ret.SReclaimable/2.0), float64(watermarkLow)))
|
||||
|
||||
if availMemory < 0 {
|
||||
availMemory = 0
|
||||
}
|
||||
|
||||
return availMemory
|
||||
}
|
||||
|
||||
const swapsFilename = "swaps"
|
||||
|
||||
// swaps file column indexes
|
||||
const (
|
||||
nameCol = 0
|
||||
// typeCol = 1
|
||||
totalCol = 2
|
||||
usedCol = 3
|
||||
// priorityCol = 4
|
||||
)
|
||||
|
||||
func SwapDevices() ([]*SwapDevice, error) {
|
||||
return SwapDevicesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
|
||||
swapsFilePath := common.HostProc(swapsFilename)
|
||||
f, err := os.Open(swapsFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return parseSwapsFile(f)
|
||||
}
|
||||
|
||||
func parseSwapsFile(r io.Reader) ([]*SwapDevice, error) {
|
||||
swapsFilePath := common.HostProc(swapsFilename)
|
||||
scanner := bufio.NewScanner(r)
|
||||
if !scanner.Scan() {
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("couldn't read file %q: %w", swapsFilePath, err)
|
||||
}
|
||||
return nil, fmt.Errorf("unexpected end-of-file in %q", swapsFilePath)
|
||||
|
||||
}
|
||||
|
||||
// Check header headerFields are as expected
|
||||
headerFields := strings.Fields(scanner.Text())
|
||||
if len(headerFields) < usedCol {
|
||||
return nil, fmt.Errorf("couldn't parse %q: too few fields in header", swapsFilePath)
|
||||
}
|
||||
if headerFields[nameCol] != "Filename" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapsFilePath, headerFields[nameCol], "Filename")
|
||||
}
|
||||
if headerFields[totalCol] != "Size" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapsFilePath, headerFields[totalCol], "Size")
|
||||
}
|
||||
if headerFields[usedCol] != "Used" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapsFilePath, headerFields[usedCol], "Used")
|
||||
}
|
||||
|
||||
var swapDevices []*SwapDevice
|
||||
for scanner.Scan() {
|
||||
fields := strings.Fields(scanner.Text())
|
||||
if len(fields) < usedCol {
|
||||
return nil, fmt.Errorf("couldn't parse %q: too few fields", swapsFilePath)
|
||||
}
|
||||
|
||||
totalKiB, err := strconv.ParseUint(fields[totalCol], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse 'Size' column in %q: %w", swapsFilePath, err)
|
||||
}
|
||||
|
||||
usedKiB, err := strconv.ParseUint(fields[usedCol], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse 'Used' column in %q: %w", swapsFilePath, err)
|
||||
}
|
||||
|
||||
swapDevices = append(swapDevices, &SwapDevice{
|
||||
Name: fields[nameCol],
|
||||
UsedBytes: usedKiB * 1024,
|
||||
FreeBytes: (totalKiB - usedKiB) * 1024,
|
||||
})
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("couldn't read file %q: %w", swapsFilePath, err)
|
||||
}
|
||||
|
||||
return swapDevices, nil
|
||||
}
|
||||
|
||||
+6
-25
@@ -11,6 +11,7 @@ import (
|
||||
"os/exec"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func GetPageSize() (uint64, error) {
|
||||
@@ -18,17 +19,7 @@ func GetPageSize() (uint64, error) {
|
||||
}
|
||||
|
||||
func GetPageSizeWithContext(ctx context.Context) (uint64, error) {
|
||||
mib := []int32{CTLVm, VmUvmexp}
|
||||
buf, length, err := common.CallSyscall(mib)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if length < sizeOfUvmexp {
|
||||
return 0, fmt.Errorf("short syscall ret %d bytes", length)
|
||||
}
|
||||
var uvmexp Uvmexp
|
||||
br := bytes.NewReader(buf)
|
||||
err = common.Read(br, binary.LittleEndian, &uvmexp)
|
||||
uvmexp, err := unix.SysctlUvmexp("vm.uvmexp")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -40,17 +31,7 @@ func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
mib := []int32{CTLVm, VmUvmexp}
|
||||
buf, length, err := common.CallSyscall(mib)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if length < sizeOfUvmexp {
|
||||
return nil, fmt.Errorf("short syscall ret %d bytes", length)
|
||||
}
|
||||
var uvmexp Uvmexp
|
||||
br := bytes.NewReader(buf)
|
||||
err = common.Read(br, binary.LittleEndian, &uvmexp)
|
||||
uvmexp, err := unix.SysctlUvmexp("vm.uvmexp")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -69,8 +50,8 @@ func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
ret.Used = ret.Total - ret.Available
|
||||
ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0
|
||||
|
||||
mib = []int32{CTLVfs, VfsGeneric, VfsBcacheStat}
|
||||
buf, length, err = common.CallSyscall(mib)
|
||||
mib := []int32{CTLVfs, VfsGeneric, VfsBcacheStat}
|
||||
buf, length, err := common.CallSyscall(mib)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -78,7 +59,7 @@ func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
return nil, fmt.Errorf("short syscall ret %d bytes", length)
|
||||
}
|
||||
var bcs Bcachestats
|
||||
br = bytes.NewReader(buf)
|
||||
br := bytes.NewReader(buf)
|
||||
err = common.Read(br, binary.LittleEndian, &bcs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// +build openbsd,386
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs mem/types_openbsd.go
|
||||
|
||||
package mem
|
||||
|
||||
const (
|
||||
CTLVfs = 10
|
||||
VfsGeneric = 0
|
||||
VfsBcacheStat = 3
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfBcachestats = 0x90
|
||||
)
|
||||
|
||||
type Bcachestats struct {
|
||||
Numbufs int64
|
||||
Numbufpages int64
|
||||
Numdirtypages int64
|
||||
Numcleanpages int64
|
||||
Pendingwrites int64
|
||||
Pendingreads int64
|
||||
Numwrites int64
|
||||
Numreads int64
|
||||
Cachehits int64
|
||||
Busymapped int64
|
||||
Dmapages int64
|
||||
Highpages int64
|
||||
Delwribufs int64
|
||||
Kvaslots int64
|
||||
Avail int64
|
||||
Highflips int64
|
||||
Highflops int64
|
||||
Dmaflips int64
|
||||
}
|
||||
-90
@@ -4,105 +4,15 @@
|
||||
package mem
|
||||
|
||||
const (
|
||||
CTLVm = 2
|
||||
CTLVfs = 10
|
||||
VmUvmexp = 4
|
||||
VfsGeneric = 0
|
||||
VfsBcacheStat = 3
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfUvmexp = 0x154
|
||||
sizeOfBcachestats = 0x78
|
||||
)
|
||||
|
||||
type Uvmexp struct {
|
||||
Pagesize int32
|
||||
Pagemask int32
|
||||
Pageshift int32
|
||||
Npages int32
|
||||
Free int32
|
||||
Active int32
|
||||
Inactive int32
|
||||
Paging int32
|
||||
Wired int32
|
||||
Zeropages int32
|
||||
Reserve_pagedaemon int32
|
||||
Reserve_kernel int32
|
||||
Anonpages int32
|
||||
Vnodepages int32
|
||||
Vtextpages int32
|
||||
Freemin int32
|
||||
Freetarg int32
|
||||
Inactarg int32
|
||||
Wiredmax int32
|
||||
Anonmin int32
|
||||
Vtextmin int32
|
||||
Vnodemin int32
|
||||
Anonminpct int32
|
||||
Vtextminpct int32
|
||||
Vnodeminpct int32
|
||||
Nswapdev int32
|
||||
Swpages int32
|
||||
Swpginuse int32
|
||||
Swpgonly int32
|
||||
Nswget int32
|
||||
Nanon int32
|
||||
Nanonneeded int32
|
||||
Nfreeanon int32
|
||||
Faults int32
|
||||
Traps int32
|
||||
Intrs int32
|
||||
Swtch int32
|
||||
Softs int32
|
||||
Syscalls int32
|
||||
Pageins int32
|
||||
Obsolete_swapins int32
|
||||
Obsolete_swapouts int32
|
||||
Pgswapin int32
|
||||
Pgswapout int32
|
||||
Forks int32
|
||||
Forks_ppwait int32
|
||||
Forks_sharevm int32
|
||||
Pga_zerohit int32
|
||||
Pga_zeromiss int32
|
||||
Zeroaborts int32
|
||||
Fltnoram int32
|
||||
Fltnoanon int32
|
||||
Fltpgwait int32
|
||||
Fltpgrele int32
|
||||
Fltrelck int32
|
||||
Fltrelckok int32
|
||||
Fltanget int32
|
||||
Fltanretry int32
|
||||
Fltamcopy int32
|
||||
Fltnamap int32
|
||||
Fltnomap int32
|
||||
Fltlget int32
|
||||
Fltget int32
|
||||
Flt_anon int32
|
||||
Flt_acow int32
|
||||
Flt_obj int32
|
||||
Flt_prcopy int32
|
||||
Flt_przero int32
|
||||
Pdwoke int32
|
||||
Pdrevs int32
|
||||
Pdswout int32
|
||||
Pdfreed int32
|
||||
Pdscans int32
|
||||
Pdanscan int32
|
||||
Pdobscan int32
|
||||
Pdreact int32
|
||||
Pdbusy int32
|
||||
Pdpageouts int32
|
||||
Pdpending int32
|
||||
Pddeact int32
|
||||
Pdreanon int32
|
||||
Pdrevnode int32
|
||||
Pdrevtext int32
|
||||
Fpswtch int32
|
||||
Kmapent int32
|
||||
}
|
||||
type Bcachestats struct {
|
||||
Numbufs int64
|
||||
Numbufpages int64
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// +build openbsd,arm64
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs mem/types_openbsd.go
|
||||
|
||||
package mem
|
||||
|
||||
const (
|
||||
CTLVfs = 10
|
||||
VfsGeneric = 0
|
||||
VfsBcacheStat = 3
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfBcachestats = 0x90
|
||||
)
|
||||
|
||||
type Bcachestats struct {
|
||||
Numbufs int64
|
||||
Numbufpages int64
|
||||
Numdirtypages int64
|
||||
Numcleanpages int64
|
||||
Pendingwrites int64
|
||||
Pendingreads int64
|
||||
Numwrites int64
|
||||
Numreads int64
|
||||
Cachehits int64
|
||||
Busymapped int64
|
||||
Dmapages int64
|
||||
Highpages int64
|
||||
Delwribufs int64
|
||||
Kvaslots int64
|
||||
Avail int64
|
||||
Highflips int64
|
||||
Highflops int64
|
||||
Dmaflips int64
|
||||
}
|
||||
+90
-7
@@ -1,8 +1,9 @@
|
||||
// +build solaris
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
@@ -52,7 +53,7 @@ func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
}
|
||||
|
||||
func zoneName() (string, error) {
|
||||
zonename, err := exec.LookPath("/usr/bin/zonename")
|
||||
zonename, err := exec.LookPath("zonename")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -66,10 +67,10 @@ func zoneName() (string, error) {
|
||||
return strings.TrimSpace(string(out)), nil
|
||||
}
|
||||
|
||||
var globalZoneMemoryCapacityMatch = regexp.MustCompile(`memory size: ([\d]+) Megabytes`)
|
||||
var globalZoneMemoryCapacityMatch = regexp.MustCompile(`[Mm]emory size: (\d+) Megabytes`)
|
||||
|
||||
func globalZoneMemoryCapacity() (uint64, error) {
|
||||
prtconf, err := exec.LookPath("/usr/sbin/prtconf")
|
||||
prtconf, err := exec.LookPath("prtconf")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -82,7 +83,7 @@ func globalZoneMemoryCapacity() (uint64, error) {
|
||||
|
||||
match := globalZoneMemoryCapacityMatch.FindAllStringSubmatch(string(out), -1)
|
||||
if len(match) != 1 {
|
||||
return 0, errors.New("memory size not contained in output of /usr/sbin/prtconf")
|
||||
return 0, fmt.Errorf("memory size not contained in output of %q", prtconf)
|
||||
}
|
||||
|
||||
totalMB, err := strconv.ParseUint(match[0][1], 10, 64)
|
||||
@@ -93,10 +94,10 @@ func globalZoneMemoryCapacity() (uint64, error) {
|
||||
return totalMB * 1024 * 1024, nil
|
||||
}
|
||||
|
||||
var kstatMatch = regexp.MustCompile(`([^\s]+)[\s]+([^\s]*)`)
|
||||
var kstatMatch = regexp.MustCompile(`(\S+)\s+(\S*)`)
|
||||
|
||||
func nonGlobalZoneMemoryCapacity() (uint64, error) {
|
||||
kstat, err := exec.LookPath("/usr/bin/kstat")
|
||||
kstat, err := exec.LookPath("kstat")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -119,3 +120,85 @@ func nonGlobalZoneMemoryCapacity() (uint64, error) {
|
||||
|
||||
return memSizeBytes, nil
|
||||
}
|
||||
|
||||
const swapCommand = "swap"
|
||||
|
||||
// The blockSize as reported by `swap -l`. See https://docs.oracle.com/cd/E23824_01/html/821-1459/fsswap-52195.html
|
||||
const blockSize = 512
|
||||
|
||||
// swapctl column indexes
|
||||
const (
|
||||
nameCol = 0
|
||||
// devCol = 1
|
||||
// swaploCol = 2
|
||||
totalBlocksCol = 3
|
||||
freeBlocksCol = 4
|
||||
)
|
||||
|
||||
func SwapDevices() ([]*SwapDevice, error) {
|
||||
return SwapDevicesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
|
||||
swapCommandPath, err := exec.LookPath(swapCommand)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find command %q: %w", swapCommand, err)
|
||||
}
|
||||
output, err := invoke.CommandWithContext(ctx, swapCommandPath, "-l")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not execute %q: %w", swapCommand, err)
|
||||
}
|
||||
|
||||
return parseSwapsCommandOutput(string(output))
|
||||
}
|
||||
|
||||
func parseSwapsCommandOutput(output string) ([]*SwapDevice, error) {
|
||||
lines := strings.Split(output, "\n")
|
||||
if len(lines) == 0 {
|
||||
return nil, fmt.Errorf("could not parse output of %q: no lines in %q", swapCommand, output)
|
||||
}
|
||||
|
||||
// Check header headerFields are as expected.
|
||||
headerFields := strings.Fields(lines[0])
|
||||
if len(headerFields) < freeBlocksCol {
|
||||
return nil, fmt.Errorf("couldn't parse %q: too few fields in header %q", swapCommand, lines[0])
|
||||
}
|
||||
if headerFields[nameCol] != "swapfile" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[nameCol], "swapfile")
|
||||
}
|
||||
if headerFields[totalBlocksCol] != "blocks" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[totalBlocksCol], "blocks")
|
||||
}
|
||||
if headerFields[freeBlocksCol] != "free" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[freeBlocksCol], "free")
|
||||
}
|
||||
|
||||
var swapDevices []*SwapDevice
|
||||
for _, line := range lines[1:] {
|
||||
if line == "" {
|
||||
continue // the terminal line is typically empty
|
||||
}
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < freeBlocksCol {
|
||||
return nil, fmt.Errorf("couldn't parse %q: too few fields", swapCommand)
|
||||
}
|
||||
|
||||
totalBlocks, err := strconv.ParseUint(fields[totalBlocksCol], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse 'Size' column in %q: %w", swapCommand, err)
|
||||
}
|
||||
|
||||
freeBlocks, err := strconv.ParseUint(fields[freeBlocksCol], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse 'Used' column in %q: %w", swapCommand, err)
|
||||
}
|
||||
|
||||
swapDevices = append(swapDevices, &SwapDevice{
|
||||
Name: fields[nameCol],
|
||||
UsedBytes: (totalBlocks - freeBlocks) * blockSize,
|
||||
FreeBytes: freeBlocks * blockSize,
|
||||
})
|
||||
}
|
||||
|
||||
return swapDevices, nil
|
||||
}
|
||||
|
||||
+70
-2
@@ -4,6 +4,8 @@ package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
@@ -11,8 +13,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
procGlobalMemoryStatusEx = common.Modkernel32.NewProc("GlobalMemoryStatusEx")
|
||||
procEnumPageFilesW = common.ModPsapi.NewProc("EnumPageFilesW")
|
||||
procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo")
|
||||
procGetPerformanceInfo = common.ModPsapi.NewProc("GetPerformanceInfo")
|
||||
procGlobalMemoryStatusEx = common.Modkernel32.NewProc("GlobalMemoryStatusEx")
|
||||
)
|
||||
|
||||
type memoryStatusEx struct {
|
||||
@@ -42,6 +46,7 @@ func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
ret := &VirtualMemoryStat{
|
||||
Total: memInfo.ullTotalPhys,
|
||||
Available: memInfo.ullAvailPhys,
|
||||
Free: memInfo.ullAvailPhys,
|
||||
UsedPercent: float64(memInfo.dwMemoryLoad),
|
||||
}
|
||||
|
||||
@@ -84,7 +89,7 @@ func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
if tot == 0 {
|
||||
usedPercent = 0
|
||||
} else {
|
||||
usedPercent = float64(used) / float64(tot)
|
||||
usedPercent = float64(used) / float64(tot) * 100
|
||||
}
|
||||
ret := &SwapMemoryStat{
|
||||
Total: tot,
|
||||
@@ -95,3 +100,66 @@ func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
var (
|
||||
pageSize uint64
|
||||
pageSizeOnce sync.Once
|
||||
)
|
||||
|
||||
type systemInfo struct {
|
||||
wProcessorArchitecture uint16
|
||||
wReserved uint16
|
||||
dwPageSize uint32
|
||||
lpMinimumApplicationAddress uintptr
|
||||
lpMaximumApplicationAddress uintptr
|
||||
dwActiveProcessorMask uintptr
|
||||
dwNumberOfProcessors uint32
|
||||
dwProcessorType uint32
|
||||
dwAllocationGranularity uint32
|
||||
wProcessorLevel uint16
|
||||
wProcessorRevision uint16
|
||||
}
|
||||
|
||||
// system type as defined in https://docs.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-enum_page_file_information
|
||||
type enumPageFileInformation struct {
|
||||
cb uint32
|
||||
reserved uint32
|
||||
totalSize uint64
|
||||
totalInUse uint64
|
||||
peakUsage uint64
|
||||
}
|
||||
|
||||
func SwapDevices() ([]*SwapDevice, error) {
|
||||
return SwapDevicesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
|
||||
pageSizeOnce.Do(func() {
|
||||
var sysInfo systemInfo
|
||||
procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&sysInfo)))
|
||||
pageSize = uint64(sysInfo.dwPageSize)
|
||||
})
|
||||
|
||||
// the following system call invokes the supplied callback function once for each page file before returning
|
||||
// see https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumpagefilesw
|
||||
var swapDevices []*SwapDevice
|
||||
result, _, _ := procEnumPageFilesW.Call(windows.NewCallback(pEnumPageFileCallbackW), uintptr(unsafe.Pointer(&swapDevices)))
|
||||
if result == 0 {
|
||||
return nil, windows.GetLastError()
|
||||
}
|
||||
|
||||
return swapDevices, nil
|
||||
}
|
||||
|
||||
// system callback as defined in https://docs.microsoft.com/en-us/windows/win32/api/psapi/nc-psapi-penum_page_file_callbackw
|
||||
func pEnumPageFileCallbackW(swapDevices *[]*SwapDevice, enumPageFileInfo *enumPageFileInformation, lpFilenamePtr *[syscall.MAX_LONG_PATH]uint16) *bool {
|
||||
*swapDevices = append(*swapDevices, &SwapDevice{
|
||||
Name: syscall.UTF16ToString((*lpFilenamePtr)[:]),
|
||||
UsedBytes: enumPageFileInfo.totalInUse * pageSize,
|
||||
FreeBytes: (enumPageFileInfo.totalSize - enumPageFileInfo.totalInUse) * pageSize,
|
||||
})
|
||||
|
||||
// return true to continue enumerating page files
|
||||
ret := true
|
||||
return &ret
|
||||
}
|
||||
|
||||
+95
-91
@@ -3,11 +3,7 @@ package net
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
)
|
||||
@@ -58,6 +54,7 @@ type InterfaceAddr struct {
|
||||
}
|
||||
|
||||
type InterfaceStat struct {
|
||||
Index int `json:"index"`
|
||||
MTU int `json:"mtu"` // maximum transmission unit
|
||||
Name string `json:"name"` // e.g., "en0", "lo0", "eth0.100"
|
||||
HardwareAddr string `json:"hardwareaddr"` // IEEE MAC-48, EUI-48 and EUI-64 form
|
||||
@@ -70,12 +67,94 @@ type FilterStat struct {
|
||||
ConnTrackMax int64 `json:"conntrackMax"`
|
||||
}
|
||||
|
||||
var constMap = map[string]int{
|
||||
"unix": syscall.AF_UNIX,
|
||||
"TCP": syscall.SOCK_STREAM,
|
||||
"UDP": syscall.SOCK_DGRAM,
|
||||
"IPv4": syscall.AF_INET,
|
||||
"IPv6": syscall.AF_INET6,
|
||||
// ConntrackStat has conntrack summary info
|
||||
type ConntrackStat struct {
|
||||
Entries uint32 `json:"entries"` // Number of entries in the conntrack table
|
||||
Searched uint32 `json:"searched"` // Number of conntrack table lookups performed
|
||||
Found uint32 `json:"found"` // Number of searched entries which were successful
|
||||
New uint32 `json:"new"` // Number of entries added which were not expected before
|
||||
Invalid uint32 `json:"invalid"` // Number of packets seen which can not be tracked
|
||||
Ignore uint32 `json:"ignore"` // Packets seen which are already connected to an entry
|
||||
Delete uint32 `json:"delete"` // Number of entries which were removed
|
||||
DeleteList uint32 `json:"delete_list"` // Number of entries which were put to dying list
|
||||
Insert uint32 `json:"insert"` // Number of entries inserted into the list
|
||||
InsertFailed uint32 `json:"insert_failed"` // # insertion attempted but failed (same entry exists)
|
||||
Drop uint32 `json:"drop"` // Number of packets dropped due to conntrack failure.
|
||||
EarlyDrop uint32 `json:"early_drop"` // Dropped entries to make room for new ones, if maxsize reached
|
||||
IcmpError uint32 `json:"icmp_error"` // Subset of invalid. Packets that can't be tracked d/t error
|
||||
ExpectNew uint32 `json:"expect_new"` // Entries added after an expectation was already present
|
||||
ExpectCreate uint32 `json:"expect_create"` // Expectations added
|
||||
ExpectDelete uint32 `json:"expect_delete"` // Expectations deleted
|
||||
SearchRestart uint32 `json:"search_restart"` // Conntrack table lookups restarted due to hashtable resizes
|
||||
}
|
||||
|
||||
func NewConntrackStat(e uint32, s uint32, f uint32, n uint32, inv uint32, ign uint32, del uint32, dlst uint32, ins uint32, insfail uint32, drop uint32, edrop uint32, ie uint32, en uint32, ec uint32, ed uint32, sr uint32) *ConntrackStat {
|
||||
return &ConntrackStat{
|
||||
Entries: e,
|
||||
Searched: s,
|
||||
Found: f,
|
||||
New: n,
|
||||
Invalid: inv,
|
||||
Ignore: ign,
|
||||
Delete: del,
|
||||
DeleteList: dlst,
|
||||
Insert: ins,
|
||||
InsertFailed: insfail,
|
||||
Drop: drop,
|
||||
EarlyDrop: edrop,
|
||||
IcmpError: ie,
|
||||
ExpectNew: en,
|
||||
ExpectCreate: ec,
|
||||
ExpectDelete: ed,
|
||||
SearchRestart: sr,
|
||||
}
|
||||
}
|
||||
|
||||
type ConntrackStatList struct {
|
||||
items []*ConntrackStat
|
||||
}
|
||||
|
||||
func NewConntrackStatList() *ConntrackStatList {
|
||||
return &ConntrackStatList{
|
||||
items: []*ConntrackStat{},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ConntrackStatList) Append(c *ConntrackStat) {
|
||||
l.items = append(l.items, c)
|
||||
}
|
||||
|
||||
func (l *ConntrackStatList) Items() []ConntrackStat {
|
||||
items := make([]ConntrackStat, len(l.items), len(l.items))
|
||||
for i, el := range l.items {
|
||||
items[i] = *el
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// Summary returns a single-element list with totals from all list items.
|
||||
func (l *ConntrackStatList) Summary() []ConntrackStat {
|
||||
summary := NewConntrackStat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
for _, cs := range l.items {
|
||||
summary.Entries += cs.Entries
|
||||
summary.Searched += cs.Searched
|
||||
summary.Found += cs.Found
|
||||
summary.New += cs.New
|
||||
summary.Invalid += cs.Invalid
|
||||
summary.Ignore += cs.Ignore
|
||||
summary.Delete += cs.Delete
|
||||
summary.DeleteList += cs.DeleteList
|
||||
summary.Insert += cs.Insert
|
||||
summary.InsertFailed += cs.InsertFailed
|
||||
summary.Drop += cs.Drop
|
||||
summary.EarlyDrop += cs.EarlyDrop
|
||||
summary.IcmpError += cs.IcmpError
|
||||
summary.ExpectNew += cs.ExpectNew
|
||||
summary.ExpectCreate += cs.ExpectCreate
|
||||
summary.ExpectDelete += cs.ExpectDelete
|
||||
summary.SearchRestart += cs.SearchRestart
|
||||
}
|
||||
return []ConntrackStat{*summary}
|
||||
}
|
||||
|
||||
func (n IOCountersStat) String() string {
|
||||
@@ -108,6 +187,11 @@ func (n InterfaceAddr) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (n ConntrackStat) String() string {
|
||||
s, _ := json.Marshal(n)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func Interfaces() ([]InterfaceStat, error) {
|
||||
return InterfacesWithContext(context.Background())
|
||||
}
|
||||
@@ -138,6 +222,7 @@ func InterfacesWithContext(ctx context.Context) ([]InterfaceStat, error) {
|
||||
}
|
||||
|
||||
r := InterfaceStat{
|
||||
Index: ifi.Index,
|
||||
Name: ifi.Name,
|
||||
MTU: ifi.MTU,
|
||||
HardwareAddr: ifi.HardwareAddr.String(),
|
||||
@@ -176,84 +261,3 @@ func getIOCountersAll(n []IOCountersStat) ([]IOCountersStat, error) {
|
||||
|
||||
return []IOCountersStat{r}, nil
|
||||
}
|
||||
|
||||
func parseNetLine(line string) (ConnectionStat, error) {
|
||||
f := strings.Fields(line)
|
||||
if len(f) < 8 {
|
||||
return ConnectionStat{}, fmt.Errorf("wrong line,%s", line)
|
||||
}
|
||||
|
||||
if len(f) == 8 {
|
||||
f = append(f, f[7])
|
||||
f[7] = "unix"
|
||||
}
|
||||
|
||||
pid, err := strconv.Atoi(f[1])
|
||||
if err != nil {
|
||||
return ConnectionStat{}, err
|
||||
}
|
||||
fd, err := strconv.Atoi(strings.Trim(f[3], "u"))
|
||||
if err != nil {
|
||||
return ConnectionStat{}, fmt.Errorf("unknown fd, %s", f[3])
|
||||
}
|
||||
netFamily, ok := constMap[f[4]]
|
||||
if !ok {
|
||||
return ConnectionStat{}, fmt.Errorf("unknown family, %s", f[4])
|
||||
}
|
||||
netType, ok := constMap[f[7]]
|
||||
if !ok {
|
||||
return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[7])
|
||||
}
|
||||
|
||||
var laddr, raddr Addr
|
||||
if f[7] == "unix" {
|
||||
laddr.IP = f[8]
|
||||
} else {
|
||||
laddr, raddr, err = parseNetAddr(f[8])
|
||||
if err != nil {
|
||||
return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s", f[8])
|
||||
}
|
||||
}
|
||||
|
||||
n := ConnectionStat{
|
||||
Fd: uint32(fd),
|
||||
Family: uint32(netFamily),
|
||||
Type: uint32(netType),
|
||||
Laddr: laddr,
|
||||
Raddr: raddr,
|
||||
Pid: int32(pid),
|
||||
}
|
||||
if len(f) == 10 {
|
||||
n.Status = strings.Trim(f[9], "()")
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func parseNetAddr(line string) (laddr Addr, raddr Addr, err error) {
|
||||
parse := func(l string) (Addr, error) {
|
||||
host, port, err := net.SplitHostPort(l)
|
||||
if err != nil {
|
||||
return Addr{}, fmt.Errorf("wrong addr, %s", l)
|
||||
}
|
||||
lport, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return Addr{}, err
|
||||
}
|
||||
return Addr{IP: host, Port: uint32(lport)}, nil
|
||||
}
|
||||
|
||||
addrs := strings.Split(line, "->")
|
||||
if len(addrs) == 0 {
|
||||
return laddr, raddr, fmt.Errorf("wrong netaddr, %s", line)
|
||||
}
|
||||
laddr, err = parse(addrs[0])
|
||||
if len(addrs) == 2 { // remote addr exists
|
||||
raddr, err = parse(addrs[1])
|
||||
if err != nil {
|
||||
return laddr, raddr, err
|
||||
}
|
||||
}
|
||||
|
||||
return laddr, raddr, err
|
||||
}
|
||||
|
||||
+425
@@ -0,0 +1,425 @@
|
||||
// +build aix
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
)
|
||||
|
||||
func parseNetstatI(output string) ([]IOCountersStat, error) {
|
||||
lines := strings.Split(string(output), "\n")
|
||||
ret := make([]IOCountersStat, 0, len(lines)-1)
|
||||
exists := make([]string, 0, len(ret))
|
||||
|
||||
// Check first line is header
|
||||
if len(lines) > 0 && strings.Fields(lines[0])[0] != "Name" {
|
||||
return nil, fmt.Errorf("not a 'netstat -i' output")
|
||||
}
|
||||
|
||||
for _, line := range lines[1:] {
|
||||
values := strings.Fields(line)
|
||||
if len(values) < 1 || values[0] == "Name" {
|
||||
continue
|
||||
}
|
||||
if common.StringsHas(exists, values[0]) {
|
||||
// skip if already get
|
||||
continue
|
||||
}
|
||||
exists = append(exists, values[0])
|
||||
|
||||
if len(values) < 9 {
|
||||
continue
|
||||
}
|
||||
|
||||
base := 1
|
||||
// sometimes Address is omitted
|
||||
if len(values) < 10 {
|
||||
base = 0
|
||||
}
|
||||
|
||||
parsed := make([]uint64, 0, 5)
|
||||
vv := []string{
|
||||
values[base+3], // Ipkts == PacketsRecv
|
||||
values[base+4], // Ierrs == Errin
|
||||
values[base+5], // Opkts == PacketsSent
|
||||
values[base+6], // Oerrs == Errout
|
||||
values[base+8], // Drops == Dropout
|
||||
}
|
||||
|
||||
for _, target := range vv {
|
||||
if target == "-" {
|
||||
parsed = append(parsed, 0)
|
||||
continue
|
||||
}
|
||||
|
||||
t, err := strconv.ParseUint(target, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parsed = append(parsed, t)
|
||||
}
|
||||
|
||||
n := IOCountersStat{
|
||||
Name: values[0],
|
||||
PacketsRecv: parsed[0],
|
||||
Errin: parsed[1],
|
||||
PacketsSent: parsed[2],
|
||||
Errout: parsed[3],
|
||||
Dropout: parsed[4],
|
||||
}
|
||||
ret = append(ret, n)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func IOCounters(pernic bool) ([]IOCountersStat, error) {
|
||||
return IOCountersWithContext(context.Background(), pernic)
|
||||
}
|
||||
|
||||
func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
|
||||
netstat, err := exec.LookPath("netstat")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, netstat, "-idn")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iocounters, err := parseNetstatI(string(out))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pernic == false {
|
||||
return getIOCountersAll(iocounters)
|
||||
}
|
||||
return iocounters, nil
|
||||
|
||||
}
|
||||
|
||||
// NetIOCountersByFile is an method which is added just a compatibility for linux.
|
||||
func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
|
||||
return IOCountersByFileWithContext(context.Background(), pernic, filename)
|
||||
}
|
||||
|
||||
func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
|
||||
return IOCounters(pernic)
|
||||
}
|
||||
|
||||
func FilterCounters() ([]FilterStat, error) {
|
||||
return FilterCountersWithContext(context.Background())
|
||||
}
|
||||
|
||||
func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
|
||||
return ConntrackStatsWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
|
||||
return ProtoCountersWithContext(context.Background(), protocols)
|
||||
}
|
||||
|
||||
func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func parseNetstatNetLine(line string) (ConnectionStat, error) {
|
||||
f := strings.Fields(line)
|
||||
if len(f) < 5 {
|
||||
return ConnectionStat{}, fmt.Errorf("wrong line,%s", line)
|
||||
}
|
||||
|
||||
var netType, netFamily uint32
|
||||
switch f[0] {
|
||||
case "tcp", "tcp4":
|
||||
netType = syscall.SOCK_STREAM
|
||||
netFamily = syscall.AF_INET
|
||||
case "udp", "udp4":
|
||||
netType = syscall.SOCK_DGRAM
|
||||
netFamily = syscall.AF_INET
|
||||
case "tcp6":
|
||||
netType = syscall.SOCK_STREAM
|
||||
netFamily = syscall.AF_INET6
|
||||
case "udp6":
|
||||
netType = syscall.SOCK_DGRAM
|
||||
netFamily = syscall.AF_INET6
|
||||
default:
|
||||
return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[0])
|
||||
}
|
||||
|
||||
laddr, raddr, err := parseNetstatAddr(f[3], f[4], netFamily)
|
||||
if err != nil {
|
||||
return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s %s", f[3], f[4])
|
||||
}
|
||||
|
||||
n := ConnectionStat{
|
||||
Fd: uint32(0), // not supported
|
||||
Family: uint32(netFamily),
|
||||
Type: uint32(netType),
|
||||
Laddr: laddr,
|
||||
Raddr: raddr,
|
||||
Pid: int32(0), // not supported
|
||||
}
|
||||
if len(f) == 6 {
|
||||
n.Status = f[5]
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`)
|
||||
|
||||
// This function only works for netstat returning addresses with a "."
|
||||
// before the port (0.0.0.0.22 instead of 0.0.0.0:22).
|
||||
func parseNetstatAddr(local string, remote string, family uint32) (laddr Addr, raddr Addr, err error) {
|
||||
parse := func(l string) (Addr, error) {
|
||||
matches := portMatch.FindStringSubmatch(l)
|
||||
if matches == nil {
|
||||
return Addr{}, fmt.Errorf("wrong addr, %s", l)
|
||||
}
|
||||
host := matches[1]
|
||||
port := matches[2]
|
||||
if host == "*" {
|
||||
switch family {
|
||||
case syscall.AF_INET:
|
||||
host = "0.0.0.0"
|
||||
case syscall.AF_INET6:
|
||||
host = "::"
|
||||
default:
|
||||
return Addr{}, fmt.Errorf("unknown family, %d", family)
|
||||
}
|
||||
}
|
||||
lport, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return Addr{}, err
|
||||
}
|
||||
return Addr{IP: host, Port: uint32(lport)}, nil
|
||||
}
|
||||
|
||||
laddr, err = parse(local)
|
||||
if remote != "*.*" { // remote addr exists
|
||||
raddr, err = parse(remote)
|
||||
if err != nil {
|
||||
return laddr, raddr, err
|
||||
}
|
||||
}
|
||||
|
||||
return laddr, raddr, err
|
||||
}
|
||||
|
||||
func parseNetstatUnixLine(f []string) (ConnectionStat, error) {
|
||||
if len(f) < 8 {
|
||||
return ConnectionStat{}, fmt.Errorf("wrong number of fields: expected >=8 got %d", len(f))
|
||||
}
|
||||
|
||||
var netType uint32
|
||||
|
||||
switch f[1] {
|
||||
case "dgram":
|
||||
netType = syscall.SOCK_DGRAM
|
||||
case "stream":
|
||||
netType = syscall.SOCK_STREAM
|
||||
default:
|
||||
return ConnectionStat{}, fmt.Errorf("unknown type: %s", f[1])
|
||||
}
|
||||
|
||||
// Some Unix Socket don't have any address associated
|
||||
addr := ""
|
||||
if len(f) == 9 {
|
||||
addr = f[8]
|
||||
}
|
||||
|
||||
c := ConnectionStat{
|
||||
Fd: uint32(0), // not supported
|
||||
Family: uint32(syscall.AF_UNIX),
|
||||
Type: uint32(netType),
|
||||
Laddr: Addr{
|
||||
IP: addr,
|
||||
},
|
||||
Status: "NONE",
|
||||
Pid: int32(0), // not supported
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Return true if proto is the corresponding to the kind parameter
|
||||
// Only for Inet lines
|
||||
func hasCorrectInetProto(kind, proto string) bool {
|
||||
switch kind {
|
||||
case "all", "inet":
|
||||
return true
|
||||
case "unix":
|
||||
return false
|
||||
case "inet4":
|
||||
return !strings.HasSuffix(proto, "6")
|
||||
case "inet6":
|
||||
return strings.HasSuffix(proto, "6")
|
||||
case "tcp":
|
||||
return proto == "tcp" || proto == "tcp4" || proto == "tcp6"
|
||||
case "tcp4":
|
||||
return proto == "tcp" || proto == "tcp4"
|
||||
case "tcp6":
|
||||
return proto == "tcp6"
|
||||
case "udp":
|
||||
return proto == "udp" || proto == "udp4" || proto == "udp6"
|
||||
case "udp4":
|
||||
return proto == "udp" || proto == "udp4"
|
||||
case "udp6":
|
||||
return proto == "udp6"
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func parseNetstatA(output string, kind string) ([]ConnectionStat, error) {
|
||||
var ret []ConnectionStat
|
||||
lines := strings.Split(string(output), "\n")
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(fields[0], "f1") {
|
||||
// Unix lines
|
||||
if len(fields) < 2 {
|
||||
// every unix connections have two lines
|
||||
continue
|
||||
}
|
||||
|
||||
c, err := parseNetstatUnixLine(fields)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse Unix Address (%s): %s", line, err)
|
||||
}
|
||||
|
||||
ret = append(ret, c)
|
||||
|
||||
} else if strings.HasPrefix(fields[0], "tcp") || strings.HasPrefix(fields[0], "udp") {
|
||||
// Inet lines
|
||||
if !hasCorrectInetProto(kind, fields[0]) {
|
||||
continue
|
||||
}
|
||||
|
||||
// On AIX, netstat display some connections with "*.*" as local addresses
|
||||
// Skip them as they aren't real connections.
|
||||
if fields[3] == "*.*" {
|
||||
continue
|
||||
}
|
||||
|
||||
c, err := parseNetstatNetLine(line)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse Inet Address (%s): %s", line, err)
|
||||
}
|
||||
|
||||
ret = append(ret, c)
|
||||
} else {
|
||||
// Header lines
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
|
||||
}
|
||||
|
||||
func Connections(kind string) ([]ConnectionStat, error) {
|
||||
return ConnectionsWithContext(context.Background(), kind)
|
||||
}
|
||||
|
||||
func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
|
||||
|
||||
args := []string{"-na"}
|
||||
switch strings.ToLower(kind) {
|
||||
default:
|
||||
fallthrough
|
||||
case "":
|
||||
kind = "all"
|
||||
case "all":
|
||||
// nothing to add
|
||||
case "inet", "inet4", "inet6":
|
||||
args = append(args, "-finet")
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
args = append(args, "-finet")
|
||||
case "udp", "udp4", "udp6":
|
||||
args = append(args, "-finet")
|
||||
case "unix":
|
||||
args = append(args, "-funix")
|
||||
}
|
||||
|
||||
netstat, err := exec.LookPath("netstat")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, netstat, args...)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err := parseNetstatA(string(out), kind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
|
||||
}
|
||||
|
||||
func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) {
|
||||
return ConnectionsMaxWithContext(context.Background(), kind, max)
|
||||
}
|
||||
|
||||
func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
|
||||
return []ConnectionStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
// Return a list of network connections opened, omitting `Uids`.
|
||||
// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be
|
||||
// removed from the API in the future.
|
||||
func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) {
|
||||
return ConnectionsWithoutUidsWithContext(context.Background(), kind)
|
||||
}
|
||||
|
||||
func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
|
||||
return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0)
|
||||
}
|
||||
|
||||
func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max)
|
||||
}
|
||||
|
||||
func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid)
|
||||
}
|
||||
|
||||
func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0)
|
||||
}
|
||||
|
||||
func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max)
|
||||
}
|
||||
|
||||
func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max)
|
||||
}
|
||||
|
||||
func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return []ConnectionStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
+14
-5
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -42,7 +43,7 @@ func parseNetstatLine(line string) (stat *IOCountersStat, linkID *uint, err erro
|
||||
|
||||
base := 1
|
||||
numberColumns := len(columns)
|
||||
// sometimes Address is ommitted
|
||||
// sometimes Address is omitted
|
||||
if numberColumns < 12 {
|
||||
base = 0
|
||||
}
|
||||
@@ -174,7 +175,7 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat,
|
||||
retIndex int
|
||||
)
|
||||
|
||||
netstat, err := exec.LookPath("/usr/sbin/netstat")
|
||||
netstat, err := exec.LookPath("netstat")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -204,7 +205,7 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat,
|
||||
}
|
||||
} else {
|
||||
// duplicated interface, list all interfaces
|
||||
ifconfig, err := exec.LookPath("/sbin/ifconfig")
|
||||
ifconfig, err := exec.LookPath("ifconfig")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -268,7 +269,15 @@ func FilterCounters() ([]FilterStat, error) {
|
||||
}
|
||||
|
||||
func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
|
||||
return nil, errors.New("NetFilterCounters not implemented for darwin")
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
|
||||
return ConntrackStatsWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
// NetProtoCounters returns network statistics for the entire system
|
||||
@@ -280,5 +289,5 @@ func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
|
||||
}
|
||||
|
||||
func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
|
||||
return nil, errors.New("NetProtoCounters not implemented for darwin")
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user