Merge pull request #13539 from ioito/hotfix/qx-cli-help

fix(climc): add --help for other cli
This commit is contained in:
Zexi Li
2022-02-24 23:48:48 +08:00
committed by GitHub
206 changed files with 14209 additions and 11082 deletions
+29 -41
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
}
+3 -2
View File
@@ -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
+12 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,9 @@
package cpu
type cpuTimes struct {
User uint64
Nice uint64
Sys uint64
Intr uint64
Idle uint64
}
+6 -1
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,9 @@
package cpu
type cpuTimes struct {
User uint32
Nice uint32
Sys uint32
Intr uint32
Idle uint32
}
+9
View File
@@ -0,0 +1,9 @@
package cpu
type cpuTimes struct {
User uint64
Nice uint64
Sys uint64
Intr uint64
Idle uint64
}
+146 -24
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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(&regBuf[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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,5 +1,4 @@
// +build darwin
// +build cgo
// +build darwin,cgo
package mem
+1 -2
View File
@@ -1,5 +1,4 @@
// +build darwin
// +build !cgo
// +build darwin,!cgo
package mem
+8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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