summaryrefslogtreecommitdiff
path: root/sd_daemon/notify.go
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@sbcglobal.net>2016-12-18 03:20:47 -0500
committerLuke Shumaker <lukeshu@sbcglobal.net>2016-12-18 03:20:47 -0500
commit3b1bdfbd2687e81bef85260f9cdfbf617ece3527 (patch)
tree93f2c45e6e616ab63e5fb21f4d2a740d8b9b05e2 /sd_daemon/notify.go
parenteb6e8a6ca87879a6ca85788fcf6d3bf8848088e6 (diff)
Implement almost all of sd-daemon. BREAKING CHANGES.v0.2.0
This does not include the sd_is_* utility functions. BREAKING CHANGES: - The import name is now "sd_daemon" instead of "sd". - The logger interface is now entirely different. - Notify now takes more arguments.
Diffstat (limited to 'sd_daemon/notify.go')
-rw-r--r--sd_daemon/notify.go84
1 files changed, 69 insertions, 15 deletions
diff --git a/sd_daemon/notify.go b/sd_daemon/notify.go
index b159a22..ad63659 100644
--- a/sd_daemon/notify.go
+++ b/sd_daemon/notify.go
@@ -1,6 +1,6 @@
// Copyright 2013-2015 Docker, Inc.
// Copyright 2014 CoreOS, Inc.
-// Copyright 2015 Luke Shumaker
+// Copyright 2015-2016 Luke Shumaker
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,31 +14,47 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package sd
+package sd_daemon
import (
- "errors"
"net"
"os"
+ "syscall"
+ "bytes"
)
-// errNotifyNoSocket is an error returned if no socket was specified.
-var errNotifyNoSocket = errors.New("No socket")
-
-// Notify sends a message to the service manager aobout state
-// changes. It is common to ignore the error.
+// Notify sends a message from process pid to the service manager
+// about state changes. If pid <= 0, or if the current process does
+// not have priveleges to send messages on behalf of other processes,
+// then the message is simply sent from the current process.
//
-// If unsetEnv is true, then (regarless of whether the function call
+// If unsetEnv is true, then (regardless of whether the function call
// itself succeeds or not) it will unset the environmental variable
// NOTIFY_SOCKET, which will cause further calls to this function to
// fail.
//
// The state parameter should countain a newline-separated list of
-// variable assignments.
+// variable assignments. See the documentation for sd_notify(3) for
+// well-known variable assignments.
+// https://www.freedesktop.org/software/systemd/man/sd_notify.html
+//
+// It is possible to include a set of file descriptors with the
+// message. This is useful for keeping files open across restarts, as
+// it enables the service manager will pass those files to the new
+// process when it is restarted (see ListenFds). Note: The service
+// manager will only actually store the file descriptors if you
+// include "FDSTORE=1" in the state.
//
-// See the documentation for sd_notify(3) for well-known variable
-// assignments.
-func Notify(unsetEnv bool, state string) error {
+// If the service manager is not listening for notifications from this
+// process (or this has already been called with unsetEnv=true), then
+// ErrDisabled is returned. If the service manager appears to be
+// listening, but there is an error sending the message, then that
+// error is returned. It is generally recommended that you ignore the
+// return value: if there is an error, this is function no-op; meaning
+// that by calling the function but ignoring the return value, you can
+// easily support both service managers that support these
+// notifications and those that do not.
+func Notify(pid int, unsetEnv bool, state string, files []*os.File) error {
if unsetEnv {
defer func() { _ = os.Unsetenv("NOTIFY_SOCKET") }()
}
@@ -49,7 +65,7 @@ func Notify(unsetEnv bool, state string) error {
}
if socketAddr.Name == "" {
- return errNotifyNoSocket
+ return ErrDisabled
}
conn, err := net.DialUnix(socketAddr.Net, nil, socketAddr)
@@ -58,6 +74,44 @@ func Notify(unsetEnv bool, state string) error {
}
defer func() { _ = conn.Close() }()
- _, err = conn.Write([]byte(state))
+ var cmsgs [][]byte
+
+ if len(files) > 0 {
+ fds := make([]int, len(files))
+ for i := range files {
+ fds[i] = int(files[i].Fd())
+ }
+ cmsg := syscall.UnixRights(fds...)
+ cmsgs = append(cmsgs, cmsg)
+ }
+
+ havePid := pid > 0 && pid != os.Getpid()
+ if havePid {
+ // The types of members of syscall.Ucred aren't
+ // guaranteed across processors. However,
+ // fortunately, they are the same on all supported
+ // processors as of go 1.7.4.
+ cmsg := syscall.UnixCredentials(&syscall.Ucred{
+ Pid: int32(pid),
+ Uid: uint32(os.Getuid()),
+ Gid: uint32(os.Getgid()),
+ })
+ cmsgs = append(cmsgs, cmsg)
+ }
+
+ // If the 2nd argument is empty, this is equivalent to .Write([]byte(state))
+ _, _, err = conn.WriteMsgUnix([]byte(state), bytes.Join(cmsgs, nil), nil)
+
+ if err != nil && havePid {
+ // Maybe it failed because we don't have privileges to
+ // spoof our pid, retry without spoofing the pid.
+ //
+ // I'm not too happy that does this silently without
+ // notifying the user, but that's what
+ // sd_pid_notify_with_fds does.
+ cmsgs = cmsgs[:len(cmsgs)-1]
+ _, _, err = conn.WriteMsgUnix([]byte(state), bytes.Join(cmsgs, nil), nil)
+ }
+
return err
}