]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-daemon/sd-daemon.c
Merge pull request #25791 from keszybz/ukify-check-inputs
[thirdparty/systemd.git] / src / libsystemd / sd-daemon / sd-daemon.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
8c47c732 2
abbbea81 3#include <errno.h>
916abb21 4#include <limits.h>
0ebee881 5#include <mqueue.h>
8dd4c05b 6#include <netinet/in.h>
4f07ddfa 7#include <poll.h>
8dd4c05b
LP
8#include <stdarg.h>
9#include <stddef.h>
10#include <stdio.h>
11#include <stdlib.h>
8dd4c05b
LP
12#include <sys/stat.h>
13#include <sys/un.h>
14#include <unistd.h>
abbbea81 15
07630cea
LP
16#include "sd-daemon.h"
17
b5efdb8a 18#include "alloc-util.h"
3ffd4af2 19#include "fd-util.h"
f4f15635 20#include "fs-util.h"
cb310866 21#include "io-util.h"
6bedfcbb 22#include "parse-util.h"
be8f4e9e 23#include "path-util.h"
dccca82b 24#include "process-util.h"
3cb46740 25#include "socket-util.h"
a9dac7a6 26#include "stat-util.h"
8dd4c05b 27#include "strv.h"
4f07ddfa 28#include "time-util.h"
8dd4c05b 29
a47806fa
LP
30#define SNDBUF_SIZE (8*1024*1024)
31
8dd4c05b 32static void unsetenv_all(bool unset_environment) {
8dd4c05b
LP
33 if (!unset_environment)
34 return;
35
44ee03d1
ZJS
36 assert_se(unsetenv("LISTEN_PID") == 0);
37 assert_se(unsetenv("LISTEN_FDS") == 0);
38 assert_se(unsetenv("LISTEN_FDNAMES") == 0);
8dd4c05b
LP
39}
40
0ebee881 41_public_ int sd_listen_fds(int unset_environment) {
abbbea81 42 const char *e;
fe96c0f8 43 int n, r;
be8f4e9e 44 pid_t pid;
abbbea81 45
50425d16
MS
46 e = getenv("LISTEN_PID");
47 if (!e) {
abbbea81
LP
48 r = 0;
49 goto finish;
50 }
51
be8f4e9e
LP
52 r = parse_pid(e, &pid);
53 if (r < 0)
abbbea81 54 goto finish;
abbbea81
LP
55
56 /* Is this for us? */
df0ff127 57 if (getpid_cached() != pid) {
abbbea81
LP
58 r = 0;
59 goto finish;
60 }
61
50425d16
MS
62 e = getenv("LISTEN_FDS");
63 if (!e) {
abbbea81
LP
64 r = 0;
65 goto finish;
66 }
67
046c93f8 68 r = safe_atoi(e, &n);
be8f4e9e 69 if (r < 0)
abbbea81 70 goto finish;
8640e111 71
046c93f8
VC
72 assert_cc(SD_LISTEN_FDS_START < INT_MAX);
73 if (n <= 0 || n > INT_MAX - SD_LISTEN_FDS_START) {
74 r = -EINVAL;
75 goto finish;
76 }
77
fe96c0f8 78 for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) {
be8f4e9e
LP
79 r = fd_cloexec(fd, true);
80 if (r < 0)
8640e111 81 goto finish;
8640e111
LP
82 }
83
046c93f8 84 r = n;
abbbea81
LP
85
86finish:
8dd4c05b
LP
87 unsetenv_all(unset_environment);
88 return r;
89}
90
91_public_ int sd_listen_fds_with_names(int unset_environment, char ***names) {
92 _cleanup_strv_free_ char **l = NULL;
93 bool have_names;
94 int n_names = 0, n_fds;
95 const char *e;
96 int r;
97
98 if (!names)
99 return sd_listen_fds(unset_environment);
100
101 e = getenv("LISTEN_FDNAMES");
102 if (e) {
90e30d76 103 n_names = strv_split_full(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
8dd4c05b
LP
104 if (n_names < 0) {
105 unsetenv_all(unset_environment);
106 return n_names;
107 }
108
109 have_names = true;
110 } else
111 have_names = false;
112
113 n_fds = sd_listen_fds(unset_environment);
114 if (n_fds <= 0)
115 return n_fds;
116
117 if (have_names) {
118 if (n_names != n_fds)
119 return -EINVAL;
120 } else {
121 r = strv_extend_n(&l, "unknown", n_fds);
122 if (r < 0)
123 return r;
abbbea81
LP
124 }
125
ae2a15bc 126 *names = TAKE_PTR(l);
8dd4c05b
LP
127
128 return n_fds;
abbbea81 129}
7c394faa 130
0ebee881 131_public_ int sd_is_fifo(int fd, const char *path) {
7c394faa
LP
132 struct stat st_fd;
133
e6803801 134 assert_return(fd >= 0, -EBADF);
7c394faa 135
7c394faa
LP
136 if (fstat(fd, &st_fd) < 0)
137 return -errno;
138
139 if (!S_ISFIFO(st_fd.st_mode))
140 return 0;
141
142 if (path) {
143 struct stat st_path;
144
fd8bccfb 145 if (stat(path, &st_path) < 0) {
7c394faa 146
945c2931 147 if (IN_SET(errno, ENOENT, ENOTDIR))
7c394faa
LP
148 return 0;
149
150 return -errno;
151 }
152
a9dac7a6 153 return stat_inode_same(&st_path, &st_fd);
7c394faa
LP
154 }
155
156 return 1;
157}
158
0ebee881 159_public_ int sd_is_special(int fd, const char *path) {
4160ec67
WD
160 struct stat st_fd;
161
e6803801 162 assert_return(fd >= 0, -EBADF);
4160ec67
WD
163
164 if (fstat(fd, &st_fd) < 0)
165 return -errno;
166
167 if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode))
168 return 0;
169
170 if (path) {
171 struct stat st_path;
172
173 if (stat(path, &st_path) < 0) {
174
945c2931 175 if (IN_SET(errno, ENOENT, ENOTDIR))
4160ec67
WD
176 return 0;
177
178 return -errno;
179 }
180
181 if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode))
a9dac7a6 182 return stat_inode_same(&st_path, &st_fd);
4160ec67
WD
183 else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode))
184 return st_path.st_rdev == st_fd.st_rdev;
185 else
186 return 0;
187 }
188
189 return 1;
190}
191
3e4f1dc9 192static int is_socket_internal(int fd, int type, int listening) {
7c394faa
LP
193 struct stat st_fd;
194
e6803801 195 assert_return(fd >= 0, -EBADF);
be8f4e9e 196 assert_return(type >= 0, -EINVAL);
7c394faa
LP
197
198 if (fstat(fd, &st_fd) < 0)
199 return -errno;
200
201 if (!S_ISSOCK(st_fd.st_mode))
202 return 0;
203
204 if (type != 0) {
205 int other_type = 0;
206 socklen_t l = sizeof(other_type);
207
208 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
209 return -errno;
210
211 if (l != sizeof(other_type))
212 return -EINVAL;
213
214 if (other_type != type)
215 return 0;
216 }
217
218 if (listening >= 0) {
219 int accepting = 0;
220 socklen_t l = sizeof(accepting);
221
222 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
223 return -errno;
224
225 if (l != sizeof(accepting))
226 return -EINVAL;
227
dde770cf 228 if (!accepting != !listening)
7c394faa
LP
229 return 0;
230 }
231
232 return 1;
233}
234
0ebee881 235_public_ int sd_is_socket(int fd, int family, int type, int listening) {
88ce42f6
LP
236 int r;
237
e6803801 238 assert_return(fd >= 0, -EBADF);
be8f4e9e 239 assert_return(family >= 0, -EINVAL);
88ce42f6 240
3e4f1dc9 241 r = is_socket_internal(fd, type, listening);
50425d16 242 if (r <= 0)
88ce42f6
LP
243 return r;
244
245 if (family > 0) {
1c633045
ZJS
246 union sockaddr_union sockaddr = {};
247 socklen_t l = sizeof(sockaddr);
88ce42f6
LP
248
249 if (getsockname(fd, &sockaddr.sa, &l) < 0)
250 return -errno;
251
b7f42664 252 if (l < sizeof(sa_family_t))
88ce42f6
LP
253 return -EINVAL;
254
255 return sockaddr.sa.sa_family == family;
256 }
257
258 return 1;
259}
260
0ebee881 261_public_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
1c633045
ZJS
262 union sockaddr_union sockaddr = {};
263 socklen_t l = sizeof(sockaddr);
7c394faa
LP
264 int r;
265
e6803801 266 assert_return(fd >= 0, -EBADF);
be8f4e9e 267 assert_return(IN_SET(family, 0, AF_INET, AF_INET6), -EINVAL);
88ce42f6 268
3e4f1dc9 269 r = is_socket_internal(fd, type, listening);
50425d16 270 if (r <= 0)
7c394faa
LP
271 return r;
272
7c394faa
LP
273 if (getsockname(fd, &sockaddr.sa, &l) < 0)
274 return -errno;
275
b7f42664 276 if (l < sizeof(sa_family_t))
7c394faa
LP
277 return -EINVAL;
278
945c2931 279 if (!IN_SET(sockaddr.sa.sa_family, AF_INET, AF_INET6))
7c394faa
LP
280 return 0;
281
be8f4e9e 282 if (family != 0)
88ce42f6
LP
283 if (sockaddr.sa.sa_family != family)
284 return 0;
285
7c394faa 286 if (port > 0) {
dfde7e8c 287 unsigned sa_port;
7c394faa 288
dfde7e8c
LP
289 r = sockaddr_port(&sockaddr.sa, &sa_port);
290 if (r < 0)
291 return r;
7c394faa 292
dfde7e8c 293 return port == sa_port;
7c394faa
LP
294 }
295
296 return 1;
297}
298
f6f372d2
ZJS
299_public_ int sd_is_socket_sockaddr(int fd, int type, const struct sockaddr* addr, unsigned addr_len, int listening) {
300 union sockaddr_union sockaddr = {};
301 socklen_t l = sizeof(sockaddr);
302 int r;
303
304 assert_return(fd >= 0, -EBADF);
305 assert_return(addr, -EINVAL);
306 assert_return(addr_len >= sizeof(sa_family_t), -ENOBUFS);
307 assert_return(IN_SET(addr->sa_family, AF_INET, AF_INET6), -EPFNOSUPPORT);
308
3e4f1dc9 309 r = is_socket_internal(fd, type, listening);
f6f372d2
ZJS
310 if (r <= 0)
311 return r;
312
313 if (getsockname(fd, &sockaddr.sa, &l) < 0)
314 return -errno;
315
316 if (l < sizeof(sa_family_t))
317 return -EINVAL;
318
319 if (sockaddr.sa.sa_family != addr->sa_family)
320 return 0;
321
322 if (sockaddr.sa.sa_family == AF_INET) {
323 const struct sockaddr_in *in = (const struct sockaddr_in *) addr;
324
325 if (l < sizeof(struct sockaddr_in) || addr_len < sizeof(struct sockaddr_in))
326 return -EINVAL;
327
328 if (in->sin_port != 0 &&
329 sockaddr.in.sin_port != in->sin_port)
330 return false;
331
332 return sockaddr.in.sin_addr.s_addr == in->sin_addr.s_addr;
333
334 } else {
335 const struct sockaddr_in6 *in = (const struct sockaddr_in6 *) addr;
336
337 if (l < sizeof(struct sockaddr_in6) || addr_len < sizeof(struct sockaddr_in6))
338 return -EINVAL;
339
340 if (in->sin6_port != 0 &&
341 sockaddr.in6.sin6_port != in->sin6_port)
342 return false;
343
344 if (in->sin6_flowinfo != 0 &&
345 sockaddr.in6.sin6_flowinfo != in->sin6_flowinfo)
346 return false;
347
348 if (in->sin6_scope_id != 0 &&
349 sockaddr.in6.sin6_scope_id != in->sin6_scope_id)
350 return false;
351
352 return memcmp(sockaddr.in6.sin6_addr.s6_addr, in->sin6_addr.s6_addr,
353 sizeof(in->sin6_addr.s6_addr)) == 0;
354 }
355}
356
0ebee881 357_public_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
1c633045
ZJS
358 union sockaddr_union sockaddr = {};
359 socklen_t l = sizeof(sockaddr);
7c394faa
LP
360 int r;
361
e6803801 362 assert_return(fd >= 0, -EBADF);
be8f4e9e 363
3e4f1dc9 364 r = is_socket_internal(fd, type, listening);
50425d16 365 if (r <= 0)
7c394faa
LP
366 return r;
367
7c394faa
LP
368 if (getsockname(fd, &sockaddr.sa, &l) < 0)
369 return -errno;
370
b7f42664 371 if (l < sizeof(sa_family_t))
7c394faa
LP
372 return -EINVAL;
373
374 if (sockaddr.sa.sa_family != AF_UNIX)
375 return 0;
376
377 if (path) {
d1d7caee 378 if (length == 0)
7c394faa
LP
379 length = strlen(path);
380
d1d7caee 381 if (length == 0)
7c394faa 382 /* Unnamed socket */
0e098b15 383 return l == offsetof(struct sockaddr_un, sun_path);
7c394faa 384
7c394faa
LP
385 if (path[0])
386 /* Normal path socket */
cd250a39 387 return
0e098b15 388 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
cd250a39 389 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
7c394faa
LP
390 else
391 /* Abstract namespace socket */
cd250a39 392 return
0e098b15 393 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
cd250a39 394 memcmp(path, sockaddr.un.sun_path, length) == 0;
7c394faa
LP
395 }
396
397 return 1;
398}
8c47c732 399
0ebee881 400_public_ int sd_is_mq(int fd, const char *path) {
916abb21
LP
401 struct mq_attr attr;
402
0260d1d5
ZJS
403 /* Check that the fd is valid */
404 assert_return(fcntl(fd, F_GETFD) >= 0, -errno);
916abb21 405
0260d1d5
ZJS
406 if (mq_getattr(fd, &attr) < 0) {
407 if (errno == EBADF)
408 /* A non-mq fd (or an invalid one, but we ruled that out above) */
409 return 0;
916abb21 410 return -errno;
0260d1d5 411 }
916abb21
LP
412
413 if (path) {
d6b218e7 414 _cleanup_free_ char *fpath = NULL;
916abb21
LP
415 struct stat a, b;
416
be8f4e9e 417 assert_return(path_is_absolute(path), -EINVAL);
916abb21
LP
418
419 if (fstat(fd, &a) < 0)
420 return -errno;
421
d6b218e7
LP
422 fpath = path_join("/dev/mqueue", path);
423 if (!fpath)
424 return -ENOMEM;
916abb21
LP
425
426 if (stat(fpath, &b) < 0)
427 return -errno;
428
a9dac7a6 429 if (!stat_inode_same(&a, &b))
916abb21
LP
430 return 0;
431 }
432
433 return 1;
916abb21
LP
434}
435
9e1d021e
LP
436_public_ int sd_pid_notify_with_fds(
437 pid_t pid,
438 int unset_environment,
439 const char *state,
440 const int *fds,
441 unsigned n_fds) {
442
f36a9d59 443 union sockaddr_union sockaddr;
cb310866 444 struct iovec iovec;
a354329f
LP
445 struct msghdr msghdr = {
446 .msg_iov = &iovec,
447 .msg_iovlen = 1,
448 .msg_name = &sockaddr,
449 };
254d1313 450 _cleanup_close_ int fd = -EBADF;
a354329f
LP
451 struct cmsghdr *cmsg = NULL;
452 const char *e;
9e1d021e 453 bool send_ucred;
f36a9d59 454 int r;
8c47c732
LP
455
456 if (!state) {
457 r = -EINVAL;
458 goto finish;
459 }
460
a354329f
LP
461 if (n_fds > 0 && !fds) {
462 r = -EINVAL;
463 goto finish;
464 }
465
50425d16
MS
466 e = getenv("NOTIFY_SOCKET");
467 if (!e)
08bfb810 468 return 0;
8c47c732 469
f36a9d59
ZJS
470 r = sockaddr_un_set_path(&sockaddr.un, e);
471 if (r < 0)
638b56cd 472 goto finish;
f36a9d59 473 msghdr.msg_namelen = r;
638b56cd 474
50425d16
MS
475 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
476 if (fd < 0) {
8c47c732
LP
477 r = -errno;
478 goto finish;
479 }
480
9e1d021e 481 (void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
a47806fa 482
cb310866 483 iovec = IOVEC_MAKE_STRING(state);
a013bd94 484
9e1d021e
LP
485 send_ucred =
486 (pid != 0 && pid != getpid_cached()) ||
487 getuid() != geteuid() ||
488 getgid() != getegid();
d4a144fa 489
9e1d021e 490 if (n_fds > 0 || send_ucred) {
96d49011 491 /* CMSG_SPACE(0) may return value different than zero, which results in miscalculated controllen. */
c463a6f1
LP
492 msghdr.msg_controllen =
493 (n_fds > 0 ? CMSG_SPACE(sizeof(int) * n_fds) : 0) +
9e1d021e 494 (send_ucred ? CMSG_SPACE(sizeof(struct ucred)) : 0);
c463a6f1 495
40f44238 496 msghdr.msg_control = alloca0(msghdr.msg_controllen);
a354329f
LP
497
498 cmsg = CMSG_FIRSTHDR(&msghdr);
64144440
ZJS
499 if (n_fds > 0) {
500 cmsg->cmsg_level = SOL_SOCKET;
501 cmsg->cmsg_type = SCM_RIGHTS;
502 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds);
a354329f 503
64144440 504 memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds);
be8f4e9e 505
9e1d021e 506 if (send_ucred)
64144440
ZJS
507 assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg));
508 }
a354329f 509
9e1d021e 510 if (send_ucred) {
64144440 511 struct ucred *ucred;
be8f4e9e 512
64144440
ZJS
513 cmsg->cmsg_level = SOL_SOCKET;
514 cmsg->cmsg_type = SCM_CREDENTIALS;
515 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
be8f4e9e 516
64144440 517 ucred = (struct ucred*) CMSG_DATA(cmsg);
9e1d021e 518 ucred->pid = pid != 0 ? pid : getpid_cached();
64144440
ZJS
519 ucred->uid = getuid();
520 ucred->gid = getgid();
521 }
be8f4e9e
LP
522 }
523
524 /* First try with fake ucred data, as requested */
525 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
526 r = 1;
8c47c732
LP
527 goto finish;
528 }
529
a354329f 530 /* If that failed, try with our own ucred instead */
9e1d021e 531 if (send_ucred) {
64144440
ZJS
532 msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred));
533 if (msghdr.msg_controllen == 0)
a354329f 534 msghdr.msg_control = NULL;
be8f4e9e
LP
535
536 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
537 r = 1;
538 goto finish;
539 }
540 }
541
542 r = -errno;
8c47c732
LP
543
544finish:
545 if (unset_environment)
44ee03d1 546 assert_se(unsetenv("NOTIFY_SOCKET") == 0);
8c47c732 547
8c47c732 548 return r;
8c47c732
LP
549}
550
4f07ddfa 551_public_ int sd_notify_barrier(int unset_environment, uint64_t timeout) {
19ee48a6 552 _cleanup_close_pair_ int pipe_fd[2] = PIPE_EBADF;
4f07ddfa
KKD
553 int r;
554
555 if (pipe2(pipe_fd, O_CLOEXEC) < 0)
556 return -errno;
557
558 r = sd_pid_notify_with_fds(0, unset_environment, "BARRIER=1", &pipe_fd[1], 1);
559 if (r <= 0)
560 return r;
561
562 pipe_fd[1] = safe_close(pipe_fd[1]);
563
0f2d351f 564 r = fd_wait_for_event(pipe_fd[0], 0 /* POLLHUP is implicit */, timeout);
4f07ddfa 565 if (r < 0)
0f2d351f 566 return r;
4f07ddfa
KKD
567 if (r == 0)
568 return -ETIMEDOUT;
569
570 return 1;
571}
572
a354329f
LP
573_public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) {
574 return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0);
575}
576
be8f4e9e 577_public_ int sd_notify(int unset_environment, const char *state) {
a354329f 578 return sd_pid_notify_with_fds(0, unset_environment, state, NULL, 0);
be8f4e9e
LP
579}
580
581_public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) {
582 _cleanup_free_ char *p = NULL;
583 int r;
584
585 if (format) {
586 va_list ap;
587
588 va_start(ap, format);
589 r = vasprintf(&p, format, ap);
590 va_end(ap);
591
592 if (r < 0 || !p)
593 return -ENOMEM;
594 }
595
596 return sd_pid_notify(pid, unset_environment, p);
597}
598
0ebee881 599_public_ int sd_notifyf(int unset_environment, const char *format, ...) {
be8f4e9e 600 _cleanup_free_ char *p = NULL;
8c47c732
LP
601 int r;
602
be8f4e9e
LP
603 if (format) {
604 va_list ap;
8c47c732 605
be8f4e9e
LP
606 va_start(ap, format);
607 r = vasprintf(&p, format, ap);
608 va_end(ap);
8c47c732 609
be8f4e9e
LP
610 if (r < 0 || !p)
611 return -ENOMEM;
612 }
8c47c732 613
be8f4e9e 614 return sd_pid_notify(0, unset_environment, p);
8c47c732 615}
40473a70 616
0ebee881 617_public_ int sd_booted(void) {
66e41181
LP
618 /* We test whether the runtime unit file directory has been
619 * created. This takes place in mount-setup.c, so is
620 * guaranteed to happen very early during boot. */
40473a70 621
21042737
YW
622 if (laccess("/run/systemd/system/", F_OK) >= 0)
623 return true;
624
625 if (errno == ENOENT)
626 return false;
627
628 return -errno;
40473a70 629}
09812eb7 630
0ebee881 631_public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
a9becdd6 632 const char *s, *p = ""; /* p is set to dummy value to do unsetting */
be8f4e9e 633 uint64_t u;
a9becdd6 634 int r = 0;
09812eb7 635
a9becdd6
ZJS
636 s = getenv("WATCHDOG_USEC");
637 if (!s)
09812eb7 638 goto finish;
09812eb7 639
a9becdd6 640 r = safe_atou64(s, &u);
be8f4e9e 641 if (r < 0)
09812eb7 642 goto finish;
0da36375 643 if (!timestamp_is_set(u)) {
09812eb7
LP
644 r = -EINVAL;
645 goto finish;
646 }
647
a9becdd6
ZJS
648 p = getenv("WATCHDOG_PID");
649 if (p) {
650 pid_t pid;
651
652 r = parse_pid(p, &pid);
653 if (r < 0)
654 goto finish;
655
656 /* Is this for us? */
df0ff127 657 if (getpid_cached() != pid) {
a9becdd6
ZJS
658 r = 0;
659 goto finish;
660 }
09812eb7
LP
661 }
662
663 if (usec)
be8f4e9e 664 *usec = u;
09812eb7
LP
665
666 r = 1;
667
668finish:
a9becdd6 669 if (unset_environment && s)
44ee03d1 670 assert_se(unsetenv("WATCHDOG_USEC") == 0);
a9becdd6 671 if (unset_environment && p)
44ee03d1 672 assert_se(unsetenv("WATCHDOG_PID") == 0);
09812eb7
LP
673
674 return r;
09812eb7 675}