Home | History | Annotate | Download | only in dbus
      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 <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 
     44 #include "sd-daemon.h"
     45 
     46 int sd_listen_fds(int unset_environment) {
     47 
     48 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
     49         return 0;
     50 #else
     51         int r, fd;
     52         const char *e;
     53         char *p = NULL;
     54         unsigned long l;
     55 
     56         if (!(e = getenv("LISTEN_PID"))) {
     57                 r = 0;
     58                 goto finish;
     59         }
     60 
     61         errno = 0;
     62         l = strtoul(e, &p, 10);
     63 
     64         if (errno != 0) {
     65                 r = -errno;
     66                 goto finish;
     67         }
     68 
     69         if (!p || *p || l <= 0) {
     70                 r = -EINVAL;
     71                 goto finish;
     72         }
     73 
     74         /* Is this for us? */
     75         if (getpid() != (pid_t) l) {
     76                 r = 0;
     77                 goto finish;
     78         }
     79 
     80         if (!(e = getenv("LISTEN_FDS"))) {
     81                 r = 0;
     82                 goto finish;
     83         }
     84 
     85         errno = 0;
     86         l = strtoul(e, &p, 10);
     87 
     88         if (errno != 0) {
     89                 r = -errno;
     90                 goto finish;
     91         }
     92 
     93         if (!p || *p) {
     94                 r = -EINVAL;
     95                 goto finish;
     96         }
     97 
     98         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
     99                 int flags;
    100 
    101                 if ((flags = fcntl(fd, F_GETFD)) < 0) {
    102                         r = -errno;
    103                         goto finish;
    104                 }
    105 
    106                 if (flags & FD_CLOEXEC)
    107                         continue;
    108 
    109                 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
    110                         r = -errno;
    111                         goto finish;
    112                 }
    113         }
    114 
    115         r = (int) l;
    116 
    117 finish:
    118         if (unset_environment) {
    119                 unsetenv("LISTEN_PID");
    120                 unsetenv("LISTEN_FDS");
    121         }
    122 
    123         return r;
    124 #endif
    125 }
    126 
    127 int sd_is_fifo(int fd, const char *path) {
    128         struct stat st_fd;
    129 
    130         if (fd < 0)
    131                 return -EINVAL;
    132 
    133         memset(&st_fd, 0, sizeof(st_fd));
    134         if (fstat(fd, &st_fd) < 0)
    135                 return -errno;
    136 
    137         if (!S_ISFIFO(st_fd.st_mode))
    138                 return 0;
    139 
    140         if (path) {
    141                 struct stat st_path;
    142 
    143                 memset(&st_path, 0, sizeof(st_path));
    144                 if (stat(path, &st_path) < 0) {
    145 
    146                         if (errno == ENOENT || errno == ENOTDIR)
    147                                 return 0;
    148 
    149                         return -errno;
    150                 }
    151 
    152                 return
    153                         st_path.st_dev == st_fd.st_dev &&
    154                         st_path.st_ino == st_fd.st_ino;
    155         }
    156 
    157         return 1;
    158 }
    159 
    160 static int sd_is_socket_internal(int fd, int type, int listening) {
    161         struct stat st_fd;
    162 
    163         if (fd < 0 || type < 0)
    164                 return -EINVAL;
    165 
    166         if (fstat(fd, &st_fd) < 0)
    167                 return -errno;
    168 
    169         if (!S_ISSOCK(st_fd.st_mode))
    170                 return 0;
    171 
    172         if (type != 0) {
    173                 int other_type = 0;
    174                 socklen_t l = sizeof(other_type);
    175 
    176                 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
    177                         return -errno;
    178 
    179                 if (l != sizeof(other_type))
    180                         return -EINVAL;
    181 
    182                 if (other_type != type)
    183                         return 0;
    184         }
    185 
    186         if (listening >= 0) {
    187                 int accepting = 0;
    188                 socklen_t l = sizeof(accepting);
    189 
    190                 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
    191                         return -errno;
    192 
    193                 if (l != sizeof(accepting))
    194                         return -EINVAL;
    195 
    196                 if (!accepting != !listening)
    197                         return 0;
    198         }
    199 
    200         return 1;
    201 }
    202 
    203 union sockaddr_union {
    204         struct sockaddr sa;
    205         struct sockaddr_in in4;
    206         struct sockaddr_in6 in6;
    207         struct sockaddr_un un;
    208         struct sockaddr_storage storage;
    209 };
    210 
    211 int sd_is_socket(int fd, int family, int type, int listening) {
    212         int r;
    213 
    214         if (family < 0)
    215                 return -EINVAL;
    216 
    217         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
    218                 return r;
    219 
    220         if (family > 0) {
    221                 union sockaddr_union sockaddr;
    222                 socklen_t l;
    223 
    224                 memset(&sockaddr, 0, sizeof(sockaddr));
    225                 l = sizeof(sockaddr);
    226 
    227                 if (getsockname(fd, &sockaddr.sa, &l) < 0)
    228                         return -errno;
    229 
    230                 if (l < sizeof(sa_family_t))
    231                         return -EINVAL;
    232 
    233                 return sockaddr.sa.sa_family == family;
    234         }
    235 
    236         return 1;
    237 }
    238 
    239 int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
    240         union sockaddr_union sockaddr;
    241         socklen_t l;
    242         int r;
    243 
    244         if (family != 0 && family != AF_INET && family != AF_INET6)
    245                 return -EINVAL;
    246 
    247         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
    248                 return r;
    249 
    250         memset(&sockaddr, 0, sizeof(sockaddr));
    251         l = sizeof(sockaddr);
    252 
    253         if (getsockname(fd, &sockaddr.sa, &l) < 0)
    254                 return -errno;
    255 
    256         if (l < sizeof(sa_family_t))
    257                 return -EINVAL;
    258 
    259         if (sockaddr.sa.sa_family != AF_INET &&
    260             sockaddr.sa.sa_family != AF_INET6)
    261                 return 0;
    262 
    263         if (family > 0)
    264                 if (sockaddr.sa.sa_family != family)
    265                         return 0;
    266 
    267         if (port > 0) {
    268                 if (sockaddr.sa.sa_family == AF_INET) {
    269                         if (l < sizeof(struct sockaddr_in))
    270                                 return -EINVAL;
    271 
    272                         return htons(port) == sockaddr.in4.sin_port;
    273                 } else {
    274                         if (l < sizeof(struct sockaddr_in6))
    275                                 return -EINVAL;
    276 
    277                         return htons(port) == sockaddr.in6.sin6_port;
    278                 }
    279         }
    280 
    281         return 1;
    282 }
    283 
    284 int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
    285         union sockaddr_union sockaddr;
    286         socklen_t l;
    287         int r;
    288 
    289         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
    290                 return r;
    291 
    292         memset(&sockaddr, 0, sizeof(sockaddr));
    293         l = sizeof(sockaddr);
    294 
    295         if (getsockname(fd, &sockaddr.sa, &l) < 0)
    296                 return -errno;
    297 
    298         if (l < sizeof(sa_family_t))
    299                 return -EINVAL;
    300 
    301         if (sockaddr.sa.sa_family != AF_UNIX)
    302                 return 0;
    303 
    304         if (path) {
    305                 if (length <= 0)
    306                         length = strlen(path);
    307 
    308                 if (length <= 0)
    309                         /* Unnamed socket */
    310                         return l == sizeof(sa_family_t);
    311 
    312                 if (path[0])
    313                         /* Normal path socket */
    314                         return
    315                                 (l >= sizeof(sa_family_t) + length + 1) &&
    316                                 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
    317                 else
    318                         /* Abstract namespace socket */
    319                         return
    320                                 (l == sizeof(sa_family_t) + length) &&
    321                                 memcmp(path, sockaddr.un.sun_path, length) == 0;
    322         }
    323 
    324         return 1;
    325 }
    326 
    327 int sd_notify(int unset_environment, const char *state) {
    328 #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
    329         return 0;
    330 #else
    331         int fd = -1, r;
    332         struct msghdr msghdr;
    333         struct iovec iovec;
    334         union sockaddr_union sockaddr;
    335         const char *e;
    336 
    337         if (!state) {
    338                 r = -EINVAL;
    339                 goto finish;
    340         }
    341 
    342         if (!(e = getenv("NOTIFY_SOCKET")))
    343                 return 0;
    344 
    345         /* Must be an abstract socket, or an absolute path */
    346         if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
    347                 r = -EINVAL;
    348                 goto finish;
    349         }
    350 
    351         if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
    352                 r = -errno;
    353                 goto finish;
    354         }
    355 
    356         memset(&sockaddr, 0, sizeof(sockaddr));
    357         sockaddr.sa.sa_family = AF_UNIX;
    358         strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
    359 
    360         if (sockaddr.un.sun_path[0] == '@')
    361                 sockaddr.un.sun_path[0] = 0;
    362 
    363         memset(&iovec, 0, sizeof(iovec));
    364         iovec.iov_base = (char*) state;
    365         iovec.iov_len = strlen(state);
    366 
    367         memset(&msghdr, 0, sizeof(msghdr));
    368         msghdr.msg_name = &sockaddr;
    369         msghdr.msg_namelen = sizeof(sa_family_t) + strlen(e);
    370 
    371         if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
    372                 msghdr.msg_namelen = sizeof(struct sockaddr_un);
    373 
    374         msghdr.msg_iov = &iovec;
    375         msghdr.msg_iovlen = 1;
    376 
    377         if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
    378                 r = -errno;
    379                 goto finish;
    380         }
    381 
    382         r = 1;
    383 
    384 finish:
    385         if (unset_environment)
    386                 unsetenv("NOTIFY_SOCKET");
    387 
    388         if (fd >= 0)
    389                 close(fd);
    390 
    391         return r;
    392 #endif
    393 }
    394 
    395 int sd_notifyf(int unset_environment, const char *format, ...) {
    396 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
    397         return 0;
    398 #else
    399         va_list ap;
    400         char *p = NULL;
    401         int r;
    402 
    403         va_start(ap, format);
    404         r = vasprintf(&p, format, ap);
    405         va_end(ap);
    406 
    407         if (r < 0 || !p)
    408                 return -ENOMEM;
    409 
    410         r = sd_notify(unset_environment, p);
    411         free(p);
    412 
    413         return r;
    414 #endif
    415 }
    416 
    417 int sd_booted(void) {
    418 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
    419         return 0;
    420 #else
    421 
    422         struct stat a, b;
    423 
    424         /* We simply test whether the systemd cgroup hierarchy is
    425          * mounted */
    426 
    427         if (lstat("/sys/fs/cgroup", &a) < 0)
    428                 return 0;
    429 
    430         if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
    431                 return 0;
    432 
    433         return a.st_dev != b.st_dev;
    434 #endif
    435 }
    436