예제 #1
0
파일: database.c 프로젝트: MonetDB/MonetDB
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);
}
예제 #2
0
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);
}
예제 #3
0
/**
 * 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;
}
예제 #4
0
파일: database.c 프로젝트: MonetDB/MonetDB
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);
}
예제 #5
0
/**
 * 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);
}