14 int count_procs(service_t sv)
19 for (cnt=0, pp=skip_first(sv->proc_list) ; pp ; pp=skip_next(pp))
22 if (p->exit_time == 0 || p->hold_time > now || p->pipefd >=0 || p->it_forked)
29 void check_service(service_t sv)
31 /* check for exited processes and possibly run crash */
35 for (pp=skip_first(sv->proc_list) ; pp ; ) {
39 if (p->exit_time == 0) {
40 /* monitoring a normal child
41 * we should have been alerted if the child exitted,
42 * but just-in-case... try sig0
44 if (kill(p->pid, 0) == -1 && errno == ESRCH && !is_saved_pid(p->pid)) {
45 p->status = 0; /* who knows. */
49 if (p->exit_time > 0 && p->hold_time == 0) {
50 /* a newly exited process */
52 /* this is a crash recover prog exitting. just set the hold time */
53 p->hold_time = p->exit_time + sv->next_hold;
55 else if (WIFSIGNALED(p->status) ||
56 (WIFEXITED(p->status) &&
57 WEXITSTATUS(p->status) != 0)) {
59 if (p->exit_time - p->start_time < 30) {
60 if (sv->next_hold < MAXINT/2)
64 if (0 && sv->crash_prog) {
67 else p->hold_time = p->exit_time + sv->next_hold;
72 /* if this file is newer than start_time and contains a pid,
73 * record that pid as this pid and shedule regular checks
75 int fd = open(sv->pidfile, 0);
81 && fstat(fd, &stb) != -1
82 && stb.st_mtime >= p->start_time
83 && (n=read(fd, pidline, sizeof(pidline)-1))> 0
84 && (pidline[n]=0 , pid=atoi(pidline)) > 1
90 waituntil(now+5); /* check it in 5 seconds and periodically */
92 /* give the program a few seconds to write to the file
93 * e.g. xdm forks, then creates the pid file
95 if (p->exit_time + 10 > now) {
97 waituntil(p->exit_time + 11);
102 /* probably exited badly if it didn't run for long
103 (keep in mind that the pidfile wasn't updated) */
104 if (p->exit_time - p->start_time < 30) {
105 if (sv->next_hold < MAXINT/2)
110 p->hold_time = p->exit_time + sv->next_hold;
113 if (fd >= 0) close(fd);
119 else if (p->exit_time > 0 && p->it_forked > 0) {
120 /* monitoring forked child
121 * try sending sig0 to see if process still exists
123 if (kill(p->it_forked, 0)== -1 && errno == ESRCH) {
127 waituntil(now+30); /* wait 30 seconds and check again */
129 if (p->pipefd >= 0) {
130 if (readyon(p->pipefd)) {
131 int n = read(p->pipefd, p->pipebuf+p->bufptr, sizeof(p->pipebuf)-p->bufptr-1);
138 while (i < p->bufptr) {
139 while (i < p->bufptr && p->pipebuf[i] != '\n')
141 if (p->pipebuf[i] == '\n' ||
142 i > sizeof(p->pipebuf)-2) {
143 /* ship out this much */
145 p->pipebuf[i] = '\0';
146 dolog(sv, p, p->pipebuf);
147 for (j=i+1 ; j<p->bufptr ; j++)
148 p->pipebuf[j-i-1] = p->pipebuf[j];
159 if (p->exit_time > 0 && p->hold_time < now && p->it_forked == 0
160 && (p->pipefd == -1 || p->exit_time +30 <now)) {
163 /* it has been 30 seconds since the parent exitted, time to
164 * discard the pipe...
169 skip_delete(sv->proc_list, &p->pid);
170 skip_delete(allprocs, &p->pid);
173 else if (p->exit_time > 0 && p->hold_time > 2)
174 waituntil(p->hold_time+1);
176 (sv->class->c_check_service)(sv);
179 int new_proc(service_t sv, char **env)
181 /* fork, register new child
182 * in child, call sv->class->new_child for final setup before exec
187 extern char **environ;
191 if (sv->enabled == 0)
193 if (sv->max_proc > 0 && count_procs(sv) >= sv->max_proc)
194 return -2; /* too many already */
196 if (sv->class->prefork(sv))
200 if (sv->watch_output) {
201 if (pipe(pipefd)== -1)
202 pipefd[0] = pipefd[1] = -1;
204 switch (pid = fork()) {
206 if (sv->watch_output) {
207 close(pipefd[0]); close(pipefd[1]);
209 return -1; /* cannot fork */
211 errors_to(ERROR_SYSLOG, NULL);
213 if (chdir(sv->home_dir)!=0) {
214 error("Couldn't chdir to %s", sv->home_dir);
219 struct passwd *pw = getpwnam(sv->username);
221 initgroups(sv->username, pw->pw_gid);
225 error("unknown user %s", sv->username);
229 (sv->class->new_child)(sv);
235 for (i=0 ; env[i] ; i++) cnt++;
236 for (i=0 ; environ[i] ; i++) cnt++;
237 envp = (char**)malloc((cnt+1) * sizeof(char*));
239 for (i=0 ; env[i] ; i++)
240 envp[cnt++] = env[i];
241 for (i=0 ; environ[i] ; i++)
242 envp[cnt++] = environ[i];
245 if (sv->watch_output && pipefd[0] >= 0) {
247 if (pipefd[1] != 1) close(1);
248 if (pipefd[1] != 2) close(2);
249 if (pipefd[1] == 0) {
250 pipefd[1] = dup(pipefd[1]);
253 open("/dev/null", 0);
260 execve(sv->program, sv->args, envp);
262 default: /* parent */
263 p = (proc_t)malloc(sizeof(struct proc));
264 if (sv->watch_output) {
266 p->pipefd = pipefd[0];
268 if (p->pipefd >= 0) listenon(p->pipefd);
269 fcntl(p->pipefd, F_SETFD, 1);
280 skip_insert(sv->proc_list, p);
282 skip_insert(allprocs, p);
283 sv->class->new_parent(sv,p);
288 void prepare_restart(void)
291 for (sp = skip_first(services) ; sp ; sp=skip_next(sp)) {
294 for (pp=skip_first(sv->proc_list) ; pp ; pp=skip_next(pp)) {
297 fcntl(p->pipefd, F_SETFD, 0);
302 static int proc_cmp(proc_t a, proc_t b, int *kp)
305 if (b) k=b->pid; else k = *kp;
309 service_t new_service(char *name, class_t class)
311 service_t sv = (service_t)malloc(sizeof(struct service));
313 sv->service = strdup(name);
316 sv->crash_prog = NULL;
320 sv->watch_output = 0;
326 sv->proc_list = skip_new(proc_cmp, NULL, NULL);
328 (*class->init_state)(sv);
332 int process_opt(service_t sv, char *opt)
334 /* max= crash= dir= user= enabled= */
336 if (strncmp(opt, "max=", 4)==0) {
337 sv->max_proc = atoi(opt+4);
339 } else if (strncmp(opt, "crash=", 6)==0) {
340 sv->crash_prog = strdup(opt+6);
342 } else if (strncmp(opt, "dir=", 4)==0) {
343 sv->home_dir = strdup(opt+4);
345 } else if (strncmp(opt, "user=", 5)==0) {
346 sv->username = strdup(opt+5);
348 } else if (strncmp(opt, "enabled=", 8)==0) {
349 if (strcmp(opt+8, "no")==0)
351 else if (strcmp(opt+8, "yes")==0)
355 } else if (strncmp(opt, "pidfile=", 8)==0) {
356 sv->pidfile = strdup(opt+8);
358 } else if (strcmp(opt, "watch_output")==0) {
359 sv->watch_output = 1;
365 static int service_cmp(service_t a, service_t b, char *k)
367 if (b) k = b->service;
368 return strcmp(a->service, k);
371 void service_init(void)
373 services = skip_new(service_cmp, NULL, NULL);
374 allprocs = skip_new(proc_cmp, NULL, NULL);
377 void free_service(service_t sv)
379 if (sv->service) free(sv->service);
380 if (sv->crash_prog) free(sv->crash_prog);
381 if (sv->classinfo) (sv->class->free_state)(sv);
382 if (sv->home_dir) free(sv->home_dir);
383 if (sv->username) free(sv->username);
384 if (sv->program) free(sv->program);
385 if (sv->pidfile) free(sv->pidfile);
386 if (sv->args) strlistfree(sv->args);
387 if (sv->proc_list) skip_free(sv->proc_list);
392 service_t find_service(char *name)
395 if (name == NULL) return NULL;
396 sp = skip_search(services, name);