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