[serial_mapper] Add serial_mapper
This binary maps nodenames to serial connections by opening all serial
connections, sending a `dlog` command then regex'ing out the nodename=
from netsvc start up.
Change-Id: I4319ab30efa11681a1af53c94437f8c7275b651d
diff --git a/cmd/serial_mapper/main.go b/cmd/serial_mapper/main.go
new file mode 100644
index 0000000..1cd7531
--- /dev/null
+++ b/cmd/serial_mapper/main.go
@@ -0,0 +1,98 @@
+// 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"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "path"
+ "regexp"
+ "time"
+
+ "fuchsia.googlesource.com/tools/logger"
+ "fuchsia.googlesource.com/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"
+)
+
+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]string)
+ re := regexp.MustCompile("nodename='(.*?)'")
+
+ for _, f := range files {
+ fPath := path.Join(serialDir, f.Name())
+
+ device, err := serial.Open(fPath)
+ if err != nil {
+ logger.Errorf(ctx, "unable to open() %s: %s", fPath, err)
+ }
+
+ _, err = io.WriteString(device, "\ndlog\n")
+ if err != nil {
+ logger.Errorf(ctx, "failed to writestring() to %s: %s", fPath, err)
+ }
+
+ errs := make(chan error)
+ c := make(chan string)
+
+ go func() {
+ defer close(c)
+ defer close(errs)
+
+ scanner := bufio.NewScanner(device)
+ for scanner.Scan() {
+ line := scanner.Text()
+ match := re.FindStringSubmatch(line)
+ if match != nil {
+ c <- match[1]
+ break
+ }
+ }
+ if err := scanner.Err(); err != nil {
+ errs <- fmt.Errorf("scan() error: %s", err)
+ }
+ }()
+
+ select {
+ case err := <-errs:
+ logger.Errorf(ctx, "%s", err)
+ // Timeout after 10 seconds.
+ case <-time.After(10 * time.Second):
+ logger.Errorf(ctx, "timed out %s", fPath)
+ case nodename := <-c:
+ mapping[nodename] = fPath
+ }
+
+ device.Close()
+ }
+
+ if len(mapping) > 0 {
+ fmt.Println("device:serial mapping:")
+ for k, v := range mapping {
+ fmt.Printf("%s\t%s\n", k, v)
+ }
+ } else {
+ fmt.Println("found nothing!")
+ }
+}