| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package main |
| |
| // Program to map Fuchsia devices to their associated serial connection on a |
| // controller machine. Connects to all available serial connections on the |
| // controller machine, invokes `dlog` (kernel debug log) and attempts to |
| // extract nodename=(.*?) from netsvc start up. |
| |
| import ( |
| "bufio" |
| "context" |
| "flag" |
| "fmt" |
| "io" |
| "io/ioutil" |
| "os/exec" |
| "path" |
| "regexp" |
| "strings" |
| "sync" |
| "time" |
| |
| "go.fuchsia.dev/tools/logger" |
| "go.fuchsia.dev/tools/serial" |
| ) |
| |
| const ( |
| // The default path under which /lib/udev/rules.d/60-serial.rules |
| // will create serial device files with full identifiers. |
| serialDir = "/dev/serial/by-id" |
| ) |
| |
| var ( |
| getFastbootSernums bool |
| ) |
| |
| type DeviceInfo struct { |
| mac string |
| nodename string |
| serial string |
| } |
| |
| func init() { |
| flag.BoolVar(&getFastbootSernums, "get-fastboot-sernums", false, "Retrieve the fastboot serial numbers as well.") |
| } |
| |
| func getDeviceInfo(fPath string) (*DeviceInfo, error) { |
| device, err := serial.Open(fPath) |
| defer device.Close() |
| if err != nil { |
| return nil, err |
| } |
| |
| if _, err = io.WriteString(device, "\ndlog\n"); err != nil { |
| return nil, err |
| } |
| |
| nodenameRe := regexp.MustCompile("nodename='(.*?)'") |
| macRe := regexp.MustCompile("macaddr: ([0-9a-f]{2}[:]){5}([0-9a-f]{2})") |
| |
| scanner := bufio.NewScanner(device) |
| info := &DeviceInfo{ |
| nodename: "", |
| mac: "", |
| serial: "", |
| } |
| for scanner.Scan() { |
| line := scanner.Text() |
| nodenameMatch := nodenameRe.FindStringSubmatch(line) |
| macMatch := macRe.FindStringSubmatch(line) |
| |
| if nodenameMatch != nil { |
| info.nodename = nodenameMatch[1] |
| } else if macMatch != nil { |
| info.mac = strings.Split(macMatch[0], " ")[1] |
| } |
| |
| if info.nodename != "" && info.mac != "" { |
| break |
| } |
| } |
| |
| if err := scanner.Err(); err != nil { |
| return nil, err |
| } else if info.nodename == "" && info.mac == "" { |
| return nil, fmt.Errorf("info not found") |
| } |
| |
| return info, nil |
| } |
| |
| func runFastbootDevices() string { |
| cmd := exec.Command("fastboot", "devices") |
| output, err := cmd.Output() |
| if err != nil { |
| return "" |
| } |
| return strings.Split(string(output), "\t")[0] |
| } |
| |
| func runFastbootReboot() error { |
| cmd := exec.Command("fastboot", "reboot") |
| return cmd.Run() |
| } |
| |
| func getSerialNumber(fPath string) (string, error) { |
| device, err := serial.Open(fPath) |
| defer device.Close() |
| if err != nil { |
| return "", err |
| } |
| if _, err := io.WriteString(device, "\ndm reboot-bootloader\n"); err != nil { |
| return "", err |
| } |
| time.Sleep(15 * time.Second) |
| if serial := runFastbootDevices(); serial != "" { |
| if err := runFastbootReboot(); err != nil { |
| return "", err |
| } |
| return serial, nil |
| } |
| return "", fmt.Errorf("could not get device into fastboot") |
| } |
| |
| func main() { |
| ctx := context.Background() |
| |
| files, err := ioutil.ReadDir(serialDir) |
| if err != nil { |
| logger.Fatalf(ctx, "failed to readdir() %s: %s", serialDir, err) |
| } |
| |
| mapping := make(map[string]*DeviceInfo) |
| var mapLock sync.Mutex |
| |
| var wg sync.WaitGroup |
| wg.Add(len(files)) |
| for _, f := range files { |
| fPath := path.Join(serialDir, f.Name()) |
| go func() { |
| defer wg.Done() |
| info, err := getDeviceInfo(fPath) |
| if err != nil { |
| logger.Errorf(ctx, "could not get device info from %s: %v", fPath, err) |
| } else { |
| mapLock.Lock() |
| mapping[fPath] = info |
| mapLock.Unlock() |
| } |
| }() |
| } |
| wg.Wait() |
| fmt.Printf("\n\nserial\tnodename\tmac\tfastbootSernum\n") |
| for k, v := range mapping { |
| if getFastbootSernums { |
| serial, err := getSerialNumber(k) |
| time.Sleep(30 * time.Second) |
| if err != nil { |
| fmt.Printf("err: %v\n", err) |
| } |
| v.serial = serial |
| } |
| fmt.Printf("%s\t%s\t%s\t%s\n", k, v.nodename, v.mac, v.serial) |
| } |
| } |