diff options
author | zeripath <art27@cantab.net> | 2021-07-14 15:43:13 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-14 10:43:13 -0400 |
commit | 3dcb3e9073d825a4ada184f832892cf4bd5836a3 (patch) | |
tree | aab77b7726f0e20f34b452df166113950ff5fc62 /cmd/serv.go | |
parent | ee43d70a0c237ef9c02b99b9b49d1af348840319 (diff) | |
download | gitea-3dcb3e9073d825a4ada184f832892cf4bd5836a3.tar.gz gitea-3dcb3e9073d825a4ada184f832892cf4bd5836a3.zip |
Second attempt at preventing zombies (#16326)
* Second attempt at preventing zombies
* Ensure that the pipes are closed in ssh.go
* Ensure that a cancellable context is passed up in cmd/* http requests
* Make cmd.fail return properly so defers are obeyed
* Ensure that something is sent to stdout in case of blocks here
Signed-off-by: Andrew Thornton <art27@cantab.net>
* placate lint
Signed-off-by: Andrew Thornton <art27@cantab.net>
* placate lint 2
Signed-off-by: Andrew Thornton <art27@cantab.net>
* placate lint 3
Signed-off-by: Andrew Thornton <art27@cantab.net>
* fixup
Signed-off-by: Andrew Thornton <art27@cantab.net>
* Apply suggestions from code review
Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: Lauris BH <lauris@nix.lv>
Diffstat (limited to 'cmd/serv.go')
-rw-r--r-- | cmd/serv.go | 84 |
1 files changed, 34 insertions, 50 deletions
diff --git a/cmd/serv.go b/cmd/serv.go index 40f8b89c9a..97ae901d27 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -6,17 +6,14 @@ package cmd import ( - "context" "fmt" "net/http" "net/url" "os" "os/exec" - "os/signal" "regexp" "strconv" "strings" - "syscall" "time" "code.gitea.io/gitea/models" @@ -75,7 +72,10 @@ var ( alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`) ) -func fail(userMessage, logMessage string, args ...interface{}) { +func fail(userMessage, logMessage string, args ...interface{}) error { + // There appears to be a chance to cause a zombie process and failure to read the Exit status + // if nothing is outputted on stdout. + fmt.Fprintln(os.Stdout, "") fmt.Fprintln(os.Stderr, "Gitea:", userMessage) if len(logMessage) > 0 { @@ -83,15 +83,19 @@ func fail(userMessage, logMessage string, args ...interface{}) { fmt.Fprintf(os.Stderr, logMessage+"\n", args...) } } + ctx, cancel := installSignals() + defer cancel() if len(logMessage) > 0 { - _ = private.SSHLog(true, fmt.Sprintf(logMessage+": ", args...)) + _ = private.SSHLog(ctx, true, fmt.Sprintf(logMessage+": ", args...)) } - - os.Exit(1) + return cli.NewExitError(fmt.Sprintf("Gitea: %s", userMessage), 1) } func runServ(c *cli.Context) error { + ctx, cancel := installSignals() + defer cancel() + // FIXME: This needs to internationalised setup("serv.log", c.Bool("debug")) @@ -109,18 +113,18 @@ func runServ(c *cli.Context) error { keys := strings.Split(c.Args()[0], "-") if len(keys) != 2 || keys[0] != "key" { - fail("Key ID format error", "Invalid key argument: %s", c.Args()[0]) + return fail("Key ID format error", "Invalid key argument: %s", c.Args()[0]) } keyID, err := strconv.ParseInt(keys[1], 10, 64) if err != nil { - fail("Key ID format error", "Invalid key argument: %s", c.Args()[1]) + return fail("Key ID format error", "Invalid key argument: %s", c.Args()[1]) } cmd := os.Getenv("SSH_ORIGINAL_COMMAND") if len(cmd) == 0 { - key, user, err := private.ServNoCommand(keyID) + key, user, err := private.ServNoCommand(ctx, keyID) if err != nil { - fail("Internal error", "Failed to check provided key: %v", err) + return fail("Internal error", "Failed to check provided key: %v", err) } switch key.Type { case models.KeyTypeDeploy: @@ -138,11 +142,11 @@ func runServ(c *cli.Context) error { words, err := shellquote.Split(cmd) if err != nil { - fail("Error parsing arguments", "Failed to parse arguments: %v", err) + return fail("Error parsing arguments", "Failed to parse arguments: %v", err) } if len(words) < 2 { - fail("Too few arguments", "Too few arguments in cmd: %s", cmd) + return fail("Too few arguments", "Too few arguments in cmd: %s", cmd) } verb := words[0] @@ -154,7 +158,7 @@ func runServ(c *cli.Context) error { var lfsVerb string if verb == lfsAuthenticateVerb { if !setting.LFS.StartServer { - fail("Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled") + return fail("Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled") } if len(words) > 2 { @@ -167,37 +171,37 @@ func runServ(c *cli.Context) error { rr := strings.SplitN(repoPath, "/", 2) if len(rr) != 2 { - fail("Invalid repository path", "Invalid repository path: %v", repoPath) + return fail("Invalid repository path", "Invalid repository path: %v", repoPath) } username := strings.ToLower(rr[0]) reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git")) if alphaDashDotPattern.MatchString(reponame) { - fail("Invalid repo name", "Invalid repo name: %s", reponame) + return fail("Invalid repo name", "Invalid repo name: %s", reponame) } if setting.EnablePprof || c.Bool("enable-pprof") { if err := os.MkdirAll(setting.PprofDataPath, os.ModePerm); err != nil { - fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err) + return fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err) } stopCPUProfiler, err := pprof.DumpCPUProfileForUsername(setting.PprofDataPath, username) if err != nil { - fail("Internal Server Error", "Unable to start CPU profile: %v", err) + return fail("Internal Server Error", "Unable to start CPU profile: %v", err) } defer func() { stopCPUProfiler() err := pprof.DumpMemProfileForUsername(setting.PprofDataPath, username) if err != nil { - fail("Internal Server Error", "Unable to dump Mem Profile: %v", err) + _ = fail("Internal Server Error", "Unable to dump Mem Profile: %v", err) } }() } requestedMode, has := allowedCommands[verb] if !has { - fail("Unknown git command", "Unknown git command %s", verb) + return fail("Unknown git command", "Unknown git command %s", verb) } if verb == lfsAuthenticateVerb { @@ -206,21 +210,20 @@ func runServ(c *cli.Context) error { } else if lfsVerb == "download" { requestedMode = models.AccessModeRead } else { - fail("Unknown LFS verb", "Unknown lfs verb %s", lfsVerb) + return fail("Unknown LFS verb", "Unknown lfs verb %s", lfsVerb) } } - results, err := private.ServCommand(keyID, username, reponame, requestedMode, verb, lfsVerb) + results, err := private.ServCommand(ctx, keyID, username, reponame, requestedMode, verb, lfsVerb) if err != nil { if private.IsErrServCommand(err) { errServCommand := err.(private.ErrServCommand) if errServCommand.StatusCode != http.StatusInternalServerError { - fail("Unauthorized", "%s", errServCommand.Error()) - } else { - fail("Internal Server Error", "%s", errServCommand.Error()) + return fail("Unauthorized", "%s", errServCommand.Error()) } + return fail("Internal Server Error", "%s", errServCommand.Error()) } - fail("Internal Server Error", "%s", err.Error()) + return fail("Internal Server Error", "%s", err.Error()) } os.Setenv(models.EnvRepoIsWiki, strconv.FormatBool(results.IsWiki)) os.Setenv(models.EnvRepoName, results.RepoName) @@ -253,7 +256,7 @@ func runServ(c *cli.Context) error { // Sign and get the complete encoded token as a string using the secret tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes) if err != nil { - fail("Internal error", "Failed to sign JWT token: %v", err) + return fail("Internal error", "Failed to sign JWT token: %v", err) } tokenAuthentication := &models.LFSTokenResponse{ @@ -266,7 +269,7 @@ func runServ(c *cli.Context) error { enc := json.NewEncoder(os.Stdout) err = enc.Encode(tokenAuthentication) if err != nil { - fail("Internal error", "Failed to encode LFS json response: %v", err) + return fail("Internal error", "Failed to encode LFS json response: %v", err) } return nil } @@ -276,25 +279,6 @@ func runServ(c *cli.Context) error { verb = strings.Replace(verb, "-", " ", 1) } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - go func() { - // install notify - signalChannel := make(chan os.Signal, 1) - - signal.Notify( - signalChannel, - syscall.SIGINT, - syscall.SIGTERM, - ) - select { - case <-signalChannel: - case <-ctx.Done(): - } - cancel() - signal.Reset() - }() - var gitcmd *exec.Cmd verbs := strings.Split(verb, " ") if len(verbs) == 2 { @@ -308,13 +292,13 @@ func runServ(c *cli.Context) error { gitcmd.Stdin = os.Stdin gitcmd.Stderr = os.Stderr if err = gitcmd.Run(); err != nil { - fail("Internal error", "Failed to execute git command: %v", err) + return fail("Internal error", "Failed to execute git command: %v", err) } // Update user key activity. if results.KeyID > 0 { - if err = private.UpdatePublicKeyInRepo(results.KeyID, results.RepoID); err != nil { - fail("Internal error", "UpdatePublicKeyInRepo: %v", err) + if err = private.UpdatePublicKeyInRepo(ctx, results.KeyID, results.RepoID); err != nil { + return fail("Internal error", "UpdatePublicKeyInRepo: %v", err) } } |