i3
src/sd-daemon.c
Go to the documentation of this file.
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 #include <stddef.h>
00044 
00045 #include "sd-daemon.h"
00046 
00047 int sd_listen_fds(int unset_environment) {
00048 
00049 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
00050         return 0;
00051 #else
00052         int r, fd;
00053         const char *e;
00054         char *p = NULL;
00055         unsigned long l;
00056 
00057         if (!(e = getenv("LISTEN_PID"))) {
00058                 r = 0;
00059                 goto finish;
00060         }
00061 
00062         errno = 0;
00063         l = strtoul(e, &p, 10);
00064 
00065         if (errno != 0) {
00066                 r = -errno;
00067                 goto finish;
00068         }
00069 
00070         if (!p || *p || l <= 0) {
00071                 r = -EINVAL;
00072                 goto finish;
00073         }
00074 
00075         /* Is this for us? */
00076         if (getpid() != (pid_t) l) {
00077                 r = 0;
00078                 goto finish;
00079         }
00080 
00081         if (!(e = getenv("LISTEN_FDS"))) {
00082                 r = 0;
00083                 goto finish;
00084         }
00085 
00086         errno = 0;
00087         l = strtoul(e, &p, 10);
00088 
00089         if (errno != 0) {
00090                 r = -errno;
00091                 goto finish;
00092         }
00093 
00094         if (!p || *p) {
00095                 r = -EINVAL;
00096                 goto finish;
00097         }
00098 
00099         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
00100                 int flags;
00101 
00102                 if ((flags = fcntl(fd, F_GETFD)) < 0) {
00103                         r = -errno;
00104                         goto finish;
00105                 }
00106 
00107                 if (flags & FD_CLOEXEC)
00108                         continue;
00109 
00110                 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
00111                         r = -errno;
00112                         goto finish;
00113                 }
00114         }
00115 
00116         r = (int) l;
00117 
00118 finish:
00119         if (unset_environment) {
00120                 unsetenv("LISTEN_PID");
00121                 unsetenv("LISTEN_FDS");
00122         }
00123 
00124         return r;
00125 #endif
00126 }
00127 
00128 int sd_is_fifo(int fd, const char *path) {
00129         struct stat st_fd;
00130 
00131         if (fd < 0)
00132                 return -EINVAL;
00133 
00134         memset(&st_fd, 0, sizeof(st_fd));
00135         if (fstat(fd, &st_fd) < 0)
00136                 return -errno;
00137 
00138         if (!S_ISFIFO(st_fd.st_mode))
00139                 return 0;
00140 
00141         if (path) {
00142                 struct stat st_path;
00143 
00144                 memset(&st_path, 0, sizeof(st_path));
00145                 if (stat(path, &st_path) < 0) {
00146 
00147                         if (errno == ENOENT || errno == ENOTDIR)
00148                                 return 0;
00149 
00150                         return -errno;
00151                 }
00152 
00153                 return
00154                         st_path.st_dev == st_fd.st_dev &&
00155                         st_path.st_ino == st_fd.st_ino;
00156         }
00157 
00158         return 1;
00159 }
00160 
00161 static int sd_is_socket_internal(int fd, int type, int listening) {
00162         struct stat st_fd;
00163 
00164         if (fd < 0 || type < 0)
00165                 return -EINVAL;
00166 
00167         if (fstat(fd, &st_fd) < 0)
00168                 return -errno;
00169 
00170         if (!S_ISSOCK(st_fd.st_mode))
00171                 return 0;
00172 
00173         if (type != 0) {
00174                 int other_type = 0;
00175                 socklen_t l = sizeof(other_type);
00176 
00177                 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
00178                         return -errno;
00179 
00180                 if (l != sizeof(other_type))
00181                         return -EINVAL;
00182 
00183                 if (other_type != type)
00184                         return 0;
00185         }
00186 
00187         if (listening >= 0) {
00188                 int accepting = 0;
00189                 socklen_t l = sizeof(accepting);
00190 
00191                 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
00192                         return -errno;
00193 
00194                 if (l != sizeof(accepting))
00195                         return -EINVAL;
00196 
00197                 if (!accepting != !listening)
00198                         return 0;
00199         }
00200 
00201         return 1;
00202 }
00203 
00204 union sockaddr_union {
00205         struct sockaddr sa;
00206         struct sockaddr_in in4;
00207         struct sockaddr_in6 in6;
00208         struct sockaddr_un un;
00209         struct sockaddr_storage storage;
00210 };
00211 
00212 int sd_is_socket(int fd, int family, int type, int listening) {
00213         int r;
00214 
00215         if (family < 0)
00216                 return -EINVAL;
00217 
00218         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
00219                 return r;
00220 
00221         if (family > 0) {
00222                 union sockaddr_union sockaddr;
00223                 socklen_t l;
00224 
00225                 memset(&sockaddr, 0, sizeof(sockaddr));
00226                 l = sizeof(sockaddr);
00227 
00228                 if (getsockname(fd, &sockaddr.sa, &l) < 0)
00229                         return -errno;
00230 
00231                 if (l < sizeof(sa_family_t))
00232                         return -EINVAL;
00233 
00234                 return sockaddr.sa.sa_family == family;
00235         }
00236 
00237         return 1;
00238 }
00239 
00240 int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
00241         union sockaddr_union sockaddr;
00242         socklen_t l;
00243         int r;
00244 
00245         if (family != 0 && family != AF_INET && family != AF_INET6)
00246                 return -EINVAL;
00247 
00248         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
00249                 return r;
00250 
00251         memset(&sockaddr, 0, sizeof(sockaddr));
00252         l = sizeof(sockaddr);
00253 
00254         if (getsockname(fd, &sockaddr.sa, &l) < 0)
00255                 return -errno;
00256 
00257         if (l < sizeof(sa_family_t))
00258                 return -EINVAL;
00259 
00260         if (sockaddr.sa.sa_family != AF_INET &&
00261             sockaddr.sa.sa_family != AF_INET6)
00262                 return 0;
00263 
00264         if (family > 0)
00265                 if (sockaddr.sa.sa_family != family)
00266                         return 0;
00267 
00268         if (port > 0) {
00269                 if (sockaddr.sa.sa_family == AF_INET) {
00270                         if (l < sizeof(struct sockaddr_in))
00271                                 return -EINVAL;
00272 
00273                         return htons(port) == sockaddr.in4.sin_port;
00274                 } else {
00275                         if (l < sizeof(struct sockaddr_in6))
00276                                 return -EINVAL;
00277 
00278                         return htons(port) == sockaddr.in6.sin6_port;
00279                 }
00280         }
00281 
00282         return 1;
00283 }
00284 
00285 int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
00286         union sockaddr_union sockaddr;
00287         socklen_t l;
00288         int r;
00289 
00290         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
00291                 return r;
00292 
00293         memset(&sockaddr, 0, sizeof(sockaddr));
00294         l = sizeof(sockaddr);
00295 
00296         if (getsockname(fd, &sockaddr.sa, &l) < 0)
00297                 return -errno;
00298 
00299         if (l < sizeof(sa_family_t))
00300                 return -EINVAL;
00301 
00302         if (sockaddr.sa.sa_family != AF_UNIX)
00303                 return 0;
00304 
00305         if (path) {
00306                 if (length <= 0)
00307                         length = strlen(path);
00308 
00309                 if (length <= 0)
00310                         /* Unnamed socket */
00311                         return l == offsetof(struct sockaddr_un, sun_path);
00312 
00313                 if (path[0])
00314                         /* Normal path socket */
00315                         return
00316                                 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
00317                                 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
00318                 else
00319                         /* Abstract namespace socket */
00320                         return
00321                                 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
00322                                 memcmp(path, sockaddr.un.sun_path, length) == 0;
00323         }
00324 
00325         return 1;
00326 }
00327 
00328 int sd_notify(int unset_environment, const char *state) {
00329 #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
00330         return 0;
00331 #else
00332         int fd = -1, r;
00333         struct msghdr msghdr;
00334         struct iovec iovec;
00335         union sockaddr_union sockaddr;
00336         const char *e;
00337 
00338         if (!state) {
00339                 r = -EINVAL;
00340                 goto finish;
00341         }
00342 
00343         if (!(e = getenv("NOTIFY_SOCKET")))
00344                 return 0;
00345 
00346         /* Must be an abstract socket, or an absolute path */
00347         if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
00348                 r = -EINVAL;
00349                 goto finish;
00350         }
00351 
00352         if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
00353                 r = -errno;
00354                 goto finish;
00355         }
00356 
00357         memset(&sockaddr, 0, sizeof(sockaddr));
00358         sockaddr.sa.sa_family = AF_UNIX;
00359         strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
00360 
00361         if (sockaddr.un.sun_path[0] == '@')
00362                 sockaddr.un.sun_path[0] = 0;
00363 
00364         memset(&iovec, 0, sizeof(iovec));
00365         iovec.iov_base = (char*) state;
00366         iovec.iov_len = strlen(state);
00367 
00368         memset(&msghdr, 0, sizeof(msghdr));
00369         msghdr.msg_name = &sockaddr;
00370         msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
00371 
00372         if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
00373                 msghdr.msg_namelen = sizeof(struct sockaddr_un);
00374 
00375         msghdr.msg_iov = &iovec;
00376         msghdr.msg_iovlen = 1;
00377 
00378         if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
00379                 r = -errno;
00380                 goto finish;
00381         }
00382 
00383         r = 1;
00384 
00385 finish:
00386         if (unset_environment)
00387                 unsetenv("NOTIFY_SOCKET");
00388 
00389         if (fd >= 0)
00390                 close(fd);
00391 
00392         return r;
00393 #endif
00394 }
00395 
00396 int sd_notifyf(int unset_environment, const char *format, ...) {
00397 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
00398         return 0;
00399 #else
00400         va_list ap;
00401         char *p = NULL;
00402         int r;
00403 
00404         va_start(ap, format);
00405         r = vasprintf(&p, format, ap);
00406         va_end(ap);
00407 
00408         if (r < 0 || !p)
00409                 return -ENOMEM;
00410 
00411         r = sd_notify(unset_environment, p);
00412         free(p);
00413 
00414         return r;
00415 #endif
00416 }
00417 
00418 int sd_booted(void) {
00419 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
00420         return 0;
00421 #else
00422 
00423         struct stat a, b;
00424 
00425         /* We simply test whether the systemd cgroup hierarchy is
00426          * mounted */
00427 
00428         if (lstat("/sys/fs/cgroup", &a) < 0)
00429                 return 0;
00430 
00431         if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
00432                 return 0;
00433 
00434         return a.st_dev != b.st_dev;
00435 #endif
00436 }