pcsc-lite  1.8.8
sd-daemon.c
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 <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/socket.h>
34 #include <sys/un.h>
35 #include <sys/fcntl.h>
36 #include <netinet/in.h>
37 #include <stdlib.h>
38 #include <errno.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <stdarg.h>
42 #include <stdio.h>
43 #include <stddef.h>
44 #include <limits.h>
45 
46 #if defined(__linux__)
47 #include <mqueue.h>
48 #endif
49 
50 #include "sd-daemon.h"
51 
52 #if (__GNUC__ >= 4)
53 #ifdef SD_EXPORT_SYMBOLS
54 /* Export symbols */
55 #define _sd_export_ __attribute__ ((visibility("default")))
56 #else
57 /* Don't export the symbols */
58 #define _sd_export_ __attribute__ ((visibility("hidden")))
59 #endif
60 #else
61 #define _sd_export_
62 #endif
63 
64 _sd_export_ int sd_listen_fds(int unset_environment) {
65 
66 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
67  return 0;
68 #else
69  int r, fd;
70  const char *e;
71  char *p = NULL;
72  unsigned long l;
73 
74  if (!(e = getenv("LISTEN_PID"))) {
75  r = 0;
76  goto finish;
77  }
78 
79  errno = 0;
80  l = strtoul(e, &p, 10);
81 
82  if (errno != 0) {
83  r = -errno;
84  goto finish;
85  }
86 
87  if (!p || *p || l <= 0) {
88  r = -EINVAL;
89  goto finish;
90  }
91 
92  /* Is this for us? */
93  if (getpid() != (pid_t) l) {
94  r = 0;
95  goto finish;
96  }
97 
98  if (!(e = secure_getenv("LISTEN_FDS"))) {
99  r = 0;
100  goto finish;
101  }
102 
103  errno = 0;
104  l = strtoul(e, &p, 10);
105 
106  if (errno != 0) {
107  r = -errno;
108  goto finish;
109  }
110 
111  if (!p || *p) {
112  r = -EINVAL;
113  goto finish;
114  }
115 
116  for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
117  int flags;
118 
119  if ((flags = fcntl(fd, F_GETFD)) < 0) {
120  r = -errno;
121  goto finish;
122  }
123 
124  if (flags & FD_CLOEXEC)
125  continue;
126 
127  if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
128  r = -errno;
129  goto finish;
130  }
131  }
132 
133  r = (int) l;
134 
135 finish:
136  if (unset_environment) {
137  unsetenv("LISTEN_PID");
138  unsetenv("LISTEN_FDS");
139  }
140 
141  return r;
142 #endif
143 }
144 
145 _sd_export_ int sd_is_fifo(int fd, const char *path) {
146  struct stat st_fd;
147 
148  if (fd < 0)
149  return -EINVAL;
150 
151  memset(&st_fd, 0, sizeof(st_fd));
152  if (fstat(fd, &st_fd) < 0)
153  return -errno;
154 
155  if (!S_ISFIFO(st_fd.st_mode))
156  return 0;
157 
158  if (path) {
159  struct stat st_path;
160 
161  memset(&st_path, 0, sizeof(st_path));
162  if (stat(path, &st_path) < 0) {
163 
164  if (errno == ENOENT || errno == ENOTDIR)
165  return 0;
166 
167  return -errno;
168  }
169 
170  return
171  st_path.st_dev == st_fd.st_dev &&
172  st_path.st_ino == st_fd.st_ino;
173  }
174 
175  return 1;
176 }
177 
178 _sd_export_ int sd_is_special(int fd, const char *path) {
179  struct stat st_fd;
180 
181  if (fd < 0)
182  return -EINVAL;
183 
184  if (fstat(fd, &st_fd) < 0)
185  return -errno;
186 
187  if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode))
188  return 0;
189 
190  if (path) {
191  struct stat st_path;
192 
193  if (stat(path, &st_path) < 0) {
194 
195  if (errno == ENOENT || errno == ENOTDIR)
196  return 0;
197 
198  return -errno;
199  }
200 
201  if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode))
202  return
203  st_path.st_dev == st_fd.st_dev &&
204  st_path.st_ino == st_fd.st_ino;
205  else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode))
206  return st_path.st_rdev == st_fd.st_rdev;
207  else
208  return 0;
209  }
210 
211  return 1;
212 }
213 
214 static int sd_is_socket_internal(int fd, int type, int listening) {
215  struct stat st_fd;
216 
217  if (fd < 0 || type < 0)
218  return -EINVAL;
219 
220  if (fstat(fd, &st_fd) < 0)
221  return -errno;
222 
223  if (!S_ISSOCK(st_fd.st_mode))
224  return 0;
225 
226  if (type != 0) {
227  int other_type = 0;
228  socklen_t l = sizeof(other_type);
229 
230  if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
231  return -errno;
232 
233  if (l != sizeof(other_type))
234  return -EINVAL;
235 
236  if (other_type != type)
237  return 0;
238  }
239 
240  if (listening >= 0) {
241  int accepting = 0;
242  socklen_t l = sizeof(accepting);
243 
244  if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
245  return -errno;
246 
247  if (l != sizeof(accepting))
248  return -EINVAL;
249 
250  if (!accepting != !listening)
251  return 0;
252  }
253 
254  return 1;
255 }
256 
258  struct sockaddr sa;
259  struct sockaddr_in in4;
260  struct sockaddr_in6 in6;
261  struct sockaddr_un un;
262  struct sockaddr_storage storage;
263 };
264 
265 _sd_export_ int sd_is_socket(int fd, int family, int type, int listening) {
266  int r;
267 
268  if (family < 0)
269  return -EINVAL;
270 
271  if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
272  return r;
273 
274  if (family > 0) {
275  union sockaddr_union sockaddr;
276  socklen_t l;
277 
278  memset(&sockaddr, 0, sizeof(sockaddr));
279  l = sizeof(sockaddr);
280 
281  if (getsockname(fd, &sockaddr.sa, &l) < 0)
282  return -errno;
283 
284  if (l < sizeof(sa_family_t))
285  return -EINVAL;
286 
287  return sockaddr.sa.sa_family == family;
288  }
289 
290  return 1;
291 }
292 
293 _sd_export_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
294  union sockaddr_union sockaddr;
295  socklen_t l;
296  int r;
297 
298  if (family != 0 && family != AF_INET && family != AF_INET6)
299  return -EINVAL;
300 
301  if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
302  return r;
303 
304  memset(&sockaddr, 0, sizeof(sockaddr));
305  l = sizeof(sockaddr);
306 
307  if (getsockname(fd, &sockaddr.sa, &l) < 0)
308  return -errno;
309 
310  if (l < sizeof(sa_family_t))
311  return -EINVAL;
312 
313  if (sockaddr.sa.sa_family != AF_INET &&
314  sockaddr.sa.sa_family != AF_INET6)
315  return 0;
316 
317  if (family > 0)
318  if (sockaddr.sa.sa_family != family)
319  return 0;
320 
321  if (port > 0) {
322  if (sockaddr.sa.sa_family == AF_INET) {
323  if (l < sizeof(struct sockaddr_in))
324  return -EINVAL;
325 
326  return htons(port) == sockaddr.in4.sin_port;
327  } else {
328  if (l < sizeof(struct sockaddr_in6))
329  return -EINVAL;
330 
331  return htons(port) == sockaddr.in6.sin6_port;
332  }
333  }
334 
335  return 1;
336 }
337 
338 _sd_export_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
339  union sockaddr_union sockaddr;
340  socklen_t l;
341  int r;
342 
343  if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
344  return r;
345 
346  memset(&sockaddr, 0, sizeof(sockaddr));
347  l = sizeof(sockaddr);
348 
349  if (getsockname(fd, &sockaddr.sa, &l) < 0)
350  return -errno;
351 
352  if (l < sizeof(sa_family_t))
353  return -EINVAL;
354 
355  if (sockaddr.sa.sa_family != AF_UNIX)
356  return 0;
357 
358  if (path) {
359  if (length <= 0)
360  length = strlen(path);
361 
362  if (length <= 0)
363  /* Unnamed socket */
364  return l == offsetof(struct sockaddr_un, sun_path);
365 
366  if (path[0])
367  /* Normal path socket */
368  return
369  (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
370  memcmp(path, sockaddr.un.sun_path, length+1) == 0;
371  else
372  /* Abstract namespace socket */
373  return
374  (l == offsetof(struct sockaddr_un, sun_path) + length) &&
375  memcmp(path, sockaddr.un.sun_path, length) == 0;
376  }
377 
378  return 1;
379 }
380 
381 _sd_export_ int sd_is_mq(int fd, const char *path) {
382 #if !defined(__linux__)
383  return 0;
384 #else
385  struct mq_attr attr;
386 
387  if (fd < 0)
388  return -EINVAL;
389 
390  if (mq_getattr(fd, &attr) < 0)
391  return -errno;
392 
393  if (path) {
394  char fpath[PATH_MAX];
395  struct stat a, b;
396 
397  if (path[0] != '/')
398  return -EINVAL;
399 
400  if (fstat(fd, &a) < 0)
401  return -errno;
402 
403  strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
404  fpath[sizeof(fpath)-1] = 0;
405 
406  if (stat(fpath, &b) < 0)
407  return -errno;
408 
409  if (a.st_dev != b.st_dev ||
410  a.st_ino != b.st_ino)
411  return 0;
412  }
413 
414  return 1;
415 #endif
416 }
417 
418 _sd_export_ int sd_notify(int unset_environment, const char *state) {
419 #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
420  return 0;
421 #else
422  int fd = -1, r;
423  struct msghdr msghdr;
424  struct iovec iovec;
425  union sockaddr_union sockaddr;
426  const char *e;
427 
428  if (!state) {
429  r = -EINVAL;
430  goto finish;
431  }
432 
433  if (!(e = getenv("NOTIFY_SOCKET")))
434  return 0;
435 
436  /* Must be an abstract socket, or an absolute path */
437  if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
438  r = -EINVAL;
439  goto finish;
440  }
441 
442  if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
443  r = -errno;
444  goto finish;
445  }
446 
447  memset(&sockaddr, 0, sizeof(sockaddr));
448  sockaddr.sa.sa_family = AF_UNIX;
449  strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
450 
451  if (sockaddr.un.sun_path[0] == '@')
452  sockaddr.un.sun_path[0] = 0;
453 
454  memset(&iovec, 0, sizeof(iovec));
455  iovec.iov_base = (char*) state;
456  iovec.iov_len = strlen(state);
457 
458  memset(&msghdr, 0, sizeof(msghdr));
459  msghdr.msg_name = &sockaddr;
460  msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
461 
462  if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
463  msghdr.msg_namelen = sizeof(struct sockaddr_un);
464 
465  msghdr.msg_iov = &iovec;
466  msghdr.msg_iovlen = 1;
467 
468  if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
469  r = -errno;
470  goto finish;
471  }
472 
473  r = 1;
474 
475 finish:
476  if (unset_environment)
477  unsetenv("NOTIFY_SOCKET");
478 
479  if (fd >= 0)
480  close(fd);
481 
482  return r;
483 #endif
484 }
485 
486 _sd_export_ int sd_notifyf(int unset_environment, const char *format, ...) {
487 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
488  return 0;
489 #else
490  va_list ap;
491  char *p = NULL;
492  int r;
493 
494  va_start(ap, format);
495  r = vasprintf(&p, format, ap);
496  va_end(ap);
497 
498  if (r < 0 || !p)
499  return -ENOMEM;
500 
501  r = sd_notify(unset_environment, p);
502  free(p);
503 
504  return r;
505 #endif
506 }
507 
508 _sd_export_ int sd_booted(void) {
509 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
510  return 0;
511 #else
512 
513  struct stat a, b;
514 
515  /* We simply test whether the systemd cgroup hierarchy is
516  * mounted */
517 
518  if (lstat("/sys/fs/cgroup", &a) < 0)
519  return 0;
520 
521  if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
522  return 0;
523 
524  return a.st_dev != b.st_dev;
525 #endif
526 }