summaryrefslogtreecommitdiffabout
path: root/src/process.cc
Unidiff
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 @@
1#include <stdio.h>
2#include <sys/types.h>
3#include <unistd.h>
4#include <signal.h>
5#include <pwd.h>
6#include <grp.h>
7#include <sys/wait.h>
8#include <syslog.h>
9#include <errno.h>
10#include <iostream>
11#include <fstream>
12#include <stdexcept>
13using namespace std;
14#include "process.h"
15#include "configuration.h"
16
17void process::check(const string& id,configuration& config) {
18 bool running = false;
19 ifstream pids(pidfile.c_str(),ios::in);
20 if(pids) {
21 pid_t pid = 0;
22 pids >> pid;
23 pids.close();
24 if(pid) {
25 if(!kill(pid,0)) {
26 running = true;
27 }
28 }
29 }
30 if(running){
31 patience = 0;
32 }else{
33 if(patience>60) { // TODO: configurable
34 patience = 0;
35 }else{
36 if(patience<10) { // TODO: configurable
37 syslog(LOG_NOTICE,"The process '%s' is down, trying to launch.",id.c_str());
38 do_notify(id,"Starting up",
39 "The named process seems to be down. Dudki will try\n"
40 "to revive it by running the specified command.\n",
41 config);
42 try {
43 launch(id,config);
44 }catch(exception& e) {
45 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what());
46 }
47 }else if(patience==10){ // TODO: configurable like the above
48 syslog(LOG_NOTICE,"Giving up on process '%s' for a while",id.c_str());
49 do_notify(id,"Giving up",
50 "After a number of attempts to relaunch the named process\n"
51 "It still seems to be down. Dudki is giving up attempts\n"
52 "to revive the process for a while.\n",
53 config);
54 }
55 patience++;
56 }
57 }
58}
59
60void process::launch(const string& id,configuration& config) {
61 uid_t uid = 0;
62 if(!user.empty()) {
63 struct passwd *ptmp = getpwnam(user.c_str());
64 if(ptmp) {
65 uid = ptmp->pw_uid;
66 }else{
67 errno=0;
68 uid = strtol(user.c_str(),NULL,0);
69 if(errno)
70 throw runtime_error("Failed to resolve User value to uid");
71 }
72 }
73 gid_t gid = 0;
74 if(!group.empty()) {
75 struct group *gtmp = getgrnam(group.c_str());
76 if(gtmp) {
77 gid = gtmp->gr_gid;
78 }else{
79 errno = 0;
80 gid = strtol(group.c_str(),NULL,0);
81 if(errno)
82 throw runtime_error("Failed to reslove Group value to gid");
83 }
84 }
85 pid_t p = fork();
86 if(p<0)
87 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()");
88 if(!p) {
89 // child
90 try {
91 setsid();
92 if(!chroot.empty()) {
93 if(::chroot(chroot.c_str()))
94 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to chroot()");
95 }
96 if(!group.empty()) {
97 // TODO: initgroups()?
98 if((getgid()!=gid) && setgid(gid))
99 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setgid()");
100 }
101 if(!user.empty()) {
102 if((getuid()!=uid) && setuid(uid))
103 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setuid()");
104 }
105 char *argv[] = { "/bin/sh", "-c", (char*)restart_cmd.c_str(), NULL };
106 close(0); close(1); close(2);
107 execv("/bin/sh",argv);
108 }catch(exception& e) {
109 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what());
110 }
111 _exit(-1);
112 }
113 // parent
114 int rv;
115 if(waitpid(p,&rv,0)<0)
116 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to waitpid()");
117}
118
119void process::do_notify(const string& id,const string& event,const string& description,configuration& config) {
120 string the_notify;
121 if(!notify.empty())
122 the_notify=notify;
123 else if(!config.notify.empty())
124 the_notify=config.notify;
125 else
126 return;
127 try {
128 string::size_type colon = the_notify.find(':');
129 if(colon==string::npos)
130 throw runtime_error("invalid notify action specification");
131 string nschema = the_notify.substr(0,colon);
132 string ntarget = the_notify.substr(colon+1);
133 if(nschema=="mailto") {
134 notify_mailto(ntarget,id,event,description,config);
135 }else
136 throw runtime_error("unrecognized notification schema");
137 }catch(exception& e) {
138 syslog(LOG_ERR,"Notification error: %s",e.what());
139 }
140}
141
142void process::notify_mailto(const string& email,const string& id,const string& event,const string& description,configuration& config) {
143 int files[2];
144 if(pipe(files))
145 throw runtime_error("Failed to pipe()");
146 pid_t pid = vfork();
147 if(pid==-1) {
148 close(files[0]);
149 close(files[1]);
150 throw runtime_error("Failed to vfork()");
151 }
152 if(!pid) {
153 // child
154 if(dup2(files[0],0)!=0)
155 _exit(-1);
156 close(1);
157 close(files[0]);
158 close(files[1]);
159 execl("/usr/sbin/sendmail","usr/sbin/sendmail","-i",email.c_str(),NULL);
160 _exit(-1);
161 }
162 // parent
163 close(files[0]);
164 FILE *mta = fdopen(files[1],"w");
165 for(headers_t::const_iterator i=mailto_headers.begin();i!=mailto_headers.end();++i) {
166 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str());
167 }
168 for(headers_t::const_iterator i=config.mailto_headers.begin();i!=config.mailto_headers.end();++i) {
169 if(mailto_headers.find(i->first)!=mailto_headers.end())
170 continue;
171 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str());
172 }
173 fprintf(mta,
174 "Subject: [%s] %s\n\n"
175 "%s\n"
176 "---\n"
177 "This message was sent automatically by the 'dudki' daemon\n",
178 id.c_str(), event.c_str(),
179 description.c_str() );
180 fclose(mta);
181 int status;
182 waitpid(pid,&status,0);
183 // TODO: check the return code
184}