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