Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-or-later
2 : /* lib/systemd Code
3 : * Copyright (C) 2016 Cumulus Networks, Inc.
4 : * Donald Sharp
5 : */
6 :
7 : #include <zebra.h>
8 : #include <sys/un.h>
9 :
10 : #include "frrevent.h"
11 : #include "systemd.h"
12 : #include "lib_errors.h"
13 :
14 : /* these are cleared from env so they don't "leak" into things we fork(),
15 : * particularly for watchfrr starting individual daemons
16 : *
17 : * watchdog_pid is currently not used since watchfrr starts forking.
18 : * (TODO: handle that better, somehow?)
19 : */
20 : static pid_t watchdog_pid = -1;
21 : static intmax_t watchdog_msec;
22 :
23 : /* not used yet, but can trigger auto-switch to journald logging */
24 : bool sd_stdout_is_journal;
25 : bool sd_stderr_is_journal;
26 :
27 : static char *notify_socket;
28 :
29 : /* talk to whatever entity claims to be systemd ;)
30 : *
31 : * refer to sd_notify docs for messages systemd accepts over this socket.
32 : * This function should be functionally equivalent to sd_notify().
33 : */
34 0 : static void systemd_send_information(const char *info)
35 : {
36 0 : int sock;
37 0 : struct sockaddr_un sun;
38 :
39 0 : if (!notify_socket)
40 0 : return;
41 :
42 0 : sock = socket(AF_UNIX, SOCK_DGRAM, 0);
43 0 : if (sock < 0)
44 : return;
45 :
46 0 : sun.sun_family = AF_UNIX;
47 0 : strlcpy(sun.sun_path, notify_socket, sizeof(sun.sun_path));
48 :
49 : /* linux abstract unix socket namespace */
50 0 : if (sun.sun_path[0] == '@')
51 0 : sun.sun_path[0] = '\0';
52 :
53 : /* nothing we can do if this errors out... */
54 0 : (void)sendto(sock, info, strlen(info), 0, (struct sockaddr *)&sun,
55 : sizeof(sun));
56 :
57 0 : close(sock);
58 : }
59 :
60 0 : void systemd_send_stopping(void)
61 : {
62 0 : systemd_send_information("STATUS=");
63 0 : systemd_send_information("STOPPING=1");
64 0 : }
65 :
66 : static struct event_loop *systemd_master = NULL;
67 :
68 0 : static void systemd_send_watchdog(struct event *t)
69 : {
70 0 : systemd_send_information("WATCHDOG=1");
71 :
72 0 : assert(watchdog_msec > 0);
73 0 : event_add_timer_msec(systemd_master, systemd_send_watchdog, NULL,
74 : watchdog_msec, NULL);
75 0 : }
76 :
77 0 : void systemd_send_started(struct event_loop *m)
78 : {
79 0 : assert(m != NULL);
80 :
81 0 : systemd_master = m;
82 :
83 0 : systemd_send_information("READY=1");
84 0 : if (watchdog_msec > 0)
85 0 : systemd_send_watchdog(NULL);
86 0 : }
87 :
88 0 : void systemd_send_status(const char *status)
89 : {
90 0 : char buffer[1024];
91 :
92 0 : snprintf(buffer, sizeof(buffer), "STATUS=%s", status);
93 0 : systemd_send_information(buffer);
94 0 : }
95 :
96 0 : static intmax_t getenv_int(const char *varname, intmax_t dflt)
97 : {
98 0 : char *val, *err;
99 0 : intmax_t intval;
100 :
101 0 : val = getenv(varname);
102 0 : if (!val)
103 : return dflt;
104 :
105 0 : intval = strtoimax(val, &err, 0);
106 0 : if (*err || !*val)
107 : return dflt;
108 : return intval;
109 : }
110 :
111 4 : void systemd_init_env(void)
112 : {
113 4 : char *tmp;
114 4 : uintmax_t dev, ino;
115 4 : int len;
116 4 : struct stat st;
117 :
118 4 : notify_socket = getenv("NOTIFY_SOCKET");
119 :
120 : /* no point in setting up watchdog w/o notify socket */
121 4 : if (notify_socket) {
122 0 : intmax_t watchdog_usec;
123 :
124 0 : watchdog_pid = getenv_int("WATCHDOG_PID", -1);
125 0 : if (watchdog_pid <= 0)
126 0 : watchdog_pid = -1;
127 :
128 : /* note this is the deadline, hence the divide by 3 */
129 0 : watchdog_usec = getenv_int("WATCHDOG_USEC", 0);
130 0 : if (watchdog_usec >= 3000)
131 0 : watchdog_msec = watchdog_usec / 3000;
132 : else {
133 0 : if (watchdog_usec != 0)
134 0 : flog_err(
135 : EC_LIB_UNAVAILABLE,
136 : "systemd expects a %jd microsecond watchdog timer, but FRR only supports millisecond resolution!",
137 : watchdog_usec);
138 0 : watchdog_msec = 0;
139 : }
140 : }
141 :
142 4 : tmp = getenv("JOURNAL_STREAM");
143 4 : if (tmp && sscanf(tmp, "%ju:%ju%n", &dev, &ino, &len) == 2
144 4 : && (size_t)len == strlen(tmp)) {
145 4 : if (fstat(1, &st) == 0 && st.st_dev == (dev_t)dev
146 0 : && st.st_ino == (ino_t)ino)
147 0 : sd_stdout_is_journal = true;
148 4 : if (fstat(2, &st) == 0 && st.st_dev == (dev_t)dev
149 0 : && st.st_ino == (ino_t)ino)
150 0 : sd_stderr_is_journal = true;
151 : }
152 :
153 : /* these should *not* be passed to any other process we start */
154 4 : unsetenv("WATCHDOG_PID");
155 4 : unsetenv("WATCHDOG_USEC");
156 4 : }
|