D-Bus 1.4.0

sd-daemon.c

00001 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
00002 
00003 /***
00004   Copyright 2010 Lennart Poettering
00005 
00006   Permission is hereby granted, free of charge, to any person
00007   obtaining a copy of this software and associated documentation files
00008   (the "Software"), to deal in the Software without restriction,
00009   including without limitation the rights to use, copy, modify, merge,
00010   publish, distribute, sublicense, and/or sell copies of the Software,
00011   and to permit persons to whom the Software is furnished to do so,
00012   subject to the following conditions:
00013 
00014   The above copyright notice and this permission notice shall be
00015   included in all copies or substantial portions of the Software.
00016 
00017   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00018   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00019   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00020   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
00021   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
00022   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00023   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00024   SOFTWARE.
00025 ***/
00026 
00027 #ifndef _GNU_SOURCE
00028 #define _GNU_SOURCE
00029 #endif
00030 
00031 #include <sys/types.h>
00032 #include <sys/stat.h>
00033 #include <sys/socket.h>
00034 #include <sys/un.h>
00035 #include <sys/fcntl.h>
00036 #include <netinet/in.h>
00037 #include <stdlib.h>
00038 #include <errno.h>
00039 #include <unistd.h>
00040 #include <string.h>
00041 #include <stdarg.h>
00042 #include <stdio.h>
00043 
00044 #include "sd-daemon.h"
00045 
00046 int sd_listen_fds(int unset_environment) {
00047 
00048 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
00049         return 0;
00050 #else
00051         int r, fd;
00052         const char *e;
00053         char *p = NULL;
00054         unsigned long l;
00055 
00056         if (!(e = getenv("LISTEN_PID"))) {
00057                 r = 0;
00058                 goto finish;
00059         }
00060 
00061         errno = 0;
00062         l = strtoul(e, &p, 10);
00063 
00064         if (errno != 0) {
00065                 r = -errno;
00066                 goto finish;
00067         }
00068 
00069         if (!p || *p || l <= 0) {
00070                 r = -EINVAL;
00071                 goto finish;
00072         }
00073 
00074         /* Is this for us? */
00075         if (getpid() != (pid_t) l) {
00076                 r = 0;
00077                 goto finish;
00078         }
00079 
00080         if (!(e = getenv("LISTEN_FDS"))) {
00081                 r = 0;
00082                 goto finish;
00083         }
00084 
00085         errno = 0;
00086         l = strtoul(e, &p, 10);
00087 
00088         if (errno != 0) {
00089                 r = -errno;
00090                 goto finish;
00091         }
00092 
00093         if (!p || *p) {
00094                 r = -EINVAL;
00095                 goto finish;
00096         }
00097 
00098         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
00099                 int flags;
00100 
00101                 if ((flags = fcntl(fd, F_GETFD)) < 0) {
00102                         r = -errno;
00103                         goto finish;
00104                 }
00105 
00106                 if (flags & FD_CLOEXEC)
00107                         continue;
00108 
00109                 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
00110                         r = -errno;
00111                         goto finish;
00112                 }
00113         }
00114 
00115         r = (int) l;
00116 
00117 finish:
00118         if (unset_environment) {
00119                 unsetenv("LISTEN_PID");
00120                 unsetenv("LISTEN_FDS");
00121         }
00122 
00123         return r;
00124 #endif
00125 }
00126 
00127 int sd_is_fifo(int fd, const char *path) {
00128         struct stat st_fd;
00129 
00130         if (fd < 0)
00131                 return -EINVAL;
00132 
00133         memset(&st_fd, 0, sizeof(st_fd));
00134         if (fstat(fd, &st_fd) < 0)
00135                 return -errno;
00136 
00137         if (!S_ISFIFO(st_fd.st_mode))
00138                 return 0;
00139 
00140         if (path) {
00141                 struct stat st_path;
00142 
00143                 memset(&st_path, 0, sizeof(st_path));
00144                 if (stat(path, &st_path) < 0) {
00145 
00146                         if (errno == ENOENT || errno == ENOTDIR)
00147                                 return 0;
00148 
00149                         return -errno;
00150                 }
00151 
00152                 return
00153                         st_path.st_dev == st_fd.st_dev &&
00154                         st_path.st_ino == st_fd.st_ino;
00155         }
00156 
00157         return 1;
00158 }
00159 
00160 static int sd_is_socket_internal(int fd, int type, int listening) {
00161         struct stat st_fd;
00162 
00163         if (fd < 0 || type < 0)
00164                 return -EINVAL;
00165 
00166         if (fstat(fd, &st_fd) < 0)
00167                 return -errno;
00168 
00169         if (!S_ISSOCK(st_fd.st_mode))
00170                 return 0;
00171 
00172         if (type != 0) {
00173                 int other_type = 0;
00174                 socklen_t l = sizeof(other_type);
00175 
00176                 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
00177                         return -errno;
00178 
00179                 if (l != sizeof(other_type))
00180                         return -EINVAL;
00181 
00182                 if (other_type != type)
00183                         return 0;
00184         }
00185 
00186         if (listening >= 0) {
00187                 int accepting = 0;
00188                 socklen_t l = sizeof(accepting);
00189 
00190                 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
00191                         return -errno;
00192 
00193                 if (l != sizeof(accepting))
00194                         return -EINVAL;
00195 
00196                 if (!accepting != !listening)
00197                         return 0;
00198         }
00199 
00200         return 1;
00201 }
00202 
00203 union sockaddr_union {
00204         struct sockaddr sa;
00205         struct sockaddr_in in4;
00206         struct sockaddr_in6 in6;
00207         struct sockaddr_un un;
00208         struct sockaddr_storage storage;
00209 };
00210 
00211 int sd_is_socket(int fd, int family, int type, int listening) {
00212         int r;
00213 
00214         if (family < 0)
00215                 return -EINVAL;
00216 
00217         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
00218                 return r;
00219 
00220         if (family > 0) {
00221                 union sockaddr_union sockaddr;
00222                 socklen_t l;
00223 
00224                 memset(&sockaddr, 0, sizeof(sockaddr));
00225                 l = sizeof(sockaddr);
00226 
00227                 if (getsockname(fd, &sockaddr.sa, &l) < 0)
00228                         return -errno;
00229 
00230                 if (l < sizeof(sa_family_t))
00231                         return -EINVAL;
00232 
00233                 return sockaddr.sa.sa_family == family;
00234         }
00235 
00236         return 1;
00237 }
00238 
00239 int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
00240         union sockaddr_union sockaddr;
00241         socklen_t l;
00242         int r;
00243 
00244         if (family != 0 && family != AF_INET && family != AF_INET6)
00245                 return -EINVAL;
00246 
00247         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
00248                 return r;
00249 
00250         memset(&sockaddr, 0, sizeof(sockaddr));
00251         l = sizeof(sockaddr);
00252 
00253         if (getsockname(fd, &sockaddr.sa, &l) < 0)
00254                 return -errno;
00255 
00256         if (l < sizeof(sa_family_t))
00257                 return -EINVAL;
00258 
00259         if (sockaddr.sa.sa_family != AF_INET &&
00260             sockaddr.sa.sa_family != AF_INET6)
00261                 return 0;
00262 
00263         if (family > 0)
00264                 if (sockaddr.sa.sa_family != family)
00265                         return 0;
00266 
00267         if (port > 0) {
00268                 if (sockaddr.sa.sa_family == AF_INET) {
00269                         if (l < sizeof(struct sockaddr_in))
00270                                 return -EINVAL;
00271 
00272                         return htons(port) == sockaddr.in4.sin_port;
00273                 } else {
00274                         if (l < sizeof(struct sockaddr_in6))
00275                                 return -EINVAL;
00276 
00277                         return htons(port) == sockaddr.in6.sin6_port;
00278                 }
00279         }
00280 
00281         return 1;
00282 }
00283 
00284 int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
00285         union sockaddr_union sockaddr;
00286         socklen_t l;
00287         int r;
00288 
00289         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
00290                 return r;
00291 
00292         memset(&sockaddr, 0, sizeof(sockaddr));
00293         l = sizeof(sockaddr);
00294 
00295         if (getsockname(fd, &sockaddr.sa, &l) < 0)
00296                 return -errno;
00297 
00298         if (l < sizeof(sa_family_t))
00299                 return -EINVAL;
00300 
00301         if (sockaddr.sa.sa_family != AF_UNIX)
00302                 return 0;
00303 
00304         if (path) {
00305                 if (length <= 0)
00306                         length = strlen(path);
00307 
00308                 if (length <= 0)
00309                         /* Unnamed socket */
00310                         return l == sizeof(sa_family_t);
00311 
00312                 if (path[0])
00313                         /* Normal path socket */
00314                         return
00315                                 (l >= sizeof(sa_family_t) + length + 1) &&
00316                                 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
00317                 else
00318                         /* Abstract namespace socket */
00319                         return
00320                                 (l == sizeof(sa_family_t) + length) &&
00321                                 memcmp(path, sockaddr.un.sun_path, length) == 0;
00322         }
00323 
00324         return 1;
00325 }
00326 
00327 int sd_notify(int unset_environment, const char *state) {
00328 #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
00329         return 0;
00330 #else
00331         int fd = -1, r;
00332         struct msghdr msghdr;
00333         struct iovec iovec;
00334         union sockaddr_union sockaddr;
00335         const char *e;
00336 
00337         if (!state) {
00338                 r = -EINVAL;
00339                 goto finish;
00340         }
00341 
00342         if (!(e = getenv("NOTIFY_SOCKET")))
00343                 return 0;
00344 
00345         /* Must be an abstract socket, or an absolute path */
00346         if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
00347                 r = -EINVAL;
00348                 goto finish;
00349         }
00350 
00351         if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
00352                 r = -errno;
00353                 goto finish;
00354         }
00355 
00356         memset(&sockaddr, 0, sizeof(sockaddr));
00357         sockaddr.sa.sa_family = AF_UNIX;
00358         strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
00359 
00360         if (sockaddr.un.sun_path[0] == '@')
00361                 sockaddr.un.sun_path[0] = 0;
00362 
00363         memset(&iovec, 0, sizeof(iovec));
00364         iovec.iov_base = (char*) state;
00365         iovec.iov_len = strlen(state);
00366 
00367         memset(&msghdr, 0, sizeof(msghdr));
00368         msghdr.msg_name = &sockaddr;
00369         msghdr.msg_namelen = sizeof(sa_family_t) + strlen(e);
00370 
00371         if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
00372                 msghdr.msg_namelen = sizeof(struct sockaddr_un);
00373 
00374         msghdr.msg_iov = &iovec;
00375         msghdr.msg_iovlen = 1;
00376 
00377         if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
00378                 r = -errno;
00379                 goto finish;
00380         }
00381 
00382         r = 1;
00383 
00384 finish:
00385         if (unset_environment)
00386                 unsetenv("NOTIFY_SOCKET");
00387 
00388         if (fd >= 0)
00389                 close(fd);
00390 
00391         return r;
00392 #endif
00393 }
00394 
00395 int sd_notifyf(int unset_environment, const char *format, ...) {
00396 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
00397         return 0;
00398 #else
00399         va_list ap;
00400         char *p = NULL;
00401         int r;
00402 
00403         va_start(ap, format);
00404         r = vasprintf(&p, format, ap);
00405         va_end(ap);
00406 
00407         if (r < 0 || !p)
00408                 return -ENOMEM;
00409 
00410         r = sd_notify(unset_environment, p);
00411         free(p);
00412 
00413         return r;
00414 #endif
00415 }
00416 
00417 int sd_booted(void) {
00418 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
00419         return 0;
00420 #else
00421 
00422         struct stat a, b;
00423 
00424         /* We simply test whether the systemd cgroup hierarchy is
00425          * mounted */
00426 
00427         if (lstat("/sys/fs/cgroup", &a) < 0)
00428                 return 0;
00429 
00430         if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
00431                 return 0;
00432 
00433         return a.st_dev != b.st_dev;
00434 #endif
00435 }