ctdb-event: Check the return status of sock_daemon_set_startup_fd
[samba.git] / ctdb / event / event_daemon.c
1 /*
2    CTDB event daemon
3
4    Copyright (C) Amitay Isaacs  2018
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "replace.h"
21 #include "system/filesys.h"
22
23 #include <popt.h>
24 #include <talloc.h>
25 #include <tevent.h>
26
27 #include "lib/util/tevent_unix.h"
28
29 #include "common/logging.h"
30 #include "common/path.h"
31 #include "common/sock_daemon.h"
32
33 #include "event/event_private.h"
34
35 struct event_daemon_state {
36         TALLOC_CTX *mem_ctx;
37         char *socket;
38         char *pidfile;
39         struct tevent_context *ev;
40         struct event_config *config;
41         struct sock_daemon_context *sockd;
42         struct event_context *eventd;
43 };
44
45 static int event_daemon_startup(void *private_data)
46 {
47         struct event_daemon_state *e_state = talloc_get_type_abort(
48                 private_data, struct event_daemon_state);
49         int ret;
50
51         ret = event_context_init(e_state,
52                                  e_state->ev,
53                                  e_state->config,
54                                  &e_state->eventd);
55         if (ret != 0) {
56                 D_ERR("Failed to initialize event context\n");
57                 return ret;
58         }
59
60         return 0;
61 }
62
63 static int event_daemon_reconfigure(void *private_data)
64 {
65         struct event_daemon_state *e_state = talloc_get_type_abort(
66                 private_data, struct event_daemon_state);
67         int ret;
68
69         ret = event_config_reload(e_state->config);
70         if (ret != 0) {
71                 D_WARNING("Configuration reload failed\n");
72         }
73
74         return 0;
75 }
76
77 static void event_daemon_shutdown(void *private_data)
78 {
79         struct event_daemon_state *e_state = talloc_get_type_abort(
80                 private_data, struct event_daemon_state);
81
82         TALLOC_FREE(e_state->eventd);
83 }
84
85 static bool event_client_connect(struct sock_client_context *client,
86                                  pid_t pid,
87                                  void *private_data)
88 {
89         struct event_daemon_state *e_state = talloc_get_type_abort(
90                 private_data, struct event_daemon_state);
91         int ret;
92
93         ret = eventd_client_add(e_state->eventd, client);
94         if (ret != 0) {
95                 D_ERR("Failed to register client, ret=%d\n", ret);
96                 return false;
97         }
98
99         return true;
100 }
101
102 static void event_client_disconnect(struct sock_client_context *client,
103                                     void *private_data)
104 {
105         struct event_daemon_state *e_state = talloc_get_type_abort(
106                 private_data, struct event_daemon_state);
107
108         eventd_client_del(e_state->eventd, client);
109 }
110
111 struct event_client_state {
112         struct tevent_context *ev;
113         struct event_context *eventd;
114         struct sock_client_context *client;
115         uint8_t *buf;
116         size_t buflen;
117 };
118
119 static void event_client_request_done(struct tevent_req *subreq);
120 static void event_client_reply_done(struct tevent_req *subreq);
121
122 static struct tevent_req *event_client_send(TALLOC_CTX *mem_ctx,
123                                             struct tevent_context *ev,
124                                             struct sock_client_context *client,
125                                             uint8_t *buf,
126                                             size_t buflen,
127                                             void *private_data)
128 {
129         struct event_daemon_state *e_state = talloc_get_type_abort(
130                 private_data, struct event_daemon_state);
131         struct tevent_req *req, *subreq;
132         struct event_client_state *state;
133
134         req = tevent_req_create(mem_ctx, &state, struct event_client_state);
135         if (req == NULL) {
136                 return NULL;
137         }
138
139         state->ev = ev;
140         state->eventd = e_state->eventd;
141         state->client = client;
142
143         subreq = event_pkt_send(state, ev, e_state->eventd, buf, buflen);
144         if (tevent_req_nomem(subreq, req)) {
145                 return tevent_req_post(req, ev);
146         }
147         tevent_req_set_callback(subreq, event_client_request_done, req);
148
149         return req;
150 }
151
152 static void event_client_request_done(struct tevent_req *subreq)
153 {
154         struct tevent_req *req = tevent_req_callback_data(
155                 subreq, struct tevent_req);
156         struct event_client_state *state = tevent_req_data(
157                 req, struct event_client_state);
158         int ret = 0;
159         bool ok;
160
161         ok = event_pkt_recv(subreq, &ret, state, &state->buf, &state->buflen);
162         TALLOC_FREE(subreq);
163         if (!ok) {
164                 tevent_req_error(req, ret);
165                 return;
166         }
167
168         ok = eventd_client_exists(state->eventd, state->client);
169         if (!ok) {
170                 /* Client has already disconnected */
171                 talloc_free(state->buf);
172                 tevent_req_done(req);
173                 return;
174         }
175
176         subreq = sock_socket_write_send(state,
177                                         state->ev,
178                                         state->client,
179                                         state->buf,
180                                         state->buflen);
181         if (tevent_req_nomem(subreq, req)) {
182                 talloc_free(state->buf);
183                 return;
184         }
185         tevent_req_set_callback(subreq, event_client_reply_done, req);
186 }
187
188 static void event_client_reply_done(struct tevent_req *subreq)
189 {
190         struct tevent_req *req = tevent_req_callback_data(
191                 subreq, struct tevent_req);
192         struct event_client_state *state = tevent_req_data(
193                 req, struct event_client_state);
194         int ret = 0;
195         bool ok;
196
197         talloc_free(state->buf);
198
199         ok = sock_socket_write_recv(subreq, &ret);
200         TALLOC_FREE(subreq);
201         if (!ok) {
202                 D_ERR("Sending reply failed\n");
203                 tevent_req_error(req, ret);
204                 return;
205         }
206
207         tevent_req_done(req);
208 }
209
210 static bool event_client_recv(struct tevent_req *req, int *perr)
211 {
212         if (tevent_req_is_unix_error(req, perr)) {
213                 return false;
214         }
215
216         return true;
217 }
218
219 static struct {
220         int pid;
221         int startup_fd;
222 } options = {
223         .pid = -1,
224         .startup_fd = -1,
225 };
226
227 struct poptOption cmdline_options[] = {
228         POPT_AUTOHELP
229         { "pid", 'P', POPT_ARG_INT, &options.pid, 0,
230                 "pid to wait for", "PID" },
231         { "startup-fd", 'S', POPT_ARG_INT, &options.startup_fd, 0,
232                 "file descriptor to notify of successful start", "FD" },
233         POPT_TABLEEND
234 };
235
236 int main(int argc, const char **argv)
237 {
238         poptContext pc;
239         struct event_daemon_state *e_state;
240         struct sock_daemon_funcs daemon_funcs;
241         struct sock_socket_funcs socket_funcs;
242         const char *log_location = "file:";
243         const char *log_level = "NOTICE";
244         const char *t;
245         int interactive = 0;
246         int opt, ret;
247         bool ok;
248
249         pc = poptGetContext(argv[0],
250                             argc,
251                             argv,
252                             cmdline_options,
253                             0);
254         while ((opt = poptGetNextOpt(pc)) != -1) {
255                 D_ERR("Invalid options %s: %s\n",
256                       poptBadOption(pc, 0),
257                       poptStrerror(opt));
258                 exit(1);
259         }
260
261         t = getenv("CTDB_INTERACTIVE");
262         if (t != NULL) {
263                 interactive = 1;
264         }
265
266         e_state = talloc_zero(NULL, struct event_daemon_state);
267         if (e_state == NULL) {
268                 D_ERR("Memory allocation error\n");
269                 ret = 1;
270                 goto fail;
271         }
272
273         e_state->mem_ctx = talloc_new(e_state);
274         if (e_state->mem_ctx == NULL) {
275                 D_ERR("Memory allocation error\n");
276                 ret = 1;
277                 goto fail;
278         }
279
280         e_state->socket = path_socket(e_state, "eventd");
281         if (e_state->socket == NULL) {
282                 D_ERR("Memory allocation error\n");
283                 ret = 1;
284                 goto fail;
285         }
286
287         e_state->pidfile = path_pidfile(e_state, "eventd");
288         if (e_state->pidfile == NULL) {
289                 D_ERR("Memory allocation error\n");
290                 ret = 1;
291                 goto fail;
292         }
293
294         ret = event_config_init(e_state, &e_state->config);
295         if (ret != 0) {
296                 D_ERR("Failed to initalize event config\n");
297                 goto fail;
298         }
299
300         e_state->ev = tevent_context_init(e_state->mem_ctx);
301         if (e_state->ev == NULL) {
302                 D_ERR("Failed to initalize tevent\n");
303                 ret = 1;
304                 goto fail;
305         }
306
307         daemon_funcs = (struct sock_daemon_funcs) {
308                 .startup = event_daemon_startup,
309                 .reconfigure = event_daemon_reconfigure,
310                 .shutdown = event_daemon_shutdown,
311         };
312
313         if (interactive == 0) {
314                 log_location = event_config_log_location(e_state->config);
315                 log_level = event_config_log_level(e_state->config);
316         }
317
318         ret = sock_daemon_setup(e_state->mem_ctx,
319                                 "ctdb-eventd",
320                                 log_location,
321                                 log_level,
322                                 &daemon_funcs,
323                                 e_state,
324                                 &e_state->sockd);
325         if (ret != 0) {
326                 D_ERR("Failed to setup sock daemon\n");
327                 goto fail;
328         }
329
330         socket_funcs = (struct sock_socket_funcs) {
331                 .connect = event_client_connect,
332                 .disconnect = event_client_disconnect,
333                 .read_send = event_client_send,
334                 .read_recv = event_client_recv,
335         };
336
337         ret = sock_daemon_add_unix(e_state->sockd,
338                                    e_state->socket,
339                                    &socket_funcs,
340                                    e_state);
341         if (ret != 0) {
342                 D_ERR("Failed to setup socket %s\n", e_state->socket);
343                 goto fail;
344         }
345
346         if (options.startup_fd != -1) {
347                 ok = sock_daemon_set_startup_fd(e_state->sockd,
348                                                 options.startup_fd);
349                 if (!ok) {
350                         goto fail;
351                 }
352         }
353
354         ret = sock_daemon_run(e_state->ev,
355                               e_state->sockd,
356                               e_state->pidfile,
357                               (interactive == 1),
358                               false,
359                               options.pid);
360         if (ret == EINTR) {
361                 ret = 0;
362         }
363
364         if (getenv("CTDB_TEST_MODE") != NULL) {
365                 talloc_report_full(e_state->mem_ctx, stderr);
366         }
367
368 fail:
369         talloc_free(e_state);
370         (void)poptFreeContext(pc);
371         exit(ret);
372 }