chiark / gitweb /
vconsole: add new utility to initialize the virtual console
[elogind.git] / src / sd-daemon.c
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
44 #include "sd-daemon.h"
45
46 int 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
117 finish:
118         if (unset_environment) {
119                 unsetenv("LISTEN_PID");
120                 unsetenv("LISTEN_FDS");
121         }
122
123         return r;
124 #endif
125 }
126
127 int 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
160 static 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
203 union 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
211 int 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
239 int 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
284 int 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
327 int 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
384 finish:
385         if (unset_environment)
386                 unsetenv("NOTIFY_SOCKET");
387
388         if (fd >= 0)
389                 close(fd);
390
391         return r;
392 #endif
393 }
394
395 int 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
417 int 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 }