github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/graceful/restart_unix.go (about) 1 // Copyright 2023 The GitBundle Inc. All rights reserved. 2 // Copyright 2017 The Gitea Authors. All rights reserved. 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file. 5 6 // This code is heavily inspired by the archived gofacebook/gracenet/net.go handler 7 8 //go:build !windows 9 10 package graceful 11 12 import ( 13 "fmt" 14 "net" 15 "os" 16 "os/exec" 17 "strings" 18 "sync" 19 "syscall" 20 ) 21 22 var killParent sync.Once 23 24 // KillParent sends the kill signal to the parent process if we are a child 25 func KillParent() { 26 killParent.Do(func() { 27 if GetManager().IsChild() { 28 ppid := syscall.Getppid() 29 if ppid > 1 { 30 _ = syscall.Kill(ppid, syscall.SIGTERM) 31 } 32 } 33 }) 34 } 35 36 // RestartProcess starts a new process passing it the active listeners. It 37 // doesn't fork, but starts a new process using the same environment and 38 // arguments as when it was originally started. This allows for a newly 39 // deployed binary to be started. It returns the pid of the newly started 40 // process when successful. 41 func RestartProcess() (int, error) { 42 listeners := getActiveListeners() 43 44 // Extract the fds from the listeners. 45 files := make([]*os.File, len(listeners)) 46 for i, l := range listeners { 47 var err error 48 // Now, all our listeners actually have File() functions so instead of 49 // individually casting we just use a hacky interface 50 files[i], err = l.(filer).File() 51 if err != nil { 52 return 0, err 53 } 54 55 if unixListener, ok := l.(*net.UnixListener); ok { 56 unixListener.SetUnlinkOnClose(false) 57 } 58 // Remember to close these at the end. 59 defer func(i int) { 60 _ = files[i].Close() 61 }(i) 62 } 63 64 // Use the original binary location. This works with symlinks such that if 65 // the file it points to has been changed we will use the updated symlink. 66 argv0, err := exec.LookPath(os.Args[0]) 67 if err != nil { 68 return 0, err 69 } 70 71 // Pass on the environment and replace the old count key with the new one. 72 var env []string 73 for _, v := range os.Environ() { 74 if !strings.HasPrefix(v, listenFDs+"=") { 75 env = append(env, v) 76 } 77 } 78 env = append(env, fmt.Sprintf("%s=%d", listenFDs, len(listeners))) 79 80 allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, files...) 81 process, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{ 82 Dir: originalWD, 83 Env: env, 84 Files: allFiles, 85 }) 86 if err != nil { 87 return 0, err 88 } 89 return process.Pid, nil 90 }