2 * $Id: fcgi_util.c,v 1.9 2000/04/27 02:27:35 robs Exp $
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.
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 "//", ".", "..") */
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.
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
,
33 (dynamic
? fcgi_dynamic_dir
: fcgi_socket_dir
), file
, NULL
);
35 (dynamic
? fcgi_dynamic_dir
: fcgi_socket_dir
), "/", file
, NULL
);
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).
44 fcgi_util_socket_get_lock_filename(pool
*p
, const char *socket_path
)
46 return ap_pstrcat(p
, socket_path
, ".lock", NULL
);
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).
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
));
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
);
79 /*******************************************************************************
80 * Convert a hostname or IP address string to an in_addr struct.
83 convert_string_to_in_addr(const char * const hostname
, struct in_addr
* const addr
)
88 addr
->s_addr
= inet_addr((char *)hostname
);
90 if (addr
->s_addr
== INADDR_NONE
) {
91 if ((hp
= gethostbyname((char *)hostname
)) == NULL
)
94 memcpy((char *) addr
, hp
->h_addr
, hp
->h_length
);
96 while (hp
->h_addr_list
[count
] != 0)
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).
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
));
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 */
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
);
129 (*socket_addr
)->sin_addr
.s_addr
= htonl(INADDR_ANY
);
132 *socket_addr_len
= sizeof(struct sockaddr_in
);
136 /*******************************************************************************
137 * Determine if a process with uid/gid can access a file with mode permissions.
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
;
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";
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";
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";
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";
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";
219 /*******************************************************************************
220 * Find a FastCGI server with a matching fs_path, and if fcgi_suexec is
221 * enabled with matching uid and gid.
224 fcgi_util_fs_get_by_id(const char *ePath
, uid_t uid
, gid_t gid
)
226 char path
[FCGI_MAXPATH
];
229 /* @@@ This should now be done in the loop below */
230 ap_cpystrn(path
, ePath
, FCGI_MAXPATH
);
233 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
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
]) {
244 if (path
[i
] == '\0' || path
[i
] == '/') {
245 if (fcgi_suexec
== NULL
|| (uid
== s
->uid
&& gid
== s
->gid
))
252 /*******************************************************************************
253 * Find a FastCGI server with a matching fs_path, and if fcgi_suexec is
254 * enabled with matching user and group.
257 fcgi_util_fs_get(const char *ePath
, const char *user
, const char *group
)
259 char path
[FCGI_MAXPATH
];
262 ap_cpystrn(path
, ePath
, FCGI_MAXPATH
);
265 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
266 if (strcmp(s
->fs_path
, path
) == 0) {
267 if (fcgi_suexec
== NULL
)
270 if (strcmp(user
, s
->user
) == 0
271 && (user
[0] == '~' || strcmp(group
, s
->group
) == 0))
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
)
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
) {
305 err
= fcgi_util_check_access(p
, fs_path
, finfo
, X_OK
, uid
, gid
);
307 return ap_psprintf(p
,
308 "access for fcgi_suexec (uid %ld, gid %ld) not allowed: %s",
309 (long)uid
, (long)gid
, err
);
315 err
= fcgi_util_check_access(p
, fs_path
, finfo
, _S_IEXEC
, fcgi_user_id
, fcgi_group_id
);
317 err
= fcgi_util_check_access(p
, fs_path
, finfo
, X_OK
, fcgi_user_id
, fcgi_group_id
);
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
);
331 /*******************************************************************************
332 * Allocate a new FastCGI server record from pool p with default values.
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
;
349 s
->listenFd
= (int)INVALID_HANDLE_VALUE
;
353 s
->envp
= &fcgi_empty_env
;
358 /*******************************************************************************
359 * Add the server to the linked list of FastCGI servers.
362 fcgi_util_fs_add(fcgi_server
*s
)
364 s
->next
= fcgi_servers
;
368 /*******************************************************************************
369 * Configure uid, gid, user, group, username for suexec.
372 fcgi_util_fs_set_uid_n_gid(pool
*p
, fcgi_server
*s
, uid_t uid
, gid_t gid
)
379 if (fcgi_suexec
== 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
;
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
);
407 /*******************************************************************************
408 * Allocate an array of ServerProcess records.
411 fcgi_util_fs_create_procs(pool
*p
, int num
)
414 ServerProcess
*proc
= (ServerProcess
*)ap_pcalloc(p
, sizeof(ServerProcess
) * num
);
416 for (i
= 0; i
< num
; i
++) {
418 proc
[i
].pid
= (HANDLE
) 0;
422 proc
[i
].state
= STATE_READY
;
428 *----------------------------------------------------------------------
432 * Provide file locking via fcntl(2) function call. This
433 * code has been borrowed from Stevens "Advanced Unix
434 * Programming", page 370.
437 * File descriptor to be locked, offsets within the file.
440 * 0 on successful locking, -1 otherwise
443 * File pointed to by file descriptor is locked or unlocked.
444 * Provided macros allow for both blocking and non-blocking
447 *----------------------------------------------------------------------
451 fcgi_util_lock_fd(int fd
, int cmd
, int type
, off_t offset
, int whence
, off_t len
)
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
);
471 int fcgi_util_gettimeofday(struct timeval
*Time
) {
476 clock
= GetTickCount();
481 Time
->tv_sec
= t
; //clock / 1000;
482 Time
->tv_usec
= (clock
- Time
->tv_sec
) * 1000;
484 if (Time
->tv_sec
== (time_t)-1)
489 return gettimeofday(Time
, NULL
);
495 FcgiRWLock
* fcgi_rdwr_create() {
496 FcgiRWLock
*newlock
= NULL
;
498 newlock
= (FcgiRWLock
*) malloc(sizeof(FcgiRWLock
));
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;
511 void fcgi_rdwr_destory(FcgiRWLock
*lock
) {
512 CloseHandle(lock
->read_event
);
513 CloseHandle(lock
->lock_mutex
);
514 CloseHandle(lock
->write_event
);
521 int fcgi_rdwr_lock(FcgiRWLock
*lock
, int type
) {
526 if (type
== WRITER
) {
527 WaitForSingleObject(lock
->write_event
,INFINITE
);
528 WaitForSingleObject(lock
->lock_mutex
, INFINITE
);
531 if (InterlockedIncrement(&lock
->counter
) == 0) {
532 WaitForSingleObject(lock
->lock_mutex
, INFINITE
);
533 SetEvent(lock
->read_event
);
536 WaitForSingleObject(lock
->read_event
,INFINITE
);
542 int fcgi_rdwr_try_lock(FcgiRWLock
*lock
, int type
) {
548 if (type
== WRITER
) {
549 dwret
= WaitForSingleObject(lock
->write_event
, 0);
550 if (dwret
== WAIT_TIMEOUT
)
553 dwret
= WaitForSingleObject(lock
->lock_mutex
, 0);
554 if (dwret
== WAIT_TIMEOUT
)
558 if (InterlockedIncrement(&lock
->counter
) == 0) {
559 dwret
= WaitForSingleObject(lock
->lock_mutex
, 0);
560 if (dwret
== WAIT_TIMEOUT
)
563 SetEvent(lock
->read_event
);
566 dwret
= WaitForSingleObject(lock
->read_event
, 0);
567 if (dwret
== WAIT_TIMEOUT
)
574 int fcgi_rdwr_unlock(FcgiRWLock
*lock
, int type
) {
576 if (type
== WRITER
) {
577 SetEvent(lock
->lock_mutex
);
578 ReleaseMutex(lock
->write_event
);
581 if (InterlockedDecrement(&lock
->counter
) < 0) {
582 ResetEvent(lock
->read_event
);
583 SetEvent(lock
->lock_mutex
);