diff --git a/cmd/root.go b/cmd/root.go index 87cf77e..e519cfa 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,21 +2,17 @@ package cmd import ( "encoding/json" + "flag" "fmt" - "log/slog" + "log" "os" - "slices" "strings" - "github.com/numtide/nixos-facter/pkg/hwinfo" - "github.com/numtide/nixos-facter/pkg/facter" - - "github.com/spf13/cobra" + "github.com/numtide/nixos-facter/pkg/hwinfo" ) var ( - cfgFile string outputPath string logLevel string hardwareFeatures []string @@ -24,119 +20,97 @@ var ( scanner = facter.Scanner{} ) -// rootCmd represents the base command when called without any subcommands -var rootCmd = &cobra.Command{ - Use: "nixos-facter", - Short: "Hardware report generator", - // todo Long description - // todo add Long description - RunE: func(cmd *cobra.Command, args []string) error { - // check the effective user id is 0 e.g. root - if os.Geteuid() != 0 { - cmd.SilenceUsage = true - return fmt.Errorf("you must run this program as root") - } +const usage = `nixos-facter [flags] +Hardware report generator - // convert the hardware features into probe features - for _, str := range hardwareFeatures { - probe, err := hwinfo.ProbeFeatureString(str) - if err != nil { - return fmt.Errorf("invalid hardware feature: %w", err) - } - scanner.Features = append(scanner.Features, probe) - } +Usage: + nixos-facter [flags] - // set the log level - if logLevel != "" { - if logLevel == "debug" { - slog.SetLogLoggerLevel(slog.LevelDebug) - } else if logLevel == "info" { - slog.SetLogLoggerLevel(slog.LevelInfo) - } else if logLevel == "warn" { - slog.SetLogLoggerLevel(slog.LevelWarn) - } else if logLevel == "error" { - slog.SetLogLoggerLevel(slog.LevelError) - } else { - return fmt.Errorf("invalid log level: %s", logLevel) - } - } +Flags: + -e, --ephemeral capture all ephemeral properties e.g. swap, filesystems and so on + -f, --hardware strings Hardware items to probe. Possible values are memory,pci,isapnp,net,floppy,misc,misc_serial,misc_par,misc_floppy,serial,cpu,bios,monitor,mouse,scsi,usb,usb_mods,adb,modem,modem_usb,parallel,parallel_lp,parallel_zip,isa,isa_isdn,isdn,kbd,prom,sbus,braille,braille_alva,braille_fhp,braille_ht,ignx11,sys,bios_vbe,isapnp_old,isapnp_new,isapnp_mod,braille_baum,manual,fb,veth,pppoe,scan,pcmcia,fork,parallel_imm,s390,cpuemu,sysfs,s390disks,udev,block,block_cdrom,block_part,edd,edd_mod,bios_ddc,bios_fb,bios_mode,input,block_mods,bios_vesa,cpuemu_debug,scsi_noserial,wlan,bios_crc,hal,bios_vram,bios_acpi,bios_ddc_ports,modules_pata,net_eeprom,x86emu,max,lxrc,all,, (default [memory,pci,net,serial,cpu,bios,monitor,scsi,usb,prom,sbus,sys,sysfs,udev,block,wlan]) + -h, --help help for nixos-facter + -o, --output string path to write the report + -s, --swap capture swap entries - report, err := scanner.Scan() - if err != nil { - return err - } +` - bytes, err := json.MarshalIndent(report, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal report to json: %w", err) - } +func init() { + // Define flags + flag.StringVar(&outputPath, "output", "", "path to write the report") + flag.BoolVar(&scanner.Swap, "swap", false, "capture swap entries") + flag.BoolVar(&scanner.Ephemeral, "ephemeral", false, "capture all ephemeral properties e.g. swap, filesystems and so on") + flag.StringVar(&logLevel, "log-level", "info", "log level") + + defaultFeatures := []string{ + "memory", "pci", "net", "serial", "cpu", "bios", "monitor", "scsi", "usb", "prom", "sbus", "sys", "sysfs", + "udev", "block", "wlan", + } - // if a file path is provided write the report to it, otherwise output the report on stdout - if outputPath == "" { - if _, err = os.Stdout.Write(bytes); err != nil { - return fmt.Errorf("failed to write report to stdout: %w", err) - } - fmt.Println() - } else if err = os.WriteFile(outputPath, bytes, 0o644); err != nil { - return fmt.Errorf("failed to write report to output path: %w", err) + probeFeatures := hwinfo.ProbeFeatureStrings() + filteredFeatures := []string{} + for _, feature := range probeFeatures { + if feature != "default" && feature != "int" { + filteredFeatures = append(filteredFeatures, feature) } + } + hardwareFeatures = defaultFeatures + flag.Func("hardware", fmt.Sprintf("Hardware items to probe. Possible values are %s", strings.Join(filteredFeatures, ",")), func(flagValue string) error { + hardwareFeatures = strings.Split(flagValue, ",") return nil - }, + }) + + // Custom usage function + flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s\n", usage) } } -// Execute adds all child commands to the root command and sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { - err := rootCmd.Execute() - if err != nil { - os.Exit(1) - } -} + flag.Parse() -func init() { - // Here you will define your flags and configuration settings. - // Cobra supports persistent flags, which, if defined here, - // will be global for your application.s - rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.nixos-facter.yaml)") + // Check if the effective user id is 0 e.g. root + if os.Geteuid() != 0 { + log.Fatalf("you must run this program as root") + } - // Cobra also supports local flags, which will only run when this action is called directly. - f := rootCmd.Flags() - f.StringVarP(&outputPath, "output", "o", "", "path to write the report") + // Convert the hardware features into probe features + for _, str := range hardwareFeatures { + probe, err := hwinfo.ProbeFeatureString(str) + if err != nil { + log.Fatalf("invalid hardware feature: %v", err) + } + scanner.Features = append(scanner.Features, probe) + } - // Options for optional ephemeral system properties. - f.BoolVarP(&scanner.Swap, "swap", "s", false, "capture swap entries") - f.BoolVarP(&scanner.Ephemeral, "ephemeral", "e", false, "capture all ephemeral properties e.g. swap, filesystems and so on") - f.StringVarP(&logLevel, "log-level", "l", "info", "log level") + // Set the log level + switch logLevel { + case "debug": + log.SetFlags(log.LstdFlags | log.Lshortfile) + case "info": + log.SetFlags(log.LstdFlags) + case "warn", "error": + log.SetFlags(0) + default: + log.Fatalf("invalid log level: %s", logLevel) + } - // We currently support all probe features at a high level as they share some generic information, - // but we do not have mappings for all of their detail sections. - // These will be added on a priority / need basis. + report, err := scanner.Scan() + if err != nil { + log.Fatalf("failed to scan: %v", err) + } - defaultFeatures := []string{ - "memory", "pci", "net", "serial", "cpu", "bios", "monitor", "scsi", "usb", "prom", "sbus", "sys", "sysfs", - "udev", "block", "wlan", + bytes, err := json.MarshalIndent(report, "", " ") + if err != nil { + log.Fatalf("failed to marshal report to json: %v", err) } - // we strip default and int from the feature list - probeFeatures := hwinfo.ProbeFeatureStrings() - slices.DeleteFunc(probeFeatures, func(str string) bool { - switch str { - case "default", "int": - return true - default: - return false + // If a file path is provided write the report to it, otherwise output the report on stdout + if outputPath == "" { + if _, err = os.Stdout.Write(bytes); err != nil { + log.Fatalf("failed to write report to stdout: %v", err) } - }) - - f.StringSliceVarP( - &hardwareFeatures, - "hardware", - "f", - defaultFeatures, - fmt.Sprintf( - "Hardware items to probe. Possible values are %s", - strings.Join(probeFeatures, ","), - ), - ) + fmt.Println() + } else if err = os.WriteFile(outputPath, bytes, 0o644); err != nil { + log.Fatalf("failed to write report to output path: %v", err) + } } diff --git a/go.mod b/go.mod index d429297..eb40ca7 100644 --- a/go.mod +++ b/go.mod @@ -4,16 +4,13 @@ go 1.22.3 require ( github.com/klauspost/cpuid/v2 v2.2.9-0.20240805145549-92d5326f011e - github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 ) require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/spf13/pflag v1.0.5 // indirect golang.org/x/sys v0.22.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 1327d1a..8af9a8d 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,6 @@ -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/klauspost/cpuid/v2 v2.2.9-0.20240805145549-92d5326f011e h1:XLeT7xVis8xyC0F4CqQ2fAcuBar61PMI7GhrUEBBKas= github.com/klauspost/cpuid/v2 v2.2.9-0.20240805145549-92d5326f011e/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -15,11 +12,6 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= diff --git a/nix/packages/nixos-facter/gomod2nix.toml b/nix/packages/nixos-facter/gomod2nix.toml index 8adff9f..f5e037c 100644 --- a/nix/packages/nixos-facter/gomod2nix.toml +++ b/nix/packages/nixos-facter/gomod2nix.toml @@ -4,9 +4,6 @@ schema = 3 [mod."github.com/davecgh/go-spew"] version = "v1.1.2-0.20180830191138-d8f796af33cc" hash = "sha256-fV9oI51xjHdOmEx6+dlq7Ku2Ag+m/bmbzPo6A4Y74qc=" - [mod."github.com/inconshreveable/mousetrap"] - version = "v1.1.0" - hash = "sha256-XWlYH0c8IcxAwQTnIi6WYqq44nOKUylSWxWO/vi+8pE=" [mod."github.com/klauspost/cpuid/v2"] version = "v2.2.9-0.20240805145549-92d5326f011e" hash = "sha256-yCZS40L97G7WZHhy/A6I8ArEkyHi86DGAW43SziYPek=" @@ -16,12 +13,6 @@ schema = 3 [mod."github.com/pmezard/go-difflib"] version = "v1.0.1-0.20181226105442-5d4384ee4fb2" hash = "sha256-XA4Oj1gdmdV/F/+8kMI+DBxKPthZ768hbKsO3d9Gx90=" - [mod."github.com/spf13/cobra"] - version = "v1.8.1" - hash = "sha256-yDF6yAHycV1IZOrt3/hofR+QINe+B2yqkcIaVov3Ky8=" - [mod."github.com/spf13/pflag"] - version = "v1.0.5" - hash = "sha256-w9LLYzxxP74WHT4ouBspH/iQZXjuAh2WQCHsuvyEjAw=" [mod."github.com/stretchr/testify"] version = "v1.9.0" hash = "sha256-uUp/On+1nK+lARkTVtb5RxlW15zxtw2kaAFuIASA+J0="