package server import ( //"io/ioutil" "os" "os/signal" "os/user" //"os/user" "path/filepath" "strconv" //"sync" "syscall" //"time" "mstore/app/config" "mstore/app/handler" "mstore/app/logger" "mstore/app/maindb" "mstore/app/operator" "mstore/app/service" "mstore/app/storage" ) type Server struct { conf *config.Config oper *operator.Operator svc *service.Service mdb *maindb.Database hand *handler.Handler logg *logger.Logger stor *storage.Storage } func NewServer() (*Server, error) { var err error srv := &Server{} srv.logg = logger.NewLogger("server") return srv, err } func (srv *Server) Handler() *handler.Handler { return srv.hand } func (srv *Server) Configure() error { var err error srv.logg.Infof("Server configure") srv.conf = config.NewConfig() if err != nil { return err } err = srv.conf.ReadConfigfile() if err != nil { //srv.logg.Warningf("Error loading config file: %v", err) //return err } err = srv.conf.ReadOptions() if err != nil { return err } return err } func (srv *Server) Build() error { var err error srv.logg.Infof("Server build") confDump := srv.conf.String() srv.logg.Infof("Current server configuration is:\n%s\n", confDump) if srv.conf.AsDaemon { logdir := filepath.Dir(srv.conf.Logpath) srv.logg.Infof("Create log directory %s", logdir) err = os.MkdirAll(logdir, 0750) if err != nil { return err } rundir := filepath.Dir(srv.conf.Runpath) srv.logg.Infof("Create run directory %s", rundir) err = os.MkdirAll(rundir, 0750) if err != nil { return err } } // Database create dbdir := srv.conf.Database.Basepath srv.logg.Infof("Create database directory %s ", dbdir) err = os.MkdirAll(dbdir, 0750) if err != nil { return err } mdb := maindb.NewDatabase(dbdir) srv.logg.Infof("Open main database") err = mdb.OpenDatabase() if err != nil { return err } srv.logg.Infof("Initialize main database") err = mdb.InitDatabase() if err != nil { return err } srv.mdb = mdb // Storage create srv.logg.Infof("Create storage directory") datadir := srv.conf.Database.Basepath err = os.MkdirAll(datadir, 0750) if err != nil { return err } srv.logg.Infof("Create storage") store := storage.NewStorage(datadir) srv.stor = store // Operator create srv.logg.Infof("Create operator") operatorParams := &operator.OperatorParams{ MainDB: srv.mdb, Store: srv.stor, } srv.oper, err = operator.NewOperator(operatorParams) if err != nil { return err } // Handler create srv.logg.Infof("Create handler") handlerParams := &handler.HandlerParams{ Operator: srv.oper, } srv.hand, err = handler.NewHandler(handlerParams) if err != nil { return err } // Service create serviceParams := &service.ServiceParams{ Handler: srv.hand, } srv.logg.Infof("Create service") srv.svc, err = service.NewService(serviceParams) if err != nil { return err } // Service build err = srv.svc.Build() if err != nil { return err } return err } func (srv *Server) Run() error { var err error currUser, err := user.Current() if err != nil { return err } srv.logg.Infof("Server run as user %s", currUser.Username) sigs := make(chan os.Signal, 1) svcDone := make(chan error, 1) // Service run srv.logg.Infof("Start service") startService := func(svc *service.Service, done chan error) { err = svc.Run() if err != nil { srv.logg.Errorf("Service error: %v", err) done <- err } } go startService(srv.svc, svcDone) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) var signal os.Signal select { case signal = <-sigs: srv.logg.Infof("Services stopped by signal: %v", signal) srv.svc.Stop() case err = <-svcDone: srv.logg.Infof("Service stopped by service error: %v", err) srv.svc.Stop() } 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.AsDaemon { // Restart process process err = srv.PseudoFork() 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 }