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