LCOV - code coverage report
Current view: top level - common/utils - msabaoth.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 232 574 40.4 %
Date: 2021-10-13 02:24:04 Functions: 19 25 76.0 %

          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        5367 : getFarmPath(char *pathbuf, size_t size, const char *extra)
      64             : {
      65        5367 :         if (_sabaoth_internal_dbfarm == NULL)
      66           0 :                 return(strdup("sabaoth not initialized"));
      67             : 
      68        5367 :         if (extra == NULL) {
      69        5367 :                 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        5665 : getDBPath(char *pathbuf, size_t size, const char *extra)
      84             : {
      85        5665 :         if (_sabaoth_internal_dbfarm == NULL)
      86           0 :                 return(strdup("sabaoth not initialized"));
      87        5665 :         if (_sabaoth_internal_dbname == NULL)
      88           0 :                 return(strdup("sabaoth was not initialized as active database"));
      89             : 
      90        5665 :         if (extra == NULL) {
      91           0 :                 snprintf(pathbuf, size, "%s%c%s",
      92             :                                  _sabaoth_internal_dbfarm, DIR_SEP, _sabaoth_internal_dbname);
      93             :         } else {
      94        5665 :                 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        1571 : msab_isuuid(const char *restrict s)
     104             : {
     105             :         int hyphens = 0;
     106             : 
     107             :         /* correct length */
     108        1571 :         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         265 : msab_dbnameinit(const char *dbname)
     133             : {
     134         265 :         if (dbname == NULL) {
     135           0 :                 _sabaoth_internal_dbname = NULL;
     136             :         } else {
     137         265 :                 _sabaoth_internal_dbname = strdup(dbname);
     138             :         }
     139         265 : }
     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         265 : msab_init(const char *dbfarm, const char *dbname)
     148             : {
     149             :         size_t len;
     150             :         DIR *d;
     151             :         char *tmp;
     152             : 
     153         265 :         assert(dbfarm != NULL);
     154             : 
     155         265 :         if (_sabaoth_internal_dbfarm != NULL)
     156          10 :                 free(_sabaoth_internal_dbfarm);
     157         265 :         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         265 :         if (_sabaoth_internal_uuid == NULL)
     164         255 :                 _sabaoth_internal_uuid = generateUUID();
     165             : 
     166         265 :         len = strlen(dbfarm);
     167         265 :         _sabaoth_internal_dbfarm = strdup(dbfarm);
     168             :         /* remove trailing slashes, newlines and spaces */
     169         265 :         len--;
     170         265 :         while (len > 0 && (
     171         265 :                            _sabaoth_internal_dbfarm[len] == '/' ||
     172         265 :                            _sabaoth_internal_dbfarm[len] == '\n' ||
     173             :                            _sabaoth_internal_dbfarm[len] == ' ')) {
     174           0 :                 _sabaoth_internal_dbfarm[len] = '\0';
     175           0 :                 len--;
     176             :         }
     177             : 
     178         265 :         msab_dbnameinit(dbname);
     179             : 
     180             :         /* clean out old UUID files in case the database crashed in a
     181             :          * previous incarnation */
     182         265 :         if (_sabaoth_internal_dbname != NULL &&
     183         265 :                 (tmp = malloc(strlen(_sabaoth_internal_dbfarm) + strlen(_sabaoth_internal_dbname) + 2)) != NULL) {
     184         265 :                 sprintf(tmp, "%s%c%s", _sabaoth_internal_dbfarm, DIR_SEP, _sabaoth_internal_dbname);
     185         265 :                 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         265 :                         len = offsetof(struct dbe, path) + strlen(tmp) + 2;
     192        2101 :                         while ((e = readdir(d)) != NULL) {
     193        1571 :                                 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         265 :                         closedir(d);
     201             :                         /* remove in a separate loop after reading the directory,
     202             :                          * so as to not have any interference */
     203         276 :                         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         265 :                 free(tmp);
     211             :         }
     212         265 : }
     213             : void
     214         265 : msab_dbpathinit(const char *dbpath)
     215             : {
     216             :         char dbfarm[FILENAME_MAX];
     217             :         const char *p;
     218             : 
     219         265 :         p = strrchr(dbpath, DIR_SEP);
     220         265 :         assert(p != NULL);
     221         265 :         strncpy(dbfarm, dbpath, p - dbpath);
     222         265 :         dbfarm[p - dbpath] = 0;
     223         265 :         msab_init(dbfarm, p + 1);
     224         265 : }
     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         934 : msab_getUUID(char **ret)
     261             : {
     262         934 :         if (_sabaoth_internal_uuid == NULL)
     263           0 :                 return(strdup("sabaoth not initialized"));
     264         934 :         *ret = strdup(_sabaoth_internal_uuid);
     265         934 :         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         771 : 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         771 :         if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), SCENARIOFILE)) != NULL)
     285             :                 return(tmp);
     286             : 
     287         771 :         if ((f = MT_fopen(pathbuf, "a+")) != NULL) {
     288         771 :                 if ((len = fread(buf, 1, 255, f)) > 0) {
     289             :                         char *p;
     290             : 
     291         506 :                         buf[len] = '\0';
     292             :                         tmp = buf;
     293             :                         /* find newlines and evaluate string */
     294        1012 :                         while ((p = strchr(tmp, '\n')) != NULL) {
     295         506 :                                 *p = '\0';
     296         506 :                                 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         771 :                 fprintf(f, "%s\n", lang);
     305         771 :                 (void)fflush(f);
     306         771 :                 (void)fclose(f);
     307         771 :                 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         506 : 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         506 :         if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), SCENARIOFILE)) != NULL)
     329             :                 return(tmp);
     330             : 
     331         506 :         if ((f = MT_fopen(pathbuf, "a+")) != NULL) {
     332         506 :                 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         506 :                         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         506 :                         (void)fclose(f);
     374             :                         (void) MT_remove(pathbuf);  /* empty file? try to remove */
     375         506 :                         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         508 : msab_marchConnection(const char *host, const int port)
     393             : {
     394             :         FILE *f;
     395             :         char pathbuf[FILENAME_MAX];
     396             :         char *tmp;
     397             : 
     398         508 :         if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), CONNECTIONFILE)) != NULL)
     399             :                 return(tmp);
     400             : 
     401         508 :         if (port <= 0 && host[0] != '/')
     402           0 :                 return(strdup("UNIX domain connections should be given as "
     403             :                                           "absolute path"));
     404             : 
     405         508 :         if ((f = MT_fopen(pathbuf, "a")) != NULL) {
     406             :                 /* append to the file */
     407         508 :                 if (port > 0) {
     408         254 :                         fprintf(f, "mapi:monetdb://%s:%i/\n", host, port);
     409             :                 } else {
     410         254 :                         fprintf(f, "mapi:monetdb://%s\n", host);
     411             :                 }
     412         508 :                 (void)fflush(f);
     413         508 :                 (void)fclose(f);
     414         508 :                 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         518 : msab_wildRetreat(void)
     431             : {
     432             :         char pathbuf[FILENAME_MAX];
     433             :         char *tmp;
     434             : 
     435         518 :         if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), SCENARIOFILE)) != NULL)
     436             :                 return(tmp);
     437             :         (void) MT_remove(pathbuf);
     438             : 
     439         518 :         if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), CONNECTIONFILE)) != NULL)
     440             :                 return(tmp);
     441             :         (void) MT_remove(pathbuf);
     442             : 
     443         518 :         if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), STARTEDFILE)) != NULL)
     444             :                 return(tmp);
     445             :         (void) MT_remove(pathbuf);
     446             : 
     447         518 :         if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), _sabaoth_internal_uuid)) != NULL)
     448             :                 return(tmp);
     449             :         (void) MT_remove(pathbuf);
     450             : 
     451         518 :         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         265 : 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         265 :         if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), UPLOGFILE)) != NULL)
     475             :                 return(tmp);
     476             : 
     477         265 :         if ((f = MT_fopen(pathbuf, "a")) != NULL) {
     478             :                 /* append to the file */
     479         265 :                 fprintf(f, "%" PRId64 "\t", (int64_t)time(NULL));
     480         265 :                 (void)fflush(f);
     481         265 :                 (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         265 :         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         265 :         if (f)
     498         265 :                 fclose(f);
     499             : 
     500             :         /* remove any stray file that would suggest we've finished starting up */
     501         265 :         if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), STARTEDFILE)) != NULL)
     502             :                 return(tmp);
     503             :         (void) MT_remove(pathbuf);
     504             : 
     505             : 
     506         265 :         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         253 : msab_registerStarted(void)
     516             : {
     517             :         char pathbuf[FILENAME_MAX];
     518             :         char *tmp;
     519             :         FILE *fp;
     520             : 
     521             :         /* flag this database as started up */
     522         253 :         if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), STARTEDFILE)) != NULL)
     523             :                 return(tmp);
     524             :         fp = MT_fopen(pathbuf, "w");
     525         253 :         if (fp)
     526         253 :                 fclose(fp);
     527             :         else
     528           0 :                 return strdup("sabaoth cannot create " STARTEDFILE);
     529             : 
     530         253 :         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         253 : msab_registerStop(void)
     539             : {
     540             :         FILE *f;
     541             :         char pathbuf[FILENAME_MAX];
     542             :         char *tmp;
     543             : 
     544         253 :         if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), UPLOGFILE)) != NULL)
     545             :                 return(tmp);
     546             : 
     547         253 :         if ((f = MT_fopen(pathbuf, "a")) != NULL) {
     548             :                 /* append to the file */
     549         253 :                 fprintf(f, "%" PRId64 "\n", (int64_t)time(NULL));
     550         253 :                 (void)fflush(f);
     551         253 :                 (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         253 :         if ((tmp = getDBPath(pathbuf, sizeof(pathbuf), _sabaoth_internal_uuid)) != NULL)
     562             :                 return(tmp);
     563             :         (void) MT_remove(pathbuf);
     564         253 :         return(NULL);
     565             : }
     566             : 
     567             : #define SECRETFILE ".secret"
     568             : #define SECRET_LENGTH (32)
     569             : char *
     570         254 : 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         254 :         if ((e = getDBPath(pathbuf, sizeof(pathbuf), SECRETFILE)) != NULL)
     578             :                 return e;
     579             : 
     580             :         // delete existing so we can recreate with appropriate permissions
     581         254 :         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         254 :         secret = malloc(SECRET_LENGTH + 1);
     589         254 :         secret[SECRET_LENGTH] = '\0';
     590             : 
     591             : #if defined(HAVE_GETENTROPY)
     592         254 :         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        4318 :         for (size_t i = 0; i < sizeof(bin_secret); i++) {
     622        4064 :                 snprintf(secret + 2 * i, 3, "%02x", bin_secret[i]);
     623             :         }
     624             : 
     625         254 :         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         254 :         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         254 :         error = fwrite(secret, 1, SECRET_LENGTH, f) < SECRET_LENGTH;
     643         254 :         error |= fclose(f) < 0;
     644         254 :         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         254 :         if (generated_secret)
     654         254 :                 *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        5367 : msab_getMyStatus(sabdb** ret)
     668             : {
     669             :         char *err;
     670        5367 :         if (_sabaoth_internal_dbname == NULL)
     671           0 :                 return(strdup("sabaoth was not initialized as active database"));
     672        5367 :         err = msab_getStatus(ret, _sabaoth_internal_dbname);
     673        5367 :         if (err != NULL)
     674             :                 return(err);
     675        5367 :         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        5367 : 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        5367 :         snprintf(buf, sizeof(buf), "%s/%s/%s", pathbuf, dbname, UPLOGFILE);
     696        5367 :         if (MT_stat(buf, &statbuf) == -1)
     697             :                 return next;
     698             : 
     699        5367 :         sdb = malloc(sizeof(sabdb));
     700        5367 :         *sdb = (sabdb) {
     701             :                 .next = next,
     702             :         };
     703             : 
     704             :         /* store the database name */
     705        5367 :         snprintf(buf, sizeof(buf), "%s/%s", pathbuf, dbname);
     706        5367 :         sdb->path = strdup(buf);
     707        5367 :         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        5367 :         snprintf(buf, sizeof(buf), "%s/%s/%s", pathbuf, dbname,
     721             :                          _sabaoth_internal_uuid);
     722        5367 :         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        5367 :                 snprintf(log, sizeof(log), "%s/%s/%s", pathbuf, dbname, UPLOGFILE);
     726        5367 :                 if ((f = MT_fopen(log, "r")) != NULL) {
     727        5367 :                         (void)fseek(f, -1, SEEK_END);
     728        5367 :                         if (fread(data, 1, 1, f) != 1) {
     729             :                                 /* the log is empty, assume no crash */
     730           0 :                                 sdb->state = SABdbInactive;
     731        5367 :                         } else if (data[0] == '\t') {
     732             :                                 /* see if the database has finished starting */
     733        5367 :                                 snprintf(buf, sizeof(buf), "%s/%s/%s",
     734             :                                                  pathbuf, dbname, STARTEDFILE);
     735        5367 :                                 if (MT_stat(buf, &statbuf) == -1) {
     736          11 :                                         sdb->state = SABdbStarting;
     737             :                                 } else {
     738        5356 :                                         sdb->state = SABdbRunning;
     739             :                                 }
     740             :                         } else { /* should be \n */
     741           0 :                                 sdb->state = SABdbInactive;
     742             :                         }
     743        5367 :                         (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        5367 :         snprintf(buf, sizeof(buf), "%s/%s/%s", pathbuf, dbname, MAINTENANCEFILE);
     795        5367 :         sdb->locked = MT_stat(buf, &statbuf) == 0;
     796             : 
     797             :         /* add scenarios that are supported */
     798        5367 :         sdb->scens = NULL;
     799        5367 :         snprintf(buf, sizeof(buf), "%s/%s/%s", pathbuf, dbname, SCENARIOFILE);
     800        5367 :         if ((f = MT_fopen(buf, "r")) != NULL) {
     801             :                 sablist* np = NULL;
     802       21468 :                 while (fgets(data, (int) sizeof(data), f) != NULL) {
     803       16101 :                         if (*data != '\0' && data[strlen(data) - 1] == '\n')
     804       16101 :                                 data[strlen(data) - 1] = '\0';
     805       16101 :                         if (sdb->scens == NULL) {
     806        5367 :                                 np = sdb->scens = malloc(sizeof(sablist));
     807             :                         } else {
     808       10734 :                                 np = np->next = malloc(sizeof(sablist));
     809             :                         }
     810       16101 :                         np->val = strdup(data);
     811       16101 :                         np->next = NULL;
     812             :                 }
     813        5367 :                 (void)fclose(f);
     814             :         }
     815             : 
     816             :         /* add how this server can be reached */
     817        5367 :         sdb->conns = NULL;
     818        5367 :         snprintf(buf, sizeof(buf), "%s/%s/%s", pathbuf, dbname, CONNECTIONFILE);
     819        5367 :         if ((f = MT_fopen(buf, "r")) != NULL) {
     820             :                 sablist* np = NULL;
     821       16101 :                 while (fgets(data, (int) sizeof(data), f) != NULL) {
     822       10734 :                         if (*data != '\0' && data[strlen(data) - 1] == '\n')
     823       10734 :                                 data[strlen(data) - 1] = '\0';
     824       10734 :                         if (sdb->conns == NULL) {
     825        5367 :                                 np = sdb->conns = malloc(sizeof(sablist));
     826             :                         } else {
     827        5367 :                                 np = np->next = malloc(sizeof(sablist));
     828             :                         }
     829       10734 :                         np->val = strdup(data);
     830       10734 :                         np->next = NULL;
     831             :                 }
     832        5367 :                 (void)fclose(f);
     833             :         }
     834             : 
     835             :         // read the secret
     836             :         do {
     837             :                 struct stat stb;
     838        5367 :                 snprintf(buf, sizeof(buf), "%s/%s/%s", pathbuf, dbname, SECRETFILE);
     839        5367 :                 if ((f = MT_fopen(buf, "r")) == NULL)
     840             :                         break;
     841       10734 :                 if (fstat(fileno(f), &stb) < 0) {
     842           0 :                         fclose(f);
     843           0 :                         break;
     844             :                 }
     845        5367 :                 size_t len = (size_t)stb.st_size;
     846        5367 :                 char *secret = malloc(len + 1);
     847        5367 :                 if (!secret) {
     848           0 :                         fclose(f);
     849           0 :                         break;
     850             :                 }
     851        5367 :                 if (fread(secret, 1, len, f) != len) {
     852           0 :                         fclose(f);
     853           0 :                         free(secret);
     854           0 :                         break;
     855             :                 }
     856        5367 :                 fclose(f);
     857        5367 :                 secret[len] = '\0';
     858        5367 :                 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        5367 : 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        5367 :         sdb = *ret = NULL;
     888             : 
     889        5367 :         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        5367 :         if ((p = getFarmPath(pathbuf, sizeof(pathbuf), NULL)) != NULL)
     896             :                 return(p);
     897        5367 :         if (dbname) {
     898        5367 :                 *ret = msab_getSingleStatus(pathbuf, dbname, NULL);
     899        5367 :                 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        5367 : msab_freeStatus(sabdb** ret)
     925             : {
     926             :         sabdb *p, *q;
     927             :         sablist *r, *s;
     928             : 
     929        5367 :         p = *ret;
     930       10734 :         while (p != NULL) {
     931        5367 :                 free(p->path);
     932        5367 :                 free(p->uri);
     933        5367 :                 free(p->secret);
     934        5367 :                 free(p->uplog);
     935        5367 :                 r = p->scens;
     936       21468 :                 while (r != NULL) {
     937       16101 :                         if (r->val != NULL)
     938       16101 :                                 free(r->val);
     939       16101 :                         s = r->next;
     940       16101 :                         free(r);
     941             :                         r = s;
     942             :                 }
     943        5367 :                 r = p->conns;
     944       16101 :                 while (r != NULL) {
     945       10734 :                         if (r->val != NULL)
     946       10734 :                                 free(r->val);
     947       10734 :                         s = r->next;
     948       10734 :                         free(r);
     949             :                         r = s;
     950             :                 }
     951        5367 :                 q = p->next;
     952        5367 :                 free(p);
     953             :                 p = q;
     954             :         }
     955        5367 : }
     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             : }

Generated by: LCOV version 1.14