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  }