summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@datawire.io>2019-08-14 21:51:23 -0400
committerLuke Shumaker <lukeshu@datawire.io>2019-08-14 22:20:05 -0400
commit8f32f1724fce55699a7a9675390c1c2b292190a4 (patch)
tree4fe1a2d2999ef5f9f4663cff4190118412a92e59
parent9d8d243a7e87775b1a5d0e02c953fb9213d04938 (diff)
Unify notify.go and notify_nonlinux.go a bit
-rw-r--r--sd_daemon/notify.go95
-rw-r--r--sd_daemon/notify_linux.go114
-rw-r--r--sd_daemon/notify_nonlinux.go49
3 files changed, 123 insertions, 135 deletions
diff --git a/sd_daemon/notify.go b/sd_daemon/notify.go
index 917e2d4..9591613 100644
--- a/sd_daemon/notify.go
+++ b/sd_daemon/notify.go
@@ -3,7 +3,7 @@
//
// Copyright 2013, 2015 Docker, Inc.
// Copyright 2014 CoreOS, Inc.
-// Copyright 2015-2018 Luke Shumaker
+// Copyright 2015-2019 Luke Shumaker
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -17,16 +17,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// +build linux
-
package sd_daemon
import (
- "bytes"
- "net"
"os"
-
- "golang.org/x/sys/unix"
)
// Notification is a message to be sent to the service manager about
@@ -36,6 +30,11 @@ type Notification struct {
// If PID <= 0, or if the current process does not have
// privileges to send messages on behalf of other processes,
// then the message is simply sent from the current process.
+ //
+ // BUG(lukeshu): Spoofing the PID is not implemented on
+ // non-Linux kernels. If you are knowledgable about how to do
+ // this on other kernels, please let me know at
+ // <lukeshu@lukeshu.com>!
PID int
// State should contain a newline-separated list of variable
@@ -75,85 +74,5 @@ type Notification struct {
// support both service managers that support these notifications and
// those that do not.
func (msg Notification) Send(unsetEnv bool) error {
- if unsetEnv {
- defer func() { _ = os.Unsetenv("NOTIFY_SOCKET") }()
- }
-
- socketAddr := &net.UnixAddr{
- Name: os.Getenv("NOTIFY_SOCKET"),
- Net: "unixgram",
- }
-
- if socketAddr.Name == "" {
- return ErrDisabled
- }
-
- conn, err := socketUnixgram(socketAddr.Name)
- if err != nil {
- return err
- }
- defer func() { _ = conn.Close() }()
-
- var cmsgs [][]byte
-
- if len(msg.Files) > 0 {
- fds := make([]int, len(msg.Files))
- for i := range msg.Files {
- fds[i] = int(msg.Files[i].Fd())
- }
- cmsg := unix.UnixRights(fds...)
- cmsgs = append(cmsgs, cmsg)
- }
-
- havePid := msg.PID > 0 && msg.PID != os.Getpid()
- if havePid {
- cmsg := unix.UnixCredentials(&unix.Ucred{
- Pid: int32(msg.PID),
- Uid: uint32(os.Getuid()),
- Gid: uint32(os.Getgid()),
- })
- cmsgs = append(cmsgs, cmsg)
- }
-
- // If the 2nd argument is empty, this is equivalent to
- //
- // conn, _ := net.DialUnix(socketAddr.Net, nil, socketAddr)
- // conn.Write([]byte(msg.State))
- _, _, err = conn.WriteMsgUnix([]byte(msg.State), bytes.Join(cmsgs, nil), socketAddr)
-
- 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 we do this silently without
- // notifying the caller, but that's what
- // sd_pid_notify_with_fds does.
- cmsgs = cmsgs[:len(cmsgs)-1]
- _, _, err = conn.WriteMsgUnix([]byte(msg.State), bytes.Join(cmsgs, nil), socketAddr)
- }
-
- return err
-}
-
-// socketUnixgram wraps socket(2), but doesn't bind(2) or connect(2)
-// the socket to anything. This is an ugly hack because none of the
-// functions in "net" actually allow you to get a AF_UNIX socket not
-// bound/connected to anything.
-//
-// At some point you begin to question if it is worth it to keep up
-// the high-level interface of "net", and messing around with FileConn
-// and UnixConn. Maybe we just drop to using unix.Socket and
-// unix.SendmsgN directly.
-func socketUnixgram(name string) (*net.UnixConn, error) {
- fd, err := unix.Socket(unix.AF_UNIX, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
- if err != nil {
- return nil, os.NewSyscallError("socket", err)
- }
- defer unix.Close(fd)
- conn, err := net.FileConn(os.NewFile(uintptr(fd), name))
- if err != nil {
- return nil, err
- }
- unixConn := conn.(*net.UnixConn)
- return unixConn, nil
+ return msg.send(unsetEnv)
}
diff --git a/sd_daemon/notify_linux.go b/sd_daemon/notify_linux.go
new file mode 100644
index 0000000..903f1ad
--- /dev/null
+++ b/sd_daemon/notify_linux.go
@@ -0,0 +1,114 @@
+// Incorporates: git://github.com/docker/docker.git 18c7c67308bd4a24a41028e63c2603bb74eac85e pkg/systemd/sd_notify.go
+// Incorporates: git://github.com/coreos/go-systemd.git a606a1e936df81b70d85448221c7b1c6d8a74ef1 daemon/sdnotify.go
+//
+// Copyright 2013, 2015 Docker, Inc.
+// Copyright 2014 CoreOS, Inc.
+// Copyright 2015-2019 Luke Shumaker
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build linux
+
+package sd_daemon
+
+import (
+ "bytes"
+ "net"
+ "os"
+
+ "golang.org/x/sys/unix"
+)
+
+func (msg Notification) send(unsetEnv bool) error {
+ if unsetEnv {
+ defer func() { _ = os.Unsetenv("NOTIFY_SOCKET") }()
+ }
+
+ socketAddr := &net.UnixAddr{
+ Name: os.Getenv("NOTIFY_SOCKET"),
+ Net: "unixgram",
+ }
+
+ if socketAddr.Name == "" {
+ return ErrDisabled
+ }
+
+ conn, err := socketUnixgram(socketAddr.Name)
+ if err != nil {
+ return err
+ }
+ defer func() { _ = conn.Close() }()
+
+ var cmsgs [][]byte
+
+ if len(msg.Files) > 0 {
+ fds := make([]int, len(msg.Files))
+ for i := range msg.Files {
+ fds[i] = int(msg.Files[i].Fd())
+ }
+ cmsg := unix.UnixRights(fds...)
+ cmsgs = append(cmsgs, cmsg)
+ }
+
+ havePid := msg.PID > 0 && msg.PID != os.Getpid()
+ if havePid {
+ cmsg := unix.UnixCredentials(&unix.Ucred{
+ Pid: int32(msg.PID),
+ Uid: uint32(os.Getuid()),
+ Gid: uint32(os.Getgid()),
+ })
+ cmsgs = append(cmsgs, cmsg)
+ }
+
+ // If the 2nd argument is empty, this is equivalent to
+ //
+ // conn, _ := net.DialUnix(socketAddr.Net, nil, socketAddr)
+ // conn.Write([]byte(msg.State))
+ _, _, err = conn.WriteMsgUnix([]byte(msg.State), bytes.Join(cmsgs, nil), socketAddr)
+
+ 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 we do this silently without
+ // notifying the caller, but that's what
+ // sd_pid_notify_with_fds does.
+ cmsgs = cmsgs[:len(cmsgs)-1]
+ _, _, err = conn.WriteMsgUnix([]byte(msg.State), bytes.Join(cmsgs, nil), socketAddr)
+ }
+
+ return err
+}
+
+// socketUnixgram wraps socket(2), but doesn't bind(2) or connect(2)
+// the socket to anything. This is an ugly hack because none of the
+// functions in "net" actually allow you to get a AF_UNIX socket not
+// bound/connected to anything.
+//
+// At some point you begin to question if it is worth it to keep up
+// the high-level interface of "net", and messing around with FileConn
+// and UnixConn. Maybe we just drop to using unix.Socket and
+// unix.SendmsgN directly.
+func socketUnixgram(name string) (*net.UnixConn, error) {
+ fd, err := unix.Socket(unix.AF_UNIX, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
+ if err != nil {
+ return nil, os.NewSyscallError("socket", err)
+ }
+ defer unix.Close(fd)
+ conn, err := net.FileConn(os.NewFile(uintptr(fd), name))
+ if err != nil {
+ return nil, err
+ }
+ unixConn := conn.(*net.UnixConn)
+ return unixConn, nil
+}
diff --git a/sd_daemon/notify_nonlinux.go b/sd_daemon/notify_nonlinux.go
index 7b37a19..d09cef0 100644
--- a/sd_daemon/notify_nonlinux.go
+++ b/sd_daemon/notify_nonlinux.go
@@ -3,7 +3,7 @@
//
// Copyright 2013, 2015 Docker, Inc.
// Copyright 2014 CoreOS, Inc.
-// Copyright 2015-2018 Luke Shumaker
+// Copyright 2015-2019 Luke Shumaker
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -30,52 +30,7 @@ import (
"golang.org/x/sys/unix"
)
-// Notification is a message to be sent to the service manager about
-// state changes.
-type Notification struct {
- // PID specifies which process to send a notification about.
- // If PID <= 0, or if the current process does not have
- // privileges to send messages on behalf of other processes,
- // then the message is simply sent from the current process.
- PID int
-
- // State should contain a newline-separated list of variable
- // assignments. See the documentation for sd_notify(3) for
- // well-known variable assignments.
- //
- // https://www.freedesktop.org/software/systemd/man/sd_notify.html
- State string
-
- // Files is a list of file descriptors to send to the service
- // manager with the message. This is useful for keeping files
- // open across restarts, as it enables the service manager to
- // 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 (again,
- // see sd_notify(3) for well-known variable assignments).
- Files []*os.File
-}
-
-// Send sends the Notification to the service manager.
-//
-// 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 notify operations to fail.
-//
-// If the service manager is not listening for notifications from this
-// process tree (or a Notification has has already been send 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, then 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 (msg Notification) Send(unsetEnv bool) error {
+func (msg Notification) send(unsetEnv bool) error {
if unsetEnv {
defer func() { _ = os.Unsetenv("NOTIFY_SOCKET") }()
}