diff --git a/main.go b/main.go index 0f83d7a..85c9f67 100644 --- a/main.go +++ b/main.go @@ -5,9 +5,13 @@ import ( "flag" "fmt" "lux/crypto" + "lux/node" "lux/proto" + "lux/rpc" "os" + "os/signal" "strings" + "syscall" ) var isNode bool @@ -17,6 +21,8 @@ var configPath string var rpcPath string var bootstrap bool var justNodeId bool +var daemonize bool +var pidPath string type NodeConfig struct { XMLName xml.Name `xml:"node"` @@ -29,6 +35,8 @@ type NodeConfig struct { ID string `xml:"id"` Address string `xml:"address"` } `xml:"neighbor"` + + RPCEndpoints []string `xml:"rpc"` } func bootstrapNode() { @@ -64,6 +72,121 @@ func bootstrapNode() { } } +func nodeMain() { + xmlBytes, err := os.ReadFile(configPath) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + var config NodeConfig + if err := xml.Unmarshal(xmlBytes, &config); err != nil { + fmt.Fprintf(os.Stderr, "failed to parse xml: %v\n", err) + os.Exit(1) + } + + // check presense of keystore and id in config + if config.KeyStore == "" { + fmt.Fprintln(os.Stderr, "no keystore path specified!") + os.Exit(1) + } + if config.ID == "" { + fmt.Fprintln(os.Stderr, "no ID in config! You need to --bootstrap") + os.Exit(1) + } + + nodeId, err := proto.ParseLuxID(config.ID) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to parse node id: %v\n", err) + os.Exit(1) + } + + // load keystore + ks := crypto.NewLuxKeyStore(config.KeyStore) + + nodeKey, ok := ks.Get(nodeId) + if !ok { + fmt.Fprintln(os.Stderr, "node key is not present in key store!") + os.Exit(1) + } + + // create node + node := node.NewLuxNode(nodeKey, ks) + + // add interior exterior channels + for _, interior := range config.Interior { + if err := node.AddInterior(interior); err != nil { + fmt.Fprintf(os.Stderr, "failed to add interior %s: %v\n", interior, err) + os.Exit(1) + } + } + for _, exterior := range config.Exterior { + if err := node.AddExterior(exterior); err != nil { + fmt.Fprintf(os.Stderr, "failed to add exterior %s: %v\n", exterior, err) + os.Exit(1) + } + } + + // add neighbors + for _, neighbor := range config.Neighbors { + neighId, err := proto.ParseLuxID(neighbor.ID) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to parse neigh id %s: %v\n", neighbor.ID, err) + os.Exit(1) + } + + if err := node.AddNeighbor(neighId, neighbor.Address); err != nil { + fmt.Fprintf(os.Stderr, "failed to add neighbor %s: %v\n", neighbor.ID, err) + os.Exit(1) + } + } + + // create rpc server + sv := rpc.NewLuxRpcServer() + // TODO: register node controllers + + // parse and and spawn rpc endpoints + for _, rpcPath := range config.RPCEndpoints { + if strings.HasPrefix(rpcPath, "unix://") { + path := rpcPath[7:] + + if err := sv.AddEndpoint("unix", path, rpc.LuxRpcTypeRoot); err != nil { + fmt.Fprintf(os.Stderr, "failed to add root rpc %s: %v\n", path, err) + os.Exit(1) + } + } else if strings.HasPrefix(rpcPath, "tcp://") { + path := rpcPath[6:] + + if err := sv.AddEndpoint("tcp", path, rpc.LuxRpcTypeQuery); err != nil { + fmt.Fprintf(os.Stderr, "failed to add query rpc %s: %v\n", path, err) + os.Exit(1) + } + } else { + fmt.Fprintf(os.Stderr, "unknown rpc type %s. It must be either unix:// or tcp://\n", rpcPath) + os.Exit(1) + } + } + + // TODO: daemonize + + // register go channel to receive unix signals, + // while hogging main thread to read them and take action. + // its important to keep main thread alive for process to run + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + + for { + sig := <-sigs + switch sig { + case syscall.SIGINT: + case syscall.SIGTERM: + // stop node daemon + node.Stop() + os.Exit(0) + } + } +} + func main() { // first, we need to determine who we are: node, host or rpc. // determine by executable name (lux binary will be symlinked to lux-node, lux-host, luc-rpc), @@ -75,6 +198,8 @@ func main() { flag.StringVar(&rpcPath, "rpc-path", "", "path to RPC UNIX socket or TCP socket, must be in unix:// or tcp:// form") flag.BoolVar(&bootstrap, "bootstrap", false, "bootstrap node keystore. config must be specified") flag.BoolVar(&justNodeId, "just-node-id", false, "when bootstrapping only output node id to stdout") + flag.BoolVar(&daemonize, "daemonize", false, "run LUX as daemon in background") + flag.StringVar(&pidPath, "pid", "", "after daemonization LUX will write its PID here") flag.Parse() if !isNode && !isHost && !isRpc { @@ -100,4 +225,8 @@ func main() { bootstrapNode() return } + + if isNode { + nodeMain() + } } diff --git a/proto/lux_id.go b/proto/lux_id.go index ddc64b4..de774d4 100644 --- a/proto/lux_id.go +++ b/proto/lux_id.go @@ -12,6 +12,15 @@ func NewLuxID() LuxID { return LuxID{uuid.New()} } +func ParseLuxID(idStr string) (LuxID, error) { + uuid, err := uuid.Parse(idStr) + if err != nil { + return LuxID{}, err + } + + return LuxID{uuid}, nil +} + func (id *LuxID) Read(rd *LuxBuffer) error { bytes, err := rd.ReadNext(16) if err != nil { diff --git a/rpc/lux_rpc_errors.go b/rpc/lux_rpc_errors.go new file mode 100644 index 0000000..aad6f49 --- /dev/null +++ b/rpc/lux_rpc_errors.go @@ -0,0 +1,14 @@ +package rpc + +// all errors are defined here. since RPC is sync locked, +// no problem would be mutating RequestID + +var LUX_RPC_ERROR_UNKNOWN_CONTROLLER = LuxRpcError{ + ErrorCode: 1, + Message: "unknown controller", +} + +var LUX_RPC_ERROR_ACCESS_DENIED = LuxRpcError{ + ErrorCode: 2, + Message: "access denied", +}