blob: ac7e7193cf0a46b9e0258fd290e7cf17f838ea9d [file] [log] [blame]
package main
import (
"flag"
"fmt"
"net"
"os"
"path/filepath"
"strconv"
"time"
"v.io/lib/cmdline"
vexec "v.io/core/veyron/lib/exec"
"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/starter"
"v.io/core/veyron2"
"v.io/core/veyron2/ipc"
"v.io/core/veyron2/mgmt"
"v.io/core/veyron2/vlog"
)
var (
// TODO(caprita): publishAs and restartExitCode should be provided by the
// config?
publishAs = flag.String("name", "", "name to publish the device manager at")
restartExitCode = flag.Int("restart_exit_code", 0, "exit code to return when device manager should be restarted")
nhName = flag.String("neighborhood_name", "", `if provided, it will enable sharing with the local neighborhood with the provided name. The address of the local mounttable will be published to the neighboorhood and everything in the neighborhood will be visible on the local mounttable.`)
dmPort = flag.Int("deviced_port", 0, "the port number of assign to the device manager service. The hostname/IP address part of --veyron.tcp.address is used along with this port. By default, the port is assigned by the OS.")
)
func runServer(*cmdline.Command, []string) error {
ctx, shutdown := veyron2.Init()
defer shutdown()
var testMode bool
// If this device manager was started by another device manager, it must
// be part of a self update to test that this binary works. In that
// case, we need to disable a lot of functionality.
if handle, err := vexec.GetChildHandle(); err == nil {
if _, err := handle.Config.Get(mgmt.ParentNameConfigKey); err == nil {
testMode = true
vlog.Infof("TEST MODE")
}
}
configState, err := config.Load()
if err != nil {
vlog.Errorf("Failed to load config passed from parent: %v", err)
return err
}
mtAclDir := filepath.Join(configState.Root, "mounttable")
if err := os.MkdirAll(mtAclDir, 0700); err != nil {
vlog.Errorf("os.MkdirAll(%q) failed: %v", mtAclDir, err)
return err
}
// TODO(ashankar,caprita): Use channels/locks to synchronize the
// setting and getting of exitErr.
var exitErr error
ns := starter.NamespaceArgs{
ACLFile: filepath.Join(mtAclDir, "acls"),
Neighborhood: *nhName,
}
if testMode {
ns.ListenSpec = ipc.ListenSpec{Addrs: ipc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
} else {
ns.ListenSpec = veyron2.GetListenSpec(ctx)
ns.Name = *publishAs
}
dev := starter.DeviceArgs{
ConfigState: configState,
TestMode: testMode,
RestartCallback: func() { exitErr = cmdline.ErrExitCode(*restartExitCode) },
}
if dev.ListenSpec, err = newDeviceListenSpec(ns.ListenSpec, *dmPort); err != nil {
return err
}
stop, err := starter.Start(ctx, starter.Args{Namespace: ns, Device: dev, MountGlobalNamespaceInLocalNamespace: true})
if err != nil {
return err
}
defer stop()
// Wait until shutdown. Ignore duplicate signals (sent by agent and
// received as part of process group).
signals.SameSignalTimeWindow = 500 * time.Millisecond
<-signals.ShutdownOnSignals(ctx)
return exitErr
}
// newDeviceListenSpec returns a copy of ls, with the ports changed to port.
func newDeviceListenSpec(ls ipc.ListenSpec, port int) (ipc.ListenSpec, error) {
orig := ls.Addrs
ls.Addrs = nil
for _, a := range orig {
host, _, err := net.SplitHostPort(a.Address)
if err != nil {
err = fmt.Errorf("net.SplitHostPort(%v) failed: %v", a.Address, err)
vlog.Errorf(err.Error())
return ls, err
}
a.Address = net.JoinHostPort(host, strconv.Itoa(port))
ls.Addrs = append(ls.Addrs, a)
}
return ls, nil
}