i3
|
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 }