package server import ( "context" "fmt" "net" "os" "os/signal" "os/user" "path/filepath" "strconv" "sync" "syscall" "time" "helmet/app/config" "helmet/app/control" "helmet/app/handler" "helmet/app/logger" "helmet/app/operator" "helmet/app/rproxy" "helmet/app/service" "helmet/pkg/network" "helmet/pkg/x509crt" ) type Server struct { conf *config.Config oper *operator.Operator svc *service.Service hand *handler.Handler log *logger.Logger x509cert []byte x509key []byte logf *os.File ctx context.Context cancel context.CancelFunc wg sync.WaitGroup listen net.Listener cont *control.Controller proxy *rproxy.Proxy } func NewServer() (*Server, error) { var err error srv := &Server{} srv.log = logger.NewLogger("server") return srv, err } func (srv *Server) Config() *config.Config { return srv.conf } func (srv *Server) Configure() error { var err error srv.conf, err = config.NewConfig() if err != nil { return err } err = srv.conf.Read() if err != nil { return err } err = srv.conf.Validate() if err != nil { return err } return err } func (srv *Server) Build() error { var err error srv.log.Infof("Build server") currUser, err := user.Current() if err != nil { err = fmt.Errorf("Error getting current user: %v\n", err) return err } cuid64, err := strconv.ParseInt(currUser.Uid, 10, 64) if err != nil { return err } cgid64, err := strconv.ParseInt(currUser.Gid, 10, 64) if err != nil { return err } euid := int(cuid64) egid := int(cgid64) if cuid64 == 0 { // Get effective user uid/guid usr, err := user.Lookup(srv.conf.RunUser) if err != nil { return err } uid64, err := strconv.ParseInt(usr.Uid, 10, 64) if err != nil { return err } gid64, err := strconv.ParseInt(usr.Gid, 10, 64) if err != nil { return err } euid = int(uid64) egid = int(gid64) } if srv.conf.AsDaemon { logDir := filepath.Dir(srv.conf.LogPath) srv.log.Infof("Create log dir: %s", logDir) err = os.MkdirAll(logDir, 0750) if err != nil { return err } err = os.Chown(logDir, euid, egid) if err != nil { return err } runDir := filepath.Dir(srv.conf.RunPath) srv.log.Infof("Create run dir: %s", runDir) err = os.MkdirAll(runDir, 0750) if err != nil { return err } err = os.Chown(runDir, euid, egid) if err != nil { return err } } // Create listener addrinfo := ":" + strconv.FormatUint(uint64(srv.conf.Service.Port), 10) listener, err := network.CreateListener(addrinfo) if err != nil { return fmt.Errorf("Cannot create listener: %v", err) } srv.listen = listener // Change effective user if cuid64 == 0 { err = syscall.Setuid(euid) if err != nil { return fmt.Errorf("Cannot change running user: %v", err) } } //return fmt.Errorf("Debug break") uidstr := strconv.FormatInt(int64(syscall.Geteuid()), 10) usr, err := user.LookupId(uidstr) if err != nil { return err } srv.log.Warningf("Now run as user: %s", usr.Username) // Create X509 certs srv.x509cert, srv.x509key, err = x509crt.CreateCertKey(srv.conf.Hostname) if err != nil { return err } // Create proxy srv.proxy = rproxy.NewProxy() // Create controller clientset, err := control.MakeClientset(srv.conf.Kubeconf) if err != nil { return err } srv.cont = control.NewController(srv.proxy, clientset, srv.conf.ExtAddress) if err != nil { return err } // Create operator operatorConfig := &operator.OperatorConfig{ Auths: srv.conf.Auths, Proxy: srv.proxy, //Database: srv.db, } srv.oper, err = operator.NewOperator(operatorConfig) if err != nil { return err } // Create handler handlerConfig := &handler.HandlerConfig{ Operator: srv.oper, } srv.hand = handler.NewHandler(handlerConfig) // Create service serviceConfig := &service.ServiceConfig{ Listener: srv.listen, Handler: srv.hand, Operator: srv.oper, X509Cert: srv.x509cert, X509Key: srv.x509key, } srv.svc = service.NewService(serviceConfig) return err } func (srv *Server) Run() error { var err error yamlConfig, err := srv.conf.YAML() if err != nil { return err } srv.log.Debugf("Server configuration:\n%s\n", yamlConfig) srv.ctx, srv.cancel = context.WithCancel(context.Background()) currUser, err := user.Current() if err != nil { return err } srv.log.Infof("Start server as user %s", currUser.Username) uidstr := strconv.FormatInt(int64(syscall.Geteuid()), 10) usr, err := user.LookupId(uidstr) if err != nil { return err } srv.log.Infof("Run server as user %s", usr.Username) if srv.conf.AsDaemon { // Redirect stdin nullFile, err := os.OpenFile("/dev/null", os.O_RDWR, 0) if err != nil { return err } err = syscall.Dup2(int(nullFile.Fd()), int(os.Stdin.Fd())) if err != nil { return err } // Redirect stderr and stout logdir := filepath.Dir(srv.conf.LogPath) err = os.MkdirAll(logdir, 0750) if err != nil { return err } logFile, err := os.OpenFile(srv.conf.LogPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640) if err != nil { return err } err = syscall.Dup2(int(logFile.Fd()), int(os.Stdout.Fd())) if err != nil { return err } err = syscall.Dup2(int(logFile.Fd()), int(os.Stderr.Fd())) if err != nil { return err } srv.logf = logFile // Write process ID pidFile, err := os.OpenFile(srv.conf.RunPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640) if err != nil { return err } defer pidFile.Close() currPid := os.Getpid() _, err = pidFile.WriteString(strconv.Itoa(currPid)) if err != nil { return err } } sigs := make(chan os.Signal, 1) done := make(chan error, 1) // Run service startService := func(svc *service.Service, done chan error) { srv.log.Infof("Run rpc service") err = svc.Run() if err != nil { srv.log.Errorf("Service error: %v", err) done <- err } } go startService(srv.svc, done) // Run controller srv.log.Infof("Run controller") go srv.cont.Run() signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) var signal os.Signal select { case signal = <-sigs: srv.log.Infof("Services stopped by signal: %v", signal) srv.cancel() srv.svc.Stop() srv.wg.Wait() } return err } func (srv *Server) Daemonize() error { var err error if srv.conf.AsDaemon { // Restart process process err = srv.pseudoFork() if err != nil { return err } } return err } func (srv *Server) logRotator() { // TODO: integrate into logger srv.wg.Add(1) var counter uint64 logFunc := func() { for { counter += 1 select { case <-srv.ctx.Done(): srv.wg.Done() srv.log.Infof("Log file rotator done") return default: } if (counter % 60) == 1 { stat, err := srv.logf.Stat() if err == nil && stat.Size() > srv.conf.LogLimit { srv.log.Infof("Rotate log file") countFiles := 3 for i := 1; i < countFiles; i++ { nextName := fmt.Sprintf("%s.%d", srv.conf.LogPath, i+1) prevName := fmt.Sprintf("%s.%d", srv.conf.LogPath, i) os.Rename(prevName, nextName) } os.Rename(srv.conf.LogPath, srv.conf.LogPath+".1") logFile, err := os.OpenFile(srv.conf.LogPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640) if err == nil { syscall.Dup2(int(logFile.Fd()), int(os.Stdout.Fd())) syscall.Dup2(int(logFile.Fd()), int(os.Stderr.Fd())) srv.logf.Close() srv.logf = logFile } } } time.Sleep(1 * time.Second) } } go logFunc() } func (srv *Server) pseudoFork() error { const successExit int = 0 var keyEnv string = "IMX0LTSELMRF8K" var err error _, isChild := os.LookupEnv(keyEnv) switch { case !isChild: os.Setenv(keyEnv, "TRUE") procAttr := syscall.ProcAttr{} cwd, err := os.Getwd() if err != nil { return err } var sysFiles = make([]uintptr, 3) sysFiles[0] = uintptr(syscall.Stdin) sysFiles[1] = uintptr(syscall.Stdout) sysFiles[2] = uintptr(syscall.Stderr) procAttr.Files = sysFiles procAttr.Env = os.Environ() procAttr.Dir = cwd _, err = syscall.ForkExec(os.Args[0], os.Args, &procAttr) if err != nil { return err } os.Exit(successExit) case isChild: _, err = syscall.Setsid() if err != nil { return err } } os.Unsetenv(keyEnv) return err }