/** * Helper function for commands in their most general form: no option * flags and just pushing all (database) arguments over to merovingian * for performing merocmd action. */ static void simple_command(int argc, char *argv[], char *merocmd, char *successmsg, char glob) { int i; sabdb *orig = NULL; sabdb *stats = NULL; char *e; if (argc == 1) { /* print help message for this command */ command_help(2, &argv[-1]); exit(1); } /* walk through the arguments and hunt for "options" */ for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--") == 0) { argv[i] = NULL; break; } if (argv[i][0] == '-') { fprintf(stderr, "%s: unknown option: %s\n", argv[0], argv[i]); command_help(argc + 1, &argv[-1]); exit(1); } } if (glob) { if ((e = MEROgetStatus(&orig, NULL)) != NULL) { fprintf(stderr, "%s: %s\n", argv[0], e); free(e); exit(2); } stats = globMatchDBS(argc, argv, &orig, argv[0]); msab_freeStatus(&orig); orig = stats; if (orig == NULL) exit(1); } else { for (i = 1; i < argc; i++) { if (argv[i] != NULL) { /* maintain input order */ if (orig == NULL) { stats = orig = calloc(1, sizeof(sabdb)); } else { stats = stats->next = calloc(1, sizeof(sabdb)); } stats->dbname = strdup(argv[i]); } } } simple_argv_cmd(argv[0], orig, merocmd, successmsg, NULL); msab_freeStatus(&orig); }
char* db_destroy(char* dbname) { sabdb* stats; char* e; char buf[8096]; if (dbname[0] == '\0') return(strdup("database name should not be an empty string")); /* the argument is the database to destroy, see what Sabaoth can * tell us about it */ if ((e = msab_getStatus(&stats, dbname)) != NULL) { snprintf(buf, sizeof(buf), "internal error: %s", e); free(e); return(strdup(buf)); } if (stats == NULL) { snprintf(buf, sizeof(buf), "no such database: %s", dbname); return(strdup(buf)); } if (stats->state == SABdbRunning) { snprintf(buf, sizeof(buf), "database '%s' is still running, " "please stop database first", dbname); msab_freeStatus(&stats); return(strdup(buf)); } /* annoyingly we have to delete file by file, and * directories recursively... */ if ((e = deletedir(stats->path)) != NULL) { snprintf(buf, sizeof(buf), "failed to destroy '%s': %s", dbname, e); free(e); msab_freeStatus(&stats); return(strdup(buf)); } msab_freeStatus(&stats); return(NULL); }
static void command_startstop(int argc, char *argv[], startstop mode) { int doall = 0; int i; char *e; sabdb *orig = NULL; sabdb *stats; sabdb *prev; char *type = NULL; char *action = NULL; char *p; char *nargv[64]; switch (mode) { case START: type = "start"; action = "starting database"; break; case STOP: type = "stop"; action = "stopping database"; break; case KILL: type = "kill"; action = "killing database"; break; } if (argc == 1) { /* print help message for this command */ command_help(2, &argv[-1]); exit(1); } else if (argc == 0) { exit(2); } /* time to collect some option flags */ for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { for (p = argv[i] + 1; *p != '\0'; p++) { switch (*p) { case 'a': doall = 1; break; case '-': if (p[1] == '\0') { if (argc - 1 > i) doall = 0; i = argc; break; } default: fprintf(stderr, "%s: unknown option: -%c\n", type, *p); command_help(2, &argv[-1]); exit(1); break; } } /* make this option no longer available, for easy use * lateron */ argv[i] = NULL; } } if ((e = MEROgetStatus(&orig, NULL)) != NULL) { fprintf(stderr, "%s: %s\n", type, e); free(e); exit(2); } if (doall != 1) { stats = globMatchDBS(argc, argv, &orig, type); msab_freeStatus(&orig); orig = stats; } argv = nargv; i = 0; argv[i++] = type; stats = orig; prev = NULL; while (stats != NULL) { /* When -a was given, we're supposed to start all known * databases. In this mode we should omit starting already * started databases, so we need to check first. */ if (doall == 1 && ( ((mode == STOP || mode == KILL) && (stats->state != SABdbRunning && stats->state != SABdbStarting)) || (mode == START && stats->state == SABdbRunning))) { /* needs not to be started/stopped, remove from list */ if (prev == NULL) { orig = stats->next; } else { prev->next = stats->next; } stats->next = NULL; msab_freeStatus(&stats); if (prev == NULL) { stats = orig; continue; } stats = prev; } prev = stats; stats = stats->next; } if (orig != NULL) { simple_argv_cmd(argv[0], orig, type, NULL, action); msab_freeStatus(&orig); } return; }
static void command_status(int argc, char *argv[]) { int doall = 1; /* we default to showing all */ int mode = 1; /* 0=crash, 1=short, 2=long */ char *state = "rbscl"; /* contains states to show */ int i; char *p; char *e; sabdb *stats; sabdb *orig; sabdb *prev; sabdb *neworig = NULL; int t; int twidth = TERMWIDTH; int dbwidth = 0; int uriwidth = 0; if (argc == 0) { exit(2); } /* time to collect some option flags */ for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { for (p = argv[i] + 1; *p != '\0'; p++) { switch (*p) { case 'c': mode = 0; break; case 'l': mode = 2; break; case 's': if (*(p + 1) != '\0') { state = ++p; } else if (i + 1 < argc && argv[i + 1][0] != '-') { state = argv[++i]; } else { fprintf(stderr, "status: -s needs an argument\n"); command_help(2, &argv[-1]); exit(1); } for (p = state; *p != '\0'; p++) { switch (*p) { case 'b': /* booting (starting up) */ case 'r': /* running (started) */ case 's': /* stopped */ case 'c': /* crashed */ case 'l': /* locked */ break; default: fprintf(stderr, "status: unknown flag for -s: -%c\n", *p); command_help(2, &argv[-1]); exit(1); } } p--; break; case '-': if (p[1] == '\0') { if (argc - 1 > i) doall = 0; i = argc; break; } default: fprintf(stderr, "status: unknown option: -%c\n", *p); command_help(2, &argv[-1]); exit(1); } } /* make this option no longer available, for easy use * lateron */ argv[i] = NULL; } else { doall = 0; } } if ((e = MEROgetStatus(&orig, NULL)) != NULL) { fprintf(stderr, "status: %s\n", e); free(e); exit(2); } /* look at the arguments and evaluate them based on a glob (hence we * listed all databases before) */ if (doall != 1) { stats = globMatchDBS(argc, argv, &orig, "status"); msab_freeStatus(&orig); orig = stats; } /* perform selection based on state (and order at the same time) */ for (p = &state[strlen(state) - 1]; p >= state; p--) { int curLock = 0; SABdbState curMode = SABdbIllegal; switch (*p) { case 'b': curMode = SABdbStarting; break; case 'r': curMode = SABdbRunning; break; case 's': curMode = SABdbInactive; break; case 'c': curMode = SABdbCrashed; break; case 'l': curLock = 1; break; } stats = orig; prev = NULL; while (stats != NULL) { if (stats->locked == curLock && (curLock == 1 || (curLock == 0 && stats->state == curMode))) { sabdb *next = stats->next; stats->next = neworig; neworig = stats; if (prev == NULL) { orig = next; } else { prev->next = next; } stats = next; } else { prev = stats; stats = stats->next; } } } msab_freeStatus(&orig); orig = neworig; if (mode == 1 && orig != NULL) { int len = 0; /* calculate dbwidth and uriwidth */ for (stats = orig; stats != NULL; stats = stats->next) { if ((t = strlen(stats->dbname)) > dbwidth) dbwidth = t; if (stats->uri != NULL && (t = strlen(stats->uri)) > uriwidth) uriwidth = t; if (uriwidth < 32) uriwidth = 32; } /* Ultra Condensed State(tm) since Feb2013: state R 6s (Running) R 14w R 99y (purely hypothetical) B 3s (Booting: in practice cannot be observed yet due to lock) S 1w (Stopped) LR12h (Locked/Running) LS (Locked/Stopped) C (Crashed) = 5 chars */ /* health health 100% 12d 42% 4s = 8 chars */ len = (dbwidth < 4 ? 4 : dbwidth) + 2 + 5 + 2 + 8 + 2 + uriwidth; if (twidth > 0 && len > twidth) { if (len - twidth < 10) { uriwidth -= len - twidth; if (dbwidth < 4) dbwidth = 4; } else { /* reduce relative to usage */ if (dbwidth < 4) { dbwidth = 4; } else { dbwidth = (int)(dbwidth * 1.0 / (dbwidth + uriwidth) * (len - twidth)); if (dbwidth < 4) dbwidth = 4; } uriwidth = twidth - (dbwidth + 2 + 5 + 2 + 8 + 2); if (uriwidth < 8) uriwidth = 8; } } else { if (dbwidth < 4) dbwidth = 4; } /* print header */ printf("%*sname%*s state health %*sremarks\n", (dbwidth - 4) / 2, "", (dbwidth - 4 + 1) / 2, "", (uriwidth - 7) / 2, ""); } for (stats = orig; stats != NULL; stats = stats->next) printStatus(stats, mode, dbwidth, uriwidth); if (orig != NULL) msab_freeStatus(&orig); }
static void command_destroy(int argc, char *argv[]) { int i; int force = 0; /* ask for confirmation */ char *e; sabdb *orig = NULL; sabdb *stats = NULL; if (argc == 1) { /* print help message for this command */ command_help(argc + 1, &argv[-1]); exit(1); } /* walk through the arguments and hunt for "options" */ for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--") == 0) { argv[i] = NULL; break; } if (argv[i][0] == '-') { if (argv[i][1] == 'f') { force = 1; argv[i] = NULL; } else { fprintf(stderr, "destroy: unknown option: %s\n", argv[i]); command_help(argc + 1, &argv[-1]); exit(1); } } } if ((e = MEROgetStatus(&orig, NULL)) != NULL) { fprintf(stderr, "destroy: %s\n", e); free(e); exit(2); } stats = globMatchDBS(argc, argv, &orig, "destroy"); msab_freeStatus(&orig); orig = stats; if (orig == NULL) exit(1); if (force == 0) { char answ; printf("you are about to remove database%s ", orig->next != NULL ? "s" : ""); for (stats = orig; stats != NULL; stats = stats->next) printf("%s'%s'", stats != orig ? ", " : "", stats->dbname); printf("\nALL data in %s will be lost, are you sure? [y/N] ", orig->next != NULL ? "these databases" : "this database"); if (scanf("%c", &answ) >= 1 && (answ == 'y' || answ == 'Y')) { /* do it! */ } else { printf("aborted\n"); exit(1); } } else { char *ret; char *out; for (stats = orig; stats != NULL; stats = stats->next) { if (stats->state == SABdbRunning || stats->state == SABdbStarting) { ret = control_send(&out, mero_host, mero_port, stats->dbname, "stop", 0, mero_pass); if (ret != NULL) free(ret); } } } simple_argv_cmd(argv[0], orig, "destroy", "destroyed database", NULL); msab_freeStatus(&orig); }
static void command_create(int argc, char *argv[]) { int i; char *mfunnel = NULL; char *password = NULL; sabdb *orig = NULL; sabdb *stats = NULL; if (argc == 1) { /* print help message for this command */ command_help(argc + 1, &argv[-1]); exit(1); } /* walk through the arguments and hunt for "options" */ for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--") == 0) { argv[i] = NULL; break; } if (argv[i][0] == '-') { if (argv[i][1] == 'm') { if (argv[i][2] != '\0') { mfunnel = &argv[i][2]; argv[i] = NULL; } else if (i + 1 < argc && argv[i + 1][0] != '-') { argv[i] = NULL; mfunnel = argv[++i]; argv[i] = NULL; } else { fprintf(stderr, "create: -m needs an argument\n"); command_help(2, &argv[-1]); exit(1); } } else if (argv[i][1] == 'p') { if (argv[i][2] != '\0') { password = &argv[i][2]; argv[i] = NULL; } else if (i + 1 < argc && argv[i + 1][0] != '-') { argv[i] = NULL; password = argv[++i]; argv[i] = NULL; } else { fprintf(stderr, "create: -p needs an argument\n"); command_help(2, &argv[-1]); exit(1); } } else { fprintf(stderr, "create: unknown option: %s\n", argv[i]); command_help(argc + 1, &argv[-1]); exit(1); } } } for (i = 1; i < argc; i++) { if (argv[i] != NULL) { /* maintain input order */ if (orig == NULL) { stats = orig = calloc(1, sizeof(sabdb)); } else { stats = stats->next = calloc(1, sizeof(sabdb)); } stats->dbname = strdup(argv[i]); } } if (mfunnel != NULL) { size_t len = strlen("create mfunnel=") + strlen(mfunnel) + 1; char *cmd = malloc(len); snprintf(cmd, len, "create mfunnel=%s", mfunnel); simple_argv_cmd(argv[0], orig, cmd, "created multiplex-funnel in maintenance mode", NULL); free(cmd); } else if (password != NULL) { size_t len = strlen("create password="******"create password=%s", password); simple_argv_cmd(argv[0], orig, cmd, "created database with password for monetdb user", NULL); free(cmd); } else { simple_argv_cmd(argv[0], orig, "create", "created database in maintenance mode", NULL); } msab_freeStatus(&orig); }
static void command_get(int argc, char *argv[]) { char doall = 1; char *p; char *property = NULL; char propall = 0; char vbuf[512]; char *buf = 0; char *e; int i; sabdb *orig, *stats; int twidth = TERMWIDTH; char *source, *value = NULL; confkeyval *kv; confkeyval *defprops = getDefaultProps(); confkeyval *props = getDefaultProps(); if (argc == 1) { /* print help message for this command */ command_help(2, &argv[-1]); exit(1); } else if (argc == 0) { exit(2); } /* time to collect some option flags */ for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { for (p = argv[i] + 1; *p != '\0'; p++) { switch (*p) { case '-': if (p[1] == '\0') { if (argc - 1 > i) doall = 0; i = argc; break; } default: fprintf(stderr, "get: unknown option: -%c\n", *p); command_help(2, &argv[-1]); exit(1); break; } } /* make this option no longer available, for easy use * lateron */ argv[i] = NULL; } else if (property == NULL) { /* first non-option is property, rest is database */ property = argv[i]; argv[i] = NULL; if (strcmp(property, "all") == 0) propall = 1; } else { doall = 0; } } if (property == NULL) { fprintf(stderr, "get: need a property argument\n"); command_help(2, &argv[-1]); exit(1); } if ((e = MEROgetStatus(&orig, NULL)) != NULL) { fprintf(stderr, "get: %s\n", e); free(e); exit(2); } /* look at the arguments and evaluate them based on a glob (hence we * listed all databases before) */ if (doall != 1) { stats = globMatchDBS(argc, argv, &orig, "get"); msab_freeStatus(&orig); orig = stats; } /* avoid work when there are no results */ if (orig == NULL) { free(props); free(defprops); return; } e = control_send(&buf, mero_host, mero_port, "#defaults", "get", 1, mero_pass); if (e != NULL) { fprintf(stderr, "get: %s\n", e); free(e); exit(2); } else if ( buf && strncmp(buf, "OK\n", 3) != 0) { fprintf(stderr, "get: %s\n", buf); free(buf); exit(1); } readPropsBuf(defprops, buf + 3); if( buf) free(buf); if (twidth > 0) { /* name = 15 */ /* prop = 8 */ /* source = 7 */ twidth -= 15 + 2 + 8 + 2 + 7 + 2; if (twidth < 6) twidth = 6; value = malloc(sizeof(char) * twidth + 1); } stats = orig; while (stats != NULL) { e = control_send(&buf, mero_host, mero_port, stats->dbname, "get", 1, mero_pass); if (e != NULL) { fprintf(stderr, "get: %s\n", e); free(e); exit(2); } else if (strncmp(buf, "OK\n", 3) != 0) { fprintf(stderr, "get: %s\n", buf); free(buf); exit(1); } readPropsBuf(props, buf + 3); free(buf); if (propall == 1) { size_t off = 0; kv = props; off += snprintf(vbuf, sizeof(vbuf), "name"); while (kv->key != NULL) { off += snprintf(vbuf + off, sizeof(vbuf) - off, ",%s", kv->key); kv++; } } else { /* check validity of properties before printing them */ if (stats == orig) { snprintf(vbuf, sizeof(vbuf), "%s", property); buf = vbuf; while ((p = strtok(buf, ",")) != NULL) { buf = NULL; if (strcmp(p, "name") == 0) continue; kv = findConfKey(props, p); if (kv == NULL) fprintf(stderr, "get: no such property: %s\n", p); } } snprintf(vbuf, sizeof(vbuf), "%s", property); } buf = vbuf; /* print header after errors */ if (stats == orig) printf(" name prop source value\n"); while ((p = strtok(buf, ",")) != NULL) { buf = NULL; /* filter properties based on object type */ kv = findConfKey(props, "type"); if (kv != NULL && kv->val != NULL) { if (strcmp(kv->val, "mfunnel") == 0) { if (strcmp(p, "name") != 0 && strcmp(p, "type") != 0 && strcmp(p, "mfunnel") != 0 && strcmp(p, "shared") != 0) continue; } } else { /* no type == database (default) */ if (strcmp(p, "mfunnel") == 0) continue; } /* special virtual case */ if (strcmp(p, "name") == 0) { source = "-"; if (twidth > 0) { abbreviateString(value, stats->dbname, twidth); } else { value = stats->dbname; } } else { kv = findConfKey(props, p); if (kv == NULL) continue; if (kv->val == NULL) { char *y = NULL; kv = findConfKey(defprops, p); source = "default"; y = kv != NULL && kv->val != NULL ? kv->val : "<unknown>"; if (twidth > 0) { abbreviateString(value, y, twidth); } else { value = y; } } else { source = "local"; if (twidth > 0) { abbreviateString(value, kv->val, twidth); } else { value = kv->val; } } } printf("%-15s %-8s %-7s %s\n", stats->dbname, p, source, value); } freeConfFile(props); stats = stats->next; } if (twidth > 0) free(value); msab_freeStatus(&orig); free(props); free(defprops); }
static void command_set(int argc, char *argv[], meroset type) { char *p = NULL; char property[24] = ""; int i; int state = 0; char *res; char *out; sabdb *orig = NULL; sabdb *stats = NULL; char *e; if (argc >= 1 && argc <= 2) { /* print help message for this command */ command_help(2, &argv[-1]); exit(1); } else if (argc == 0) { exit(2); } /* time to collect some option flags */ for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { for (p = argv[i] + 1; *p != '\0'; p++) { switch (*p) { case '-': if (p[1] == '\0') { i = argc; break; } default: fprintf(stderr, "%s: unknown option: -%c\n", argv[0], *p); command_help(2, &argv[-1]); exit(1); break; } } /* make this option no longer available, for easy use * lateron */ argv[i] = NULL; } else if (property[0] == '\0') { /* first non-option is property, rest is database */ p = argv[i]; if (type == SET) { if ((p = strchr(argv[i], '=')) == NULL) { fprintf(stderr, "set: need property=value\n"); command_help(2, &argv[-1]); exit(1); } *p = '\0'; snprintf(property, sizeof(property), "%s", argv[i]); *p++ = '='; p = argv[i]; } else { snprintf(property, sizeof(property), "%s", argv[i]); } argv[i] = NULL; } } if (property[0] == '\0') { fprintf(stderr, "%s: need a property argument\n", argv[0]); command_help(2, &argv[-1]); exit(1); } if ((e = MEROgetStatus(&orig, NULL)) != NULL) { fprintf(stderr, "%s: %s\n", argv[0], e); free(e); exit(2); } stats = globMatchDBS(argc, argv, &orig, argv[0]); msab_freeStatus(&orig); orig = stats; if (orig == NULL) { /* error already printed by globMatchDBS */ exit(1); } /* handle rename separately due to single argument constraint */ if (strcmp(property, "name") == 0) { if (type == INHERIT) { fprintf(stderr, "inherit: cannot default to a database name\n"); exit(1); } if (orig->next != NULL) { fprintf(stderr, "%s: cannot rename multiple databases to " "the same name\n", argv[0]); exit(1); } out = control_send(&res, mero_host, mero_port, orig->dbname, p, 0, mero_pass); if (out != NULL || strcmp(res, "OK") != 0) { res = out == NULL ? res : out; fprintf(stderr, "%s: %s\n", argv[0], res); state |= 1; } free(res); msab_freeStatus(&orig); exit(state); } for (stats = orig; stats != NULL; stats = stats->next) { if (type == INHERIT) { strncat(property, "=", sizeof(property) - strlen(property) - 1); p = property; } out = control_send(&res, mero_host, mero_port, stats->dbname, p, 0, mero_pass); if (out != NULL || strcmp(res, "OK") != 0) { res = out == NULL ? res : out; fprintf(stderr, "%s: %s\n", argv[0], res); state |= 1; } free(res); } msab_freeStatus(&orig); exit(state); }
void discoveryRunner(void *d) { int sock = *(int *)d; int s = -1; struct sockaddr_storage peer_addr; socklen_t peer_addr_len; fd_set fds; struct timeval tv; int c; /* avoid first announce, the HELO will cause an announce when it's * received by ourself */ time_t deadline = 1; time_t now = 0; int forceannc = 0; sabdb *orig; sabdb *stats; confkeyval *ckv; confkeyval *kv; confkeyval *discttl; err e; remotedb rdb; remotedb prv; char *val; ssize_t nread; char buf[512]; /* our packages should be pretty small */ char host[128]; char service[8]; /* start shouting around that we're here ;) request others to tell * what databases they have */ snprintf(buf, 512, "HELO %s", _mero_hostname); broadcast(buf); ckv = getDefaultProps(); discttl = findConfKey(_mero_props, "discoveryttl"); /* main loop */ while (_mero_keep_listening == 1) { now = time(NULL); /* do a round of announcements, we're ahead of the ttl because * when we announce, we add 60 seconds to avoid a "gap" */ if (forceannc == 1 || deadline <= now) { forceannc = 0; /* set new deadline */ deadline = now + discttl->ival; /* list all known databases */ if ((e = msab_getStatus(&stats, NULL)) != NULL) { Mfprintf(_mero_discerr, "msab_getStatus error: %s, " "discovery services disabled\n", e); free(e); return; } for (orig = stats; stats != NULL; stats = stats->next) { readProps(ckv, stats->path); kv = findConfKey(ckv, "shared"); val = kv->val == NULL ? "" : kv->val; /* skip databases under maintenance */ if (strcmp(val, "no") != 0 && stats->locked != 1) { /* craft ANNC message for this db */ if (strcmp(val, "yes") == 0) val = ""; snprintf(buf, 512, "ANNC %s%s%s mapi:monetdb://%s:%u/ %d", stats->dbname, val[0] == '\0' ? "" : "/", val, _mero_hostname, (unsigned int)getConfNum(_mero_props, "port"), discttl->ival + 60); broadcast(buf); } freeConfFile(ckv); } if (orig != NULL) msab_freeStatus(&orig); if (getConfNum(_mero_props, "control") != 0) { /* announce control port */ snprintf(buf, 512, "ANNC * %s:%u %d", _mero_hostname, (unsigned int)getConfNum(_mero_props, "port"), discttl->ival + 60); /* coverity[string_null] */ broadcast(buf); } } /* do a round to see if we have to cleanup anything (expired * ttl) */ pthread_mutex_lock(&_mero_remotedb_lock); prv = NULL; rdb = _mero_remotedbs; while (rdb != NULL) { if (rdb->ttl > 0 && rdb->ttl <= now) { /* expired, let's remove */ if (prv == NULL) { _mero_remotedbs = rdb->next; } else { prv->next = rdb->next; } Mfprintf(_mero_discout, "neighbour database %s%s " "has expired\n", rdb->conn, rdb->fullname); free(rdb->dbname); free(rdb->conn); free(rdb->fullname); free(rdb); break; } prv = rdb; rdb = rdb->next; } pthread_mutex_unlock(&_mero_remotedb_lock); peer_addr_len = sizeof(struct sockaddr_storage); FD_ZERO(&fds); FD_SET(sock, &fds); /* Wait up to 5 seconds. */ tv.tv_sec = 5; tv.tv_usec = 0; nread = select(sock + 1, &fds, NULL, NULL, &tv); if (nread <= 0) { /* assume only failure is EINTR */ /* nothing interesting has happened */ buf[0] = '\0'; continue; } nread = recvfrom(sock, buf, 512, 0, (struct sockaddr *)&peer_addr, &peer_addr_len); if (nread == -1) { buf[0] = '\0'; continue; /* ignore failed request */ } s = getnameinfo((struct sockaddr *)&peer_addr, peer_addr_len, host, 128, service, 8, NI_NUMERICSERV); if (s != 0) { Mfprintf(_mero_discerr, "cannot retrieve name info: %s\n", gai_strerror(s)); continue; /* skip this message */ } /* ignore messages from broadcast interface */ if (strcmp(host, "0.0.0.0") == 0) continue; /* forward messages not coming from ourself to all routes that * are active */ if (strcmp(host, _mero_hostname) != 0) { disc_message_tap h = _mero_disc_msg_taps; for (; h != NULL; h = h->next) { if (write(h->fd, buf, nread) == -1) { /* really nothing to be done here, since this is * best effort stuff, keep the condition to keep * fortification warnings off */ } } } if (strncmp(buf, "HELO ", 5) == 0) { /* HELLO message, respond with current databases */ Mfprintf(_mero_discout, "new neighbour %s (%s)\n", buf + 5, host); /* sleep a random amount of time to avoid an avalanche of * ANNC messages flooding the network */ /* coverity[dont_call] */ c = 1 + (int)(2500.0 * (rand() / (RAND_MAX + 1.0))); sleep_ms(c); /* force an announcement round by dropping the deadline */ forceannc = 1; continue; } else if (strncmp(buf, "LEAV ", 5) == 0) { /* LEAVE message, unregister database */ char *sp = NULL; char *dbname; char *conn; strtok_r(buf, " ", &sp); /* discard the msg type */ dbname = strtok_r(NULL, " ", &sp); conn = strtok_r(NULL, " ", &sp); if (dbname == NULL || conn == NULL) continue; if (removeRemoteDB(dbname, conn) == 0) Mfprintf(_mero_discout, "received leave request for unknown database " "%s%s from %s\n", conn, dbname, host); } else if (strncmp(buf, "ANNC ", 5) == 0) { /* ANNOUNCE message, register database */ char *sp = NULL; char *dbname; char *conn; char *ttl; strtok_r(buf, " ", &sp); /* discard the msg type */ dbname = strtok_r(NULL, " ", &sp); conn = strtok_r(NULL, " ", &sp); ttl = strtok_r(NULL, " ", &sp); if (dbname == NULL || conn == NULL || ttl == NULL) continue; if (addRemoteDB(dbname, conn, atoi(ttl)) == 1) { if (strcmp(dbname, "*") == 0) { Mfprintf(_mero_discout, "registered neighbour %s\n", conn); } else { Mfprintf(_mero_discout, "new database " "%s%s (ttl=%ss)\n", conn, dbname, ttl); } } } else { Mfprintf(_mero_discout, "ignoring unknown message from " "%s:%s: '%s'\n", host, service, buf); } } /* now notify of our soon to be absence ;) */ /* list all known databases */ if ((e = msab_getStatus(&stats, NULL)) != NULL) { Mfprintf(_mero_discerr, "msab_getStatus error: %s, " "discovery services disabled\n", e); free(e); free(ckv); return; } /* craft LEAV messages for each db */ c = 0; orig = stats; while (stats != NULL) { readProps(ckv, stats->path); kv = findConfKey(ckv, "shared"); if (kv->val != NULL && strcmp(kv->val, "no") != 0) { snprintf(buf, 512, "LEAV %s mapi:monetdb://%s:%u/", stats->dbname, _mero_hostname, (unsigned int)getConfNum(_mero_props, "port")); broadcast(buf); c = 1; } freeConfFile(ckv); stats = stats->next; } if (orig != NULL) msab_freeStatus(&orig); /* deregister this merovingian, so it doesn't remain a stale entry */ if (getConfNum(_mero_props, "control") != 0) { snprintf(buf, 512, "LEAV * %s:%u", _mero_hostname, (unsigned int)getConfNum(_mero_props, "port")); broadcast(buf); } free(ckv); }
/** * The terminateProcess function tries to let the given mserver process * shut down gracefully within a given time-out. If that fails, it * sends the deadly SIGKILL signal to the mserver process and returns. */ void terminateProcess(void *p) { dpair d = (dpair)p; sabdb *stats; char *er; int i; confkeyval *kv; /* make local copies since d will disappear when killed */ pid_t pid = d->pid; char *dbname = strdup(d->dbname); er = msab_getStatus(&stats, dbname); if (er != NULL) { Mfprintf(stderr, "cannot terminate process " LLFMT ": %s\n", (long long int)pid, er); free(er); free(dbname); return; } if (stats == NULL) { Mfprintf(stderr, "strange, process " LLFMT " serves database '%s' " "which does not exist\n", (long long int)pid, dbname); free(dbname); return; } switch (stats->state) { case SABdbRunning: /* ok, what we expect */ break; case SABdbCrashed: Mfprintf(stderr, "cannot shut down database '%s', mserver " "(pid " LLFMT ") has crashed\n", dbname, (long long int)pid); msab_freeStatus(&stats); free(dbname); return; case SABdbInactive: Mfprintf(stdout, "database '%s' appears to have shut down already\n", dbname); fflush(stdout); msab_freeStatus(&stats); free(dbname); return; default: Mfprintf(stderr, "unknown state: %d", (int)stats->state); msab_freeStatus(&stats); free(dbname); return; } if (d->type == MEROFUN) { multiplexDestroy(dbname); free(dbname); return; } else if (d->type != MERODB) { /* barf */ Mfprintf(stderr, "cannot stop merovingian process role: %s\n", dbname); free(dbname); return; } /* ok, once we get here, we'll be shutting down the server */ Mfprintf(stdout, "sending process " LLFMT " (database '%s') the " "TERM signal\n", (long long int)pid, dbname); kill(pid, SIGTERM); kv = findConfKey(_mero_props, "exittimeout"); for (i = 0; i < atoi(kv->val) * 2; i++) { if (stats != NULL) msab_freeStatus(&stats); sleep_ms(500); er = msab_getStatus(&stats, dbname); if (er != NULL) { Mfprintf(stderr, "unexpected problem: %s\n", er); free(er); /* don't die, just continue, so we KILL in the end */ } else if (stats == NULL) { Mfprintf(stderr, "hmmmm, database '%s' suddenly doesn't exist " "any more\n", dbname); } else { switch (stats->state) { case SABdbRunning: /* ok, try again */ break; case SABdbCrashed: Mfprintf (stderr, "database '%s' crashed after SIGTERM\n", dbname); msab_freeStatus(&stats); free(dbname); return; case SABdbInactive: Mfprintf(stdout, "database '%s' has shut down\n", dbname); fflush(stdout); msab_freeStatus(&stats); free(dbname); return; default: Mfprintf(stderr, "unknown state: %d", (int)stats->state); break; } } } Mfprintf(stderr, "timeout of %s seconds expired, sending process " LLFMT " (database '%s') the KILL signal\n", kv->val, (long long int)pid, dbname); kill(pid, SIGKILL); msab_freeStatus(&stats); free(dbname); return; }
char* db_create(char* dbname) { sabdb *stats; size_t c; char* e; char* dbfarm; char buf[8096]; char path[8096]; FILE *f; if ((e = db_validname(dbname)) != NULL) return(e); /* the argument is the database to create, see what Sabaoth can * tell us about it */ if ((e = msab_getStatus(&stats, dbname)) != NULL) { snprintf(buf, sizeof(buf), "internal error: %s", e); free(e); return(strdup(buf)); } /* if sabaoth doesn't know, then it's green light for us! */ if (stats != NULL) { msab_freeStatus(&stats); snprintf(buf, sizeof(buf), "database '%s' already exists", dbname); return(strdup(buf)); } if ((e = msab_getDBfarm(&dbfarm)) != NULL) { snprintf(buf, sizeof(buf), "internal error: %s", e); free(e); return(strdup(buf)); } /* create the directory */ c = snprintf(path, sizeof(path), "%s/%s", dbfarm, dbname); if (c >= sizeof(path)) { free(dbfarm); return(strdup("path/dbname combination too long, " "path would get truncated")); } if (mkdir(path, 0755) == -1) { snprintf(buf, sizeof(buf), "unable to create %s: %s", dbname, strerror(errno)); free(dbfarm); return(strdup(buf)); } /* perform another length check, with the .maintenance file, * which happens to be the longest */ c = snprintf(path, sizeof(path), "%s/%s/.maintenance", dbfarm, dbname); if (c >= sizeof(path)) { /* try to cleanup */ snprintf(path, sizeof(path), "%s/%s", dbfarm, dbname); rmdir(path); free(dbfarm); return(strdup("path/dbname combination too long, " "filenames inside would get truncated")); } /* put this database under maintenance, make sure no race condition * ever can happen, by putting it under maintenance before it even * exists for Merovingian */ if ((f = fopen(path, "w")) != NULL) fclose(f); /* if this fails, below probably fails too */ /* avoid GDK making fugly complaints */ snprintf(path, sizeof(path), "%s/%s/.gdk_lock", dbfarm, dbname); if ((f = fopen(path, "w")) == NULL) { snprintf(buf, sizeof(buf), "cannot write lock file: %s", strerror(errno)); free(dbfarm); return(strdup(buf)); } fclose(f); /* generate a vault key */ snprintf(path, sizeof(path), "%s/%s/.vaultkey", dbfarm, dbname); if ((e = generatePassphraseFile(path)) != NULL) { free(dbfarm); return(e); } /* without an .uplog file, Merovingian won't work, this * needs to be last to avoid race conditions */ snprintf(path, sizeof(path), "%s/%s/.uplog", dbfarm, dbname); fclose(fopen(path, "w")); free(dbfarm); return(NULL); }
/** * Frees up the sabdb structure returned by getStatus. */ str SABAOTHfreeStatus(sabdb** ret) { str err = msab_freeStatus(ret); if (err != NULL) excFromMem(MAL, "sabaoth.freestatus", err); return(MAL_SUCCEED); }