PUBLIC int mime_run_command(const char *cmd, FILE *fo) { sigset_t nset; FILE *nfo; pid_t pid; int p[2]; int flags; if (cmd == NULL) return 0; flags = get_cmd_flags(cmd, &cmd); if (fo == NULL) /* no output file, just return the flags! */ return flags; if ((flags & CMD_FLAG_SHELLCMD) != 0) { /* run command under the shell */ char *cp; char *shellcmd; if ((shellcmd = value(ENAME_SHELL)) == NULL) shellcmd = __UNCONST(_PATH_CSHELL); (void)sasprintf(&cp, "%s -c '%s'", shellcmd, cmd); cmd = cp; } if (prepare_pipe(&nset, p) != 0) { warn("mime_run_command: prepare_pipe"); return flags; /* XXX - this or -1? */ } flush_files(fo, 0); /* flush fo, all registered files, and stdout */ switch (pid = start_command(cmd, &nset, p[READ], fileno(fo), NULL)) { case -1: /* error */ /* start_command already did a warn(). */ warnx("mime_run_command: %s", cmd); /* tell a bit more */ (void)close(p[READ]); (void)close(p[WRITE]); return flags; /* XXX - this or -1? */ case 0: /* child */ assert(/*CONSTCOND*/ 0); /* a real coding error! */ /* NOTREACHED */ default: /* parent */ (void)close(p[READ]); nfo = fdopen(p[WRITE], "w"); if (nfo == NULL) { warn("mime_run_command: fdopen"); (void)close(p[WRITE]); warn("fdopen"); return flags; } register_file(nfo, 1, pid); return flags; } }
PUBLIC void mime_run_function(void (*fn)(FILE *, FILE *, void *), FILE *fo, void *cookie) { sigset_t nset; FILE *nfo; pid_t pid; int p[2]; if (prepare_pipe(&nset, p) != 0) { warn("mime_run_function: pipe"); return; } flush_files(fo, 0); /* flush fo, all registered files, and stdout */ switch (pid = fork()) { case -1: /* error */ warn("mime_run_function: fork"); (void)close(p[READ]); (void)close(p[WRITE]); return; case 0: /* child */ (void)close(p[WRITE]); prepare_child(&nset, p[READ], fileno(fo)); fn(stdin, stdout, cookie); (void)fflush(stdout); _exit(0); /* NOTREACHED */ default: /* parent */ (void)close(p[READ]); nfo = fdopen(p[WRITE], "w"); if (nfo == NULL) { warn("run_function: fdopen"); (void)close(p[WRITE]); return; } register_file(nfo, 1, pid); return; } }
/** * \param[in] argc argument count * \param[in] argv argument array * \return 0 on success, 1 on error * * \attention In daemon mode, it finishes immediately. */ int main(int argc, char** argv) { openlog(INCRON_DAEMON_NAME, INCRON_LOG_OPTS, INCRON_LOG_FACIL); syslog(LOG_NOTICE, "starting service (version %s, built on %s %s)", INCRON_VERSION, __DATE__, __TIME__); try { Inotify in; in.SetNonBlock(true); EventDispatcher ed(&in); try { load_tables(&in, &ed); } catch (InotifyException e) { int err = e.GetErrorNumber(); syslog(LOG_CRIT, "%s: (%i) %s", e.GetMessage().c_str(), err, strerror(err)); syslog(LOG_NOTICE, "stopping service"); closelog(); return 1; } if (DAEMON) daemon(0, 0); prepare_pipe(); signal(SIGTERM, on_signal); signal(SIGINT, on_signal); signal(SIGCHLD, on_signal); uint32_t wm = IN_CLOSE_WRITE | IN_DELETE | IN_MOVE | IN_DELETE_SELF | IN_UNMOUNT; InotifyWatch watch(INCRON_TABLE_BASE, wm); in.Add(watch); syslog(LOG_NOTICE, "ready to process filesystem events"); InotifyEvent e; struct pollfd pfd[2]; pfd[0].fd = in.GetDescriptor(); pfd[0].events = (short) POLLIN; pfd[0].revents = (short) 0; pfd[1].fd = g_cldPipe[0]; pfd[1].events = (short) POLLIN; pfd[1].revents = (short) 0; while (!g_fFinish) { int res = poll(pfd, 2, -1); if (res > 0) { if (pfd[1].revents & ((short) POLLIN)) { char c; while (read(pfd[1].fd, &c, 1) > 0) {} UserTable::FinishDone(); } else { in.WaitForEvents(true); } } else if (res < 0) { if (errno != EINTR) throw InotifyException("polling failed", errno, NULL); } while (in.GetEvent(e)) { if (e.GetWatch() == &watch) { if (e.IsType(IN_DELETE_SELF) || e.IsType(IN_UNMOUNT)) { syslog(LOG_CRIT, "base directory destroyed, exitting"); g_fFinish = true; } else if (!e.GetName().empty()) { SUT_MAP::iterator it = g_ut.find(e.GetName()); if (it != g_ut.end()) { UserTable* pUt = (*it).second; if (e.IsType(IN_CLOSE_WRITE) || e.IsType(IN_MOVED_TO)) { syslog(LOG_INFO, "table for user %s changed, reloading", e.GetName().c_str()); pUt->Dispose(); pUt->Load(); } else if (e.IsType(IN_MOVED_FROM) || e.IsType(IN_DELETE)) { syslog(LOG_INFO, "table for user %s destroyed, removing", e.GetName().c_str()); delete pUt; g_ut.erase(it); } } else if (e.IsType(IN_CLOSE_WRITE) || e.IsType(IN_MOVED_TO)) { if (check_user(e.GetName().c_str())) { syslog(LOG_INFO, "table for user %s created, loading", e.GetName().c_str()); UserTable* pUt = new UserTable(&in, &ed, e.GetName()); g_ut.insert(SUT_MAP::value_type(e.GetName(), pUt)); pUt->Load(); } } } } else { ed.DispatchEvent(e); } } } if (g_cldPipe[0] != -1) close(g_cldPipe[0]); if (g_cldPipe[1] != -1) close(g_cldPipe[1]); } catch (InotifyException e) { int err = e.GetErrorNumber(); syslog(LOG_CRIT, "*** unhandled exception occurred ***"); syslog(LOG_CRIT, " %s", e.GetMessage().c_str()); syslog(LOG_CRIT, " error: (%i) %s", err, strerror(err)); } syslog(LOG_NOTICE, "stopping service"); closelog(); return 0; }
/*- *----------------------------------------------------------------------- * JobExec -- * Execute the shell for the given job. Called from JobStart * * Side Effects: * A shell is executed, outputs is altered and the Job structure added * to the job table. *----------------------------------------------------------------------- */ static void JobExec(Job *job) { pid_t cpid; /* ID of new child */ struct job_pid *p; int fds[4]; int *fdout = fds; int *fderr = fds+2; int i; banner(job, stdout); setup_engine(1); /* Create the pipe by which we'll get the shell's output. */ if (pipe(fdout) == -1) Punt("Cannot create pipe: %s", strerror(errno)); if (pipe(fderr) == -1) Punt("Cannot create pipe: %s", strerror(errno)); block_signals(); if ((cpid = fork()) == -1) { Punt("Cannot fork"); unblock_signals(); } else if (cpid == 0) { supervise_jobs = false; /* standard pipe code to route stdout and stderr */ close(fdout[0]); if (dup2(fdout[1], 1) == -1) Punt("Cannot dup2(outPipe): %s", strerror(errno)); if (fdout[1] != 1) close(fdout[1]); close(fderr[0]); if (dup2(fderr[1], 2) == -1) Punt("Cannot dup2(errPipe): %s", strerror(errno)); if (fderr[1] != 2) close(fderr[1]); /* * We want to switch the child into a different process family * so we can kill it and all its descendants in one fell swoop, * by killing its process family, but not commit suicide. */ (void)setpgid(0, getpid()); if (random_delay) if (!(nJobs == 1 && no_jobs_left())) usleep(random() % random_delay); setup_all_signals(SigHandler, SIG_DFL); unblock_signals(); /* this exits directly */ run_gnode_parallel(job->node); /*NOTREACHED*/ } else { supervise_jobs = true; job->pid = cpid; /* we set the current position in the buffers to the beginning * and mark another stream to watch in the outputs mask */ for (i = 0; i < 2; i++) prepare_pipe(&job->in[i], fds+2*i); } /* * Now the job is actually running, add it to the table. */ nJobs++; Lst_AtEnd(&runningJobs, job); if (job->flags & JOB_IS_EXPENSIVE) expensive_job = true; p = emalloc(sizeof(struct job_pid)); p->pid = cpid; Lst_AtEnd(&job_pids, p); job->p = Lst_Last(&job_pids); unblock_signals(); if (DEBUG(JOB)) { LstNode ln; (void)fprintf(stdout, "Running %ld (%s)\n", (long)cpid, job->node->name); for (ln = Lst_First(&job->node->commands); ln != NULL ; ln = Lst_Adv(ln)) fprintf(stdout, "\t%s\n", (char *)Lst_Datum(ln)); (void)fflush(stdout); } }
/** * \param[in] argc argument count * \param[in] argv argument array * \return 0 on success, 1 on error * * \attention In daemon mode, it finishes immediately. */ int main(int argc, char** argv) { AppArgs::Init(); if (!( AppArgs::AddOption("about", '?', AAT_NO_VALUE, false) && AppArgs::AddOption("help", 'h', AAT_NO_VALUE, false) && AppArgs::AddOption("foreground", 'n', AAT_NO_VALUE, false) && AppArgs::AddOption("kill", 'k', AAT_NO_VALUE, false) && AppArgs::AddOption("config", 'f', AAT_MANDATORY_VALUE, false) && AppArgs::AddOption("version", 'V', AAT_NO_VALUE, false))) { fprintf(stderr, "error while initializing application"); return 1; } AppArgs::Parse(argc, argv); if (AppArgs::ExistsOption("help")) { fprintf(stderr, "%s\n", INCROND_HELP); return 0; } if (AppArgs::ExistsOption("about")) { fprintf(stderr, "%s\n", INCROND_DESCRIPTION); return 0; } if (AppArgs::ExistsOption("version")) { fprintf(stderr, "%s\n", INCROND_VERSION); return 0; } IncronCfg::Init(); std::string cfg; if (!AppArgs::GetOption("config", cfg)) cfg = INCRON_CONFIG; IncronCfg::Load(cfg); std::string lckdir; IncronCfg::GetValue("lockfile_dir", lckdir); std::string lckfile; IncronCfg::GetValue("lockfile_name", lckfile); AppInstance app(lckfile, lckdir); if (AppArgs::ExistsOption("kill")) { fprintf(stderr, "attempting to terminate a running instance of incrond...\n"); if (app.Terminate()) { fprintf(stderr, "the instance notified, going down\n"); return 0; } else { fprintf(stderr, "error - incrond probably not running\n"); return 1; } } if (AppArgs::ExistsOption("foreground")) g_daemon = false; openlog(INCROND_NAME, INCRON_LOG_OPTS, INCRON_LOG_FACIL); syslog(LOG_NOTICE, "starting service (version %s, built on %s %s)", INCRON_VERSION, __DATE__, __TIME__); AppArgs::Destroy(); int ret = 0; std::string sysBase; std::string userBase; if (!IncronCfg::GetValue("system_table_dir", sysBase)) throw InotifyException("configuration is corrupted", EINVAL); if (access(sysBase.c_str(), R_OK) != 0) { syslog(LOG_CRIT, "cannot read directory for system tables (%s): (%i) %s", sysBase.c_str(), errno, strerror(errno)); if (!g_daemon) fprintf(stderr, "cannot read directory for system tables (%s): (%i) %s", sysBase.c_str(), errno, strerror(errno)); ret = 1; goto error; } if (!IncronCfg::GetValue("user_table_dir", userBase)) throw InotifyException("configuration is corrupted", EINVAL); if (access(userBase.c_str(), R_OK) != 0) { syslog(LOG_CRIT, "cannot read directory for user tables (%s): (%i) %s", userBase.c_str(), errno, strerror(errno)); if (!g_daemon) fprintf(stderr, "cannot read directory for user tables (%s): (%i) %s", userBase.c_str(), errno, strerror(errno)); ret = 1; goto error; } try { if (g_daemon) if (daemon(0, 0) == -1) { syslog(LOG_CRIT, "daemonizing failed: (%i) %s", errno, strerror(errno)); fprintf(stderr, "daemonizing failed: (%i) %s\n", errno, strerror(errno)); ret = 1; goto error; } try { if (!app.Lock()) { syslog(LOG_CRIT, "another instance of incrond already running"); if (!g_daemon) fprintf(stderr, "another instance of incrond already running\n"); ret = 1; goto error; } } catch (AppInstException e) { syslog(LOG_CRIT, "instance lookup failed: (%i) %s", e.GetErrorNumber(), strerror(e.GetErrorNumber())); if (!g_daemon) fprintf(stderr, "instance lookup failed: (%i) %s\n", e.GetErrorNumber(), strerror(e.GetErrorNumber())); ret = 1; goto error; } prepare_pipe(); Inotify in; in.SetNonBlock(true); in.SetCloseOnExec(true); uint32_t wm = IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_MOVE | IN_DELETE_SELF | IN_UNMOUNT; InotifyWatch stw(sysBase, wm); in.Add(stw); InotifyWatch utw(userBase, wm); in.Add(utw); EventDispatcher ed(g_cldPipe[0], &in, &stw, &utw); try { load_tables(&ed); } catch (InotifyException e) { int err = e.GetErrorNumber(); syslog(LOG_CRIT, "%s: (%i) %s", e.GetMessage().c_str(), err, strerror(err)); ret = 1; goto error; } ed.Rebuild(); // not too efficient, but simple signal(SIGTERM, on_signal); signal(SIGINT, on_signal); signal(SIGCHLD, on_signal); syslog(LOG_NOTICE, "ready to process filesystem events"); while (!g_fFinish) { int res = poll(ed.GetPollData(), ed.GetSize(), -1); if (res > 0) { if (ed.ProcessEvents()) UserTable::FinishDone(); } else if (res < 0) { switch (errno) { case EINTR: // syscall interrupted - continue polling break; case EAGAIN: // not enough resources - wait a moment and try again syslog(LOG_WARNING, "polling failed due to resource shortage, retrying later..."); sleep(POLL_EAGAIN_WAIT); break; default: throw InotifyException("polling failed", errno, NULL); } } } free_tables(&ed); if (g_cldPipe[0] != -1) close(g_cldPipe[0]); if (g_cldPipe[1] != -1) close(g_cldPipe[1]); } catch (InotifyException e) { int err = e.GetErrorNumber(); syslog(LOG_CRIT, "*** unhandled exception occurred ***"); syslog(LOG_CRIT, " %s", e.GetMessage().c_str()); syslog(LOG_CRIT, " error: (%i) %s", err, strerror(err)); ret = 1; } error: syslog(LOG_NOTICE, "stopping service"); closelog(); return ret; }