• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
sd_listen_fds(int unset_environment)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 
sd_is_fifo(int fd,const char * path)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 
sd_is_socket_internal(int fd,int type,int listening)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 
sd_is_socket(int fd,int family,int type,int listening)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 
sd_is_socket_inet(int fd,int family,int type,int listening,uint16_t port)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 
sd_is_socket_unix(int fd,int type,int listening,const char * path,size_t length)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 
sd_notify(int unset_environment,const char * state)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 
sd_notifyf(int unset_environment,const char * format,...)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 
sd_booted(void)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 }
436