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); }
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); }
/** * Returns a list of populated sabdb structs. If dbname == NULL, the * list contains sabdb structs for all found databases in the dbfarm. * Otherwise, at most one sabdb struct is returned for the database from * the dbfarm that matches dbname. * If no database could be found, an empty list is returned. Each list * is terminated by a NULL entry. */ str SABAOTHgetStatus(sabdb** ret, str dbname) { str err = msab_getStatus(ret, dbname); if (err != NULL) excFromMem(MAL, "sabaoth.getstatus", err); return(MAL_SUCCEED); }