ctdb-scripts: Use load_script_options() in miscellaneous scripts
[bbaumbach/samba-autobuild/.git] / ctdb / tools / ctdb_event.c
1 /*
2    CTDB event daemon control tool
3
4    Copyright (C) Amitay Isaacs  2016
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/network.h"
22 #include "system/time.h"
23
24 #include <talloc.h>
25 #include <tevent.h>
26
27 #include "lib/util/debug.h"
28
29 #include "protocol/protocol_util.h"
30 #include "client/client_event.h"
31 #include "common/logging.h"
32
33 struct tool_context {
34         struct tevent_context *ev;
35         struct ctdb_event_context *eclient;
36 };
37
38 static void usage(void);
39
40 static char *compact_args(const char **argv, int argc, int from)
41 {
42         char *arg_str = NULL;
43         int i;
44
45         for (i = from; i < argc; i++) {
46                 arg_str = talloc_asprintf_append(arg_str, "%s ", argv[i]);
47                 if (arg_str == NULL) {
48                         fprintf(stderr, "talloc_asprintf_append() failed\n");
49                         exit(1);
50                 }
51         }
52
53         return arg_str;
54 }
55
56 static int command_run(TALLOC_CTX *mem_ctx, struct tool_context *tctx,
57                        int argc, const char **argv)
58 {
59         struct tevent_req *req;
60         enum ctdb_event event;
61         const char *arg_str;
62         int timeout;
63         int eargs;
64         int ret, result;
65         bool status;
66
67         if (argc < 2) {
68                 usage();
69         }
70
71         event = ctdb_event_from_string(argv[0]);
72         if (event == CTDB_EVENT_MAX) {
73                 fprintf(stderr, "Invalid event '%s'\n", argv[0]);
74                 return 1;
75         }
76
77         timeout = atoi(argv[1]);
78         if (timeout < 0) {
79                 timeout = 0;
80         }
81
82         switch (event) {
83                 case CTDB_EVENT_INIT:
84                 case CTDB_EVENT_SETUP:
85                 case CTDB_EVENT_STARTUP:
86                 case CTDB_EVENT_MONITOR:
87                 case CTDB_EVENT_IPREALLOCATED:
88                         eargs = 0;
89                         break;
90
91                 case CTDB_EVENT_TAKE_IP:
92                 case CTDB_EVENT_RELEASE_IP:
93                         eargs = 3;
94                         break;
95
96                 case CTDB_EVENT_UPDATE_IP:
97                         eargs = 4;
98                         break;
99
100                 default:
101                         eargs = -1;
102                         break;
103         }
104
105         if (eargs < 0) {
106                 fprintf(stderr, "Cannot run event %s\n", argv[0]);
107                 return 1;
108         }
109
110         if (argc != 2 + eargs) {
111                 fprintf(stderr, "Insufficient arguments for event %s\n",
112                         argv[0]);
113                 return 1;
114         }
115
116         arg_str = compact_args(argv, argc, 2);
117
118         req = ctdb_event_run_send(mem_ctx, tctx->ev, tctx->eclient,
119                                   event, timeout, arg_str);
120         if (req == NULL) {
121                 return ENOMEM;
122         }
123
124         tevent_req_poll(req, tctx->ev);
125
126         status = ctdb_event_run_recv(req, &ret, &result);
127         talloc_free(req);
128         if (! status) {
129                 fprintf(stderr, "Failed to run event %s, ret=%d\n",
130                         argv[0], ret);
131                 return ret;
132         }
133
134         if (result == -ETIME) {
135                 fprintf(stderr, "Event %s timed out\n", argv[0]);
136         } else if (result == -ECANCELED) {
137                 fprintf(stderr, "Event %s got cancelled\n", argv[0]);
138         } else if (result != 0) {
139                 fprintf(stderr, "Failed to run event %s, result=%d\n",
140                         argv[0], result);
141         }
142
143         ret = (result < 0) ? -result : result;
144         return ret;
145 }
146
147 static double timeval_delta(struct timeval *tv2, struct timeval *tv)
148 {
149         return (tv2->tv_sec - tv->tv_sec) +
150                (tv2->tv_usec - tv->tv_usec) * 1.0e-6;
151 }
152
153 static void print_status_one(struct ctdb_script *script)
154 {
155         if (script->status == -ETIME) {
156                 printf("%-20s %-10s %s", script->name, "TIMEDOUT",
157                        ctime(&script->start.tv_sec));
158         } else if (script->status == -ENOEXEC) {
159                 printf("%-20s %-10s\n", script->name, "DISABLED");
160         } else if (script->status < 0) {
161                 printf("%-20s %-10s (%s)\n", script->name, "CANNOT RUN",
162                        strerror(-script->status));
163         } else if (script->status == 0) {
164                 printf("%-20s %-10s %.3lf %s", script->name, "OK",
165                        timeval_delta(&script->finished, &script->start),
166                        ctime(&script->start.tv_sec));
167         } else {
168                 printf("%-20s %-10s %.3lf %s", script->name, "ERROR",
169                        timeval_delta(&script->finished, &script->start),
170                        ctime(&script->start.tv_sec));
171         }
172
173         if (script->status != 0 && script->status != -ENOEXEC) {
174                 printf("  OUTPUT: %s\n", script->output);
175         }
176 }
177
178 static int command_status(TALLOC_CTX *mem_ctx, struct tool_context *tctx,
179                           int argc, const char **argv)
180 {
181         struct tevent_req *req;
182         struct ctdb_script_list *script_list;
183         enum ctdb_event event;
184         uint32_t state;
185         int ret, result, event_status, i;
186         bool status;
187
188         if (argc < 1) {
189                 event = CTDB_EVENT_MONITOR;
190         } else {
191                 event = ctdb_event_from_string(argv[0]);
192                 if (event == CTDB_EVENT_MAX) {
193                         fprintf(stderr, "Invalid event '%s'\n", argv[0]);
194                         return EINVAL;
195                 }
196         }
197
198         if (argc < 2) {
199                 state = CTDB_EVENT_LAST_RUN;
200         } else {
201                 if (strcmp(argv[1], "lastrun") == 0) {
202                         state = CTDB_EVENT_LAST_RUN;
203                 } else if (strcmp(argv[1], "lastpass") == 0) {
204                         state = CTDB_EVENT_LAST_PASS;
205                 } else if (strcmp(argv[1], "lastfail") == 0) {
206                         state = CTDB_EVENT_LAST_FAIL;
207                 } else {
208                         fprintf(stderr, "Invalid state %s\n", argv[1]);
209                         return EINVAL;
210                 }
211         }
212
213         req = ctdb_event_status_send(mem_ctx, tctx->ev, tctx->eclient,
214                                      event, state);
215         if (req == NULL) {
216                 return ENOMEM;
217         }
218
219         tevent_req_poll(req, tctx->ev);
220
221         status = ctdb_event_status_recv(req, &ret, &result, &event_status,
222                                         mem_ctx, &script_list);
223         talloc_free(req);
224         if (! status) {
225                 fprintf(stderr, "Failed to get event %s status, ret=%d\n",
226                         ctdb_event_to_string(event), ret);
227                 return ret;
228         }
229
230         if (result != 0) {
231                 fprintf(stderr, "Failed to get event %s status, result=%d\n",
232                         ctdb_event_to_string(event), result);
233                 return result;
234         }
235
236         if (script_list == NULL) {
237                 if (state == CTDB_EVENT_LAST_RUN) {
238                         printf("Event %s has never run\n",
239                                ctdb_event_to_string(event));
240                 } else if (state == CTDB_EVENT_LAST_PASS) {
241                         printf("Event %s has never passed\n",
242                                 ctdb_event_to_string(event));
243                 } else if (state == CTDB_EVENT_LAST_FAIL) {
244                         printf("Event %s has never failed\n",
245                                 ctdb_event_to_string(event));
246                 }
247         } else {
248                 for (i=0; i<script_list->num_scripts; i++) {
249                         print_status_one(&script_list->script[i]);
250                 }
251                 talloc_free(script_list);
252         }
253
254         ret = (event_status < 0) ? -event_status : event_status;
255         return ret;
256 }
257
258 static int command_script_list(TALLOC_CTX *mem_ctx, struct tool_context *tctx,
259                                int argc, const char **argv)
260 {
261         struct tevent_req *req;
262         struct ctdb_script_list *script_list = NULL;
263         int ret, result, i;
264         bool status;
265
266         if (argc != 0) {
267                 usage();
268         }
269
270         req = ctdb_event_script_list_send(mem_ctx, tctx->ev, tctx->eclient);
271         if (req == NULL) {
272                 return ENOMEM;
273         }
274
275         tevent_req_poll(req, tctx->ev);
276
277         status = ctdb_event_script_list_recv(req, &ret, &result,
278                                              mem_ctx, &script_list);
279         talloc_free(req);
280         if (! status) {
281                 fprintf(stderr, "Failed to get script list, ret=%d\n", ret);
282                 return ret;
283         }
284
285         if (result != 0) {
286                 fprintf(stderr, "Failed to get script list, result=%d\n",
287                         result);
288                 return result;
289         }
290
291         if (script_list == NULL || script_list->num_scripts == 0) {
292                 printf("No event scripts found\n");
293         } else {
294                 for (i=0; i<script_list->num_scripts; i++) {
295                         struct ctdb_script *s;
296
297                         s = &script_list->script[i];
298
299                         if (s->status == -ENOEXEC) {
300                                 printf("%-20s DISABLED\n", s->name);
301                         } else {
302                                 printf("%-20s\n", s->name);
303                         }
304                 }
305                 talloc_free(script_list);
306         }
307
308         return 0;
309 }
310
311 static int command_script_enable(TALLOC_CTX *mem_ctx,
312                                  struct tool_context *tctx,
313                                  int argc, const char **argv)
314 {
315         struct tevent_req *req;
316         int ret, result;
317         bool status;
318
319         if (argc != 1) {
320                 usage();
321         }
322
323         req = ctdb_event_script_enable_send(mem_ctx, tctx->ev, tctx->eclient,
324                                             argv[0]);
325         if (req == NULL) {
326                 return ENOMEM;
327         }
328
329         tevent_req_poll(req, tctx->ev);
330
331         status = ctdb_event_script_enable_recv(req, &ret, &result);
332         talloc_free(req);
333         if (! status) {
334                 fprintf(stderr, "Failed to enable script %s, ret=%d\n",
335                         argv[0], ret);
336                 return ret;
337         }
338
339         if (result == -ENOENT) {
340                 fprintf(stderr, "Script %s does not exist\n", argv[0]);
341         } else if (result == -EINVAL) {
342                 fprintf(stderr, "Script name %s is invalid\n", argv[0]);
343         } else if (result != 0) {
344                 fprintf(stderr, "Failed to enable script %s, result=%d\n",
345                         argv[0], result);
346         }
347
348         ret = (result < 0) ? -result : result;
349         return ret;
350 }
351
352 static int command_script_disable(TALLOC_CTX *mem_ctx,
353                                   struct tool_context *tctx,
354                                   int argc, const char **argv)
355 {
356         struct tevent_req *req;
357         int ret, result;
358         bool status;
359
360         if (argc != 1) {
361                 usage();
362         }
363
364         req = ctdb_event_script_disable_send(mem_ctx, tctx->ev, tctx->eclient,
365                                              argv[0]);
366         if (req == NULL) {
367                 return ENOMEM;
368         }
369
370         tevent_req_poll(req, tctx->ev);
371
372         status = ctdb_event_script_disable_recv(req, &ret, &result);
373         talloc_free(req);
374         if (! status) {
375                 fprintf(stderr, "Failed to disable script %s, ret=%d\n",
376                         argv[0], ret);
377                 return ret;
378         }
379
380         if (result == -ENOENT) {
381                 fprintf(stderr, "Script %s does not exist\n", argv[0]);
382         } else if (result == -EINVAL) {
383                 fprintf(stderr, "Script name %s is invalid\n", argv[0]);
384         } else if (result != 0) {
385                 fprintf(stderr, "Failed to disable script %s, result=%d\n",
386                         argv[0], result);
387         }
388
389         ret = (result < 0) ? -result : result;
390         return ret;
391 }
392
393 static const struct ctdb_event_cmd {
394         const char *name;
395         const char *str1;
396         const char *str2;
397         int (*fn)(TALLOC_CTX *, struct tool_context *, int, const char **);
398         const char *msg;
399         const char *args;
400 } ctdb_event_commands[] = {
401         { "run", "run", NULL, command_run,
402                 "Run an event", "<event> <timeout> <args>" },
403         { "status", "status", NULL, command_status,
404                 "Get last status of an event",
405                 "[<event>] [lastrun|lastpass|lastfail]" },
406         { "script list", "script", "list", command_script_list,
407                 "Get list of event scripts", NULL },
408         { "script enable", "script", "enable", command_script_enable,
409                 "Enable an event script", "<script>" },
410         { "script disable", "script", "disable", command_script_disable,
411                 "Disable an event script", "<script>" },
412 };
413
414 static void usage(void)
415 {
416         const struct ctdb_event_cmd *cmd;
417         int i;
418
419         printf("Usage: ctdb_event <command> <args>\n");
420         printf("Commands:\n");
421         for (i=0; i<ARRAY_SIZE(ctdb_event_commands); i++) {
422                 cmd = &ctdb_event_commands[i];
423
424                 printf("  %-15s %-37s  %s\n",
425                        cmd->name, cmd->args ? cmd->args : "", cmd->msg);
426         }
427
428         exit(1);
429 }
430
431 static const struct ctdb_event_cmd *match_command(const char *str1,
432                                                   const char *str2)
433 {
434         const struct ctdb_event_cmd *cmd;
435         bool match = false;
436         int i;
437
438         for (i=0; i<ARRAY_SIZE(ctdb_event_commands); i++) {
439                 cmd = &ctdb_event_commands[i];
440                 if (strlen(str1) == strlen(cmd->str1) &&
441                     strcmp(str1, cmd->str1) == 0) {
442                         match = true;
443                 }
444                 if (cmd->str2 != NULL) {
445                         if (str2 == NULL) {
446                                 match = false;
447                         } else {
448                                 if (strcmp(str2, cmd->str2) == 0) {
449                                         match = true;
450                                 } else {
451                                         match = false;
452                                 }
453                         }
454                 }
455
456                 if (match) {
457                         return cmd;
458                 }
459         }
460
461         return NULL;
462 }
463
464 static int process_command(const char *sockpath,
465                            const struct ctdb_event_cmd *cmd,
466                            int argc, const char **argv)
467 {
468         TALLOC_CTX *tmp_ctx;
469         struct tool_context *tctx;
470         int ret;
471
472         tmp_ctx = talloc_new(NULL);
473         if (tmp_ctx == NULL) {
474                 fprintf(stderr, "Memory allocation error\n");
475                 goto fail;
476         }
477
478         tctx = talloc_zero(tmp_ctx, struct tool_context);
479         if (tctx == NULL) {
480                 fprintf(stderr, "Memory allocation error\n");
481                 goto fail;
482         }
483
484         tctx->ev = tevent_context_init(tctx);
485         if (tctx->ev == NULL) {
486                 fprintf(stderr, "Failed to initialize tevent\n");
487                 goto fail;
488         }
489
490         ret = ctdb_event_init(tmp_ctx, tctx->ev, sockpath, &tctx->eclient);
491         if (ret != 0) {
492                 fprintf(stderr, "ctdb_event_init() failed, ret=%d\n", ret);
493                 goto fail;
494         }
495
496         if (cmd->str2 == NULL) {
497                 ret = cmd->fn(tmp_ctx, tctx, argc-1, &argv[1]);
498         } else {
499                 ret = cmd->fn(tmp_ctx, tctx, argc-2, &argv[2]);
500         }
501
502         talloc_free(tmp_ctx);
503         return ret;
504
505 fail:
506         TALLOC_FREE(tmp_ctx);
507         return 1;
508 }
509
510 int main(int argc, const char **argv)
511 {
512         const struct ctdb_event_cmd *cmd;
513         const char *eventd_socket;
514         const char *t;
515
516         if (argc < 3) {
517                 usage();
518         }
519
520         eventd_socket = argv[1];
521
522         cmd = match_command(argv[2], argv[3]);
523         if (cmd == NULL) {
524                 fprintf(stderr, "Unknown command '%s %s'", argv[2], argv[3]);
525                 exit(1);
526         }
527
528         /* Enable logging */
529         setup_logging("ctdb_event", DEBUG_STDERR);
530         t = getenv("CTDB_DEBUGLEVEL");
531         if (t == NULL || ! debug_level_parse(t, &DEBUGLEVEL)) {
532                 DEBUGLEVEL = DEBUG_ERR;
533         }
534
535         return process_command(eventd_socket, cmd, argc-2, &argv[2]);
536 }