chiark / gitweb /
gcc: disable warn_unused_result attribute warnings
[elogind.git] / src / sd-daemon.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
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 (fstat(fd, &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__)
329         return 0;
330 #else
331         int fd = -1, r;
332         struct msghdr msghdr;
333         struct iovec iovec;
334         union sockaddr_union sockaddr;
335         struct ucred *ucred;
336         union {
337                 struct cmsghdr cmsghdr;
338                 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
339         } control;
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(&control, 0, sizeof(control));
373         control.cmsghdr.cmsg_level = SOL_SOCKET;
374         control.cmsghdr.cmsg_type = SCM_CREDENTIALS;
375         control.cmsghdr.cmsg_len = CMSG_LEN(sizeof(struct ucred));
376
377         ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
378         ucred->pid = getpid();
379         ucred->uid = getuid();
380         ucred->gid = getgid();
381
382         memset(&msghdr, 0, sizeof(msghdr));
383         msghdr.msg_name = &sockaddr;
384         msghdr.msg_namelen = sizeof(struct sockaddr_un);
385         msghdr.msg_iov = &iovec;
386         msghdr.msg_iovlen = 1;
387         msghdr.msg_control = &control;
388         msghdr.msg_controllen = control.cmsghdr.cmsg_len;
389
390         if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
391                 r = -errno;
392                 goto finish;
393         }
394
395         r = 1;
396
397 finish:
398         if (unset_environment)
399                 unsetenv("NOTIFY_SOCKET");
400
401         if (fd >= 0)
402                 close(fd);
403
404         return r;
405 #endif
406 }
407
408 int sd_notifyf(int unset_environment, const char *format, ...) {
409 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
410         return 0;
411 #else
412         va_list ap;
413         char *p = NULL;
414         int r;
415
416         va_start(ap, format);
417         r = vasprintf(&p, format, ap);
418         va_end(ap);
419
420         if (r < 0 || !p)
421                 return -ENOMEM;
422
423         r = sd_notify(unset_environment, p);
424         free(p);
425
426         return r;
427 #endif
428 }