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 46int 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 117finish: 118 if (unset_environment) { 119 unsetenv("LISTEN_PID"); 120 unsetenv("LISTEN_FDS"); 121 } 122 123 return r; 124#endif 125} 126 127int 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 160static 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 203union 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 211int 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 239int 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 284int 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 327int 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 384finish: 385 if (unset_environment) 386 unsetenv("NOTIFY_SOCKET"); 387 388 if (fd >= 0) 389 close(fd); 390 391 return r; 392#endif 393} 394 395int 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 417int 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