veyron/services/mgmt/device/deviced: re-implement deviced using cmdline library

As we've been adding more options to run deviced as a command (e.g. install,
uninstall), it makes sense to use the cmdline library.

Change-Id: Icee15d70dbb02b09bcb5bce17f63abfb9df2b5f6
diff --git a/services/mgmt/device/deviced/commands.go b/services/mgmt/device/deviced/commands.go
new file mode 100644
index 0000000..a597091
--- /dev/null
+++ b/services/mgmt/device/deviced/commands.go
@@ -0,0 +1,57 @@
+package main
+
+import (
+	"os"
+
+	"v.io/lib/cmdline"
+
+	"v.io/core/veyron/services/mgmt/device/impl"
+	"v.io/core/veyron2/vlog"
+)
+
+var installFrom string
+
+var cmdInstall = &cmdline.Command{
+	Run:      runInstall,
+	Name:     "install",
+	Short:    "Install the device manager.",
+	Long:     "Performs installation of device manager based on the config specified via the environment.",
+	ArgsName: "[-- <arguments for device manager>]",
+	ArgsLong: `
+Arguments to be passed to the installed device manager`,
+}
+
+func init() {
+	cmdInstall.Flags.StringVar(&installFrom, "from", "", "if specified, performs the installation from the provided application envelope object name")
+}
+
+func runInstall(_ *cmdline.Command, args []string) error {
+	if installFrom != "" {
+		// TODO(caprita): Also pass args into InstallFrom.
+		if err := impl.InstallFrom(installFrom); err != nil {
+			vlog.Errorf("InstallFrom(%v) failed: %v", installFrom, err)
+			return err
+		}
+		return nil
+	}
+	if err := impl.SelfInstall(args, os.Environ()); err != nil {
+		vlog.Errorf("SelfInstall failed: %v", err)
+		return err
+	}
+	return nil
+}
+
+var cmdUninstall = &cmdline.Command{
+	Run:   runUninstall,
+	Name:  "uninstall",
+	Short: "Uninstall the device manager.",
+	Long:  "Removes the device manager installation based on the config specified via the environment.",
+}
+
+func runUninstall(*cmdline.Command, []string) error {
+	if err := impl.Uninstall(); err != nil {
+		vlog.Errorf("Uninstall failed: %v", err)
+		return err
+	}
+	return nil
+}
diff --git a/services/mgmt/device/deviced/main.go b/services/mgmt/device/deviced/main.go
index 4e72e4e..f0631db 100644
--- a/services/mgmt/device/deviced/main.go
+++ b/services/mgmt/device/deviced/main.go
@@ -1,104 +1,16 @@
 package main
 
-import (
-	"flag"
-	"os"
-
-	"v.io/core/veyron2/naming"
-	"v.io/core/veyron2/rt"
-	"v.io/core/veyron2/vlog"
-
-	"v.io/core/veyron/lib/signals"
-	"v.io/core/veyron/profiles/roaming"
-	"v.io/core/veyron/services/mgmt/device/config"
-	"v.io/core/veyron/services/mgmt/device/impl"
-)
-
-var (
-	// TODO(caprita): publishAs and stopExitCode should be provided by the config?
-	publishAs    = flag.String("name", "", "name to publish the device manager at")
-	stopExitCode = flag.Int("stop_exit_code", 0, "exit code to return when stopped via the Stop RPC")
-	installSelf  = flag.Bool("install_self", false, "perform installation using environment and command-line flags")
-	installFrom  = flag.String("install_from", "", "if not-empty, perform installation from the provided application envelope object name")
-	uninstall    = flag.Bool("uninstall", false, "uninstall the installation specified via the config")
-)
+import "v.io/lib/cmdline"
 
 func main() {
-	exitCode := 0
-	defer func() {
-		os.Exit(exitCode)
-	}()
-	flag.Parse()
-	runtime, err := rt.New()
-	if err != nil {
-		vlog.Fatalf("Could not initialize runtime: %v", err)
+	rootCmd := cmdline.Command{
+		Name:  "deviced",
+		Short: "Veyron device manager setup",
+		Long: `
+deviced can be used to launch, configure, or manage the device manager.
+`,
+		Children: []*cmdline.Command{cmdInstall, cmdUninstall},
+		Run:      runServer,
 	}
-	defer runtime.Cleanup()
-
-	if len(*installFrom) > 0 {
-		if err := impl.InstallFrom(*installFrom); err != nil {
-			vlog.Errorf("InstallFrom failed: %v", err)
-			exitCode = 1
-		}
-		return
-	}
-
-	if *installSelf {
-		// TODO(caprita): Make the flags survive updates.
-		if err := impl.SelfInstall(flag.Args(), os.Environ()); err != nil {
-			vlog.Errorf("SelfInstall failed: %v", err)
-			exitCode = 1
-		}
-		return
-	}
-
-	if *uninstall {
-		if err := impl.Uninstall(); err != nil {
-			vlog.Errorf("Uninstall failed: %v", err)
-			exitCode = 1
-		}
-		return
-	}
-
-	server, err := runtime.NewServer()
-	if err != nil {
-		vlog.Errorf("NewServer() failed: %v", err)
-		exitCode = 1
-		return
-	}
-	defer server.Stop()
-	endpoints, err := server.Listen(roaming.ListenSpec)
-	if err != nil {
-		vlog.Errorf("Listen(%s) failed: %v", roaming.ListenSpec, err)
-		exitCode = 1
-		return
-	}
-	name := naming.JoinAddressName(endpoints[0].String(), "")
-	vlog.VI(0).Infof("Device manager object name: %v", name)
-	configState, err := config.Load()
-	if err != nil {
-		vlog.Errorf("Failed to load config passed from parent: %v", err)
-		exitCode = 1
-		return
-	}
-	configState.Name = name
-	// TODO(caprita): We need a way to set config fields outside of the
-	// update mechanism (since that should ideally be an opaque
-	// implementation detail).
-	dispatcher, err := impl.NewDispatcher(runtime.Principal(), configState, func() { exitCode = *stopExitCode })
-	if err != nil {
-		vlog.Errorf("Failed to create dispatcher: %v", err)
-		exitCode = 1
-		return
-	}
-	if err := server.ServeDispatcher(*publishAs, dispatcher); err != nil {
-		vlog.Errorf("Serve(%v) failed: %v", *publishAs, err)
-		exitCode = 1
-		return
-	}
-	vlog.VI(0).Infof("Device manager published as: %v", *publishAs)
-	impl.InvokeCallback(runtime.NewContext(), name)
-
-	// Wait until shutdown.
-	<-signals.ShutdownOnSignals(runtime)
+	rootCmd.Main()
 }
diff --git a/services/mgmt/device/deviced/server.go b/services/mgmt/device/deviced/server.go
new file mode 100644
index 0000000..efc649a
--- /dev/null
+++ b/services/mgmt/device/deviced/server.go
@@ -0,0 +1,72 @@
+package main
+
+import (
+	"flag"
+
+	"v.io/lib/cmdline"
+
+	"v.io/core/veyron/lib/signals"
+	"v.io/core/veyron/profiles/roaming"
+	"v.io/core/veyron/services/mgmt/device/config"
+	"v.io/core/veyron/services/mgmt/device/impl"
+	"v.io/core/veyron2/naming"
+	"v.io/core/veyron2/rt"
+	"v.io/core/veyron2/vlog"
+)
+
+var (
+	// TODO(caprita): publishAs and stopExitCode should be provided by the
+	// config?
+	publishAs    = flag.String("name", "", "name to publish the device manager at")
+	stopExitCode = flag.Int("stop_exit_code", 0, "exit code to return when stopped via the Stop RPC")
+)
+
+func runServer(*cmdline.Command, []string) error {
+	runtime, err := rt.New()
+	if err != nil {
+		vlog.Errorf("Could not initialize runtime: %v", err)
+		return err
+	}
+	defer runtime.Cleanup()
+
+	server, err := runtime.NewServer()
+	if err != nil {
+		vlog.Errorf("NewServer() failed: %v", err)
+		return err
+	}
+	defer server.Stop()
+	endpoints, err := server.Listen(roaming.ListenSpec)
+	if err != nil {
+		vlog.Errorf("Listen(%s) failed: %v", roaming.ListenSpec, err)
+		return err
+	}
+	name := naming.JoinAddressName(endpoints[0].String(), "")
+	vlog.VI(0).Infof("Device manager object name: %v", name)
+	configState, err := config.Load()
+	if err != nil {
+		vlog.Errorf("Failed to load config passed from parent: %v", err)
+		return err
+	}
+	configState.Name = name
+	// TODO(caprita): We need a way to set config fields outside of the
+	// update mechanism (since that should ideally be an opaque
+	// implementation detail).
+
+	var exitErr error
+	dispatcher, err := impl.NewDispatcher(runtime.Principal(), configState, func() { exitErr = cmdline.ErrExitCode(*stopExitCode) })
+	if err != nil {
+		vlog.Errorf("Failed to create dispatcher: %v", err)
+		return err
+	}
+	if err := server.ServeDispatcher(*publishAs, dispatcher); err != nil {
+		vlog.Errorf("Serve(%v) failed: %v", *publishAs, err)
+		return err
+	}
+	vlog.VI(0).Infof("Device manager published as: %v", *publishAs)
+	impl.InvokeCallback(runtime.NewContext(), name)
+
+	// Wait until shutdown.
+	<-signals.ShutdownOnSignals(runtime)
+
+	return exitErr
+}
diff --git a/tools/mgmt/device/dminstall b/tools/mgmt/device/dminstall
index 4f46805..b2d38dd 100755
--- a/tools/mgmt/device/dminstall
+++ b/tools/mgmt/device/dminstall
@@ -193,7 +193,7 @@
     exit 1
   fi
 
-  VEYRON_DM_CURRENT="${INSTALL_DIR}/deviced.curr" VEYRON_DM_ROOT="${DM_ROOT}" VEYRON_DM_HELPER="${SETUID_SCRIPT}" "${BIN_INSTALL}/deviced" --install_self -- --name="${PUBLISH}" --stop_exit_code=${STOP_EXIT_CODE} "$@"
+  VEYRON_DM_CURRENT="${INSTALL_DIR}/deviced.curr" VEYRON_DM_ROOT="${DM_ROOT}" VEYRON_DM_HELPER="${SETUID_SCRIPT}" "${BIN_INSTALL}/deviced" install -- --name="${PUBLISH}" --stop_exit_code=${STOP_EXIT_CODE} "$@"
   echo "Device manager installed."
 
   local -r SECURITY_DIR="${INSTALL_DIR}/security"
diff --git a/tools/mgmt/device/dmuninstall b/tools/mgmt/device/dmuninstall
index ce12747..489e068 100755
--- a/tools/mgmt/device/dmuninstall
+++ b/tools/mgmt/device/dmuninstall
@@ -39,7 +39,7 @@
 
   local -r BIN_INSTALL="${INSTALL_DIR}/bin"
 
-  VEYRON_DM_CURRENT="${INSTALL_DIR}/deviced.curr" VEYRON_DM_ROOT="${DM_ROOT}" VEYRON_DM_HELPER="unused" "${BIN_INSTALL}/deviced" --uninstall
+  VEYRON_DM_CURRENT="${INSTALL_DIR}/deviced.curr" VEYRON_DM_ROOT="${DM_ROOT}" VEYRON_DM_HELPER="unused" "${BIN_INSTALL}/deviced" uninstall
   echo "Device manager uninstalled."
 
   if [[ ${SINGLE_USER} == false ]]; then