-rw-r--r-- | src/process.cc | 184 |
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> | ||
13 | using namespace std; | ||
14 | #include "process.h" | ||
15 | #include "configuration.h" | ||
16 | |||
17 | void 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 | |||
60 | void 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 | |||
119 | void 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 | |||
142 | void 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 | } | ||