1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9/**
10 * monetdb
11 * Fabian Groffen
12 * MonetDB Database Administrator's Toolkit
13 *
14 * A group of MonetDB servers in a dbfarm can be under control of
15 * Merovingian, a daemon which by itself does not allow any user
16 * interaction. The monetdb utility is designed to be the interface for
17 * the DBA to the dbfarm and its vicinity. Creating or deleting
18 * databases next to retrieving status information about them are the
19 * primary goals of this tool.
20 */
21
22#include "monetdb_config.h"
23#include "utils.h"
24#include "properties.h"
25#include "glob.h"
26#include "control.h"
27#include "msabaoth.h"
28#include "mutils.h"
29#include <string.h> /* strerror */
30#include <sys/stat.h> /* mkdir, stat, umask */
31#include <sys/types.h> /* mkdir, readdir */
32#include <dirent.h> /* readdir */
33#include <unistd.h> /* stat, rmdir, unlink, ioctl */
34#include <time.h> /* strftime */
35#include <sys/socket.h> /* socket */
36#ifdef HAVE_SYS_UN_H
37#include <sys/un.h> /* sockaddr_un */
38#endif
39#ifdef HAVE_STROPTS_H
40#include <stropts.h> /* ioctl on Solaris */
41#endif
42#ifdef HAVE_SYS_IOCTL_H
43#include <sys/ioctl.h>
44#endif
45#ifdef HAVE_TERMIOS_H
46#include <termios.h> /* TIOCGWINSZ/TIOCSWINSZ */
47#endif
48
49static char *mero_host = NULL;
50static int mero_port = -1;
51static char *mero_pass = NULL;
52static char monetdb_quiet = 0;
53static int TERMWIDTH = 0; /* default to no wrapping */
54
55static void
56command_help(int argc, char *argv[])
57{
58 if (argc < 2) {
59 printf("Usage: monetdb [options] command [command-options-and-arguments]\n");
60 printf(" where command is one of:\n");
61 printf(" create, destroy, lock, release\n");
62 printf(" status, start, stop, kill\n");
63 printf(" profilerstart, profilerstop\n");
64 printf(" set, get, inherit\n");
65 printf(" discover, help, version\n");
66 printf(" options can be:\n");
67 printf(" -q suppress status output\n");
68 printf(" -h host hostname to contact (remote merovingian)\n");
69 printf(" -p port port to contact\n");
70 printf(" -P pass password to use to login at remote merovingian\n");
71 printf(" use the help command to get help for a particular command\n");
72 } else if (strcmp(argv[1], "create") == 0) {
73 printf("Usage: monetdb create [-m pattern] [-p pass] database [database ...]\n");
74 printf(" Initialises a new database or multiplexfunnel in the MonetDB Server. A\n");
75 printf(" database created with this command makes it available\n");
76 printf(" for use, however in maintenance mode (see monetdb lock).\n");
77 printf("Options:\n");
78 printf(" -m pattern create a multiplex funnel for pattern.\n");
79 printf(" -p pass create database with given password for database user.\n");
80 } else if (strcmp(argv[1], "destroy") == 0) {
81 printf("Usage: monetdb destroy [-f] database [database ...]\n");
82 printf(" Removes the given database, including all its data and\n");
83 printf(" logfiles. Once destroy has completed, all data is lost.\n");
84 printf(" Be careful when using this command.\n");
85 printf("Options:\n");
86 printf(" -f do not ask for confirmation, destroy right away\n");
87 } else if (strcmp(argv[1], "lock") == 0) {
88 printf("Usage: monetdb lock database [database ...]\n");
89 printf(" Puts the given database in maintenance mode. A database\n");
90 printf(" under maintenance can only be connected to by the DBA.\n");
91 printf(" A database which is under maintenance is not started\n");
92 printf(" automatically. Use the \"release\" command to bring\n");
93 printf(" the database back for normal usage.\n");
94 } else if (strcmp(argv[1], "release") == 0) {
95 printf("Usage: monetdb release database [database ...]\n");
96 printf(" Brings back a database from maintenance mode. A released\n");
97 printf(" database is available again for normal use. Use the\n");
98 printf(" \"lock\" command to take a database under maintenance.\n");
99 } else if (strcmp(argv[1], "profilerstart") == 0) {
100 printf("Usage: monetdb profilerstart database [database ...]\n");
101 printf(" Starts the collection of profiling events. The property\n");
102 printf(" \""PROFILERLOGPROPERTY"\" should be set. Use the \"profilerstop\"\n");
103 printf(" command to stop the profiler.\n");
104 } else if (strcmp(argv[1], "profilerstop") == 0) {
105 printf("Usage: monetdb profilerstop database [database ...]\n");
106 printf(" Stops the collection of profiling events.\n");
107 } else if (strcmp(argv[1], "status") == 0) {
108 printf("Usage: monetdb status [-lc] [expression ...]\n");
109 printf(" Shows the state of a given glob-style database match, or\n");
110 printf(" all known if none given. Instead of the normal mode, a\n");
111 printf(" long and crash mode control what information is displayed.\n");
112 printf("Options:\n");
113 printf(" -l extended information listing\n");
114 printf(" -c crash statistics listing\n");
115 printf(" -s only show databases matching a state, combination\n");
116 printf(" possible from r (running), s (stopped), c (crashed)\n");
117 printf(" b (booting) and l (locked).\n");
118 } else if (strcmp(argv[1], "start") == 0) {
119 printf("Usage: monetdb start [-a] database [database ...]\n");
120 printf(" Starts the given database, if the MonetDB Database Server\n");
121 printf(" is running.\n");
122 printf("Options:\n");
123 printf(" -a start all known databases\n");
124 } else if (strcmp(argv[1], "stop") == 0) {
125 printf("Usage: monetdb stop [-a] database [database ...]\n");
126 printf(" Stops the given database, if the MonetDB Database Server\n");
127 printf(" is running.\n");
128 printf("Options:\n");
129 printf(" -a stop all known databases\n");
130 } else if (strcmp(argv[1], "kill") == 0) {
131 printf("Usage: monetdb kill [-a] database [database ...]\n");
132 printf(" Kills the given database, if the MonetDB Database Server\n");
133 printf(" is running. Note: killing a database should only be done\n");
134 printf(" as last resort to stop a database. A database being\n");
135 printf(" killed may end up with data loss.\n");
136 printf("Options:\n");
137 printf(" -a kill all known databases\n");
138 } else if (strcmp(argv[1], "set") == 0) {
139 printf("Usage: monetdb set property=value database [database ...]\n");
140 printf(" sets property to value for the given database\n");
141 printf(" for a list of properties, use `monetdb get all`\n");
142 } else if (strcmp(argv[1], "get") == 0) {
143 printf("Usage: monetdb get <\"all\" | property,...> [database ...]\n");
144 printf(" gets value for property for the given database, or\n");
145 printf(" retrieves all properties for the given database\n");
146 } else if (strcmp(argv[1], "inherit") == 0) {
147 printf("Usage: monetdb inherit property database [database ...]\n");
148 printf(" unsets property, reverting to its inherited value from\n");
149 printf(" the default configuration for the given database\n");
150 } else if (strcmp(argv[1], "discover") == 0) {
151 printf("Usage: monetdb discover [expression]\n");
152 printf(" Lists the remote databases discovered by the MonetDB\n");
153 printf(" Database Server. Databases in this list can be connected\n");
154 printf(" to as well. If expression is given, all entries are\n");
155 printf(" matched against a limited glob-style expression.\n");
156 } else if (strcmp(argv[1], "master") == 0) {
157 printf("Usage: monetdb master <dbname> [path]\n");
158 printf(" Sets the database <dbname> into master mode.\n");
159 printf(" This will actually stop the database take a snapshot\n");
160 printf(" set the server into master mode and restart it.\n");
161 } else if (strcmp(argv[1], "replica") == 0) {
162 printf("Usage: monetdb replica <dbname> <mastername>\n");
163 printf(" Creates a new replica with name <dbname> from the\n");
164 printf(" database <mastername> The database <mastername> must \n");
165 printf(" have been declared as master with the \"monetdb master\"\n");
166 printf(" command.\n");
167 } else if (strcmp(argv[1], "help") == 0) {
168 printf("Yeah , help on help, how desparate can you be? ;)\n");
169 } else if (strcmp(argv[1], "version") == 0) {
170 printf("Usage: monetdb version\n");
171 printf(" prints the version of this monetdb utility\n");
172 } else {
173 printf("help: unknown command: %s\n", argv[1]);
174 }
175}
176
177static void
178command_version(void)
179{
180 printf("MonetDB Database Server Toolkit v%s", VERSION);
181#ifdef MONETDB_RELEASE
182 printf(" (%s)", MONETDB_RELEASE);
183#else
184 const char *rev = mercurial_revision();
185 if (strcmp(rev, "Unknown") != 0)
186 printf(" (hg id: %s)", rev);
187#endif
188 printf("\n");
189}
190
191static int
192cmpsabdb(const void *p1, const void *p2)
193{
194 const sabdb *q1 = *(sabdb* const*)p1;
195 const sabdb *q2 = *(sabdb* const*)p2;
196
197 return strcmp(q1->dbname, q2->dbname);
198}
199
200/**
201 * Helper function to perform the equivalent of
202 * msab_getStatus(&stats, x) but over the network.
203 */
204static char *
205MEROgetStatus(sabdb **ret, char *database)
206{
207 sabdb *orig;
208 sabdb *stats;
209 sabdb *w = NULL;
210 size_t swlen = 50;
211 size_t swpos = 0;
212 sabdb **sw;
213 char *p;
214 char *buf;
215 char *e;
216
217 if (database == NULL)
218 database = "#all";
219
220 e = control_send(&buf, mero_host, mero_port,
221 database, "status", 1, mero_pass);
222 if (e != NULL)
223 return(e);
224
225 sw = malloc(sizeof(sabdb *) * swlen);
226 orig = NULL;
227 if ((p = strtok(buf, "\n")) != NULL) {
228 if (strcmp(p, "OK") != 0) {
229 p = strdup(p);
230 free(buf);
231 free(sw);
232 return(p);
233 }
234 for (swpos = 0; (p = strtok(NULL, "\n")) != NULL; swpos++) {
235 e = msab_deserialise(&stats, p);
236 if (e != NULL) {
237 printf("WARNING: failed to parse response from "
238 "monetdbd: %s\n", e);
239 free(e);
240 swpos--;
241 continue;
242 }
243 if (swpos == swlen)
244 sw = realloc(sw, sizeof(sabdb *) * (swlen = swlen * 2));
245 sw[swpos] = stats;
246 }
247 }
248
249 free(buf);
250
251 if (swpos > 1) {
252 qsort(sw, swpos, sizeof(sabdb *), cmpsabdb);
253 orig = w = sw[0];
254 for (swlen = 1; swlen < swpos; swlen++)
255 w = w->next = sw[swlen];
256 } else if (swpos == 1) {
257 orig = sw[0];
258 orig->next = NULL;
259 }
260
261 free(sw);
262
263 *ret = orig;
264 return(NULL);
265}
266
267static void
268printStatus(sabdb *stats, int mode, int dbwidth, int uriwidth)
269{
270 sabuplog uplog;
271 char *e;
272
273 if ((e = msab_getUplogInfo(&uplog, stats)) != NULL) {
274 fprintf(stderr, "status: internal error: %s\n", e);
275 free(e);
276 return;
277 }
278
279 if (mode == 1) {
280 /* short one-line (default) mode */
281 char state = '\0';
282 char locked = '\0';
283 char uptime[12];
284 char avg[8];
285 char info[32];
286 char *dbname;
287 char *uri;
288
289 switch (stats->state) {
290 case SABdbStarting:
291 state = 'B';
292 break;
293 case SABdbRunning:
294 state = 'R';
295 break;
296 case SABdbCrashed:
297 state = 'C';
298 break;
299 case SABdbInactive:
300 state = 'S';
301 break;
302 default:
303 state = ' ';
304 break;
305 }
306 /* override if locked for brevity */
307 if (stats->locked)
308 locked = 'L';
309
310 info[0] = '\0';
311 if (stats->state == SABdbStarting) {
312 struct tm *t;
313 t = localtime(&uplog.laststart);
314 strftime(info, sizeof(info), "starting up since %Y-%m-%d %H:%M:%S", t);
315 } else if (uplog.lastcrash != -1 &&
316 stats->state != SABdbRunning &&
317 uplog.crashavg1 == 1)
318 {
319 struct tm *t;
320 t = localtime(&uplog.lastcrash);
321 strftime(info, sizeof(info), "crashed on %Y-%m-%d %H:%M:%S", t);
322 }
323
324 switch (stats->state) {
325 case SABdbRunning:
326 case SABdbStarting:
327 secondsToString(uptime, time(NULL) - uplog.laststart, 1);
328 break;
329 case SABdbCrashed:
330 secondsToString(uptime, time(NULL) - uplog.lastcrash, 1);
331 break;
332 case SABdbInactive:
333 if (uplog.laststop != -1) {
334 secondsToString(uptime, time(NULL) - uplog.laststop, 1);
335 break;
336 } /* else fall through */
337 default:
338 uptime[0] = '\0';
339 break;
340 }
341
342 /* cut too long names */
343 dbname = malloc(sizeof(char) * (dbwidth + 1));
344 abbreviateString(dbname, stats->dbname, dbwidth);
345 uri = malloc(sizeof(char) * (uriwidth + 1));
346 abbreviateString(uri,
347 info[0] != '\0' ? info : stats->uri ? stats->uri : "",
348 uriwidth);
349 /* dbname | state | health | uri/crash */
350 printf("%-*s %c%c%3s", dbwidth, dbname,
351 locked ? locked : state, locked ? state : ' ', uptime);
352 free(dbname);
353 if (uplog.startcntr) {
354 secondsToString(avg, uplog.avguptime, 1);
355 printf(" %3d%% %3s",
356 100 - (uplog.crashcntr * 100 / uplog.startcntr), avg);
357 } else {
358 printf(" ");
359 }
360 printf(" %-*s\n", uriwidth, uri);
361 free(uri);
362 } else if (mode == 2) {
363 /* long mode */
364 char *state;
365 sablist *entry;
366 char up[32];
367 struct tm *t;
368
369 switch (stats->state) {
370 case SABdbStarting:
371 state = "starting up";
372 break;
373 case SABdbRunning:
374 state = "running";
375 break;
376 case SABdbCrashed:
377 state = "crashed";
378 break;
379 case SABdbInactive:
380 state = "stopped";
381 break;
382 default:
383 state = "unknown";
384 break;
385 }
386
387 printf("%s:\n", stats->dbname);
388 printf(" connection uri: %s\n", stats->uri);
389 printf(" database name: %s\n", stats->dbname);
390 printf(" state: %s\n", state);
391 printf(" locked: %s\n", stats->locked ? "yes" : "no");
392 entry = stats->scens;
393 printf(" scenarios:");
394 if (entry == NULL) {
395 printf(" (none)");
396 } else while (entry != NULL) {
397 printf(" %s", entry->val);
398 entry = entry->next;
399 }
400 printf("\n");
401 printf(" start count: %d\n stop count: %d\n crash count: %d\n",
402 uplog.startcntr, uplog.stopcntr, uplog.crashcntr);
403 if (stats->state == SABdbRunning) {
404 secondsToString(up, time(NULL) - uplog.laststart, 999);
405 printf(" current uptime: %s\n", up);
406 }
407 secondsToString(up, uplog.avguptime, 999);
408 printf(" average uptime: %s\n", up);
409 secondsToString(up, uplog.maxuptime, 999);
410 printf(" maximum uptime: %s\n", up);
411 secondsToString(up, uplog.minuptime, 999);
412 printf(" minimum uptime: %s\n", up);
413 if (uplog.lastcrash != -1) {
414 t = localtime(&uplog.lastcrash);
415 strftime(up, 32, "%Y-%m-%d %H:%M:%S", t);
416 } else {
417 sprintf(up, "(unknown)");
418 }
419 printf(" last start with crash: %s\n", up);
420 if (uplog.laststart != -1) {
421 t = localtime(&uplog.laststart);
422 strftime(up, 32, "%Y-%m-%d %H:%M:%S", t);
423 } else {
424 sprintf(up, "(unknown)");
425 }
426 printf(" last start: %s\n", up);
427 if (uplog.laststop != -1) {
428 t = localtime(&uplog.laststop);
429 strftime(up, 32, "%Y-%m-%d %H:%M:%S", t);
430 } else {
431 sprintf(up, "(unknown)");
432 }
433 printf(" last stop: %s\n", up);
434 printf(" average of crashes in the last start attempt: %d\n",
435 uplog.crashavg1);
436 printf(" average of crashes in the last 10 start attempts: %.2f\n",
437 uplog.crashavg10);
438 printf(" average of crashes in the last 30 start attempts: %.2f\n",
439 uplog.crashavg30);
440 } else {
441 /* this shows most used properties, and is shown also for modes
442 * that are added but we don't understand (yet) */
443 char buf[64];
444 char up[32];
445 char min[8], avg[8], max[8];
446 struct tm *t;
447 size_t off = 0;
448 /* dbname, status -- since, crash averages */
449
450 switch (stats->state) {
451 case SABdbStarting:
452 snprintf(buf, sizeof(buf), "starting ");
453 off = sizeof("starting ") - 1;
454 /* fall through */
455 case SABdbRunning:
456 t = localtime(&uplog.laststart);
457 strftime(buf + off, sizeof(buf) - off,
458 "up since %Y-%m-%d %H:%M:%S, ", t);
459 secondsToString(up, time(NULL) - uplog.laststart, 999);
460 strcat(buf, up);
461 break;
462 case SABdbCrashed:
463 t = localtime(&uplog.lastcrash);
464 strftime(buf, sizeof(buf), "crashed on %Y-%m-%d %H:%M:%S", t);
465 break;
466 case SABdbInactive:
467 snprintf(buf, sizeof(buf), "not running");
468 break;
469 default:
470 snprintf(buf, sizeof(buf), "unknown");
471 break;
472 }
473 if (stats->locked)
474 strcat(buf, ", locked");
475 printf("database %s, %s\n", stats->dbname, buf);
476 printf(" crash average: %d.00 %.2f %.2f (over 1, 15, 30 starts) "
477 "in total %d crashes\n",
478 uplog.crashavg1, uplog.crashavg10, uplog.crashavg30,
479 uplog.crashcntr);
480 secondsToString(min, uplog.minuptime, 1);
481 secondsToString(avg, uplog.avguptime, 1);
482 secondsToString(max, uplog.maxuptime, 1);
483 printf(" uptime stats (min/avg/max): %s/%s/%s over %d runs\n",
484 min, avg, max, uplog.stopcntr);
485 }
486}
487
488static sabdb *
489globMatchDBS(int argc, char *argv[], sabdb **orig, char *cmd)
490{
491 sabdb *w = NULL;
492 sabdb *top = NULL;
493 sabdb *prev;
494 sabdb *stats;
495 int i;
496 char matched;
497
498 for (i = 1; i < argc; i++) {
499 matched = 0;
500 if (argv[i] != NULL) {
501 prev = NULL;
502 for (stats = *orig; stats != NULL; stats = stats->next) {
503 if (db_glob(argv[i], stats->dbname)) {
504 matched = 1;
505 /* move out of orig into w, such that we can't
506 * get double matches in the same output list
507 * (as side effect also avoids a double free
508 * lateron) */
509 if (w == NULL) {
510 top = w = stats;
511 } else {
512 w = w->next = stats;
513 }
514 if (prev == NULL) {
515 *orig = stats->next;
516 /* little hack to revisit the now top of the
517 * list */
518 w->next = *orig;
519 stats = w;
520 continue;
521 } else {
522 prev->next = stats->next;
523 stats = prev;
524 }
525 }
526 prev = stats;
527 }
528 if (w != NULL)
529 w->next = NULL;
530 if (matched == 0) {
531 fprintf(stderr, "%s: no such database: %s\n", cmd, argv[i]);
532 argv[i] = NULL;
533 }
534 }
535 }
536 return(top);
537}
538
539/**
540 * Helper function to run over the sabdb list and perform merocmd for
541 * the value and reporting status on the performed command. Either a
542 * message is printed when success, or when premsg is not NULL, premsg
543 * is printed before the action, and "done" printed afterwards.
544 */
545static void
546simple_argv_cmd(char *cmd, sabdb *dbs, char *merocmd,
547 char *successmsg, char *premsg)
548{
549 int state = 0; /* return status */
550 int hadwork = 0; /* if we actually did something */
551 char *ret;
552 char *out;
553
554 /* do for each listed database */
555 for (; dbs != NULL; dbs = dbs->next) {
556 if (premsg != NULL && !monetdb_quiet) {
557 printf("%s '%s'... ", premsg, dbs->dbname);
558 fflush(stdout);
559 }
560
561 ret = control_send(&out, mero_host, mero_port,
562 dbs->dbname, merocmd, 0, mero_pass);
563
564 if (ret != NULL) {
565 if (premsg != NULL && !monetdb_quiet)
566 printf("FAILED\n");
567 fprintf(stderr, "%s: %s\n",
568 cmd, ret);
569 free(ret);
570 exit(2);
571 }
572
573 if (strcmp(out, "OK") == 0) {
574 if (!monetdb_quiet) {
575 if (premsg != NULL) {
576 printf("done\n");
577 } else {
578 printf("%s: %s\n", successmsg, dbs->dbname);
579 }
580 }
581 } else {
582 if (premsg != NULL && !monetdb_quiet)
583 printf("FAILED\n");
584 fprintf(stderr, "%s: %s\n", cmd, out);
585 free(out);
586
587 state |= 1;
588 }
589
590 hadwork = 1;
591 }
592
593 if (hadwork == 0) {
594 char *argv[2] = { "monetdb", cmd };
595 command_help(2, argv);
596 exit(1);
597 }
598
599 if (state != 0)
600 exit(state);
601}
602
603/**
604 * Helper function for commands in their most general form: no option
605 * flags and just pushing all (database) arguments over to merovingian
606 * for performing merocmd action.
607 */
608static void
609simple_command(int argc, char *argv[], char *merocmd, char *successmsg, char glob)
610{
611 int i;
612 sabdb *orig = NULL;
613 sabdb *stats = NULL;
614 char *e;
615
616 if (argc == 1) {
617 /* print help message for this command */
618 command_help(2, &argv[-1]);
619 exit(1);
620 }
621
622 /* walk through the arguments and hunt for "options" */
623 for (i = 1; i < argc; i++) {
624 if (strcmp(argv[i], "--") == 0) {
625 argv[i] = NULL;
626 break;
627 }
628 if (argv[i][0] == '-') {
629 fprintf(stderr, "%s: unknown option: %s\n", argv[0], argv[i]);
630 command_help(argc + 1, &argv[-1]);
631 exit(1);
632 }
633 }
634
635 if (glob) {
636 if ((e = MEROgetStatus(&orig, NULL)) != NULL) {
637 fprintf(stderr, "%s: %s\n", argv[0], e);
638 free(e);
639 exit(2);
640 }
641 stats = globMatchDBS(argc, argv, &orig, argv[0]);
642 msab_freeStatus(&orig);
643 orig = stats;
644
645 if (orig == NULL)
646 exit(1);
647 } else {
648 for (i = 1; i < argc; i++) {
649 if (argv[i] != NULL) {
650 /* maintain input order */
651 if (orig == NULL) {
652 stats = orig = calloc(1, sizeof(sabdb));
653 } else {
654 stats = stats->next = calloc(1, sizeof(sabdb));
655 }
656 stats->dbname = strdup(argv[i]);
657 }
658 }
659 }
660
661 simple_argv_cmd(argv[0], orig, merocmd, successmsg, NULL);
662 msab_freeStatus(&orig);
663}
664
665static void
666command_status(int argc, char *argv[])
667{
668 int doall = 1; /* we default to showing all */
669 int mode = 1; /* 0=crash, 1=short, 2=long */
670 char *state = "rbscl"; /* contains states to show */
671 int i;
672 char *p;
673 char *e;
674 sabdb *stats;
675 sabdb *orig;
676 sabdb *prev;
677 sabdb *neworig = NULL;
678 int t;
679 int twidth = TERMWIDTH;
680 int dbwidth = 0;
681 int uriwidth = 0;
682
683 if (argc == 0) {
684 exit(2);
685 }
686
687 /* time to collect some option flags */
688 for (i = 1; i < argc; i++) {
689 if (argv[i][0] == '-') {
690 for (p = argv[i] + 1; *p != '\0'; p++) {
691 switch (*p) {
692 case 'c':
693 mode = 0;
694 break;
695 case 'l':
696 mode = 2;
697 break;
698 case 's':
699 if (*(p + 1) != '\0') {
700 state = ++p;
701 } else if (i + 1 < argc && argv[i + 1][0] != '-') {
702 state = argv[++i];
703 } else {
704 fprintf(stderr, "status: -s needs an argument\n");
705 command_help(2, &argv[-1]);
706 exit(1);
707 }
708 for (p = state; *p != '\0'; p++) {
709 switch (*p) {
710 case 'b': /* booting (starting up) */
711 case 'r': /* running (started) */
712 case 's': /* stopped */
713 case 'c': /* crashed */
714 case 'l': /* locked */
715 break;
716 default:
717 fprintf(stderr, "status: unknown flag for -s: -%c\n", *p);
718 command_help(2, &argv[-1]);
719 exit(1);
720 }
721 }
722 p--;
723 break;
724 case '-':
725 if (p[1] == '\0') {
726 if (argc - 1 > i)
727 doall = 0;
728 i = argc;
729 break;
730 }
731 /* fall through */
732 default:
733 fprintf(stderr, "status: unknown option: -%c\n", *p);
734 command_help(2, &argv[-1]);
735 exit(1);
736 }
737 }
738 /* make this option no longer available, for easy use
739 * lateron */
740 argv[i] = NULL;
741 } else {
742 doall = 0;
743 }
744 }
745
746 if ((e = MEROgetStatus(&orig, NULL)) != NULL) {
747 fprintf(stderr, "status: %s\n", e);
748 free(e);
749 exit(2);
750 }
751
752 /* look at the arguments and evaluate them based on a glob (hence we
753 * listed all databases before) */
754 if (doall != 1) {
755 stats = globMatchDBS(argc, argv, &orig, "status");
756 msab_freeStatus(&orig);
757 orig = stats;
758 }
759
760 /* perform selection based on state (and order at the same time) */
761 for (p = &state[strlen(state) - 1]; p >= state; p--) {
762 bool curLock = false;
763 SABdbState curMode = SABdbIllegal;
764 switch (*p) {
765 case 'b':
766 curMode = SABdbStarting;
767 break;
768 case 'r':
769 curMode = SABdbRunning;
770 break;
771 case 's':
772 curMode = SABdbInactive;
773 break;
774 case 'c':
775 curMode = SABdbCrashed;
776 break;
777 case 'l':
778 curLock = true;
779 break;
780 }
781 stats = orig;
782 prev = NULL;
783 while (stats != NULL) {
784 if (stats->locked == curLock &&
785 (curLock ||
786 (!curLock && stats->state == curMode)))
787 {
788 sabdb *next = stats->next;
789 stats->next = neworig;
790 neworig = stats;
791 if (prev == NULL) {
792 orig = next;
793 } else {
794 prev->next = next;
795 }
796 stats = next;
797 } else {
798 prev = stats;
799 stats = stats->next;
800 }
801 }
802 }
803 msab_freeStatus(&orig);
804 orig = neworig;
805
806 if (mode == 1 && orig != NULL) {
807 int len = 0;
808
809 /* calculate dbwidth and uriwidth */
810 for (stats = orig; stats != NULL; stats = stats->next) {
811 if ((t = strlen(stats->dbname)) > dbwidth)
812 dbwidth = t;
813 if (stats->uri != NULL && (t = strlen(stats->uri)) > uriwidth)
814 uriwidth = t;
815 if (uriwidth < 32)
816 uriwidth = 32;
817 }
818
819 /* Ultra Condensed State(tm) since Feb2013:
820 state
821 R 6s (Running)
822 R 14w
823 R 99y (purely hypothetical)
824 B 3s (Booting: in practice cannot be observed yet due to lock)
825 S 1w (Stopped)
826 LR12h (Locked/Running)
827 LS (Locked/Stopped)
828 C (Crashed)
829 = 5 chars
830 */
831
832 /* health
833 health
834 100% 12d
835 42% 4s
836 = 8 chars
837 */
838
839 len = (dbwidth < 4 ? 4 : dbwidth) + 2 + 5 + 2 + 8 + 2 + uriwidth;
840 if (twidth > 0 && len > twidth) {
841 if (len - twidth < 10) {
842 uriwidth -= len - twidth;
843 if (dbwidth < 4)
844 dbwidth = 4;
845 } else {
846 /* reduce relative to usage */
847 if (dbwidth < 4) {
848 dbwidth = 4;
849 } else {
850 dbwidth = (int)(dbwidth * 1.0 / (dbwidth + uriwidth) * (len - twidth));
851 if (dbwidth < 4)
852 dbwidth = 4;
853 }
854 uriwidth = twidth - (dbwidth + 2 + 5 + 2 + 8 + 2);
855 if (uriwidth < 8)
856 uriwidth = 8;
857 }
858 } else {
859 if (dbwidth < 4)
860 dbwidth = 4;
861 }
862
863 /* print header */
864 printf("%*sname%*s state health %*sremarks\n",
865 (dbwidth - 4) / 2, "", (dbwidth - 4 + 1) / 2, "",
866 (uriwidth - 7) / 2, "");
867 }
868
869 for (stats = orig; stats != NULL; stats = stats->next)
870 printStatus(stats, mode, dbwidth, uriwidth);
871
872 if (orig != NULL)
873 msab_freeStatus(&orig);
874}
875
876static int
877cmpurl(const void *p1, const void *p2)
878{
879 const char *q1 = *(char* const*)p1;
880 const char *q2 = *(char* const*)p2;
881
882 if (strncmp("mapi:monetdb://", q1, 15) == 0)
883 q1 += 15;
884 if (strncmp("mapi:monetdb://", q2, 15) == 0)
885 q2 += 15;
886 return strcmp(q1, q2);
887}
888
889static void
890command_discover(int argc, char *argv[])
891{
892 char path[8096];
893 char *buf;
894 char *p, *q;
895 size_t twidth = TERMWIDTH;
896 char *location = NULL;
897 char *match = NULL;
898 size_t numlocs = 50;
899 size_t posloc = 0;
900 size_t loclen = 0;
901 char **locations = malloc(sizeof(char*) * numlocs);
902
903 if (argc == 0) {
904 exit(2);
905 } else if (argc > 2) {
906 /* print help message for this command */
907 command_help(2, &argv[-1]);
908 exit(1);
909 } else if (argc == 2) {
910 match = argv[1];
911 }
912
913 /* Send the pass phrase to unlock the information available in
914 * merovingian. Anelosimus eximius is a social species of spiders,
915 * which help each other, just like merovingians do among each
916 * other. */
917 p = control_send(&buf, mero_host, mero_port,
918 "anelosimus", "eximius", 1, mero_pass);
919 if (p != NULL) {
920 printf("%s: %s\n", argv[0], p);
921 free(p);
922 exit(2);
923 }
924
925 if ((p = strtok(buf, "\n")) != NULL) {
926 if (strcmp(p, "OK") != 0) {
927 fprintf(stderr, "%s: %s\n", argv[0], p);
928 exit(1);
929 }
930 if (twidth > 0)
931 location = malloc(twidth + 1);
932 while ((p = strtok(NULL, "\n")) != NULL) {
933 if ((q = strchr(p, '\t')) == NULL) {
934 /* doesn't look correct */
935 printf("%s: WARNING: discarding incorrect line: %s\n",
936 argv[0], p);
937 continue;
938 }
939 *q++ = '\0';
940
941 snprintf(path, sizeof(path), "%s%s", q, p);
942
943 if (match == NULL || db_glob(match, path)) {
944 if (twidth > 0) {
945 /* cut too long location name */
946 abbreviateString(location, path, twidth);
947 } else {
948 location = path;
949 }
950 /* store what we found */
951 if (posloc == numlocs)
952 locations = realloc(locations,
953 sizeof(char *) * (numlocs = numlocs * 2));
954 locations[posloc++] = strdup(location);
955 if (strlen(location) > loclen)
956 loclen = strlen(location);
957 }
958 }
959 if (twidth > 0)
960 free(location);
961 }
962
963 free(buf);
964
965 if (posloc > 0) {
966 printf("%*slocation\n",
967 (int)(loclen - 8 /* "location" */ - ((loclen - 8) / 2)), "");
968 qsort(locations, posloc, sizeof(char *), cmpurl);
969 for (loclen = 0; loclen < posloc; loclen++) {
970 printf("%s\n", locations[loclen]);
971 free(locations[loclen]);
972 }
973 }
974
975 free(locations);
976}
977
978typedef enum {
979 START = 0,
980 STOP,
981 KILL
982} startstop;
983
984static void
985command_startstop(int argc, char *argv[], startstop mode)
986{
987 int doall = 0;
988 int i;
989 char *e;
990 sabdb *orig = NULL;
991 sabdb *stats;
992 sabdb *prev;
993 char *type = NULL;
994 char *action = NULL;
995 char *p;
996 char *nargv[64];
997
998 switch (mode) {
999 case START:
1000 type = "start";
1001 action = "starting database";
1002 break;
1003 case STOP:
1004 type = "stop";
1005 action = "stopping database";
1006 break;
1007 case KILL:
1008 type = "kill";
1009 action = "killing database";
1010 break;
1011 }
1012
1013 if (argc == 1) {
1014 /* print help message for this command */
1015 command_help(2, &argv[-1]);
1016 exit(1);
1017 } else if (argc == 0) {
1018 exit(2);
1019 }
1020
1021 /* time to collect some option flags */
1022 for (i = 1; i < argc; i++) {
1023 if (argv[i][0] == '-') {
1024 for (p = argv[i] + 1; *p != '\0'; p++) {
1025 switch (*p) {
1026 case 'a':
1027 doall = 1;
1028 break;
1029 case '-':
1030 if (p[1] == '\0') {
1031 if (argc - 1 > i)
1032 doall = 0;
1033 i = argc;
1034 break;
1035 }
1036 /* fall through */
1037 default:
1038 fprintf(stderr, "%s: unknown option: -%c\n", type, *p);
1039 command_help(2, &argv[-1]);
1040 exit(1);
1041 break;
1042 }
1043 }
1044 /* make this option no longer available, for easy use
1045 * lateron */
1046 argv[i] = NULL;
1047 }
1048 }
1049
1050 if ((e = MEROgetStatus(&orig, NULL)) != NULL) {
1051 fprintf(stderr, "%s: %s\n", type, e);
1052 free(e);
1053 exit(2);
1054 }
1055 if (!doall) {
1056 stats = globMatchDBS(argc, argv, &orig, type);
1057 msab_freeStatus(&orig);
1058 orig = stats;
1059 }
1060
1061 argv = nargv;
1062 i = 0;
1063 argv[i++] = type;
1064
1065 stats = orig;
1066 prev = NULL;
1067 while (stats != NULL) {
1068 /* When -a was given, we're supposed to start all known
1069 * databases. In this mode we should omit starting already
1070 * started databases, so we need to check first. */
1071
1072 if (doall && (
1073 ((mode == STOP || mode == KILL) && (stats->state != SABdbRunning && stats->state != SABdbStarting))
1074 || (mode == START && stats->state == SABdbRunning)))
1075 {
1076 /* needs not to be started/stopped, remove from list */
1077 if (prev == NULL) {
1078 orig = stats->next;
1079 } else {
1080 prev->next = stats->next;
1081 }
1082 stats->next = NULL;
1083 msab_freeStatus(&stats);
1084 if (prev == NULL) {
1085 stats = orig;
1086 continue;
1087 }
1088 stats = prev;
1089 }
1090 prev = stats;
1091 stats = stats->next;
1092 }
1093
1094 if (orig != NULL) {
1095 simple_argv_cmd(argv[0], orig, type, NULL, action);
1096 msab_freeStatus(&orig);
1097 }
1098
1099 return;
1100}
1101
1102typedef enum {
1103 SET = 0,
1104 INHERIT
1105} meroset;
1106
1107static _Noreturn void command_set(int argc, char *argv[], meroset type);
1108
1109static void
1110command_set(int argc, char *argv[], meroset type)
1111{
1112 char *p = NULL;
1113 char property[24] = "";
1114 int i;
1115 int state = 0;
1116 char *res;
1117 char *out;
1118 sabdb *orig = NULL;
1119 sabdb *stats = NULL;
1120 char *e;
1121
1122 if (argc >= 1 && argc <= 2) {
1123 /* print help message for this command */
1124 command_help(2, &argv[-1]);
1125 exit(1);
1126 } else if (argc == 0) {
1127 exit(2);
1128 }
1129
1130 /* time to collect some option flags */
1131 for (i = 1; i < argc; i++) {
1132 if (argv[i][0] == '-') {
1133 for (p = argv[i] + 1; *p != '\0'; p++) {
1134 switch (*p) {
1135 case '-':
1136 if (p[1] == '\0') {
1137 i = argc;
1138 break;
1139 }
1140 /* fall through */
1141 default:
1142 fprintf(stderr, "%s: unknown option: -%c\n",
1143 argv[0], *p);
1144 command_help(2, &argv[-1]);
1145 exit(1);
1146 break;
1147 }
1148 }
1149 /* make this option no longer available, for easy use
1150 * lateron */
1151 argv[i] = NULL;
1152 } else if (property[0] == '\0') {
1153 /* first non-option is property, rest is database */
1154 p = argv[i];
1155 if (type == SET) {
1156 if ((p = strchr(argv[i], '=')) == NULL) {
1157 fprintf(stderr, "set: need property=value\n");
1158 command_help(2, &argv[-1]);
1159 exit(1);
1160 }
1161 *p = '\0';
1162 snprintf(property, sizeof(property), "%s", argv[i]);
1163 *p++ = '=';
1164 p = argv[i];
1165 } else {
1166 snprintf(property, sizeof(property), "%s", argv[i]);
1167 }
1168 argv[i] = NULL;
1169 }
1170 }
1171
1172 if (property[0] == '\0') {
1173 fprintf(stderr, "%s: need a property argument\n", argv[0]);
1174 command_help(2, &argv[-1]);
1175 exit(1);
1176 }
1177
1178 if ((e = MEROgetStatus(&orig, NULL)) != NULL) {
1179 fprintf(stderr, "%s: %s\n", argv[0], e);
1180 free(e);
1181 exit(2);
1182 }
1183 stats = globMatchDBS(argc, argv, &orig, argv[0]);
1184 msab_freeStatus(&orig);
1185 orig = stats;
1186
1187 if (orig == NULL) {
1188 /* error already printed by globMatchDBS */
1189 exit(1);
1190 }
1191
1192 /* handle rename separately due to single argument constraint */
1193 if (strcmp(property, "name") == 0) {
1194 if (type == INHERIT) {
1195 fprintf(stderr, "inherit: cannot default to a database name\n");
1196 exit(1);
1197 }
1198
1199 if (orig->next != NULL) {
1200 fprintf(stderr, "%s: cannot rename multiple databases to "
1201 "the same name\n", argv[0]);
1202 exit(1);
1203 }
1204
1205 out = control_send(&res, mero_host, mero_port,
1206 orig->dbname, p, 0, mero_pass);
1207 if (out != NULL || strcmp(res, "OK") != 0) {
1208 res = out == NULL ? res : out;
1209 fprintf(stderr, "%s: %s\n", argv[0], res);
1210 state |= 1;
1211 }
1212 free(res);
1213
1214 msab_freeStatus(&orig);
1215 exit(state);
1216 }
1217
1218 for (stats = orig; stats != NULL; stats = stats->next) {
1219 if (type == INHERIT) {
1220 strncat(property, "=", sizeof(property) - strlen(property) - 1);
1221 p = property;
1222 }
1223 out = control_send(&res, mero_host, mero_port,
1224 stats->dbname, p, 0, mero_pass);
1225 if (out != NULL || strcmp(res, "OK") != 0) {
1226 res = out == NULL ? res : out;
1227 fprintf(stderr, "%s: %s\n", argv[0], res);
1228 state |= 1;
1229 }
1230 free(res);
1231 }
1232
1233 msab_freeStatus(&orig);
1234 exit(state);
1235}
1236
1237static void
1238command_get(int argc, char *argv[])
1239{
1240 char doall = 1;
1241 char *p;
1242 char *property = NULL;
1243 char propall = 0;
1244 char vbuf[512];
1245 char *buf = 0;
1246 char *e;
1247 int i;
1248 sabdb *orig, *stats;
1249 int twidth = TERMWIDTH;
1250 char *source, *value = NULL;
1251 confkeyval *kv;
1252 confkeyval *defprops = getDefaultProps();
1253 confkeyval *props = getDefaultProps();
1254
1255 if (argc == 1) {
1256 /* print help message for this command */
1257 command_help(2, &argv[-1]);
1258 exit(1);
1259 } else if (argc == 0) {
1260 exit(2);
1261 }
1262
1263 /* time to collect some option flags */
1264 for (i = 1; i < argc; i++) {
1265 if (argv[i][0] == '-') {
1266 for (p = argv[i] + 1; *p != '\0'; p++) {
1267 switch (*p) {
1268 case '-':
1269 if (p[1] == '\0') {
1270 if (argc - 1 > i)
1271 doall = 0;
1272 i = argc;
1273 break;
1274 }
1275 /* fall through */
1276 default:
1277 fprintf(stderr, "get: unknown option: -%c\n", *p);
1278 command_help(2, &argv[-1]);
1279 exit(1);
1280 break;
1281 }
1282 }
1283 /* make this option no longer available, for easy use
1284 * lateron */
1285 argv[i] = NULL;
1286 } else if (property == NULL) {
1287 /* first non-option is property, rest is database */
1288 property = argv[i];
1289 argv[i] = NULL;
1290 if (strcmp(property, "all") == 0)
1291 propall = 1;
1292 } else {
1293 doall = 0;
1294 }
1295 }
1296
1297 if (property == NULL) {
1298 fprintf(stderr, "get: need a property argument\n");
1299 command_help(2, &argv[-1]);
1300 exit(1);
1301 }
1302 if ((e = MEROgetStatus(&orig, NULL)) != NULL) {
1303 fprintf(stderr, "get: %s\n", e);
1304 free(e);
1305 exit(2);
1306 }
1307
1308 /* look at the arguments and evaluate them based on a glob (hence we
1309 * listed all databases before) */
1310 if (!doall) {
1311 stats = globMatchDBS(argc, argv, &orig, "get");
1312 msab_freeStatus(&orig);
1313 orig = stats;
1314 }
1315
1316 /* avoid work when there are no results */
1317 if (orig == NULL) {
1318 free(props);
1319 free(defprops);
1320 return;
1321 }
1322
1323 e = control_send(&buf, mero_host, mero_port,
1324 "#defaults", "get", 1, mero_pass);
1325 if (e != NULL) {
1326 fprintf(stderr, "get: %s\n", e);
1327 free(e);
1328 exit(2);
1329 } else if (buf == NULL) {
1330 fprintf(stderr, "get: malloc failed\n");
1331 exit(2);
1332 } else if (strncmp(buf, "OK\n", 3) != 0) {
1333 fprintf(stderr, "get: %s\n", buf);
1334 free(buf);
1335 exit(1);
1336 }
1337 readPropsBuf(defprops, buf + 3);
1338 free(buf);
1339
1340 if (twidth > 0) {
1341 /* name = 15 */
1342 /* prop = 8 */
1343 /* source = 7 */
1344 twidth -= 15 + 2 + 8 + 2 + 7 + 2;
1345 if (twidth < 6)
1346 twidth = 6;
1347 value = malloc(sizeof(char) * twidth + 1);
1348 }
1349 stats = orig;
1350 while (stats != NULL) {
1351 e = control_send(&buf, mero_host, mero_port,
1352 stats->dbname, "get", 1, mero_pass);
1353 if (e != NULL) {
1354 fprintf(stderr, "get: %s\n", e);
1355 free(e);
1356 exit(2);
1357 } else if (buf == NULL) {
1358 fprintf(stderr, "get: malloc failed\n");
1359 exit(2);
1360 } else if (strncmp(buf, "OK\n", 3) != 0) {
1361 fprintf(stderr, "get: %s\n", buf);
1362 free(buf);
1363 exit(1);
1364 }
1365 readPropsBuf(props, buf + 3);
1366 free(buf);
1367
1368 if (propall == 1) {
1369 size_t off = 0;
1370 kv = props;
1371 off += snprintf(vbuf, sizeof(vbuf), "name");
1372 while (kv->key != NULL) {
1373 off += snprintf(vbuf + off, sizeof(vbuf) - off,
1374 ",%s", kv->key);
1375 kv++;
1376 }
1377 } else {
1378 /* check validity of properties before printing them */
1379 if (stats == orig) {
1380 snprintf(vbuf, sizeof(vbuf), "%s", property);
1381 buf = vbuf;
1382 while ((p = strtok(buf, ",")) != NULL) {
1383 buf = NULL;
1384 if (strcmp(p, "name") == 0)
1385 continue;
1386 kv = findConfKey(props, p);
1387 if (kv == NULL)
1388 fprintf(stderr, "get: no such property: %s\n", p);
1389 }
1390 }
1391 snprintf(vbuf, sizeof(vbuf), "%s", property);
1392 }
1393 buf = vbuf;
1394 /* print header after errors */
1395 if (stats == orig)
1396 printf(" name prop source value\n");
1397
1398 while ((p = strtok(buf, ",")) != NULL) {
1399 buf = NULL;
1400
1401 /* filter properties based on object type */
1402 kv = findConfKey(props, "type");
1403 if (kv != NULL && kv->val != NULL) {
1404 if (strcmp(kv->val, "mfunnel") == 0) {
1405 if (strcmp(p, "name") != 0 &&
1406 strcmp(p, "type") != 0 &&
1407 strcmp(p, "mfunnel") != 0 &&
1408 strcmp(p, "shared") != 0)
1409 continue;
1410 }
1411 } else { /* no type == database (default) */
1412 if (strcmp(p, "mfunnel") == 0)
1413 continue;
1414 }
1415
1416 /* special virtual case */
1417 if (strcmp(p, "name") == 0) {
1418 source = "-";
1419 if (twidth > 0) {
1420 abbreviateString(value, stats->dbname, twidth);
1421 } else {
1422 value = stats->dbname;
1423 }
1424 } else {
1425 kv = findConfKey(props, p);
1426 if (kv == NULL)
1427 continue;
1428 if (kv->val == NULL) {
1429 char *y = NULL;
1430 kv = findConfKey(defprops, p);
1431 source = "default";
1432 y = kv != NULL && kv->val != NULL ? kv->val : "<unknown>";
1433 if (twidth > 0) {
1434 abbreviateString(value, y, twidth);
1435 } else {
1436 value = y;
1437 }
1438 } else {
1439 source = "local";
1440 if (twidth > 0) {
1441 abbreviateString(value, kv->val, twidth);
1442 } else {
1443 value = kv->val;
1444 }
1445 }
1446 }
1447
1448 printf("%-15s %-8s %-7s %s\n",
1449 stats->dbname, p, source, value);
1450 }
1451
1452 freeConfFile(props);
1453 stats = stats->next;
1454 }
1455
1456 if (twidth > 0)
1457 free(value);
1458 msab_freeStatus(&orig);
1459 free(props);
1460 free(defprops);
1461}
1462
1463static void
1464command_create(int argc, char *argv[])
1465{
1466 int i;
1467 char *mfunnel = NULL;
1468 char *password = NULL;
1469 sabdb *orig = NULL;
1470 sabdb *stats = NULL;
1471
1472 if (argc == 1) {
1473 /* print help message for this command */
1474 command_help(argc + 1, &argv[-1]);
1475 exit(1);
1476 }
1477
1478 /* walk through the arguments and hunt for "options" */
1479 for (i = 1; i < argc; i++) {
1480 if (strcmp(argv[i], "--") == 0) {
1481 argv[i] = NULL;
1482 break;
1483 }
1484 if (argv[i][0] == '-') {
1485 if (argv[i][1] == 'm') {
1486 if (argv[i][2] != '\0') {
1487 mfunnel = &argv[i][2];
1488 argv[i] = NULL;
1489 } else if (i + 1 < argc && argv[i + 1][0] != '-') {
1490 argv[i] = NULL;
1491 mfunnel = argv[++i];
1492 argv[i] = NULL;
1493 } else {
1494 fprintf(stderr, "create: -m needs an argument\n");
1495 command_help(2, &argv[-1]);
1496 exit(1);
1497 }
1498 } else if (argv[i][1] == 'p') {
1499 if (argv[i][2] != '\0') {
1500 password = &argv[i][2];
1501 argv[i] = NULL;
1502 } else if (i + 1 < argc && argv[i + 1][0] != '-') {
1503 argv[i] = NULL;
1504 password = argv[++i];
1505 argv[i] = NULL;
1506 } else {
1507 fprintf(stderr, "create: -p needs an argument\n");
1508 command_help(2, &argv[-1]);
1509 exit(1);
1510 }
1511 } else {
1512 fprintf(stderr, "create: unknown option: %s\n", argv[i]);
1513 command_help(argc + 1, &argv[-1]);
1514 exit(1);
1515 }
1516 }
1517 }
1518
1519 for (i = 1; i < argc; i++) {
1520 if (argv[i] != NULL) {
1521 /* maintain input order */
1522 if (orig == NULL) {
1523 stats = orig = calloc(1, sizeof(sabdb));
1524 } else {
1525 stats = stats->next = calloc(1, sizeof(sabdb));
1526 }
1527 stats->dbname = strdup(argv[i]);
1528 }
1529 }
1530
1531 if (mfunnel != NULL) {
1532 size_t len = strlen("create mfunnel=") + strlen(mfunnel) + 1;
1533 char *cmd = malloc(len);
1534 snprintf(cmd, len, "create mfunnel=%s", mfunnel);
1535 simple_argv_cmd(argv[0], orig, cmd,
1536 "created multiplex-funnel in maintenance mode", NULL);
1537 free(cmd);
1538 } else if (password != NULL) {
1539 size_t len = strlen("create password=") + strlen(password) + 1;
1540 char *cmd = malloc(len);
1541 snprintf(cmd, len, "create password=%s", password);
1542 simple_argv_cmd(argv[0], orig, cmd,
1543 "created database with password for monetdb user", NULL);
1544 free(cmd);
1545 } else {
1546 simple_argv_cmd(argv[0], orig, "create",
1547 "created database in maintenance mode", NULL);
1548 }
1549 msab_freeStatus(&orig);
1550}
1551
1552static void
1553command_destroy(int argc, char *argv[])
1554{
1555 int i;
1556 int force = 0; /* ask for confirmation */
1557 char *e;
1558 sabdb *orig = NULL;
1559 sabdb *stats = NULL;
1560
1561 if (argc == 1) {
1562 /* print help message for this command */
1563 command_help(argc + 1, &argv[-1]);
1564 exit(1);
1565 }
1566
1567 /* walk through the arguments and hunt for "options" */
1568 for (i = 1; i < argc; i++) {
1569 if (strcmp(argv[i], "--") == 0) {
1570 argv[i] = NULL;
1571 break;
1572 }
1573 if (argv[i][0] == '-') {
1574 if (argv[i][1] == 'f') {
1575 force = 1;
1576 argv[i] = NULL;
1577 } else {
1578 fprintf(stderr, "destroy: unknown option: %s\n", argv[i]);
1579 command_help(argc + 1, &argv[-1]);
1580 exit(1);
1581 }
1582 }
1583 }
1584
1585 if ((e = MEROgetStatus(&orig, NULL)) != NULL) {
1586 fprintf(stderr, "destroy: %s\n", e);
1587 free(e);
1588 exit(2);
1589 }
1590 stats = globMatchDBS(argc, argv, &orig, "destroy");
1591 msab_freeStatus(&orig);
1592 orig = stats;
1593
1594 if (orig == NULL)
1595 exit(1);
1596
1597 if (force == 0) {
1598 char answ;
1599 printf("you are about to remove database%s ", orig->next != NULL ? "s" : "");
1600 for (stats = orig; stats != NULL; stats = stats->next)
1601 printf("%s'%s'", stats != orig ? ", " : "", stats->dbname);
1602 printf("\nALL data in %s will be lost, are you sure? [y/N] ",
1603 orig->next != NULL ? "these databases" : "this database");
1604 if (scanf("%c", &answ) >= 1 &&
1605 (answ == 'y' || answ == 'Y'))
1606 {
1607 /* do it! */
1608 } else {
1609 printf("aborted\n");
1610 exit(1);
1611 }
1612 } else {
1613 char *ret;
1614 char *out;
1615 for (stats = orig; stats != NULL; stats = stats->next) {
1616 if (stats->state == SABdbRunning || stats->state == SABdbStarting) {
1617 ret = control_send(&out, mero_host, mero_port,
1618 stats->dbname, "stop", 0, mero_pass);
1619 if (ret != NULL)
1620 free(ret);
1621 }
1622 }
1623 }
1624
1625 simple_argv_cmd(argv[0], orig, "destroy", "destroyed database", NULL);
1626 msab_freeStatus(&orig);
1627}
1628
1629static void
1630command_lock(int argc, char *argv[])
1631{
1632 simple_command(argc, argv, "lock", "put database under maintenance", 1);
1633}
1634
1635static void
1636command_release(int argc, char *argv[])
1637{
1638 simple_command(argc, argv, "release", "taken database out of maintenance mode", 1);
1639}
1640
1641static void
1642command_profilerstart(int argc, char *argv[])
1643{
1644 simple_command(argc, argv, "profilerstart", "started profiler", 1);
1645}
1646
1647static void
1648command_profilerstop(int argc, char *argv[])
1649{
1650 simple_command(argc, argv, "profilerstop", "stopped profiler", 1);
1651}
1652
1653int
1654main(int argc, char *argv[])
1655{
1656 char buf[1024];
1657 int i;
1658#ifdef TIOCGWINSZ
1659 struct winsize ws;
1660
1661 if (ioctl(fileno(stdout), TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0)
1662 TERMWIDTH = ws.ws_col;
1663#endif
1664
1665 /* Start handling the arguments.
1666 * monetdb [monetdb_options] command [options] [database [...]]
1667 * this means we first scout for monetdb_options which stops as soon
1668 * as we find a non-option argument, which then must be command */
1669
1670 /* first handle the simple no argument case */
1671 if (argc <= 1) {
1672 command_help(0, NULL);
1673 return(1);
1674 }
1675
1676 /* handle monetdb_options */
1677 for (i = 1; argc > i && argv[i][0] == '-'; i++) {
1678 switch (argv[i][1]) {
1679 case 'v':
1680 command_version();
1681 return(0);
1682 case 'q':
1683 monetdb_quiet = 1;
1684 break;
1685 case 'h':
1686 if (strlen(&argv[i][2]) > 0) {
1687 mero_host = &argv[i][2];
1688 } else {
1689 if (i + 1 < argc) {
1690 mero_host = argv[++i];
1691 } else {
1692 fprintf(stderr, "monetdb: -h needs an argument\n");
1693 return(1);
1694 }
1695 }
1696 break;
1697 case 'p':
1698 if (strlen(&argv[i][2]) > 0) {
1699 mero_port = atoi(&argv[i][2]);
1700 } else {
1701 if (i + 1 < argc) {
1702 mero_port = atoi(argv[++i]);
1703 } else {
1704 fprintf(stderr, "monetdb: -p needs an argument\n");
1705 return(1);
1706 }
1707 }
1708 break;
1709 case 'P':
1710 /* take care we remove the password from argv so it
1711 * doesn't show up in e.g. ps -ef output */
1712 if (strlen(&argv[i][2]) > 0) {
1713 mero_pass = strdup(&argv[i][2]);
1714 memset(&argv[i][2], 0, strlen(mero_pass));
1715 } else {
1716 if (i + 1 < argc) {
1717 mero_pass = strdup(argv[++i]);
1718 memset(argv[i], 0, strlen(mero_pass));
1719 } else {
1720 fprintf(stderr, "monetdb: -P needs an argument\n");
1721 return(1);
1722 }
1723 }
1724 break;
1725 case '-':
1726 /* skip -- */
1727 if (argv[i][2] == '\0')
1728 break;
1729 if (strcmp(&argv[i][2], "version") == 0) {
1730 command_version();
1731 return(0);
1732 } else if (strcmp(&argv[i][2], "help") == 0) {
1733 command_help(0, NULL);
1734 return(0);
1735 }
1736 /* fall through */
1737 default:
1738 fprintf(stderr, "monetdb: unknown option: %s\n", argv[i]);
1739 command_help(0, NULL);
1740 return(1);
1741 break;
1742 }
1743 }
1744
1745 /* check consistency of -h -p and -P args */
1746 if (mero_pass != NULL && (mero_host == NULL || *mero_host == '/')) {
1747 fprintf(stderr, "monetdb: -P requires -h to be used with a TCP hostname\n");
1748 exit(1);
1749 } else if (mero_host != NULL && *mero_host != '/' && mero_pass == NULL) {
1750 fprintf(stderr, "monetdb: -h requires -P to be used\n");
1751 exit(1);
1752 }
1753
1754 /* see if we still have arguments at this stage */
1755 if (i >= argc) {
1756 command_help(0, NULL);
1757 return(1);
1758 }
1759
1760 /* commands that do not need merovingian to be running */
1761 if (strcmp(argv[i], "help") == 0) {
1762 command_help(argc - i, &argv[i]);
1763 return(0);
1764 } else if (strcmp(argv[i], "version") == 0) {
1765 command_version();
1766 return(0);
1767 }
1768
1769 /* use UNIX socket if no hostname given */
1770 if (mero_host == NULL || *mero_host == '/') {
1771 /* a socket looks like /tmp/.s.merovingian.<tcpport>, try
1772 * finding such port. If mero_host is set, it is the location
1773 * where we should search, which defaults to '/tmp' */
1774 if (mero_host == NULL)
1775 mero_host = "/tmp";
1776 /* first try the port given (or else its default) */
1777 snprintf(buf, sizeof(buf), "%s/.s.merovingian.%d",
1778 mero_host, mero_port == -1 ? 50000 : mero_port);
1779 if (control_ping(buf, -1, NULL) == 0) {
1780 mero_host = buf;
1781 } else {
1782 /* if port wasn't given, we can try and search
1783 * for available sockets */
1784 if (mero_port == -1) {
1785 DIR *d;
1786 struct dirent *e;
1787 struct stat s;
1788
1789 d = opendir(mero_host);
1790 if (d == NULL) {
1791 fprintf(stderr, "monetdb: cannot find a control socket, use -h and/or -p\n");
1792 exit(1);
1793 }
1794 while ((e = readdir(d)) != NULL) {
1795 if (strncmp(e->d_name, ".s.merovingian.", 15) != 0)
1796 continue;
1797 snprintf(buf, sizeof(buf), "%s/%s", mero_host, e->d_name);
1798 if (stat(buf, &s) == -1)
1799 continue;
1800 if (S_ISSOCK(s.st_mode)) {
1801 if (control_ping(buf, -1, NULL) == 0) {
1802 mero_host = buf;
1803 break;
1804 }
1805 }
1806 }
1807 closedir(d);
1808 }
1809 }
1810
1811 if (mero_host != buf) {
1812 fprintf(stderr, "monetdb: cannot find a control socket, use -h and/or -p\n");
1813 exit(1);
1814 }
1815 /* don't confuse control_send lateron */
1816 mero_port = -1;
1817 }
1818 /* for TCP connections */
1819 if (mero_host != NULL && *mero_host != '/' && mero_port == -1)
1820 mero_port = 50000;
1821
1822 /* handle regular commands */
1823 if (strcmp(argv[i], "create") == 0) {
1824 command_create(argc - i, &argv[i]);
1825 } else if (strcmp(argv[i], "destroy") == 0) {
1826 command_destroy(argc - i, &argv[i]);
1827 } else if (strcmp(argv[i], "lock") == 0) {
1828 command_lock(argc - i, &argv[i]);
1829 } else if (strcmp(argv[i], "release") == 0) {
1830 command_release(argc - i, &argv[i]);
1831 } else if (strcmp(argv[i], "profilerstart") == 0) {
1832 command_profilerstart(argc - i, &argv[i]);
1833 } else if (strcmp(argv[i], "profilerstop") == 0) {
1834 command_profilerstop(argc - i, &argv[i]);
1835 } else if (strcmp(argv[i], "status") == 0) {
1836 command_status(argc - i, &argv[i]);
1837 } else if (strcmp(argv[i], "start") == 0) {
1838 command_startstop(argc - i, &argv[i], START);
1839 } else if (strcmp(argv[i], "stop") == 0) {
1840 command_startstop(argc - i, &argv[i], STOP);
1841 } else if (strcmp(argv[i], "kill") == 0) {
1842 command_startstop(argc - i, &argv[i], KILL);
1843 } else if (strcmp(argv[i], "set") == 0) {
1844 command_set(argc - i, &argv[i], SET);
1845 } else if (strcmp(argv[i], "get") == 0) {
1846 command_get(argc - i, &argv[i]);
1847 } else if (strcmp(argv[i], "inherit") == 0) {
1848 command_set(argc - i, &argv[i], INHERIT);
1849 } else if (strcmp(argv[i], "discover") == 0) {
1850 command_discover(argc - i, &argv[i]);
1851 } else {
1852 fprintf(stderr, "monetdb: unknown command: %s\n", argv[i]);
1853 command_help(0, NULL);
1854 }
1855
1856 if (mero_pass != NULL)
1857 free(mero_pass);
1858
1859 return(0);
1860}
1861
1862/* vim:set ts=4 sw=4 noexpandtab: */
1863