diff --git a/README.rst b/README.rst index e099cdc38..021a9c1d2 100644 --- a/README.rst +++ b/README.rst @@ -1069,6 +1069,11 @@ written to disk. $ echo your password: $database_password your password: +If you want process signals to be sent to the command, for example if you are +running ``exec-env`` to launch a server and your server handles SIGTERM, then the +``--same-process`` flag can be used to instruct ``sops`` to start your command in +the same process instead of a child process. This uses the ``execve`` system call +and is supported on Unix-like systems. If the command you want to run only operates on files, you can use ``exec-file`` instead. By default, SOPS will use a FIFO to pass the contents of the diff --git a/cmd/sops/main.go b/cmd/sops/main.go index 557dc631e..f682204e7 100644 --- a/cmd/sops/main.go +++ b/cmd/sops/main.go @@ -163,6 +163,10 @@ func main() { Name: "user", Usage: "the user to run the command as", }, + cli.BoolFlag{ + Name: "same-process", + Usage: "run command in the current process instead of in a child process", + }, }, keyserviceFlags...), Action: func(c *cli.Context) error { if c.NArg() != 2 { @@ -195,6 +199,10 @@ func main() { if c.Bool("background") { log.Warn("exec-env's --background option is deprecated and will be removed in a future version of sops") + + if c.Bool("same-process") { + return common.NewExitError("Error: The --same-process flag cannot be used with --background", codes.ErrorConflictingParameters) + } } tree, err := decryptTree(opts) @@ -225,12 +233,13 @@ func main() { } if err := exec.ExecWithEnv(exec.ExecOpts{ - Command: command, - Plaintext: []byte{}, - Background: c.Bool("background"), - Pristine: c.Bool("pristine"), - User: c.String("user"), - Env: env, + Command: command, + Plaintext: []byte{}, + Background: c.Bool("background"), + Pristine: c.Bool("pristine"), + User: c.String("user"), + SameProcess: c.Bool("same-process"), + Env: env, }); err != nil { return toExitError(err) } diff --git a/cmd/sops/subcommand/exec/exec.go b/cmd/sops/subcommand/exec/exec.go index be74a31a4..3ac7cfd63 100644 --- a/cmd/sops/subcommand/exec/exec.go +++ b/cmd/sops/subcommand/exec/exec.go @@ -2,6 +2,7 @@ package exec import ( "bytes" + "fmt" "os" "path/filepath" "runtime" @@ -23,14 +24,15 @@ func init() { } type ExecOpts struct { - Command string - Plaintext []byte - Background bool - Pristine bool - Fifo bool - User string - Filename string - Env []string + Command string + Plaintext []byte + Background bool + SameProcess bool + Pristine bool + Fifo bool + User string + Filename string + Env []string } func GetFile(dir, filename string) *os.File { @@ -115,6 +117,10 @@ func ExecWithEnv(opts ExecOpts) error { SwitchUser(opts.User) } + if runtime.GOOS == "windows" && opts.SameProcess { + return fmt.Errorf("The --same-process flag is not supported on Windows") + } + var env []string if !opts.Pristine { @@ -134,6 +140,15 @@ func ExecWithEnv(opts ExecOpts) error { env = append(env, opts.Env...) + if opts.SameProcess { + if opts.Background { + log.Fatal("background is not supported for same-process") + } + + // Note that the call does NOT return, unless an error happens. + return ExecSyscall(opts.Command, env) + } + cmd := BuildCommand(opts.Command) cmd.Env = env diff --git a/cmd/sops/subcommand/exec/exec_unix.go b/cmd/sops/subcommand/exec/exec_unix.go index cc831e798..a7f63ce8d 100644 --- a/cmd/sops/subcommand/exec/exec_unix.go +++ b/cmd/sops/subcommand/exec/exec_unix.go @@ -11,6 +11,10 @@ import ( "syscall" ) +func ExecSyscall(command string, env []string) error { + return syscall.Exec("/bin/sh", []string{"/bin/sh", "-c", command}, env) +} + func BuildCommand(command string) *exec.Cmd { return exec.Command("/bin/sh", "-c", command) } diff --git a/cmd/sops/subcommand/exec/exec_windows.go b/cmd/sops/subcommand/exec/exec_windows.go index 7e0f21d74..a510f2826 100644 --- a/cmd/sops/subcommand/exec/exec_windows.go +++ b/cmd/sops/subcommand/exec/exec_windows.go @@ -4,6 +4,11 @@ import ( "os/exec" ) +func ExecSyscall(command string, env []string) error { + log.Fatal("same-process not available on windows") + return nil +} + func BuildCommand(command string) *exec.Cmd { return exec.Command("cmd.exe", "/C", command) }