Line data Source code
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 - 2021 MonetDB B.V.
7 : */
8 :
9 : /**
10 : * Sabaoth
11 : * Fabian Groffen
12 : * Cluster support
13 : *
14 : * The cluster facilitation currently only deals with (de-)registering
15 : * of services offered by the local server to other servers. This
16 : * module allows programs to be aware of mservers in a dbfarm on a local
17 : * machine.
18 : */
19 :
20 : #include "monetdb_config.h"
21 : #include <unistd.h> /* unlink and friends */
22 : #include <sys/types.h>
23 : #ifdef HAVE_DIRENT_H
24 : #include <dirent.h> /* readdir, DIR */
25 : #endif
26 : #include <sys/stat.h>
27 : #include <fcntl.h>
28 : #include <time.h>
29 : #include <string.h> /* for getting error messages */
30 : #include <stddef.h>
31 : #include <ctype.h>
32 : #if defined(HAVE_GETENTROPY) && defined(HAVE_SYS_RANDOM_H)
33 : #include <sys/random.h>
34 : #endif
35 :
36 : #include "msabaoth.h"
37 : #include "mutils.h"
38 : #include "muuid.h"
39 : #include "mstring.h"
40 :
41 : #if defined(_MSC_VER) && _MSC_VER >= 1400
42 : #define close _close
43 : #define unlink _unlink
44 : #define fdopen _fdopen
45 : #define fileno _fileno
46 : #endif
47 :
48 : #ifndef O_CLOEXEC
49 : #define O_CLOEXEC 0
50 : #endif
51 :
52 : /** the directory where the databases are (aka dbfarm) */
53 : static char *_sabaoth_internal_dbfarm = NULL;
54 : /** the database which is "active" */
55 : static char *_sabaoth_internal_dbname = NULL;
56 : /** identifier of the current process */
57 : static char *_sabaoth_internal_uuid = NULL;
58 :
59 : /**
60 : * Retrieves the dbfarm path plus an optional extra component added
61 : */
62 : static char *
63 5310 : getFarmPath(char *pathbuf, size_t size, const char *extra)
64 : {
65 5310 : if (_sabaoth_internal_dbfarm == NULL)
66 0 : return(strdup("sabaoth not initialized"));
67 :
68 5310 : if (extra == NULL) {
69 5310 : snprintf(pathbuf, size, "%s", _sabaoth_internal_dbfarm);
70 : } else {
71 0 : snprintf(pathbuf, size, "%s%c%s",
72 : _sabaoth_internal_dbfarm, DIR_SEP, extra);
73 : }
74 :
75 : return(NULL);
76 : }
77 :
78 : /**
79 : * Retrieves the path to the database directory with an optional extra
80 : * component added
81 : */
82 : static char *
83 5621 : getDBPath(char *pathbuf, size_t size, const char *extra)
84 : {
85 5621 : if (_sabaoth_internal_dbfarm == NULL)
86 0 : return(strdup("sabaoth not initialized"));
87 5621 : if (_sabaoth_internal_dbname == NULL)
88 0 : return(strdup("sabaoth was not initialized as active database"));
89 :
90 5621 : if (extra == NULL) {
91 0 : snprintf(pathbuf, size, "%s%c%s",
92 : _sabaoth_internal_dbfarm, DIR_SEP, _sabaoth_internal_dbname);
93 : } else {
94 5621 : snprintf(pathbuf, size, "%s%c%s%c%s",
95 : _sabaoth_internal_dbfarm, DIR_SEP,
96 : _sabaoth_internal_dbname, DIR_SEP, extra);
97 : }
98 :
99 : return(NULL);
100 : }
101 :
102 : static inline int
103 1555 : msab_isuuid(const char *restrict s)
104 : {
105 : int hyphens = 0;
106 :
107 : /* correct length */
108 1555 : if (strlen(s) != 36)
109 : return 0;
110 :
111 : /* hyphens at correct locations */
112 11 : if (s[8] != '-' ||
113 11 : s[13] != '-' ||
114 11 : s[18] != '-' ||
115 11 : s[23] != '-')
116 : return 0;
117 : /* only hexadecimals and hypens */
118 407 : while (*s) {
119 396 : if (!isxdigit((unsigned char) *s)) {
120 44 : if (*s == '-')
121 44 : hyphens++;
122 : else
123 : return 0;
124 : }
125 396 : s++;
126 : }
127 : /* correct number of hyphens */
128 11 : return hyphens == 4;
129 : }
130 :
131 : void
132 263 : msab_dbnameinit(const char *dbname)
133 : {
134 263 : if (dbname == NULL) {
135 0 : _sabaoth_internal_dbname = NULL;
136 : } else {
137 263 : _sabaoth_internal_dbname = strdup(dbname);
138 : }
139 263 : }
140 :
141 : /**
142 : * Initialises this Sabaoth instance to use the given dbfarm and dbname.
143 : * dbname may be NULL to indicate that there is no active database. The
144 : * arguments are copied for internal use.
145 : */
146 : static void
147 263 : msab_init(const char *dbfarm, const char *dbname)
148 : {
149 : size_t len;
150 : DIR *d;
151 : char *tmp;
152 :
153 263 : assert(dbfarm != NULL);
154 :
155 263 : if (_sabaoth_internal_dbfarm != NULL)
156 10 : free(_sabaoth_internal_dbfarm);
157 263 : if (_sabaoth_internal_dbname != NULL)
158 10 : free(_sabaoth_internal_dbname);
159 :
160 : /* this UUID is supposed to be unique per-process, we use it later on
161 : * to determine if a database is (started by) the current process,
162 : * since locking always succeeds for the same process */
163 263 : if (_sabaoth_internal_uuid == NULL)
164 253 : _sabaoth_internal_uuid = generateUUID();
165 :
166 263 : len = strlen(dbfarm);
167 263 : _sabaoth_internal_dbfarm = strdup(dbfarm);
168 : /* remove trailing slashes, newlines and spaces */
169 263 : len--;
170 263 : while (len > 0 && (
171 263 : _sabaoth_internal_dbfarm[len] == '/' ||
172 263 : _sabaoth_internal_dbfarm[len] == '\n' ||
173 : _sabaoth_internal_dbfarm[len] == ' ')) {
174 0 : _sabaoth_internal_dbfarm[len] = '\0';
175 0 : len--;
176 : }
177 :
178 263 : msab_dbnameinit(dbname);
179 :
180 : /* clean out old UUID files in case the database crashed in a
181 : * previous incarnation */
182 263 : if (_sabaoth_internal_dbname != NULL &&
183 263 : (tmp = malloc(strlen(_sabaoth_internal_dbfarm) + strlen(_sabaoth_internal_dbname) + 2)) != NULL) {
184 263 : sprintf(tmp, "%s%c%s", _sabaoth_internal_dbfarm, DIR_SEP, _sabaoth_internal_dbname);
185 263 : if ((d = opendir(tmp)) != NULL) {
186 : struct dbe {
187 : struct dbe *next;
188 : char path[FLEXIBLE_ARRAY_MEMBER];
189 : } *dbe = NULL, *db;
190 : struct dirent *e;
191 263 : len = offsetof(struct dbe, path) + strlen(tmp) + 2;
192 2081 : while ((e = readdir(d)) != NULL) {
193 1555 : if (msab_isuuid(e->d_name) &&
194 11 : (db = malloc(strlen(e->d_name) + len)) != NULL) {
195 11 : db->next = dbe;
196 : dbe = db;
197 11 : sprintf(db->path, "%s%c%s", tmp, DIR_SEP, e->d_name);
198 : }
199 : }
200 263 : closedir(d);
201 : /* remove in a separate loop after reading the directory,
202 : * so as to not have any interference */
203 274 : while (dbe != NULL) {
204 11 : (void) MT_remove(dbe->path);
205 : db = dbe;
206 11 : dbe = dbe->next;
207 11 : free(db);
208 : }
209 : }
210 263 : free(tmp);
211 : }
212 263 : }
213 : void
214 263 : msab_dbpathinit(const char *dbpath)
215 : {
216 : char dbfarm[FILENAME_MAX];
217 : const char *p;
218 :
219 263 : p = strrchr(dbpath, DIR_SEP);
220 263 : assert(p != NULL);
221 263 : strncpy(dbfarm, dbpath, p - dbpath);
222 263 : dbfarm[p - dbpath] = 0;
223 263 : msab_init(dbfarm, p + 1);
224 263 : }
225 : void
226 0 : msab_dbfarminit(const char *dbfarm)
227 : {
228 0 : msab_init(dbfarm, NULL);
229 0 : }
230 :
231 : /**
232 : * Returns the dbfarm as received during msab_init. Returns an
233 : * exception if not initialised.
234 : */
235 : char *
236 0 : msab_getDBfarm(char **ret)
237 : {
238 0 : if (_sabaoth_internal_dbfarm == NULL)
239 0 : return(strdup("sabaoth not initialized"));
240 0 : *ret = strdup(_sabaoth_internal_dbfarm);
241 0 : return(NULL);
242 : }
243 :
244 : /**
245 : * Returns the dbname as received during msab_init. Throws an
246 : * exception if not initialised or dbname was set to NULL.
247 : */
248 : char *
249 0 : msab_getDBname(char **ret)
250 : {
251 0 : if (_sabaoth_internal_dbfarm == NULL)
252 0 : return(strdup("sabaoth not initialized"));
253 0 : if (_sabaoth_internal_dbname == NULL)
254 0 : return(strdup("sabaoth was not initialized as active database"));
255 0 : *ret = strdup(_sabaoth_internal_dbname);
256 0 : return(NULL);
257 : }
258 :
259 : char *
260 887 : msab_getUUID(char **ret)
261 : {
262 887 : if (_sabaoth_internal_uuid == NULL)
263 0 : return(strdup("sabaoth not initialized"));
264 887 : *ret = strdup(_sabaoth_internal_uuid);
265 887 : return NULL;
266 : }
267 :
268 : #define SCENARIOFILE ".scen"
269 :
270 : /**
271 : * Writes the given language to the scenarios file. If the file doesn't
272 : * exist, it is created. Multiple invocations of this function for the
273 : * same language are ignored.
274 : */
275 : char *
276 765 : msab_marchScenario(const char *lang)
277 : {
278 : FILE *f;
279 : char buf[2*FILENAME_MAX];
280 : size_t len;
281 : char pathbuf[FILENAME_MAX];
282 : char *tmp;
283 :
284 765 : if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), SCENARIOFILE)) != NULL)
285 : return(tmp);
286 :
287 765 : if ((f = MT_fopen(pathbuf, "a+")) != NULL) {
288 765 : if ((len = fread(buf, 1, 255, f)) > 0) {
289 : char *p;
290 :
291 502 : buf[len] = '\0';
292 : tmp = buf;
293 : /* find newlines and evaluate string */
294 1004 : while ((p = strchr(tmp, '\n')) != NULL) {
295 502 : *p = '\0';
296 502 : if (strcmp(tmp, lang) == 0) {
297 0 : (void)fclose(f);
298 0 : return(NULL);
299 : }
300 : tmp = p;
301 : }
302 : }
303 : /* append to the file */
304 765 : fprintf(f, "%s\n", lang);
305 765 : (void)fflush(f);
306 765 : (void)fclose(f);
307 765 : return(NULL);
308 : }
309 0 : snprintf(buf, sizeof(buf), "failed to open file: %s (%s)",
310 0 : strerror(errno), pathbuf);
311 0 : return(strdup(buf));
312 : }
313 :
314 : /**
315 : * Removes the given language from the scenarios file. If the scenarios
316 : * file is empty (before or) after removing the language, the file is
317 : * removed.
318 : */
319 : char *
320 502 : msab_retreatScenario(const char *lang)
321 : {
322 : FILE *f;
323 : char buf[2*FILENAME_MAX]; /* should be enough to hold the entire file */
324 : size_t len;
325 : char pathbuf[FILENAME_MAX];
326 : char *tmp;
327 :
328 502 : if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), SCENARIOFILE)) != NULL)
329 : return(tmp);
330 :
331 502 : if ((f = MT_fopen(pathbuf, "a+")) != NULL) {
332 502 : if ((len = fread(buf, 1, 255, f)) > 0) {
333 : char *p;
334 : char written = 0;
335 :
336 0 : buf[len] = '\0';
337 : tmp = buf;
338 : /* find newlines and evaluate string */
339 0 : while ((p = strchr(tmp, '\n')) != NULL) {
340 0 : *p = '\0';
341 0 : if (strcmp(tmp, lang) == 0) {
342 0 : memmove(tmp, p + 1, strlen(p + 1) + 1);
343 : written = 1;
344 : } else {
345 0 : *p = '\n';
346 0 : tmp = p+1;
347 : }
348 : }
349 0 : if (written != 0) {
350 0 : rewind(f);
351 0 : len = strlen(buf) + 1;
352 0 : if (fwrite(buf, 1, len, f) < len) {
353 0 : snprintf(buf, sizeof(buf), "failed to write: %s (%s)",
354 0 : strerror(errno), pathbuf);
355 0 : (void)fclose(f);
356 0 : return(strdup(buf));
357 : }
358 0 : fflush(f);
359 0 : fclose(f);
360 0 : return(NULL);
361 : }
362 0 : (void)fclose(f);
363 : (void) MT_remove(pathbuf);
364 0 : return(NULL);
365 : } else {
366 502 : if (ferror(f)) {
367 : /* some error */
368 0 : snprintf(buf, sizeof(buf), "failed to write: %s (%s)",
369 0 : strerror(errno), pathbuf);
370 0 : (void)fclose(f);
371 0 : return strdup(buf);
372 : }
373 502 : (void)fclose(f);
374 : (void) MT_remove(pathbuf); /* empty file? try to remove */
375 502 : return(NULL);
376 : }
377 : }
378 0 : snprintf(buf, sizeof(buf), "failed to open file: %s (%s)",
379 0 : strerror(errno), pathbuf);
380 0 : return(strdup(buf));
381 : }
382 :
383 : #define CONNECTIONFILE ".conn"
384 : /**
385 : * Writes an URI to the connection file based on the given arguments.
386 : * If the file doesn't exist, it is created. Multiple invocations of
387 : * this function for the same arguments are NOT ignored. If port is set
388 : * to <= 0, this function treats the host argument as UNIX domain
389 : * socket, in which case host must start with a '/'.
390 : */
391 : char *
392 504 : msab_marchConnection(const char *host, const int port)
393 : {
394 : FILE *f;
395 : char pathbuf[FILENAME_MAX];
396 : char *tmp;
397 :
398 504 : if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), CONNECTIONFILE)) != NULL)
399 : return(tmp);
400 :
401 504 : if (port <= 0 && host[0] != '/')
402 0 : return(strdup("UNIX domain connections should be given as "
403 : "absolute path"));
404 :
405 504 : if ((f = MT_fopen(pathbuf, "a")) != NULL) {
406 : /* append to the file */
407 504 : if (port > 0) {
408 252 : fprintf(f, "mapi:monetdb://%s:%i/\n", host, port);
409 : } else {
410 252 : fprintf(f, "mapi:monetdb://%s\n", host);
411 : }
412 504 : (void)fflush(f);
413 504 : (void)fclose(f);
414 504 : return(NULL);
415 : } else {
416 : char buf[FILENAME_MAX + 1024];
417 0 : snprintf(buf, sizeof(buf), "failed to open file: %s (%s)",
418 0 : strerror(errno), pathbuf);
419 0 : return(strdup(buf));
420 : }
421 : }
422 :
423 : #define STARTEDFILE ".started"
424 : /**
425 : * Removes all known publications of available services. The function
426 : * name is a nostalgic phrase from "Defender of the Crown" from the
427 : * Commodore Amiga age.
428 : */
429 : char *
430 514 : msab_wildRetreat(void)
431 : {
432 : char pathbuf[FILENAME_MAX];
433 : char *tmp;
434 :
435 514 : if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), SCENARIOFILE)) != NULL)
436 : return(tmp);
437 : (void) MT_remove(pathbuf);
438 :
439 514 : if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), CONNECTIONFILE)) != NULL)
440 : return(tmp);
441 : (void) MT_remove(pathbuf);
442 :
443 514 : if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), STARTEDFILE)) != NULL)
444 : return(tmp);
445 : (void) MT_remove(pathbuf);
446 :
447 514 : if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), _sabaoth_internal_uuid)) != NULL)
448 : return(tmp);
449 : (void) MT_remove(pathbuf);
450 :
451 514 : return(NULL);
452 : }
453 :
454 : #define UPLOGFILE ".uplog"
455 : /**
456 : * Writes a start attempt to the sabaoth start/stop log. Examination of
457 : * the log at a later stage reveals crashes of the server. In addition
458 : * to updating the uplog file, it also leaves the unique signature of
459 : * the current process behind.
460 : */
461 : char *
462 263 : msab_registerStarting(void)
463 : {
464 : /* The sabaoth uplog is in fact a simple two column table that
465 : * contains a start time and a stop time. Start times are followed
466 : * by a tab character, while stop times are followed by a newline.
467 : * This allows to detect crashes, while sabaoth only appends to the
468 : * uplog. */
469 :
470 : FILE *f;
471 : char pathbuf[FILENAME_MAX];
472 : char *tmp;
473 :
474 263 : if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), UPLOGFILE)) != NULL)
475 : return(tmp);
476 :
477 263 : if ((f = MT_fopen(pathbuf, "a")) != NULL) {
478 : /* append to the file */
479 263 : fprintf(f, "%" PRId64 "\t", (int64_t)time(NULL));
480 263 : (void)fflush(f);
481 263 : (void)fclose(f);
482 : } else {
483 : char buf[2*FILENAME_MAX];
484 0 : snprintf(buf, sizeof(buf), "failed to open file: %s (%s)",
485 0 : strerror(errno), pathbuf);
486 0 : return(strdup(buf));
487 : }
488 :
489 : /* we treat errors here (albeit being quite unlikely) as non-fatal,
490 : * since they will cause wrong state information in the worst case
491 : * later on */
492 263 : if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), _sabaoth_internal_uuid)) != NULL) {
493 0 : free(tmp);
494 0 : return(NULL);
495 : }
496 : f = MT_fopen(pathbuf, "w");
497 263 : if (f)
498 263 : fclose(f);
499 :
500 : /* remove any stray file that would suggest we've finished starting up */
501 263 : if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), STARTEDFILE)) != NULL)
502 : return(tmp);
503 : (void) MT_remove(pathbuf);
504 :
505 :
506 263 : return(NULL);
507 : }
508 :
509 : /**
510 : * Removes the starting state, and turns this into a fully started
511 : * engine. The caller is responsible for calling registerStarting()
512 : * first.
513 : */
514 : char *
515 251 : msab_registerStarted(void)
516 : {
517 : char pathbuf[FILENAME_MAX];
518 : char *tmp;
519 : FILE *fp;
520 :
521 : /* flag this database as started up */
522 251 : if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), STARTEDFILE)) != NULL)
523 : return(tmp);
524 : fp = MT_fopen(pathbuf, "w");
525 251 : if (fp)
526 251 : fclose(fp);
527 : else
528 0 : return strdup("sabaoth cannot create " STARTEDFILE);
529 :
530 251 : return(NULL);
531 : }
532 :
533 : /**
534 : * Writes a start attempt to the sabaoth start/stop log. Examination of
535 : * the log at a later stage reveals crashes of the server.
536 : */
537 : char *
538 251 : msab_registerStop(void)
539 : {
540 : FILE *f;
541 : char pathbuf[FILENAME_MAX];
542 : char *tmp;
543 :
544 251 : if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), UPLOGFILE)) != NULL)
545 : return(tmp);
546 :
547 251 : if ((f = MT_fopen(pathbuf, "a")) != NULL) {
548 : /* append to the file */
549 251 : fprintf(f, "%" PRId64 "\n", (int64_t)time(NULL));
550 251 : (void)fflush(f);
551 251 : (void)fclose(f);
552 : } else {
553 : char buf[2*FILENAME_MAX];
554 0 : snprintf(buf, sizeof(buf), "failed to open file: %s (%s)",
555 0 : strerror(errno), pathbuf);
556 0 : return(strdup(buf));
557 : }
558 :
559 : /* remove server signature, it's no problem when it's left behind,
560 : * but for the sake of keeping things clean ... */
561 251 : if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), _sabaoth_internal_uuid)) != NULL)
562 : return(tmp);
563 : (void) MT_remove(pathbuf);
564 251 : return(NULL);
565 : }
566 :
567 : #define SECRETFILE ".secret"
568 : #define SECRET_LENGTH (32)
569 : char *
570 252 : msab_pickSecret(char **generated_secret)
571 : {
572 : unsigned char bin_secret[SECRET_LENGTH / 2];
573 : char *secret;
574 : char pathbuf[FILENAME_MAX];
575 : char *e;
576 :
577 252 : if ((e = getDBPath(pathbuf, sizeof(pathbuf), SECRETFILE)) != NULL)
578 : return e;
579 :
580 : // delete existing so we can recreate with appropriate permissions
581 252 : if (MT_remove(pathbuf) < 0 && errno != ENOENT) {
582 : char err[FILENAME_MAX + 512];
583 0 : snprintf(err, sizeof(err), "unable to remove '%s': %s",
584 : pathbuf, strerror(errno));
585 0 : return strdup(err);
586 : }
587 :
588 252 : secret = malloc(SECRET_LENGTH + 1);
589 252 : secret[SECRET_LENGTH] = '\0';
590 :
591 : #if defined(HAVE_GETENTROPY)
592 252 : if (getentropy(bin_secret, sizeof(bin_secret)) < 0) {
593 0 : free(secret);
594 0 : return strdup("getentropy failed");
595 : }
596 : #elif defined(HAVE_RAND_S)
597 : for (size_t i = 0; i < sizeof(bin_secret) / sizeof(unsigned int); i++) {
598 : unsigned int r;
599 : if (rand_s(&r) != 0) {
600 : if (generated_secret)
601 : *generated_secret = NULL;
602 : free(secret);
603 : return NULL;
604 : }
605 : for (size_t j = 0; j < sizeof(unsigned int); j++) {
606 : bin_secret[i] = (unsigned char) (r & 0xFF);
607 : r >>= 8;
608 : }
609 : }
610 : #else
611 : (void)bin_secret;
612 : if (generated_secret)
613 : // do not return an error, just continue without a secret
614 : *generated_secret = NULL;
615 : free(secret);
616 : return NULL;
617 : #endif
618 : #if defined(HAVE_GETENTROPY) || defined(HAVE_RAND_S)
619 : int fd;
620 : FILE *f;
621 4284 : for (size_t i = 0; i < sizeof(bin_secret); i++) {
622 4032 : snprintf(secret + 2 * i, 3, "%02x", bin_secret[i]);
623 : }
624 :
625 252 : if ((fd = MT_open(pathbuf, O_CREAT | O_WRONLY | O_CLOEXEC)) == -1) {
626 : char err[512];
627 0 : snprintf(err, sizeof(err), "unable to open '%s': %s",
628 0 : pathbuf, strerror(errno));
629 0 : free(secret);
630 0 : return strdup(err);
631 : }
632 252 : if ((f = fdopen(fd, "w")) == NULL) {
633 : char err[512];
634 0 : snprintf(err, sizeof(err), "unable to open '%s': %s",
635 0 : pathbuf, strerror(errno));
636 0 : close(fd);
637 : (void)MT_remove(pathbuf);
638 0 : free(secret);
639 0 : return strdup(err);
640 : }
641 : bool error;
642 252 : error = fwrite(secret, 1, SECRET_LENGTH, f) < SECRET_LENGTH;
643 252 : error |= fclose(f) < 0;
644 252 : if (error) {
645 : char err[512];
646 0 : snprintf(err, sizeof(err), "cannot write secret: %s",
647 0 : strerror(errno));
648 : (void)MT_remove(pathbuf);
649 0 : free(secret);
650 0 : return strdup(err);
651 : }
652 :
653 252 : if (generated_secret)
654 252 : *generated_secret = secret;
655 : else
656 0 : free(secret);
657 : return NULL;
658 : #endif
659 : }
660 :
661 : /**
662 : * Returns the status as NULL terminated sabdb struct list for the
663 : * current database. Since the current database should always exist,
664 : * this function never returns NULL.
665 : */
666 : char *
667 5310 : msab_getMyStatus(sabdb** ret)
668 : {
669 : char *err;
670 5310 : if (_sabaoth_internal_dbname == NULL)
671 0 : return(strdup("sabaoth was not initialized as active database"));
672 5310 : err = msab_getStatus(ret, _sabaoth_internal_dbname);
673 5310 : if (err != NULL)
674 : return(err);
675 5310 : if (*ret == NULL)
676 0 : return(strdup("could not find my own database?!?"));
677 : return(NULL);
678 : }
679 :
680 : #define MAINTENANCEFILE ".maintenance"
681 :
682 : static sabdb *
683 5310 : msab_getSingleStatus(const char *pathbuf, const char *dbname, sabdb *next)
684 : {
685 : char buf[FILENAME_MAX];
686 : char data[8096];
687 : char log[FILENAME_MAX];
688 : FILE *f;
689 : int fd;
690 : struct stat statbuf;
691 :
692 : sabdb *sdb;
693 : sdb = NULL;
694 :
695 5310 : snprintf(buf, sizeof(buf), "%s/%s/%s", pathbuf, dbname, UPLOGFILE);
696 5310 : if (MT_stat(buf, &statbuf) == -1)
697 : return next;
698 :
699 5310 : sdb = malloc(sizeof(sabdb));
700 5310 : *sdb = (sabdb) {
701 : .next = next,
702 : };
703 :
704 : /* store the database name */
705 5310 : snprintf(buf, sizeof(buf), "%s/%s", pathbuf, dbname);
706 5310 : sdb->path = strdup(buf);
707 5310 : sdb->dbname = sdb->path + strlen(sdb->path) - strlen(dbname);
708 :
709 :
710 : /* check the state of the server by looking at its gdk lock:
711 : * - if we can lock it, the server has crashed or isn't running
712 : * - if we can't open it because it's locked, the server is
713 : * running
714 : * - to distinguish between a crash and proper shutdown, consult
715 : * the uplog
716 : * - one exception to all above; if this is the same process, we
717 : * cannot lock (it always succeeds), hence, if we have the
718 : * same signature, we assume running if the uplog states so.
719 : */
720 5310 : snprintf(buf, sizeof(buf), "%s/%s/%s", pathbuf, dbname,
721 : _sabaoth_internal_uuid);
722 5310 : if (MT_stat(buf, &statbuf) == 0) {
723 : /* database has the same process signature as ours, which
724 : * means, it must be us, rely on the uplog state */
725 5310 : snprintf(log, sizeof(log), "%s/%s/%s", pathbuf, dbname, UPLOGFILE);
726 5310 : if ((f = MT_fopen(log, "r")) != NULL) {
727 5310 : (void)fseek(f, -1, SEEK_END);
728 5310 : if (fread(data, 1, 1, f) != 1) {
729 : /* the log is empty, assume no crash */
730 0 : sdb->state = SABdbInactive;
731 5310 : } else if (data[0] == '\t') {
732 : /* see if the database has finished starting */
733 5310 : snprintf(buf, sizeof(buf), "%s/%s/%s",
734 : pathbuf, dbname, STARTEDFILE);
735 5310 : if (MT_stat(buf, &statbuf) == -1) {
736 11 : sdb->state = SABdbStarting;
737 : } else {
738 5299 : sdb->state = SABdbRunning;
739 : }
740 : } else { /* should be \n */
741 0 : sdb->state = SABdbInactive;
742 : }
743 5310 : (void)fclose(f);
744 : }
745 0 : } else if (snprintf(buf, sizeof(buf), "%s/%s/%s", pathbuf, dbname, ".gdk_lock"),
746 0 : ((fd = MT_lockf(buf, F_TLOCK)) == -2)) {
747 : /* Locking failed; this can be because the lockfile couldn't
748 : * be created. Probably there is no Mserver running for
749 : * that case also.
750 : */
751 0 : sdb->state = SABdbInactive;
752 0 : } else if (fd == -1) {
753 : #ifndef WIN32
754 : /* extract process ID from lock file */
755 0 : if ((f = MT_fopen(buf, "r")) != NULL) {
756 : int pid;
757 0 : if (fscanf(f, "USR=%*d PID=%d TIME=", &pid) == 1)
758 0 : sdb->pid = pid;
759 0 : fclose(f);
760 : }
761 : #endif
762 : /* see if the database has finished starting */
763 0 : snprintf(buf, sizeof(buf), "%s/%s/%s", pathbuf, dbname, STARTEDFILE);
764 0 : if (MT_stat(buf, &statbuf) == -1) {
765 0 : sdb->state = SABdbStarting;
766 : } else {
767 0 : sdb->state = SABdbRunning;
768 : }
769 : } else {
770 : /* file was not locked (we just locked it), check for a crash
771 : * in the uplog */
772 0 : snprintf(log, sizeof(log), "%s/%s/%s", pathbuf, dbname, STARTEDFILE);
773 : /* just to be sure, remove the .started file */
774 : (void) MT_remove(log); /* may fail, that's fine */
775 0 : snprintf(log, sizeof(log), "%s/%s/%s", pathbuf, dbname, UPLOGFILE);
776 0 : if ((f = MT_fopen(log, "r")) != NULL) {
777 0 : (void)fseek(f, -1, SEEK_END);
778 0 : if (fread(data, 1, 1, f) != 1) {
779 : /* the log is empty, assume no crash */
780 0 : sdb->state = SABdbInactive;
781 0 : } else if (data[0] == '\n') {
782 0 : sdb->state = SABdbInactive;
783 : } else { /* should be \t */
784 0 : sdb->state = SABdbCrashed;
785 : }
786 0 : (void)fclose(f);
787 : } else {
788 : /* no uplog, so presumably never started */
789 0 : sdb->state = SABdbInactive;
790 : }
791 0 : MT_lockf(buf, F_ULOCK);
792 0 : close(fd);
793 : }
794 5310 : snprintf(buf, sizeof(buf), "%s/%s/%s", pathbuf, dbname, MAINTENANCEFILE);
795 5310 : sdb->locked = MT_stat(buf, &statbuf) == 0;
796 :
797 : /* add scenarios that are supported */
798 5310 : sdb->scens = NULL;
799 5310 : snprintf(buf, sizeof(buf), "%s/%s/%s", pathbuf, dbname, SCENARIOFILE);
800 5310 : if ((f = MT_fopen(buf, "r")) != NULL) {
801 : sablist* np = NULL;
802 21240 : while (fgets(data, (int) sizeof(data), f) != NULL) {
803 15930 : if (*data != '\0' && data[strlen(data) - 1] == '\n')
804 15930 : data[strlen(data) - 1] = '\0';
805 15930 : if (sdb->scens == NULL) {
806 5310 : np = sdb->scens = malloc(sizeof(sablist));
807 : } else {
808 10620 : np = np->next = malloc(sizeof(sablist));
809 : }
810 15930 : np->val = strdup(data);
811 15930 : np->next = NULL;
812 : }
813 5310 : (void)fclose(f);
814 : }
815 :
816 : /* add how this server can be reached */
817 5310 : sdb->conns = NULL;
818 5310 : snprintf(buf, sizeof(buf), "%s/%s/%s", pathbuf, dbname, CONNECTIONFILE);
819 5310 : if ((f = MT_fopen(buf, "r")) != NULL) {
820 : sablist* np = NULL;
821 15930 : while (fgets(data, (int) sizeof(data), f) != NULL) {
822 10620 : if (*data != '\0' && data[strlen(data) - 1] == '\n')
823 10620 : data[strlen(data) - 1] = '\0';
824 10620 : if (sdb->conns == NULL) {
825 5310 : np = sdb->conns = malloc(sizeof(sablist));
826 : } else {
827 5310 : np = np->next = malloc(sizeof(sablist));
828 : }
829 10620 : np->val = strdup(data);
830 10620 : np->next = NULL;
831 : }
832 5310 : (void)fclose(f);
833 : }
834 :
835 : // read the secret
836 : do {
837 : struct stat stb;
838 5310 : snprintf(buf, sizeof(buf), "%s/%s/%s", pathbuf, dbname, SECRETFILE);
839 5310 : if ((f = MT_fopen(buf, "r")) == NULL)
840 : break;
841 10620 : if (fstat(fileno(f), &stb) < 0) {
842 0 : fclose(f);
843 0 : break;
844 : }
845 5310 : size_t len = (size_t)stb.st_size;
846 5310 : char *secret = malloc(len + 1);
847 5310 : if (!secret) {
848 0 : fclose(f);
849 0 : break;
850 : }
851 5310 : if (fread(secret, 1, len, f) != len) {
852 0 : fclose(f);
853 0 : free(secret);
854 0 : break;
855 : }
856 5310 : fclose(f);
857 5310 : secret[len] = '\0';
858 5310 : sdb->secret = secret;
859 : } while (0);
860 :
861 : return sdb;
862 : }
863 :
864 : /**
865 : * Returns a list of populated sabdb structs. If dbname == NULL, the
866 : * list contains sabdb structs for all found databases in the dbfarm.
867 : * Otherwise, at most one sabdb struct is returned for the database from
868 : * the dbfarm that matches dbname.
869 : * If no database could be found, an empty list is returned. Each list
870 : * is terminated by a NULL entry.
871 : */
872 : char *
873 5310 : msab_getStatus(sabdb** ret, const char *dbname)
874 : {
875 : DIR *d;
876 : struct dirent *e;
877 : char data[8096];
878 : char pathbuf[FILENAME_MAX];
879 : char *p;
880 :
881 : /* Caching strategies (might be nice) should create a new struct with
882 : * the last modified time_t of the files involved, such that a stat is
883 : * sufficient to see if reparsing is necessary. The gdk_lock always has
884 : * to be checked to detect crashes. */
885 :
886 : sabdb *sdb;
887 5310 : sdb = *ret = NULL;
888 :
889 5310 : if (dbname && strpbrk(dbname, "/\\") != NULL) {
890 0 : snprintf(data, sizeof(data),
891 : "database name contains disallowed characters");
892 0 : return strdup(data);
893 : }
894 : /* scan the parent for directories */
895 5310 : if ((p = getFarmPath(pathbuf, sizeof(pathbuf), NULL)) != NULL)
896 : return(p);
897 5310 : if (dbname) {
898 5310 : *ret = msab_getSingleStatus(pathbuf, dbname, NULL);
899 5310 : return NULL;
900 : }
901 :
902 0 : d = opendir(pathbuf);
903 0 : if (d == NULL) {
904 0 : snprintf(data, sizeof(data), "failed to open directory %s: %s",
905 0 : pathbuf, strerror(errno));
906 0 : return(strdup(data));
907 : }
908 0 : while ((e = readdir(d)) != NULL) {
909 0 : if (strcmp(e->d_name, "..") == 0 || strcmp(e->d_name, ".") == 0)
910 0 : continue;
911 :
912 0 : sdb = msab_getSingleStatus(pathbuf, e->d_name, sdb);
913 : }
914 0 : (void)closedir(d);
915 :
916 0 : *ret = sdb;
917 0 : return(NULL);
918 : }
919 :
920 : /**
921 : * Frees up the sabdb structure returned by getStatus.
922 : */
923 : void
924 5310 : msab_freeStatus(sabdb** ret)
925 : {
926 : sabdb *p, *q;
927 : sablist *r, *s;
928 :
929 5310 : p = *ret;
930 10620 : while (p != NULL) {
931 5310 : free(p->path);
932 5310 : free(p->uri);
933 5310 : free(p->secret);
934 5310 : free(p->uplog);
935 5310 : r = p->scens;
936 21240 : while (r != NULL) {
937 15930 : if (r->val != NULL)
938 15930 : free(r->val);
939 15930 : s = r->next;
940 15930 : free(r);
941 : r = s;
942 : }
943 5310 : r = p->conns;
944 15930 : while (r != NULL) {
945 10620 : if (r->val != NULL)
946 10620 : free(r->val);
947 10620 : s = r->next;
948 10620 : free(r);
949 : r = s;
950 : }
951 5310 : q = p->next;
952 5310 : free(p);
953 : p = q;
954 : }
955 5310 : }
956 :
957 : /**
958 : * Parses the .uplog file for the given database, and fills ret with the
959 : * parsed information.
960 : */
961 : char *
962 0 : msab_getUplogInfo(sabuplog *ret, const sabdb *db)
963 : {
964 : char log[FILENAME_MAX];
965 : char data[24];
966 : char *p;
967 : FILE *f;
968 : time_t start, stop, up;
969 : int avg10[10];
970 : int avg30[30];
971 : int i = 0;
972 :
973 : /* early bailout if cached */
974 0 : if (db->uplog != NULL) {
975 0 : *ret = *db->uplog;
976 0 : return(NULL);
977 : }
978 :
979 0 : memset(avg10, 0, sizeof(int) * 10);
980 0 : memset(avg30, 0, sizeof(int) * 30);
981 :
982 : /* clear the struct */
983 0 : *ret = (sabuplog) {
984 : .minuptime = -1,
985 : .lastcrash = -1,
986 : .laststop = -1,
987 : };
988 :
989 0 : snprintf(log, sizeof(log), "%s/%s", db->path, UPLOGFILE);
990 0 : if ((f = MT_fopen(log, "r")) != NULL) {
991 : int c;
992 : start = stop = up = 0;
993 : p = data;
994 0 : while ((c = getc(f)) != EOF) {
995 0 : switch (c) {
996 0 : case '\t':
997 : /* start attempt */
998 0 : ret->startcntr++;
999 0 : if (start != 0)
1000 0 : ret->lastcrash = start;
1001 0 : memmove(&avg10[0], &avg10[1], sizeof(int) * 9);
1002 0 : memmove(&avg30[0], &avg30[1], sizeof(int) * 29);
1003 0 : avg10[9] = avg30[29] = ret->crashavg1 =
1004 0 : (start != 0);
1005 0 : *p = '\0';
1006 0 : ret->laststart = start = (time_t)atol(data);
1007 : p = data;
1008 0 : break;
1009 0 : case '\n':
1010 : /* successful stop */
1011 0 : ret->stopcntr++;
1012 0 : *p = '\0';
1013 0 : ret->laststop = stop = (time_t)atol(data);
1014 : p = data;
1015 0 : i = (int) (stop - start);
1016 0 : if (i > ret->maxuptime)
1017 0 : ret->maxuptime = i;
1018 0 : if (ret->minuptime == -1 || ret->minuptime > stop - start)
1019 0 : ret->minuptime = stop - start;
1020 0 : up += i;
1021 : start = 0;
1022 0 : break;
1023 0 : default:
1024 : /* timestamp */
1025 0 : *p++ = c;
1026 0 : break;
1027 : }
1028 : }
1029 0 : if (start != 0 && db->state != SABdbRunning)
1030 0 : ret->lastcrash = start;
1031 0 : memmove(&avg10[0], &avg10[1], sizeof(int) * 9);
1032 0 : memmove(&avg30[0], &avg30[1], sizeof(int) * 29);
1033 0 : avg10[9] = avg30[29] = ret->crashavg1 =
1034 0 : (start != 0 ? (db->state != SABdbRunning) : 0);
1035 0 : ret->crashcntr =
1036 0 : ret->startcntr - (db->state == SABdbRunning) -
1037 0 : ret->stopcntr;
1038 0 : for (i = 0; i < 10; i++)
1039 0 : ret->crashavg10 += avg10[i];
1040 0 : ret->crashavg10 = ret->crashavg10 / 10.0;
1041 0 : for (i = 0; i < 30; i++)
1042 0 : ret->crashavg30 += avg30[i];
1043 0 : ret->crashavg30 = ret->crashavg30 / 30.0;
1044 :
1045 0 : if (ret->stopcntr > 0) {
1046 0 : ret->avguptime = (time_t)(((double)up / (double)ret->stopcntr) + 0.5);
1047 : } else {
1048 0 : ret->avguptime = 0;
1049 0 : ret->minuptime = 0;
1050 0 : ret->maxuptime = 0;
1051 : }
1052 0 : (void)fclose(f);
1053 : } else {
1054 : char buf[2*FILENAME_MAX];
1055 0 : snprintf(buf, sizeof(buf), "could not open file %s: %s",
1056 0 : log, strerror(errno));
1057 0 : return(strdup(buf));
1058 : }
1059 :
1060 : /* Don't store the sabuplog in the sabdb as there is no good reason
1061 : * to retrieve the sabuplog struct more than once for a database
1062 : * (without refetching the sabdb struct). Currently, only a
1063 : * serialisation/deserialisation of a sabdb struct will prefill the
1064 : * sabuplog struct. */
1065 0 : return(NULL);
1066 : }
1067 :
1068 : /* used in the serialisation to be able to change it in the future */
1069 : #define SABDBVER "2"
1070 :
1071 : /**
1072 : * Produces a string representation suitable for storage/sending.
1073 : */
1074 : char *
1075 0 : msab_serialise(char **ret, const sabdb *db)
1076 : {
1077 : char buf[8096];
1078 : char scens[64];
1079 : sablist *l;
1080 : sabuplog dbu;
1081 : char *p;
1082 : size_t avail;
1083 : size_t len;
1084 :
1085 0 : scens[0] = '\0';
1086 : p = scens;
1087 : avail = sizeof(scens) - 1;
1088 0 : for (l = db->scens; l != NULL; l = l->next) {
1089 0 : len = strlen(l->val);
1090 0 : if (len > avail)
1091 : break;
1092 0 : memcpy(p, l->val, len);
1093 0 : p += len + 1;
1094 0 : avail -= len + 1;
1095 0 : memcpy(p - 1, "'", 2);
1096 : }
1097 0 : if (p != scens)
1098 0 : p[-1] = '\0';
1099 :
1100 0 : if ((p = msab_getUplogInfo(&dbu, db)) != NULL)
1101 : return(p);
1102 :
1103 : /* sabdb + sabuplog structs in one */
1104 0 : snprintf(buf, sizeof(buf), "sabdb:" SABDBVER ":"
1105 : "%s,%s,%d,%d,%s,"
1106 : "%d,%d,%d,"
1107 : "%" PRId64 ",%" PRId64 ",%" PRId64 ","
1108 : "%" PRId64 ",%" PRId64 ",%" PRId64 ","
1109 : "%d,%f,%f",
1110 0 : db->dbname, db->uri ? db->uri : "", db->locked,
1111 0 : (int) db->state, scens,
1112 : dbu.startcntr, dbu.stopcntr, dbu.crashcntr,
1113 0 : (int64_t) dbu.avguptime, (int64_t) dbu.maxuptime,
1114 0 : (int64_t) dbu.minuptime, (int64_t) dbu.lastcrash,
1115 0 : (int64_t) dbu.laststart, (int64_t) dbu.laststop,
1116 : dbu.crashavg1, dbu.crashavg10, dbu.crashavg30);
1117 :
1118 0 : *ret = strdup(buf);
1119 0 : return(NULL);
1120 : }
1121 :
1122 : /**
1123 : * Produces a sabdb struct out of a serialised string.
1124 : */
1125 : char *
1126 0 : msab_deserialise(sabdb **ret, const char *sdb)
1127 : {
1128 : char *dbname;
1129 : char *uri;
1130 : char *scens;
1131 : sabdb *s;
1132 : sabuplog *u;
1133 : const char *lasts;
1134 : char buf[FILENAME_MAX];
1135 :
1136 0 : if (strncmp(sdb, "sabdb:", 6) != 0) {
1137 0 : snprintf(buf, sizeof(buf),
1138 : "string is not a sabdb struct: %s", sdb);
1139 0 : return(strdup(buf));
1140 : }
1141 0 : sdb += 6;
1142 : /* Protocol 1 was used uptil Oct2012 and is no longer supported.
1143 : * Since Jul2012 a new state
1144 : * SABdbStarting was introduced, but not exposed to the client
1145 : * in serialise. In Feb2013, the path component was removed
1146 : * and replaced by a URI field. This meant dbname could no
1147 : * longer be deduced from path, and hence sent separately.
1148 : * Since the conns property became useless in the light of the
1149 : * added uri, it was dropped. On top of this, a laststop
1150 : * property was added to the uplog struct.
1151 : * These four changes were effectuated in protocol 2. When
1152 : * reading protocol 1, we use the path field to set dbname, but
1153 : * ignore the path information (and set uri to "<unknown>". The
1154 : * SABdbStarting state never occurs. */
1155 0 : if (strncmp(sdb, SABDBVER ":", sizeof(SABDBVER)) != 0) {
1156 0 : snprintf(buf, sizeof(buf),
1157 : "string has unsupported version: %s", sdb);
1158 0 : return(strdup(buf));
1159 : }
1160 0 : sdb += sizeof(SABDBVER);
1161 0 : lasts = strchr(sdb, ',');
1162 0 : if (lasts == NULL) {
1163 0 : snprintf(buf, sizeof(buf),
1164 : "string does not contain dbname: %s", sdb);
1165 0 : return(strdup(buf));
1166 : }
1167 0 : dbname = malloc(lasts - sdb + 1);
1168 0 : strcpy_len(dbname, sdb, lasts - sdb + 1);
1169 0 : sdb = ++lasts;
1170 0 : lasts = strchr(sdb, ',');
1171 0 : if (lasts == NULL) {
1172 0 : snprintf(buf, sizeof(buf),
1173 : "string does not contain uri: %s", sdb);
1174 0 : free(dbname);
1175 0 : return(strdup(buf));
1176 : }
1177 0 : uri = malloc(lasts - sdb + 1);
1178 0 : strcpy_len(uri, sdb, lasts - sdb + 1);
1179 0 : sdb = ++lasts;
1180 : int locked, state, n;
1181 0 : switch (sscanf(sdb, "%d,%d%n", &locked, &state, &n)) {
1182 0 : case 0:
1183 0 : free(uri);
1184 0 : free(dbname);
1185 0 : snprintf(buf, sizeof(buf),
1186 : "string does not contain locked state: %s", lasts);
1187 0 : return(strdup(buf));
1188 0 : case 1:
1189 0 : free(uri);
1190 0 : free(dbname);
1191 0 : snprintf(buf, sizeof(buf),
1192 : "string does not contain state: %s", lasts);
1193 0 : return(strdup(buf));
1194 0 : case -1:
1195 0 : free(uri);
1196 0 : free(dbname);
1197 0 : return strdup("should not happen");
1198 : default:
1199 : break;
1200 : }
1201 0 : sdb += n;
1202 0 : if (*sdb++ != ',' || (lasts = strchr(sdb, ',')) == NULL) {
1203 0 : snprintf(buf, sizeof(buf),
1204 : "string does not contain scenarios: %s", lasts);
1205 0 : free(uri);
1206 0 : free(dbname);
1207 0 : return(strdup(buf));
1208 : }
1209 0 : if (lasts > sdb) {
1210 0 : scens = malloc(lasts - sdb + 1);
1211 0 : strcpy_len(scens, sdb, lasts - sdb + 1);
1212 : } else {
1213 : scens = NULL;
1214 : }
1215 0 : sdb = ++lasts;
1216 : int startcntr, stopcntr, crashcntr;
1217 : int64_t avguptime, maxuptime, minuptime, lastcrash, laststart, laststop;
1218 : int crashavg1;
1219 : double crashavg10, crashavg30;
1220 0 : switch (sscanf(sdb, "%d,%d,%d,%" SCNd64 ",%" SCNd64 ",%" SCNd64 ",%" SCNd64 ",%" SCNd64 ",%" SCNd64 ",%d,%lf,%lf%n", &startcntr, &stopcntr, &crashcntr, &avguptime, &maxuptime, &minuptime, &lastcrash, &laststart, &laststop, &crashavg1, &crashavg10, &crashavg30, &n)) {
1221 0 : case -1:
1222 0 : free(dbname);
1223 0 : free(uri);
1224 0 : free(scens);
1225 0 : return strdup("should not happen");
1226 0 : case 0:
1227 0 : snprintf(buf, sizeof(buf),
1228 : "string does not contain startcounter: %s", sdb);
1229 0 : goto bailout;
1230 0 : case 1:
1231 0 : snprintf(buf, sizeof(buf),
1232 : "string does not contain stopcounter: %s", sdb);
1233 0 : goto bailout;
1234 0 : case 2:
1235 0 : snprintf(buf, sizeof(buf),
1236 : "string does not contain crashcounter: %s", sdb);
1237 0 : goto bailout;
1238 0 : case 3:
1239 0 : snprintf(buf, sizeof(buf),
1240 : "string does not contain avguptime: %s", sdb);
1241 0 : goto bailout;
1242 0 : case 4:
1243 0 : snprintf(buf, sizeof(buf),
1244 : "string does not contain maxuptime: %s", sdb);
1245 0 : goto bailout;
1246 0 : case 5:
1247 0 : snprintf(buf, sizeof(buf),
1248 : "string does not contain minuptime: %s", sdb);
1249 0 : goto bailout;
1250 0 : case 6:
1251 0 : snprintf(buf, sizeof(buf),
1252 : "string does not contain lastcrash: %s", sdb);
1253 0 : goto bailout;
1254 0 : case 7:
1255 0 : snprintf(buf, sizeof(buf),
1256 : "string does not contain laststart: %s", sdb);
1257 0 : goto bailout;
1258 0 : case 8:
1259 0 : snprintf(buf, sizeof(buf),
1260 : "string does not contain laststop: %s", sdb);
1261 0 : goto bailout;
1262 0 : case 9:
1263 0 : snprintf(buf, sizeof(buf),
1264 : "string does not contain crashavg1: %s", sdb);
1265 0 : goto bailout;
1266 0 : case 10:
1267 0 : snprintf(buf, sizeof(buf),
1268 : "string does not contain crashavg10: %s", sdb);
1269 0 : goto bailout;
1270 0 : case 11:
1271 0 : snprintf(buf, sizeof(buf),
1272 : "string does not contain crashavg30: %s", sdb);
1273 0 : goto bailout;
1274 : case 12:
1275 : break;
1276 : }
1277 0 : sdb += n;
1278 0 : if (*sdb) {
1279 0 : snprintf(buf, sizeof(buf),
1280 : "string contains additional garbage after crashavg30: %s",
1281 : sdb);
1282 0 : goto bailout;
1283 : }
1284 :
1285 0 : u = malloc(sizeof(sabuplog));
1286 0 : s = malloc(sizeof(sabdb));
1287 0 : *u = (sabuplog) {
1288 : .startcntr = startcntr,
1289 : .stopcntr = stopcntr,
1290 : .crashcntr = crashcntr,
1291 0 : .avguptime = (time_t) avguptime,
1292 0 : .maxuptime = (time_t) maxuptime,
1293 0 : .minuptime = (time_t) minuptime,
1294 0 : .lastcrash = (time_t) lastcrash,
1295 0 : .laststart = (time_t) laststart,
1296 0 : .laststop = (time_t) laststop,
1297 : .crashavg1 = crashavg1,
1298 : .crashavg10 = crashavg10,
1299 : .crashavg30 = crashavg30,
1300 : };
1301 0 : *s = (sabdb) {
1302 : .dbname = dbname,
1303 : .path = dbname,
1304 : .uri = uri,
1305 : .locked = locked,
1306 0 : .state = (SABdbState) state,
1307 : .uplog = u,
1308 : };
1309 0 : if (scens) {
1310 0 : sablist **sp = &s->scens;
1311 : char *sc = scens;
1312 0 : while (sc) {
1313 0 : *sp = malloc(sizeof(sablist));
1314 0 : char *p = strchr(sc, '\'');
1315 0 : if (p)
1316 0 : *p++ = 0;
1317 0 : **sp = (sablist) {
1318 0 : .val = strdup(sc),
1319 : };
1320 : sc = p;
1321 0 : sp = &(*sp)->next;
1322 : }
1323 0 : free(scens);
1324 : }
1325 :
1326 0 : *ret = s;
1327 0 : return(NULL);
1328 0 : bailout:
1329 0 : free(dbname);
1330 0 : free(uri);
1331 0 : free(scens);
1332 0 : return strdup(buf);
1333 : }
|