From da323d62044ca98455763300596645dc082bd007 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Wed, 18 Jan 2017 16:49:40 -0500 Subject: Change the notify signature (again). BREAKING CHANGE. I think this should reduce the cognitive load of using the function. --- sd_daemon/log_test.go | 8 ++-- sd_daemon/notify.go | 98 ++++++++++++++++++++++++++++-------------------- sd_daemon/notify_test.go | 23 ++++++++++++ 3 files changed, 85 insertions(+), 44 deletions(-) create mode 100644 sd_daemon/notify_test.go diff --git a/sd_daemon/log_test.go b/sd_daemon/log_test.go index 257ccb9..d9937e2 100644 --- a/sd_daemon/log_test.go +++ b/sd_daemon/log_test.go @@ -1,4 +1,4 @@ -// Copyright 2016 Luke Shumaker +// Copyright 2016-2017 Luke Shumaker // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,11 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -package sd_daemon +package sd_daemon_test import ( "log/syslog" "testing" + + "git.lukeshu.com/go/libsystemd/sd_daemon" ) type cbWriter func(p []byte) (n int, err error) @@ -27,7 +29,7 @@ func (cb cbWriter) Write(p []byte) (n int, err error) { func TestLog(t *testing.T) { var written []byte - log := NewLogger(cbWriter(func(p []byte) (n int, err error) { + log := sd_daemon.NewLogger(cbWriter(func(p []byte) (n int, err error) { written = p return len(p), nil })) diff --git a/sd_daemon/notify.go b/sd_daemon/notify.go index f2b9076..fcd8060 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-2016 Luke Shumaker +// Copyright 2015-2017 Luke Shumaker // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -24,39 +24,52 @@ import ( "golang.org/x/sys/unix" ) -// 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. +// 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 + // priveleges to send messages on behalf of other processes, + // then the message is simply sent from the current process. + PID int + + // State should countain 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 calls to this function to -// fail. -// -// The state parameter should countain 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 -// -// 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 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). +// NOTIFY_SOCKET, which will cause further notify operations to fail. // // If the service manager is not listening for notifications from this -// process tree (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 { +// 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 { if unsetEnv { defer func() { _ = os.Unsetenv("NOTIFY_SOCKET") }() } @@ -78,37 +91,40 @@ func Notify(pid int, unsetEnv bool, state string, files []*os.File) error { var cmsgs [][]byte - if len(files) > 0 { - fds := make([]int, len(files)) - for i := range files { - fds[i] = int(files[i].Fd()) + 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 := pid > 0 && pid != os.Getpid() + havePid := msg.PID > 0 && msg.PID != os.Getpid() if havePid { cmsg := unix.UnixCredentials(&unix.Ucred{ - Pid: int32(pid), + 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 .Write([]byte(state)) - _, _, err = conn.WriteMsgUnix([]byte(state), bytes.Join(cmsgs, nil), socketAddr) + // 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. + // 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 + // 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(state), bytes.Join(cmsgs, nil), socketAddr) + _, _, err = conn.WriteMsgUnix([]byte(msg.State), bytes.Join(cmsgs, nil), socketAddr) } return err diff --git a/sd_daemon/notify_test.go b/sd_daemon/notify_test.go new file mode 100644 index 0000000..b9910ae --- /dev/null +++ b/sd_daemon/notify_test.go @@ -0,0 +1,23 @@ +// Copyright 2016-2017 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. + +package sd_daemon_test + +import ( + "git.lukeshu.com/go/libsystemd/sd_daemon" +) + +func ExampleNotification() { + sd_daemon.Notification{State: "READY=1\nSTATUS=Daemon is running"}.Send(false) +} -- cgit v1.2.3