summaryrefslogtreecommitdiffabout
path: root/src
Unidiff
Diffstat (limited to 'src') (more/less context) (ignore whitespace changes)
-rw-r--r--src/process.cc7
1 files changed, 6 insertions, 1 deletions
diff --git a/src/process.cc b/src/process.cc
index 6d3b2a2..4807b98 100644
--- a/src/process.cc
+++ b/src/process.cc
@@ -1,297 +1,302 @@
1#include <stdio.h> 1#include <stdio.h>
2#include <sys/types.h> 2#include <sys/types.h>
3#include <unistd.h> 3#include <unistd.h>
4#include <signal.h> 4#include <signal.h>
5#include <pwd.h> 5#include <pwd.h>
6#include <grp.h> 6#include <grp.h>
7#include <dirent.h> 7#include <dirent.h>
8#include <sys/wait.h> 8#include <sys/wait.h>
9#include <syslog.h> 9#include <syslog.h>
10#include <errno.h> 10#include <errno.h>
11#include <iostream> 11#include <iostream>
12#include <fstream> 12#include <fstream>
13#include <sstream> 13#include <sstream>
14#include <stdexcept> 14#include <stdexcept>
15#include <string> 15#include <string>
16#include <vector> 16#include <vector>
17using namespace std; 17using namespace std;
18#include "process.h" 18#include "process.h"
19#include "configuration.h" 19#include "configuration.h"
20 20
21static multimap<string,pid_t> procpids; 21static multimap<string,pid_t> procpids;
22 22
23void process::check() const { 23void process::check() const {
24 if(!pidfile.empty()) { 24 if(!pidfile.empty()) {
25 signal(0); 25 signal(0);
26 }else if(!process_name.empty()) { 26 }else if(!process_name.empty()) {
27 if(procpids.empty()) 27 if(procpids.empty())
28 gather_proc_info(); 28 gather_proc_info();
29 if(procpids.find(process_name)==procpids.end()) 29 if(procpids.find(process_name)==procpids.end())
30 throw runtime_error("no such process"); 30 throw runtime_error("no such process");
31 } // XXX: or else? 31 } // XXX: or else?
32} 32}
33void process::check(const string& id,configuration& config) { 33void process::check(const string& id,configuration& config) {
34 try { 34 try {
35 check(); 35 check();
36 patience = 0; 36 patience = 0;
37 }catch(exception& e) { 37 }catch(exception& e) {
38 if(patience>60) { // TODO: configurable 38 if(patience>60) { // TODO: configurable
39 patience = 0; 39 patience = 0;
40 }else{ 40 }else{
41 if(patience<10) { // TODO: configurable 41 if(patience<10) { // TODO: configurable
42 syslog(LOG_NOTICE,"The process '%s' is down, trying to launch.",id.c_str()); 42 syslog(LOG_NOTICE,"The process '%s' is down, trying to launch.",id.c_str());
43 do_notify(id,"Starting up", 43 do_notify(id,"Starting up",
44 "The named process seems to be down. Dudki will try\n" 44 "The named process seems to be down. Dudki will try\n"
45 "to revive it by running the specified command.\n", 45 "to revive it by running the specified command.\n",
46 config); 46 config);
47 try { 47 try {
48 launch(id,config); 48 launch(id,config);
49 }catch(exception& e) { 49 }catch(exception& e) {
50 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what()); 50 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what());
51 } 51 }
52 }else if(patience==10){ // TODO: configurable like the above 52 }else if(patience==10){ // TODO: configurable like the above
53 syslog(LOG_NOTICE,"Giving up on process '%s' for a while",id.c_str()); 53 syslog(LOG_NOTICE,"Giving up on process '%s' for a while",id.c_str());
54 do_notify(id,"Giving up", 54 do_notify(id,"Giving up",
55 "After a number of attempts to relaunch the named process\n" 55 "After a number of attempts to relaunch the named process\n"
56 "It still seems to be down. Dudki is giving up attempts\n" 56 "It still seems to be down. Dudki is giving up attempts\n"
57 "to revive the process for a while.\n", 57 "to revive the process for a while.\n",
58 config); 58 config);
59 } 59 }
60 patience++; 60 patience++;
61 } 61 }
62 } 62 }
63} 63}
64 64
65void process::launch(const string& id,configuration& config) { 65void process::launch(const string& id,configuration& config) {
66 uid_t uid = (uid_t)-1; 66 uid_t uid = (uid_t)-1;
67 gid_t gid = (gid_t)-1; 67 gid_t gid = (gid_t)-1;
68 if(!user.empty()) { 68 if(!user.empty()) {
69 struct passwd *ptmp = getpwnam(user.c_str()); 69 struct passwd *ptmp = getpwnam(user.c_str());
70 if(ptmp) { 70 if(ptmp) {
71 uid = ptmp->pw_uid; 71 uid = ptmp->pw_uid;
72 gid = ptmp->pw_gid; 72 gid = ptmp->pw_gid;
73 }else{ 73 }else{
74 errno=0; 74 errno=0;
75 uid = strtol(user.c_str(),NULL,0); 75 uid = strtol(user.c_str(),NULL,0);
76 if(errno) 76 if(errno)
77 throw runtime_error("Failed to resolve User value to uid"); 77 throw runtime_error("Failed to resolve User value to uid");
78 } 78 }
79 } 79 }
80 if(!group.empty()) { 80 if(!group.empty()) {
81 struct group *gtmp = getgrnam(group.c_str()); 81 struct group *gtmp = getgrnam(group.c_str());
82 if(gtmp) { 82 if(gtmp) {
83 gid = gtmp->gr_gid; 83 gid = gtmp->gr_gid;
84 }else{ 84 }else{
85 errno = 0; 85 errno = 0;
86 gid = strtol(group.c_str(),NULL,0); 86 gid = strtol(group.c_str(),NULL,0);
87 if(errno) 87 if(errno)
88 throw runtime_error("Failed to reslove Group value to gid"); 88 throw runtime_error("Failed to reslove Group value to gid");
89 } 89 }
90 } 90 }
91 pid_t p = fork(); 91 pid_t p = fork();
92 if(p<0) 92 if(p<0)
93 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()"); 93 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()");
94 if(!p) { 94 if(!p) {
95 // child 95 // child
96 try { 96 try {
97 setsid(); 97 setsid();
98 if(!group.empty()) { 98 if(!group.empty()) {
99 if(user.empty()) { 99 if(user.empty()) {
100 if((getgid()!=gid) && setgid(gid)) 100 if((getgid()!=gid) && setgid(gid))
101 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setgid()"); 101 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setgid()");
102 }else{ 102 }else{
103 if(initgroups(user.c_str(),gid)) 103 if(initgroups(user.c_str(),gid))
104 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to initgroups()"); 104 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to initgroups()");
105 } 105 }
106 } 106 }
107 if(!chroot.empty()) { 107 if(!chroot.empty()) {
108 if(::chroot(chroot.c_str())) 108 if(::chroot(chroot.c_str()))
109 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to chroot()"); 109 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to chroot()");
110 } 110 }
111 if(!user.empty()) { 111 if(!user.empty()) {
112 if((getuid()!=uid) && setuid(uid)) 112 if((getuid()!=uid) && setuid(uid))
113 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setuid()"); 113 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setuid()");
114 } 114 }
115 char *argv[] = { "/bin/sh", "-c", (char*)restart_cmd.c_str(), NULL }; 115 char *argv[] = { "/bin/sh", "-c", (char*)restart_cmd.c_str(), NULL };
116 close(0); close(1); close(2); 116 close(0); close(1); close(2);
117 execv("/bin/sh",argv); 117 execv("/bin/sh",argv);
118 }catch(exception& e) { 118 }catch(exception& e) {
119 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what()); 119 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what());
120 } 120 }
121 _exit(-1); 121 _exit(-1);
122 } 122 }
123 // parent 123 // parent
124 int rv; 124 int rv;
125 if(waitpid(p,&rv,0)<0) 125 if(waitpid(p,&rv,0)<0)
126 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to waitpid()"); 126 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to waitpid()");
127} 127}
128 128
129void process::do_notify(const string& id,const string& event,const string& description,configuration& config) { 129void process::do_notify(const string& id,const string& event,const string& description,configuration& config) {
130 string the_notify; 130 string the_notify;
131 if(!notify.empty()) 131 if(!notify.empty())
132 the_notify=notify; 132 the_notify=notify;
133 else if(!config.notify.empty()) 133 else if(!config.notify.empty())
134 the_notify=config.notify; 134 the_notify=config.notify;
135 else 135 else
136 return; 136 return;
137 try { 137 try {
138 string::size_type colon = the_notify.find(':'); 138 string::size_type colon = the_notify.find(':');
139 if(colon==string::npos) 139 if(colon==string::npos)
140 throw runtime_error("invalid notify action specification"); 140 throw runtime_error("invalid notify action specification");
141 string nschema = the_notify.substr(0,colon); 141 string nschema = the_notify.substr(0,colon);
142 string ntarget = the_notify.substr(colon+1); 142 string ntarget = the_notify.substr(colon+1);
143 if(nschema=="mailto") { 143 if(nschema=="mailto") {
144 notify_mailto(ntarget,id,event,description,config); 144 notify_mailto(ntarget,id,event,description,config);
145 }else 145 }else
146 throw runtime_error("unrecognized notification schema"); 146 throw runtime_error("unrecognized notification schema");
147 }catch(exception& e) { 147 }catch(exception& e) {
148 syslog(LOG_ERR,"Notification error: %s",e.what()); 148 syslog(LOG_ERR,"Notification error: %s",e.what());
149 } 149 }
150} 150}
151 151
152void process::notify_mailto(const string& email,const string& id,const string& event,const string& description,configuration& config) { 152void process::notify_mailto(const string& email,const string& id,const string& event,const string& description,configuration& config) {
153 int files[2]; 153 int files[2];
154 if(pipe(files)) 154 if(pipe(files))
155 throw runtime_error("Failed to pipe()"); 155 throw runtime_error("Failed to pipe()");
156 pid_t pid = vfork(); 156 pid_t pid = vfork();
157 if(pid==-1) { 157 if(pid==-1) {
158 close(files[0]); 158 close(files[0]);
159 close(files[1]); 159 close(files[1]);
160 throw runtime_error("Failed to vfork()"); 160 throw runtime_error("Failed to vfork()");
161 } 161 }
162 if(!pid) { 162 if(!pid) {
163 // child 163 // child
164 if(dup2(files[0],0)!=0) 164 if(dup2(files[0],0)!=0)
165 _exit(-1); 165 _exit(-1);
166 close(1); 166 close(1);
167 close(files[0]); 167 close(files[0]);
168 close(files[1]); 168 close(files[1]);
169 execl("/usr/sbin/sendmail","usr/sbin/sendmail","-i",email.c_str(),NULL); 169 execl("/usr/sbin/sendmail","usr/sbin/sendmail","-i",email.c_str(),NULL);
170 _exit(-1); 170 _exit(-1);
171 } 171 }
172 // parent 172 // parent
173 int status;
174 if(waitpid(pid,&status,WNOHANG)) {
175 close(files[0]);
176 close(files[1]);
177 throw runtime_error("vfork()ed sendmail child exited unexpectedly");
178 }
173 close(files[0]); 179 close(files[0]);
174 FILE *mta = fdopen(files[1],"w"); 180 FILE *mta = fdopen(files[1],"w");
175 for(headers_t::const_iterator i=mailto_headers.begin();i!=mailto_headers.end();++i) { 181 for(headers_t::const_iterator i=mailto_headers.begin();i!=mailto_headers.end();++i) {
176 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str()); 182 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str());
177 } 183 }
178 for(headers_t::const_iterator i=config.mailto_headers.begin();i!=config.mailto_headers.end();++i) { 184 for(headers_t::const_iterator i=config.mailto_headers.begin();i!=config.mailto_headers.end();++i) {
179 if(mailto_headers.find(i->first)!=mailto_headers.end()) 185 if(mailto_headers.find(i->first)!=mailto_headers.end())
180 continue; 186 continue;
181 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str()); 187 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str());
182 } 188 }
183 fprintf(mta, 189 fprintf(mta,
184 "Subject: [%s] %s\n\n" 190 "Subject: [%s] %s\n\n"
185 "%s\n" 191 "%s\n"
186 "---\n" 192 "---\n"
187 "This message was sent automatically by the 'dudki' daemon\n", 193 "This message was sent automatically by the 'dudki' daemon\n",
188 id.c_str(), event.c_str(), 194 id.c_str(), event.c_str(),
189 description.c_str() ); 195 description.c_str() );
190 fclose(mta); 196 fclose(mta);
191 int status;
192 waitpid(pid,&status,0); 197 waitpid(pid,&status,0);
193 // TODO: check the return code 198 // TODO: check the return code
194} 199}
195 200
196void process::signal(int signum) const { 201void process::signal(int signum) const {
197 if(!pidfile.empty()) { 202 if(!pidfile.empty()) {
198 ifstream pids(pidfile.c_str(),ios::in); 203 ifstream pids(pidfile.c_str(),ios::in);
199 if(!pids) 204 if(!pids)
200 throw runtime_error("no pidfile found"); 205 throw runtime_error("no pidfile found");
201 pid_t pid = 0; 206 pid_t pid = 0;
202 pids >> pid; 207 pids >> pid;
203 pids.close(); 208 pids.close();
204 if(!pid) 209 if(!pid)
205 throw runtime_error("no pid in pidfile"); 210 throw runtime_error("no pid in pidfile");
206 if(kill(pid,signum)) 211 if(kill(pid,signum))
207 throw runtime_error("failed to signal process"); 212 throw runtime_error("failed to signal process");
208 }else if(!process_name.empty()) { 213 }else if(!process_name.empty()) {
209 if(procpids.empty()) 214 if(procpids.empty())
210 gather_proc_info(); 215 gather_proc_info();
211 pair<multimap<string,pid_t>::const_iterator,multimap<string,pid_t>::const_iterator> range = procpids.equal_range(process_name); 216 pair<multimap<string,pid_t>::const_iterator,multimap<string,pid_t>::const_iterator> range = procpids.equal_range(process_name);
212 int count = 0; 217 int count = 0;
213 for(multimap<string,pid_t>::const_iterator i=range.first;i!=range.second;++i) { 218 for(multimap<string,pid_t>::const_iterator i=range.first;i!=range.second;++i) {
214 pid_t pid = i->second; 219 pid_t pid = i->second;
215 if(kill(i->second,signum)) 220 if(kill(i->second,signum))
216 throw runtime_error("failed to signal process"); 221 throw runtime_error("failed to signal process");
217 count++; 222 count++;
218 } 223 }
219 if(!count) 224 if(!count)
220 throw runtime_error("no running instance detected"); 225 throw runtime_error("no running instance detected");
221 }else 226 }else
222 throw runtime_error("nothing is known about the process"); 227 throw runtime_error("nothing is known about the process");
223} 228}
224 229
225void process::prepare_herd() { 230void process::prepare_herd() {
226 procpids.clear(); 231 procpids.clear();
227} 232}
228void process::unprepare_herd() { 233void process::unprepare_herd() {
229 procpids.clear(); 234 procpids.clear();
230} 235}
231void process::gather_proc_info() { 236void process::gather_proc_info() {
232 vector<pid_t> allpids; 237 vector<pid_t> allpids;
233 DIR *pd = opendir("/proc"); 238 DIR *pd = opendir("/proc");
234 if(!pd) 239 if(!pd)
235 throw runtime_error("failed to open /proc"); 240 throw runtime_error("failed to open /proc");
236 struct dirent *pde; 241 struct dirent *pde;
237 pid_t selfpid = getpid(); 242 pid_t selfpid = getpid();
238 while(pde=readdir(pd)) { 243 while(pde=readdir(pd)) {
239 errno=0; 244 errno=0;
240 pid_t pid = atoi(pde->d_name); 245 pid_t pid = atoi(pde->d_name);
241 if((!pid) || pid==selfpid) 246 if((!pid) || pid==selfpid)
242 continue; 247 continue;
243 allpids.push_back(pid); 248 allpids.push_back(pid);
244 } 249 }
245 closedir(pd); 250 closedir(pd);
246 char s[256]; 251 char s[256];
247 procpids.clear(); 252 procpids.clear();
248 for(vector<pid_t>::const_iterator i=allpids.begin();i!=allpids.end();++i) { 253 for(vector<pid_t>::const_iterator i=allpids.begin();i!=allpids.end();++i) {
249 int r = snprintf(s,sizeof(s),"/proc/%d/stat",*i); 254 int r = snprintf(s,sizeof(s),"/proc/%d/stat",*i);
250 if(r>=sizeof(s) || r<1) 255 if(r>=sizeof(s) || r<1)
251 continue; 256 continue;
252 string cmd; 257 string cmd;
253 ifstream ss(s,ios::in); 258 ifstream ss(s,ios::in);
254 if(ss) { 259 if(ss) {
255 getline(ss,cmd); 260 getline(ss,cmd);
256 string::size_type op = cmd.find('('); 261 string::size_type op = cmd.find('(');
257 if(op==string::npos) 262 if(op==string::npos)
258 continue; 263 continue;
259 cmd.erase(0,op+1); 264 cmd.erase(0,op+1);
260 string::size_type cp = cmd.find(')'); 265 string::size_type cp = cmd.find(')');
261 if(cp==string::npos) 266 if(cp==string::npos)
262 continue; 267 continue;
263 cmd.erase(cp); 268 cmd.erase(cp);
264 }else{ 269 }else{
265 r = snprintf(s,sizeof(s),"/proc/%d/status",*i); 270 r = snprintf(s,sizeof(s),"/proc/%d/status",*i);
266 if(r>=sizeof(s) || r<1) 271 if(r>=sizeof(s) || r<1)
267 continue; 272 continue;
268 ifstream ss(s,ios::in); 273 ifstream ss(s,ios::in);
269 if(!ss) 274 if(!ss)
270 continue; 275 continue;
271 ss >> cmd; 276 ss >> cmd;
272 if(cmd.empty()) 277 if(cmd.empty())
273 continue; 278 continue;
274 } 279 }
275 r = snprintf(s,sizeof(s),"/proc/%d/cmdline",*i); 280 r = snprintf(s,sizeof(s),"/proc/%d/cmdline",*i);
276 if(r>=sizeof(s) || r<1) 281 if(r>=sizeof(s) || r<1)
277 continue; 282 continue;
278 ifstream cs(s,ios::binary); 283 ifstream cs(s,ios::binary);
279 if(!cs) 284 if(!cs)
280 continue; 285 continue;
281 string command; 286 string command;
282 while(cs) { 287 while(cs) {
283 string cl; 288 string cl;
284 getline(cs,cl,(char)0); 289 getline(cs,cl,(char)0);
285 string::size_type lsl = cl.rfind('/'); 290 string::size_type lsl = cl.rfind('/');
286 if(lsl!=string::npos) 291 if(lsl!=string::npos)
287 cl.erase(0,lsl+1); 292 cl.erase(0,lsl+1);
288 if(cl.substr(0,cmd.length())==cmd) { 293 if(cl.substr(0,cmd.length())==cmd) {
289 command = cl; 294 command = cl;
290 break; 295 break;
291 } 296 }
292 } 297 }
293 procpids.insert(pair<string,pid_t>(cmd,*i)); 298 procpids.insert(pair<string,pid_t>(cmd,*i));
294 if((!command.empty()) && cmd!=command) 299 if((!command.empty()) && cmd!=command)
295 procpids.insert(pair<string,pid_t>(command,*i)); 300 procpids.insert(pair<string,pid_t>(command,*i));
296 } 301 }
297} 302}