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 | |
49 | static char *mero_host = NULL; |
50 | static int mero_port = -1; |
51 | static char *mero_pass = NULL; |
52 | static char monetdb_quiet = 0; |
53 | static int TERMWIDTH = 0; /* default to no wrapping */ |
54 | |
55 | static void |
56 | command_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 | |
177 | static void |
178 | command_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 | |
191 | static int |
192 | cmpsabdb(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 | */ |
204 | static char * |
205 | MEROgetStatus(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 | |
267 | static void |
268 | printStatus(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 | |
488 | static sabdb * |
489 | globMatchDBS(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 | */ |
545 | static void |
546 | simple_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 | */ |
608 | static void |
609 | simple_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 | |
665 | static void |
666 | command_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 | |
876 | static int |
877 | cmpurl(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 | |
889 | static void |
890 | command_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 | |
978 | typedef enum { |
979 | START = 0, |
980 | STOP, |
981 | KILL |
982 | } startstop; |
983 | |
984 | static void |
985 | command_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 | |
1102 | typedef enum { |
1103 | SET = 0, |
1104 | INHERIT |
1105 | } meroset; |
1106 | |
1107 | static _Noreturn void command_set(int argc, char *argv[], meroset type); |
1108 | |
1109 | static void |
1110 | command_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 | |
1237 | static void |
1238 | command_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 | |
1463 | static void |
1464 | command_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 | |
1552 | static void |
1553 | command_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 | |
1629 | static void |
1630 | command_lock(int argc, char *argv[]) |
1631 | { |
1632 | simple_command(argc, argv, "lock" , "put database under maintenance" , 1); |
1633 | } |
1634 | |
1635 | static void |
1636 | command_release(int argc, char *argv[]) |
1637 | { |
1638 | simple_command(argc, argv, "release" , "taken database out of maintenance mode" , 1); |
1639 | } |
1640 | |
1641 | static void |
1642 | command_profilerstart(int argc, char *argv[]) |
1643 | { |
1644 | simple_command(argc, argv, "profilerstart" , "started profiler" , 1); |
1645 | } |
1646 | |
1647 | static void |
1648 | command_profilerstop(int argc, char *argv[]) |
1649 | { |
1650 | simple_command(argc, argv, "profilerstop" , "stopped profiler" , 1); |
1651 | } |
1652 | |
1653 | int |
1654 | main(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 | |