* Added option to let parent wait for background child.
authorUrban Wallasch <urban.wallasch@freenet.de>
Fri, 12 Apr 2019 08:11:25 +0000 (10:11 +0200)
committerUrban Wallasch <urban.wallasch@freenet.de>
Fri, 12 Apr 2019 08:11:25 +0000 (10:11 +0200)
* Fixed child attempts to reopen logfile bug.
* Added gogopherd.sh init script.

config.go
gogopherd.cfg.example
gogopherd.go
gogopherd.sh [new file with mode: 0755]
logger.go

index 0403cb02aa6c20452057348fe540bbf30a65260a..d7c53a1080dda30566a43aa1da80d5b47b39c4bf 100644 (file)
--- a/config.go
+++ b/config.go
@@ -38,6 +38,7 @@ var cfg = struct {
        nworker int
        logfile string
        bkguser string
+       bkgwait bool
        verbose bool
 }{
        iface:   "localhost",
@@ -57,6 +58,7 @@ var cfg = struct {
        njobs:   50,
        logfile: "",
        bkguser: "",
+       bkgwait: false,
        verbose: false,
 }
 
@@ -111,6 +113,8 @@ func parseConfigFile(filename string) error {
                        cfg.logfile = val
                case "bkguser":
                        cfg.bkguser = val
+               case "bkgwait":
+                       cfg.bkgwait = strToBool(val)
                case "verbose":
                        cfg.verbose = strToBool(val)
                default:
@@ -143,6 +147,7 @@ func initialize() {
        flag.IntVar(&cfg.njobs, "lj", cfg.njobs, "set worker job queue size to `num`")
        flag.StringVar(&cfg.logfile, "L", cfg.logfile, "write log to `file`; empty for stderr")
        flag.StringVar(&cfg.bkguser, "b", cfg.bkguser, "restart the process in the background as `user`")
+       flag.BoolVar(&cfg.bkgwait, "w", cfg.bkgwait, "wait for background process")
        flag.BoolVar(&cfg.verbose, "v", cfg.verbose, "produce verbose output")
        flag.Parse()
        if help {
@@ -194,6 +199,7 @@ func initialize() {
        tracer.Print("njobs:   ", cfg.njobs)
        tracer.Print("logfile: ", cfg.logfile)
        tracer.Print("bkguser: ", cfg.bkguser)
+       tracer.Print("bkgwait: ", cfg.bkgwait)
        tracer.Print("verbose: ", cfg.verbose)
        tracer.Print("gogopherd version ", version)
 }
index 5d9451812468d6d4bbc2713ea813a16fdf3e9e5b..e4e1d209b8295dfb932c572e9c3cb37a8e76ebe5 100644 (file)
@@ -41,6 +41,8 @@ logfile = ""
 
 # Daemonize as this user; leave empty for foreground operation
 bkguser = ""
+# Wait for background process started with bkguser
+bkgwait = false
 
 # Produce verbose output
 verbose = false
index 30773e3328a6ee73897669eec14366f132d5a98d..3ccc60938b8ae4d720a6125bed000d1c1215b90a 100644 (file)
@@ -31,6 +31,7 @@ import (
 )
 
 var shutting_down bool = false
+var isdaemon bool = false
 
 func createIndex(dirname string, selector string) (string, error) {
        fi, err := ioutil.ReadDir(dirname)
@@ -178,7 +179,7 @@ func handleRequest(conn net.Conn) {
                return
        }
        req = strings.TrimSpace(req)
-       logger.Print("[", conn.RemoteAddr(),"] '", req, "'")
+       logger.Print("[", conn.RemoteAddr(), "] '", req, "'")
        // canonicalize and validate referenced path
        rpath, err := validatePath(cfg.docroot, filepath.Join(cfg.docroot, filepath.FromSlash(req)))
        if check(err, "validatePath "+rpath) != nil {
@@ -283,25 +284,25 @@ func serveTCP(listener net.Listener) {
                        return
                }
                if checkFatal(err, "Accept") == nil {
-                       logger.Print("[", conn.RemoteAddr(),"] connect")
+                       logger.Print("[", conn.RemoteAddr(), "] connect")
                        jobs <- conn
                }
                <-throttle
        }
 }
 
-func daemonize(asuser string, stdfiles []*os.File, xfiles []*os.File, env []string) (int, error) {
+func daemonize(asuser string, stdfiles []*os.File, xfiles []*os.File, env []string) (*exec.Cmd, error) {
        userCurrent, err := user.Current()
        if err != nil {
-               return -1, err
+               return nil, err
        }
        userRequired, err := user.Lookup(asuser)
        if err != nil {
-               return -1, err
+               return nil, err
        }
        executable, err := filepath.Abs(os.Args[0])
        if err != nil {
-               return -1, err
+               return nil, err
        }
        cmd := exec.Command(executable)
        cmd.Args = append([]string{executable}, os.Args[1:]...)
@@ -317,6 +318,9 @@ func daemonize(asuser string, stdfiles []*os.File, xfiles []*os.File, env []stri
                        Credential: &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)},
                }
        }
+       if cfg.bkgwait {
+               cmd.SysProcAttr.Pdeathsig = syscall.SIGTERM
+       }
        if len(stdfiles) > 0 {
                cmd.Stdin = stdfiles[0]
        }
@@ -329,12 +333,14 @@ func daemonize(asuser string, stdfiles []*os.File, xfiles []*os.File, env []stri
        cmd.ExtraFiles = xfiles
        err = cmd.Start()
        if err != nil {
-               return -1, err
+               return nil, err
        }
-       return cmd.Process.Pid, nil
+       return cmd, nil
 }
 
 func main() {
+       const isdaemon_mark = "_ISDAEMON_"
+       isdaemon = os.Getenv(isdaemon_mark) == "1"
        initialize()
 
        if cfg.bkguser == "" {
@@ -344,8 +350,7 @@ func main() {
                checkFatal(err, "Listen")
                go serveTCP(listener)
        } else {
-               const isdaemon = "_ISDAEMON_"
-               if os.Getenv(isdaemon) != "1" {
+               if !isdaemon {
                        // parent
                        service := cfg.iface + ":" + cfg.port
                        address, err := net.ResolveTCPAddr("tcp", service)
@@ -360,15 +365,21 @@ func main() {
                        fstd := []*os.File{
                                nil,
                                nil,
-                               os.Stderr,  // inherit our logging fd
+                               os.Stderr, // inherit our logging fd
                        }
                        env := []string{
                                "PATH=\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"",
-                               fmt.Sprintf("%s=1", isdaemon),
+                               fmt.Sprintf("%s=1", isdaemon_mark),
                        }
-                       cpid, err := daemonize(cfg.bkguser, fstd, fxtra, env)
+                       cmd, err := daemonize(cfg.bkguser, fstd, fxtra, env)
                        checkFatal(err, "daemonize")
-                       tracer.Print("background process spawned, child pid=", cpid, ", bye.")
+                       cpid := cmd.Process.Pid
+                       tracer.Print("background process spawned, child pid=", cpid)
+                       if cfg.bkgwait {
+                               err = cmd.Wait()
+                               logger.Print("child process termintated (", err, ")")
+                       }
+                       tracer.Print("parent: bye.")
                        os.Exit(0)
                } else {
                        // child
diff --git a/gogopherd.sh b/gogopherd.sh
new file mode 100755 (executable)
index 0000000..1354cf6
--- /dev/null
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+### BEGIN INIT INFO
+# Provides:        gopherd
+# Required-Start:  $network
+# Required-Stop:   $network
+# Default-Start:   2 3 4 5
+# Default-Stop:
+# Short-Description: Start gopher server
+### END INIT INFO
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin
+
+. /lib/lsb/init-functions
+
+PREFIX=/usr/local
+
+DAEMON="$PREFIX/bin/gogopherd"
+CFGFILE="$PREFIX/etc/gogopherd.cfg"
+PIDFILE=/var/run/gogopherd.pid
+GOGOPTS="$GOGOPTS -c $CFGFILE"
+
+test -x $DAEMON || exit 5
+test -f $CFGFILE || exit 5
+
+LOCKFILE=/run/lock/gogopherd
+
+case $1 in
+       start)
+               log_daemon_msg "Starting gopher server" "gogopherd"
+               (
+                       flock -w 180 9
+                       start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --make-pidfile --background --exec $DAEMON -- $GOGOPTS
+               ) 9>$LOCKFILE
+               log_end_msg $?
+               ;;
+       stop)
+               log_daemon_msg "Stopping gopher server" "gogopherd"
+               start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE --remove-pidfile --retry=TERM/30/KILL/5 --exec $DAEMON
+               log_end_msg $?
+               rm -f $PIDFILE
+               ;;
+       restart|force-reload)
+               $0 stop && sleep 2 && $0 start
+               ;;
+       try-restart)
+               if $0 status >/dev/null; then
+                       $0 restart
+               else
+                       exit 0
+               fi
+               ;;
+       reload)
+               exit 3
+               ;;
+       status)
+               status_of_proc $DAEMON "gogopherd"
+               ;;
+       *)
+               echo "Usage: $0 {start|stop|restart|try-restart|force-reload|status}"
+               exit 2
+               ;;
+esac
index 00882071276cb0538f06951de15544b85b1c442b..773249ae01a0c02b7e72bd67c54f7f2198451d07 100644 (file)
--- a/logger.go
+++ b/logger.go
@@ -25,7 +25,7 @@ var (
 )
 
 func initLogger(verbose bool, logfilename string) {
-       if len(logfilename) > 0 {
+       if !isdaemon && len(logfilename) > 0 {
                logfile, err := os.OpenFile(logfilename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
                if err != nil {
                        log.Print("Failed to open log file", logfilename, ":", err)