]> git.ipfire.org Git - people/ms/systemd.git/blob - socket.c
add simple event loop
[people/ms/systemd.git] / socket.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <unistd.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <sys/poll.h>
9
10 #include "name.h"
11 #include "socket.h"
12 #include "log.h"
13
14 static const NameActiveState state_table[_SOCKET_STATE_MAX] = {
15 [SOCKET_DEAD] = NAME_INACTIVE,
16 [SOCKET_START_PRE] = NAME_ACTIVATING,
17 [SOCKET_START_POST] = NAME_ACTIVATING,
18 [SOCKET_LISTENING] = NAME_ACTIVE,
19 [SOCKET_RUNNING] = NAME_ACTIVE,
20 [SOCKET_STOP_PRE] = NAME_DEACTIVATING,
21 [SOCKET_STOP_POST] = NAME_DEACTIVATING,
22 [SOCKET_MAINTAINANCE] = NAME_INACTIVE,
23 };
24
25 static int socket_load(Name *n) {
26 Socket *s = SOCKET(n);
27
28 exec_context_defaults(&s->exec_context);
29 s->backlog = SOMAXCONN;
30
31 return name_load_fragment_and_dropin(n);
32 }
33
34 static const char* listen_lookup(int type) {
35
36 if (type == SOCK_STREAM)
37 return "ListenStream";
38 else if (type == SOCK_DGRAM)
39 return "ListenDatagram";
40 else if (type == SOCK_SEQPACKET)
41 return "ListenSequentialPacket";
42
43 assert_not_reached("Unkown socket type");
44 return NULL;
45 }
46
47 static void socket_dump(Name *n, FILE *f, const char *prefix) {
48
49 static const char* const state_table[_SOCKET_STATE_MAX] = {
50 [SOCKET_DEAD] = "dead",
51 [SOCKET_START_PRE] = "start-pre",
52 [SOCKET_START_POST] = "start-post",
53 [SOCKET_LISTENING] = "listening",
54 [SOCKET_RUNNING] = "running",
55 [SOCKET_STOP_PRE] = "stop-pre",
56 [SOCKET_STOP_POST] = "stop-post",
57 [SOCKET_MAINTAINANCE] = "maintainance"
58 };
59
60 static const char* const command_table[_SOCKET_EXEC_MAX] = {
61 [SOCKET_EXEC_START_PRE] = "StartPre",
62 [SOCKET_EXEC_START_POST] = "StartPost",
63 [SOCKET_EXEC_STOP_PRE] = "StopPre",
64 [SOCKET_EXEC_STOP_POST] = "StopPost"
65 };
66
67 SocketExecCommand c;
68 Socket *s = SOCKET(n);
69 SocketPort *p;
70
71 assert(s);
72
73 fprintf(f,
74 "%sSocket State: %s\n"
75 "%sBindIPv6Only: %s\n"
76 "%sBacklog: %u\n",
77 prefix, state_table[s->state],
78 prefix, yes_no(s->bind_ipv6_only),
79 prefix, s->backlog);
80
81 LIST_FOREACH(p, s->ports) {
82
83 if (p->type == SOCKET_SOCKET) {
84 const char *t;
85 int r;
86 char *k;
87
88 if ((r = socket_address_print(&p->address, &k)) < 0)
89 t = strerror(-r);
90 else
91 t = k;
92
93 fprintf(f, "%s%s: %s\n", prefix, listen_lookup(p->address.type), k);
94 free(k);
95 } else
96 fprintf(f, "%sListenFIFO: %s\n", prefix, p->path);
97 }
98
99 exec_context_dump(&s->exec_context, f, prefix);
100
101 for (c = 0; c < _SOCKET_EXEC_MAX; c++) {
102 ExecCommand *i;
103
104 LIST_FOREACH(i, s->exec_command[c])
105 fprintf(f, "%s%s: %s\n", prefix, command_table[c], i->path);
106 }
107 }
108
109 static void socket_set_state(Socket *s, SocketState state) {
110 SocketState old_state;
111 assert(s);
112
113 old_state = s->state;
114 s->state = state;
115
116 name_notify(NAME(s), state_table[old_state], state_table[s->state]);
117 }
118
119 static void close_fds(Socket *s) {
120 SocketPort *p;
121
122 assert(s);
123
124 LIST_FOREACH(p, s->ports) {
125 if (p->fd < 0)
126 continue;
127
128 name_unwatch_fd(NAME(s), p->fd);
129 assert_se(close_nointr(p->fd) >= 0);
130
131 p->fd = -1;
132 }
133 }
134
135 static int socket_start(Name *n) {
136 Socket *s = SOCKET(n);
137 SocketPort *p;
138 int r;
139
140 assert(s);
141
142 if (s->state == SOCKET_START_PRE ||
143 s->state == SOCKET_START_POST)
144 return 0;
145
146 if (s->state == SOCKET_LISTENING ||
147 s->state == SOCKET_RUNNING)
148 return -EALREADY;
149
150 if (s->state == SOCKET_STOP_PRE ||
151 s->state == SOCKET_STOP_POST)
152 return -EAGAIN;
153
154 assert(s->state == SOCKET_DEAD || s->state == SOCKET_MAINTAINANCE);
155
156 LIST_FOREACH(p, s->ports) {
157
158 assert(p->fd < 0);
159
160 if (p->type == SOCKET_SOCKET) {
161
162 if ((r = socket_address_listen(&p->address, s->backlog, s->bind_ipv6_only, &p->fd)) < 0)
163 goto rollback;
164
165 } else {
166 struct stat st;
167 assert(p->type == SOCKET_FIFO);
168
169 if (mkfifo(p->path, 0666 & ~s->exec_context.umask) < 0 && errno != EEXIST) {
170 r = -errno;
171 goto rollback;
172 }
173
174 if ((p->fd = open(p->path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) {
175 r = -errno;
176 goto rollback;
177 }
178
179 if (fstat(p->fd, &st) < 0) {
180 r = -errno;
181 goto rollback;
182 }
183
184 /* FIXME verify user, access mode */
185
186 if (!S_ISFIFO(st.st_mode)) {
187 r = -EEXIST;
188 goto rollback;
189 }
190 }
191
192 if ((r = name_watch_fd(n, p->fd, POLLIN)) < 0)
193 goto rollback;
194 }
195
196 socket_set_state(s, SOCKET_LISTENING);
197
198 return 0;
199
200 rollback:
201 close_fds(s);
202
203 socket_set_state(s, SOCKET_MAINTAINANCE);
204
205 return r;
206 }
207
208 static int socket_stop(Name *n) {
209 Socket *s = SOCKET(n);
210
211 assert(s);
212
213 if (s->state == SOCKET_START_PRE ||
214 s->state == SOCKET_START_POST)
215 return -EAGAIN;
216
217 if (s->state == SOCKET_DEAD ||
218 s->state == SOCKET_MAINTAINANCE)
219 return -EALREADY;
220
221 if (s->state == SOCKET_STOP_PRE ||
222 s->state == SOCKET_STOP_POST)
223 return 0;
224
225 assert(s->state == SOCKET_LISTENING || s->state == SOCKET_RUNNING);
226
227 close_fds(s);
228
229 socket_set_state(s, SOCKET_DEAD);
230
231 return 0;
232 }
233
234 static NameActiveState socket_active_state(Name *n) {
235 assert(n);
236
237 return state_table[SOCKET(n)->state];
238 }
239
240 static void socket_fd_event(Name *n, int fd, uint32_t events) {
241 Socket *s = SOCKET(n);
242
243 assert(n);
244
245 if (events != POLLIN)
246 goto fail;
247
248 log_info("POLLIN on %s", name_id(n));
249
250 return;
251
252 fail:
253 close_fds(s);
254 socket_set_state(s, SOCKET_MAINTAINANCE);
255 }
256
257 static void socket_free_hook(Name *n) {
258 SocketExecCommand c;
259 Socket *s = SOCKET(n);
260 SocketPort *p;
261
262 assert(s);
263
264 while ((p = s->ports)) {
265 LIST_REMOVE(SocketPort, s->ports, p);
266
267 if (p->fd >= 0)
268 close_nointr(p->fd);
269 free(p->path);
270 free(p);
271 }
272
273 exec_context_free(&s->exec_context);
274
275 for (c = 0; c < _SOCKET_EXEC_MAX; c++)
276 exec_command_free_list(s->exec_command[c]);
277
278 if (s->service)
279 s->service->socket = NULL;
280 }
281
282 const NameVTable socket_vtable = {
283 .suffix = ".socket",
284
285 .load = socket_load,
286 .dump = socket_dump,
287
288 .start = socket_start,
289 .stop = socket_stop,
290 .reload = NULL,
291
292 .active_state = socket_active_state,
293
294 .fd_event = socket_fd_event,
295
296 .free_hook = socket_free_hook
297 };