]> git.proxmox.com Git - mirror_frr.git/blob - lib/systemd.c
Merge pull request #8870 from anlancs/master-fix-reload-service
[mirror_frr.git] / lib / systemd.c
1 /* lib/systemd Code
2 * Copyright (C) 2016 Cumulus Networks, Inc.
3 * Donald Sharp
4 *
5 * This file is part of Quagga.
6 *
7 * Quagga is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2, or (at your option) any
10 * later version.
11 *
12 * Quagga is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; see the file COPYING; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include <zebra.h>
23 #include <sys/un.h>
24
25 #include "thread.h"
26 #include "systemd.h"
27 #include "lib_errors.h"
28
29 /* these are cleared from env so they don't "leak" into things we fork(),
30 * particularly for watchfrr starting individual daemons
31 *
32 * watchdog_pid is currently not used since watchfrr starts forking.
33 * (TODO: handle that better, somehow?)
34 */
35 static pid_t watchdog_pid = -1;
36 static intmax_t watchdog_msec;
37
38 /* not used yet, but can trigger auto-switch to journald logging */
39 bool sd_stdout_is_journal;
40 bool sd_stderr_is_journal;
41
42 static char *notify_socket;
43
44 /* talk to whatever entity claims to be systemd ;)
45 *
46 * refer to sd_notify docs for messages systemd accepts over this socket.
47 * This function should be functionally equivalent to sd_notify().
48 */
49 static void systemd_send_information(const char *info)
50 {
51 int sock;
52 struct sockaddr_un sun;
53
54 if (!notify_socket)
55 return;
56
57 sock = socket(AF_UNIX, SOCK_DGRAM, 0);
58 if (sock < 0)
59 return;
60
61 sun.sun_family = AF_UNIX;
62 strlcpy(sun.sun_path, notify_socket, sizeof(sun.sun_path));
63
64 /* linux abstract unix socket namespace */
65 if (sun.sun_path[0] == '@')
66 sun.sun_path[0] = '\0';
67
68 /* nothing we can do if this errors out... */
69 (void)sendto(sock, info, strlen(info), 0, (struct sockaddr *)&sun,
70 sizeof(sun));
71
72 close(sock);
73 }
74
75 void systemd_send_stopping(void)
76 {
77 systemd_send_information("STATUS=");
78 systemd_send_information("STOPPING=1");
79 }
80
81 static struct thread_master *systemd_master = NULL;
82
83 static int systemd_send_watchdog(struct thread *t)
84 {
85 systemd_send_information("WATCHDOG=1");
86
87 assert(watchdog_msec > 0);
88 thread_add_timer_msec(systemd_master, systemd_send_watchdog, NULL,
89 watchdog_msec, NULL);
90 return 1;
91 }
92
93 void systemd_send_started(struct thread_master *m)
94 {
95 assert(m != NULL);
96
97 systemd_master = m;
98
99 systemd_send_information("READY=1");
100 if (watchdog_msec > 0)
101 systemd_send_watchdog(NULL);
102 }
103
104 void systemd_send_status(const char *status)
105 {
106 char buffer[1024];
107
108 snprintf(buffer, sizeof(buffer), "STATUS=%s", status);
109 systemd_send_information(buffer);
110 }
111
112 static intmax_t getenv_int(const char *varname, intmax_t dflt)
113 {
114 char *val, *err;
115 intmax_t intval;
116
117 val = getenv(varname);
118 if (!val)
119 return dflt;
120
121 intval = strtoimax(val, &err, 0);
122 if (*err || !*val)
123 return dflt;
124 return intval;
125 }
126
127 void systemd_init_env(void)
128 {
129 char *tmp;
130 uintmax_t dev, ino;
131 int len;
132 struct stat st;
133
134 notify_socket = getenv("NOTIFY_SOCKET");
135
136 /* no point in setting up watchdog w/o notify socket */
137 if (notify_socket) {
138 intmax_t watchdog_usec;
139
140 watchdog_pid = getenv_int("WATCHDOG_PID", -1);
141 if (watchdog_pid <= 0)
142 watchdog_pid = -1;
143
144 /* note this is the deadline, hence the divide by 3 */
145 watchdog_usec = getenv_int("WATCHDOG_USEC", 0);
146 if (watchdog_usec >= 3000)
147 watchdog_msec = watchdog_usec / 3000;
148 else {
149 if (watchdog_usec != 0)
150 flog_err(
151 EC_LIB_UNAVAILABLE,
152 "systemd expects a %jd microsecond watchdog timer, but FRR only supports millisecond resolution!",
153 watchdog_usec);
154 watchdog_msec = 0;
155 }
156 }
157
158 tmp = getenv("JOURNAL_STREAM");
159 if (tmp && sscanf(tmp, "%ju:%ju%n", &dev, &ino, &len) == 2
160 && (size_t)len == strlen(tmp)) {
161 if (fstat(1, &st) == 0 && st.st_dev == (dev_t)dev
162 && st.st_ino == (ino_t)ino)
163 sd_stdout_is_journal = true;
164 if (fstat(2, &st) == 0 && st.st_dev == (dev_t)dev
165 && st.st_ino == (ino_t)ino)
166 sd_stderr_is_journal = true;
167 }
168
169 /* these should *not* be passed to any other process we start */
170 unsetenv("WATCHDOG_PID");
171 unsetenv("WATCHDOG_USEC");
172 }