chiark / gitweb /
unit: turn display-manager.target into a service
[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
93         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
94                 int flags;
95
96                 if ((flags = fcntl(fd, F_GETFD)) < 0) {
97                         r = -errno;
98                         goto finish;
99                 }
100
101                 if (flags & FD_CLOEXEC)
102                         continue;
103
104                 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
105                         r = -errno;
106                         goto finish;
107                 }
108         }
109
110         r = (int) l;
111
112 finish:
113         if (unset_environment) {
114                 unsetenv("LISTEN_PID");
115                 unsetenv("LISTEN_FDS");
116         }
117
118         return r;
119 #endif
120 }
121
122 int sd_is_fifo(int fd, const char *path) {
123         struct stat st_fd;
124
125         if (fd < 0)
126                 return -EINVAL;
127
128         memset(&st_fd, 0, sizeof(st_fd));
129         if (fstat(fd, &st_fd) < 0)
130                 return -errno;
131
132         if (!S_ISFIFO(st_fd.st_mode))
133                 return 0;
134
135         if (path) {
136                 struct stat st_path;
137
138                 memset(&st_path, 0, sizeof(st_path));
139                 if (fstat(fd, &st_path) < 0) {
140
141                         if (errno == ENOENT || errno == ENOTDIR)
142                                 return 0;
143
144                         return -errno;
145                 }
146
147                 return
148                         st_path.st_dev == st_fd.st_dev &&
149                         st_path.st_ino == st_fd.st_ino;
150         }
151
152         return 1;
153 }
154
155 static int sd_is_socket_internal(int fd, int type, int listening) {
156         struct stat st_fd;
157
158         if (fd < 0 || type < 0)
159                 return -EINVAL;
160
161         if (fstat(fd, &st_fd) < 0)
162                 return -errno;
163
164         if (!S_ISSOCK(st_fd.st_mode))
165                 return 0;
166
167         if (type != 0) {
168                 int other_type = 0;
169                 socklen_t l = sizeof(other_type);
170
171                 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
172                         return -errno;
173
174                 if (l != sizeof(other_type))
175                         return -EINVAL;
176
177                 if (other_type != type)
178                         return 0;
179         }
180
181         if (listening >= 0) {
182                 int accepting = 0;
183                 socklen_t l = sizeof(accepting);
184
185                 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
186                         return -errno;
187
188                 if (l != sizeof(accepting))
189                         return -EINVAL;
190
191                 if (!accepting != !listening)
192                         return 0;
193         }
194
195         return 1;
196 }
197
198 union sockaddr_union {
199         struct sockaddr sa;
200         struct sockaddr_in in4;
201         struct sockaddr_in6 in6;
202         struct sockaddr_un un;
203         struct sockaddr_storage storage;
204 };
205
206 int sd_is_socket(int fd, int family, int type, int listening) {
207         int r;
208
209         if (family < 0)
210                 return -EINVAL;
211
212         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
213                 return r;
214
215         if (family > 0) {
216                 union sockaddr_union sockaddr;
217                 socklen_t l;
218
219                 memset(&sockaddr, 0, sizeof(sockaddr));
220                 l = sizeof(sockaddr);
221
222                 if (getsockname(fd, &sockaddr.sa, &l) < 0)
223                         return -errno;
224
225                 if (l < sizeof(sa_family_t))
226                         return -EINVAL;
227
228                 return sockaddr.sa.sa_family == family;
229         }
230
231         return 1;
232 }
233
234 int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
235         union sockaddr_union sockaddr;
236         socklen_t l;
237         int r;
238
239         if (family != 0 && family != AF_INET && family != AF_INET6)
240                 return -EINVAL;
241
242         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
243                 return r;
244
245         memset(&sockaddr, 0, sizeof(sockaddr));
246         l = sizeof(sockaddr);
247
248         if (getsockname(fd, &sockaddr.sa, &l) < 0)
249                 return -errno;
250
251         if (l < sizeof(sa_family_t))
252                 return -EINVAL;
253
254         if (sockaddr.sa.sa_family != AF_INET &&
255             sockaddr.sa.sa_family != AF_INET6)
256                 return 0;
257
258         if (family > 0)
259                 if (sockaddr.sa.sa_family != family)
260                         return 0;
261
262         if (port > 0) {
263                 if (sockaddr.sa.sa_family == AF_INET) {
264                         if (l < sizeof(struct sockaddr_in))
265                                 return -EINVAL;
266
267                         return htons(port) == sockaddr.in4.sin_port;
268                 } else {
269                         if (l < sizeof(struct sockaddr_in6))
270                                 return -EINVAL;
271
272                         return htons(port) == sockaddr.in6.sin6_port;
273                 }
274         }
275
276         return 1;
277 }
278
279 int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
280         union sockaddr_union sockaddr;
281         socklen_t l;
282         int r;
283
284         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
285                 return r;
286
287         memset(&sockaddr, 0, sizeof(sockaddr));
288         l = sizeof(sockaddr);
289
290         if (getsockname(fd, &sockaddr.sa, &l) < 0)
291                 return -errno;
292
293         if (l < sizeof(sa_family_t))
294                 return -EINVAL;
295
296         if (sockaddr.sa.sa_family != AF_UNIX)
297                 return 0;
298
299         if (path) {
300                 if (length <= 0)
301                         length = strlen(path);
302
303                 if (length <= 0)
304                         /* Unnamed socket */
305                         return l == sizeof(sa_family_t);
306
307                 if (path[0])
308                         /* Normal path socket */
309                         return
310                                 (l >= sizeof(sa_family_t) + length + 1) &&
311                                 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
312                 else
313                         /* Abstract namespace socket */
314                         return
315                                 (l == sizeof(sa_family_t) + length) &&
316                                 memcmp(path, sockaddr.un.sun_path, length) == 0;
317         }
318
319         return 1;
320 }