Call ap_reset_timeout() periodically. Don't use the wrapper if the User/Group is...
[mod_fastcgi.git] / fcgi_util.c
blob0e3ff47e436672b236ccca1498e61217f199e278
1 /*
2 * $Id: fcgi_util.c,v 1.15 2000/09/19 16:26:52 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_wrapper 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_wrapper == 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_wrapper 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_wrapper == 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 a wrapper is in use, let the wrapper determine what it can
289 * and can't execute */
290 if (fcgi_wrapper)
291 return NULL;
293 if (finfo == NULL) {
294 finfo = (struct stat *)ap_palloc(p, sizeof(struct stat));
295 if (stat(fs_path, finfo) < 0)
296 return ap_psprintf(p, "stat() failed: %s", strerror(errno));
299 /* No Parse Header scripts aren't allowed.
300 * @@@ Well... we really could quite easily */
301 if (strncmp(strrchr(fs_path, '/'), "/nph-", 5) == 0)
302 return ap_psprintf(p, "NPH scripts cannot be run as FastCGI");
304 if (finfo->st_mode == 0)
305 return ap_psprintf(p, "script not found or unable to stat()");
307 if (S_ISDIR(finfo->st_mode))
308 return ap_psprintf(p, "script is a directory!");
310 #ifdef WIN32
311 err = fcgi_util_check_access(p, fs_path, finfo, _S_IEXEC, fcgi_user_id, fcgi_group_id);
312 #else
313 err = fcgi_util_check_access(p, fs_path, finfo, X_OK, fcgi_user_id, fcgi_group_id);
314 #endif
315 if (err) {
316 return ap_psprintf(p,
317 "access for server (uid %ld, gid %ld) not allowed: %s",
318 (long)fcgi_user_id, (long)fcgi_group_id, err);
321 return NULL;
326 /*******************************************************************************
327 * Allocate a new FastCGI server record from pool p with default values.
329 fcgi_server *
330 fcgi_util_fs_new(pool *p)
332 fcgi_server *s = (fcgi_server *) ap_pcalloc(p, sizeof(fcgi_server));
334 /* Initialize anything who's init state is not zeroizzzzed */
335 s->listenQueueDepth = FCGI_DEFAULT_LISTEN_Q;
336 s->appConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT;
337 s->idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT;
338 s->initStartDelay = DEFAULT_INIT_START_DELAY;
339 s->restartDelay = FCGI_DEFAULT_RESTART_DELAY;
340 s->restartOnExit = FALSE;
341 s->directive = APP_CLASS_UNKNOWN;
342 s->processPriority = FCGI_DEFAULT_PRIORITY;
343 #ifdef WIN32
344 s->listenFd = (int)INVALID_HANDLE_VALUE;
345 #else
346 s->listenFd = -2;
347 #endif
348 s->envp = &fcgi_empty_env;
350 return s;
353 /*******************************************************************************
354 * Add the server to the linked list of FastCGI servers.
356 void
357 fcgi_util_fs_add(fcgi_server *s)
359 s->next = fcgi_servers;
360 fcgi_servers = s;
363 /*******************************************************************************
364 * Configure uid, gid, user, group, username for wrapper.
366 const char *
367 fcgi_util_fs_set_uid_n_gid(pool *p, fcgi_server *s, uid_t uid, gid_t gid)
369 #ifndef WIN32
370 struct passwd *pw;
371 struct group *gr;
372 #endif
374 if (fcgi_wrapper == NULL)
375 return NULL;
377 #ifndef WIN32
378 s->uid = uid;
379 pw = getpwuid(uid);
380 if (pw == NULL) {
381 return ap_psprintf(p,
382 "getpwuid() couldn't determine the username for uid '%ld', "
383 "you probably need to modify the User directive: %s",
384 (long)uid, strerror(errno));
386 s->user = ap_pstrdup(p, pw->pw_name);
387 s->username = s->user;
389 s->gid = gid;
390 gr = getgrgid(gid);
391 if (gr == NULL) {
392 return ap_psprintf(p,
393 "getgrgid() couldn't determine the group name for gid '%ld', "
394 "you probably need to modify the Group directive: %s",
395 (long)gid, strerror(errno));
397 s->group = ap_pstrdup(p, gr->gr_name);
398 #endif
399 return NULL;
402 /*******************************************************************************
403 * Allocate an array of ServerProcess records.
405 ServerProcess *
406 fcgi_util_fs_create_procs(pool *p, int num)
408 int i;
409 ServerProcess *proc = (ServerProcess *)ap_pcalloc(p, sizeof(ServerProcess) * num);
411 for (i = 0; i < num; i++) {
412 #ifdef WIN32
413 proc[i].handle = INVALID_HANDLE_VALUE;
414 #endif
415 proc[i].pid = 0;
416 proc[i].state = STATE_READY;
418 return proc;
422 *----------------------------------------------------------------------
424 * fcgi_util_lock_fd
426 * Provide file locking via fcntl(2) function call. This
427 * code has been borrowed from Stevens "Advanced Unix
428 * Programming", page 370.
430 * Inputs:
431 * File descriptor to be locked, offsets within the file.
433 * Results:
434 * 0 on successful locking, -1 otherwise
436 * Side effects:
437 * File pointed to by file descriptor is locked or unlocked.
438 * Provided macros allow for both blocking and non-blocking
439 * behavior.
441 *----------------------------------------------------------------------
444 #ifndef WIN32
445 int
446 fcgi_util_lock_fd(int fd, int cmd, int type, off_t offset, int whence, off_t len)
448 int res = 0;
449 struct flock lock;
451 lock.l_type = type; /* F_RDLCK, F_WRLCK, F_UNLCK */
452 lock.l_start = offset; /* byte offset, relative to whence */
453 lock.l_whence = whence; /* SEEK_SET, SEET_CUR, SEEK_END */
454 lock.l_len = len; /* # of bytes, (0 indicates to EOF) */
456 /* Don't be fooled into thinking we've set a lock when we've
457 merely caught a signal. */
459 /* This is OK only if there is a hard_timeout() in effect! */
460 while ((res = fcntl(fd, cmd, &lock)) == -1 && errno == EINTR);
461 return res;
463 #endif
465 int fcgi_util_gettimeofday(struct timeval *Time) {
466 #ifdef WIN32
467 DWORD clock;
468 time_t t;
470 clock = GetTickCount();
472 t = time(NULL);
474 Time->tv_sec = t; //clock / 1000;
475 Time->tv_usec = (clock - Time->tv_sec) * 1000;
477 if (Time->tv_sec == (time_t)-1)
478 return -1;
479 else
480 return 0;
481 #else
482 return gettimeofday(Time, NULL);
483 #endif
486 #ifdef WIN32
489 * We allow only one writer at a time and a writer can proceed only
490 * when there are no readers.
492 * @@@ For named pipes we could lock only the process we're going to waste.
495 FcgiRWLock * fcgi_rdwr_create()
497 FcgiRWLock *newlock = NULL;
499 newlock = (FcgiRWLock *) malloc(sizeof(FcgiRWLock));
500 ap_assert(newlock);
502 newlock->write_lock = CreateEvent(NULL, FALSE, TRUE, NULL);
503 newlock->mutex = CreateMutex(NULL, FALSE, NULL);
504 newlock->counter = 0;
506 return newlock;
509 void fcgi_rdwr_destroy(FcgiRWLock *lock)
511 CloseHandle(lock->write_lock);
512 CloseHandle(lock->mutex);
513 free(lock);
516 int fcgi_rdwr_lock(FcgiRWLock *lock, int type)
518 if (type == WRITER)
520 WaitForSingleObject(lock->write_lock, INFINITE);
522 else
524 WaitForSingleObject(lock->mutex, INFINITE);
525 if (lock->counter == 0)
527 WaitForSingleObject(lock->write_lock, INFINITE);
529 ++lock->counter;
530 ReleaseMutex(lock->mutex);
533 return 0;
536 int fcgi_rdwr_unlock(FcgiRWLock *lock, int type)
538 if (type == WRITER)
540 SetEvent(lock->write_lock);
542 else
544 WaitForSingleObject(lock->mutex, INFINITE);
545 --lock->counter;
546 if (lock->counter == 0)
548 SetEvent(lock->write_lock);
550 ReleaseMutex(lock->mutex);
553 return 0;
556 int fcgi_rdwr_try_lock(FcgiRWLock *lock, int type)
558 DWORD rc;
560 if (type == WRITER)
562 rc = WaitForSingleObject(lock->write_lock, 0);
563 if (rc == WAIT_TIMEOUT || rc == WAIT_FAILED)
565 return -1;
568 else
570 ap_assert(0);
573 return 0;
576 #endif