chiark / gitweb /
README shipped; Changelog added.
[userv.git] / overlord.c
1 /*
2  * userv - overlord.c
3  * daemon main program, collects request and forks handlers
4  *
5  * Copyright (C)1996-1997 Ian Jackson
6  *
7  * This is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with userv; if not, write to the Free Software
19  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21
22 #include <wait.h>
23 #include <unistd.h>
24 #include <signal.h>
25 #include <syslog.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <assert.h>
30 #include <fnmatch.h>
31 #include <sys/types.h>
32 #include <sys/time.h>
33 #include <sys/stat.h>
34 #include <sys/socket.h>
35 #include <dirent.h>
36 #include <sys/un.h>
37
38 #include "config.h"
39 #include "common.h"
40 #include "daemon.h"
41
42 static void checkstalepipes(void) {
43   /* There is an unimportant race here.  If there is a stale pipe but
44    * another pair of processes with the same pids is about to create a
45    * new one we can check that the pipe is stale before they recreate
46    * it but then only remove it afterwards; then we remove the pipe
47    * they're actually using causing that invocation to fail with
48    * ENOENT on the pipe.  However, this can only happen if things are
49    * already shafted, because we check for stale pipes at startup
50    * before any children have been started, and then only when a child
51    * dies unhelpfully - and we actually have to have some stale pipes
52    * for the race to exist.
53    */
54   DIR *dir;
55   struct dirent *de;
56   struct stat stab;
57   time_t now;
58   unsigned long timediff;
59   int r;
60   
61   if (time(&now) == -1) { syslog(LOG_ERR,"get current time: %m"); return; }
62   dir= opendir(".");
63   if (!dir) { syslog(LOG_ERR,"open directory " VARDIR ": %m"); return; }
64   while ((de= readdir(dir))) {
65     if (fnmatch(PIPEPATTERN,de->d_name,FNM_PATHNAME|FNM_PERIOD)) continue;
66     r= lstat(de->d_name,&stab); if (r && errno==ENOENT) continue;
67     if (r) { syslog(LOG_ERR,"could not stat `" VARDIR "/%s': %m",de->d_name); continue; }
68     timediff= (unsigned long)now - (unsigned long)stab.st_ctime;
69     if (timediff >= (~0UL>>1) || timediff < 3600) continue;
70     if (unlink(de->d_name) && errno!=ENOENT)
71       syslog(LOG_ERR,"could not remove stale pipe `%s': %m",de->d_name);
72   }
73   if (closedir(dir)) syslog(LOG_ERR,"close directory " VARDIR ": %m");
74 }
75
76 static void sighandler_chld(int x) {
77   pid_t r;
78   int status, es;
79
80   es= errno;
81   for (;;) {
82     r= waitpid((pid_t)-1,&status,WNOHANG);
83     if (!r || (r==-1 && errno==ECHILD)) break;
84     if (r==-1) { syslog(LOG_ERR,"wait in sigchild handler gave error: %m"); break; }
85     if (WIFSIGNALED(status)) {
86       if (WCOREDUMP(status))
87         syslog(LOG_ERR,"call pid %ld dumped core due to signal %s",(long)r,
88                strsignal(WTERMSIG(status)));
89       else
90         syslog(LOG_ERR,"call pid %ld died due to signal %s",
91                (long)r,strsignal(WTERMSIG(status)));
92     } else if (!WIFEXITED(status)) {
93       syslog(LOG_ERR,"call pid %ld died due to unknown reason, code %ld",
94              (long)r,status);
95     } else if (WEXITSTATUS(status)>12) {
96       if (WEXITSTATUS(status)>24)
97         syslog(LOG_ERR,"call pid %ld exited with status %ld >24",
98                (long)r,WEXITSTATUS(status));
99       checkstalepipes();
100     }
101   }
102   errno= es;
103   return;
104 }
105
106 static void blocksignals(int how) {
107   int r;
108   sigset_t set;
109
110   sigemptyset(&set);
111   sigaddset(&set,SIGCHLD);
112   r= sigprocmask(how,&set,0); assert(!r);
113 }
114
115 int main(int argc, char *const *argv) {
116   int mfd, sfd, csocklen, e;
117   struct sigaction childact;
118   struct sockaddr_un ssockname, csockname;
119   pid_t child;
120
121 #ifdef NDEBUG
122   abort(); /* Do not disable assertions in this security-critical code ! */
123 #endif
124
125   if (argc>1) { fputs("usage: uservd\n",stderr); exit(3); }
126
127   openlog(USERVD_LOGIDENT,LOG_NDELAY,USERVD_LOGFACILITY);
128
129   if (chdir(VARDIR)) { syslog(LOG_CRIT,"cannot change to " VARDIR ": %m"); exit(4); }
130   checkstalepipes();
131
132   mfd= socket(AF_UNIX,SOCK_STREAM,0);
133   if (mfd<0) { syslog(LOG_CRIT,"cannot create master socket: %m"); exit(4); }
134
135   assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUS));
136   ssockname.sun_family= AF_UNIX;
137   strcpy(ssockname.sun_path,RENDEZVOUS);
138   unlink(RENDEZVOUS);
139   if (bind(mfd,(struct sockaddr*)&ssockname,sizeof(ssockname)))
140     { syslog(LOG_CRIT,"cannot bind master socket: %m"); exit(4); }
141   if (listen(mfd,5))
142     { syslog(LOG_CRIT,"cannot listen on master socket: %m"); exit(4); }
143
144   childact.sa_handler= sighandler_chld;
145   sigemptyset(&childact.sa_mask);
146   childact.sa_flags= SA_NOCLDSTOP;
147   if (sigaction(SIGCHLD,&childact,0))
148     { syslog(LOG_CRIT,"cannot setup sigchld handler: %m"); exit(4); }
149   syslog(LOG_NOTICE,"started");
150   for (;;) {
151     csocklen= sizeof(csockname);
152     blocksignals(SIG_UNBLOCK);
153     sfd= accept(mfd,(struct sockaddr*)&csockname,&csocklen);
154     e= errno;
155     blocksignals(SIG_BLOCK);
156     if (sfd<0) {
157       errno= e;
158       if (errno == EINTR) continue;
159       if (errno == ENOMEM || errno == EPROTO || errno == EAGAIN) {
160         syslog(LOG_ERR,"unable to accept connection: %m");
161         continue;
162       } else {
163         syslog(LOG_CRIT,"unable to accept new connections: %m");
164         exit(5);
165       }
166     }
167     child= nondebug_fork();
168     if (child == (pid_t)-1) {
169       syslog(LOG_ERR,"unable to fork server: %m");
170       close(sfd);
171       continue;
172     }
173     if (!child) {
174       close(mfd);
175       closelog();
176       blocksignals(SIG_UNBLOCK);
177       servicerequest(sfd);
178     }
179     close(sfd);
180   }
181 }