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