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