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); }
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); }
/** * Sets a single property, performing all necessary checks to validate * the key and associated value. */ char * setProp(char *path, char *key, char *val) { char *err; char buf[8096]; confkeyval *props = getDefaultProps(); confkeyval *kv; readProps(props, path); kv = findConfKey(props, key); if (kv == NULL) { snprintf(buf, sizeof(buf), "no such property: %s", key); freeConfFile(props); free(props); return(strdup(buf)); } /* first just attempt to set the value (type-check) in memory */ if ((err = setConfVal(kv, val)) != NULL) { freeConfFile(props); free(props); return(err); } if (val != NULL) { /* handle the semantially enriched types */ if (strcmp(key, "forward") == 0) { if (strcmp(val, "proxy") != 0 && strcmp(val, "redirect") != 0) { snprintf(buf, sizeof(buf), "expected 'proxy' or 'redirect' " "for property 'forward', got: %s", val); freeConfFile(props); free(props); return(strdup(buf)); } } else if (strcmp(key, "shared") == 0) { char *value = val; /* check if tag matches [A-Za-z0-9./]+ */ if (*value == '\0') { freeConfFile(props); free(props); return(strdup("tag to share cannot be empty")); } while (*value != '\0') { if (!( (*value >= 'A' && *value <= 'Z') || (*value >= 'a' && *value <= 'z') || (*value >= '0' && *value <= '9') || (*value == '.' || *value == '/') )) { snprintf(buf, sizeof(buf), "invalid character '%c' at %d " "in tag name '%s'\n", *value, (int)(value - val), val); freeConfFile(props); free(props); return(strdup(buf)); } value++; } } } /* ok, if we've reached this point we can write this stuff out! */ writeProps(props, path); freeConfFile(props); free(props); return(NULL); }
int main(int argc, char *argv[]) { err e; int argp; char dbfarm[1024]; char *pidfilename = NULL; char *p; FILE *pidfile = NULL; char control_usock[1024]; char mapi_usock[1024]; dpair d = NULL; struct _dpair dpcons; struct _dpair dpmero; struct _dpair dpdisc; struct _dpair dpcont; int pfd[2]; pthread_t tid = 0; struct sigaction sa; int ret; int lockfd = -1; int sock = -1; int usock = -1; int unsock = -1; int socku = -1; unsigned short port = 0; char discovery = 0; struct stat sb; FILE *oerr = NULL; pthread_mutexattr_t mta; int thret; char merodontfork = 0; confkeyval ckv[] = { {"logfile", strdup("merovingian.log"), 0, STR}, {"pidfile", strdup("merovingian.pid"), 0, STR}, {"sockdir", strdup("/tmp"), 0, STR}, {"port", strdup(MERO_PORT), atoi(MERO_PORT), INT}, {"exittimeout", strdup("60"), 60, INT}, {"forward", strdup("proxy"), 0, OTHER}, {"discovery", strdup("true"), 1, BOOLEAN}, {"discoveryttl", strdup("600"), 600, INT}, {"control", strdup("false"), 0, BOOLEAN}, {"passphrase", NULL, 0, STR}, { NULL, NULL, 0, INVALID} }; confkeyval *kv; int retfd = -1; /* seed the randomiser for when we create a database, send responses * to HELO, etc */ srand(time(NULL)); /* figure out our hostname */ gethostname(_mero_hostname, 128); /* where is the mserver5 binary we fork on demand? * first try to locate it based on our binary location, fall-back to * hardcoded bin-dir */ _mero_mserver = get_bin_path(); if (_mero_mserver != NULL) { /* replace the trailing monetdbd by mserver5, fits nicely since * they happen to be of same length */ char *s = strrchr(_mero_mserver, '/'); if (s != NULL && strcmp(s + 1, "monetdbd") == 0) { s++; *s++ = 'm'; *s++ = 's'; *s++ = 'e'; *s++ = 'r'; *s++ = 'v'; *s++ = 'e'; *s++ = 'r'; *s++ = '5'; if (stat(_mero_mserver, &sb) == -1) _mero_mserver = NULL; } } /* setup default database properties, constants: unlike historical * versions, we do not want changing defaults any more */ _mero_db_props = getDefaultProps(); kv = findConfKey(_mero_db_props, "shared"); kv->val = strdup("yes"); kv = findConfKey(_mero_db_props, "readonly"); kv->val = strdup("no"); kv = findConfKey(_mero_db_props, "nclients"); kv->val = strdup("64"); kv = findConfKey(_mero_db_props, "type"); kv->val = strdup("database"); kv = findConfKey(_mero_db_props, "optpipe"); kv->val = strdup("default_pipe"); { /* nrthreads */ int ncpus = -1; char cnt[8]; #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN) /* this works on Linux, Solaris and AIX */ ncpus = sysconf(_SC_NPROCESSORS_ONLN); #elif defined(HAVE_SYS_SYSCTL_H) && defined(HW_NCPU) /* BSD */ size_t len = sizeof(int); int mib[3]; /* Everyone should have permission to make this call, * if we get a failure something is really wrong. */ mib[0] = CTL_HW; mib[1] = HW_NCPU; mib[2] = -1; sysctl(mib, 3, &ncpus, &len, NULL, 0); #elif defined(WIN32) SYSTEM_INFO sysinfo; GetSystemInfo(&sysinfo); ncpus = sysinfo.dwNumberOfProcessors; #endif if (ncpus > 0) { snprintf(cnt, sizeof(cnt), "%d", ncpus); kv = findConfKey(_mero_db_props, "nthreads"); kv->val = strdup(cnt); } } *dbfarm = '\0'; if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "help") == 0) { exit(command_help(argc - 1, &argv[1])); } else if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "version") == 0) { exit(command_version()); } else if (strcmp(argv[1], "create") == 0) { exit(command_create(argc - 1, &argv[1])); } else if (strcmp(argv[1], "get") == 0) { exit(command_get(ckv, argc - 1, &argv[1])); } else if (strcmp(argv[1], "set") == 0) { exit(command_set(ckv, argc - 1, &argv[1])); } else if (strcmp(argv[1], "start") == 0) { if (argc > 3 && strcmp(argv[2], "-n") == 0) merodontfork = 1; if (argc == 3 + merodontfork) { int len; len = snprintf(dbfarm, sizeof(dbfarm), "%s", argv[2 + merodontfork]); if (len > 0 && (size_t)len >= sizeof(dbfarm)) { Mfprintf(stderr, "fatal: dbfarm exceeds allocated " \ "path length, please file a bug at " \ "http://bugs.monetdb.org/\n"); exit(1); } } else { command_help(argc, argv); exit(1); } } else if (strcmp(argv[1], "stop") == 0) { exit(command_stop(ckv, argc - 1, &argv[1])); } else { fprintf(stderr, "monetdbd: unknown command: %s\n", argv[1]); command_help(0, NULL); exit(1); } } else { command_help(0, NULL); exit(1); } assert(*dbfarm != '\0'); /* fork into background before doing anything more */ if (!merodontfork) { char buf[4]; /* Fork into the background immediately. By doing this our child * can simply do everything it needs to do itself. Via a pipe it * will tell us if it is happy or not. */ if (pipe(pfd) == -1) { Mfprintf(stderr, "unable to create pipe: %s\n", strerror(errno)); return(1); } switch (fork()) { case -1: /* oops, forking went wrong! */ Mfprintf(stderr, "unable to fork into background: %s\n", strerror(errno)); return(1); case 0: /* detach client from controlling tty, we only write to the * pipe to daddy */ if (setsid() < 0) Mfprintf(stderr, "hmmm, can't detach from controlling tty, " "continuing anyway\n"); retfd = open("/dev/null", O_RDONLY); dup2(retfd, 0); close(retfd); close(pfd[0]); /* close unused read end */ retfd = pfd[1]; /* store the write end */ break; default: /* the parent, we want it to die, after we know the child * has a good time */ close(pfd[1]); /* close unused write end */ if (read(pfd[0], &buf, 1) != 1) { Mfprintf(stderr, "unable to retrieve startup status\n"); return(1); } close(pfd[0]); return(buf[0]); /* whatever the child returned, we return */ } } /* use after the logger thread has started */ #define MERO_EXIT(status) if (!merodontfork) { \ char s = status; \ if (write(retfd, &s, 1) != 1 || close(retfd) != 0) { \ Mfprintf(stderr, "could not write to parent\n"); \ } \ if (status != 0) { \ Mfprintf(stderr, "fatal startup condition encountered, " \ "aborting startup\n"); \ goto shutdown; \ } \ } else { \ if (status != 0) { \ Mfprintf(stderr, "fatal startup condition encountered, " \ "aborting startup\n"); \ goto shutdown; \ } \ } /* use before logger thread has started */ #define MERO_EXIT_CLEAN(status) if (!merodontfork) { \ char s = status; \ if (write(retfd, &s, 1) != 1 || close(retfd) != 0) { \ Mfprintf(stderr, "could not write to parent\n"); \ } \ exit(s); \ } else { \ exit(status); \ } /* check if dbfarm actually exists */ if (stat(dbfarm, &sb) == -1) { Mfprintf(stderr, "dbfarm directory '%s' does not exist, " "use monetdbd create first\n", dbfarm); MERO_EXIT_CLEAN(1); } /* chdir to dbfarm so we are at least in a known to exist location */ if (chdir(dbfarm) < 0) { Mfprintf(stderr, "could not move to dbfarm '%s': %s\n", dbfarm, strerror(errno)); MERO_EXIT_CLEAN(1); } /* absolutise dbfarm if it isn't yet (we're in it now) */ if (dbfarm[0] != '/') { if (getcwd(dbfarm, sizeof(dbfarm)) == NULL) { if (errno == ERANGE) { Mfprintf(stderr, "current path exceeds allocated path length" \ "please file a bug at http://bugs.monetdb.org\n"); } else { Mfprintf(stderr, "could not get dbfarm working directory: %s\n", strerror(errno)); } MERO_EXIT_CLEAN(1); } } if (_mero_mserver == NULL) { _mero_mserver = BINDIR "/mserver5"; if (stat(_mero_mserver, &sb) == -1) { /* exit early if this is not going to work well */ Mfprintf(stderr, "cannot stat %s executable: %s\n", _mero_mserver, strerror(errno)); MERO_EXIT_CLEAN(1); } } /* read the merovingian properties from the dbfarm */ if (readProps(ckv, ".") != 0) { Mfprintf(stderr, "cannot find or read properties file, was " "this dbfarm created by `monetdbd create`?\n"); MERO_EXIT_CLEAN(1); } _mero_props = ckv; pidfilename = getConfVal(_mero_props, "pidfile"); p = getConfVal(_mero_props, "forward"); if (strcmp(p, "redirect") != 0 && strcmp(p, "proxy") != 0) { Mfprintf(stderr, "invalid forwarding mode: %s, defaulting to proxy\n", p); kv = findConfKey(_mero_props, "forward"); setConfVal(kv, "proxy"); writeProps(_mero_props, "."); } kv = findConfKey(_mero_props, "port"); if (kv->ival <= 0 || kv->ival > 65535) { Mfprintf(stderr, "invalid port number: %s, defaulting to %s\n", kv->val, MERO_PORT); setConfVal(kv, MERO_PORT); writeProps(_mero_props, "."); } port = (unsigned short)kv->ival; discovery = getConfNum(_mero_props, "discovery"); /* check and trim the hash-algo from the passphrase for easy use * lateron */ kv = findConfKey(_mero_props, "passphrase"); if (kv->val != NULL) { char *h = kv->val + 1; if ((p = strchr(h, '}')) == NULL) { Mfprintf(stderr, "warning: incompatible passphrase (not hashed as " MONETDB5_PASSWDHASH "), disabling passphrase\n"); } else { *p = '\0'; if (strcmp(h, MONETDB5_PASSWDHASH) != 0) { Mfprintf(stderr, "warning: passphrase hash '%s' incompatible, " "expected '%s', disabling passphrase\n", h, MONETDB5_PASSWDHASH); } else { setConfVal(kv, p + 1); } } } /* set up UNIX socket paths for control and mapi */ p = getConfVal(_mero_props, "sockdir"); snprintf(control_usock, sizeof(control_usock), "%s/" CONTROL_SOCK "%d", p, port); snprintf(mapi_usock, sizeof(control_usock), "%s/" MERO_SOCK "%d", p, port); /* lock such that we are alone on this world */ if ((lockfd = MT_lockf(".merovingian_lock", F_TLOCK, 4, 1)) == -1) { /* locking failed */ Mfprintf(stderr, "another monetdbd is already running\n"); MERO_EXIT_CLEAN(1); } else if (lockfd == -2) { /* directory or something doesn't exist */ Mfprintf(stderr, "unable to create %s/.merovingian_lock file: %s\n", dbfarm, strerror(errno)); MERO_EXIT_CLEAN(1); } _mero_topdp = &dpcons; _mero_topdp->pid = 0; _mero_topdp->type = MERO; _mero_topdp->dbname = NULL; /* where should our msg output go to? */ p = getConfVal(_mero_props, "logfile"); /* write to the given file */ _mero_topdp->out = open(p, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR); if (_mero_topdp->out == -1) { Mfprintf(stderr, "unable to open '%s': %s\n", p, strerror(errno)); MT_lockf(".merovingian_lock", F_ULOCK, 4, 1); close(lockfd); MERO_EXIT_CLEAN(1); } _mero_topdp->err = _mero_topdp->out; _mero_logfile = fdopen(_mero_topdp->out, "a"); d = _mero_topdp->next = &dpmero; /* redirect stdout */ if (pipe(pfd) == -1) { Mfprintf(stderr, "unable to create pipe: %s\n", strerror(errno)); MERO_EXIT(1); } d->out = pfd[0]; dup2(pfd[1], 1); close(pfd[1]); /* redirect stderr */ if (pipe(pfd) == -1) { Mfprintf(stderr, "unable to create pipe: %s\n", strerror(errno)); MERO_EXIT(1); } /* before it is too late, save original stderr */ oerr = fdopen(dup(2), "w"); d->err = pfd[0]; dup2(pfd[1], 2); close(pfd[1]); d->pid = getpid(); d->type = MERO; d->dbname = "merovingian"; /* separate entry for the neighbour discovery service */ d = d->next = &dpdisc; if (pipe(pfd) == -1) { Mfprintf(stderr, "unable to create pipe: %s\n", strerror(errno)); MERO_EXIT(1); } d->out = pfd[0]; _mero_discout = fdopen(pfd[1], "a"); if (pipe(pfd) == -1) { Mfprintf(stderr, "unable to create pipe: %s\n", strerror(errno)); MERO_EXIT(1); } d->err = pfd[0]; _mero_discerr = fdopen(pfd[1], "a"); d->pid = getpid(); d->type = MERO; d->dbname = "discovery"; d->next = NULL; /* separate entry for the control runner */ d = d->next = &dpcont; if (pipe(pfd) == -1) { Mfprintf(stderr, "unable to create pipe: %s\n", strerror(errno)); MERO_EXIT(1); } d->out = pfd[0]; _mero_ctlout = fdopen(pfd[1], "a"); if (pipe(pfd) == -1) { Mfprintf(stderr, "unable to create pipe: %s\n", strerror(errno)); MERO_EXIT(1); } d->err = pfd[0]; _mero_ctlerr = fdopen(pfd[1], "a"); d->pid = getpid(); d->type = MERO; d->dbname = "control"; d->next = NULL; /* allow a thread to relock this mutex */ pthread_mutexattr_init(&mta); pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&_mero_topdp_lock, &mta); if ((thret = pthread_create(&tid, NULL, (void *(*)(void *))logListener, (void *)NULL)) != 0) { Mfprintf(oerr, "%s: FATAL: unable to create logthread: %s\n", argv[0], strerror(thret)); MERO_EXIT(1); } sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = handler; if ( sigaction(SIGINT, &sa, NULL) == -1 || sigaction(SIGQUIT, &sa, NULL) == -1 || sigaction(SIGTERM, &sa, NULL) == -1) { Mfprintf(oerr, "%s: FATAL: unable to create signal handlers: %s\n", argv[0], strerror(errno)); MERO_EXIT(1); } sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = huphandler; if (sigaction(SIGHUP, &sa, NULL) == -1) { Mfprintf(oerr, "%s: FATAL: unable to create signal handlers: %s\n", argv[0], strerror(errno)); MERO_EXIT(1); } sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = segvhandler; if (sigaction(SIGSEGV, &sa, NULL) == -1) { Mfprintf(oerr, "%s: FATAL: unable to create signal handlers: %s\n", argv[0], strerror(errno)); MERO_EXIT(1); } sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = SIG_IGN; if (sigaction(SIGPIPE, &sa, NULL) == -1) { Mfprintf(oerr, "%s: FATAL: unable to create signal handlers: %s\n", argv[0], strerror(errno)); MERO_EXIT(1); } sa.sa_flags = SA_SIGINFO; sigemptyset(&sa.sa_mask); sa.sa_sigaction = childhandler; if (sigaction(SIGCHLD, &sa, NULL) == -1) { Mfprintf(oerr, "%s: FATAL: unable to create signal handlers: %s\n", argv[0], strerror(errno)); MERO_EXIT(1); } /* make sure we will be able to write our pid */ if ((pidfile = fopen(pidfilename, "w")) == NULL) { Mfprintf(stderr, "unable to open '%s%s%s' for writing: %s\n", pidfilename[0] != '/' ? dbfarm : "", pidfilename[0] != '/' ? "/" : "", pidfilename, strerror(errno)); MERO_EXIT(1); } msab_dbfarminit(dbfarm); /* write out the pid */ Mfprintf(pidfile, "%d\n", (int)d->pid); fclose(pidfile); Mfprintf(stdout, "Merovingian %s (%s) starting\n", MERO_VERSION, MONETDB_RELEASE); Mfprintf(stdout, "monitoring dbfarm %s\n", dbfarm); /* open up connections */ if ( (e = openConnectionTCP(&sock, port, stdout)) == NO_ERR && /* coverity[operator_confusion] */ (unlink(control_usock) | unlink(mapi_usock) | 1) && (e = openConnectionUNIX(&socku, mapi_usock, 0, stdout)) == NO_ERR && (discovery == 0 || (e = openConnectionUDP(&usock, port)) == NO_ERR) && (e = openConnectionUNIX(&unsock, control_usock, S_IRWXO, _mero_ctlout)) == NO_ERR ) { pthread_t ctid = 0; pthread_t dtid = 0; if (discovery == 1) { _mero_broadcastsock = socket(AF_INET, SOCK_DGRAM, 0); ret = 1; if ((setsockopt(_mero_broadcastsock, SOL_SOCKET, SO_BROADCAST, &ret, sizeof(ret))) == -1) { Mfprintf(stderr, "cannot create broadcast package, " "discovery services disabled\n"); close(usock); usock = -1; } _mero_broadcastaddr.sin_family = AF_INET; _mero_broadcastaddr.sin_addr.s_addr = htonl(INADDR_BROADCAST); /* the target port is our configured port, not elegant, but how * else can we do it? can't broadcast to all ports or something */ _mero_broadcastaddr.sin_port = htons(port); } /* From this point merovingian considers itself to be in position to * start running, so flag the parent we will have fun. */ MERO_EXIT(0); /* Paranoia umask, but good, because why would people have to sniff * our private parts? */ umask(S_IRWXG | S_IRWXO); /* handle control commands */ if ((thret = pthread_create(&ctid, NULL, (void *(*)(void *))controlRunner, (void *)&unsock)) != 0) { Mfprintf(stderr, "unable to create control command thread: %s\n", strerror(thret)); ctid = 0; } /* start neighbour discovery and notification thread */ if (usock >= 0 && (thret = pthread_create(&dtid, NULL, (void *(*)(void *))discoveryRunner, (void *)&usock)) != 0) { Mfprintf(stderr, "unable to start neighbour discovery thread: %s\n", strerror(thret)); dtid = 0; } /* handle external connections main loop */ e = acceptConnections(sock, socku); /* wait for the control runner and discovery thread to have * finished announcing they're going down */ close(unsock); if (ctid != 0) pthread_join(ctid, NULL); if (usock >= 0) { close(usock); if (dtid != 0) pthread_join(dtid, NULL); } } /* control channel is already closed at this point */ if (unsock != -1 && unlink(control_usock) == -1) Mfprintf(stderr, "unable to unlink control socket '%s': %s\n", control_usock, strerror(errno)); if (socku != -1 && unlink(mapi_usock) == -1) Mfprintf(stderr, "unable to unlink mapi socket '%s': %s\n", mapi_usock, strerror(errno)); if (e != NO_ERR) { /* console */ Mfprintf(oerr, "%s: %s\n", argv[0], e); /* logfile */ Mfprintf(stderr, "%s\n", e); MERO_EXIT(1); } shutdown: /* stop started mservers */ kv = findConfKey(ckv, "exittimeout"); if (d->next != NULL && atoi(kv->val) > 0) { dpair t; threadlist tl = NULL, tlw = tl; pthread_mutex_lock(&_mero_topdp_lock); t = d->next; while (t != NULL) { if (tl == NULL) { tl = tlw = malloc(sizeof(struct _threadlist)); } else { tlw = tlw->next = malloc(sizeof(struct _threadlist)); } tlw->next = NULL; if ((thret = pthread_create(&(tlw->tid), NULL, (void *(*)(void *))terminateProcess, (void *)t)) != 0) { Mfprintf(stderr, "%s: unable to create thread to terminate " "database '%s': %s\n", argv[0], t->dbname, strerror(thret)); tlw->tid = 0; } t = t->next; } pthread_mutex_unlock(&_mero_topdp_lock); /* wait for all processes to be terminated */ tlw = tl; while (tlw != NULL) { if (tlw->tid != 0 && (argp = pthread_join(tlw->tid, NULL)) != 0) { Mfprintf(stderr, "failed to wait for termination thread: " "%s\n", strerror(argp)); } tl = tlw->next; free(tlw); tlw = tl; } } /* need to do this here, since the logging thread is shut down as * next thing */ Mfprintf(stdout, "Merovingian %s stopped\n", MERO_VERSION); _mero_keep_logging = 0; if (tid != 0 && (argp = pthread_join(tid, NULL)) != 0) { Mfprintf(oerr, "failed to wait for logging thread: %s\n", strerror(argp)); } if (_mero_topdp != NULL) { close(_mero_topdp->out); if (_mero_topdp->out != _mero_topdp->err) close(_mero_topdp->err); } /* remove files that suggest our existence */ unlink(".merovingian_lock"); if (pidfilename != NULL) { unlink(pidfilename); free(pidfilename); } /* mostly for valgrind... */ freeConfFile(ckv); if (_mero_db_props != NULL) { freeConfFile(_mero_db_props); free(_mero_db_props); } if (lockfd >= 0) { MT_lockf(".merovingian_lock", F_ULOCK, 4, 1); close(lockfd); } /* the child's return code at this point doesn't matter, as noone * will see it */ return(0); }