Beta WinNT support. David Allen [djallen@raleigh.ibm.com]
[mod_fastcgi.git] / fcgi_util.c
blobd2140c40708b0882491d9b6749a1a1ba727f4e9e
1 /*
2 * $Id: fcgi_util.c,v 1.9 2000/04/27 02:27:35 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";
168 if (mode & _S_IEXEC && !(statBuf->st_mode & _S_IEXEC))
169 return "execute not allowed";
170 #endif
172 #if !defined(__EMX__) && !defined(WIN32)
173 /* If the gid is same as the file's group, check the group bits */
174 if (gid == statBuf->st_gid) {
175 if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
176 return "read not allowed by group";
177 if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
178 return "write not allowed by group";
179 if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
180 return "execute not allowed by group";
181 return NULL;
184 /* Get the user membership for the file's group. If the
185 * uid is a member, check the group bits. */
187 const struct group * const gr = getgrgid(statBuf->st_gid);
188 const struct passwd * const pw = getpwuid(uid);
190 if (gr != NULL && pw != NULL) {
191 char **user = gr->gr_mem;
192 for ( ; *user != NULL; user++) {
193 if (strcmp(*user, pw->pw_name) == 0) {
194 if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
195 return "read not allowed by group";
196 if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
197 return "write not allowed by group";
198 if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
199 return "execute not allowed by group";
200 return NULL;
206 /* That just leaves the other bits.. */
207 if (mode & R_OK && !(statBuf->st_mode & S_IROTH))
208 return "read not allowed";
209 if (mode & W_OK && !(statBuf->st_mode & S_IWOTH))
210 return "write not allowed";
211 if (mode & X_OK && !(statBuf->st_mode & S_IXOTH))
212 return "execute not allowed";
213 #endif
215 return NULL;
219 /*******************************************************************************
220 * Find a FastCGI server with a matching fs_path, and if fcgi_suexec is
221 * enabled with matching uid and gid.
223 fcgi_server *
224 fcgi_util_fs_get_by_id(const char *ePath, uid_t uid, gid_t gid)
226 char path[FCGI_MAXPATH];
227 fcgi_server *s;
229 /* @@@ This should now be done in the loop below */
230 ap_cpystrn(path, ePath, FCGI_MAXPATH);
231 ap_no2slash(path);
233 for (s = fcgi_servers; s != NULL; s = s->next) {
234 int i;
235 const char *fs_path = s->fs_path;
236 for (i = 0; fs_path[i] && path[i]; ++i) {
237 if (fs_path[i] != path[i]) {
238 break;
241 if (fs_path[i]) {
242 continue;
244 if (path[i] == '\0' || path[i] == '/') {
245 if (fcgi_suexec == NULL || (uid == s->uid && gid == s->gid))
246 return s;
249 return NULL;
252 /*******************************************************************************
253 * Find a FastCGI server with a matching fs_path, and if fcgi_suexec is
254 * enabled with matching user and group.
256 fcgi_server *
257 fcgi_util_fs_get(const char *ePath, const char *user, const char *group)
259 char path[FCGI_MAXPATH];
260 fcgi_server *s;
262 ap_cpystrn(path, ePath, FCGI_MAXPATH);
263 ap_no2slash(path);
265 for (s = fcgi_servers; s != NULL; s = s->next) {
266 if (strcmp(s->fs_path, path) == 0) {
267 if (fcgi_suexec == NULL)
268 return s;
270 if (strcmp(user, s->user) == 0
271 && (user[0] == '~' || strcmp(group, s->group) == 0))
273 return s;
277 return NULL;
280 const char *
281 fcgi_util_fs_is_path_ok(pool * const p, const char * const fs_path,
282 struct stat *finfo, const uid_t uid, const gid_t gid)
284 const char *err;
286 if (finfo == NULL) {
287 finfo = (struct stat *)ap_palloc(p, sizeof(struct stat));
288 if (stat(fs_path, finfo) < 0)
289 return ap_psprintf(p, "stat() failed: %s", strerror(errno));
292 /* No Parse Header scripts aren't allowed.
293 * @@@ Well... we really could quite easily */
294 if (strncmp(strrchr(fs_path, '/'), "/nph-", 5) == 0)
295 return ap_psprintf(p, "NPH scripts cannot be run as FastCGI");
297 if (finfo->st_mode == 0)
298 return ap_psprintf(p, "script not found or unable to stat()");
300 if (S_ISDIR(finfo->st_mode))
301 return ap_psprintf(p, "script is a directory!");
303 if (fcgi_suexec != NULL) {
304 #ifndef WIN32
305 err = fcgi_util_check_access(p, fs_path, finfo, X_OK, uid, gid);
306 if (err) {
307 return ap_psprintf(p,
308 "access for fcgi_suexec (uid %ld, gid %ld) not allowed: %s",
309 (long)uid, (long)gid, err);
311 #endif
313 else {
314 #ifdef WIN32
315 err = fcgi_util_check_access(p, fs_path, finfo, _S_IEXEC, fcgi_user_id, fcgi_group_id);
316 #else
317 err = fcgi_util_check_access(p, fs_path, finfo, X_OK, fcgi_user_id, fcgi_group_id);
318 #endif
319 if (err) {
320 return ap_psprintf(p,
321 "access for server (uid %ld, gid %ld) not allowed: %s",
322 (long)fcgi_user_id, (long)fcgi_group_id, err);
326 return NULL;
331 /*******************************************************************************
332 * Allocate a new FastCGI server record from pool p with default values.
334 fcgi_server *
335 fcgi_util_fs_new(pool *p)
337 fcgi_server *s = (fcgi_server *) ap_pcalloc(p, sizeof(fcgi_server));
339 /* Initialize anything who's init state is not zeroizzzzed */
340 s->listenQueueDepth = FCGI_DEFAULT_LISTEN_Q;
341 s->appConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT;
342 s->idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT;
343 s->initStartDelay = DEFAULT_INIT_START_DELAY;
344 s->restartDelay = FCGI_DEFAULT_RESTART_DELAY;
345 s->restartOnExit = FALSE;
346 s->directive = APP_CLASS_UNKNOWN;
347 s->processPriority = FCGI_DEFAULT_PRIORITY;
348 #ifdef WIN32
349 s->listenFd = (int)INVALID_HANDLE_VALUE;
350 #else
351 s->listenFd = -2;
352 #endif
353 s->envp = &fcgi_empty_env;
355 return s;
358 /*******************************************************************************
359 * Add the server to the linked list of FastCGI servers.
361 void
362 fcgi_util_fs_add(fcgi_server *s)
364 s->next = fcgi_servers;
365 fcgi_servers = s;
368 /*******************************************************************************
369 * Configure uid, gid, user, group, username for suexec.
371 const char *
372 fcgi_util_fs_set_uid_n_gid(pool *p, fcgi_server *s, uid_t uid, gid_t gid)
374 #ifndef WIN32
375 struct passwd *pw;
376 struct group *gr;
377 #endif
379 if (fcgi_suexec == NULL)
380 return NULL;
382 #ifndef WIN32
383 s->uid = uid;
384 pw = getpwuid(uid);
385 if (pw == NULL) {
386 return ap_psprintf(p,
387 "getpwuid() couldn't determine the username for uid '%ld', "
388 "you probably need to modify the User directive: %s",
389 (long)uid, strerror(errno));
391 s->user = ap_pstrdup(p, pw->pw_name);
392 s->username = s->user;
394 s->gid = gid;
395 gr = getgrgid(gid);
396 if (gr == NULL) {
397 return ap_psprintf(p,
398 "getgrgid() couldn't determine the group name for gid '%ld', "
399 "you probably need to modify the Group directive: %s",
400 (long)gid, strerror(errno));
402 s->group = ap_pstrdup(p, gr->gr_name);
403 #endif
404 return NULL;
407 /*******************************************************************************
408 * Allocate an array of ServerProcess records.
410 ServerProcess *
411 fcgi_util_fs_create_procs(pool *p, int num)
413 int i;
414 ServerProcess *proc = (ServerProcess *)ap_pcalloc(p, sizeof(ServerProcess) * num);
416 for (i = 0; i < num; i++) {
417 #ifdef WIN32
418 proc[i].pid = (HANDLE) 0;
419 #else
420 proc[i].pid = 0;
421 #endif
422 proc[i].state = STATE_READY;
424 return proc;
428 *----------------------------------------------------------------------
430 * fcgi_util_lock_fd
432 * Provide file locking via fcntl(2) function call. This
433 * code has been borrowed from Stevens "Advanced Unix
434 * Programming", page 370.
436 * Inputs:
437 * File descriptor to be locked, offsets within the file.
439 * Results:
440 * 0 on successful locking, -1 otherwise
442 * Side effects:
443 * File pointed to by file descriptor is locked or unlocked.
444 * Provided macros allow for both blocking and non-blocking
445 * behavior.
447 *----------------------------------------------------------------------
450 int
451 fcgi_util_lock_fd(int fd, int cmd, int type, off_t offset, int whence, off_t len)
453 int res = 0;
454 #ifndef WIN32
455 struct flock lock;
457 lock.l_type = type; /* F_RDLCK, F_WRLCK, F_UNLCK */
458 lock.l_start = offset; /* byte offset, relative to whence */
459 lock.l_whence = whence; /* SEEK_SET, SEET_CUR, SEEK_END */
460 lock.l_len = len; /* # of bytes, (0 indicates to EOF) */
462 /* Don't be fooled into thinking we've set a lock when we've
463 merely caught a signal. */
465 /* This is OK only if there is a hard_timeout() in effect! */
466 while ((res = fcntl(fd, cmd, &lock)) == -1 && errno == EINTR);
467 #endif
468 return res;
471 int fcgi_util_gettimeofday(struct timeval *Time) {
472 #ifdef WIN32
473 DWORD clock;
474 time_t t;
476 clock = GetTickCount();
479 t = time(NULL);
481 Time->tv_sec = t; //clock / 1000;
482 Time->tv_usec = (clock - Time->tv_sec) * 1000;
484 if (Time->tv_sec == (time_t)-1)
485 return -1;
486 else
487 return 0;
488 #else
489 return gettimeofday(Time, NULL);
490 #endif
493 #ifdef WIN32
495 FcgiRWLock * fcgi_rdwr_create() {
496 FcgiRWLock *newlock = NULL;
498 newlock = (FcgiRWLock *) malloc(sizeof(FcgiRWLock));
500 if (newlock == NULL)
501 return NULL;
503 newlock->read_event = CreateEvent(NULL, TRUE, FALSE, NULL);
504 newlock->lock_mutex = CreateEvent(NULL, FALSE, TRUE, NULL);
505 newlock->write_event = CreateMutex(NULL, FALSE, NULL);
506 newlock->counter = -1;
508 return newlock;
511 void fcgi_rdwr_destory(FcgiRWLock *lock) {
512 CloseHandle(lock->read_event);
513 CloseHandle(lock->lock_mutex);
514 CloseHandle(lock->write_event);
516 free(lock);
517 lock = NULL;
521 int fcgi_rdwr_lock(FcgiRWLock *lock, int type) {
523 if (lock == NULL)
524 return -1;
526 if (type == WRITER) {
527 WaitForSingleObject(lock->write_event,INFINITE);
528 WaitForSingleObject(lock->lock_mutex, INFINITE);
530 else {
531 if (InterlockedIncrement(&lock->counter) == 0) {
532 WaitForSingleObject(lock->lock_mutex, INFINITE);
533 SetEvent(lock->read_event);
536 WaitForSingleObject(lock->read_event,INFINITE);
539 return 0;
542 int fcgi_rdwr_try_lock(FcgiRWLock *lock, int type) {
543 DWORD dwret;
545 if (lock == NULL)
546 return -1;
548 if (type == WRITER) {
549 dwret = WaitForSingleObject(lock->write_event, 0);
550 if (dwret == WAIT_TIMEOUT)
551 return -1;
553 dwret = WaitForSingleObject(lock->lock_mutex, 0);
554 if (dwret == WAIT_TIMEOUT)
555 return -1;
557 else {
558 if (InterlockedIncrement(&lock->counter) == 0) {
559 dwret = WaitForSingleObject(lock->lock_mutex, 0);
560 if (dwret == WAIT_TIMEOUT)
561 return -1;
563 SetEvent(lock->read_event);
566 dwret = WaitForSingleObject(lock->read_event, 0);
567 if (dwret == WAIT_TIMEOUT)
568 return -1;
571 return 0;
574 int fcgi_rdwr_unlock(FcgiRWLock *lock, int type) {
576 if (type == WRITER) {
577 SetEvent(lock->lock_mutex);
578 ReleaseMutex(lock->write_event);
580 else {
581 if (InterlockedDecrement(&lock->counter) < 0) {
582 ResetEvent(lock->read_event);
583 SetEvent(lock->lock_mutex);
587 return 0;
589 #endif