chiark / gitweb /
update fixme
[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 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include <sys/fcntl.h>
32 #include <netinet/in.h>
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <unistd.h>
36 #include <string.h>
37
38 #include "sd-daemon.h"
39
40 int sd_listen_fds(int unset_environment) {
41
42 #ifdef DISABLE_SYSTEMD
43         return 0;
44 #else
45         int r, fd;
46         const char *e;
47         char *p = NULL;
48         unsigned long l;
49
50         if (!(e = getenv("LISTEN_PID"))) {
51                 r = 0;
52                 goto finish;
53         }
54
55         errno = 0;
56         l = strtoul(e, &p, 10);
57
58         if (errno != 0) {
59                 r = -errno;
60                 goto finish;
61         }
62
63         if (!p || *p || l <= 0) {
64                 r = -EINVAL;
65                 goto finish;
66         }
67
68         /* Is this for us? */
69         if (getpid() != (pid_t) l) {
70                 r = 0;
71                 goto finish;
72         }
73
74         if (!(e = getenv("LISTEN_FDS"))) {
75                 r = 0;
76                 goto finish;
77         }
78
79         errno = 0;
80         l = strtoul(e, &p, 10);
81
82         if (errno != 0) {
83                 r = -errno;
84                 goto finish;
85         }
86
87         if (!p || *p) {
88                 r = -EINVAL;
89                 goto finish;
90         }
91
92         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
93                 int flags;
94
95                 if ((flags = fcntl(fd, F_GETFD)) < 0) {
96                         r = -errno;
97                         goto finish;
98                 }
99
100                 if (flags & FD_CLOEXEC)
101                         continue;
102
103                 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
104                         r = -errno;
105                         goto finish;
106                 }
107         }
108
109         r = (int) l;
110
111 finish:
112         if (unset_environment) {
113                 unsetenv("LISTEN_PID");
114                 unsetenv("LISTEN_FDS");
115         }
116
117         return r;
118 #endif
119 }
120
121 int sd_is_fifo(int fd, const char *path) {
122         struct stat st_fd;
123
124         if (fd < 0)
125                 return -EINVAL;
126
127         memset(&st_fd, 0, sizeof(st_fd));
128         if (fstat(fd, &st_fd) < 0)
129                 return -errno;
130
131         if (!S_ISFIFO(st_fd.st_mode))
132                 return 0;
133
134         if (path) {
135                 struct stat st_path;
136
137                 memset(&st_path, 0, sizeof(st_path));
138                 if (fstat(fd, &st_path) < 0) {
139
140                         if (errno == ENOENT || errno == ENOTDIR)
141                                 return 0;
142
143                         return -errno;
144                 }
145
146                 return
147                         st_path.st_dev == st_fd.st_dev &&
148                         st_path.st_ino == st_fd.st_ino;
149         }
150
151         return 1;
152 }
153
154 static int sd_is_socket_internal(int fd, int type, int listening) {
155         struct stat st_fd;
156
157         if (fd < 0 || type < 0)
158                 return -EINVAL;
159
160         if (fstat(fd, &st_fd) < 0)
161                 return -errno;
162
163         if (!S_ISSOCK(st_fd.st_mode))
164                 return 0;
165
166         if (type != 0) {
167                 int other_type = 0;
168                 socklen_t l = sizeof(other_type);
169
170                 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
171                         return -errno;
172
173                 if (l != sizeof(other_type))
174                         return -EINVAL;
175
176                 if (other_type != type)
177                         return 0;
178         }
179
180         if (listening >= 0) {
181                 int accepting = 0;
182                 socklen_t l = sizeof(accepting);
183
184                 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
185                         return -errno;
186
187                 if (l != sizeof(accepting))
188                         return -EINVAL;
189
190                 if (!accepting != !listening)
191                         return 0;
192         }
193
194         return 1;
195 }
196
197 union sockaddr_union {
198         struct sockaddr sa;
199         struct sockaddr_in in4;
200         struct sockaddr_in6 in6;
201         struct sockaddr_un un;
202         struct sockaddr_storage storage;
203 };
204
205 int sd_is_socket(int fd, int family, int type, int listening) {
206         int r;
207
208         if (family < 0)
209                 return -EINVAL;
210
211         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
212                 return r;
213
214         if (family > 0) {
215                 union sockaddr_union sockaddr;
216                 socklen_t l;
217
218                 memset(&sockaddr, 0, sizeof(sockaddr));
219                 l = sizeof(sockaddr);
220
221                 if (getsockname(fd, &sockaddr.sa, &l) < 0)
222                         return -errno;
223
224                 if (l < sizeof(sa_family_t))
225                         return -EINVAL;
226
227                 return sockaddr.sa.sa_family == family;
228         }
229
230         return 1;
231 }
232
233 int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
234         union sockaddr_union sockaddr;
235         socklen_t l;
236         int r;
237
238         if (family != 0 && family != AF_INET && family != AF_INET6)
239                 return -EINVAL;
240
241         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
242                 return r;
243
244         memset(&sockaddr, 0, sizeof(sockaddr));
245         l = sizeof(sockaddr);
246
247         if (getsockname(fd, &sockaddr.sa, &l) < 0)
248                 return -errno;
249
250         if (l < sizeof(sa_family_t))
251                 return -EINVAL;
252
253         if (sockaddr.sa.sa_family != AF_INET &&
254             sockaddr.sa.sa_family != AF_INET6)
255                 return 0;
256
257         if (family > 0)
258                 if (sockaddr.sa.sa_family != family)
259                         return 0;
260
261         if (port > 0) {
262                 if (sockaddr.sa.sa_family == AF_INET) {
263                         if (l < sizeof(struct sockaddr_in))
264                                 return -EINVAL;
265
266                         return htons(port) == sockaddr.in4.sin_port;
267                 } else {
268                         if (l < sizeof(struct sockaddr_in6))
269                                 return -EINVAL;
270
271                         return htons(port) == sockaddr.in6.sin6_port;
272                 }
273         }
274
275         return 1;
276 }
277
278 int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
279         union sockaddr_union sockaddr;
280         socklen_t l;
281         int r;
282
283         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
284                 return r;
285
286         memset(&sockaddr, 0, sizeof(sockaddr));
287         l = sizeof(sockaddr);
288
289         if (getsockname(fd, &sockaddr.sa, &l) < 0)
290                 return -errno;
291
292         if (l < sizeof(sa_family_t))
293                 return -EINVAL;
294
295         if (sockaddr.sa.sa_family != AF_UNIX)
296                 return 0;
297
298         if (path) {
299                 if (length <= 0)
300                         length = strlen(path);
301
302                 if (length <= 0)
303                         /* Unnamed socket */
304                         return l == sizeof(sa_family_t);
305
306                 if (path[0])
307                         /* Normal path socket */
308                         return
309                                 (l >= sizeof(sa_family_t) + length + 1) &&
310                                 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
311                 else
312                         /* Abstract namespace socket */
313                         return
314                                 (l == sizeof(sa_family_t) + length) &&
315                                 memcmp(path, sockaddr.un.sun_path, length) == 0;
316         }
317
318         return 1;
319 }