You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

restart_unix.go 2.9KB

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