coar 99/04/28 12:20:29
Modified: apr/include apr_config.h.in apr_lib.h apr/lib Makefile.in Added: apr/include apr_pools.h hsregex.h apr/lib apr_pools.c Log: {Whew!} First pass at sanitising the pool stuff (pools, arrays, and tables) for APR. At least it compiles on Linux, but there's lot left to do: make sure it compiles on other platforms (esp. Windows), fix the #if'd out BUFF-referencing section, remove spurious prototypes from include/apr_pools.h since they're in include/apr_lib.h, and general cleanup and sanity-checking. Revision Changes Path 1.3 +167 -68 apache-apr/apr/include/apr_config.h.in Index: apr_config.h.in =================================================================== RCS file: /home/cvs/apache-apr/apr/include/apr_config.h.in,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- apr_config.h.in 1999/04/09 05:37:00 1.2 +++ apr_config.h.in 1999/04/28 19:20:01 1.3 @@ -1,5 +1,6 @@ /* - * Start.. + * Include all of the system header files appropriate for this platform, + * and make library-wide definitions. */ #ifndef APR_CONFIG_H #define APR_CONFIG_H @@ -10,7 +11,7 @@ */ #ifdef __cplusplus extern "C" { -#endif +#endif /* __cplusplus */ /* * List of header files which exist for this platform. @@ -71,7 +72,25 @@ */ #undef HAVE_MMAP -#define API_EXPORT(type) type +/* + * Known problems with system header files that we can fix. + */ +#ifdef ULTRIX +/* + * We don't want to include Ultrix's sys/resource.h, even if it's + * there. + */ +#undef HAVE_SYS_RESOURCE_H +/* + * Versions of Ultrix prior to 4.3 included a bundled cc with a broken + * const. Don't try to use it; in fact, make sure it's define'd away + * before any header files try to use it. + */ +#ifdef __ultrix__ +#define const +#endif /* __ultrix__ */ + +#endif /* ULTRIX */ /* * We now know what we have, so make use of it. @@ -79,186 +98,204 @@ #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> -#endif +#endif /* HAVE_SYS_TYPES_H */ #ifdef HAVE_CRYPT_H #include <crypt.h> -#endif +#endif /* HAVE_CRYPT_H */ #ifdef HAVE_CTYPE_H #include <ctype.h> -#endif +#endif /* HAVE_CTYPE_H */ #ifdef HAVE_DIR_H #include <dir.h> -#endif +#endif /* HAVE_DIR_H */ #ifdef HAVE_DIRENT_H #include <dirent.h> -#endif +#endif /* HAVE_DIRENT_H */ #ifdef HAVE_ERRNO_H #include <errno.h> -#endif +#endif /* HAVE_ERRNO_H *? #ifdef HAVE_NET_ERRNO_H #include <net/errno.h> -#endif +#endif /* HAVE_NET_ERRNO_H */ #ifdef HAVE_FCNTL_H #include <fcntl.h> -#endif +#endif /* HAVE_FCNTL_H */ #ifdef HAVE_GRP_H #include <grp.h> -#endif +#endif /* HAVE_GRP_H */ #ifdef HAVE_IO_H #include <io.h> -#endif +#endif /* HAVE_IO_H */ #ifdef HAVE_LIMITS_H #include <limits.h> -#endif +#endif /* HAVE_LIMITS_H */ #ifdef HAVE_MALLOC_H #include <malloc.h> -#endif +#endif /* HAVE_MALLOC_H */ #ifdef HAVE_MATH_H #include <math.h> -#endif +#endif /* HAVE_MATH_H */ #ifdef HAVE_MEMORY_H #include <memory.h> -#endif +#endif /* HAVE_MEMORY_H */ #ifdef HAVE_NETDB_H #include <netdb.h> -#endif +#endif /* HAVE_NETDB_H */ #ifdef HAVE_PROCESS_H #include <process.h> -#endif +#endif /* HAVE_PROCESS_H */ #ifdef HAVE_PWD_H #include <pwd.h> -#endif +#endif /* HAVE_PWD_H */ #ifdef HAVE_SETJMP_H #include <setjmp.h> -#endif +#endif /* HAVE_SETJMP_H */ #ifdef HAVE_SIGNAL_H #include <signal.h> -#endif +#endif /* HAVE_SIGNAL_H */ #ifdef HAVE_STDARG_H #include <stdarg.h> -#endif +#endif /* HAVE_STDARG_H */ #ifdef HAVE_STDDEF_H #include <stddef.h> -#endif +#endif /* HAVE_STDDEF_H */ #ifdef HAVE_STDIO_H #include <stdio.h> -#endif +#endif /* HAVE_STDIO_H */ #ifdef HAVE_STDLIB_H #include <stdlib.h> -#endif +#endif /* HAVE_STDLIB_H */ #ifdef HAVE_STRING_H #include <string.h> -#endif +#endif /* HAVE_STRING_H */ +/* + * Some sole-platform inclusions. + */ #ifdef TPF + #ifdef HAVE_TPFEQ_H #include <tpfeq.h> -#endif +#endif /* HAVE_TPFEQ_H */ + #ifdef HAVE_TPFIO_H #include <tpfio.h> -#endif +#endif /* HAVE_TPFIO_H */ + #ifdef HAVE_OSRELDATE_H #include <osreldate.h> -#endif +#endif /* HAVE_OSRELDATE_H */ + #ifdef HAVE_SYSAPI_H #include <sysapi.h> -#endif +#endif /* HAVE_SYSAPI_H */ + #ifdef HAVE_SYSGTIME_H #include <sysgtime.h> -#endif +#endif /* HAVE_SYSGTIME_H */ + #endif /* TPF */ #ifdef HAVE_TIME_H #include <time.h> -#endif +#endif /* HAVE_TIME_H */ #ifdef HAVE_SYS_TIME_H #include <sys/time.h> -#endif +#endif /* HAVE_SYS_TIME_H */ #ifdef HAVE_SYS_TIMES_H #include <sys/times.h> -#endif +#endif /* HAVE_SYS_TIMES_H */ #ifdef HAVE_UNISTD_H #include <unistd.h> -#endif +#endif /* HAVE_UNISTD_H */ #ifdef HAVE_UNIX_H #include <unix.h> -#endif +#endif /* HAVE_UNIX_H */ +/* + * Header files to include *except* for a particular platform. + */ #ifndef OS2 + #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> -#endif -#endif +#endif /* HAVE_ARPA_INET_H */ + +#endif /* OS2 */ #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> -#endif +#endif /* HAVE_NETINET_IN_H */ #ifdef HAVE_SYS_FILE_H #include <sys/file.h> -#endif +#endif /* HAVE_SYS_FILE_H */ #ifdef HAVE_SYS_IOCTL_H #include <sys/ioctl.h> -#endif +#endif /* HAVE_SYS_IOCTL_H */ #ifdef HAVE_SYS_MMAN_H #include <sys/mman.h> -#endif +#endif /* HAVE_SYS_MMAN_H */ #ifdef HAVE_SYS_PARAM_H #include <sys/param.h> -#endif +#endif /* HAVE SYS_PARAM_H */ #ifdef HAVE_SYS_RESOURCE_H #include <sys/resource.h> -#endif +#endif /* HAVE_SYS_RESOURCE_H */ #ifdef HAVE_SYS_SELECT_H #include <sys/select.h> -#endif +#endif /* HAVE_SYS_SELECT_H */ #ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> -#endif +#endif /* HAVE SYS_SOCKET_H */ #ifdef HAVE_SYS_STAT_H #include <sys/stat.h> -#endif +#endif /* HAVE_SYS_STAT_H */ -#ifdef HAVE__SYS_WAIT_H +#ifdef HAVE_SYS_WAIT_H #include <sys/wait.h> -#endif +#endif /* HAVE_SYS_WAIT_H */ /* + * End of include-file processing. + */ + +/* * Platform-specific definitions. */ #ifdef _OSD_POSIX @@ -269,50 +306,53 @@ #ifdef UTS21 #define NEED_HASHBANG_EMUL #define NO_USE_SIGACTION -#endif +#endif /* UTS1 */ #ifdef TPF #define NO_USE_SIGACTION #define NO_SLACK -#endif +#endif /* TPF */ #ifdef LYNXOS #define NO_USE_SIGACTION -#endif +#endif /* LYNXOS */ #ifdef __MACHTEN__ #define NO_USE_SIGACTION -#endif +#endif /* __MACHTEN__ */ #ifdef NEWOS #define NO_USE_SIGACTION -#endif +#endif /* NEWOS */ #ifdef RISCIX #define NO_USE_SIGACTION -#endif +#endif /* RISCIX */ #ifdef MPE #define NO_SLACK -#endif +#endif /* MPE */ #ifdef AUX3 #define NO_SLACK -#endif +#endif /* AUX3 */ #ifdef NEXT #define NO_USE_SIGACTION -#endif +#endif /* NEXT */ + /* * IF we're on a Linux platform, AND the kernel is 2.0 or later, AND * we have the features.h header file available, include it. */ #ifdef LINUX + #if (LINUX >= 20) && defined(HAVE_FEATURES_H) #include <features.h> -#endif -#endif +#endif /* (LINUX >= 20) && defined(HAVE_FEATURES_H) */ +#endif /* LINUX */ + /* * Now things that are dependent upon our derived platform settings. */ @@ -325,11 +365,11 @@ */ #define execle apr_execle #define execve(path,argv,envp) apr_execve(path,argv,envp) -#endif +#endif /* NEED_HASHBANG_MULTI */ #ifdef CHARSET_EBCDIC #include "ebcdic.h" -#endif +#endif /* CHARSET_EBCDIC */ #ifndef NO_USE_SIGACTION /* @@ -339,16 +379,17 @@ #if defined(SIG_IGN) && !defined(SIG_ERR) #define SIG_ERR ((Sigfunc *)-1) -#endif +#endif /* defined(SIG_IGN) && !defined(SIG_ERR) */ /* * For some strange reason, QNX defines signal to signal. Eliminate it. */ #ifdef signal #undef signal -#endif +#endif /* signal */ #define signal(s,f) apr_signal(s,f) Sigfunc *signal(int signo, Sigfunc * func); + #endif /* NO_USE_SIGACTION */ /* @@ -365,7 +406,7 @@ */ #ifndef LOW_SLACK_LINE #define LOW_SLACK_LINE 15 -#endif +#endif /* LOW_SLACK_LINE */ /* #define HIGH_SLACK_LINE 255 */ /* @@ -376,18 +417,76 @@ */ #ifdef NO_SLACK #define apr_slack(fd,line) (fd) -#else +#else /* NO_SLACK */ int apr_slack(int fd, int line); #define APR_SLACK_LOW 1 #define APR_SLACK_HIGH 2 +#endif /* NO_SLACK */ + +/* So that we can use inline on some critical functions, and use + * GNUC attributes (such as to get -Wall warnings for printf-like + * functions). Only do this in gcc 2.7 or later ... it may work + * on earlier stuff, but why chance it. + * + * We've since discovered that the gcc shipped with NeXT systems + * as "cc" is completely broken. It claims to be __GNUC__ and so + * on, but it doesn't implement half of the things that __GNUC__ + * means. In particular it's missing inline and the __attribute__ + * stuff. So we hack around it. PR#1613. -djg + */ +#if !defined(__GNUC__) || (__GNUC__ < 2) \ + || ((__GNUC__ == 2) && (__GNUC_MINOR__ < 7)) \ + || defined(NEXT) +#define APR_INLINE +#define __attribute__(__x) +#define ENUM_BITFIELD(e,n,w) signed int n : w +#else +#define APR_INLINE __inline__ +#define USE_GNU_INLINE +#define ENUM_BITFIELD(e,n,w) e n : w #endif +#ifdef ULTRIX +#define apr_fdopen(d,m) fdopen((d), (char *)(m)) +#else /* ULTRIX */ +#define apr_fdopen(d,m) fdopen((d), (m)) +#endif /* ULTRIX */ + /* + * Begint he actual APR definitions . + */ + +#define API_EXPORT(type) type +#define API_EXPORT_NONSTD(type) type + +/* + * A set of flags which indicate places where the server should raise(SIGSTOP). + * This is useful for debugging, because you can then attach to that process + * with gdb and continue. This is important in cases where one_process + * debugging isn't possible. + */ +#define APR_SIGSTOP_DETACH 1 +#define APR_SIGSTOP_MAKE_CHILD 2 +#define APR_SIGSTOP_SPAWN_CHILD 4 +#define APR_SIGSTOP_PIPED_LOG_SPAWN 8 +#define APR_SIGSTOP_CGI_CHILD 16 + +#ifdef DEBUG_SIGSTOP +extern int raise_sigstop_flags; +#define RAISE_SIGSTOP(x) \ + do { \ + if (raise_sigstop_flags & APR_SIGSTOP_##x) raise(SIGSTOP);\ + } while (0) +#else +#define RAISE_SIGSTOP(x) +#endif + +/* * Close up the C++ enclosure. */ #ifdef __cplusplus } -#endif +#endif /* __cplusplus */ /* * End.. 1.2 +179 -20 apache-apr/apr/include/apr_lib.h Index: apr_lib.h =================================================================== RCS file: /home/cvs/apache-apr/apr/include/apr_lib.h,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- apr_lib.h 1999/03/24 18:39:09 1.1 +++ apr_lib.h 1999/04/28 19:20:01 1.2 @@ -56,35 +56,85 @@ * The apr_vsnprintf/apr_snprintf functions are based on, and used with the * permission of, the SIO stdio-replacement strx_* functions by Panos * Tsirigotis <[EMAIL PROTECTED]> for xinetd. + * + * This header file defines the public interfaces for the APR general-purpose + * library routines. No others should need to be #included. */ #ifndef APR_LIB_H #define APR_LIB_H #include "apr_config.h" +#include "hsregex.h" #ifdef __cplusplus extern "C" { -#endif +#endif /* __cplusplus */ -API_EXPORT(char *) apr_cpystrn(char *, const char *, size_t); -int apr_slack(int, int); -int apr_execle(const char *, const char *, ...); -int apr_execve(const char *, const char *argv[], const char *envp[]); +/* + * Define the structures used by the APR general-purpose library. + */ + +/* + * Memory allocation stuff, like pools, arrays, and tables. Pools + * and tables are opaque structures to applications, but arrays are + * published. + */ +typedef struct apr_pool_t apr_pool_t; +typedef struct apr_table_t apr_table_t; +typedef struct apr_child_info_t apr_child_info_t; +typedef void apr_mutex_t; +typedef struct apr_array_header_t { + apr_pool_t *pool; + int elt_size; + int nelts; + int nalloc; + char *elts; +} apr_array_header_t; + +/* + * Structure used by the variable-formatter routines. + */ +typedef struct apr_vformatter_buff_t { + char *curpos; + char *endpos; +} apr_vformatter_buff_t; + +enum kill_conditions { + kill_never, /* process is never sent any signals */ + kill_always, /* process is sent SIGKILL on pool cleanup */ + kill_after_timeout, /* SIGTERM, wait 3 seconds, SIGKILL */ + just_wait, /* wait forever for the process to complete */ + kill_only_once /* send SIGTERM and then wait */ +}; + +/* + * Define the prototypes for the various APR GP routines. + */ +API_EXPORT(char *) apr_cpystrn(char *d, const char *s, size_t l); +API_EXPORT(apr_mutex_t *) apr_create_mutex(void *m); +API_EXPORT(int) apr_slack(int l, int h); +API_EXPORT_NONSTD(int) apr_execle(const char *c, const char *a, ...); +API_EXPORT_NONSTD(int) apr_execve(const char *c, const char *argv[], + const char *envp[]); -/* small utility macros to make things easier to read */ +/* + * Small utility macros to make things easier to read. Not usually a + * goal, to be sure.. + */ #ifdef WIN32 #define apr_killpg(x, y) -#else +#else /* WIN32 */ #ifdef NO_KILLPG #define apr_killpg(x, y) (kill (-(x), (y))) -#else +#else /* NO_KILLPG */ #define apr_killpg(x, y) (killpg ((x), (y))) -#endif +#endif /* NO_KILLPG */ #endif /* WIN32 */ -/* apr_vformatter() is a generic printf-style formatting routine +/* + * apr_vformatter() is a generic printf-style formatting routine * with some extensions. The extensions are: * * %pA takes a struct in_addr *, and prints it as a.b.c.d @@ -133,16 +183,12 @@ * or until apr_vformatter returns. */ -typedef struct apr_vformatter_buff_t { - char *curpos; - char *endpos; -} apr_vformatter_buff_t; - API_EXPORT(int) apr_vformatter(int (*flush_func)(apr_vformatter_buff_t *b), - apr_vformatter_buff_t *, const char *fmt, + apr_vformatter_buff_t *c, const char *fmt, va_list ap); -/* These are snprintf implementations based on apr_vformatter(). +/* + * These are snprintf implementations based on apr_vformatter(). * * Note that various standards and implementations disagree on the return * value of snprintf, and side-effects due to %n in the formatting string. @@ -163,10 +209,123 @@ va_list ap); /* - * Pool stuff. + * APR memory structure manipulators (pools, tables, and arrays). */ -typedef struct apr_pool_t apr_pool_t; -typedef struct apr_mutex_t apr_mutex_t; +API_EXPORT(apr_pool_t *) apr_make_sub_pool(apr_pool_t *p); +API_EXPORT(void) apr_clear_pool(apr_pool_t *p); +API_EXPORT(void) apr_destroy_pool(apr_pool_t *p); +API_EXPORT(long) apr_bytes_in_pool(apr_pool_t *p); +API_EXPORT(long) apr_bytes_in_free_blocks(void); +API_EXPORT(apr_pool_t *) apr_find_pool(const void *ts); +API_EXPORT(int) apr_pool_is_ancestor(apr_pool_t *a, apr_pool_t *b); +API_EXPORT(void) apr_pool_join(apr_pool_t *p, apr_pool_t *sub); +API_EXPORT(void *) apr_palloc(apr_pool_t *p, int reqsize); +API_EXPORT(void *) apr_pcalloc(apr_pool_t *p, int size); +API_EXPORT(char *) apr_pstrdup(apr_pool_t *p, const char *s); +API_EXPORT(char *) apr_pstrndup(apr_pool_t *p, const char *s, int n); +API_EXPORT_NONSTD(char *) apr_pstrcat(apr_pool_t *p, ...); +API_EXPORT(char *) apr_pvsprintf(apr_pool_t *p, const char *fmt, va_list ap); +API_EXPORT_NONSTD(char *) apr_psprintf(apr_pool_t *p, const char *fmt, ...); +API_EXPORT(apr_array_header_t *) apr_make_array(apr_pool_t *p, int nelts, + int elt_size); +API_EXPORT(void *) apr_push_array(apr_array_header_t *arr); +API_EXPORT(void) apr_array_cat(apr_array_header_t *dst, + const apr_array_header_t *src); +API_EXPORT(apr_array_header_t *) apr_copy_array(apr_pool_t *p, + const apr_array_header_t *arr); +API_EXPORT(apr_array_header_t *) + apr_copy_array_hdr(apr_pool_t *p, + const apr_array_header_t *arr); +API_EXPORT(apr_array_header_t *) + apr_append_arrays(apr_pool_t *p, + const apr_array_header_t *first, + const apr_array_header_t *second); +API_EXPORT(char *) apr_array_pstrcat(apr_pool_t *p, + const apr_array_header_t *arr, + const char sep); +API_EXPORT(apr_table_t *) apr_make_table(apr_pool_t *p, int nelts); +API_EXPORT(apr_table_t *) apr_copy_table(apr_pool_t *p, const apr_table_t *t); +API_EXPORT(void) apr_clear_table(apr_table_t *t); +API_EXPORT(const char *) apr_table_get(const apr_table_t *t, const char *key); +API_EXPORT(void) apr_table_set(apr_table_t *t, const char *key, + const char *val); +API_EXPORT(void) apr_table_setn(apr_table_t *t, const char *key, + const char *val); +API_EXPORT(void) apr_table_unset(apr_table_t *t, const char *key); +API_EXPORT(void) apr_table_merge(apr_table_t *t, const char *key, + const char *val); +API_EXPORT(void) apr_table_mergen(apr_table_t *t, const char *key, + const char *val); +API_EXPORT(void) apr_table_add(apr_table_t *t, const char *key, + const char *val); +API_EXPORT(void) apr_table_addn(apr_table_t *t, const char *key, + const char *val); +API_EXPORT(apr_table_t *) apr_overlay_tables(apr_pool_t *p, + const apr_table_t *overlay, + const apr_table_t *base); +API_EXPORT(void) + apr_table_do(int (*comp) (void *, const char *, const char *), + void *rec, const apr_table_t *t, ...); +API_EXPORT(void) apr_overlap_tables(apr_table_t *a, const apr_table_t *b, + unsigned flags); +API_EXPORT(void) apr_register_cleanup(apr_pool_t *p, void *data, + void (*plain_cleanup) (void *), + void (*child_cleanup) (void *)); +API_EXPORT(void) apr_kill_cleanup(apr_pool_t *p, void *data, + void (*cleanup) (void *)); +API_EXPORT(void) apr_run_cleanup(apr_pool_t *p, void *data, + void (*cleanup) (void *)); +API_EXPORT(void) apr_cleanup_for_exec(void); +API_EXPORT_NONSTD(void) apr_null_cleanup(void *data); +API_EXPORT(void) apr_note_cleanups_for_fd(apr_pool_t *p, int fd); +API_EXPORT(void) apr_kill_cleanups_for_fd(apr_pool_t *p, int fd); +API_EXPORT(int) apr_popenf(apr_pool_t *a, const char *name, int flg, int mode); +API_EXPORT(int) apr_pclosef(apr_pool_t *a, int fd); +API_EXPORT(void) apr_note_cleanups_for_file(apr_pool_t *p, FILE *fp); +API_EXPORT(FILE *) apr_pfopen(apr_pool_t *a, const char *name, + const char *mode); +API_EXPORT(FILE *) apr_pfdopen(apr_pool_t *a, int fd, const char *mode); +API_EXPORT(int) apr_pfclose(apr_pool_t *a, FILE *fd); +API_EXPORT(DIR *) apr_popendir(apr_pool_t *p, const char *name); +API_EXPORT(void) apr_pclosedir(apr_pool_t *p, DIR * d); +API_EXPORT(void) apr_note_cleanups_for_socket(apr_pool_t *p, int fd); +API_EXPORT(void) apr_kill_cleanups_for_socket(apr_pool_t *p, int sock); +API_EXPORT(int) apr_psocket(apr_pool_t *p, int domain, int type, int protocol); +API_EXPORT(int) apr_pclosesocket(apr_pool_t *a, int sock); +API_EXPORT(regex_t *) apr_pregcomp(apr_pool_t *p, const char *pattern, + int cflags); +API_EXPORT(void) apr_pregfree(apr_pool_t *p, regex_t *reg); +API_EXPORT(void) apr_note_subprocess(apr_pool_t *a, pid_t pid, + enum kill_conditions how); +API_EXPORT(int) + apr_spawn_child(apr_pool_t *p, + int (*func) (void *a, apr_child_info_t *c), + void *data, enum kill_conditions kill_how, + FILE **pipe_in, FILE **pipe_out, + FILE **pipe_err); +#if 0 +API_EXPORT(int) + apr_bspawn_child(apr_pool_t *p, + int (*func) (void *v, apr_child_info_t *c), + void *data, enum kill_conditions kill_how, + BUFF **pipe_in, BUFF **pipe_out, BUFF **pipe_err); +#endif /* 0 */ + +/* + * Routine definitions that only work on Windows. + */ +#ifdef WIN32 +API_EXPORT(void) apr_note_cleanups_for_h(apr_pool_t *p, HANDLE hDevice); +API_EXPORT(int) apr_pcloseh(apr_pool_t *a, HANDLE hDevice); +#endif /* WIN32 */ + +#ifdef TPF +#define apr_block_alarms() (0) +#define apr_unblock_alarms() (0) +#else /* TPF */ +API_EXPORT(void) apr_block_alarms(void); +API_EXPORT(void) apr_unblock_alarms(void); +#endif /* TPF */ #ifdef __cplusplus } 1.1 apache-apr/apr/include/apr_pools.h Index: apr_pools.h =================================================================== /* ==================================================================== * Copyright (c) 1995-1999 The Apache Group. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * 4. The names "Apache Server" and "Apache Group" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Group and was originally based * on public domain software written at the National Center for * Supercomputing Applications, University of Illinois, Urbana-Champaign. * For more information on the Apache Group and the Apache HTTP server * project, please see <http://www.apache.org/>. * */ #ifndef APR_POOLS_H #define APR_POOLS_H #ifdef __cplusplus extern "C" { #endif /* * Resource allocation routines... * * designed so that we don't have to keep track of EVERYTHING so that * it can be explicitly freed later (a fundamentally unsound strategy --- * particularly in the presence of die()). * * Instead, we maintain pools, and allocate items (both memory and I/O * handlers) from the pools --- currently there are two, one for per * transaction info, and one for config info. When a transaction is over, * we can delete everything in the per-transaction pool without fear, and * without thinking too hard about it either. * * rst */ /* Arenas for configuration info and transaction info * --- actual layout of the pool structure is private to * alloc.c. */ /* Need declaration of DIR on Win32 */ #ifdef WIN32 #include "../os/win32/readdir.h" #endif #include "apr_lib.h" struct process_chain { pid_t pid; enum kill_conditions kill_how; struct process_chain *next; }; struct apr_pool_t { union block_hdr *first; union block_hdr *last; struct cleanup *cleanups; struct process_chain *subprocesses; apr_pool_t *sub_pools; apr_pool_t *sub_next; apr_pool_t *sub_prev; apr_pool_t *parent; char *free_first_avail; #ifdef ALLOC_USE_MALLOC void *allocation_list; #endif #ifdef POOL_DEBUG apr_pool_t *joined; #endif }; struct apr_table_t { /* This has to be first to promote backwards compatibility with * older modules which cast a apr_table_t * to an apr_array_header_t *... * they should use the table_elts() function for most of the * cases they do this for. */ apr_array_header_t a; #ifdef MAKE_TABLE_PROFILE void *creator; #endif }; /* * Tables. Implemented alist style, for now, though we try to keep * it so that imposing a hash table structure on top in the future * wouldn't be *too* hard... * * Note that key comparisons for these are case-insensitive, largely * because that's what's appropriate and convenient everywhere they're * currently being used... */ typedef struct apr_table_entry_t { char *key; /* maybe NULL in future; * check when iterating thru table_elts */ char *val; } apr_table_entry_t; apr_pool_t *apr_init_alloc(void); /* Set up everything */ /* used to guarantee to the pool debugging code that the sub pool will not be * destroyed before the parent pool */ #ifndef POOL_DEBUG #ifdef apr_pool_join #undef apr_pool_join #endif /* apr_pool_join */ #define apr_pool_join(a,b) #endif /* POOL_DEBUG */ /* Clearing out EVERYTHING in an pool... destroys any sub-pools */ /* Preparing for exec() --- close files, etc., but *don't* flush I/O * buffers, *don't* wait for subprocesses, and *don't* free any memory. */ /* routines to allocate memory from an pool... */ API_EXPORT_NONSTD(char *) apr_psprintf(apr_pool_t *, const char *fmt, ...) __attribute__((format(printf,2,3))); /* array and alist management... keeping lists of things. * Common enough to want common support code ... */ /* apr_array_pstrcat generates a new string from the pool containing * the concatenated sequence of substrings referenced as elements within * the array. The string will be empty if all substrings are empty or null, * or if there are no elements in the array. * If sep is non-NUL, it will be inserted between elements as a separator. */ /* copy_array copies the *entire* array. copy_array_hdr just copies * the header, and arranges for the elements to be copied if (and only * if) the code subsequently does a push or arraycat. */ /* Conceptually, apr_overlap_tables does this: apr_array_header_t *barr = apr_table_elts(b); apr_table_entry_t *belt = (apr_table_entry_t *)barr->elts; int i; for (i = 0; i < barr->nelts; ++i) { if (flags & APR_OVERLAP_TABLES_MERGE) { apr_table_mergen(a, belt[i].key, belt[i].val); } else { apr_table_setn(a, belt[i].key, belt[i].val); } } Except that it is more efficient (less space and cpu-time) especially when b has many elements. Notice the assumptions on the keys and values in b -- they must be in an ancestor of a's pool. In practice b and a are usually from the same pool. */ #define APR_OVERLAP_TABLES_SET (0) #define APR_OVERLAP_TABLES_MERGE (1) /* XXX: these know about the definition of struct table in alloc.c. That * definition is not here because it is supposed to be private, and by not * placing it here we are able to get compile-time diagnostics from modules * written which assume that a table is the same as an apr_array_header_t. -djg */ #define apr_table_elts(t) ((apr_array_header_t *)(t)) #define apr_is_empty_table(t) (((t) == NULL)||(((apr_array_header_t *)(t))->nelts == 0)) /* routines to remember allocation of other sorts of things... * generic interface first. Note that we want to have two separate * cleanup functions in the general case, one for exec() preparation, * to keep CGI scripts and the like from inheriting access to things * they shouldn't be able to touch, and one for actually cleaning up, * when the actual server process wants to get rid of the thing, * whatever it is. * * kill_cleanup disarms a cleanup, presumably because the resource in * question has been closed, freed, or whatever, and it's scarce * enough to want to reclaim (e.g., descriptors). It arranges for the * resource not to be cleaned up a second time (it might have been * reallocated). run_cleanup does the same, but runs it first. * * Cleanups are identified for purposes of finding & running them off by the * plain_cleanup and data, which should presumably be unique. * * NB any code which invokes register_cleanup or kill_cleanup directly * is a critical section which should be guarded by block_alarms() and * unblock_alarms() below... */ /* A "do-nothing" cleanup, for register_cleanup; it's faster to do * things this way than to test for NULL. */ /* The time between when a resource is actually allocated, and when * its cleanup is registered is a critical section, during which the * resource could leak if we got interrupted or timed out. So, anything * which registers cleanups should bracket resource allocation and the * cleanup registry with these. (This is done internally by run_cleanup). * * NB they are actually implemented in http_main.c, since they are bound * up with timeout handling in general... */ /* Common cases which want utility support.. * the note_cleanups_for_foo routines are for */ API_EXPORT(FILE *) apr_pfopen(apr_pool_t *, const char *name, const char *fmode); API_EXPORT(FILE *) apr_pfdopen(apr_pool_t *, int fd, const char *fmode); API_EXPORT(int) apr_popenf(apr_pool_t *, const char *name, int flg, int mode); API_EXPORT(void) apr_note_cleanups_for_file(apr_pool_t *, FILE *); API_EXPORT(void) apr_note_cleanups_for_fd(apr_pool_t *, int); API_EXPORT(void) apr_kill_cleanups_for_fd(apr_pool_t *p, int fd); API_EXPORT(void) apr_note_cleanups_for_socket(apr_pool_t *, int); API_EXPORT(void) apr_kill_cleanups_for_socket(apr_pool_t *p, int sock); API_EXPORT(int) apr_psocket(apr_pool_t *p, int, int, int); API_EXPORT(int) apr_pclosesocket(apr_pool_t *a, int sock); API_EXPORT(regex_t *) apr_pregcomp(apr_pool_t *p, const char *pattern, int cflags); API_EXPORT(void) apr_pregfree(apr_pool_t *p, regex_t * reg); /* routines to note closes... file descriptors are constrained enough * on some systems that we want to support this. */ API_EXPORT(int) apr_pfclose(apr_pool_t *, FILE *); API_EXPORT(int) apr_pclosef(apr_pool_t *, int fd); /* routines to deal with directories */ API_EXPORT(DIR *) apr_popendir(apr_pool_t *p, const char *name); API_EXPORT(void) apr_pclosedir(apr_pool_t *p, DIR * d); /* ... even child processes (which we may want to wait for, * or to kill outright, on unexpected termination). * * apr_spawn_child is a utility routine which handles an awful lot of * the rigamarole associated with spawning a child --- it arranges * for pipes to the child's stdin and stdout, if desired (if not, * set the associated args to NULL). It takes as args a function * to call in the child, and an argument to be passed to the function. */ API_EXPORT(void) apr_note_subprocess(apr_pool_t *a, pid_t pid, enum kill_conditions how); /* magic numbers --- min free bytes to consider a free pool block useable, * and the min amount to allocate if we have to go to malloc() */ #ifndef BLOCK_MINFREE #define BLOCK_MINFREE 4096 #endif #ifndef BLOCK_MINALLOC #define BLOCK_MINALLOC 8192 #endif /* Finally, some accounting */ API_EXPORT(long) apr_bytes_in_pool(apr_pool_t *p); API_EXPORT(long) apr_bytes_in_free_blocks(void); #ifdef __cplusplus } #endif #endif /* !APR_POOLS_H */ 1.1 apache-apr/apr/include/hsregex.h Index: hsregex.h =================================================================== /* DON'T EVEN THINK ABOUT EDITING THIS, go see regex/Makefile, * search for mkh */ #ifndef _REGEX_H_ #define _REGEX_H_ /* never again */ /* ========= begin header generated by ./mkh ========= */ #ifdef __cplusplus extern "C" { #endif /* === regex2.h === */ #ifndef API_EXPORT #ifdef WIN32 #define API_EXPORT(type) __declspec(dllexport) type __stdcall #else #define API_EXPORT(type) type #endif #endif #if defined(RHAPSODY) #define ap_private_extern __private_extern__ #else #define ap_private_extern #endif typedef off_t regoff_t; typedef struct { int re_magic; size_t re_nsub; /* number of parenthesized subexpressions */ const char *re_endp; /* end pointer for REG_PEND */ struct re_guts *re_g; /* none of your business :-) */ } regex_t; typedef struct { regoff_t rm_so; /* start of match */ regoff_t rm_eo; /* end of match */ } regmatch_t; /* === regcomp.c === */ API_EXPORT(int) regcomp(regex_t *, const char *, int); #define REG_BASIC 0000 #define REG_EXTENDED 0001 #define REG_ICASE 0002 #define REG_NOSUB 0004 #define REG_NEWLINE 0010 #define REG_NOSPEC 0020 #define REG_PEND 0040 #define REG_DUMP 0200 /* === regerror.c === */ #define REG_NOMATCH 1 #define REG_BADPAT 2 #define REG_ECOLLATE 3 #define REG_ECTYPE 4 #define REG_EESCAPE 5 #define REG_ESUBREG 6 #define REG_EBRACK 7 #define REG_EPAREN 8 #define REG_EBRACE 9 #define REG_BADBR 10 #define REG_ERANGE 11 #define REG_ESPACE 12 #define REG_BADRPT 13 #define REG_EMPTY 14 #define REG_ASSERT 15 #define REG_INVARG 16 #define REG_ATOI 255 /* convert name to number (!) */ #define REG_ITOA 0400 /* convert number to name (!) */ API_EXPORT(size_t) regerror(int, const regex_t *, char *, size_t); /* === regexec.c === */ API_EXPORT(int) regexec(const regex_t *, const char *, size_t, regmatch_t [], int); #define REG_NOTBOL 00001 #define REG_NOTEOL 00002 #define REG_STARTEND 00004 #define REG_TRACE 00400 /* tracing of execution */ #define REG_LARGE 01000 /* force large representation */ #define REG_BACKR 02000 /* force use of backref code */ /* === regfree.c === */ API_EXPORT(void) regfree(regex_t *); #ifdef __cplusplus } #endif /* ========= end header generated by ./mkh ========= */ #endif 1.5 +7 -3 apache-apr/apr/lib/Makefile.in Index: Makefile.in =================================================================== RCS file: /home/cvs/apache-apr/apr/lib/Makefile.in,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- Makefile.in 1999/04/14 11:06:24 1.4 +++ Makefile.in 1999/04/28 19:20:15 1.5 @@ -4,6 +4,7 @@ #LDFLAGS=$(LDFLAGS1) $(EXTRA_LDFLAGS) [EMAIL PROTECTED]@ [EMAIL PROTECTED]@ [EMAIL PROTECTED]@ @CFLAGS@ @OPTIM@ [EMAIL PROTECTED]@ [EMAIL PROTECTED]@ $(LDLIBS) @@ -16,6 +17,7 @@ apr_fnmatch.o \ apr_execve.o \ apr_md5.o \ + apr_pools.o \ apr_signal.o \ apr_slack.o \ apr_snprintf.o @@ -57,9 +59,11 @@ apr_execve.o: apr_execve.c $(INCDIR)/apr_config.h apr_fnmatch.o: apr_fnmatch.c $(INCDIR)/apr_config.h \ $(INCDIR)/apr_fnmatch.h -apr_md5c.o: apr_md5c.c $(INCDIR)/apr_config.h \ - $(INCDIR)/apr_md5.h $(INCDIR)/apr_lib.h +apr_md5.o: apr_md5.c $(INCDIR)/apr_config.h $(INCDIR)/apr_md5.h \ + $(INCDIR)/apr_lib.h $(INCDIR)/hsregex.h +apr_pools.o: apr_pools.c $(INCDIR)/apr_config.h \ + $(INCDIR)/apr_pools.h $(INCDIR)/apr_lib.h $(INCDIR)/hsregex.h apr_signal.o: apr_signal.c $(INCDIR)/apr_config.h apr_slack.o: apr_slack.c $(INCDIR)/apr_config.h apr_snprintf.o: apr_snprintf.c $(INCDIR)/apr_config.h \ - $(INCDIR)/apr_lib.h + $(INCDIR)/apr_lib.h $(INCDIR)/hsregex.h 1.1 apache-apr/apr/lib/apr_pools.c Index: apr_pools.c =================================================================== /* ==================================================================== * Copyright (c) 1995-1999 The Apache Group. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * 4. The names "Apache Server" and "Apache Group" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Group and was originally based * on public domain software written at the National Center for * Supercomputing Applications, University of Illinois, Urbana-Champaign. * For more information on the Apache Group and the Apache HTTP server * project, please see <http://www.apache.org/>. * */ /* * Resource allocation code... the code here is responsible for making * sure that nothing leaks. * * rst --- 4/95 --- 6/95 */ #include "apr_config.h" #include "apr_pools.h" /* * Debugging support: Define this to enable code which helps detect re-use * of freed memory and other such nonsense. * * The theory is simple. The FILL_BYTE (0xa5) is written over all malloc'd * memory as we receive it, and is written over everything that we free up * during a clear_pool. We check that blocks on the free list always * have the FILL_BYTE in them, and we check during palloc() that the bytes * still have FILL_BYTE in them. If you ever see garbage URLs or whatnot * containing lots of 0xa5s then you know something used data that's been * freed or uninitialized. */ /* #define ALLOC_DEBUG */ /* * Debugging support: If defined all allocations will be done with * malloc and free()d appropriately at the end. This is intended to be * used with something like Electric Fence or Purify to help detect * memory problems. Note that if you're using efence then you should also * add in ALLOC_DEBUG. But don't add in ALLOC_DEBUG if you're using Purify * because ALLOC_DEBUG would hide all the uninitialized read errors that * Purify can diagnose. */ /* #define ALLOC_USE_MALLOC */ /* * Pool debugging support: This is intended to detect cases where the * wrong pool is used when assigning data to an object in another pool. * In particular, it causes the table_{set,add,merge}n routines to check * that their arguments are safe for the table they're being placed in. * It currently only works with the unix multiprocess model, but could * be extended to others. */ /* #define POOL_DEBUG */ /* * Provide diagnostic information about make_table() calls which are * possibly too small. This requires a recent gcc which supports * __builtin_return_address(). The error_log output will be a * message such as: * table_push: table created by 0x804d874 hit limit of 10 * Use "l *0x804d874" to find the source that corresponds to. It * indicates that a table allocated by a call at that address has * possibly too small an initial table size guess. */ /* #define MAKE_TABLE_PROFILE */ /* * Provide some statistics on the cost of allocations. It requires a * bit of an understanding of how alloc.c works. */ /* #define ALLOC_STATS */ #ifdef POOL_DEBUG #ifdef ALLOC_USE_MALLOC #error "sorry, no support for ALLOC_USE_MALLOC and POOL_DEBUG at the same time" #endif /* ALLOC_USE_MALLOC */ #ifdef MULTITHREAD # error "sorry, no support for MULTITHREAD and POOL_DEBUG at the same time" #endif /* MULTITHREAD */ #endif /* POOL_DEBUG */ #ifdef ALLOC_USE_MALLOC #undef BLOCK_MINFREE #undef BLOCK_MINALLOC #define BLOCK_MINFREE 0 #define BLOCK_MINALLOC 0 #endif /* ALLOC_USE_MALLOC */ /***************************************************************** * * Managing free storage blocks... */ union align { /* * Types which are likely to have the longest RELEVANT alignment * restrictions... */ char *cp; void (*f) (void); long l; FILE *fp; double d; }; #define CLICK_SZ (sizeof(union align)) union block_hdr { union align a; /* Actual header... */ struct { char *endp; union block_hdr *next; char *first_avail; #ifdef POOL_DEBUG union block_hdr *global_next; apr_pool_t *owning_pool; #endif /* POOL_DEBUG */ } h; }; /* * Static cells for managing our internal synchronisation. */ static union block_hdr *block_freelist = NULL; static apr_mutex_t *alloc_mutex = NULL; static apr_mutex_t *spawn_mutex = NULL; #ifdef POOL_DEBUG static char *known_stack_point; static int stack_direction; static union block_hdr *global_block_list; #define FREE_POOL ((apr_pool_t *)(-1)) #endif /* POOL_DEBUG */ #ifdef ALLOC_STATS static unsigned long long num_free_blocks_calls; static unsigned long long num_blocks_freed; static unsigned max_blocks_in_one_free; static unsigned num_malloc_calls; static unsigned num_malloc_bytes; #endif /* ALLOC_STATS */ #ifdef ALLOC_DEBUG #define FILL_BYTE ((char)(0xa5)) #define debug_fill(ptr,size) ((void)memset((ptr), FILL_BYTE, (size))) static APR_INLINE void debug_verify_filled(const char *ptr, const char *endp, const char *error_msg) { for ( ; ptr < endp; ++ptr) { if (*ptr != FILL_BYTE) { fputs(error_msg, stderr); abort(); exit(1); } } } #else /* ALLOC_DEBUG */ #define debug_fill(a,b) #define debug_verify_filled(a,b,c) #endif /* ALLOC_DEBUG */ /* * Get a completely new block from the system pool. Note that we rely on * malloc() to provide aligned memory. */ static union block_hdr *malloc_block(int size) { union block_hdr *blok; #ifdef ALLOC_DEBUG /* make some room at the end which we'll fill and expect to be * always filled */ size += CLICK_SZ; #endif /* ALLOC_DEBUG */ #ifdef ALLOC_STATS ++num_malloc_calls; num_malloc_bytes += size + sizeof(union block_hdr); #endif /* ALLOC_STATS */ blok = (union block_hdr *) malloc(size + sizeof(union block_hdr)); if (blok == NULL) { fprintf(stderr, "Ouch! malloc failed in malloc_block()\n"); exit(1); } debug_fill(blok, size + sizeof(union block_hdr)); blok->h.next = NULL; blok->h.first_avail = (char *) (blok + 1); blok->h.endp = size + blok->h.first_avail; #ifdef ALLOC_DEBUG blok->h.endp -= CLICK_SZ; #endif /* ALLOC_DEBUG */ #ifdef POOL_DEBUG blok->h.global_next = global_block_list; global_block_list = blok; blok->h.owning_pool = NULL; #endif /* POOL_DEBUG */ return blok; } #if defined(ALLOC_DEBUG) && !defined(ALLOC_USE_MALLOC) static void chk_on_blk_list(union block_hdr *blok, union block_hdr *free_blk) { debug_verify_filled(blok->h.endp, blok->h.endp + CLICK_SZ, "Ouch! Someone trounced the padding " "at the end of a block!\n"); while (free_blk) { if (free_blk == blok) { fprintf(stderr, "Ouch! Freeing free block\n"); abort(); exit(1); } free_blk = free_blk->h.next; } } #else /* defined(ALLOC_DEBUG) && !defined(ALLOC_USE_MALLOC) */ #define chk_on_blk_list(_x, _y) #endif /* defined(ALLOC_DEBUG) && !defined(ALLOC_USE_MALLOC) */ /* Free a chain of blocks --- must be called with alarms blocked. */ static void free_blocks(union block_hdr *blok) { #ifdef ALLOC_USE_MALLOC union block_hdr *next; for ( ; blok; blok = next) { next = blok->h.next; free(blok); } #else /* ALLOC_USE_MALLOC */ #ifdef ALLOC_STATS unsigned num_blocks; #endif /* ALLOC_STATS */ /* * First, put new blocks at the head of the free list --- * we'll eventually bash the 'next' pointer of the last block * in the chain to point to the free blocks we already had. */ union block_hdr *old_free_list; if (blok == NULL) { return; /* Sanity check --- freeing empty pool? */ } (void) apr_acquire_mutex(alloc_mutex); old_free_list = block_freelist; block_freelist = blok; /* * Next, adjust first_avail pointers of each block --- have to do it * sooner or later, and it simplifies the search in new_block to do it * now. */ #ifdef ALLOC_STATS num_blocks = 1; #endif /* ALLOC_STATS */ while (blok->h.next != NULL) { #ifdef ALLOC_STATS ++num_blocks; #endif /* ALLOC_STATS */ chk_on_blk_list(blok, old_free_list); blok->h.first_avail = (char *) (blok + 1); debug_fill(blok->h.first_avail, blok->h.endp - blok->h.first_avail); #ifdef POOL_DEBUG blok->h.owning_pool = FREE_POOL; #endif /* POOL_DEBUG */ blok = blok->h.next; } chk_on_blk_list(blok, old_free_list); blok->h.first_avail = (char *) (blok + 1); debug_fill(blok->h.first_avail, blok->h.endp - blok->h.first_avail); #ifdef POOL_DEBUG blok->h.owning_pool = FREE_POOL; #endif /* POOL_DEBUG */ /* Finally, reset next pointer to get the old free blocks back */ blok->h.next = old_free_list; #ifdef ALLOC_STATS if (num_blocks > max_blocks_in_one_free) { max_blocks_in_one_free = num_blocks; } ++num_free_blocks_calls; num_blocks_freed += num_blocks; #endif /* ALLOC_STATS */ (void) apr_release_mutex(alloc_mutex); #endif /* ALLOC_USE_MALLOC */ } /* * Get a new block, from our own free list if possible, from the system * if necessary. Must be called with alarms blocked. */ static union block_hdr *new_block(int min_size) { union block_hdr **lastptr = &block_freelist; union block_hdr *blok = block_freelist; /* First, see if we have anything of the required size * on the free list... */ while (blok != NULL) { if (min_size + BLOCK_MINFREE <= blok->h.endp - blok->h.first_avail) { *lastptr = blok->h.next; blok->h.next = NULL; debug_verify_filled(blok->h.first_avail, blok->h.endp, "Ouch! Someone trounced a block " "on the free list!\n"); return blok; } else { lastptr = &blok->h.next; blok = blok->h.next; } } /* Nope. */ min_size += BLOCK_MINFREE; blok = malloc_block((min_size > BLOCK_MINALLOC) ? min_size : BLOCK_MINALLOC); return blok; } /* Accounting */ static long bytes_in_block_list(union block_hdr *blok) { long size = 0; while (blok) { size += blok->h.endp - (char *) (blok + 1); blok = blok->h.next; } return size; } /***************************************************************** * * Pool internals and management... * NB that subprocesses are not handled by the generic cleanup code, * basically because we don't want cleanups for multiple subprocesses * to result in multiple three-second pauses. */ struct process_chain; struct cleanup; static void run_cleanups(struct cleanup *c); static void free_proc_chain(struct process_chain *p); static apr_pool_t *permanent_pool; /* Each pool structure is allocated in the start of its own first block, * so we need to know how many bytes that is (once properly aligned...). * This also means that when a pool's sub-pool is destroyed, the storage * associated with it is *completely* gone, so we have to make sure it * gets taken off the parent's sub-pool list... */ #define POOL_HDR_CLICKS (1 + ((sizeof(struct apr_pool_t) - 1) / CLICK_SZ)) #define POOL_HDR_BYTES (POOL_HDR_CLICKS * CLICK_SZ) API_EXPORT(apr_pool_t *) apr_make_sub_pool(apr_pool_t *p) { union block_hdr *blok; apr_pool_t *new_pool; apr_block_alarms(); (void) apr_acquire_mutex(alloc_mutex); blok = new_block(POOL_HDR_BYTES); new_pool = (apr_pool_t *) blok->h.first_avail; blok->h.first_avail += POOL_HDR_BYTES; #ifdef POOL_DEBUG blok->h.owning_pool = new_pool; #endif memset((char *) new_pool, '\0', sizeof(struct apr_pool_t)); new_pool->free_first_avail = blok->h.first_avail; new_pool->first = new_pool->last = blok; if (p) { new_pool->parent = p; new_pool->sub_next = p->sub_pools; if (new_pool->sub_next) { new_pool->sub_next->sub_prev = new_pool; } p->sub_pools = new_pool; } (void) apr_release_mutex(alloc_mutex); apr_unblock_alarms(); return new_pool; } #ifdef POOL_DEBUG static void stack_var_init(char *s) { char t; if (s < &t) { stack_direction = 1; /* stack grows up */ } else { stack_direction = -1; /* stack grows down */ } } #endif #ifdef ALLOC_STATS static void dump_stats(void) { fprintf(stderr, "alloc_stats: [%d] #free_blocks %llu #blocks %llu max " "%u #malloc %u #bytes %u\n", (int) getpid(), num_free_blocks_calls, num_blocks_freed, max_blocks_in_one_free, num_malloc_calls, num_malloc_bytes); } #endif apr_pool_t *apr_init_alloc(void) { #ifdef POOL_DEBUG char s; known_stack_point = &s; stack_var_init(&s); #endif alloc_mutex = apr_create_mutex(NULL); spawn_mutex = apr_create_mutex(NULL); permanent_pool = apr_make_sub_pool(NULL); #ifdef ALLOC_STATS atexit(dump_stats); #endif return permanent_pool; } API_EXPORT(void) apr_clear_pool(apr_pool_t *a) { apr_block_alarms(); (void) apr_acquire_mutex(alloc_mutex); while (a->sub_pools) { apr_destroy_pool(a->sub_pools); } (void) apr_release_mutex(alloc_mutex); /* * Don't hold the mutex during cleanups. */ run_cleanups(a->cleanups); a->cleanups = NULL; free_proc_chain(a->subprocesses); a->subprocesses = NULL; free_blocks(a->first->h.next); a->first->h.next = NULL; a->last = a->first; a->first->h.first_avail = a->free_first_avail; debug_fill(a->first->h.first_avail, a->first->h.endp - a->first->h.first_avail); #ifdef ALLOC_USE_MALLOC { void *c, *n; for (c = a->allocation_list; c; c = n) { n = *(void **)c; free(c); } a->allocation_list = NULL; } #endif apr_unblock_alarms(); } API_EXPORT(void) apr_destroy_pool(apr_pool_t *a) { apr_block_alarms(); apr_clear_pool(a); (void) apr_acquire_mutex(alloc_mutex); if (a->parent) { if (a->parent->sub_pools == a) { a->parent->sub_pools = a->sub_next; } if (a->sub_prev) { a->sub_prev->sub_next = a->sub_next; } if (a->sub_next) { a->sub_next->sub_prev = a->sub_prev; } } (void) apr_release_mutex(alloc_mutex); free_blocks(a->first); apr_unblock_alarms(); } API_EXPORT(long) apr_bytes_in_pool(apr_pool_t *p) { return bytes_in_block_list(p->first); } API_EXPORT(long) apr_bytes_in_free_blocks(void) { return bytes_in_block_list(block_freelist); } /***************************************************************** * POOL_DEBUG support */ #ifdef POOL_DEBUG /* the unix linker defines this symbol as the last byte + 1 of * the executable... so it includes TEXT, BSS, and DATA */ extern char _end; /* is ptr in the range [lo,hi) */ #define is_ptr_in_range(ptr, lo, hi) \ (((unsigned long)(ptr) - (unsigned long)(lo)) \ < (unsigned long)(hi) - (unsigned long)(lo)) /* Find the pool that ts belongs to, return NULL if it doesn't * belong to any pool. */ API_EXPORT(apr_pool_t *) apr_find_pool(const void *ts) { const char *s = ts; union block_hdr **pb; union block_hdr *b; /* short-circuit stuff which is in TEXT, BSS, or DATA */ if (is_ptr_in_range(s, 0, &_end)) { return NULL; } /* consider stuff on the stack to also be in the NULL pool... * XXX: there's cases where we don't want to assume this */ if ((stack_direction == -1 && is_ptr_in_range(s, &ts, known_stack_point)) || (stack_direction == 1 && is_ptr_in_range(s, known_stack_point, &ts))) { abort(); return NULL; } apr_block_alarms(); /* search the global_block_list */ for (pb = &global_block_list; *pb; pb = &b->h.global_next) { b = *pb; if (is_ptr_in_range(s, b, b->h.endp)) { if (b->h.owning_pool == FREE_POOL) { fprintf(stderr, "Ouch! find_pool() called on pointer " "in a free block\n"); abort(); exit(1); } if (b != global_block_list) { /* * promote b to front of list, this is a hack to speed * up the lookup */ *pb = b->h.global_next; b->h.global_next = global_block_list; global_block_list = b; } apr_unblock_alarms(); return b->h.owning_pool; } } apr_unblock_alarms(); return NULL; } /* return TRUE iff a is an ancestor of b * NULL is considered an ancestor of all pools */ API_EXPORT(int) apr_pool_is_ancestor(apr_pool_t *a, apr_pool_t *b) { if (a == NULL) { return 1; } while (a->joined) { a = a->joined; } while (b) { if (a == b) { return 1; } b = b->parent; } return 0; } /* * All blocks belonging to sub will be changed to point to p * instead. This is a guarantee by the caller that sub will not * be destroyed before p is. */ API_EXPORT(void) apr_pool_join(apr_pool_t *p, apr_pool_t *sub) { union block_hdr *b; /* We could handle more general cases... but this is it for now. */ if (sub->parent != p) { fprintf(stderr, "pool_join: p is not parent of sub\n"); abort(); } apr_block_alarms(); while (p->joined) { p = p->joined; } sub->joined = p; for (b = global_block_list; b; b = b->h.global_next) { if (b->h.owning_pool == sub) { b->h.owning_pool = p; } } apr_unblock_alarms(); } #endif /***************************************************************** * * Allocating stuff... */ API_EXPORT(void *) apr_palloc(apr_pool_t *a, int reqsize) { #ifdef ALLOC_USE_MALLOC int size = reqsize + CLICK_SZ; void *ptr; apr_block_alarms(); ptr = malloc(size); if (ptr == NULL) { fputs("Ouch! Out of memory!\n", stderr); exit(1); } debug_fill(ptr, size); /* might as well get uninitialized protection */ *(void **)ptr = a->allocation_list; a->allocation_list = ptr; apr_unblock_alarms(); return (char *)ptr + CLICK_SZ; #else /* * Round up requested size to an even number of alignment units * (core clicks) */ int nclicks = 1 + ((reqsize - 1) / CLICK_SZ); int size = nclicks * CLICK_SZ; /* First, see if we have space in the block most recently * allocated to this pool */ union block_hdr *blok = a->last; char *first_avail = blok->h.first_avail; char *new_first_avail; if (reqsize <= 0) { return NULL; } new_first_avail = first_avail + size; if (new_first_avail <= blok->h.endp) { debug_verify_filled(first_avail, blok->h.endp, "Ouch! Someone trounced past the end " "of their allocation!\n"); blok->h.first_avail = new_first_avail; return (void *) first_avail; } /* Nope --- get a new one that's guaranteed to be big enough */ apr_block_alarms(); (void) apr_acquire_mutex(alloc_mutex); blok = new_block(size); a->last->h.next = blok; a->last = blok; #ifdef POOL_DEBUG blok->h.owning_pool = a; #endif (void) apr_release_mutex(alloc_mutex); apr_unblock_alarms(); first_avail = blok->h.first_avail; blok->h.first_avail += size; return (void *) first_avail; #endif } API_EXPORT(void *) apr_pcalloc(apr_pool_t *a, int size) { void *res = apr_palloc(a, size); memset(res, '\0', size); return res; } API_EXPORT(char *) apr_pstrdup(apr_pool_t *a, const char *s) { char *res; size_t len; if (s == NULL) { return NULL; } len = strlen(s) + 1; res = apr_palloc(a, len); memcpy(res, s, len); return res; } API_EXPORT(char *) apr_pstrndup(apr_pool_t *a, const char *s, int n) { char *res; if (s == NULL) { return NULL; } res = apr_palloc(a, n + 1); memcpy(res, s, n); res[n] = '\0'; return res; } API_EXPORT_NONSTD(char *) apr_pstrcat(apr_pool_t *a, ...) { char *cp, *argp, *res; /* Pass one --- find length of required string */ int len = 0; va_list adummy; va_start(adummy, a); while ((cp = va_arg(adummy, char *)) != NULL) { len += strlen(cp); } va_end(adummy); /* Allocate the required string */ res = (char *) apr_palloc(a, len + 1); cp = res; *cp = '\0'; /* Pass two --- copy the argument strings into the result space */ va_start(adummy, a); while ((argp = va_arg(adummy, char *)) != NULL) { strcpy(cp, argp); cp += strlen(argp); } va_end(adummy); /* Return the result string */ return res; } /* * apr_psprintf is implemented by writing directly into the current * block of the pool, starting right at first_avail. If there's * insufficient room, then a new block is allocated and the earlier * output is copied over. The new block isn't linked into the pool * until all the output is done. * * Note that this is completely safe because nothing else can * allocate in this pool while apr_psprintf is running. alarms are * blocked, and the only thing outside of alloc.c that's invoked * is apr_vformatter -- which was purposefully written to be * self-contained with no callouts. */ struct psprintf_data { apr_vformatter_buff_t vbuff; #ifdef ALLOC_USE_MALLOC char *base; #else union block_hdr *blok; int got_a_new_block; #endif }; static int psprintf_flush(apr_vformatter_buff_t *vbuff) { struct psprintf_data *ps = (struct psprintf_data *)vbuff; #ifdef ALLOC_USE_MALLOC int size; char *ptr; size = (char *)ps->vbuff.curpos - ps->base; ptr = realloc(ps->base, 2*size); if (ptr == NULL) { fputs("Ouch! Out of memory!\n", stderr); exit(1); } ps->base = ptr; ps->vbuff.curpos = ptr + size; ps->vbuff.endpos = ptr + 2*size - 1; return 0; #else union block_hdr *blok; union block_hdr *nblok; size_t cur_len; char *strp; blok = ps->blok; strp = ps->vbuff.curpos; cur_len = strp - blok->h.first_avail; /* must try another blok */ (void) apr_acquire_mutex(alloc_mutex); nblok = new_block(2 * cur_len); (void) apr_release_mutex(alloc_mutex); memcpy(nblok->h.first_avail, blok->h.first_avail, cur_len); ps->vbuff.curpos = nblok->h.first_avail + cur_len; /* save a byte for the NUL terminator */ ps->vbuff.endpos = nblok->h.endp - 1; /* did we allocate the current blok? if so free it up */ if (ps->got_a_new_block) { debug_fill(blok->h.first_avail, blok->h.endp - blok->h.first_avail); (void) apr_acquire_mutex(alloc_mutex); blok->h.next = block_freelist; block_freelist = blok; (void) apr_release_mutex(alloc_mutex); } ps->blok = nblok; ps->got_a_new_block = 1; /* note that we've deliberately not linked the new block onto * the pool yet... because we may need to flush again later, and * we'd have to spend more effort trying to unlink the block. */ return 0; #endif } API_EXPORT(char *) apr_pvsprintf(apr_pool_t *p, const char *fmt, va_list ap) { #ifdef ALLOC_USE_MALLOC struct psprintf_data ps; void *ptr; apr_block_alarms(); ps.base = malloc(512); if (ps.base == NULL) { fputs("Ouch! Out of memory!\n", stderr); exit(1); } /* need room at beginning for allocation_list */ ps.vbuff.curpos = ps.base + CLICK_SZ; ps.vbuff.endpos = ps.base + 511; apr_vformatter(psprintf_flush, &ps.vbuff, fmt, ap); *ps.vbuff.curpos++ = '\0'; ptr = ps.base; /* shrink */ ptr = realloc(ptr, (char *)ps.vbuff.curpos - (char *)ptr); if (ptr == NULL) { fputs("Ouch! Out of memory!\n", stderr); exit(1); } *(void **)ptr = p->allocation_list; p->allocation_list = ptr; apr_unblock_alarms(); return (char *)ptr + CLICK_SZ; #else struct psprintf_data ps; char *strp; int size; apr_block_alarms(); ps.blok = p->last; ps.vbuff.curpos = ps.blok->h.first_avail; ps.vbuff.endpos = ps.blok->h.endp - 1; /* save one for NUL */ ps.got_a_new_block = 0; apr_vformatter(psprintf_flush, &ps.vbuff, fmt, ap); strp = ps.vbuff.curpos; *strp++ = '\0'; size = strp - ps.blok->h.first_avail; size = (1 + ((size - 1) / CLICK_SZ)) * CLICK_SZ; strp = ps.blok->h.first_avail; /* save away result pointer */ ps.blok->h.first_avail += size; /* have to link the block in if it's a new one */ if (ps.got_a_new_block) { p->last->h.next = ps.blok; p->last = ps.blok; #ifdef POOL_DEBUG ps.blok->h.owning_pool = p; #endif } apr_unblock_alarms(); return strp; #endif } API_EXPORT_NONSTD(char *) apr_psprintf(apr_pool_t *p, const char *fmt, ...) { va_list ap; char *res; va_start(ap, fmt); res = apr_pvsprintf(p, fmt, ap); va_end(ap); return res; } /***************************************************************** * * The 'array' functions... */ static void make_array_core(apr_array_header_t *res, apr_pool_t *p, int nelts, int elt_size) { /* * Assure sanity if someone asks for * array of zero elts. */ if (nelts < 1) { nelts = 1; } res->elts = apr_pcalloc(p, nelts * elt_size); res->pool = p; res->elt_size = elt_size; res->nelts = 0; /* No active elements yet... */ res->nalloc = nelts; /* ...but this many allocated */ } API_EXPORT(apr_array_header_t *) apr_make_array(apr_pool_t *p, int nelts, int elt_size) { apr_array_header_t *res; res = (apr_array_header_t *) apr_palloc(p, sizeof(apr_array_header_t)); make_array_core(res, p, nelts, elt_size); return res; } API_EXPORT(void *) apr_push_array(apr_array_header_t *arr) { if (arr->nelts == arr->nalloc) { int new_size = (arr->nalloc <= 0) ? 1 : arr->nalloc * 2; char *new_data; new_data = apr_pcalloc(arr->pool, arr->elt_size * new_size); memcpy(new_data, arr->elts, arr->nalloc * arr->elt_size); arr->elts = new_data; arr->nalloc = new_size; } ++arr->nelts; return arr->elts + (arr->elt_size * (arr->nelts - 1)); } API_EXPORT(void) apr_array_cat(apr_array_header_t *dst, const apr_array_header_t *src) { int elt_size = dst->elt_size; if (dst->nelts + src->nelts > dst->nalloc) { int new_size = (dst->nalloc <= 0) ? 1 : dst->nalloc * 2; char *new_data; while (dst->nelts + src->nelts > new_size) { new_size *= 2; } new_data = apr_pcalloc(dst->pool, elt_size * new_size); memcpy(new_data, dst->elts, dst->nalloc * elt_size); dst->elts = new_data; dst->nalloc = new_size; } memcpy(dst->elts + dst->nelts * elt_size, src->elts, elt_size * src->nelts); dst->nelts += src->nelts; } API_EXPORT(apr_array_header_t *) apr_copy_array(apr_pool_t *p, const apr_array_header_t *arr) { apr_array_header_t *res = apr_make_array(p, arr->nalloc, arr->elt_size); memcpy(res->elts, arr->elts, arr->elt_size * arr->nelts); res->nelts = arr->nelts; return res; } /* This cute function copies the array header *only*, but arranges * for the data section to be copied on the first push or arraycat. * It's useful when the elements of the array being copied are * read only, but new stuff *might* get added on the end; we have the * overhead of the full copy only where it is really needed. */ static APR_INLINE void copy_array_hdr_core(apr_array_header_t *res, const apr_array_header_t *arr) { res->elts = arr->elts; res->elt_size = arr->elt_size; res->nelts = arr->nelts; res->nalloc = arr->nelts; /* Force overflow on push */ } API_EXPORT(apr_array_header_t *) apr_copy_array_hdr(apr_pool_t *p, const apr_array_header_t *arr) { apr_array_header_t *res; res = (apr_array_header_t *) apr_palloc(p, sizeof(apr_array_header_t)); res->pool = p; copy_array_hdr_core(res, arr); return res; } /* The above is used here to avoid consing multiple new array bodies... */ API_EXPORT(apr_array_header_t *) apr_append_arrays(apr_pool_t *p, const apr_array_header_t *first, const apr_array_header_t *second) { apr_array_header_t *res = apr_copy_array_hdr(p, first); apr_array_cat(res, second); return res; } /* apr_array_pstrcat generates a new string from the pool containing * the concatenated sequence of substrings referenced as elements within * the array. The string will be empty if all substrings are empty or null, * or if there are no elements in the array. * If sep is non-NUL, it will be inserted between elements as a separator. */ API_EXPORT(char *) apr_array_pstrcat(apr_pool_t *p, const apr_array_header_t *arr, const char sep) { char *cp, *res, **strpp; int i, len; if (arr->nelts <= 0 || arr->elts == NULL) { /* Empty table? */ return (char *) apr_pcalloc(p, 1); } /* Pass one --- find length of required string */ len = 0; for (i = 0, strpp = (char **) arr->elts; ; ++strpp) { if (strpp && *strpp != NULL) { len += strlen(*strpp); } if (++i >= arr->nelts) { break; } if (sep) { ++len; } } /* Allocate the required string */ res = (char *) apr_palloc(p, len + 1); cp = res; /* Pass two --- copy the argument strings into the result space */ for (i = 0, strpp = (char **) arr->elts; ; ++strpp) { if (strpp && *strpp != NULL) { len = strlen(*strpp); memcpy(cp, *strpp, len); cp += len; } if (++i >= arr->nelts) { break; } if (sep) { *cp++ = sep; } } *cp = '\0'; /* Return the result string */ return res; } /***************************************************************** * * The "table" functions. */ /* * XXX: if you tweak this you should look at is_empty_table() and table_elts() * in alloc.h */ #ifdef MAKE_TABLE_PROFILE static apr_table_entry_t *table_push(apr_table_t *t) { if (t->a.nelts == t->a.nalloc) { fprintf(stderr, "table_push: table created by %p hit limit of %u\n", t->creator, t->a.nalloc); } return (apr_table_entry_t *) apr_push_array(&t->a); } #else /* MAKE_TABLE_PROFILE */ #define table_push(t) ((apr_table_entry_t *) apr_push_array(&(t)->a)) #endif /* MAKE_TABLE_PROFILE */ API_EXPORT(apr_table_t *) apr_make_table(apr_pool_t *p, int nelts) { apr_table_t *t = apr_palloc(p, sizeof(apr_table_t)); make_array_core(&t->a, p, nelts, sizeof(apr_table_entry_t)); #ifdef MAKE_TABLE_PROFILE t->creator = __builtin_return_address(0); #endif return t; } API_EXPORT(apr_table_t *) apr_copy_table(apr_pool_t *p, const apr_table_t *t) { apr_table_t *new = apr_palloc(p, sizeof(apr_table_t)); #ifdef POOL_DEBUG /* we don't copy keys and values, so it's necessary that t->a.pool * have a life span at least as long as p */ if (!apr_pool_is_ancestor(t->a.pool, p)) { fprintf(stderr, "copy_table: t's pool is not an ancestor of p\n"); abort(); } #endif make_array_core(&new->a, p, t->a.nalloc, sizeof(apr_table_entry_t)); memcpy(new->a.elts, t->a.elts, t->a.nelts * sizeof(apr_table_entry_t)); new->a.nelts = t->a.nelts; return new; } API_EXPORT(void) apr_clear_table(apr_table_t *t) { t->a.nelts = 0; } API_EXPORT(const char *) apr_table_get(const apr_table_t *t, const char *key) { apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; int i; if (key == NULL) { return NULL; } for (i = 0; i < t->a.nelts; ++i) { if (!strcasecmp(elts[i].key, key)) { return elts[i].val; } } return NULL; } API_EXPORT(void) apr_table_set(apr_table_t *t, const char *key, const char *val) { register int i, j, k; apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; int done = 0; for (i = 0; i < t->a.nelts; ) { if (!strcasecmp(elts[i].key, key)) { if (!done) { elts[i].val = apr_pstrdup(t->a.pool, val); done = 1; ++i; } else { /* delete an extraneous element */ for (j = i, k = i + 1; k < t->a.nelts; ++j, ++k) { elts[j].key = elts[k].key; elts[j].val = elts[k].val; } --t->a.nelts; } } else { ++i; } } if (!done) { elts = (apr_table_entry_t *) table_push(t); elts->key = apr_pstrdup(t->a.pool, key); elts->val = apr_pstrdup(t->a.pool, val); } } API_EXPORT(void) apr_table_setn(apr_table_t *t, const char *key, const char *val) { register int i, j, k; apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; int done = 0; #ifdef POOL_DEBUG { if (!apr_pool_is_ancestor(apr_find_pool(key), t->a.pool)) { fprintf(stderr, "table_set: key not in ancestor pool of t\n"); abort(); } if (!apr_pool_is_ancestor(apr_find_pool(val), t->a.pool)) { fprintf(stderr, "table_set: val not in ancestor pool of t\n"); abort(); } } #endif for (i = 0; i < t->a.nelts; ) { if (!strcasecmp(elts[i].key, key)) { if (!done) { elts[i].val = (char *)val; done = 1; ++i; } else { /* delete an extraneous element */ for (j = i, k = i + 1; k < t->a.nelts; ++j, ++k) { elts[j].key = elts[k].key; elts[j].val = elts[k].val; } --t->a.nelts; } } else { ++i; } } if (!done) { elts = (apr_table_entry_t *) table_push(t); elts->key = (char *)key; elts->val = (char *)val; } } API_EXPORT(void) apr_table_unset(apr_table_t *t, const char *key) { register int i, j, k; apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; for (i = 0; i < t->a.nelts; ) { if (!strcasecmp(elts[i].key, key)) { /* found an element to skip over * there are any number of ways to remove an element from * a contiguous block of memory. I've chosen one that * doesn't do a memcpy/bcopy/array_delete, *shrug*... */ for (j = i, k = i + 1; k < t->a.nelts; ++j, ++k) { elts[j].key = elts[k].key; elts[j].val = elts[k].val; } --t->a.nelts; } else { ++i; } } } API_EXPORT(void) apr_table_merge(apr_table_t *t, const char *key, const char *val) { apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; int i; for (i = 0; i < t->a.nelts; ++i) { if (!strcasecmp(elts[i].key, key)) { elts[i].val = apr_pstrcat(t->a.pool, elts[i].val, ", ", val, NULL); return; } } elts = (apr_table_entry_t *) table_push(t); elts->key = apr_pstrdup(t->a.pool, key); elts->val = apr_pstrdup(t->a.pool, val); } API_EXPORT(void) apr_table_mergen(apr_table_t *t, const char *key, const char *val) { apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; int i; #ifdef POOL_DEBUG { if (!apr_pool_is_ancestor(apr_find_pool(key), t->a.pool)) { fprintf(stderr, "table_set: key not in ancestor pool of t\n"); abort(); } if (!apr_pool_is_ancestor(apr_find_pool(val), t->a.pool)) { fprintf(stderr, "table_set: key not in ancestor pool of t\n"); abort(); } } #endif for (i = 0; i < t->a.nelts; ++i) { if (!strcasecmp(elts[i].key, key)) { elts[i].val = apr_pstrcat(t->a.pool, elts[i].val, ", ", val, NULL); return; } } elts = (apr_table_entry_t *) table_push(t); elts->key = (char *)key; elts->val = (char *)val; } API_EXPORT(void) apr_table_add(apr_table_t *t, const char *key, const char *val) { apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; elts = (apr_table_entry_t *) table_push(t); elts->key = apr_pstrdup(t->a.pool, key); elts->val = apr_pstrdup(t->a.pool, val); } API_EXPORT(void) apr_table_addn(apr_table_t *t, const char *key, const char *val) { apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; #ifdef POOL_DEBUG { if (!apr_pool_is_ancestor(apr_find_pool(key), t->a.pool)) { fprintf(stderr, "table_set: key not in ancestor pool of t\n"); abort(); } if (!apr_pool_is_ancestor(apr_find_pool(val), t->a.pool)) { fprintf(stderr, "table_set: key not in ancestor pool of t\n"); abort(); } } #endif elts = (apr_table_entry_t *) table_push(t); elts->key = (char *)key; elts->val = (char *)val; } API_EXPORT(apr_table_t *) apr_overlay_tables(apr_pool_t *p, const apr_table_t *overlay, const apr_table_t *base) { apr_table_t *res; #ifdef POOL_DEBUG /* we don't copy keys and values, so it's necessary that * overlay->a.pool and base->a.pool have a life span at least * as long as p */ if (!apr_pool_is_ancestor(overlay->a.pool, p)) { fprintf(stderr, "overlay_tables: overlay's pool is not an ancestor of p\n"); abort(); } if (!apr_pool_is_ancestor(base->a.pool, p)) { fprintf(stderr, "overlay_tables: base's pool is not an ancestor of p\n"); abort(); } #endif res = apr_palloc(p, sizeof(apr_table_t)); /* behave like append_arrays */ res->a.pool = p; copy_array_hdr_core(&res->a, &overlay->a); apr_array_cat(&res->a, &base->a); return res; } /* And now for something completely abstract ... * For each key value given as a vararg: * run the function pointed to as * int comp(void *r, char *key, char *value); * on each valid key-value pair in the table t that matches the vararg key, * or once for every valid key-value pair if the vararg list is empty, * until the function returns false (0) or we finish the table. * * Note that we restart the traversal for each vararg, which means that * duplicate varargs will result in multiple executions of the function * for each matching key. Note also that if the vararg list is empty, * only one traversal will be made and will cut short if comp returns 0. * * Note that the table_get and table_merge functions assume that each key in * the table is unique (i.e., no multiple entries with the same key). This * function does not make that assumption, since it (unfortunately) isn't * true for some of Apache's tables. * * Note that rec is simply passed-on to the comp function, so that the * caller can pass additional info for the task. */ API_EXPORT(void) apr_table_do(int (*comp) (void *, const char *, const char *), void *rec, const apr_table_t *t, ...) { va_list vp; char *argp; apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; int rv, i; va_start(vp, t); argp = va_arg(vp, char *); do { for (rv = 1, i = 0; rv && (i < t->a.nelts); ++i) { if (elts[i].key && (!argp || !strcasecmp(elts[i].key, argp))) { rv = (*comp) (rec, elts[i].key, elts[i].val); } } } while (argp && ((argp = va_arg(vp, char *)) != NULL)); va_end(vp); } /* Curse libc and the fact that it doesn't guarantee a stable sort. We * have to enforce stability ourselves by using the order field. If it * provided a stable sort then we wouldn't even need temporary storage to * do the work below. -djg * * ("stable sort" means that equal keys retain their original relative * ordering in the output.) */ typedef struct { char *key; char *val; int order; } overlap_key; static int sort_overlap(const void *va, const void *vb) { const overlap_key *a = va; const overlap_key *b = vb; int r; r = strcasecmp(a->key, b->key); if (r) { return r; } return a->order - b->order; } /* prefer to use the stack for temp storage for overlaps smaller than this */ #ifndef APR_OVERLAP_TABLES_ON_STACK #define APR_OVERLAP_TABLES_ON_STACK (512) #endif API_EXPORT(void) apr_overlap_tables(apr_table_t *a, const apr_table_t *b, unsigned flags) { overlap_key cat_keys_buf[APR_OVERLAP_TABLES_ON_STACK]; overlap_key *cat_keys; int nkeys; apr_table_entry_t *e; apr_table_entry_t *last_e; overlap_key *left; overlap_key *right; overlap_key *last; nkeys = a->a.nelts + b->a.nelts; if (nkeys < APR_OVERLAP_TABLES_ON_STACK) { cat_keys = cat_keys_buf; } else { /* XXX: could use scratch free space in a or b's pool instead... * which could save an allocation in b's pool. */ cat_keys = apr_palloc(b->a.pool, sizeof(overlap_key) * nkeys); } nkeys = 0; /* Create a list of the entries from a concatenated with the entries * from b. */ e = (apr_table_entry_t *)a->a.elts; last_e = e + a->a.nelts; while (e < last_e) { cat_keys[nkeys].key = e->key; cat_keys[nkeys].val = e->val; cat_keys[nkeys].order = nkeys; ++nkeys; ++e; } e = (apr_table_entry_t *)b->a.elts; last_e = e + b->a.nelts; while (e < last_e) { cat_keys[nkeys].key = e->key; cat_keys[nkeys].val = e->val; cat_keys[nkeys].order = nkeys; ++nkeys; ++e; } qsort(cat_keys, nkeys, sizeof(overlap_key), sort_overlap); /* Now iterate over the sorted list and rebuild a. * Start by making sure it has enough space. */ a->a.nelts = 0; if (a->a.nalloc < nkeys) { a->a.elts = apr_palloc(a->a.pool, a->a.elt_size * nkeys * 2); a->a.nalloc = nkeys * 2; } /* * In both the merge and set cases we retain the invariant: * * left->key, (left+1)->key, (left+2)->key, ..., (right-1)->key * are all equal keys. (i.e. strcasecmp returns 0) * * We essentially need to find the maximal * right for each key, then we can do a quick merge or set as * appropriate. */ if (flags & APR_OVERLAP_TABLES_MERGE) { left = cat_keys; last = left + nkeys; while (left < last) { right = left + 1; if (right == last || strcasecmp(left->key, right->key)) { apr_table_addn(a, left->key, left->val); left = right; } else { char *strp; char *value; size_t len; /* Have to merge some headers. Let's re-use the order field, * since it's handy... we'll store the length of val there. */ left->order = strlen(left->val); len = left->order; do { right->order = strlen(right->val); len += 2 + right->order; ++right; } while (right < last && !strcasecmp(left->key, right->key)); /* right points one past the last header to merge */ value = apr_palloc(a->a.pool, len + 1); strp = value; for (;;) { memcpy(strp, left->val, left->order); strp += left->order; ++left; if (left == right) { break; } *strp++ = ','; *strp++ = ' '; } *strp = 0; apr_table_addn(a, (left-1)->key, value); } } } else { left = cat_keys; last = left + nkeys; while (left < last) { right = left + 1; while (right < last && !strcasecmp(left->key, right->key)) { ++right; } apr_table_addn(a, (right-1)->key, (right-1)->val); left = right; } } } /***************************************************************** * * Managing generic cleanups. */ struct cleanup { void *data; void (*plain_cleanup) (void *); void (*child_cleanup) (void *); struct cleanup *next; }; API_EXPORT(void) apr_register_cleanup(apr_pool_t *p, void *data, void (*plain_cleanup) (void *), void (*child_cleanup) (void *)) { struct cleanup *c; c = (struct cleanup *) apr_palloc(p, sizeof(struct cleanup)); c->data = data; c->plain_cleanup = plain_cleanup; c->child_cleanup = child_cleanup; c->next = p->cleanups; p->cleanups = c; } API_EXPORT(void) apr_kill_cleanup(apr_pool_t *p, void *data, void (*cleanup) (void *)) { struct cleanup *c = p->cleanups; struct cleanup **lastp = &p->cleanups; while (c) { if (c->data == data && c->plain_cleanup == cleanup) { *lastp = c->next; break; } lastp = &c->next; c = c->next; } } API_EXPORT(void) apr_run_cleanup(apr_pool_t *p, void *data, void (*cleanup) (void *)) { apr_block_alarms(); /* Run cleanup only once! */ (*cleanup) (data); apr_kill_cleanup(p, data, cleanup); apr_unblock_alarms(); } static void run_cleanups(struct cleanup *c) { while (c) { (*c->plain_cleanup) (c->data); c = c->next; } } static void run_child_cleanups(struct cleanup *c) { while (c) { (*c->child_cleanup) (c->data); c = c->next; } } static void cleanup_pool_for_exec(apr_pool_t *p) { run_child_cleanups(p->cleanups); p->cleanups = NULL; for (p = p->sub_pools; p; p = p->sub_next) { cleanup_pool_for_exec(p); } } API_EXPORT(void) apr_cleanup_for_exec(void) { #if !defined(WIN32) && !defined(OS2) /* * Don't need to do anything on NT or OS/2, because I * am actually going to spawn the new process - not * exec it. All handles that are not inheritable, will * be automajically closed. The only problem is with * file handles that are open, but there isn't much * I can do about that (except if the child decides * to go out and close them */ apr_block_alarms(); cleanup_pool_for_exec(permanent_pool); apr_unblock_alarms(); #endif /* ndef WIN32 */ } API_EXPORT_NONSTD(void) apr_null_cleanup(void *data) { /* do nothing cleanup routine */ } /***************************************************************** * * Files and file descriptors; these are just an application of the * generic cleanup interface. */ static void fd_cleanup(void *fdv) { close((int) (long) fdv); } API_EXPORT(void) apr_note_cleanups_for_fd(apr_pool_t *p, int fd) { apr_register_cleanup(p, (void *) (long) fd, fd_cleanup, fd_cleanup); } API_EXPORT(void) apr_kill_cleanups_for_fd(apr_pool_t *p, int fd) { apr_kill_cleanup(p, (void *) (long) fd, fd_cleanup); } API_EXPORT(int) apr_popenf(apr_pool_t *a, const char *name, int flg, int mode) { int fd; int save_errno; apr_block_alarms(); fd = open(name, flg, mode); save_errno = errno; if (fd >= 0) { fd = apr_slack(fd, APR_SLACK_HIGH); apr_note_cleanups_for_fd(a, fd); } apr_unblock_alarms(); errno = save_errno; return fd; } API_EXPORT(int) apr_pclosef(apr_pool_t *a, int fd) { int res; int save_errno; apr_block_alarms(); res = close(fd); save_errno = errno; apr_kill_cleanup(a, (void *) (long) fd, fd_cleanup); apr_unblock_alarms(); errno = save_errno; return res; } #ifdef WIN32 static void h_cleanup(void *fdv) { CloseHandle((HANDLE) fdv); } API_EXPORT(void) apr_note_cleanups_for_h(apr_pool_t *p, HANDLE hDevice) { apr_register_cleanup(p, (void *) hDevice, h_cleanup, h_cleanup); } API_EXPORT(int) apr_pcloseh(apr_pool_t *a, HANDLE hDevice) { int res=0; int save_errno; apr_block_alarms(); if (!CloseHandle(hDevice)) { res = GetLastError(); } save_errno = errno; apr_kill_cleanup(a, (void *) hDevice, h_cleanup); apr_unblock_alarms(); errno = save_errno; return res; } #endif /* Note that we have separate plain_ and child_ cleanups for FILE *s, * since fclose() would flush I/O buffers, which is extremely undesirable; * we just close the descriptor. */ static void file_cleanup(void *fpv) { fclose((FILE *) fpv); } static void file_child_cleanup(void *fpv) { close(fileno((FILE *) fpv)); } API_EXPORT(void) apr_note_cleanups_for_file(apr_pool_t *p, FILE *fp) { apr_register_cleanup(p, (void *) fp, file_cleanup, file_child_cleanup); } API_EXPORT(FILE *) apr_pfopen(apr_pool_t *a, const char *name, const char *mode) { FILE *fd = NULL; int baseFlag, desc; int modeFlags = 0; int saved_errno; #ifdef WIN32 modeFlags = _S_IREAD | _S_IWRITE; #else /* WIN32 */ modeFlags = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; #endif /* WIN32 */ apr_block_alarms(); if (*mode == 'a') { /* Work around faulty implementations of fopen */ baseFlag = (*(mode + 1) == '+') ? O_RDWR : O_WRONLY; desc = open(name, baseFlag | O_APPEND | O_CREAT, modeFlags); if (desc >= 0) { desc = apr_slack(desc, APR_SLACK_LOW); fd = apr_fdopen(desc, mode); } } else { fd = fopen(name, mode); } saved_errno = errno; if (fd != NULL) { apr_note_cleanups_for_file(a, fd); } apr_unblock_alarms(); errno = saved_errno; return fd; } API_EXPORT(FILE *) apr_pfdopen(apr_pool_t *a, int fd, const char *mode) { FILE *f; int saved_errno; apr_block_alarms(); f = apr_fdopen(fd, mode); saved_errno = errno; if (f != NULL) { apr_note_cleanups_for_file(a, f); } apr_unblock_alarms(); errno = saved_errno; return f; } API_EXPORT(int) apr_pfclose(apr_pool_t *a, FILE *fd) { int res; apr_block_alarms(); res = fclose(fd); apr_kill_cleanup(a, (void *) fd, file_cleanup); apr_unblock_alarms(); return res; } /* * DIR * with cleanup */ static void dir_cleanup(void *dv) { closedir((DIR *) dv); } API_EXPORT(DIR *) apr_popendir(apr_pool_t *p, const char *name) { DIR *d; int save_errno; apr_block_alarms(); d = opendir(name); if (d == NULL) { save_errno = errno; apr_unblock_alarms(); errno = save_errno; return NULL; } apr_register_cleanup(p, (void *) d, dir_cleanup, dir_cleanup); apr_unblock_alarms(); return d; } API_EXPORT(void) apr_pclosedir(apr_pool_t *p, DIR * d) { apr_block_alarms(); apr_kill_cleanup(p, (void *) d, dir_cleanup); closedir(d); apr_unblock_alarms(); } /***************************************************************** * * Files and file descriptors; these are just an application of the * generic cleanup interface. */ static void socket_cleanup(void *fdv) { closesocket((int) (long) fdv); } API_EXPORT(void) apr_note_cleanups_for_socket(apr_pool_t *p, int fd) { apr_register_cleanup(p, (void *) (long) fd, socket_cleanup, socket_cleanup); } API_EXPORT(void) apr_kill_cleanups_for_socket(apr_pool_t *p, int sock) { apr_kill_cleanup(p, (void *) (long) sock, socket_cleanup); } API_EXPORT(int) apr_psocket(apr_pool_t *p, int domain, int type, int protocol) { int fd; apr_block_alarms(); fd = socket(domain, type, protocol); if (fd == -1) { int save_errno = errno; apr_unblock_alarms(); errno = save_errno; return -1; } apr_note_cleanups_for_socket(p, fd); apr_unblock_alarms(); return fd; } API_EXPORT(int) apr_pclosesocket(apr_pool_t *a, int sock) { int res; int save_errno; apr_block_alarms(); res = closesocket(sock); #ifdef WIN32 errno = WSAGetLastError(); #endif /* WIN32 */ save_errno = errno; apr_kill_cleanup(a, (void *) (long) sock, socket_cleanup); apr_unblock_alarms(); errno = save_errno; return res; } /* * Here's a pool-based interface to POSIX regex's regcomp(). * Note that we return regex_t instead of being passed one. * The reason is that if you use an already-used regex_t structure, * the memory that you've already allocated gets forgotten, and * regfree() doesn't clear it. So we don't allow it. */ static void regex_cleanup(void *preg) { regfree((regex_t *) preg); } API_EXPORT(regex_t *) apr_pregcomp(apr_pool_t *p, const char *pattern, int cflags) { regex_t *preg = apr_palloc(p, sizeof(regex_t)); if (regcomp(preg, pattern, cflags)) { return NULL; } apr_register_cleanup(p, (void *) preg, regex_cleanup, regex_cleanup); return preg; } API_EXPORT(void) apr_pregfree(apr_pool_t *p, regex_t * reg) { apr_block_alarms(); regfree(reg); apr_kill_cleanup(p, (void *) reg, regex_cleanup); apr_unblock_alarms(); } /***************************************************************** * * More grotty system stuff... subprocesses. Frump. These don't use * the generic cleanup interface because I don't want multiple * subprocesses to result in multiple three-second pauses; the * subprocesses have to be "freed" all at once. If someone comes * along with another resource they want to allocate which has the * same property, we might want to fold support for that into the * generic interface, but for now, it's a special case */ API_EXPORT(void) apr_note_subprocess(apr_pool_t *a, pid_t pid, enum kill_conditions how) { struct process_chain *new = (struct process_chain *) apr_palloc(a, sizeof(struct process_chain)); new->pid = pid; new->kill_how = how; new->next = a->subprocesses; a->subprocesses = new; } #ifdef WIN32 #define os_pipe(fds) _pipe(fds, 512, O_BINARY | O_NOINHERIT) #else #define os_pipe(fds) pipe(fds) #endif /* WIN32 */ /* for apr_fdopen, to get binary mode */ #if defined (OS2) || defined (WIN32) #define BINMODE "b" #else #define BINMODE #endif static pid_t spawn_child_core(apr_pool_t *p, int (*func) (void *, apr_child_info_t *), void *data,enum kill_conditions kill_how, int *pipe_in, int *pipe_out, int *pipe_err) { pid_t pid; int in_fds[2]; int out_fds[2]; int err_fds[2]; int save_errno; if (pipe_in && os_pipe(in_fds) < 0) { return 0; } if (pipe_out && os_pipe(out_fds) < 0) { save_errno = errno; if (pipe_in) { close(in_fds[0]); close(in_fds[1]); } errno = save_errno; return 0; } if (pipe_err && os_pipe(err_fds) < 0) { save_errno = errno; if (pipe_in) { close(in_fds[0]); close(in_fds[1]); } if (pipe_out) { close(out_fds[0]); close(out_fds[1]); } errno = save_errno; return 0; } #ifdef WIN32 { HANDLE thread_handle; int hStdIn, hStdOut, hStdErr; int old_priority; apr_child_info_t info; (void) apr_acquire_mutex(spawn_mutex); thread_handle = GetCurrentThread(); /* doesn't need to be closed */ old_priority = GetThreadPriority(thread_handle); SetThreadPriority(thread_handle, THREAD_PRIORITY_HIGHEST); /* Now do the right thing with your pipes */ if (pipe_in) { hStdIn = dup(fileno(stdin)); if (dup2(in_fds[0], fileno(stdin))) { apr_log_error(APLOG_MARK, APLOG_ERR, NULL, "dup2(stdin) failed"); } close(in_fds[0]); } if (pipe_out) { hStdOut = dup(fileno(stdout)); close(fileno(stdout)); if (dup2(out_fds[1], fileno(stdout))) { apr_log_error(APLOG_MARK, APLOG_ERR, NULL, "dup2(stdout) failed"); } close(out_fds[1]); } if (pipe_err) { hStdErr = dup(fileno(stderr)); if (dup2(err_fds[1], fileno(stderr))) { apr_log_error(APLOG_MARK, APLOG_ERR, NULL, "dup2(stdin) failed"); } close(err_fds[1]); } info.hPipeInputRead = GetStdHandle(STD_INPUT_HANDLE); info.hPipeOutputWrite = GetStdHandle(STD_OUTPUT_HANDLE); info.hPipeErrorWrite = GetStdHandle(STD_ERROR_HANDLE); pid = (*func) (data, &info); if (pid == -1) { pid = 0; /* map Win32 error code onto Unix default */ } if (!pid) { save_errno = errno; close(in_fds[1]); close(out_fds[0]); close(err_fds[0]); } /* restore the original stdin, stdout and stderr */ if (pipe_in) { dup2(hStdIn, fileno(stdin)); close(hStdIn); } if (pipe_out) { dup2(hStdOut, fileno(stdout)); close(hStdOut); } if (pipe_err) { dup2(hStdErr, fileno(stderr)); close(hStdErr); } if (pid) { apr_note_subprocess(p, pid, kill_how); if (pipe_in) { *pipe_in = in_fds[1]; } if (pipe_out) { *pipe_out = out_fds[0]; } if (pipe_err) { *pipe_err = err_fds[0]; } } SetThreadPriority(thread_handle, old_priority); (void) apr_release_mutex(spawn_mutex); /* * go on to the end of the function, where you can * unblock alarms and return the pid */ } #elif defined(OS2) { int save_in=-1, save_out=-1, save_err=-1; if (pipe_out) { save_out = dup(STDOUT_FILENO); dup2(out_fds[1], STDOUT_FILENO); close(out_fds[1]); } if (pipe_in) { save_in = dup(STDIN_FILENO); dup2(in_fds[0], STDIN_FILENO); close(in_fds[0]); } if (pipe_err) { save_err = dup(STDERR_FILENO); dup2(err_fds[1], STDERR_FILENO); close(err_fds[1]); } pid = func(data, NULL); if (pid) { apr_note_subprocess(p, pid, kill_how); } if (pipe_out) { close(STDOUT_FILENO); dup2(save_out, STDOUT_FILENO); close(save_out); *pipe_out = out_fds[0]; } if (pipe_in) { close(STDIN_FILENO); dup2(save_in, STDIN_FILENO); close(save_in); *pipe_in = in_fds[1]; } if (pipe_err) { close(STDERR_FILENO); dup2(save_err, STDERR_FILENO); close(save_err); *pipe_err = err_fds[0]; } } #elif defined(TPF) return (pid = apr_tpf_spawn_child(p, func, data, kill_how, pipe_in, pipe_out, pipe_err, out_fds, in_fds, err_fds)); #else if ((pid = fork()) < 0) { save_errno = errno; if (pipe_in) { close(in_fds[0]); close(in_fds[1]); } if (pipe_out) { close(out_fds[0]); close(out_fds[1]); } if (pipe_err) { close(err_fds[0]); close(err_fds[1]); } errno = save_errno; return 0; } if (!pid) { /* Child process */ RAISE_SIGSTOP(SPAWN_CHILD); if (pipe_out) { close(out_fds[0]); dup2(out_fds[1], STDOUT_FILENO); close(out_fds[1]); } if (pipe_in) { close(in_fds[1]); dup2(in_fds[0], STDIN_FILENO); close(in_fds[0]); } if (pipe_err) { close(err_fds[0]); dup2(err_fds[1], STDERR_FILENO); close(err_fds[1]); } /* * HP-UX SIGCHLD fix goes here, if someone will remind me * what it is... */ signal(SIGCHLD, SIG_DFL); /* Was that it? */ func(data, NULL); exit(1); /* Should only get here if the exec in func() failed */ } /* Parent process */ apr_note_subprocess(p, pid, kill_how); if (pipe_out) { close(out_fds[1]); *pipe_out = out_fds[0]; } if (pipe_in) { close(in_fds[0]); *pipe_in = in_fds[1]; } if (pipe_err) { close(err_fds[1]); *pipe_err = err_fds[0]; } #endif /* WIN32 */ return pid; } API_EXPORT(int) apr_spawn_child(apr_pool_t *p, int (*func) (void *v, apr_child_info_t *c), void *data, enum kill_conditions kill_how, FILE **pipe_in, FILE **pipe_out, FILE **pipe_err) { int fd_in, fd_out, fd_err; pid_t pid; int save_errno; apr_block_alarms(); pid = spawn_child_core(p, func, data, kill_how, pipe_in ? &fd_in : NULL, pipe_out ? &fd_out : NULL, pipe_err ? &fd_err : NULL); if (pid == 0) { save_errno = errno; apr_unblock_alarms(); errno = save_errno; return 0; } if (pipe_out) { *pipe_out = apr_fdopen(fd_out, "r" BINMODE); if (*pipe_out) { apr_note_cleanups_for_file(p, *pipe_out); } else { close(fd_out); } } if (pipe_in) { *pipe_in = apr_fdopen(fd_in, "w" BINMODE); if (*pipe_in) { apr_note_cleanups_for_file(p, *pipe_in); } else { close(fd_in); } } if (pipe_err) { *pipe_err = apr_fdopen(fd_err, "r" BINMODE); if (*pipe_err) { apr_note_cleanups_for_file(p, *pipe_err); } else { close(fd_err); } } apr_unblock_alarms(); return pid; } #if 0 API_EXPORT(int) apr_bspawn_child(apr_pool_t *p, int (*func) (void *v, apr_child_info_t *c), void *data, enum kill_conditions kill_how, BUFF **pipe_in, BUFF **pipe_out, BUFF **pipe_err) { #ifdef WIN32 SECURITY_ATTRIBUTES sa = {0}; HANDLE hPipeOutputRead = NULL; HANDLE hPipeOutputWrite = NULL; HANDLE hPipeInputRead = NULL; HANDLE hPipeInputWrite = NULL; HANDLE hPipeErrorRead = NULL; HANDLE hPipeErrorWrite = NULL; HANDLE hPipeInputWriteDup = NULL; HANDLE hPipeOutputReadDup = NULL; HANDLE hPipeErrorReadDup = NULL; HANDLE hCurrentProcess; pid_t pid = 0; apr_child_info_t info; apr_block_alarms(); /* * First thing to do is to create the pipes that we will use * for stdin, stdout, and stderr in the child process. */ sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; /* Create pipes for standard input/output/error redirection. */ if (pipe_in && !CreatePipe(&hPipeInputRead, &hPipeInputWrite, &sa, 0)) { return 0; } if (pipe_out && !CreatePipe(&hPipeOutputRead, &hPipeOutputWrite, &sa, 0)) { if (pipe_in) { CloseHandle(hPipeInputRead); CloseHandle(hPipeInputWrite); } return 0; } if (pipe_err && !CreatePipe(&hPipeErrorRead, &hPipeErrorWrite, &sa, 0)) { if (pipe_in) { CloseHandle(hPipeInputRead); CloseHandle(hPipeInputWrite); } if (pipe_out) { CloseHandle(hPipeOutputRead); CloseHandle(hPipeOutputWrite); } return 0; } /* * When the pipe handles are created, the security descriptor * indicates that the handle can be inherited. However, we do not * want the server side handles to the pipe to be inherited by the * child CGI process. If the child CGI does inherit the server * side handles, then the child may be left around if the server * closes its handles (e.g. if the http connection is aborted), * because the child will have a valid copy of handles to both * sides of the pipes, and no I/O error will occur. Microsoft * recommends using DuplicateHandle to turn off the inherit bit * under NT and Win95. */ hCurrentProcess = GetCurrentProcess(); if ((pipe_in && !DuplicateHandle(hCurrentProcess, hPipeInputWrite, hCurrentProcess, &hPipeInputWriteDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) || (pipe_out && !DuplicateHandle(hCurrentProcess, hPipeOutputRead, hCurrentProcess, &hPipeOutputReadDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) || (pipe_err && !DuplicateHandle(hCurrentProcess, hPipeErrorRead, hCurrentProcess, &hPipeErrorReadDup, 0, FALSE, DUPLICATE_SAME_ACCESS))) { if (pipe_in) { CloseHandle(hPipeInputRead); CloseHandle(hPipeInputWrite); } if (pipe_out) { CloseHandle(hPipeOutputRead); CloseHandle(hPipeOutputWrite); } if (pipe_err) { CloseHandle(hPipeErrorRead); CloseHandle(hPipeErrorWrite); } return 0; } else { if (pipe_in) { CloseHandle(hPipeInputWrite); hPipeInputWrite = hPipeInputWriteDup; } if (pipe_out) { CloseHandle(hPipeOutputRead); hPipeOutputRead = hPipeOutputReadDup; } if (pipe_err) { CloseHandle(hPipeErrorRead); hPipeErrorRead = hPipeErrorReadDup; } } /* The script writes stdout to this pipe handle */ info.hPipeOutputWrite = hPipeOutputWrite; /* The script reads stdin from this pipe handle */ info.hPipeInputRead = hPipeInputRead; /* The script writes stderr to this pipe handle */ info.hPipeErrorWrite = hPipeErrorWrite; /* * Try to launch the CGI. Under the covers, this call * will try to pick up the appropriate interpreter if * one is needed. */ pid = func(data, &info); if (pid == -1) { /* Things didn't work, so cleanup */ pid = 0; /* map Win32 error code onto Unix default */ CloseHandle(hPipeOutputRead); CloseHandle(hPipeInputWrite); CloseHandle(hPipeErrorRead); } else { if (pipe_out) { /* * This pipe represents stdout for the script, * so we read from this pipe. */ /* Create a read buffer */ *pipe_out = apr_bcreate(p, B_RD); /* Setup the cleanup routine for the handle */ apr_note_cleanups_for_h(p, hPipeOutputRead); /* Associate the handle with the new buffer */ apr_bpushh(*pipe_out, hPipeOutputRead); } if (pipe_in) { /* * This pipe represents stdin for the script, so we * write to this pipe. */ /* Create a write buffer */ *pipe_in = apr_bcreate(p, B_WR); /* Setup the cleanup routine for the handle */ apr_note_cleanups_for_h(p, hPipeInputWrite); /* Associate the handle with the new buffer */ apr_bpushh(*pipe_in, hPipeInputWrite); } if (pipe_err) { /* * This pipe represents stderr for the script, so * we read from this pipe. */ /* Create a read buffer */ *pipe_err = apr_bcreate(p, B_RD); /* Setup the cleanup routine for the handle */ apr_note_cleanups_for_h(p, hPipeErrorRead); /* Associate the handle with the new buffer */ apr_bpushh(*pipe_err, hPipeErrorRead); } } /* * Now that handles have been inherited, close them to be safe. * You don't want to read or write to them accidentally, and we * sure don't want to have a handle leak. */ CloseHandle(hPipeOutputWrite); CloseHandle(hPipeInputRead); CloseHandle(hPipeErrorWrite); #else int fd_in, fd_out, fd_err; pid_t pid; int save_errno; apr_block_alarms(); pid = spawn_child_core(p, func, data, kill_how, pipe_in ? &fd_in : NULL, pipe_out ? &fd_out : NULL, pipe_err ? &fd_err : NULL); if (pid == 0) { save_errno = errno; apr_unblock_alarms(); errno = save_errno; return 0; } if (pipe_out) { *pipe_out = apr_bcreate(p, B_RD); apr_note_cleanups_for_fd(p, fd_out); apr_bpushfd(*pipe_out, fd_out, fd_out); } if (pipe_in) { *pipe_in = apr_bcreate(p, B_WR); apr_note_cleanups_for_fd(p, fd_in); apr_bpushfd(*pipe_in, fd_in, fd_in); } if (pipe_err) { *pipe_err = apr_bcreate(p, B_RD); apr_note_cleanups_for_fd(p, fd_err); apr_bpushfd(*pipe_err, fd_err, fd_err); } #endif apr_unblock_alarms(); return pid; } #endif static void free_proc_chain(struct process_chain *procs) { /* Dispose of the subprocesses we've spawned off in the course of * whatever it was we're cleaning up now. This may involve killing * some of them off... */ struct process_chain *p; int need_timeout = 0; int status; if (procs == NULL) { return; /* No work. Whew! */ } /* First, check to see if we need to do the SIGTERM, sleep, SIGKILL * dance with any of the processes we're cleaning up. If we've got * any kill-on-sight subprocesses, ditch them now as well, so they * don't waste any more cycles doing whatever it is that they shouldn't * be doing anymore. */ #ifdef WIN32 /* Pick up all defunct processes */ for (p = procs; p; p = p->next) { if (GetExitCodeProcess((HANDLE) p->pid, &status)) { p->kill_how = kill_never; } } for (p = procs; p; p = p->next) { if (p->kill_how == kill_after_timeout) { need_timeout = 1; } else if (p->kill_how == kill_always) { TerminateProcess((HANDLE) p->pid, 1); } } /* Sleep only if we have to... */ if (need_timeout) { sleep(3); } /* OK, the scripts we just timed out for have had a chance to clean up * --- now, just get rid of them, and also clean up the system accounting * goop... */ for (p = procs; p; p = p->next) { if (p->kill_how == kill_after_timeout) { TerminateProcess((HANDLE) p->pid, 1); } } for (p = procs; p; p = p->next) { CloseHandle((HANDLE) p->pid); } #else #ifndef NEED_WAITPID /* Pick up all defunct processes */ for (p = procs; p; p = p->next) { if (waitpid(p->pid, (int *) 0, WNOHANG) > 0) { p->kill_how = kill_never; } } #endif for (p = procs; p; p = p->next) { if ((p->kill_how == kill_after_timeout) || (p->kill_how == kill_only_once)) { /* * Subprocess may be dead already. Only need the timeout if not. */ if (kill(p->pid, SIGTERM) != -1) { need_timeout = 1; } } else if (p->kill_how == kill_always) { kill(p->pid, SIGKILL); } } /* Sleep only if we have to... */ if (need_timeout) { sleep(3); } /* OK, the scripts we just timed out for have had a chance to clean up * --- now, just get rid of them, and also clean up the system accounting * goop... */ for (p = procs; p; p = p->next) { if (p->kill_how == kill_after_timeout) { kill(p->pid, SIGKILL); } if (p->kill_how != kill_never) { waitpid(p->pid, &status, 0); } } #endif /* WIN32 */ }