int main(int argc, const char **argv) { int c, len; #ifdef HAVE_LIBPQ char *pass_replace = "--pg-pass=WITHHELD", *conninfo_replace = "--pg-connect=WITHHELD"; #endif oml_setup(&argc, argv); poptContext optCon = poptGetContext(NULL, argc, (const char**) argv, options, 0); while ((c = poptGetNextOpt(optCon)) >= 0) { switch (c) { case 'v': printf(V_STRING, VERSION); printf("OML Protocol V%d--%d\n", MIN_PROTOCOL_VERSION, MAX_PROTOCOL_VERSION); printf(COPYRIGHT); return 0; } } #ifdef HAVE_LIBPQ /* Cleanup command line to avoid showing credentials in ps(1) output, amongst * others. * * XXX: This is a poor man's security measure, as this creates a race * condition where, prior to doing the following, the credentials are still * visible to everybody. */ if (pg_pass || pg_conninfo) { for(c=1; c<argc; c++) { len = strlen(argv[c]); if(pg_pass && !strncmp(argv[c],"--pg-pass", 9)) { strncpy((char*)argv[c], pass_replace, len); ((char*)argv[c])[len] = 0; } if(pg_conninfo && !strncmp(argv[c],"--pg-connect", 12)) { strncpy((char*)argv[c], conninfo_replace, len); ((char*)argv[c])[len] = 0; } } } #endif /* HAVE_LIBPQ */ logging_setup (logfile_name, log_level); if (c < -1) { die ("%s: %s\n", poptBadOption (optCon, POPT_BADOPTION_NOALIAS), poptStrerror (c)); } loginfo(V_STRING, VERSION); loginfo("OML Protocol V%d--%d\n", MIN_PROTOCOL_VERSION, MAX_PROTOCOL_VERSION); loginfo(COPYRIGHT); eventloop_init(); eventloop_set_socket_timeout(socket_timeout); Socket* server_sock; server_sock = socket_server_new("server", NULL, listen_service, on_connect, NULL); if (!server_sock) { die ("Failed to create listening socket for service %s\n", listen_service); } drop_privileges (uidstr, gidstr); /* Important that this comes after drop_privileges(). */ if(database_setup_backend(dbbackend)) { die("Failed to setup database backend '%s'\n", dbbackend); } signal_setup(); hook_setup(); eventloop_run(); signal_cleanup(); hook_cleanup(); oml_cleanup(); oml_memreport(O_LOG_INFO); return 0; }
/** Initialise the event hook if specified. * * This function creates two pipes, forks, redirects the child's stdin and * stdout to/from these pipes, then executes the hook program * * The hook program is expected to first print an identifying banner (\see * HOOK_BANNER), then wait for commands on stdin. * * Though a reverse pipe is also created for the hook's stdout to be available * to the main server process, it is not currently used for anything else than * getting the banner. Also, a read(2) on it from the main process is blocking. */ void hook_setup (void) { int pto[2], pfrom[2]; fd_set readfds; struct timeval timeout = { .tv_sec=5, .tv_usec=0, }; char buf[sizeof(HOOK_BANNER)]; if (!hook) return; if (pipe(pto) || pipe(pfrom)) { logwarn("hook: Cannot create pipes to `%s': %s\n", hook, strerror(errno)); goto clean_pipes; } hookpid = fork(); if (hookpid < 0) { logwarn("hook: Cannot fork for `%s': %s\n", hook, strerror(errno)); hookpid = -1; goto clean_pipes; } else if (0 == hookpid) { /* Child process */ close(pto[1]); close(pfrom[0]); dup2(pto[0], STDIN_FILENO); dup2(pfrom[1], STDOUT_FILENO); execlp(hook, hook, NULL); logwarn("hook: Cannot execute `%s': %s\n", hook, strerror(errno)); exit(1); } else { /* Parent process */ close(pto[0]); close(pfrom[1]); hookpipe[0] = pfrom[0]; hookpipe[1] = pto[1]; /* Wait for banner or timeout */ FD_ZERO(&readfds); FD_SET(hookpipe[0], &readfds); logdebug("hook: Waiting for `%s' to respond...\n", hook); if(select(hookpipe[0]+1, &readfds, NULL, NULL, &timeout) < 1) { logwarn("hook: `%s' (PID %d) not responding\n", hook, hookpid); hook_cleanup(); goto clean_pipes; } /* Only hookpipe[0] was in readfds, so we know why we're here */ if(hook_read(&buf, sizeof(buf)) <= 0) { logwarn("hook: Cannot get banner from `%s' (PID %d): %s\n", hook, hookpid, strerror(errno)); } else if (strncmp(buf, HOOK_BANNER, sizeof(HOOK_BANNER)-1)) { /* XXX: Ignore final '\n' instead of '\0' */ buf[sizeof(buf)-1]=0; logwarn("hook: Incorrect banner from `%s' (PID %d): `%s'\n", hook, hookpid, buf); goto clean_pipes; } loginfo("hook: `%s' in place\n", hook); } return; clean_pipes: logdebug("hook: Giving up on `%s'\n", hook); close(pto[0]); close(pfrom[0]); close(pto[1]); close(pfrom[1]); hook_clean_pipes(); } /** Determine whether an event hook has been enabled * \return 1 if hook enabled, 0 otherwise */ int hook_enabled(void) { return hookpipe[0] >= 0 && hookpipe[1] >= 0; } /** Write commands to the event hook. * * This function writes commands into the pipe connected to the event hook's * stdin. It takes the same parameters as write(2), but skips the file * descriptor. * * \param buf buffer from which +count+ bytes of data will be read out andwritten into event hook's +stdin+ * \param count the number of bytes from +buf+ to write into the hook's +stdin+ * \return the same as write(2), and sets +errno+ accordingly */ ssize_t hook_write (const void *buf, size_t count) { int n; if (-1 == hookpipe[1]) return -1; logdebug("hook: Sending command fd %d: '%s'\n", hookpipe[1], buf); n = write(hookpipe[1], buf, count); return n; }