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#include "monetdb_config.h"
10#include <unistd.h> /* chdir */
11#include <string.h> /* strerror */
12#include <sys/types.h>
13#include <sys/stat.h>
14#include <signal.h> /* kill */
15
16#include "mutils.h" /* MT_lockf */
17#include "mcrypt.h" /* mcrypt_BackendSum */
18#include "mstring.h"
19#include "utils/utils.h"
20#include "utils/properties.h"
21#include "utils/control.h"
22
23#include "merovingian.h"
24#include "argvcmds.h"
25
26int
27command_help(int argc, char *argv[])
28{
29 int exitcode = 0;
30
31 if (argc < 2) {
32 printf("usage: monetdbd [ command [ command-options ] ] <dbfarm>\n");
33 printf(" where command is one of:\n");
34 printf(" create, start, stop, get, set, version or help\n");
35 printf(" use the help command to get help for a particular command\n");
36 printf(" The dbfarm to operate on must always be given to\n");
37 printf(" monetdbd explicitly.\n");
38 } else if (strcmp(argv[1], "create") == 0) {
39 printf("usage: monetdbd create <dbfarm>\n");
40 printf(" Initialises a new dbfarm for a MonetDB Server. dbfarm\n");
41 printf(" must be a path in the filesystem where a directory can be\n");
42 printf(" created, or a directory that is writable that already exists.\n");
43 } else if (strcmp(argv[1], "start") == 0) {
44 printf("usage: monetdbd start [-n] <dbfarm>\n");
45 printf(" Starts the monetdbd deamon for the given dbfarm.\n");
46 printf(" When -n is given, monetdbd will not fork into the background.\n");
47 } else if (strcmp(argv[1], "stop") == 0) {
48 printf("usage: monetdbd stop <dbfarm>\n");
49 printf(" Stops a running monetdbd deamon for the given dbfarm.\n");
50 } else if (strcmp(argv[1], "set") == 0) {
51 printf("usage: monetdbd set property=value <dbfarm>\n");
52 printf(" Sets property to value for the given dbfarm.\n");
53 printf(" For a list of properties, use `monetdbd get all`\n");
54 } else if (strcmp(argv[1], "get") == 0) {
55 printf("usage: monetdbd get <\"all\" | property,...> <dbfarm>\n");
56 printf(" Gets value for property for the given dbfarm, or\n");
57 printf(" retrieves all properties.\n");
58 } else {
59 printf("help: unknown command: %s\n", argv[1]);
60 exitcode = 1;
61 }
62
63 return(exitcode);
64}
65
66int
67command_version(void)
68{
69 printf("MonetDB Database Server v%s", VERSION);
70 /* coverity[pointless_string_compare] */
71#ifdef MONETDB_RELEASE
72 printf(" (%s)", MONETDB_RELEASE);
73#else
74 const char *rev = mercurial_revision();
75 if (strcmp(rev, "Unknown") != 0)
76 printf(" (hg id: %s)", rev);
77#endif
78 printf("\n");
79 return 0;
80}
81
82int
83command_create(int argc, char *argv[])
84{
85 char path[2048];
86 char *p;
87 char *dbfarm;
88 confkeyval phrase[2];
89
90 if (argc != 2) {
91 command_help(2, &argv[-1]);
92 return(1);
93 }
94
95 dbfarm = argv[1];
96
97 /* check if dbfarm actually exists */
98 strcpy_len(path, dbfarm, sizeof(path));
99 p = path;
100 while ((p = strchr(p + 1, '/')) != NULL) {
101 *p = '\0';
102 if (mkdir(path, 0755) == -1 && errno != EEXIST) {
103 fprintf(stderr,
104 "unable to create directory '%s': %s\n",
105 path, strerror(errno));
106 return(1);
107 }
108 *p = '/';
109 }
110 if (mkdir(dbfarm, 0755) == -1 && errno != EEXIST) {
111 fprintf(stderr, "unable to create directory '%s': %s\n",
112 dbfarm, strerror(errno));
113 return(1);
114 }
115
116 phrase[0].key = NULL;
117 if (readProps(phrase, dbfarm) == 0) {
118 fprintf(stderr, "directory is already initialised: %s\n", dbfarm);
119 return(1);
120 }
121
122 phrase[0].key = "control";
123 phrase[0].val = "false";
124 phrase[1].key = NULL;
125 if (writeProps(phrase, dbfarm) != 0) {
126 fprintf(stderr, "unable to create file in directory '%s': %s\n",
127 dbfarm, strerror(errno));
128 return(1);
129 }
130
131 return(0);
132}
133
134int
135command_get(confkeyval *ckv, int argc, char *argv[])
136{
137 char *p;
138 char *dbfarm;
139 char *property = NULL;
140 char *value;
141 char buf[512];
142 char vbuf[512];
143 confkeyval *kv;
144 int meropid = -1;
145
146 if (argc != 3) {
147 command_help(2, &argv[-1]);
148 return(1);
149 }
150
151 dbfarm = argv[2];
152
153 /* read the merovingian properties from the dbfarm */
154 if (readProps(ckv, dbfarm) != 0) {
155 fprintf(stderr, "unable to read properties from %s: %s\n",
156 dbfarm, strerror(errno));
157 return(1);
158 }
159
160 property = argv[1];
161 if (strcmp(property, "all") == 0) {
162 size_t off = 0;
163 kv = ckv;
164 property = vbuf;
165 /* hardwired read-only properties */
166 off += snprintf(property, sizeof(vbuf),
167 "hostname,dbfarm,status,mserver");
168 while (kv->key != NULL) {
169 off += snprintf(property + off, sizeof(vbuf) - off,
170 ",%s", kv->key);
171 kv++;
172 }
173 /* deduced read-only properties */
174 off += snprintf(property + off, sizeof(vbuf) - off,
175 ",mapisock,controlsock");
176 }
177
178 /* chdir to dbfarm so we can open relative files (like pidfile) */
179 if (chdir(dbfarm) < 0) {
180 fprintf(stderr, "could not move to dbfarm '%s': %s\n",
181 dbfarm, strerror(errno));
182 return(1);
183 }
184
185 if (strstr(property, "status") != NULL ||
186 strstr(property, "mserver") != NULL)
187 {
188 /* check if there is a merovingian serving this dbfarm */
189 int ret;
190 if ((ret = MT_lockf(".merovingian_lock", F_TLOCK, 4, 1)) == -1) {
191 /* locking failed, merovingian is running */
192 FILE *pf;
193 char *pfile = getConfVal(ckv, "pidfile");
194
195 if (pfile != NULL && (pf = fopen(pfile, "r")) != NULL) {
196 if (fgets(buf, sizeof(buf), pf) != NULL) {
197 meropid = atoi(buf);
198 }
199 fclose(pf);
200 }
201 } else {
202 if (ret >= 0) {
203 /* release a possible lock */
204 MT_lockf(".merovingian_lock", F_ULOCK, 4, 1);
205 close(ret);
206 }
207 meropid = 0;
208 }
209 }
210
211 printf(" property value\n");
212 while ((p = strtok(property, ",")) != NULL) {
213 property = NULL;
214 if (strcmp(p, "dbfarm") == 0) {
215 value = dbfarm;
216 } else if (strcmp(p, "mserver") == 0) {
217 if (meropid == 0) {
218 value = "unknown (monetdbd not running)";
219 } else {
220 char *res;
221 /* get binpath from running merovingian */
222 kv = findConfKey(ckv, "sockdir");
223 value = kv->val;
224 kv = findConfKey(ckv, "port");
225 snprintf(buf, sizeof(buf), "%s/" CONTROL_SOCK "%d",
226 value, kv->ival);
227 value = control_send(&res, buf, -1, "", "mserver", 1, NULL);
228 if (value != NULL) {
229 free(value);
230 value = "unknown (failed to connect to monetdbd)";
231 } else {
232 if (strncmp(res, "OK\n", 3) != 0) {
233 free(res);
234 value = "unknown (unsupported monetdbd)";
235 } else {
236 snprintf(buf, sizeof(buf), "%s", res + 3);
237 value = buf;
238 free(res);
239 }
240 }
241 }
242 } else if (strcmp(p, "hostname") == 0) {
243 value = _mero_hostname;
244 } else if (strcmp(p, "mapisock") == 0) {
245 kv = findConfKey(ckv, "sockdir");
246 value = kv->val;
247 kv = findConfKey(ckv, "port");
248 snprintf(buf, sizeof(buf), "%s/" MERO_SOCK "%d",
249 value, kv->ival);
250 value = buf;
251 } else if (strcmp(p, "controlsock") == 0) {
252 kv = findConfKey(ckv, "sockdir");
253 value = kv->val;
254 kv = findConfKey(ckv, "port");
255 snprintf(buf, sizeof(buf), "%s/" CONTROL_SOCK "%d",
256 value, kv->ival);
257 value = buf;
258 } else if (strcmp(p, "status") == 0) {
259 if (meropid > 0) {
260 char *res;
261 confkeyval cport[] = {
262 {"controlport", NULL, -1, INT},
263 { NULL, NULL, 0, INVALID}
264 };
265
266 /* re-read, this time with empty defaults, so we can see
267 * what's available (backwards compatability) */
268 if (readProps(cport, ".") != 0) {
269 fprintf(stderr, "unable to read properties from %s: %s\n",
270 dbfarm, strerror(errno));
271 return(1);
272 }
273
274 /* try to retrieve running merovingian version */
275 kv = findConfKey(ckv, "sockdir");
276 value = kv->val;
277 kv = findConfKey(cport, "controlport"); /* backwards compat */
278 if (kv->ival == -1)
279 kv = findConfKey(ckv, "port");
280 snprintf(buf, sizeof(buf), "%s/" CONTROL_SOCK "%d",
281 value, kv->ival);
282 freeConfFile(cport);
283 value = control_send(&res, buf, -1, "", "version", 1, NULL);
284 if (value != NULL) {
285 free(value);
286 value = NULL;
287 } else {
288 if (strncmp(res, "OK\n", 3) != 0) {
289 free(res);
290 } else {
291 value = res + 3;
292 }
293 }
294
295 snprintf(buf, sizeof(buf),
296 "monetdbd[%d] %s is serving this dbfarm",
297 meropid, value == NULL ? "(unknown version)" : value);
298 if (value != NULL)
299 free(res);
300 value = buf;
301 } else if (meropid < 0) {
302 value = "a monetdbd is serving this dbfarm, "
303 "but a pidfile was not found/is corrupt";
304 } else {
305 value = "no monetdbd is serving this dbfarm";
306 }
307 } else if (strcmp(p, "logfile") == 0 || strcmp(p, "pidfile") == 0) {
308 kv = findConfKey(ckv, p);
309 if (kv->val != NULL && kv->val[0] != '/') {
310 snprintf(buf, sizeof(buf), "%s/%s", dbfarm, kv->val);
311 value = buf;
312 } else {
313 value = kv->val;
314 }
315 } else {
316 kv = findConfKey(ckv, p);
317 if (kv == NULL) {
318 fprintf(stderr, "get: no such property: %s\n", p);
319 continue;
320 }
321 if (kv->val == NULL) {
322 value = "<unknown>";
323 } else {
324 value = kv->val;
325 }
326 }
327 printf("%-15s %s\n", p, value);
328 }
329
330 return(0);
331}
332
333int
334command_set(confkeyval *ckv, int argc, char *argv[])
335{
336 char *p = NULL;
337 char h[256];
338 char *property;
339 char *dbfarm;
340 confkeyval *kv;
341 FILE *pfile = NULL;
342 char buf[8];
343 pid_t meropid;
344
345 if (argc != 3) {
346 command_help(2, &argv[-1]);
347 return(1);
348 }
349
350 dbfarm = argv[2];
351
352 /* read the merovingian properties from the dbfarm */
353 if (readProps(ckv, dbfarm) != 0) {
354 fprintf(stderr, "unable to read properties from %s: %s\n",
355 dbfarm, strerror(errno));
356 return(1);
357 }
358
359 property = argv[1];
360
361 if ((p = strchr(property, '=')) == NULL) {
362 fprintf(stderr, "set: need property=value\n");
363 command_help(2, &argv[-1]);
364 return(1);
365 }
366 *p++ = '\0';
367
368 /* handle pseudo properties returned by get */
369 if (strcmp(property, "hostname") == 0 ||
370 strcmp(property, "dbfarm") == 0 ||
371 strcmp(property, "mserver") == 0)
372 {
373 fprintf(stderr, "set: %s is read-only for monetdbd\n", property);
374 return(1);
375 }
376 if (strcmp(property, "mapisock") == 0 ||
377 strcmp(property, "controlsock") == 0)
378 {
379 fprintf(stderr, "set: mapisock and controlsock are deduced from "
380 "sockdir and port, change those instead\n");
381 return(1);
382 }
383
384 if ((kv = findConfKey(ckv, property)) == NULL) {
385 fprintf(stderr, "set: no such property: %s\n", property);
386 return(1);
387 }
388 if (strcmp(property, "passphrase") == 0) {
389 char dohash = 1;
390 /* allow to either set a hash ({X}xxx), or convert the given
391 * string to its hash */
392 if (*p == '{') {
393 char *q;
394 if ((q = strchr(p + 1, '}')) != NULL) {
395 *q = '\0';
396 if (strcmp(p + 1, MONETDB5_PASSWDHASH) != 0) {
397 fprintf(stderr, "set: passphrase hash '%s' incompatible, "
398 "expected '%s'\n",
399 h, MONETDB5_PASSWDHASH);
400 return(1);
401 }
402 *q = '}';
403 dohash = 0;
404 }
405 }
406 if (dohash == 1) {
407 p = mcrypt_BackendSum(p, strlen(p));
408 if(p) {
409 snprintf(h, sizeof(h), "{%s}%s", MONETDB5_PASSWDHASH, p);
410 free(p);
411 p = h;
412 } else {
413 fprintf(stderr, "set: passphrase hash '%s' backend not found\n", MONETDB5_PASSWDHASH);
414 return(1);
415 }
416 }
417 }
418 if ((p = setConfVal(kv, p)) != NULL) {
419 fprintf(stderr, "set: failed to set property %s: %s\n", property, p);
420 free(p);
421 return(1);
422 }
423 if (writeProps(ckv, dbfarm) != 0) {
424 fprintf(stderr, "set: failed to write properties: %s\n",
425 strerror(errno));
426 return(1);
427 }
428
429 property = getConfVal(ckv, "pidfile");
430
431 /* chdir to dbfarm so we can open relative files (like pidfile) */
432 if (chdir(dbfarm) < 0) {
433 fprintf(stderr, "could not move to dbfarm '%s': %s\n",
434 dbfarm, strerror(errno));
435 return(1);
436 }
437
438 if ((pfile = fopen(property, "r")) != NULL) {
439 if (fgets(buf, sizeof(buf), pfile) != NULL &&
440 (meropid = atoi(buf)) != 0 &&
441 kill(meropid, SIGHUP) == -1)
442 {
443 fprintf(stderr, "sending SIGHUP to monetdbd[%d] failed: %s\n",
444 (int)meropid, strerror(errno));
445 fclose(pfile);
446 return(1);
447 }
448 fclose(pfile);
449 }
450
451 return(0);
452}
453
454int
455command_stop(confkeyval *ckv, int argc, char *argv[])
456{
457 char *dbfarm;
458 char *pidfilename = NULL;
459 FILE *pfile = NULL;
460 char buf[8];
461 pid_t daemon;
462 struct timeval tv;
463 int i;
464
465 if (argc != 2) {
466 command_help(2, &argv[-1]);
467 return(1);
468 }
469
470 dbfarm = argv[1];
471
472 /* read the merovingian properties from the dbfarm */
473 if (readProps(ckv, dbfarm) != 0) {
474 fprintf(stderr, "unable to read properties from %s: %s\n",
475 dbfarm, strerror(errno));
476 return(1);
477 }
478
479 pidfilename = getConfVal(ckv, "pidfile");
480
481 /* chdir to dbfarm so we can open relative files (like pidfile) */
482 if (chdir(dbfarm) < 0) {
483 fprintf(stderr, "could not move to dbfarm '%s': %s\n",
484 dbfarm, strerror(errno));
485 return(1);
486 }
487
488 if ((pfile = fopen(pidfilename, "r")) == NULL) {
489 fprintf(stderr, "unable to open %s: %s\n",
490 pidfilename, strerror(errno));
491 return(1);
492 }
493
494 if (fgets(buf, sizeof(buf), pfile) == NULL) {
495 fprintf(stderr, "unable to read from %s: %s\n",
496 pidfilename, strerror(errno));
497 fclose(pfile);
498 return(1);
499 }
500 fclose(pfile);
501
502 daemon = atoi(buf);
503 if (daemon == 0) {
504 fprintf(stderr, "invalid pid in pidfile %s: %s\n",
505 pidfilename, buf);
506 return(1);
507 }
508
509 if (kill(daemon, SIGTERM) == -1) {
510 fprintf(stderr, "unable to shut down monetdbd[%d]: %s\n",
511 (int)daemon, strerror(errno));
512 return(1);
513 }
514
515 /* wait up to 30 seconds for monetdbd to actually stop */
516 for (i = 0; i < 60; i++) {
517 tv.tv_sec = 0;
518 tv.tv_usec = 500000;
519 select(0, NULL, NULL, NULL, &tv);
520 if (kill(daemon, 0) == -1) {
521 /* daemon has died */
522 return(0);
523 }
524 }
525
526 /* done waiting, use harsher measures */
527 kill(daemon, SIGKILL);
528
529 return(0);
530}
531