mirror of
https://github.com/rclone/rclone.git
synced 2024-11-22 00:13:49 +01:00
atexit: Terminate with non-zero status after receiving signal
When rclone received a SIGINT (Ctrl+C) or SIGTERM signal while an atexit function is registered it always terminated with status code 0. Unix convention is to exit with a non-zero status code. Often it's `128 + int(signum), but at least not zero. With this change fatal signals handled by the `atexit` package cause a non-zero exit code. On Unix systems it's `128 + int(signum)` while on other systems, such as Windows, it's always 2 ("error not otherwise categorised"). Resolves #5437. Signed-off-by: Michael Hanselmann <public@hansmi.ch>
This commit is contained in:
parent
cf19073ac9
commit
6b7d7d0441
@ -51,7 +51,7 @@ func Register(fn func()) FnHandle {
|
||||
fs.Infof(nil, "Signal received: %s", sig)
|
||||
Run()
|
||||
fs.Infof(nil, "Exiting...")
|
||||
os.Exit(0)
|
||||
os.Exit(exitCode(sig))
|
||||
}()
|
||||
})
|
||||
|
||||
|
@ -4,6 +4,12 @@ package atexit
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/rclone/rclone/lib/exitcode"
|
||||
)
|
||||
|
||||
var exitSignals = []os.Signal{os.Interrupt}
|
||||
|
||||
func exitCode(_ os.Signal) int {
|
||||
return exitcode.UncategorizedError
|
||||
}
|
||||
|
41
lib/atexit/atexit_test.go
Normal file
41
lib/atexit/atexit_test.go
Normal file
@ -0,0 +1,41 @@
|
||||
package atexit
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/rclone/rclone/lib/exitcode"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type fakeSignal struct{}
|
||||
|
||||
func (*fakeSignal) String() string {
|
||||
return "fake"
|
||||
}
|
||||
|
||||
func (*fakeSignal) Signal() {
|
||||
}
|
||||
|
||||
var _ os.Signal = (*fakeSignal)(nil)
|
||||
|
||||
func TestExitCode(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "windows", "plan9":
|
||||
for _, i := range []os.Signal{
|
||||
os.Interrupt,
|
||||
os.Kill,
|
||||
} {
|
||||
assert.Equal(t, exitCode(i), exitcode.UncategorizedError)
|
||||
}
|
||||
|
||||
default:
|
||||
// SIGINT (2) and SIGKILL (9) are portable numbers specified by POSIX.
|
||||
assert.Equal(t, exitCode(os.Interrupt), 128+2)
|
||||
assert.Equal(t, exitCode(os.Kill), 128+9)
|
||||
}
|
||||
|
||||
// Never a real signal
|
||||
assert.Equal(t, exitCode(&fakeSignal{}), exitcode.UncategorizedError)
|
||||
}
|
@ -5,6 +5,20 @@ package atexit
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/rclone/rclone/lib/exitcode"
|
||||
)
|
||||
|
||||
var exitSignals = []os.Signal{syscall.SIGINT, syscall.SIGTERM} // Not syscall.SIGQUIT as we want the default behaviour
|
||||
|
||||
// exitCode calculates the exit code for the given signal. Many Unix programs
|
||||
// exit with 128+signum if they handle signals. Most shell also implement the
|
||||
// same convention if a program is terminated by an uncaught and/or fatal
|
||||
// signal.
|
||||
func exitCode(sig os.Signal) int {
|
||||
if real, ok := sig.(syscall.Signal); ok && int(real) > 0 {
|
||||
return 128 + int(real)
|
||||
}
|
||||
|
||||
return exitcode.UncategorizedError
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user