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