mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-25 15:53:36 +01:00
3dcb3e9073
* 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>
478 lines
12 KiB
Go
478 lines
12 KiB
Go
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"code.gitea.io/gitea/modules/log"
|
|
"code.gitea.io/gitea/modules/private"
|
|
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
var (
|
|
// CmdManager represents the manager command
|
|
CmdManager = cli.Command{
|
|
Name: "manager",
|
|
Usage: "Manage the running gitea process",
|
|
Description: "This is a command for managing the running gitea process",
|
|
Subcommands: []cli.Command{
|
|
subcmdShutdown,
|
|
subcmdRestart,
|
|
subcmdFlushQueues,
|
|
subcmdLogging,
|
|
},
|
|
}
|
|
subcmdShutdown = cli.Command{
|
|
Name: "shutdown",
|
|
Usage: "Gracefully shutdown the running process",
|
|
Flags: []cli.Flag{
|
|
cli.BoolFlag{
|
|
Name: "debug",
|
|
},
|
|
},
|
|
Action: runShutdown,
|
|
}
|
|
subcmdRestart = cli.Command{
|
|
Name: "restart",
|
|
Usage: "Gracefully restart the running process - (not implemented for windows servers)",
|
|
Flags: []cli.Flag{
|
|
cli.BoolFlag{
|
|
Name: "debug",
|
|
},
|
|
},
|
|
Action: runRestart,
|
|
}
|
|
subcmdFlushQueues = cli.Command{
|
|
Name: "flush-queues",
|
|
Usage: "Flush queues in the running process",
|
|
Action: runFlushQueues,
|
|
Flags: []cli.Flag{
|
|
cli.DurationFlag{
|
|
Name: "timeout",
|
|
Value: 60 * time.Second,
|
|
Usage: "Timeout for the flushing process",
|
|
}, cli.BoolFlag{
|
|
Name: "non-blocking",
|
|
Usage: "Set to true to not wait for flush to complete before returning",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "debug",
|
|
},
|
|
},
|
|
}
|
|
defaultLoggingFlags = []cli.Flag{
|
|
cli.StringFlag{
|
|
Name: "group, g",
|
|
Usage: "Group to add logger to - will default to \"default\"",
|
|
}, cli.StringFlag{
|
|
Name: "name, n",
|
|
Usage: "Name of the new logger - will default to mode",
|
|
}, cli.StringFlag{
|
|
Name: "level, l",
|
|
Usage: "Logging level for the new logger",
|
|
}, cli.StringFlag{
|
|
Name: "stacktrace-level, L",
|
|
Usage: "Stacktrace logging level",
|
|
}, cli.StringFlag{
|
|
Name: "flags, F",
|
|
Usage: "Flags for the logger",
|
|
}, cli.StringFlag{
|
|
Name: "expression, e",
|
|
Usage: "Matching expression for the logger",
|
|
}, cli.StringFlag{
|
|
Name: "prefix, p",
|
|
Usage: "Prefix for the logger",
|
|
}, cli.BoolFlag{
|
|
Name: "color",
|
|
Usage: "Use color in the logs",
|
|
}, cli.BoolFlag{
|
|
Name: "debug",
|
|
},
|
|
}
|
|
subcmdLogging = cli.Command{
|
|
Name: "logging",
|
|
Usage: "Adjust logging commands",
|
|
Subcommands: []cli.Command{
|
|
{
|
|
Name: "pause",
|
|
Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)",
|
|
Flags: []cli.Flag{
|
|
cli.BoolFlag{
|
|
Name: "debug",
|
|
},
|
|
},
|
|
Action: runPauseLogging,
|
|
}, {
|
|
Name: "resume",
|
|
Usage: "Resume logging",
|
|
Flags: []cli.Flag{
|
|
cli.BoolFlag{
|
|
Name: "debug",
|
|
},
|
|
},
|
|
Action: runResumeLogging,
|
|
}, {
|
|
Name: "release-and-reopen",
|
|
Usage: "Cause Gitea to release and re-open files used for logging",
|
|
Flags: []cli.Flag{
|
|
cli.BoolFlag{
|
|
Name: "debug",
|
|
},
|
|
},
|
|
Action: runReleaseReopenLogging,
|
|
}, {
|
|
Name: "remove",
|
|
Usage: "Remove a logger",
|
|
ArgsUsage: "[name] Name of logger to remove",
|
|
Flags: []cli.Flag{
|
|
cli.BoolFlag{
|
|
Name: "debug",
|
|
}, cli.StringFlag{
|
|
Name: "group, g",
|
|
Usage: "Group to add logger to - will default to \"default\"",
|
|
},
|
|
},
|
|
Action: runRemoveLogger,
|
|
}, {
|
|
Name: "add",
|
|
Usage: "Add a logger",
|
|
Subcommands: []cli.Command{
|
|
{
|
|
Name: "console",
|
|
Usage: "Add a console logger",
|
|
Flags: append(defaultLoggingFlags,
|
|
cli.BoolFlag{
|
|
Name: "stderr",
|
|
Usage: "Output console logs to stderr - only relevant for console",
|
|
}),
|
|
Action: runAddConsoleLogger,
|
|
}, {
|
|
Name: "file",
|
|
Usage: "Add a file logger",
|
|
Flags: append(defaultLoggingFlags, []cli.Flag{
|
|
cli.StringFlag{
|
|
Name: "filename, f",
|
|
Usage: "Filename for the logger - this must be set.",
|
|
}, cli.BoolTFlag{
|
|
Name: "rotate, r",
|
|
Usage: "Rotate logs",
|
|
}, cli.Int64Flag{
|
|
Name: "max-size, s",
|
|
Usage: "Maximum size in bytes before rotation",
|
|
}, cli.BoolTFlag{
|
|
Name: "daily, d",
|
|
Usage: "Rotate logs daily",
|
|
}, cli.IntFlag{
|
|
Name: "max-days, D",
|
|
Usage: "Maximum number of daily logs to keep",
|
|
}, cli.BoolTFlag{
|
|
Name: "compress, z",
|
|
Usage: "Compress rotated logs",
|
|
}, cli.IntFlag{
|
|
Name: "compression-level, Z",
|
|
Usage: "Compression level to use",
|
|
},
|
|
}...),
|
|
Action: runAddFileLogger,
|
|
}, {
|
|
Name: "conn",
|
|
Usage: "Add a net conn logger",
|
|
Flags: append(defaultLoggingFlags, []cli.Flag{
|
|
cli.BoolFlag{
|
|
Name: "reconnect-on-message, R",
|
|
Usage: "Reconnect to host for every message",
|
|
}, cli.BoolFlag{
|
|
Name: "reconnect, r",
|
|
Usage: "Reconnect to host when connection is dropped",
|
|
}, cli.StringFlag{
|
|
Name: "protocol, P",
|
|
Usage: "Set protocol to use: tcp, unix, or udp (defaults to tcp)",
|
|
}, cli.StringFlag{
|
|
Name: "address, a",
|
|
Usage: "Host address and port to connect to (defaults to :7020)",
|
|
},
|
|
}...),
|
|
Action: runAddConnLogger,
|
|
}, {
|
|
Name: "smtp",
|
|
Usage: "Add an SMTP logger",
|
|
Flags: append(defaultLoggingFlags, []cli.Flag{
|
|
cli.StringFlag{
|
|
Name: "username, u",
|
|
Usage: "Mail server username",
|
|
}, cli.StringFlag{
|
|
Name: "password, P",
|
|
Usage: "Mail server password",
|
|
}, cli.StringFlag{
|
|
Name: "host, H",
|
|
Usage: "Mail server host (defaults to: 127.0.0.1:25)",
|
|
}, cli.StringSliceFlag{
|
|
Name: "send-to, s",
|
|
Usage: "Email address(es) to send to",
|
|
}, cli.StringFlag{
|
|
Name: "subject, S",
|
|
Usage: "Subject header of sent emails",
|
|
},
|
|
}...),
|
|
Action: runAddSMTPLogger,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
)
|
|
|
|
func runRemoveLogger(c *cli.Context) error {
|
|
setup("manager", c.Bool("debug"))
|
|
group := c.String("group")
|
|
if len(group) == 0 {
|
|
group = log.DEFAULT
|
|
}
|
|
name := c.Args().First()
|
|
ctx, cancel := installSignals()
|
|
defer cancel()
|
|
|
|
statusCode, msg := private.RemoveLogger(ctx, group, name)
|
|
switch statusCode {
|
|
case http.StatusInternalServerError:
|
|
return fail("InternalServerError", msg)
|
|
}
|
|
|
|
fmt.Fprintln(os.Stdout, msg)
|
|
return nil
|
|
}
|
|
|
|
func runAddSMTPLogger(c *cli.Context) error {
|
|
setup("manager", c.Bool("debug"))
|
|
vals := map[string]interface{}{}
|
|
mode := "smtp"
|
|
if c.IsSet("host") {
|
|
vals["host"] = c.String("host")
|
|
} else {
|
|
vals["host"] = "127.0.0.1:25"
|
|
}
|
|
|
|
if c.IsSet("username") {
|
|
vals["username"] = c.String("username")
|
|
}
|
|
if c.IsSet("password") {
|
|
vals["password"] = c.String("password")
|
|
}
|
|
|
|
if !c.IsSet("send-to") {
|
|
return fmt.Errorf("Some recipients must be provided")
|
|
}
|
|
vals["sendTos"] = c.StringSlice("send-to")
|
|
|
|
if c.IsSet("subject") {
|
|
vals["subject"] = c.String("subject")
|
|
} else {
|
|
vals["subject"] = "Diagnostic message from Gitea"
|
|
}
|
|
|
|
return commonAddLogger(c, mode, vals)
|
|
}
|
|
|
|
func runAddConnLogger(c *cli.Context) error {
|
|
setup("manager", c.Bool("debug"))
|
|
vals := map[string]interface{}{}
|
|
mode := "conn"
|
|
vals["net"] = "tcp"
|
|
if c.IsSet("protocol") {
|
|
switch c.String("protocol") {
|
|
case "udp":
|
|
vals["net"] = "udp"
|
|
case "unix":
|
|
vals["net"] = "unix"
|
|
}
|
|
}
|
|
if c.IsSet("address") {
|
|
vals["address"] = c.String("address")
|
|
} else {
|
|
vals["address"] = ":7020"
|
|
}
|
|
if c.IsSet("reconnect") {
|
|
vals["reconnect"] = c.Bool("reconnect")
|
|
}
|
|
if c.IsSet("reconnect-on-message") {
|
|
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
|
|
}
|
|
return commonAddLogger(c, mode, vals)
|
|
}
|
|
|
|
func runAddFileLogger(c *cli.Context) error {
|
|
setup("manager", c.Bool("debug"))
|
|
vals := map[string]interface{}{}
|
|
mode := "file"
|
|
if c.IsSet("filename") {
|
|
vals["filename"] = c.String("filename")
|
|
} else {
|
|
return fmt.Errorf("filename must be set when creating a file logger")
|
|
}
|
|
if c.IsSet("rotate") {
|
|
vals["rotate"] = c.Bool("rotate")
|
|
}
|
|
if c.IsSet("max-size") {
|
|
vals["maxsize"] = c.Int64("max-size")
|
|
}
|
|
if c.IsSet("daily") {
|
|
vals["daily"] = c.Bool("daily")
|
|
}
|
|
if c.IsSet("max-days") {
|
|
vals["maxdays"] = c.Int("max-days")
|
|
}
|
|
if c.IsSet("compress") {
|
|
vals["compress"] = c.Bool("compress")
|
|
}
|
|
if c.IsSet("compression-level") {
|
|
vals["compressionLevel"] = c.Int("compression-level")
|
|
}
|
|
return commonAddLogger(c, mode, vals)
|
|
}
|
|
|
|
func runAddConsoleLogger(c *cli.Context) error {
|
|
setup("manager", c.Bool("debug"))
|
|
vals := map[string]interface{}{}
|
|
mode := "console"
|
|
if c.IsSet("stderr") && c.Bool("stderr") {
|
|
vals["stderr"] = c.Bool("stderr")
|
|
}
|
|
return commonAddLogger(c, mode, vals)
|
|
}
|
|
|
|
func commonAddLogger(c *cli.Context, mode string, vals map[string]interface{}) error {
|
|
if len(c.String("level")) > 0 {
|
|
vals["level"] = log.FromString(c.String("level")).String()
|
|
}
|
|
if len(c.String("stacktrace-level")) > 0 {
|
|
vals["stacktraceLevel"] = log.FromString(c.String("stacktrace-level")).String()
|
|
}
|
|
if len(c.String("expression")) > 0 {
|
|
vals["expression"] = c.String("expression")
|
|
}
|
|
if len(c.String("prefix")) > 0 {
|
|
vals["prefix"] = c.String("prefix")
|
|
}
|
|
if len(c.String("flags")) > 0 {
|
|
vals["flags"] = log.FlagsFromString(c.String("flags"))
|
|
}
|
|
if c.IsSet("color") {
|
|
vals["colorize"] = c.Bool("color")
|
|
}
|
|
group := "default"
|
|
if c.IsSet("group") {
|
|
group = c.String("group")
|
|
}
|
|
name := mode
|
|
if c.IsSet("name") {
|
|
name = c.String("name")
|
|
}
|
|
ctx, cancel := installSignals()
|
|
defer cancel()
|
|
|
|
statusCode, msg := private.AddLogger(ctx, group, name, mode, vals)
|
|
switch statusCode {
|
|
case http.StatusInternalServerError:
|
|
return fail("InternalServerError", msg)
|
|
}
|
|
|
|
fmt.Fprintln(os.Stdout, msg)
|
|
return nil
|
|
}
|
|
|
|
func runShutdown(c *cli.Context) error {
|
|
ctx, cancel := installSignals()
|
|
defer cancel()
|
|
|
|
setup("manager", c.Bool("debug"))
|
|
statusCode, msg := private.Shutdown(ctx)
|
|
switch statusCode {
|
|
case http.StatusInternalServerError:
|
|
return fail("InternalServerError", msg)
|
|
}
|
|
|
|
fmt.Fprintln(os.Stdout, msg)
|
|
return nil
|
|
}
|
|
|
|
func runRestart(c *cli.Context) error {
|
|
ctx, cancel := installSignals()
|
|
defer cancel()
|
|
|
|
setup("manager", c.Bool("debug"))
|
|
statusCode, msg := private.Restart(ctx)
|
|
switch statusCode {
|
|
case http.StatusInternalServerError:
|
|
return fail("InternalServerError", msg)
|
|
}
|
|
|
|
fmt.Fprintln(os.Stdout, msg)
|
|
return nil
|
|
}
|
|
|
|
func runFlushQueues(c *cli.Context) error {
|
|
ctx, cancel := installSignals()
|
|
defer cancel()
|
|
|
|
setup("manager", c.Bool("debug"))
|
|
statusCode, msg := private.FlushQueues(ctx, c.Duration("timeout"), c.Bool("non-blocking"))
|
|
switch statusCode {
|
|
case http.StatusInternalServerError:
|
|
return fail("InternalServerError", msg)
|
|
}
|
|
|
|
fmt.Fprintln(os.Stdout, msg)
|
|
return nil
|
|
}
|
|
|
|
func runPauseLogging(c *cli.Context) error {
|
|
ctx, cancel := installSignals()
|
|
defer cancel()
|
|
|
|
setup("manager", c.Bool("debug"))
|
|
statusCode, msg := private.PauseLogging(ctx)
|
|
switch statusCode {
|
|
case http.StatusInternalServerError:
|
|
return fail("InternalServerError", msg)
|
|
}
|
|
|
|
fmt.Fprintln(os.Stdout, msg)
|
|
return nil
|
|
}
|
|
|
|
func runResumeLogging(c *cli.Context) error {
|
|
ctx, cancel := installSignals()
|
|
defer cancel()
|
|
|
|
setup("manager", c.Bool("debug"))
|
|
statusCode, msg := private.ResumeLogging(ctx)
|
|
switch statusCode {
|
|
case http.StatusInternalServerError:
|
|
return fail("InternalServerError", msg)
|
|
}
|
|
|
|
fmt.Fprintln(os.Stdout, msg)
|
|
return nil
|
|
}
|
|
|
|
func runReleaseReopenLogging(c *cli.Context) error {
|
|
ctx, cancel := installSignals()
|
|
defer cancel()
|
|
|
|
setup("manager", c.Bool("debug"))
|
|
statusCode, msg := private.ReleaseReopenLogging(ctx)
|
|
switch statusCode {
|
|
case http.StatusInternalServerError:
|
|
return fail("InternalServerError", msg)
|
|
}
|
|
|
|
fmt.Fprintln(os.Stdout, msg)
|
|
return nil
|
|
}
|