code.gitea.io/gitea@v1.22.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 "time" 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, listenFDsEnv+"=") { 75 env = append(env, v) 76 } 77 } 78 env = append(env, fmt.Sprintf("%s=%d", listenFDsEnv, len(listeners))) 79 80 if notifySocketAddr != "" { 81 env = append(env, fmt.Sprintf("%s=%s", notifySocketEnv, notifySocketAddr)) 82 } 83 84 if watchdogTimeout != 0 { 85 watchdogStr := strconv.FormatInt(int64(watchdogTimeout/time.Millisecond), 10) 86 env = append(env, fmt.Sprintf("%s=%s", watchdogTimeoutEnv, watchdogStr)) 87 } 88 89 sb := &strings.Builder{} 90 for i, unlink := range getActiveListenersToUnlink() { 91 if !unlink { 92 continue 93 } 94 _, _ = sb.WriteString(strconv.Itoa(i)) 95 _, _ = sb.WriteString(",") 96 } 97 unlinkStr := sb.String() 98 if len(unlinkStr) > 0 { 99 unlinkStr = unlinkStr[:len(unlinkStr)-1] 100 env = append(env, fmt.Sprintf("%s=%s", unlinkFDsEnv, unlinkStr)) 101 } 102 103 allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, files...) 104 process, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{ 105 Dir: originalWD, 106 Env: env, 107 Files: allFiles, 108 }) 109 if err != nil { 110 return 0, err 111 } 112 processPid := process.Pid 113 _ = process.Release() // no wait, so release 114 return processPid, nil 115 }