Reccomend against using appConnTimeout
[mod_fastcgi.git] / fcgi_util.c
blobb213ac2cb471a8c8af0389f8a3844db1086c8d57
1 /*
2 * $Id: fcgi_util.c,v 1.12 2000/05/12 13:52:29 robs Exp $
3 */
5 #include "fcgi.h"
7 /*******************************************************************************
8 * Compute printable MD5 hash. Pool p is used for scratch as well as for
9 * allocating the hash - use temp storage, and dup it if you need to keep it.
11 char *
12 fcgi_util_socket_hash_filename(pool *p, const char *path,
13 const char *user, const char *group)
15 char *buf = ap_pstrcat(p, path, user, group, NULL);
17 /* Canonicalize the path (remove "//", ".", "..") */
18 ap_getparents(buf);
20 return ap_md5(p, (unsigned char *)buf);
23 /*******************************************************************************
24 * Return absolute path to file in either "regular" FCGI socket directory or
25 * the dynamic directory. Result is allocated in pool p.
27 const char *
28 fcgi_util_socket_make_path_absolute(pool * const p,
29 const char *const file, const int dynamic)
31 return (const char *)ap_pstrcat(p,
32 #ifdef WIN32
33 (dynamic ? fcgi_dynamic_dir : fcgi_socket_dir), file, NULL);
34 #else
35 (dynamic ? fcgi_dynamic_dir : fcgi_socket_dir), "/", file, NULL);
36 #endif
39 /*******************************************************************************
40 * Allocate a new string from pool p with the name of a Unix/Domain socket's
41 * lock file (used by dynamic only).
43 const char *
44 fcgi_util_socket_get_lock_filename(pool *p, const char *socket_path)
46 return ap_pstrcat(p, socket_path, ".lock", NULL);
49 #ifndef WIN32
50 /*******************************************************************************
51 * Build a Domain Socket Address structure, and calculate its size.
52 * The error message is allocated from the pool p. If you don't want the
53 * struct sockaddr_un also allocated from p, pass it preallocated (!=NULL).
55 const char *
56 fcgi_util_socket_make_domain_addr(pool *p, struct sockaddr_un **socket_addr,
57 int *socket_addr_len, const char *socket_path)
59 int socket_pathLen = strlen(socket_path);
61 if (socket_pathLen >= sizeof((*socket_addr)->sun_path)) {
62 return ap_pstrcat(p, "path \"", socket_path,
63 "\" is too long for a Domain socket", NULL);
66 if (*socket_addr == NULL)
67 *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_un));
68 else
69 memset(*socket_addr, 0, sizeof(struct sockaddr_un));
71 (*socket_addr)->sun_family = AF_UNIX;
72 strcpy((*socket_addr)->sun_path, socket_path);
74 *socket_addr_len = SUN_LEN(*socket_addr);
75 return NULL;
77 #endif
79 /*******************************************************************************
80 * Convert a hostname or IP address string to an in_addr struct.
82 static int
83 convert_string_to_in_addr(const char * const hostname, struct in_addr * const addr)
85 struct hostent *hp;
86 int count;
88 addr->s_addr = inet_addr((char *)hostname);
90 if (addr->s_addr == INADDR_NONE) {
91 if ((hp = gethostbyname((char *)hostname)) == NULL)
92 return -1;
94 memcpy((char *) addr, hp->h_addr, hp->h_length);
95 count = 0;
96 while (hp->h_addr_list[count] != 0)
97 count++;
99 return count;
101 return 1;
105 /*******************************************************************************
106 * Build an Inet Socket Address structure, and calculate its size.
107 * The error message is allocated from the pool p. If you don't want the
108 * struct sockaddr_in also allocated from p, pass it preallocated (!=NULL).
110 const char *
111 fcgi_util_socket_make_inet_addr(pool *p, struct sockaddr_in **socket_addr,
112 int *socket_addr_len, const char *host, unsigned short port)
114 if (*socket_addr == NULL)
115 *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_in));
116 else
117 memset(*socket_addr, 0, sizeof(struct sockaddr_in));
119 (*socket_addr)->sin_family = AF_INET;
120 (*socket_addr)->sin_port = htons(port);
122 /* Get an in_addr represention of the host */
123 if (host != NULL) {
124 if (convert_string_to_in_addr(host, &(*socket_addr)->sin_addr) != 1) {
125 return ap_pstrcat(p, "failed to resolve \"", host,
126 "\" to exactly one IP address", NULL);
128 } else {
129 (*socket_addr)->sin_addr.s_addr = htonl(INADDR_ANY);
132 *socket_addr_len = sizeof(struct sockaddr_in);
133 return NULL;
136 /*******************************************************************************
137 * Determine if a process with uid/gid can access a file with mode permissions.
139 const char *
140 fcgi_util_check_access(pool *tp,
141 const char * const path, const struct stat *statBuf,
142 const int mode, const uid_t uid, const gid_t gid)
144 if (statBuf == NULL) {
145 static struct stat staticStatBuf;
147 if (stat(path, &staticStatBuf) < 0)
148 return ap_psprintf(tp, "stat() failed: %s", strerror(errno));
149 statBuf = &staticStatBuf;
152 #ifndef WIN32
153 /* If the uid owns the file, check the owner bits */
154 if (uid == statBuf->st_uid) {
155 if (mode & R_OK && !(statBuf->st_mode & S_IRUSR))
156 return "read not allowed by owner";
157 if (mode & W_OK && !(statBuf->st_mode & S_IWUSR))
158 return "write not allowed by owner";
159 if (mode & X_OK && !(statBuf->st_mode & S_IXUSR))
160 return "execute not allowed by owner";
161 return NULL;
163 #else
164 if (mode & _S_IREAD && !(statBuf->st_mode & _S_IREAD))
165 return "read not allowed";
166 if (mode & _S_IWRITE && !(statBuf->st_mode & _S_IWRITE))
167 return "write not allowed";
169 // I don't think this works on FAT, but since I don't know how to check..
170 // if (mode & _S_IEXEC && !(statBuf->st_mode & _S_IEXEC))
171 // return "execute not allowed";
172 #endif
174 #if !defined(__EMX__) && !defined(WIN32)
175 /* If the gid is same as the file's group, check the group bits */
176 if (gid == statBuf->st_gid) {
177 if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
178 return "read not allowed by group";
179 if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
180 return "write not allowed by group";
181 if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
182 return "execute not allowed by group";
183 return NULL;
186 /* Get the user membership for the file's group. If the
187 * uid is a member, check the group bits. */
189 const struct group * const gr = getgrgid(statBuf->st_gid);
190 const struct passwd * const pw = getpwuid(uid);
192 if (gr != NULL && pw != NULL) {
193 char **user = gr->gr_mem;
194 for ( ; *user != NULL; user++) {
195 if (strcmp(*user, pw->pw_name) == 0) {
196 if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
197 return "read not allowed by group";
198 if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
199 return "write not allowed by group";
200 if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
201 return "execute not allowed by group";
202 return NULL;
208 /* That just leaves the other bits.. */
209 if (mode & R_OK && !(statBuf->st_mode & S_IROTH))
210 return "read not allowed";
211 if (mode & W_OK && !(statBuf->st_mode & S_IWOTH))
212 return "write not allowed";
213 if (mode & X_OK && !(statBuf->st_mode & S_IXOTH))
214 return "execute not allowed";
215 #endif
217 return NULL;
221 /*******************************************************************************
222 * Find a FastCGI server with a matching fs_path, and if fcgi_suexec is
223 * enabled with matching uid and gid.
225 fcgi_server *
226 fcgi_util_fs_get_by_id(const char *ePath, uid_t uid, gid_t gid)
228 char path[FCGI_MAXPATH];
229 fcgi_server *s;
231 /* @@@ This should now be done in the loop below */
232 ap_cpystrn(path, ePath, FCGI_MAXPATH);
233 ap_no2slash(path);
235 for (s = fcgi_servers; s != NULL; s = s->next) {
236 int i;
237 const char *fs_path = s->fs_path;
238 for (i = 0; fs_path[i] && path[i]; ++i) {
239 if (fs_path[i] != path[i]) {
240 break;
243 if (fs_path[i]) {
244 continue;
246 if (path[i] == '\0' || path[i] == '/') {
247 if (fcgi_suexec == NULL || (uid == s->uid && gid == s->gid))
248 return s;
251 return NULL;
254 /*******************************************************************************
255 * Find a FastCGI server with a matching fs_path, and if fcgi_suexec is
256 * enabled with matching user and group.
258 fcgi_server *
259 fcgi_util_fs_get(const char *ePath, const char *user, const char *group)
261 char path[FCGI_MAXPATH];
262 fcgi_server *s;
264 ap_cpystrn(path, ePath, FCGI_MAXPATH);
265 ap_no2slash(path);
267 for (s = fcgi_servers; s != NULL; s = s->next) {
268 if (strcmp(s->fs_path, path) == 0) {
269 if (fcgi_suexec == NULL)
270 return s;
272 if (strcmp(user, s->user) == 0
273 && (user[0] == '~' || strcmp(group, s->group) == 0))
275 return s;
279 return NULL;
282 const char *
283 fcgi_util_fs_is_path_ok(pool * const p, const char * const fs_path,
284 struct stat *finfo, const uid_t uid, const gid_t gid)
286 const char *err;
288 if (finfo == NULL) {
289 finfo = (struct stat *)ap_palloc(p, sizeof(struct stat));
290 if (stat(fs_path, finfo) < 0)
291 return ap_psprintf(p, "stat() failed: %s", strerror(errno));
294 /* No Parse Header scripts aren't allowed.
295 * @@@ Well... we really could quite easily */
296 if (strncmp(strrchr(fs_path, '/'), "/nph-", 5) == 0)
297 return ap_psprintf(p, "NPH scripts cannot be run as FastCGI");
299 if (finfo->st_mode == 0)
300 return ap_psprintf(p, "script not found or unable to stat()");
302 if (S_ISDIR(finfo->st_mode))
303 return ap_psprintf(p, "script is a directory!");
305 if (fcgi_suexec != NULL) {
306 #ifndef WIN32
307 err = fcgi_util_check_access(p, fs_path, finfo, X_OK, uid, gid);
308 if (err) {
309 return ap_psprintf(p,
310 "access for fcgi_suexec (uid %ld, gid %ld) not allowed: %s",
311 (long)uid, (long)gid, err);
313 #endif
315 else {
316 #ifdef WIN32
317 err = fcgi_util_check_access(p, fs_path, finfo, _S_IEXEC, fcgi_user_id, fcgi_group_id);
318 #else
319 err = fcgi_util_check_access(p, fs_path, finfo, X_OK, fcgi_user_id, fcgi_group_id);
320 #endif
321 if (err) {
322 return ap_psprintf(p,
323 "access for server (uid %ld, gid %ld) not allowed: %s",
324 (long)fcgi_user_id, (long)fcgi_group_id, err);
328 return NULL;
333 /*******************************************************************************
334 * Allocate a new FastCGI server record from pool p with default values.
336 fcgi_server *
337 fcgi_util_fs_new(pool *p)
339 fcgi_server *s = (fcgi_server *) ap_pcalloc(p, sizeof(fcgi_server));
341 /* Initialize anything who's init state is not zeroizzzzed */
342 s->listenQueueDepth = FCGI_DEFAULT_LISTEN_Q;
343 s->appConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT;
344 s->idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT;
345 s->initStartDelay = DEFAULT_INIT_START_DELAY;
346 s->restartDelay = FCGI_DEFAULT_RESTART_DELAY;
347 s->restartOnExit = FALSE;
348 s->directive = APP_CLASS_UNKNOWN;
349 s->processPriority = FCGI_DEFAULT_PRIORITY;
350 #ifdef WIN32
351 s->listenFd = (int)INVALID_HANDLE_VALUE;
352 #else
353 s->listenFd = -2;
354 #endif
355 s->envp = &fcgi_empty_env;
357 return s;
360 /*******************************************************************************
361 * Add the server to the linked list of FastCGI servers.
363 void
364 fcgi_util_fs_add(fcgi_server *s)
366 s->next = fcgi_servers;
367 fcgi_servers = s;
370 /*******************************************************************************
371 * Configure uid, gid, user, group, username for suexec.
373 const char *
374 fcgi_util_fs_set_uid_n_gid(pool *p, fcgi_server *s, uid_t uid, gid_t gid)
376 #ifndef WIN32
377 struct passwd *pw;
378 struct group *gr;
379 #endif
381 if (fcgi_suexec == NULL)
382 return NULL;
384 #ifndef WIN32
385 s->uid = uid;
386 pw = getpwuid(uid);
387 if (pw == NULL) {
388 return ap_psprintf(p,
389 "getpwuid() couldn't determine the username for uid '%ld', "
390 "you probably need to modify the User directive: %s",
391 (long)uid, strerror(errno));
393 s->user = ap_pstrdup(p, pw->pw_name);
394 s->username = s->user;
396 s->gid = gid;
397 gr = getgrgid(gid);
398 if (gr == NULL) {
399 return ap_psprintf(p,
400 "getgrgid() couldn't determine the group name for gid '%ld', "
401 "you probably need to modify the Group directive: %s",
402 (long)gid, strerror(errno));
404 s->group = ap_pstrdup(p, gr->gr_name);
405 #endif
406 return NULL;
409 /*******************************************************************************
410 * Allocate an array of ServerProcess records.
412 ServerProcess *
413 fcgi_util_fs_create_procs(pool *p, int num)
415 int i;
416 ServerProcess *proc = (ServerProcess *)ap_pcalloc(p, sizeof(ServerProcess) * num);
418 for (i = 0; i < num; i++) {
419 #ifdef WIN32
420 proc[i].pid = (HANDLE) 0;
421 #else
422 proc[i].pid = 0;
423 #endif
424 proc[i].state = STATE_READY;
426 return proc;
430 *----------------------------------------------------------------------
432 * fcgi_util_lock_fd
434 * Provide file locking via fcntl(2) function call. This
435 * code has been borrowed from Stevens "Advanced Unix
436 * Programming", page 370.
438 * Inputs:
439 * File descriptor to be locked, offsets within the file.
441 * Results:
442 * 0 on successful locking, -1 otherwise
444 * Side effects:
445 * File pointed to by file descriptor is locked or unlocked.
446 * Provided macros allow for both blocking and non-blocking
447 * behavior.
449 *----------------------------------------------------------------------
452 #ifndef WIN32
453 int
454 fcgi_util_lock_fd(int fd, int cmd, int type, off_t offset, int whence, off_t len)
456 int res = 0;
457 struct flock lock;
459 lock.l_type = type; /* F_RDLCK, F_WRLCK, F_UNLCK */
460 lock.l_start = offset; /* byte offset, relative to whence */
461 lock.l_whence = whence; /* SEEK_SET, SEET_CUR, SEEK_END */
462 lock.l_len = len; /* # of bytes, (0 indicates to EOF) */
464 /* Don't be fooled into thinking we've set a lock when we've
465 merely caught a signal. */
467 /* This is OK only if there is a hard_timeout() in effect! */
468 while ((res = fcntl(fd, cmd, &lock)) == -1 && errno == EINTR);
469 return res;
471 #endif
473 int fcgi_util_gettimeofday(struct timeval *Time) {
474 #ifdef WIN32
475 DWORD clock;
476 time_t t;
478 clock = GetTickCount();
480 t = time(NULL);
482 Time->tv_sec = t; //clock / 1000;
483 Time->tv_usec = (clock - Time->tv_sec) * 1000;
485 if (Time->tv_sec == (time_t)-1)
486 return -1;
487 else
488 return 0;
489 #else
490 return gettimeofday(Time, NULL);
491 #endif
494 #ifdef WIN32
496 FcgiRWLock * fcgi_rdwr_create() {
497 FcgiRWLock *newlock = NULL;
499 newlock = (FcgiRWLock *) malloc(sizeof(FcgiRWLock));
501 if (newlock == NULL)
502 return NULL;
504 newlock->read_event = CreateEvent(NULL, TRUE, FALSE, NULL);
505 newlock->lock_mutex = CreateEvent(NULL, FALSE, TRUE, NULL);
506 newlock->write_event = CreateMutex(NULL, FALSE, NULL);
507 newlock->counter = -1;
509 return newlock;
512 void fcgi_rdwr_destroy(FcgiRWLock *lock) {
513 CloseHandle(lock->read_event);
514 CloseHandle(lock->lock_mutex);
515 CloseHandle(lock->write_event);
517 free(lock);
518 lock = NULL;
522 int fcgi_rdwr_lock(FcgiRWLock *lock, int type) {
524 if (lock == NULL)
525 return -1;
527 if (type == WRITER) {
528 WaitForSingleObject(lock->write_event,INFINITE);
529 WaitForSingleObject(lock->lock_mutex, INFINITE);
531 else {
532 if (InterlockedIncrement(&lock->counter) == 0) {
533 WaitForSingleObject(lock->lock_mutex, INFINITE);
534 SetEvent(lock->read_event);
537 WaitForSingleObject(lock->read_event,INFINITE);
540 return 0;
543 int fcgi_rdwr_try_lock(FcgiRWLock *lock, int type) {
544 DWORD dwret;
546 if (lock == NULL)
547 return -1;
549 if (type == WRITER) {
550 dwret = WaitForSingleObject(lock->write_event, 0);
551 if (dwret == WAIT_TIMEOUT)
552 return -1;
554 dwret = WaitForSingleObject(lock->lock_mutex, 0);
555 if (dwret == WAIT_TIMEOUT)
556 return -1;
558 else {
559 if (InterlockedIncrement(&lock->counter) == 0) {
560 dwret = WaitForSingleObject(lock->lock_mutex, 0);
561 if (dwret == WAIT_TIMEOUT)
562 return -1;
564 SetEvent(lock->read_event);
567 dwret = WaitForSingleObject(lock->read_event, 0);
568 if (dwret == WAIT_TIMEOUT)
569 return -1;
572 return 0;
575 int fcgi_rdwr_unlock(FcgiRWLock *lock, int type) {
577 if (type == WRITER) {
578 SetEvent(lock->lock_mutex);
579 ReleaseMutex(lock->write_event);
581 else {
582 if (InterlockedDecrement(&lock->counter) < 0) {
583 ResetEvent(lock->read_event);
584 SetEvent(lock->lock_mutex);
588 return 0;
590 #endif