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  }