initial AP2 support (win)
[mod_fastcgi.git] / fcgi_util.c
blob0f4181d3ebe1e3ee78b1b15b9a59e5c5bd5a9853
1 /*
2 * $Id: fcgi_util.c,v 1.23 2002/07/23 00:54:18 robs Exp $
3 */
5 #include "fcgi.h"
7 #ifdef WIN32
8 #pragma warning( disable : 4100 )
9 #endif
11 uid_t
12 fcgi_util_get_server_uid(const server_rec * const s)
14 #ifdef APACHE2
15 /* AP2TODO get the server's uid */
16 return 0;
17 #else
18 return s->server_uid;
19 #endif
22 uid_t
23 fcgi_util_get_server_gid(const server_rec * const s)
25 #ifdef APACHE2
26 /* AP2TODO get the server's gid */
27 return 0;
28 #else
29 return s->server_gid;
30 #endif
33 /*******************************************************************************
34 * Compute printable MD5 hash. Pool p is used for scratch as well as for
35 * allocating the hash - use temp storage, and dup it if you need to keep it.
37 char *
38 fcgi_util_socket_hash_filename(pool *p, const char *path,
39 const char *user, const char *group)
41 char *buf = ap_pstrcat(p, path, user, group, NULL);
43 /* Canonicalize the path (remove "//", ".", "..") */
44 ap_getparents(buf);
46 return ap_md5(p, (unsigned char *)buf);
50 /*******************************************************************************
51 * Concat src1 and src2 using the approprate path seperator for the platform.
53 static char * make_full_path(pool *a, const char *src1, const char *src2)
55 #ifdef WIN32
56 register int x;
57 char * p ;
58 char * q ;
60 x = strlen(src1);
62 if (x == 0) {
63 p = ap_pstrcat(a, "\\", src2, NULL);
65 else if (src1[x - 1] != '\\' && src1[x - 1] != '/') {
66 p = ap_pstrcat(a, src1, "\\", src2, NULL);
68 else {
69 p = ap_pstrcat(a, src1, src2, NULL);
72 q = p ;
73 while (*q)
75 if (*q == '/') {
76 *q = '\\' ;
78 ++q;
81 return p ;
82 #else
83 return ap_make_full_path(a, src1, src2);
84 #endif
87 /*******************************************************************************
88 * Return absolute path to file in either "regular" FCGI socket directory or
89 * the dynamic directory. Result is allocated in pool p.
91 const char *
92 fcgi_util_socket_make_path_absolute(pool * const p,
93 const char *const file, const int dynamic)
95 #ifdef APACHE2
96 if (ap_os_is_path_absolute(p, (char *) file))
97 #else
98 if (ap_os_is_path_absolute(file))
99 #endif
101 return file;
103 else
105 const char * parent_dir = dynamic ? fcgi_dynamic_dir : fcgi_socket_dir;
106 return (const char *) make_full_path(p, parent_dir, file);
110 #ifndef WIN32
111 /*******************************************************************************
112 * Build a Domain Socket Address structure, and calculate its size.
113 * The error message is allocated from the pool p. If you don't want the
114 * struct sockaddr_un also allocated from p, pass it preallocated (!=NULL).
116 const char *
117 fcgi_util_socket_make_domain_addr(pool *p, struct sockaddr_un **socket_addr,
118 int *socket_addr_len, const char *socket_path)
120 int socket_pathLen = strlen(socket_path);
122 if (socket_pathLen >= sizeof((*socket_addr)->sun_path)) {
123 return ap_pstrcat(p, "path \"", socket_path,
124 "\" is too long for a Domain socket", NULL);
127 if (*socket_addr == NULL)
128 *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_un));
129 else
130 memset(*socket_addr, 0, sizeof(struct sockaddr_un));
132 (*socket_addr)->sun_family = AF_UNIX;
133 strcpy((*socket_addr)->sun_path, socket_path);
135 *socket_addr_len = SUN_LEN(*socket_addr);
136 return NULL;
138 #endif
140 /*******************************************************************************
141 * Convert a hostname or IP address string to an in_addr struct.
143 static int
144 convert_string_to_in_addr(const char * const hostname, struct in_addr * const addr)
146 struct hostent *hp;
147 int count;
149 addr->s_addr = inet_addr((char *)hostname);
151 if (addr->s_addr == INADDR_NONE) {
152 if ((hp = gethostbyname((char *)hostname)) == NULL)
153 return -1;
155 memcpy((char *) addr, hp->h_addr, hp->h_length);
156 count = 0;
157 while (hp->h_addr_list[count] != 0)
158 count++;
160 return count;
162 return 1;
166 /*******************************************************************************
167 * Build an Inet Socket Address structure, and calculate its size.
168 * The error message is allocated from the pool p. If you don't want the
169 * struct sockaddr_in also allocated from p, pass it preallocated (!=NULL).
171 const char *
172 fcgi_util_socket_make_inet_addr(pool *p, struct sockaddr_in **socket_addr,
173 int *socket_addr_len, const char *host, unsigned short port)
175 if (*socket_addr == NULL)
176 *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_in));
177 else
178 memset(*socket_addr, 0, sizeof(struct sockaddr_in));
180 (*socket_addr)->sin_family = AF_INET;
181 (*socket_addr)->sin_port = htons(port);
183 /* Get an in_addr represention of the host */
184 if (host != NULL) {
185 if (convert_string_to_in_addr(host, &(*socket_addr)->sin_addr) != 1) {
186 return ap_pstrcat(p, "failed to resolve \"", host,
187 "\" to exactly one IP address", NULL);
189 } else {
190 (*socket_addr)->sin_addr.s_addr = htonl(INADDR_ANY);
193 *socket_addr_len = sizeof(struct sockaddr_in);
194 return NULL;
197 /*******************************************************************************
198 * Determine if a process with uid/gid can access a file with mode permissions.
200 const char *
201 fcgi_util_check_access(pool *tp,
202 const char * const path, const struct stat *statBuf,
203 const int mode, const uid_t uid, const gid_t gid)
205 if (statBuf == NULL) {
206 static struct stat staticStatBuf;
208 if (stat(path, &staticStatBuf) < 0)
209 return ap_psprintf(tp, "stat(%s) failed: %s", path, strerror(errno));
210 statBuf = &staticStatBuf;
213 #ifndef WIN32
214 /* If the uid owns the file, check the owner bits */
215 if (uid == statBuf->st_uid) {
216 if (mode & R_OK && !(statBuf->st_mode & S_IRUSR))
217 return "read not allowed by owner";
218 if (mode & W_OK && !(statBuf->st_mode & S_IWUSR))
219 return "write not allowed by owner";
220 if (mode & X_OK && !(statBuf->st_mode & S_IXUSR))
221 return "execute not allowed by owner";
222 return NULL;
224 #else
225 if (mode & _S_IREAD && !(statBuf->st_mode & _S_IREAD))
226 return "read not allowed";
227 if (mode & _S_IWRITE && !(statBuf->st_mode & _S_IWRITE))
228 return "write not allowed";
230 // I don't think this works on FAT, but since I don't know how to check..
231 // if (mode & _S_IEXEC && !(statBuf->st_mode & _S_IEXEC))
232 // return "execute not allowed";
233 #endif
235 #if !defined(__EMX__) && !defined(WIN32)
236 /* If the gid is same as the file's group, check the group bits */
237 if (gid == statBuf->st_gid) {
238 if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
239 return "read not allowed by group";
240 if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
241 return "write not allowed by group";
242 if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
243 return "execute not allowed by group";
244 return NULL;
247 /* Get the user membership for the file's group. If the
248 * uid is a member, check the group bits. */
250 const struct group * const gr = getgrgid(statBuf->st_gid);
251 const struct passwd * const pw = getpwuid(uid);
253 if (gr != NULL && pw != NULL) {
254 char **user = gr->gr_mem;
255 for ( ; *user != NULL; user++) {
256 if (strcmp(*user, pw->pw_name) == 0) {
257 if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
258 return "read not allowed by group";
259 if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
260 return "write not allowed by group";
261 if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
262 return "execute not allowed by group";
263 return NULL;
269 /* That just leaves the other bits.. */
270 if (mode & R_OK && !(statBuf->st_mode & S_IROTH))
271 return "read not allowed";
272 if (mode & W_OK && !(statBuf->st_mode & S_IWOTH))
273 return "write not allowed";
274 if (mode & X_OK && !(statBuf->st_mode & S_IXOTH))
275 return "execute not allowed";
276 #endif
278 return NULL;
282 /*******************************************************************************
283 * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is
284 * enabled with matching uid and gid.
286 fcgi_server *
287 fcgi_util_fs_get_by_id(const char *ePath, uid_t uid, gid_t gid)
289 char path[FCGI_MAXPATH];
290 fcgi_server *s;
292 /* @@@ This should now be done in the loop below */
293 ap_cpystrn(path, ePath, FCGI_MAXPATH);
294 ap_no2slash(path);
296 for (s = fcgi_servers; s != NULL; s = s->next) {
297 int i;
298 const char *fs_path = s->fs_path;
299 for (i = 0; fs_path[i] && path[i]; ++i) {
300 if (fs_path[i] != path[i]) {
301 break;
304 if (fs_path[i]) {
305 continue;
307 if (path[i] == '\0' || path[i] == '/') {
308 if (fcgi_wrapper == NULL || (uid == s->uid && gid == s->gid))
309 return s;
312 return NULL;
315 /*******************************************************************************
316 * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is
317 * enabled with matching user and group.
319 fcgi_server *
320 fcgi_util_fs_get(const char *ePath, const char *user, const char *group)
322 char path[FCGI_MAXPATH];
323 fcgi_server *s;
325 ap_cpystrn(path, ePath, FCGI_MAXPATH);
326 ap_no2slash(path);
328 for (s = fcgi_servers; s != NULL; s = s->next) {
329 if (strcmp(s->fs_path, path) == 0) {
330 if (fcgi_wrapper == NULL)
331 return s;
333 if (strcmp(user, s->user) == 0
334 && (user[0] == '~' || strcmp(group, s->group) == 0))
336 return s;
340 return NULL;
343 const char *
344 fcgi_util_fs_is_path_ok(pool * const p, const char * const fs_path, struct stat *finfo)
346 const char *err;
348 if (finfo == NULL) {
349 finfo = (struct stat *)ap_palloc(p, sizeof(struct stat));
350 if (stat(fs_path, finfo) < 0)
351 return ap_psprintf(p, "stat(%s) failed: %s", fs_path, strerror(errno));
354 /* No Parse Header scripts aren't allowed.
355 * @@@ Well... we really could quite easily */
356 if (strncmp(strrchr(fs_path, '/'), "/nph-", 5) == 0)
357 return ap_psprintf(p, "NPH scripts cannot be run as FastCGI");
359 if (finfo->st_mode == 0)
360 return ap_psprintf(p, "script not found or unable to stat()");
362 if (S_ISDIR(finfo->st_mode))
363 return ap_psprintf(p, "script is a directory!");
365 /* Let the wrapper determine what it can and can't execute */
366 if (! fcgi_wrapper)
368 #ifdef WIN32
369 err = fcgi_util_check_access(p, fs_path, finfo, _S_IEXEC, fcgi_user_id, fcgi_group_id);
370 #else
371 err = fcgi_util_check_access(p, fs_path, finfo, X_OK, fcgi_user_id, fcgi_group_id);
372 #endif
373 if (err) {
374 return ap_psprintf(p,
375 "access for server (uid %ld, gid %ld) not allowed: %s",
376 (long)fcgi_user_id, (long)fcgi_group_id, err);
380 return NULL;
385 /*******************************************************************************
386 * Allocate a new FastCGI server record from pool p with default values.
388 fcgi_server *
389 fcgi_util_fs_new(pool *p)
391 fcgi_server *s = (fcgi_server *) ap_pcalloc(p, sizeof(fcgi_server));
393 /* Initialize anything who's init state is not zeroizzzzed */
394 s->listenQueueDepth = FCGI_DEFAULT_LISTEN_Q;
395 s->appConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT;
396 s->idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT;
397 s->initStartDelay = DEFAULT_INIT_START_DELAY;
398 s->restartDelay = FCGI_DEFAULT_RESTART_DELAY;
399 s->restartOnExit = FALSE;
400 s->directive = APP_CLASS_UNKNOWN;
401 s->processPriority = FCGI_DEFAULT_PRIORITY;
402 s->envp = &fcgi_empty_env;
404 #ifdef WIN32
405 s->listenFd = (int) INVALID_HANDLE_VALUE;
406 #else
407 s->listenFd = -2;
408 #endif
410 return s;
413 /*******************************************************************************
414 * Add the server to the linked list of FastCGI servers.
416 void
417 fcgi_util_fs_add(fcgi_server *s)
419 s->next = fcgi_servers;
420 fcgi_servers = s;
423 /*******************************************************************************
424 * Configure uid, gid, user, group, username for wrapper.
426 const char *
427 fcgi_util_fs_set_uid_n_gid(pool *p, fcgi_server *s, uid_t uid, gid_t gid)
429 #ifndef WIN32
430 struct passwd *pw;
431 struct group *gr;
432 #endif
434 if (fcgi_wrapper == NULL)
435 return NULL;
437 #ifndef WIN32
438 s->uid = uid;
439 pw = getpwuid(uid);
440 if (pw == NULL) {
441 return ap_psprintf(p,
442 "getpwuid() couldn't determine the username for uid '%ld', "
443 "you probably need to modify the User directive: %s",
444 (long)uid, strerror(errno));
446 s->user = ap_pstrdup(p, pw->pw_name);
447 s->username = s->user;
449 s->gid = gid;
450 gr = getgrgid(gid);
451 if (gr == NULL) {
452 return ap_psprintf(p,
453 "getgrgid() couldn't determine the group name for gid '%ld', "
454 "you probably need to modify the Group directive: %s",
455 (long)gid, strerror(errno));
457 s->group = ap_pstrdup(p, gr->gr_name);
458 #endif
459 return NULL;
462 /*******************************************************************************
463 * Allocate an array of ServerProcess records.
465 ServerProcess *
466 fcgi_util_fs_create_procs(pool *p, int num)
468 int i;
469 ServerProcess *proc = (ServerProcess *)ap_pcalloc(p, sizeof(ServerProcess) * num);
471 for (i = 0; i < num; i++) {
472 #ifdef WIN32
473 proc[i].handle = INVALID_HANDLE_VALUE;
474 proc[i].terminationEvent = INVALID_HANDLE_VALUE;
475 #endif
476 proc[i].pid = 0;
477 proc[i].state = FCGI_READY_STATE;
479 return proc;
482 int fcgi_util_ticks(struct timeval * tv)
484 #ifdef WIN32
485 // millisecs is sufficent granularity
486 DWORD millis = GetTickCount();
488 tv->tv_sec = millis / 1000;
489 tv->tv_usec = (millis % 1000) * 1000;
491 return 0;
492 #else
493 return gettimeofday(tv, NULL);
494 #endif