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