github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/osutil/udev/main.go.sample (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "flag" 6 "fmt" 7 "io/ioutil" 8 "log" 9 "os" 10 "os/signal" 11 "syscall" 12 13 "github.com/snapcore/snapd/osutil/udev/crawler" 14 "github.com/snapcore/snapd/osutil/udev/netlink" 15 16 "github.com/kr/pretty" 17 ) 18 19 var ( 20 filePath *string 21 monitorMode, infoMode *bool 22 ) 23 24 func init() { 25 filePath = flag.String("file", "", "Optionnal input file path with matcher-rules (default: no matcher)") 26 monitorMode = flag.Bool("monitor", false, "Enable monitor mode") 27 infoMode = flag.Bool("info", false, "Enable crawler mode") 28 } 29 30 func main() { 31 flag.Parse() 32 33 matcher, err := getOptionnalMatcher() 34 if err != nil { 35 log.Fatalln(err.Error()) 36 } 37 38 if monitorMode == nil && infoMode == nil { 39 log.Fatalln("You should use only one mode:", os.Args[0], "-monitor|-info") 40 } 41 42 if (monitorMode != nil && *monitorMode) && (infoMode != nil && *infoMode) { 43 log.Fatalln("Unable to enable both mode : monitor & info") 44 } 45 46 if *monitorMode { 47 monitor(matcher) 48 } 49 50 if *infoMode { 51 info(matcher) 52 } 53 } 54 55 // info run info mode 56 func info(matcher netlink.Matcher) { 57 log.Println("Get existing devices...") 58 59 queue := make(chan crawler.Device) 60 errors := make(chan error) 61 quit := crawler.ExistingDevices(queue, errors, matcher) 62 63 // Signal handler to quit properly monitor mode 64 signals := make(chan os.Signal, 1) 65 signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) 66 go func() { 67 <-signals 68 log.Println("Exiting info mode...") 69 quit <- struct{}{} 70 os.Exit(0) 71 }() 72 73 // Handling message from queue 74 for { 75 select { 76 case device, more := <-queue: 77 if !more { 78 log.Printf("Finished processing existing devices\n") 79 return 80 } 81 log.Printf("Detect device at %s with env %v\n", device.KObj, device.Env) 82 case err := <-errors: 83 log.Printf("ERROR: %v", err) 84 } 85 } 86 } 87 88 // monitor run monitor mode 89 func monitor(matcher netlink.Matcher) { 90 log.Println("Monitoring UEvent kernel message to user-space...") 91 92 conn := new(netlink.UEventConn) 93 if err := conn.Connect(netlink.UdevEvent); err != nil { 94 log.Fatalln("Unable to connect to Netlink Kobject UEvent socket") 95 } 96 defer conn.Close() 97 98 queue := make(chan netlink.UEvent) 99 errors := make(chan error) 100 quit := conn.Monitor(queue, errors, matcher) 101 102 // Signal handler to quit properly monitor mode 103 signals := make(chan os.Signal, 1) 104 signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) 105 go func() { 106 <-signals 107 log.Println("Exiting monitor mode...") 108 quit <- struct{}{} 109 os.Exit(0) 110 }() 111 112 // Handling message from queue 113 for { 114 select { 115 case uevent := <-queue: 116 log.Printf("Handle %s\n", pretty.Sprint(uevent)) 117 case err := <-errors: 118 log.Printf("ERROR: %v", err) 119 } 120 } 121 122 } 123 124 // getOptionnalMatcher Parse and load config file which contains rules for matching 125 func getOptionnalMatcher() (matcher netlink.Matcher, err error) { 126 if filePath == nil || *filePath == "" { 127 return nil, nil 128 } 129 130 stream, err := ioutil.ReadFile(*filePath) 131 if err != nil { 132 return nil, err 133 } 134 135 if stream == nil { 136 return nil, fmt.Errorf("Empty, no rules provided in \"%s\", err: %s", *filePath, err.Error()) 137 } 138 139 var rules netlink.RuleDefinitions 140 if err := json.Unmarshal(stream, &rules); err != nil { 141 return nil, fmt.Errorf("Wrong rule syntax in \"%s\", err: %s", *filePath, err.Error()) 142 } 143 144 return &rules, nil 145 }