chiark / gitweb /
sd-daemon: introduce sd_watchdog_enabled() for parsing $WATCHDOG_USEC
authorLennart Poettering <lennart@poettering.net>
Sun, 22 Dec 2013 21:14:05 +0000 (22:14 +0100)
committerLennart Poettering <lennart@poettering.net>
Sun, 22 Dec 2013 21:19:03 +0000 (22:19 +0100)
Also, introduce a new environment variable named $WATCHDOG_PID which
cotnains the PID of the process that is supposed to send the keep-alive
events. This is similar how $LISTEN_FDS and $LISTEN_PID work together,
and protects against confusing processes further down the process tree
due to inherited environment.

14 files changed:
Makefile-man.am
man/sd-daemon.xml
man/sd_notify.xml
man/sd_watchdog_enabled.xml [new file with mode: 0644]
src/core/execute.c
src/core/execute.h
src/core/mount.c
src/core/service.c
src/core/socket.c
src/core/swap.c
src/libsystemd-bus/sd-event.c
src/libsystemd-daemon/libsystemd-daemon.sym
src/libsystemd-daemon/sd-daemon.c
src/systemd/sd-daemon.h

index 968de7da627af7b8f3c73a7048edfbab82501136..df8860a3f0ff33123f196cc59c68df9c9139df8e 100644 (file)
@@ -41,6 +41,7 @@ MANPAGES += \
        man/sd_journal_stream_fd.3 \
        man/sd_listen_fds.3 \
        man/sd_notify.3 \
+       man/sd_watchdog_enabled.3 \
        man/shutdown.8 \
        man/sysctl.d.5 \
        man/systemctl.1 \
@@ -1133,6 +1134,7 @@ EXTRA_DIST += \
        man/sd_seat_get_active.xml \
        man/sd_session_is_active.xml \
        man/sd_uid_get_state.xml \
+       man/sd_watchdog_enabled.xml \
        man/shutdown.xml \
        man/sysctl.d.xml \
        man/systemctl.xml \
index 6e804e1a6c7f03aaa826db119a94a772249709af..74011123bb2a77885b68b9869396cea0fd1b2b4f 100644 (file)
                         <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>sd_booted</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>sd_is_fifo</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+                        <citerefentry><refentrytitle>sd_watchdog_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
index 55965ffce4e999d9f0a1b34e1b674683976cf497..683967cd482c9c2f8c06de46e492c9ece0f2ea9c 100644 (file)
                                 <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
                                 for details. It is recommended to send
                                 this message if the
-                                <varname>WATCHDOG_USEC=</varname>
-                                environment variable has been set for
-                                the service process, in every half the
-                                time interval that is specified in the
-                                variable.</para></listitem>
+                                <varname>$WATCHDOG_PID</varname>
+                                environment variable has been set to
+                                the PID of the service process, in
+                                every half the time interval that is
+                                specified in the
+                                <varname>$WATCHDOG_USEC</varname>
+                                environment variable. See
+                                <citerefentry><refentrytitle>sd_watchdog_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+                                for details.</para></listitem>
                         </varlistentry>
                 </variablelist>
 
                         <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
-                        <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+                        <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+                        <citerefentry><refentrytitle>sd_watchdog_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
                 </para>
         </refsect1>
 
diff --git a/man/sd_watchdog_enabled.xml b/man/sd_watchdog_enabled.xml
new file mode 100644 (file)
index 0000000..e42ae43
--- /dev/null
@@ -0,0 +1,198 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+        "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  This file is part of systemd.
+
+  Copyright 2013 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<refentry id="sd_watchdog_enabled">
+
+        <refentryinfo>
+                <title>sd_watchdog_enabled</title>
+                <productname>systemd</productname>
+
+                <authorgroup>
+                        <author>
+                                <contrib>Developer</contrib>
+                                <firstname>Lennart</firstname>
+                                <surname>Poettering</surname>
+                                <email>lennart@poettering.net</email>
+                        </author>
+                </authorgroup>
+        </refentryinfo>
+
+        <refmeta>
+                <refentrytitle>sd_watchdog_enabled</refentrytitle>
+                <manvolnum>3</manvolnum>
+        </refmeta>
+
+        <refnamediv>
+                <refname>sd_watchdog_enabled</refname>
+                <refpurpose>Check whether the service manager expects watchdog keep-alive notifications from a service</refpurpose>
+        </refnamediv>
+
+        <refsynopsisdiv>
+                <funcsynopsis>
+                        <funcsynopsisinfo>#include &lt;systemd/sd-daemon.h&gt;</funcsynopsisinfo>
+
+                        <funcprototype>
+                                <funcdef>int <function>sd_watchdog_enabled</function></funcdef>
+                                <paramdef>int <parameter>unset_environment</parameter></paramdef>
+                                <paramdef>const uint64_t *<parameter>usec</parameter></paramdef>
+                        </funcprototype>
+                </funcsynopsis>
+        </refsynopsisdiv>
+
+        <refsect1>
+                <title>Description</title>
+                <para><function>sd_watchdog_enabled()</function> may
+                be called by a service to detect whether the service
+                manager expects regular keep-alive watchdog
+                notification events from it, and the timeout after
+                which the manager will act on the service if it did
+                not get such a notification.</para>
+
+                <para>If the <parameter>unset_environment</parameter>
+                parameter is non-zero,
+                <function>sd_watchdog_enabled()</function> will unset
+                the <varname>$WATCHDOG_USEC</varname> and
+                <varname>$WATCHDOG_PID</varname> environment variables
+                before returning (regardless whether the function call
+                itself succeeded or not). Further calls to
+                <function>sd_watchdog_enabled()</function> will then
+                return with zero, but the variable is no longer
+                inherited by child processes.</para>
+
+                <para>If the <parameter>usec</parameter> parameter is
+                non-NULL <function>sd_watchdog_enabled()</function>
+                will return the timeout in µs for the watchdog
+                logic. The service manager will usually terminate a
+                service when it did not get a notification message
+                within the specified time after startup and after each
+                previous message. It is recommended that a daemon
+                sends a keep-alive notification message to the service
+                manager every half of the time returned
+                here. Notification messages may be sent with
+                <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+                with a message string of
+                <literal>WATCHDOG=1</literal>.</para>
+
+                <para>To enable service supervision with the watchdog
+                logic use <varname>WatchdogSec=</varname> in service
+                files. See
+                <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+                for details.</para>
+        </refsect1>
+
+        <refsect1>
+                <title>Return Value</title>
+
+                <para>On failure, this call returns a negative
+                errno-style error code. If the service manager expects
+                watchdog keep-alive notification messages to be sent,
+                &gt; 0 is returned, otherwise 0 is returned. Only if
+                the return value is &gt; 0 the
+                <parameter>usec</parameter> parameter is valid after
+                the call.</para>
+        </refsect1>
+
+        <refsect1>
+                <title>Notes</title>
+
+                <para>This function is provided by the reference
+                implementation of APIs for new-style daemons and
+                distributed with the systemd package. The algorithm
+                it implements is simple, and can easily be
+                reimplemented in daemons if it is important to support
+                this interface without using the reference
+                implementation.</para>
+
+                <para>Internally, this functions parses the
+                <varname>$WATCHDOG_PID</varname> and
+                <varname>$WATCHDOG_USEC</varname> environment
+                variable. The call will ignore these variables if
+                <varname>$WATCHDOG_PID</varname> does containe the PID
+                of the current process, under the assumption that in
+                that case the variables were set for a different
+                process further up the process tree.</para>
+
+                <para>For details about the algorithm check the
+                liberally licensed reference implementation sources:
+                <ulink url="http://cgit.freedesktop.org/systemd/systemd/plain/src/libsystemd-daemon/sd-daemon.c"/>
+                and <ulink
+                url="http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-daemon.h"/></para>
+
+                <para><function>sd_watchdog_enabled()</function> is
+                implemented in the reference implementation's
+                <filename>sd-daemon.c</filename> and
+                <filename>sd-daemon.h</filename> files. These
+                interfaces are available as shared library, which can
+                be compiled and linked to with the
+                <constant>libsystemd-daemon</constant> <citerefentry><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+                file. Alternatively, applications consuming these APIs
+                may copy the implementation into their source
+                tree. For more details about the reference
+                implementation see
+                <citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+
+                <para>If the reference implementation is used as
+                drop-in files and -DDISABLE_SYSTEMD is set during
+                compilation, these functions will always return 0 and
+                otherwise become a NOP.</para>
+        </refsect1>
+
+        <refsect1>
+                <title>Environment</title>
+
+                <variablelist class='environment-variables'>
+                        <varlistentry>
+                                <term><varname>$WATCHDOG_PID</varname></term>
+
+                                <listitem><para>Set by the system
+                                manager for supervised process for
+                                which watchdog support is enabled, and
+                                contains the PID of that process. See
+                                above for details.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><varname>$WATCHDOG_USEC</varname></term>
+
+                                <listitem><para>Set by the system
+                                manager for supervised process for
+                                which watchdog support is enabled, and
+                                contains the watchdog timeout in µs
+                                See above for
+                                details.</para></listitem>
+                        </varlistentry>
+                </variablelist>
+        </refsect1>
+
+        <refsect1>
+                <title>See Also</title>
+                <para>
+                        <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+                        <citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+                        <citerefentry><refentrytitle>daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+                        <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+                        <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+                </para>
+        </refsect1>
+
+</refentry>
index 4265717895e196c147f47d47566acc99dc403992..9b33ec0ec5efbefec0002bd00dc20be8e825d328 100644 (file)
@@ -1020,6 +1020,7 @@ static void do_idle_pipe_dance(int idle_pipe[4]) {
 static int build_environment(
                 ExecContext *c,
                 unsigned n_fds,
+                usec_t watchdog_usec,
                 const char *home,
                 const char *username,
                 const char *shell,
@@ -1032,7 +1033,7 @@ static int build_environment(
         assert(c);
         assert(ret);
 
-        our_env = new(char*, 8);
+        our_env = new0(char*, 10);
         if (!our_env)
                 return -ENOMEM;
 
@@ -1046,6 +1047,16 @@ static int build_environment(
                 our_env[n_env++] = x;
         }
 
+        if (watchdog_usec > 0) {
+                if (asprintf(&x, "WATCHDOG_PID=%lu", (unsigned long) getpid()) < 0)
+                        return -ENOMEM;
+                our_env[n_env++] = x;
+
+                if (asprintf(&x, "WATCHDOG_USEC=%llu", (unsigned long long) watchdog_usec) < 0)
+                        return -ENOMEM;
+                our_env[n_env++] = x;
+        }
+
         if (home) {
                 x = strappend("HOME=", home);
                 if (!x)
@@ -1084,7 +1095,7 @@ static int build_environment(
         }
 
         our_env[n_env++] = NULL;
-        assert(n_env <= 8);
+        assert(n_env <= 10);
 
         *ret = our_env;
         our_env = NULL;
@@ -1104,6 +1115,7 @@ int exec_spawn(ExecCommand *command,
                CGroupControllerMask cgroup_supported,
                const char *cgroup_path,
                const char *unit_id,
+               usec_t watchdog_usec,
                int idle_pipe[4],
                ExecRuntime *runtime,
                pid_t *ret) {
@@ -1560,7 +1572,7 @@ int exec_spawn(ExecCommand *command,
                         }
                 }
 
-                err = build_environment(context, n_fds, home, username, shell, &our_env);
+                err = build_environment(context, n_fds, watchdog_usec, home, username, shell, &our_env);
                 if (r < 0) {
                         r = EXIT_MEMORY;
                         goto fail_child;
index bd3db0b633274eb1028292d20b41137b2b5cf90e..989373f4818f20a9c40c364dccd5fdb32cee312e 100644 (file)
@@ -181,6 +181,7 @@ int exec_spawn(ExecCommand *command,
                CGroupControllerMask cgroup_mask,
                const char *cgroup_path,
                const char *unit_id,
+               usec_t watchdog_usec,
                int pipe_fd[2],
                ExecRuntime *runtime,
                pid_t *ret);
index 2b7934e40412a9aa606af30b23c6179fbac1c909..09efa1b6e0649f652f814cdc319d2432c95d14e5 100644 (file)
@@ -787,6 +787,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
                        UNIT(m)->manager->cgroup_supported,
                        UNIT(m)->cgroup_path,
                        UNIT(m)->id,
+                       0,
                        NULL,
                        m->exec_runtime,
                        &pid);
index 87eaa29378b369a19c4cf462588e4adf6a90be55..4eb3d9e6688d1ff4990dc30a08d424f8001b2135 100644 (file)
@@ -1750,7 +1750,7 @@ static int service_spawn(
         if (r < 0)
                 goto fail;
 
-        our_env = new0(char*, 5);
+        our_env = new0(char*, 4);
         if (!our_env) {
                 r = -ENOMEM;
                 goto fail;
@@ -1768,12 +1768,6 @@ static int service_spawn(
                         goto fail;
                 }
 
-        if (s->watchdog_usec > 0)
-                if (asprintf(our_env + n_env++, "WATCHDOG_USEC=%llu", (unsigned long long) s->watchdog_usec) < 0) {
-                        r = -ENOMEM;
-                        goto fail;
-                }
-
         if (UNIT(s)->manager->running_as != SYSTEMD_SYSTEM)
                 if (asprintf(our_env + n_env++, "MANAGERPID=%lu", (unsigned long) getpid()) < 0) {
                         r = -ENOMEM;
@@ -1804,6 +1798,7 @@ static int service_spawn(
                        UNIT(s)->manager->cgroup_supported,
                        path,
                        UNIT(s)->id,
+                       s->watchdog_usec,
                        s->type == SERVICE_IDLE ? UNIT(s)->manager->idle_pipe : NULL,
                        s->exec_runtime,
                        &pid);
index 2442221355356a1e4f485d4485dcf93b0a25cb3f..31fc2a25269a659125ee42f284792e8040943af8 100644 (file)
@@ -1254,6 +1254,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
                        UNIT(s)->manager->cgroup_supported,
                        UNIT(s)->cgroup_path,
                        UNIT(s)->id,
+                       0,
                        NULL,
                        s->exec_runtime,
                        &pid);
index 79862e5b87183a0bc9503b2e487fe07f9b09a6ea..e0627db965ab10f5f47b03f3723445c72931d1c3 100644 (file)
@@ -645,6 +645,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
                        UNIT(s)->manager->cgroup_supported,
                        UNIT(s)->cgroup_path,
                        UNIT(s)->id,
+                       0,
                        NULL,
                        s->exec_runtime,
                        &pid);
index a1baac52aaaa324a5b372d05583d30293ad18502..bfc798ca6e91b1b558c6325179383d97fccd00d8 100644 (file)
@@ -2164,17 +2164,10 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) {
 
         if (b) {
                 struct epoll_event ev = {};
-                const char *env;
 
-                env = getenv("WATCHDOG_USEC");
-                if (!env)
-                        return false;
-
-                r = safe_atou64(env, &e->watchdog_period);
-                if (r < 0)
+                r = sd_watchdog_enabled(false, &e->watchdog_period);
+                if (r <= 0)
                         return r;
-                if (e->watchdog_period <= 0)
-                        return -EIO;
 
                 /* Issue first ping immediately */
                 sd_notify(false, "WATCHDOG=1");
index f4402389319d6b4be16315f4ac9fdb174c488743..aa9be51c6ab3ea077930b1f2b018d653815c53b8 100644 (file)
@@ -25,3 +25,8 @@ global:
 local:
         *;
 };
+
+LIBSYSTEMD_DAEMON_209 {
+global:
+        sd_watchdog_enabled;
+} LIBSYSTEMD_DAEMON_31;
index 485b301023f3a7d36e388c282a4816e18c2f9499..94230c9ed66120faf3856598931e36b00eeed9fd 100644 (file)
@@ -518,3 +518,69 @@ _sd_export_ int sd_booted(void) {
         return !!S_ISDIR(st.st_mode);
 #endif
 }
+
+_sd_export_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
+
+#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
+        return 0;
+#else
+        unsigned long long ll;
+        unsigned long l;
+        const char *e;
+        char *p = NULL;
+        int r;
+
+        e = getenv("WATCHDOG_PID");
+        if (!e) {
+                r = 0;
+                goto finish;
+        }
+
+        errno = 0;
+        l = strtoul(e, &p, 10);
+        if (errno > 0) {
+                r = -errno;
+                goto finish;
+        }
+        if (!p || p == e || *p || l <= 0) {
+                r = -EINVAL;
+                goto finish;
+        }
+
+        /* Is this for us? */
+        if (getpid() != (pid_t) l) {
+                r = 0;
+                goto finish;
+        }
+
+        e = getenv("WATCHDOG_USEC");
+        if (!e) {
+                r = -EINVAL;
+                goto finish;
+        }
+
+        errno = 0;
+        ll = strtoull(e, &p, 10);
+        if (errno > 0) {
+                r = -errno;
+                goto finish;
+        }
+        if (!p || p == e || *p || l <= 0) {
+                r = -EINVAL;
+                goto finish;
+        }
+
+        if (usec)
+                *usec = ll;
+
+        r = 1;
+
+finish:
+        if (unset_environment) {
+                unsetenv("WATCHDOG_PID");
+                unsetenv("WATCHDOG_USEC");
+        }
+
+        return r;
+#endif
+}
index daa3f4c857946ed81b607a6ed07864b39e04e077..43deb8cc7dfadf8b21cd9e717051279b9a5bdc86 100644 (file)
@@ -186,6 +186,8 @@ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t
   the file descriptor is a POSIX Message Queue of the specified name,
   0 otherwise. If path is NULL a message queue name check is not
   done. Returns a negative errno style error code on failure.
+
+  See sd_is_mq(3) for more information.
 */
 int sd_is_mq(int fd, const char *path);
 
@@ -220,7 +222,8 @@ int sd_is_mq(int fd, const char *path);
      WATCHDOG=1   Tells systemd to update the watchdog timestamp.
                   Services using this feature should do this in
                   regular intervals. A watchdog framework can use the
-                  timestamps to detect failed services.
+                  timestamps to detect failed services. Also see
+                  sd_watchdog_enabled() below.
 
   Daemons can choose to send additional variables. However, it is
   recommended to prefix variable names not listed above with X_.
@@ -275,6 +278,22 @@ int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_(
 */
 int sd_booted(void);
 
+/*
+  Returns > 0 if the service manager expects watchdog keep-alive
+  events to be sent regularly via sd_notify(0, "WATCHDOG=1"). Returns
+  0 if it does not expect this. If the usec argument is non-NULL
+  returns the watchdog timeout in µs after which the service manager
+  will act on a process that has not sent a watchdog keep alive
+  message. This function is useful to implement services that
+  recognize automatically if they are being run under supervision of
+  systemd with WatchdogSec= set. It is recommended for clients to
+  generate keep-alive pings via sd_notify(0, "WATCHDOG=1") every half
+  of the returned time.
+
+  See sd_watchdog_enabled(3) for more information.
+*/
+int sd_watchdog_enabled(int unset_environment, uint64_t *usec);
+
 #ifdef __cplusplus
 }
 #endif