summaryrefslogtreecommitdiffabout
path: root/src/process.cc
Unidiff
Diffstat (limited to 'src/process.cc') (more/less context) (ignore whitespace changes)
-rw-r--r--src/process.cc14
1 files changed, 8 insertions, 6 deletions
diff --git a/src/process.cc b/src/process.cc
index 96c874f..6d3b2a2 100644
--- a/src/process.cc
+++ b/src/process.cc
@@ -1,231 +1,233 @@
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(user.empty()) { 98 if(!group.empty()) {
99 if((getgid()!=gid) && setgid(gid)) 99 if(user.empty()) {
100 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setgid()"); 100 if((getgid()!=gid) && setgid(gid))
101 }else{ 101 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setgid()");
102 if(initgroups(user.c_str(),gid)) 102 }else{
103 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to initgroups()"); 103 if(initgroups(user.c_str(),gid))
104 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to initgroups()");
105 }
104 } 106 }
105 if(!chroot.empty()) { 107 if(!chroot.empty()) {
106 if(::chroot(chroot.c_str())) 108 if(::chroot(chroot.c_str()))
107 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to chroot()"); 109 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to chroot()");
108 } 110 }
109 if(!user.empty()) { 111 if(!user.empty()) {
110 if((getuid()!=uid) && setuid(uid)) 112 if((getuid()!=uid) && setuid(uid))
111 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setuid()"); 113 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setuid()");
112 } 114 }
113 char *argv[] = { "/bin/sh", "-c", (char*)restart_cmd.c_str(), NULL }; 115 char *argv[] = { "/bin/sh", "-c", (char*)restart_cmd.c_str(), NULL };
114 close(0); close(1); close(2); 116 close(0); close(1); close(2);
115 execv("/bin/sh",argv); 117 execv("/bin/sh",argv);
116 }catch(exception& e) { 118 }catch(exception& e) {
117 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());
118 } 120 }
119 _exit(-1); 121 _exit(-1);
120 } 122 }
121 // parent 123 // parent
122 int rv; 124 int rv;
123 if(waitpid(p,&rv,0)<0) 125 if(waitpid(p,&rv,0)<0)
124 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to waitpid()"); 126 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to waitpid()");
125} 127}
126 128
127void 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) {
128 string the_notify; 130 string the_notify;
129 if(!notify.empty()) 131 if(!notify.empty())
130 the_notify=notify; 132 the_notify=notify;
131 else if(!config.notify.empty()) 133 else if(!config.notify.empty())
132 the_notify=config.notify; 134 the_notify=config.notify;
133 else 135 else
134 return; 136 return;
135 try { 137 try {
136 string::size_type colon = the_notify.find(':'); 138 string::size_type colon = the_notify.find(':');
137 if(colon==string::npos) 139 if(colon==string::npos)
138 throw runtime_error("invalid notify action specification"); 140 throw runtime_error("invalid notify action specification");
139 string nschema = the_notify.substr(0,colon); 141 string nschema = the_notify.substr(0,colon);
140 string ntarget = the_notify.substr(colon+1); 142 string ntarget = the_notify.substr(colon+1);
141 if(nschema=="mailto") { 143 if(nschema=="mailto") {
142 notify_mailto(ntarget,id,event,description,config); 144 notify_mailto(ntarget,id,event,description,config);
143 }else 145 }else
144 throw runtime_error("unrecognized notification schema"); 146 throw runtime_error("unrecognized notification schema");
145 }catch(exception& e) { 147 }catch(exception& e) {
146 syslog(LOG_ERR,"Notification error: %s",e.what()); 148 syslog(LOG_ERR,"Notification error: %s",e.what());
147 } 149 }
148} 150}
149 151
150void 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) {
151 int files[2]; 153 int files[2];
152 if(pipe(files)) 154 if(pipe(files))
153 throw runtime_error("Failed to pipe()"); 155 throw runtime_error("Failed to pipe()");
154 pid_t pid = vfork(); 156 pid_t pid = vfork();
155 if(pid==-1) { 157 if(pid==-1) {
156 close(files[0]); 158 close(files[0]);
157 close(files[1]); 159 close(files[1]);
158 throw runtime_error("Failed to vfork()"); 160 throw runtime_error("Failed to vfork()");
159 } 161 }
160 if(!pid) { 162 if(!pid) {
161 // child 163 // child
162 if(dup2(files[0],0)!=0) 164 if(dup2(files[0],0)!=0)
163 _exit(-1); 165 _exit(-1);
164 close(1); 166 close(1);
165 close(files[0]); 167 close(files[0]);
166 close(files[1]); 168 close(files[1]);
167 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);
168 _exit(-1); 170 _exit(-1);
169 } 171 }
170 // parent 172 // parent
171 close(files[0]); 173 close(files[0]);
172 FILE *mta = fdopen(files[1],"w"); 174 FILE *mta = fdopen(files[1],"w");
173 for(headers_t::const_iterator i=mailto_headers.begin();i!=mailto_headers.end();++i) { 175 for(headers_t::const_iterator i=mailto_headers.begin();i!=mailto_headers.end();++i) {
174 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str()); 176 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str());
175 } 177 }
176 for(headers_t::const_iterator i=config.mailto_headers.begin();i!=config.mailto_headers.end();++i) { 178 for(headers_t::const_iterator i=config.mailto_headers.begin();i!=config.mailto_headers.end();++i) {
177 if(mailto_headers.find(i->first)!=mailto_headers.end()) 179 if(mailto_headers.find(i->first)!=mailto_headers.end())
178 continue; 180 continue;
179 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str()); 181 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str());
180 } 182 }
181 fprintf(mta, 183 fprintf(mta,
182 "Subject: [%s] %s\n\n" 184 "Subject: [%s] %s\n\n"
183 "%s\n" 185 "%s\n"
184 "---\n" 186 "---\n"
185 "This message was sent automatically by the 'dudki' daemon\n", 187 "This message was sent automatically by the 'dudki' daemon\n",
186 id.c_str(), event.c_str(), 188 id.c_str(), event.c_str(),
187 description.c_str() ); 189 description.c_str() );
188 fclose(mta); 190 fclose(mta);
189 int status; 191 int status;
190 waitpid(pid,&status,0); 192 waitpid(pid,&status,0);
191 // TODO: check the return code 193 // TODO: check the return code
192} 194}
193 195
194void process::signal(int signum) const { 196void process::signal(int signum) const {
195 if(!pidfile.empty()) { 197 if(!pidfile.empty()) {
196 ifstream pids(pidfile.c_str(),ios::in); 198 ifstream pids(pidfile.c_str(),ios::in);
197 if(!pids) 199 if(!pids)
198 throw runtime_error("no pidfile found"); 200 throw runtime_error("no pidfile found");
199 pid_t pid = 0; 201 pid_t pid = 0;
200 pids >> pid; 202 pids >> pid;
201 pids.close(); 203 pids.close();
202 if(!pid) 204 if(!pid)
203 throw runtime_error("no pid in pidfile"); 205 throw runtime_error("no pid in pidfile");
204 if(kill(pid,signum)) 206 if(kill(pid,signum))
205 throw runtime_error("failed to signal process"); 207 throw runtime_error("failed to signal process");
206 }else if(!process_name.empty()) { 208 }else if(!process_name.empty()) {
207 if(procpids.empty()) 209 if(procpids.empty())
208 gather_proc_info(); 210 gather_proc_info();
209 pair<multimap<string,pid_t>::const_iterator,multimap<string,pid_t>::const_iterator> range = procpids.equal_range(process_name); 211 pair<multimap<string,pid_t>::const_iterator,multimap<string,pid_t>::const_iterator> range = procpids.equal_range(process_name);
210 int count = 0; 212 int count = 0;
211 for(multimap<string,pid_t>::const_iterator i=range.first;i!=range.second;++i) { 213 for(multimap<string,pid_t>::const_iterator i=range.first;i!=range.second;++i) {
212 pid_t pid = i->second; 214 pid_t pid = i->second;
213 if(kill(i->second,signum)) 215 if(kill(i->second,signum))
214 throw runtime_error("failed to signal process"); 216 throw runtime_error("failed to signal process");
215 count++; 217 count++;
216 } 218 }
217 if(!count) 219 if(!count)
218 throw runtime_error("no running instance detected"); 220 throw runtime_error("no running instance detected");
219 }else 221 }else
220 throw runtime_error("nothing is known about the process"); 222 throw runtime_error("nothing is known about the process");
221} 223}
222 224
223void process::prepare_herd() { 225void process::prepare_herd() {
224 procpids.clear(); 226 procpids.clear();
225} 227}
226void process::unprepare_herd() { 228void process::unprepare_herd() {
227 procpids.clear(); 229 procpids.clear();
228} 230}
229void process::gather_proc_info() { 231void process::gather_proc_info() {
230 vector<pid_t> allpids; 232 vector<pid_t> allpids;
231 DIR *pd = opendir("/proc"); 233 DIR *pd = opendir("/proc");