summaryrefslogtreecommitdiffabout
path: root/src/process.cc
Side-by-side diff
Diffstat (limited to 'src/process.cc') (more/less context) (ignore whitespace changes)
-rw-r--r--src/process.cc184
1 files changed, 184 insertions, 0 deletions
diff --git a/src/process.cc b/src/process.cc
new file mode 100644
index 0000000..fda35e8
--- a/dev/null
+++ b/src/process.cc
@@ -0,0 +1,184 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <errno.h>
+#include <iostream>
+#include <fstream>
+#include <stdexcept>
+using namespace std;
+#include "process.h"
+#include "configuration.h"
+
+void process::check(const string& id,configuration& config) {
+ bool running = false;
+ ifstream pids(pidfile.c_str(),ios::in);
+ if(pids) {
+ pid_t pid = 0;
+ pids >> pid;
+ pids.close();
+ if(pid) {
+ if(!kill(pid,0)) {
+ running = true;
+ }
+ }
+ }
+ if(running){
+ patience = 0;
+ }else{
+ if(patience>60) { // TODO: configurable
+ patience = 0;
+ }else{
+ if(patience<10) { // TODO: configurable
+ syslog(LOG_NOTICE,"The process '%s' is down, trying to launch.",id.c_str());
+ do_notify(id,"Starting up",
+ "The named process seems to be down. Dudki will try\n"
+ "to revive it by running the specified command.\n",
+ config);
+ try {
+ launch(id,config);
+ }catch(exception& e) {
+ syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what());
+ }
+ }else if(patience==10){ // TODO: configurable like the above
+ syslog(LOG_NOTICE,"Giving up on process '%s' for a while",id.c_str());
+ do_notify(id,"Giving up",
+ "After a number of attempts to relaunch the named process\n"
+ "It still seems to be down. Dudki is giving up attempts\n"
+ "to revive the process for a while.\n",
+ config);
+ }
+ patience++;
+ }
+ }
+}
+
+void process::launch(const string& id,configuration& config) {
+ uid_t uid = 0;
+ if(!user.empty()) {
+ struct passwd *ptmp = getpwnam(user.c_str());
+ if(ptmp) {
+ uid = ptmp->pw_uid;
+ }else{
+ errno=0;
+ uid = strtol(user.c_str(),NULL,0);
+ if(errno)
+ throw runtime_error("Failed to resolve User value to uid");
+ }
+ }
+ gid_t gid = 0;
+ if(!group.empty()) {
+ struct group *gtmp = getgrnam(group.c_str());
+ if(gtmp) {
+ gid = gtmp->gr_gid;
+ }else{
+ errno = 0;
+ gid = strtol(group.c_str(),NULL,0);
+ if(errno)
+ throw runtime_error("Failed to reslove Group value to gid");
+ }
+ }
+ pid_t p = fork();
+ if(p<0)
+ throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()");
+ if(!p) {
+ // child
+ try {
+ setsid();
+ if(!chroot.empty()) {
+ if(::chroot(chroot.c_str()))
+ throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to chroot()");
+ }
+ if(!group.empty()) {
+ // TODO: initgroups()?
+ if((getgid()!=gid) && setgid(gid))
+ throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setgid()");
+ }
+ if(!user.empty()) {
+ if((getuid()!=uid) && setuid(uid))
+ throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setuid()");
+ }
+ char *argv[] = { "/bin/sh", "-c", (char*)restart_cmd.c_str(), NULL };
+ close(0); close(1); close(2);
+ execv("/bin/sh",argv);
+ }catch(exception& e) {
+ syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what());
+ }
+ _exit(-1);
+ }
+ // parent
+ int rv;
+ if(waitpid(p,&rv,0)<0)
+ throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to waitpid()");
+}
+
+void process::do_notify(const string& id,const string& event,const string& description,configuration& config) {
+ string the_notify;
+ if(!notify.empty())
+ the_notify=notify;
+ else if(!config.notify.empty())
+ the_notify=config.notify;
+ else
+ return;
+ try {
+ string::size_type colon = the_notify.find(':');
+ if(colon==string::npos)
+ throw runtime_error("invalid notify action specification");
+ string nschema = the_notify.substr(0,colon);
+ string ntarget = the_notify.substr(colon+1);
+ if(nschema=="mailto") {
+ notify_mailto(ntarget,id,event,description,config);
+ }else
+ throw runtime_error("unrecognized notification schema");
+ }catch(exception& e) {
+ syslog(LOG_ERR,"Notification error: %s",e.what());
+ }
+}
+
+void process::notify_mailto(const string& email,const string& id,const string& event,const string& description,configuration& config) {
+ int files[2];
+ if(pipe(files))
+ throw runtime_error("Failed to pipe()");
+ pid_t pid = vfork();
+ if(pid==-1) {
+ close(files[0]);
+ close(files[1]);
+ throw runtime_error("Failed to vfork()");
+ }
+ if(!pid) {
+ // child
+ if(dup2(files[0],0)!=0)
+ _exit(-1);
+ close(1);
+ close(files[0]);
+ close(files[1]);
+ execl("/usr/sbin/sendmail","usr/sbin/sendmail","-i",email.c_str(),NULL);
+ _exit(-1);
+ }
+ // parent
+ close(files[0]);
+ FILE *mta = fdopen(files[1],"w");
+ for(headers_t::const_iterator i=mailto_headers.begin();i!=mailto_headers.end();++i) {
+ fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str());
+ }
+ for(headers_t::const_iterator i=config.mailto_headers.begin();i!=config.mailto_headers.end();++i) {
+ if(mailto_headers.find(i->first)!=mailto_headers.end())
+ continue;
+ fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str());
+ }
+ fprintf(mta,
+ "Subject: [%s] %s\n\n"
+ "%s\n"
+ "---\n"
+ "This message was sent automatically by the 'dudki' daemon\n",
+ id.c_str(), event.c_str(),
+ description.c_str() );
+ fclose(mta);
+ int status;
+ waitpid(pid,&status,0);
+ // TODO: check the return code
+}