github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/osutil/udev/crawler/device.go (about)

     1  package crawler
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/snapcore/snapd/osutil/udev/netlink"
    13  )
    14  
    15  const (
    16  	BASE_DEVPATH = "/sys/devices"
    17  )
    18  
    19  type Device struct {
    20  	KObj string
    21  	Env  map[string]string
    22  }
    23  
    24  // ExistingDevices return all plugged devices matched by the matcher
    25  // All uevent files inside /sys/devices is crawled to match right env values
    26  func ExistingDevices(queue chan Device, errors chan error, matcher netlink.Matcher) chan struct{} {
    27  	quit := make(chan struct{}, 1)
    28  
    29  	if matcher != nil {
    30  		if err := matcher.Compile(); err != nil {
    31  			errors <- fmt.Errorf("Wrong matcher, err: %v", err)
    32  			quit <- struct{}{}
    33  			return quit
    34  		}
    35  	}
    36  
    37  	go func() {
    38  		err := filepath.Walk(BASE_DEVPATH, func(path string, info os.FileInfo, err error) error {
    39  			select {
    40  			case <-quit:
    41  				return fmt.Errorf("abort signal receive")
    42  			default:
    43  				if err != nil {
    44  					return err
    45  				}
    46  
    47  				if info.IsDir() || info.Name() != "uevent" {
    48  					return nil
    49  				}
    50  
    51  				env, err := getEventFromUEventFile(path)
    52  				if err != nil {
    53  					return err
    54  				}
    55  
    56  				kObj := filepath.Dir(path)
    57  
    58  				// Append to env subsystem if existing
    59  				if link, err := os.Readlink(kObj + "/subsystem"); err == nil {
    60  					env["SUBSYSTEM"] = filepath.Base(link)
    61  				}
    62  
    63  				if matcher == nil || matcher.EvaluateEnv(env) {
    64  
    65  					queue <- Device{
    66  						KObj: kObj,
    67  						Env:  env,
    68  					}
    69  				}
    70  				return nil
    71  			}
    72  		})
    73  
    74  		if err != nil {
    75  			errors <- err
    76  		}
    77  		close(queue)
    78  	}()
    79  	return quit
    80  }
    81  
    82  // getEventFromUEventFile return all env var define in file
    83  // syntax: name=value for each line
    84  // Fonction use for /sys/.../uevent files
    85  func getEventFromUEventFile(path string) (rv map[string]string, err error) {
    86  	f, err := os.Open(path)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	defer f.Close()
    92  
    93  	data, err := ioutil.ReadAll(f)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	rv = make(map[string]string, 0)
    99  	buf := bufio.NewScanner(bytes.NewBuffer(data))
   100  
   101  	var line string
   102  	for buf.Scan() {
   103  		line = buf.Text()
   104  		field := strings.SplitN(line, "=", 2)
   105  		if len(field) != 2 {
   106  			return
   107  		}
   108  		rv[field[0]] = field[1]
   109  	}
   110  
   111  	return
   112  }