📄 mal_sabaoth.mx
字号:
@' The contents of this file are subject to the MonetDB Public License@' Version 1.1 (the "License"); you may not use this file except in@' compliance with the License. You may obtain a copy of the License at@' http://monetdb.cwi.nl/Legal/MonetDBLicense-1.1.html@'@' Software distributed under the License is distributed on an "AS IS"@' basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the@' License for the specific language governing rights and limitations@' under the License.@'@' The Original Code is the MonetDB Database System.@'@' The Initial Developer of the Original Code is CWI.@' Portions created by CWI are Copyright (C) 1997-2007 CWI.@' All Rights Reserved.@f mal_sabaoth@a Fabian Groffen@+ Cluster supportThe cluster facilitation currently only deals with (de-)registering ofservices offered by the local server to other servers.The name of this module is inspired by the Armada setting of anchienttimes and origanisational structures. Sabaoth, stands for ``Lord ofHosts'' in an army setting as found in the Bible's New Testament. Thismodule allows an army of Mservers to be aware of each other on a localmachine and redirect to each other when necessary.@- Implementation@h#ifndef _MAL_SABAOTH_DEF#define _MAL_SABAOTH_DEF#include <mal.h>#include <mal_exception.h>@-@c#include "mal_config.h"#include "mal_sabaoth.h"#include <stdio.h> /* fseek, rewind */#include <unistd.h> /* unlink and friends */#include <errno.h>#include <string.h> /* for getting error messages */#include <assert.h>/** the directory where the databases are (aka dbfarm) */str _sabaoth_internal_dbfarm = NULL;/** the database which is "active" */str _sabaoth_internal_dbname = NULL;static str getFarmPath(str *ret, size_t size, str extra) { if (_sabaoth_internal_dbfarm == NULL) throw(MAL, "sabaoth.assert", "Sabaoth was not initialised!"); if (extra == NULL) { snprintf(*ret, size, "%s", _sabaoth_internal_dbfarm); } else { snprintf(*ret, size, "%s%c%s", _sabaoth_internal_dbfarm, DIR_SEP, extra); } return(MAL_SUCCEED);}static str getDBPath(str *ret, size_t size, str extra) { if (_sabaoth_internal_dbfarm == NULL) throw(MAL, "sabaoth.assert", "Sabaoth was not initialised!"); if (_sabaoth_internal_dbname == NULL) throw(MAL, "sabaoth.assert", "Cannot use this function when no database is active"); if (extra == NULL) { snprintf(*ret, size, "%s%c%s", _sabaoth_internal_dbfarm, DIR_SEP, _sabaoth_internal_dbname); } else { snprintf(*ret, size, "%s%c%s%c%s", _sabaoth_internal_dbfarm, DIR_SEP, _sabaoth_internal_dbname, DIR_SEP, extra); } return(MAL_SUCCEED);}@hmal_export void SABAOTHinit(str dbfarm, str dbname);@c/** * Initialises this Sabaoth instance to use the given dbfarm and dbname. * dbfarm may be NULL to indicate that there is no active database. The * arguments are copied for internal use. */void SABAOTHinit(str dbfarm, str dbname) { size_t len; assert(dbfarm != NULL); if (_sabaoth_internal_dbfarm != NULL) free(_sabaoth_internal_dbfarm); if (_sabaoth_internal_dbname != NULL) free(_sabaoth_internal_dbname); len = strlen(dbfarm) + 1; _sabaoth_internal_dbfarm = malloc(sizeof(char) * (len)); memcpy(_sabaoth_internal_dbfarm, dbfarm, len); /* remove trailing slashes */ while (len > 0 && _sabaoth_internal_dbfarm[len - 1] == '/') { _sabaoth_internal_dbfarm[len - 1] = '\0'; len--; } if (dbname == NULL) { _sabaoth_internal_dbname = NULL; } else { len = strlen(dbname) + 1; _sabaoth_internal_dbname = malloc(sizeof(char) * (len)); memcpy(_sabaoth_internal_dbname, dbname, len); }}@hmal_export str SABAOTHmarchScenario(int *ret, str *lang);@c#define SCENARIOFILE ".scen"/** * Writes the given language to the scenarios file. If the file doesn't * exist, it is created. Multiple invocations of this function for the * same language are ignored. */str SABAOTHmarchScenario(int *ret, str *lang) { FILE *f; str buf = alloca(sizeof(char) * 256); /* should be enough for now */ int len; str path = alloca(sizeof(char) * (PATHLENGTH + 1)); str tmp; (void)ret; rethrow("sabaoth.marchScenario", tmp, getDBPath(&path, PATHLENGTH, SCENARIOFILE)); if ((f = fopen(path, "a+")) != NULL) { if ((len = fread(buf, 1, 255, f)) > 0) { str p; buf[len] = '\0'; /* find newlines and evaluate string */ while ((p = strchr(buf, '\n')) != NULL) { *p = '\0'; if (strcmp(buf, *lang) == 0) { (void)fclose(f); return(MAL_SUCCEED); } buf = p; } } /* append to the file */ fprintf(f, "%s\n", *lang); (void)fflush(f); (void)fclose(f); return(MAL_SUCCEED); } throw(IO, "sabaoth.marchScenario", "unable to open file %s", path);}@hmal_export str SABAOTHretreatScenario(int *ret, str *lang);@c/** * Removes the given language from the scenarios file. If the scenarios * file is empty (before or) after removing the language, the file is * removed. */str SABAOTHretreatScenario(int *ret, str *lang) { FILE *f; str buf = alloca(sizeof(char) * 256); /* should be enough for now */ int len; str path = alloca(sizeof(char) * (PATHLENGTH + 1)); str tmp; (void)ret; rethrow("sabaoth.retreatScenario", tmp, getDBPath(&path, PATHLENGTH, SCENARIOFILE)); if ((f = fopen(path, "a+")) != NULL) { if ((len = fread(buf, 1, 255, f)) > 0) { str p; FILE *tmp = tmpfile(); int written = 0; buf[len] = '\0'; /* find newlines and evaluate string */ while ((p = strchr(buf, '\n')) != NULL) { *p = '\0'; if (strcmp(buf, *lang) != 0) { fprintf(tmp, "%s\n", buf); written = 1; } buf = p; } if (written != 0) { buf = alloca(sizeof(char) * 256); /* no idea how to "move" a file by it's fd (sounds * impossible anyway) and tmpnam is so much "DO NOT USE" * that I decided to just copy over the file again... */ rewind(f); fflush(tmp); rewind(tmp); len = fread(buf, 1, 256, tmp); fwrite(buf, 1, len, f); fflush(f); fclose(f); fclose(tmp); /* this should remove it automagically */ return(MAL_SUCCEED); } else { (void)fclose(f); unlink(path); return(MAL_SUCCEED); } } else if (len == 0) { (void)fclose(f); unlink(path); return(MAL_SUCCEED); } else { /* some error */ str err = strerror(errno); (void)fclose(f); throw(IO, "sabaoth.retreatScenario", "error while reading from file: %s", err); } } throw(IO, "sabaoth.retreatScenario", "unable to open file %s", path);}@hmal_export str SABAOTHmarchConnection(int *ret, str *host, int *port, bit *ssl);@c#define CONNECTIONFILE ".conn"/** * Writes an URI to the connection file based on the given arguments. * If the file doesn't exist, it is created. Multiple invocations of * this function for the same arguments are NOT ignored. */str SABAOTHmarchConnection(int *ret, str *host, int *port, bit *ssl) { FILE *f; str path = alloca(sizeof(char) * (PATHLENGTH + 1)); str tmp; (void)ret; rethrow("sabaoth.marchConnection", tmp, getDBPath(&path, PATHLENGTH, CONNECTIONFILE)); if ((f = fopen(path, "a")) != NULL) { /* append to the file */ fprintf(f, "mapi%s:monetdb://%s:%i/\n", *ssl ? "s" : "", *host, *port); (void)fflush(f); (void)fclose(f); return(MAL_SUCCEED); } throw(IO, "sabaoth.marchConnection", "unable to open file %s", path);}@hmal_export str SABAOTHwildRetreat(int *ret);@c/** * Removes all known publications of available services. The function * name is a nostalgic phrase from "Defender of the Crown" from the * Commodore Amiga age. */str SABAOTHwildRetreat(int *ret) { str path = alloca(sizeof(char) * (PATHLENGTH + 1)); str tmp; (void)ret; rethrow("sabaoth.wildRetreat", tmp, getDBPath(&path, PATHLENGTH, SCENARIOFILE)); unlink(path); rethrow("sabaoth.wildRetreat", tmp, getDBPath(&path, PATHLENGTH, CONNECTIONFILE)); unlink(path); return(MAL_SUCCEED);}@hmal_export str SABAOTHregisterStart(int *ret);@c#define UPLOGFILE ".uplog"/** * Writes a start attempt to the sabaoth start/stop log. Examination of * the log at a later stage might reveal crashes of the server. */str SABAOTHregisterStart(int *ret) { /* The sabaoth uplog is in fact a simple two column table that * contains a start time and a stop time. Start times are followed * by a tab character, while stop times are followed by a newline. * This allows to detect crashes, while sabaoth only appends to the * uplog. */ FILE *f; str path = alloca(sizeof(char) * (PATHLENGTH + 1)); str tmp; (void)ret; rethrow("sabaoth.registerStart", tmp, getDBPath(&path, PATHLENGTH, UPLOGFILE)); if ((f = fopen(path, "a")) != NULL) { /* append to the file */ fprintf(f, SZFMT "\t", (size_t)time(NULL)); (void)fflush(f); (void)fclose(f); return(MAL_SUCCEED); } throw(IO, "sabaoth.registerStart", "unable to open file %s", path);}@hmal_export str SABAOTHregisterStop(int *ret);@c/** * Writes a start attempt to the sabaoth start/stop log. Examination of * the log at a later stage might reveal crashes of the server. */str SABAOTHregisterStop(int *ret) { /* The sabaoth uplog is in fact a simple two column table that * contains a start time and a stop time. Start times are followed * by a tab character, while stop times are followed by a newline. * This allows to detect crashes, while sabaoth only appends to the * uplog. */ FILE *f; str path = alloca(sizeof(char) * (PATHLENGTH + 1)); str tmp; (void)ret; rethrow("sabaoth.registerStop", tmp, getDBPath(&path, PATHLENGTH, UPLOGFILE)); if ((f = fopen(path, "a")) != NULL) { /* append to the file */ fprintf(f, SZFMT "\n", (size_t)time(NULL)); (void)fflush(f); (void)fclose(f); return(MAL_SUCCEED); } throw(IO, "sabaoth.registerStop", "unable to open file %s", path);}@htypedef struct Ssablist { str val; /* list value */ struct Ssablist* next; /* pointer to the next available value*/} sablist;#define SABdbIllegal 0#define SABdbRunning 1#define SABdbCrashed 2#define SABdbInactive 3typedef struct Ssabdb { str dbname; /* database name as string */ /* str path; */ short state; /* SABdbRunning, SABdbCrashed or SABdbInactive */ sablist* scens; /* scenarios available for this database */ sablist* conns; /* connections available for this database */ struct Ssabdb* next; /* next database */} sabdb;/* Caching strategies (might be nice) should create a new struct with * the last modified time_t of the files involved, such that a stat is * sufficient to see if reparsing is necessary. The gdk_lock always has * to be checked to detect crashes. */mal_export str SABAOTHgetStatus(sabdb** ret, str dbname);@c/** */str SABAOTHgetStatus(sabdb** ret, str dbname) { DIR *d; struct dirent *e; str buf = alloca(sizeof(char) * (PATHLENGTH + 1)); str data = alloca(sizeof(char) * 8096); str path = alloca(sizeof(char) * (PATHLENGTH + 1)); str p; int len; FILE *f; int fd; sabdb* sdb = *ret; sdb = NULL; buf[PATHLENGTH] = '\0'; /* scan the parent for directories */ rethrow("sabaoth.getStatus", p, getFarmPath(&path, PATHLENGTH, NULL)); d = opendir(path); while ((e = readdir(d)) != NULL) { if (dbname != NULL && strcmp(e->d_name, dbname) != 0) continue; sdb = malloc(sizeof(sabdb)); /* store the database name */ sdb->dbname = strdup(e->d_name); /* add scenarios that are supported */ sdb->scens = NULL; snprintf(buf, PATHLENGTH, "%s/%s/%s", path, e->d_name, SCENARIOFILE); if ((f = fopen(buf, "r")) != NULL) { if ((len = fread(data, 1, 8095, f)) > 0) { sablist* np = NULL; buf[len] = '\0'; while ((p = strchr(buf, '\n')) != NULL) { *p = '\0'; if (sdb->scens == NULL) { sdb->scens = malloc(sizeof(sablist)); sdb->scens->val = strdup(buf); sdb->scens->next = NULL; np = sdb->scens; } else { np = np->next = malloc(sizeof(sablist)); np->val = strdup(buf); np->next = NULL; } buf = p + 1; } /* trailing stuff without an ending newline, is simply * not valid in Sabaoth, so we don't care */ } (void)fclose(f); } /* add how this server can be reached */ sdb->conns = NULL; snprintf(buf, PATHLENGTH, "%s/%s/%s", path, e->d_name, CONNECTIONFILE); if ((f = fopen(buf, "r")) != NULL) { if ((len = fread(data, 1, 8095, f)) > 0) { sablist* np = NULL; buf[len] = '\0'; while ((p = strchr(buf, '\n')) != NULL) { *p = '\0'; if (sdb->conns == NULL) { sdb->conns = malloc(sizeof(sablist)); sdb->conns->val = strdup(buf); sdb->conns->next = NULL; np = sdb->conns; } else { np = np->next = malloc(sizeof(sablist)); np->val = strdup(buf); np->next = NULL; } buf = p + 1; } /* trailing stuff without an ending newline, is simply * not valid in Sabaoth, so we don't care */ } (void)fclose(f); } /* check the state of the server by looking at its gdk lock: * - if we can lock it, the server has crashed or isn't running * - if we can't open it because it's locked, the server is * running * - to distinguish between a crash and proper shutdown, consult * the uplog */ snprintf(buf, PATHLENGTH, "%s/%s/%s", path, e->d_name, ".gdk_lock"); fd = MT_lockf(buf, F_TLOCK, 4, 1); if (fd == -2) { /* Locking failed; this can be because the lockfile couldn't * be created. Probably there is no Mserver running for * that case also. */ sdb->state = SABdbInactive; } else if (fd == -1) { /* lock denied, so Mserver is running */ sdb->state = SABdbRunning; } else { char *log = alloca(sizeof(char) * (PATHLENGTH + 1)); log[PATHLENGTH] = '\0'; /* locking succeed, check for a crash in the uplog */ close(fd); snprintf(log, PATHLENGTH, "%s/%s/%s", path, e->d_name, UPLOGFILE); if ((f = fopen(log, "r")) != NULL) { (void)fseek(f, -1, SEEK_END); if (fread(data, 1, 1, f) != 1) { /* the log is corrupt/wrong, assume no crash */ sdb->state = SABdbInactive; } else if (data[0] == '\n') { sdb->state = SABdbInactive; } else { /* should be \t */ sdb->state = SABdbCrashed; } (void)fclose(f); } else { /* no uplog file? assume no crash */ sdb->state = SABdbInactive; } fd = MT_lockf(buf, F_ULOCK, 4, 1); if (fd >= 0) close(fd); } } (void) closedir(d); return(MAL_SUCCEED);}@h#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -