i3
sd-daemon.c
Go to the documentation of this file.
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2 
3 /***
4  Copyright 2010 Lennart Poettering
5 
6  Permission is hereby granted, free of charge, to any person
7  obtaining a copy of this software and associated documentation files
8  (the "Software"), to deal in the Software without restriction,
9  including without limitation the rights to use, copy, modify, merge,
10  publish, distribute, sublicense, and/or sell copies of the Software,
11  and to permit persons to whom the Software is furnished to do so,
12  subject to the following conditions:
13 
14  The above copyright notice and this permission notice shall be
15  included in all copies or substantial portions of the Software.
16 
17  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  SOFTWARE.
25 ***/
26 
27 #ifndef _GNU_SOURCE
28 #define _GNU_SOURCE
29 #endif
30 
31 #include "sd-daemon.h"
32 
33 #include <errno.h>
34 #include <netinet/in.h>
35 #include <stdarg.h>
36 #include <stddef.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/fcntl.h>
41 #include <sys/socket.h>
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #include <sys/un.h>
45 #include <unistd.h>
46 
47 int sd_listen_fds(int unset_environment) {
48  int r, fd;
49  const char *e;
50  char *p = NULL;
51  unsigned long l;
52 
53  if (!(e = getenv("LISTEN_PID"))) {
54  r = 0;
55  goto finish;
56  }
57 
58  errno = 0;
59  l = strtoul(e, &p, 10);
60 
61  if (errno != 0) {
62  r = -errno;
63  goto finish;
64  }
65 
66  if (!p || *p || l <= 0) {
67  r = -EINVAL;
68  goto finish;
69  }
70 
71  /* Is this for us? */
72  if (getpid() != (pid_t)l) {
73  r = 0;
74  goto finish;
75  }
76 
77  if (!(e = getenv("LISTEN_FDS"))) {
78  r = 0;
79  goto finish;
80  }
81 
82  errno = 0;
83  l = strtoul(e, &p, 10);
84 
85  if (errno != 0) {
86  r = -errno;
87  goto finish;
88  }
89 
90  if (!p || *p) {
91  r = -EINVAL;
92  goto finish;
93  }
94 
95  for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int)l; fd++) {
96  int flags;
97 
98  if ((flags = fcntl(fd, F_GETFD)) < 0) {
99  r = -errno;
100  goto finish;
101  }
102 
103  if (flags & FD_CLOEXEC)
104  continue;
105 
106  if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
107  r = -errno;
108  goto finish;
109  }
110  }
111 
112  r = (int)l;
113 
114 finish:
115  if (unset_environment) {
116  unsetenv("LISTEN_PID");
117  unsetenv("LISTEN_FDS");
118  }
119 
120  return r;
121 }
122 
123 int sd_is_fifo(int fd, const char *path) {
124  struct stat st_fd;
125 
126  if (fd < 0)
127  return -EINVAL;
128 
129  memset(&st_fd, 0, sizeof(st_fd));
130  if (fstat(fd, &st_fd) < 0)
131  return -errno;
132 
133  if (!S_ISFIFO(st_fd.st_mode))
134  return 0;
135 
136  if (path) {
137  struct stat st_path;
138 
139  memset(&st_path, 0, sizeof(st_path));
140  if (stat(path, &st_path) < 0) {
141  if (errno == ENOENT || errno == ENOTDIR)
142  return 0;
143 
144  return -errno;
145  }
146 
147  return st_path.st_dev == st_fd.st_dev &&
148  st_path.st_ino == st_fd.st_ino;
149  }
150 
151  return 1;
152 }
153 
154 static int sd_is_socket_internal(int fd, int type, int listening) {
155  struct stat st_fd;
156 
157  if (fd < 0 || type < 0)
158  return -EINVAL;
159 
160  if (fstat(fd, &st_fd) < 0)
161  return -errno;
162 
163  if (!S_ISSOCK(st_fd.st_mode))
164  return 0;
165 
166  if (type != 0) {
167  int other_type = 0;
168  socklen_t l = sizeof(other_type);
169 
170  if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
171  return -errno;
172 
173  if (l != sizeof(other_type))
174  return -EINVAL;
175 
176  if (other_type != type)
177  return 0;
178  }
179 
180  if (listening >= 0) {
181  int accepting = 0;
182  socklen_t l = sizeof(accepting);
183 
184  if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
185  return -errno;
186 
187  if (l != sizeof(accepting))
188  return -EINVAL;
189 
190  if (!accepting != !listening)
191  return 0;
192  }
193 
194  return 1;
195 }
196 
198  struct sockaddr sa;
199  struct sockaddr_in in4;
200  struct sockaddr_in6 in6;
201  struct sockaddr_un un;
202  struct sockaddr_storage storage;
203 };
204 
205 int sd_is_socket(int fd, int family, int type, int listening) {
206  int r;
207 
208  if (family < 0)
209  return -EINVAL;
210 
211  if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
212  return r;
213 
214  if (family > 0) {
215  union sockaddr_union sockaddr;
216  socklen_t l;
217 
218  memset(&sockaddr, 0, sizeof(sockaddr));
219  l = sizeof(sockaddr);
220 
221  if (getsockname(fd, &sockaddr.sa, &l) < 0)
222  return -errno;
223 
224  if (l < sizeof(sa_family_t))
225  return -EINVAL;
226 
227  return sockaddr.sa.sa_family == family;
228  }
229 
230  return 1;
231 }
232 
233 int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
234  union sockaddr_union sockaddr;
235  socklen_t l;
236  int r;
237 
238  if (family != 0 && family != AF_INET && family != AF_INET6)
239  return -EINVAL;
240 
241  if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
242  return r;
243 
244  memset(&sockaddr, 0, sizeof(sockaddr));
245  l = sizeof(sockaddr);
246 
247  if (getsockname(fd, &sockaddr.sa, &l) < 0)
248  return -errno;
249 
250  if (l < sizeof(sa_family_t))
251  return -EINVAL;
252 
253  if (sockaddr.sa.sa_family != AF_INET &&
254  sockaddr.sa.sa_family != AF_INET6)
255  return 0;
256 
257  if (family > 0)
258  if (sockaddr.sa.sa_family != family)
259  return 0;
260 
261  if (port > 0) {
262  if (sockaddr.sa.sa_family == AF_INET) {
263  if (l < sizeof(struct sockaddr_in))
264  return -EINVAL;
265 
266  return htons(port) == sockaddr.in4.sin_port;
267  } else {
268  if (l < sizeof(struct sockaddr_in6))
269  return -EINVAL;
270 
271  return htons(port) == sockaddr.in6.sin6_port;
272  }
273  }
274 
275  return 1;
276 }
277 
278 int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
279  union sockaddr_union sockaddr;
280  socklen_t l;
281  int r;
282 
283  if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
284  return r;
285 
286  memset(&sockaddr, 0, sizeof(sockaddr));
287  l = sizeof(sockaddr);
288 
289  if (getsockname(fd, &sockaddr.sa, &l) < 0)
290  return -errno;
291 
292  if (l < sizeof(sa_family_t))
293  return -EINVAL;
294 
295  if (sockaddr.sa.sa_family != AF_UNIX)
296  return 0;
297 
298  if (path) {
299  if (length <= 0)
300  length = strlen(path);
301 
302  if (length <= 0)
303  /* Unnamed socket */
304  return l == offsetof(struct sockaddr_un, sun_path);
305 
306  if (path[0])
307  /* Normal path socket */
308  return (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
309  memcmp(path, sockaddr.un.sun_path, length + 1) == 0;
310  else
311  /* Abstract namespace socket */
312  return (l == offsetof(struct sockaddr_un, sun_path) + length) &&
313  memcmp(path, sockaddr.un.sun_path, length) == 0;
314  }
315 
316  return 1;
317 }
318 
319 int sd_notify(int unset_environment, const char *state) {
320 #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
321  return 0;
322 #else
323  int fd = -1, r;
324  struct msghdr msghdr;
325  struct iovec iovec;
326  union sockaddr_union sockaddr;
327  const char *e;
328 
329  if (!state) {
330  r = -EINVAL;
331  goto finish;
332  }
333 
334  if (!(e = getenv("NOTIFY_SOCKET")))
335  return 0;
336 
337  /* Must be an abstract socket, or an absolute path */
338  if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
339  r = -EINVAL;
340  goto finish;
341  }
342 
343  if (strlen(e) > sizeof(sockaddr.un.sun_path)) {
344  r = -EINVAL;
345  goto finish;
346  }
347 
348  if ((fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) {
349  r = -errno;
350  goto finish;
351  }
352 
353  memset(&sockaddr, 0, sizeof(sockaddr));
354  sockaddr.sa.sa_family = AF_UNIX;
355  strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path) - 1);
356 
357  if (sockaddr.un.sun_path[0] == '@')
358  sockaddr.un.sun_path[0] = 0;
359 
360  memset(&iovec, 0, sizeof(iovec));
361  iovec.iov_base = (char *)state;
362  iovec.iov_len = strlen(state);
363 
364  memset(&msghdr, 0, sizeof(msghdr));
365  msghdr.msg_name = &sockaddr;
366  msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
367 
368  if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
369  msghdr.msg_namelen = sizeof(struct sockaddr_un);
370 
371  msghdr.msg_iov = &iovec;
372  msghdr.msg_iovlen = 1;
373 
374  if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
375  r = -errno;
376  goto finish;
377  }
378 
379  r = 1;
380 
381 finish:
382  if (unset_environment)
383  unsetenv("NOTIFY_SOCKET");
384 
385  if (fd >= 0)
386  close(fd);
387 
388  return r;
389 #endif
390 }
391 
392 int sd_notifyf(int unset_environment, const char *format, ...) {
393 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
394  return 0;
395 #else
396  va_list ap;
397  char *p = NULL;
398  int r;
399 
400  va_start(ap, format);
401  r = vasprintf(&p, format, ap);
402  va_end(ap);
403 
404  if (r < 0 || !p)
405  return -ENOMEM;
406 
407  r = sd_notify(unset_environment, p);
408  free(p);
409 
410  return r;
411 #endif
412 }
413 
414 int sd_booted(void) {
415 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
416  return 0;
417 #else
418 
419  struct stat a, b;
420 
421  /* We simply test whether the systemd cgroup hierarchy is mounted */
422 
423  if (lstat("/sys/fs/cgroup", &a) < 0)
424  return 0;
425 
426  if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
427  return 0;
428 
429  return a.st_dev != b.st_dev;
430 #endif
431 }
sockaddr_union::in6
struct sockaddr_in6 in6
Definition: sd-daemon.c:200
SD_LISTEN_FDS_START
#define SD_LISTEN_FDS_START
Definition: sd-daemon.h:102
sd_is_socket
int sd_is_socket(int fd, int family, int type, int listening)
Definition: sd-daemon.c:205
sd_booted
int sd_booted(void)
Definition: sd-daemon.c:414
sockaddr_union
Definition: sd-daemon.c:197
sd_is_socket_unix
int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length)
Definition: sd-daemon.c:278
sd_listen_fds
int sd_listen_fds(int unset_environment)
Definition: sd-daemon.c:47
sockaddr_union::storage
struct sockaddr_storage storage
Definition: sd-daemon.c:202
sockaddr_union::sa
struct sockaddr sa
Definition: sd-daemon.c:198
state
static cmdp_state state
Definition: commands_parser.c:144
sd_notifyf
int sd_notifyf(int unset_environment, const char *format,...)
Definition: sd-daemon.c:392
sd_notify
int sd_notify(int unset_environment, const char *state)
Definition: sd-daemon.c:319
sd_is_socket_internal
static int sd_is_socket_internal(int fd, int type, int listening)
Definition: sd-daemon.c:154
sd_is_fifo
int sd_is_fifo(int fd, const char *path)
Definition: sd-daemon.c:123
sd_is_socket_inet
int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port)
Definition: sd-daemon.c:233
sockaddr_union::in4
struct sockaddr_in in4
Definition: sd-daemon.c:199
sockaddr_union::un
struct sockaddr_un un
Definition: sd-daemon.c:201
sd-daemon.h