package server import ( "context" //"syscall" //"os" //"os/signal" //"errors" //"flag" //"fmt" "os" "os/signal" "os/user" "path/filepath" "strconv" "syscall" "engine/internal/config" "engine/internal/handler" "engine/internal/logger" "engine/internal/logic" "engine/internal/service" ) type Server struct { conf *config.Config svc *service.Service lg *logic.Logic hand *handler.Handler ctx context.Context canc context.CancelFunc log *logger.Logger } func NewServer() (*Server, error) { var err error srv := &Server{ log: logger.NewLogger("server"), } srv.ctx, srv.canc = context.WithCancel(context.Background()) return srv, err } func (srv *Server) Configure() error { var err error srv.log.Infof("Configure server") srv.conf = config.NewConfig() err = srv.conf.ReadFile() if err != nil { return err } err = srv.conf.ReadEnv() if err != nil { return err } err = srv.conf.ReadOpts() if err != nil { return err } yamlConfig, err := srv.conf.Yaml() if err != nil { return err } srv.log.Debugf("Server configuration: \n%s\n", yamlConfig) return err } 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 } func (srv *Server) Daemonize() error { var err error if srv.conf.Daemon { srv.log.Debugf("Running server as daemon") // Restart process process err = srv.PseudoFork() if err != nil { return err } currUser, err := user.Current() if err != nil { return err } currID, err := strconv.Atoi(currUser.Uid) if err != nil { return err } if currID == 0 { // Change GID sysGroup, err := user.LookupGroup(srv.conf.Group) if err != nil { return err } gid, err := strconv.Atoi(sysGroup.Gid) if err != nil { return err } err = syscall.Setgid(gid) if err != nil { return err } // Change UID sysUser, err := user.Lookup(srv.conf.User) if err != nil { return err } uid, err := strconv.Atoi(sysUser.Uid) if err != nil { return err } err = syscall.Setuid(uid) if err != nil { return err } } // 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 } // Write process ID rundir := filepath.Dir(srv.conf.RunPath) err = os.MkdirAll(rundir, 0750) if err != nil { return err } 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 } } return err } func (srv *Server) Build() error { var err error srv.log.Infof("Build server") // Create logic logicConfig := &logic.LogicConfig{} srv.lg, err = logic.NewLogic(logicConfig) if err != nil { return err } // Create handler handlerConfig := &handler.HandlerConfig{ Logic: srv.lg, } srv.hand, err = handler.NewHandler(handlerConfig) if err != nil { return err } // Create service serviceConfig := &service.ServiceConfig{ PortNum: srv.conf.Service.PortNum, WebDir: srv.conf.Service.WebDir, Handler: srv.hand, } srv.svc, err = service.NewService(serviceConfig) if err != nil { return err } err = srv.svc.Build() if err != nil { return err } return err } func (srv *Server) Run() error { var err error srv.log.Infof("Running server") sigs := make(chan os.Signal, 1) done := make(chan error, 1) startService := func(svc *service.Service, done chan error) { err = svc.Run() if err != nil { srv.log.Errorf("Service error: %v", err) done <- err } } go startService(srv.svc, done) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP) var signal os.Signal select { case signal = <-sigs: srv.log.Infof("Services stopped by signal: %v", signal) srv.svc.Stop() case err = <-done: srv.log.Infof("Service stopped by error: %v", err) srv.svc.Stop() } if signal == syscall.SIGHUP { //srv.PseudoFork() } return err }