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  }