chiark / gitweb /
095dd44e89d2778c5241568e1cd5bfff0f565cc8
[chiark-utils.git] / cprogs / prefork.c
1 /* common stuff for cgi-fcgi-interp and prefork-interp */
2
3 #include "prefork.h"
4
5 const char *interp, *ident;
6
7 struct sha256_ctx identsc;
8
9 uid_t us;
10 const char *run_base, *script, *socket_path, *lock_path;
11 const char *run_base_mkdir_p;
12 bool logging;
13
14 void common_diee(const char *m) { diee("%s", m); }
15 void common_die (const char *m) { die ("%s", m); }
16
17 void vmsgcore(int estatus, int errnoval, const char *fmt, va_list al) {
18   int r;
19
20   if (logging) {
21     const char *fmt_use = fmt;
22     char *fmt_free = 0;
23     if (errnoval) {
24       r = asprintf(&fmt_free, "%s: %%m", fmt);
25       if (r) {
26         fmt_free = 0;
27       } else {
28         fmt_use = fmt_free;
29       }
30     }
31     vsyslog(LOG_ERR, fmt_use, al);
32     free(fmt_free);
33   } else {
34     fprintf(stderr, "%s: ", our_name);
35     vfprintf(stderr,fmt,al);
36     if (errnoval!=-1) fprintf(stderr,": %s",strerror(errnoval));
37     fputc('\n',stderr);
38   }
39   if (estatus) exit(estatus);
40 }
41
42 void usagemessage(void) { fusagemessage(stderr); }
43
44 void of_help(const struct cmdinfo *ci, const char *val) {
45   fusagemessage(stdout);
46   if (ferror(stdout)) diee("write usage message to stdout");
47   exit(0);
48 }
49
50 void of_iassign(const struct cmdinfo *ci, const char *val) {
51   long v;
52   char *ep;
53   errno= 0; v= strtol(val,&ep,10);
54   if (!*val || *ep || errno || v<INT_MIN || v>INT_MAX)
55     badusage("bad integer argument `%s' for --%s",val,ci->olong);
56   *ci->iassignto = v;
57 }
58
59 void ident_addstring(const struct cmdinfo *ci, const char *string) {
60   /* ci may be 0 and is provided so this can be .call */
61   sha256_update(&identsc,strlen(string)+1,string);
62 }
63
64 void off_ident_addenv(const struct cmdinfo *ci, const char *name) {
65   const char *val = getenv(name);
66   if (val) {
67     sha256_update(&identsc,strlen(name),name); /* no nul */
68     sha256_update(&identsc,1,"=");
69     ident_addstring(0,val);
70   } else {
71     ident_addstring(0,name);
72   }
73 }
74
75 bool find_run_base_var_run(void) {
76   struct stat stab;
77   char *try;
78   int r;
79
80   try = m_asprintf("%s/%lu", "/var/run/user", us);
81   r = lstat(try, &stab);
82   if (r<0) {
83     if (errno == ENOENT ||
84         errno == ENOTDIR ||
85         errno == EACCES ||
86         errno == EPERM)
87       return 0; /* oh well */
88     diee("stat /var/run/user/UID");
89   }
90   if (!S_ISDIR(stab.st_mode)) {
91     warning("%s not a directory, falling back to ~\n", try);
92     return 0;
93   }
94   if (stab.st_uid != us) {
95     warning("%s not owned by uid %lu, falling back to ~\n", try,
96             (unsigned long)us);
97     return 0;
98   }
99   if (stab.st_mode & 0077) {
100     warning("%s writeable by group or other, falling back to ~\n", try);
101     return 0;
102   }
103   run_base = m_asprintf("%s/%s", try, our_name);
104   return 1;
105 }
106
107 static bool find_run_base_home(void) {
108   struct passwd *pw;
109   struct utsname ut;
110   char *dot, *try;
111   int r;
112
113   pw = getpwuid(us);  if (!pw) diee("getpwent(uid)");
114
115   r = uname(&ut);   if (r) diee("uname(2)");
116   dot = strchr(ut.nodename, '.');
117   if (dot) *dot = 0;
118   if (sizeof(ut.nodename) > 32)
119     ut.nodename[32] = 0;
120
121   run_base_mkdir_p = m_asprintf("%s/.%s", pw->pw_dir, our_name);
122   try = m_asprintf("%s/%s", run_base_mkdir_p, ut.nodename);
123   run_base = try;
124   return 1;
125 }
126
127 void find_socket_path(void) {
128   struct sockaddr_un sun;
129   int r;
130
131   us = getuid();  if (us==(uid_t)-1) diee("getuid");
132
133   find_run_base_var_run() ||
134     find_run_base_home() ||
135     (abort(),0);
136
137   int maxidentlen = sizeof(sun.sun_path) - strlen(run_base) - 10 - 2;
138
139   if (!ident) {
140     if (maxidentlen < MINHEXHASH)
141       die("base directory `%s'"
142           " leaves only %d characters for id hash"
143           " which is too little (<%d)",
144           run_base, maxidentlen, MINHEXHASH);
145
146     int identlen = maxidentlen > 64 ? 64 : maxidentlen;
147     char *hexident = xmalloc(identlen + 2);
148     unsigned char bbuf[32];
149     int i;
150
151     ident_addstring(0,interp);
152     ident_addstring(0,script);
153     sha256_digest(&identsc,sizeof(bbuf),bbuf);
154
155     for (i=0; i<identlen; i += 2)
156       sprintf(hexident+i, "%02x", bbuf[i/2]);
157
158     hexident[identlen] = 0;
159     ident = hexident;
160   }
161
162   if (strlen(ident) > maxidentlen)
163     die("base directory `%s' plus ident `%s' too long"
164         " (with spare) for socket (max ident %d)\n",
165         run_base, ident, maxidentlen);
166
167   r = mkdir(run_base, 0700);
168   if (r && errno==ENOENT && run_base_mkdir_p) {
169     r = mkdir(run_base_mkdir_p, 0700);
170     if (r) diee("mkdir %s (since %s was ENOENT)",run_base_mkdir_p,run_base);
171     r = mkdir(run_base, 0700);
172   }
173   if (r) {
174     if (!(errno == EEXIST))
175       diee("mkdir %s",run_base);
176   }
177
178   socket_path = m_asprintf("%s/s%s",run_base,ident);
179 }  
180
181 // Returns fd
182 int acquire_lock(void) {
183   int r;
184   int lockfd = -1;
185
186   lock_path = m_asprintf("%s/l%s",run_base,ident);
187
188   lockfd = open(lock_path, O_CREAT|O_RDWR, 0600);
189   if (lockfd<0) diee("create lock (%s)", lock_path);
190
191   r = flock(lockfd, LOCK_EX);
192   if (r) diee("lock lock (%s)", lock_path);
193
194   return lockfd;
195 }
196
197 static void shbang_opts(const char *const **argv_io,
198                         const struct cmdinfo *cmdinfos) {
199   myopt(argv_io, cmdinfos);
200
201   interp = *(*argv_io)++;
202   if (!interp) badusage("need interpreter argument");
203 }
204
205 void process_opts(const char *const **argv_io) {
206   const char *smashedopt;
207
208   sha256_init(&identsc);
209
210   if ((*argv_io)[0] &&
211       (smashedopt = (*argv_io)[1]) &&
212       smashedopt[0]=='-' &&
213       (strchr(smashedopt,' ') || strchr(smashedopt,','))) {
214     /* single argument containg all the options and <interp> */
215     *argv_io += 2; /* eat argv[0] and smashedopt */
216     const char *split_args[MAX_OPTS+1];
217     int split_argc = 0;
218     split_args[split_argc++] = (*argv_io)[0];
219     for (;;) {
220       if (split_argc >= MAX_OPTS) die("too many options in combined arg");
221       split_args[split_argc++] = smashedopt;
222       if (smashedopt[0] != '-') /* never true on first iteration */
223         break;
224       char *delim = strchr(smashedopt,' ');
225       if (!delim) delim = strchr(smashedopt,',');
226       if (!delim) badusage("combined arg lacks <interpreter>");
227       *delim = 0;
228       smashedopt = delim+1;
229     }
230     assert(split_argc <= MAX_OPTS);
231     split_args[split_argc++] = 0;
232
233     const char *const *split_argv = split_args;
234
235     shbang_opts(&split_argv, cmdinfos);
236     /* sets interp */
237   } else {
238     shbang_opts(argv_io, cmdinfos);
239   }
240
241   if (**argv_io)
242     script = *(*argv_io)++;
243 }