]>
Commit | Line | Data |
---|---|---|
5cb5a6ff LP |
1 | /*-*- Mode: C; c-basic-offset: 8 -*-*/ |
2 | ||
83c60c9f LP |
3 | #include <sys/types.h> |
4 | #include <sys/stat.h> | |
5 | #include <unistd.h> | |
6 | #include <errno.h> | |
7 | #include <fcntl.h> | |
9152c765 | 8 | #include <sys/poll.h> |
83c60c9f | 9 | |
5cb5a6ff LP |
10 | #include "name.h" |
11 | #include "socket.h" | |
83c60c9f LP |
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 | }; | |
5cb5a6ff LP |
24 | |
25 | static int socket_load(Name *n) { | |
26 | Socket *s = SOCKET(n); | |
27 | ||
28 | exec_context_defaults(&s->exec_context); | |
542563ba | 29 | s->backlog = SOMAXCONN; |
5cb5a6ff LP |
30 | |
31 | return name_load_fragment_and_dropin(n); | |
32 | } | |
33 | ||
542563ba LP |
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 | ||
5cb5a6ff LP |
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); | |
542563ba | 69 | SocketPort *p; |
5cb5a6ff LP |
70 | |
71 | assert(s); | |
72 | ||
5cb5a6ff LP |
73 | fprintf(f, |
74 | "%sSocket State: %s\n" | |
542563ba LP |
75 | "%sBindIPv6Only: %s\n" |
76 | "%sBacklog: %u\n", | |
5cb5a6ff | 77 | prefix, state_table[s->state], |
542563ba LP |
78 | prefix, yes_no(s->bind_ipv6_only), |
79 | prefix, s->backlog); | |
80 | ||
81 | LIST_FOREACH(p, s->ports) { | |
5cb5a6ff | 82 | |
542563ba LP |
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 | } | |
5cb5a6ff LP |
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 | ||
83c60c9f LP |
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 | ||
9152c765 LP |
128 | name_unwatch_fd(NAME(s), p->fd); |
129 | assert_se(close_nointr(p->fd) >= 0); | |
130 | ||
83c60c9f LP |
131 | p->fd = -1; |
132 | } | |
133 | } | |
134 | ||
542563ba | 135 | static int socket_start(Name *n) { |
83c60c9f LP |
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 | } | |
9152c765 LP |
191 | |
192 | if ((r = name_watch_fd(n, p->fd, POLLIN)) < 0) | |
193 | goto rollback; | |
83c60c9f LP |
194 | } |
195 | ||
196 | socket_set_state(s, SOCKET_LISTENING); | |
197 | ||
542563ba | 198 | return 0; |
83c60c9f LP |
199 | |
200 | rollback: | |
201 | close_fds(s); | |
202 | ||
203 | socket_set_state(s, SOCKET_MAINTAINANCE); | |
204 | ||
205 | return r; | |
542563ba LP |
206 | } |
207 | ||
208 | static int socket_stop(Name *n) { | |
83c60c9f LP |
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 | ||
542563ba LP |
231 | return 0; |
232 | } | |
233 | ||
5cb5a6ff | 234 | static NameActiveState socket_active_state(Name *n) { |
83c60c9f | 235 | assert(n); |
5cb5a6ff | 236 | |
83c60c9f | 237 | return state_table[SOCKET(n)->state]; |
5cb5a6ff LP |
238 | } |
239 | ||
9152c765 LP |
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 | ||
5cb5a6ff | 257 | static void socket_free_hook(Name *n) { |
5cb5a6ff LP |
258 | SocketExecCommand c; |
259 | Socket *s = SOCKET(n); | |
542563ba | 260 | SocketPort *p; |
5cb5a6ff LP |
261 | |
262 | assert(s); | |
263 | ||
542563ba LP |
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 | } | |
5cb5a6ff LP |
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 | ||
542563ba LP |
288 | .start = socket_start, |
289 | .stop = socket_stop, | |
5cb5a6ff LP |
290 | .reload = NULL, |
291 | ||
292 | .active_state = socket_active_state, | |
293 | ||
9152c765 LP |
294 | .fd_event = socket_fd_event, |
295 | ||
5cb5a6ff LP |
296 | .free_hook = socket_free_hook |
297 | }; |