home *** CD-ROM | disk | FTP | other *** search
- /* ====================================================================
- * Copyright (c) 1995-1997 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.
- *
- * 5. 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 "httpd.h"
-
- #include <stdarg.h>
-
- /*****************************************************************
- *
- * Managing free storage blocks...
- */
-
- union align
- {
- /* Types which are likely to have the longest RELEVANT alignment
- * restrictions...
- */
-
- char *cp;
- void (*f)();
- 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;
- } h;
- };
-
- union block_hdr *block_freelist = NULL;
-
-
-
- /* Get a completely new block from the system pool. Note that we rely on
- malloc() to provide aligned memory. */
-
- union block_hdr *malloc_block (int size)
- {
- union block_hdr *blok =
- (union block_hdr *)malloc(size + sizeof(union block_hdr));
-
- if (blok == NULL) {
- fprintf (stderr, "Ouch! malloc failed in malloc_block()\n");
- exit (1);
- }
- blok->h.next = NULL;
- blok->h.first_avail = (char *)(blok + 1);
- blok->h.endp = size + blok->h.first_avail;
-
- return blok;
- }
-
-
-
- void chk_on_blk_list (union block_hdr *blok, union block_hdr *free_blk)
- {
- /* Debugging code. Left in for the moment. */
-
- while (free_blk) {
- if (free_blk == blok) {
- fprintf (stderr, "Ouch! Freeing free block\n");
- exit (1);
- }
- free_blk = free_blk->h.next;
- }
- }
-
- /* Free a chain of blocks --- must be called with alarms blocked. */
-
- void free_blocks (union block_hdr *blok)
- {
- /* 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 = block_freelist;
-
- if (blok == NULL) return; /* Sanity check --- freeing empty pool? */
-
- 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.
- */
-
- while (blok->h.next != NULL) {
- chk_on_blk_list (blok, old_free_list);
- blok->h.first_avail = (char *)(blok + 1);
- blok = blok->h.next;
- }
-
- chk_on_blk_list (blok, old_free_list);
- blok->h.first_avail = (char *)(blok + 1);
-
- /* Finally, reset next pointer to get the old free blocks back */
-
- blok->h.next = old_free_list;
- }
-
-
-
-
- /* Get a new block, from our own free list if possible, from the system
- * if necessary. Must be called with alarms blocked.
- */
-
- 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;
- return blok;
- }
- else {
- lastptr = &blok->h.next;
- blok = blok->h.next;
- }
- }
-
- /* Nope. */
-
- min_size += BLOCK_MINFREE;
- return malloc_block((min_size > BLOCK_MINALLOC) ? min_size : BLOCK_MINALLOC);
- }
-
-
-
- /* Accounting */
-
- 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 *);
- static void free_proc_chain (struct process_chain *);
-
- struct pool {
- union block_hdr *first;
- union block_hdr *last;
- struct cleanup *cleanups;
- struct process_chain *subprocesses;
- struct pool *sub_pools;
- struct pool *sub_next;
- struct pool *sub_prev;
- struct pool *parent;
- char *free_first_avail;
- };
-
- pool *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 pool) - 1) / CLICK_SZ))
- #define POOL_HDR_BYTES (POOL_HDR_CLICKS * CLICK_SZ)
-
- struct pool *make_sub_pool (struct pool *p)
- {
- union block_hdr *blok;
- pool *new_pool;
-
- block_alarms();
-
- blok = new_block (0);
- new_pool = (pool *)blok->h.first_avail;
- blok->h.first_avail += POOL_HDR_BYTES;
-
- memset ((char *)new_pool, '\0', sizeof (struct pool));
- 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;
- }
-
- unblock_alarms();
-
- return new_pool;
- }
-
- void init_alloc() { permanent_pool = make_sub_pool (NULL); }
-
- void clear_pool (struct pool *a)
- {
- block_alarms();
-
- while (a->sub_pools)
- destroy_pool (a->sub_pools);
-
- a->sub_pools = NULL;
-
- 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;
-
- unblock_alarms();
- }
-
- void destroy_pool (pool *a)
- {
- block_alarms();
- clear_pool (a);
-
- 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;
- }
-
- free_blocks (a->first);
- unblock_alarms();
- }
-
- long bytes_in_pool (pool *p) { return bytes_in_block_list (p->first); }
- long bytes_in_free_blocks () { return bytes_in_block_list (block_freelist); }
-
- /*****************************************************************
- *
- * Allocating stuff...
- */
-
-
- void *palloc (struct pool *a, int reqsize)
- {
- /* 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) {
- blok->h.first_avail = new_first_avail;
- return (void *)first_avail;
- }
-
- /* Nope --- get a new one that's guaranteed to be big enough */
-
- block_alarms();
- blok = new_block (size);
- a->last->h.next = blok;
- a->last = blok;
- unblock_alarms();
-
- first_avail = blok->h.first_avail;
- blok->h.first_avail += size;
-
- return (void *)first_avail;
- }
-
- void *pcalloc(struct pool *a, int size)
- {
- void *res = palloc (a, size);
- memset (res, '\0', size);
- return res;
- }
-
- char *pstrdup(struct pool *a, const char *s)
- {
- char *res;
- if (s == NULL) return NULL;
- res = palloc (a, strlen(s) + 1);
- strcpy (res, s);
- return res;
- }
-
- char *pstrndup(struct pool *a, const char *s, int n)
- {
- char *res;
- if (s == NULL) return NULL;
- res = palloc (a, n + 1);
- strncpy (res, s, n);
- res[n] = '\0';
- return res;
- }
-
- char *pstrcat(pool *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 *)palloc(a, len + 1);
- cp = res;
-
- /* 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;
- }
-
-
- /*****************************************************************
- *
- * The 'array' functions...
- */
-
- array_header *make_array (pool *p, int nelts, int elt_size)
- {
- array_header *res = (array_header *)palloc(p, sizeof(array_header));
-
- if (nelts < 1) nelts = 1; /* Assure sanity if someone asks for
- * array of zero elts.
- */
-
- res->elts = 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 */
-
- return res;
- }
-
- void *push_array (array_header *arr)
- {
- if (arr->nelts == arr->nalloc) {
- int new_size = (arr->nalloc <= 0) ? 1 : arr->nalloc * 2;
- char *new_data;
-
- new_data = 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));
- }
-
- void array_cat (array_header *dst, const array_header *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 = 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;
- }
-
- array_header *copy_array (pool *p, const array_header *arr)
- {
- array_header *res = 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.
- */
-
- array_header *copy_array_hdr (pool *p, const array_header *arr)
- {
- array_header *res = (array_header *)palloc(p, sizeof(array_header));
-
- res->elts = arr->elts;
-
- res->pool = p;
- res->elt_size = arr->elt_size;
- res->nelts = arr->nelts;
- res->nalloc = arr->nelts; /* Force overflow on push */
-
- return res;
- }
-
- /* The above is used here to avoid consing multiple new array bodies... */
-
- array_header *append_arrays (pool *p,
- const array_header *first,
- const array_header *second)
- {
- array_header *res = copy_array_hdr (p, first);
-
- array_cat (res, second);
- return res;
- }
-
-
- /*****************************************************************
- *
- * The "table" functions.
- */
-
- table *make_table (pool *p, int nelts) {
- return make_array (p, nelts, sizeof (table_entry));
- }
-
- table *copy_table (pool *p, const table *t) {
- return copy_array (p, t);
- }
-
- void clear_table (table *t)
- {
- t->nelts = 0;
- }
-
- array_header *table_elts (table *t) { return t; }
-
- char *table_get (const table *t, const char *key)
- {
- table_entry *elts = (table_entry *)t->elts;
- int i;
-
- if (key == NULL) return NULL;
-
- for (i = 0; i < t->nelts; ++i)
- if (!strcasecmp (elts[i].key, key))
- return elts[i].val;
-
- return NULL;
- }
-
- void table_set (table *t, const char *key, const char *val)
- {
- register int i, j, k;
- table_entry *elts = (table_entry *)t->elts;
- int done = 0;
-
- for (i = 0; i < t->nelts; ++i)
- if (!strcasecmp (elts[i].key, key)) {
- if (!done) {
- elts[i].val = pstrdup(t->pool, val);
- done = 1;
- }
- else { /* delete an extraneous element */
- for (j = i, k = i + 1; k < t->nelts; ++j, ++k) {
- elts[j].key = elts[k].key;
- elts[j].val = elts[k].val;
- }
- --t->nelts;
- }
- }
-
- if (!done) {
- elts = (table_entry *)push_array(t);
- elts->key = pstrdup (t->pool, key);
- elts->val = pstrdup (t->pool, val);
- }
- }
-
- void table_unset( table *t, const char *key )
- {
- register int i, j, k;
- table_entry *elts = (table_entry *)t->elts;
-
- for (i = 0; i < t->nelts; ++i)
- 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->nelts; ++j, ++k) {
- elts[j].key = elts[k].key;
- elts[j].val = elts[k].val;
- }
- --t->nelts;
- }
- }
-
- void table_merge (table *t, const char *key, const char *val)
- {
- table_entry *elts = (table_entry *)t->elts;
- int i;
-
- for (i = 0; i < t->nelts; ++i)
- if (!strcasecmp (elts[i].key, key)) {
- elts[i].val = pstrcat (t->pool, elts[i].val, ", ", val, NULL);
- return;
- }
-
- elts = (table_entry *)push_array(t);
- elts->key = pstrdup (t->pool, key);
- elts->val = pstrdup (t->pool, val);
- }
-
- void table_add (table *t, const char *key, const char *val)
- {
- table_entry *elts = (table_entry *)t->elts;
-
- elts = (table_entry *)push_array(t);
- elts->key = pstrdup (t->pool, key);
- elts->val = pstrdup (t->pool, val);
- }
-
- table* overlay_tables (pool *p, const table *overlay, const table *base)
- {
- return append_arrays (p, overlay, base);
- }
-
- /* 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.
- */
- void table_do (int (*comp)(void *, const char *, const char *), void *rec,
- const table *t, ...)
- {
- va_list vp;
- char *argp;
- table_entry *elts = (table_entry *)t->elts;
- int rv, i;
-
- va_start(vp, t);
-
- argp = va_arg(vp, char *);
-
- do {
- for (rv = 1, i = 0; rv && (i < t->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);
- }
-
- /*****************************************************************
- *
- * Managing generic cleanups.
- */
-
- struct cleanup {
- void *data;
- void (*plain_cleanup)(void *);
- void (*child_cleanup)(void *);
- struct cleanup *next;
- };
-
- void register_cleanup (pool *p, void *data, void (*plain_cleanup)(void *),
- void (*child_cleanup)(void *))
- {
- struct cleanup *c = (struct cleanup *)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;
- }
-
- void kill_cleanup (pool *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;
- }
- }
-
- void run_cleanup (pool *p, void *data, void (*cleanup)(void *))
- {
- block_alarms(); /* Run cleanup only once! */
- (*cleanup)(data);
- kill_cleanup (p, data, cleanup);
- 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 (pool *p)
- {
- run_child_cleanups (p->cleanups);
- p->cleanups = NULL;
-
- for (p = p->sub_pools; p; p = p->sub_next)
- cleanup_pool_for_exec (p);
- }
-
- void cleanup_for_exec()
- {
- block_alarms();
- cleanup_pool_for_exec (permanent_pool);
- unblock_alarms();
- }
-
- /*****************************************************************
- *
- * Files and file descriptors; these are just an application of the
- * generic cleanup interface.
- */
-
- static void fd_cleanup (void *fdv) { close ((int)fdv); }
-
- void note_cleanups_for_fd (pool *p, int fd) {
- register_cleanup (p, (void *)fd, fd_cleanup, fd_cleanup);
- }
-
- void kill_cleanups_for_fd(pool *p,int fd)
- {
- kill_cleanup(p,(void *)fd,fd_cleanup);
- }
-
- int popenf(pool *a, const char *name, int flg, int mode)
- {
- int fd;
- int save_errno;
-
- block_alarms();
- fd = open(name, flg, mode);
- save_errno = errno;
- if (fd >= 0) {
- fd = ap_slack (fd, AP_SLACK_HIGH);
- note_cleanups_for_fd (a, fd);
- }
- unblock_alarms();
- errno = save_errno;
- return fd;
- }
-
- int pclosef(pool *a, int fd)
- {
- int res;
- int save_errno;
-
- block_alarms();
- res = close(fd);
- save_errno = errno;
- kill_cleanup(a, (void *)fd, fd_cleanup);
- unblock_alarms();
- errno = save_errno;
- return res;
- }
-
- /* 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)); }
-
- void note_cleanups_for_file (pool *p, FILE *fp) {
- register_cleanup (p, (void *)fp, file_cleanup, file_child_cleanup);
- }
-
- FILE *pfopen(pool *a, const char *name, const char *mode)
- {
- FILE *fd = NULL;
- int baseFlag, desc;
-
- 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,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
- if (desc >= 0) {
- desc = ap_slack(desc, AP_SLACK_LOW);
- fd = fdopen(desc, mode);
- }
- } else {
- fd = fopen(name, mode);
- }
-
- if (fd != NULL) note_cleanups_for_file (a, fd);
- unblock_alarms();
- return fd;
- }
-
- FILE *pfdopen(pool *a,int fd, const char *mode)
- {
- FILE *f;
-
- block_alarms();
- f=fdopen(fd,mode);
- if(f != NULL)
- note_cleanups_for_file(a,f);
- unblock_alarms();
- return f;
- }
-
-
- int pfclose(pool *a, FILE *fd)
- {
- int res;
-
- block_alarms();
- res = fclose(fd);
- kill_cleanup(a, (void *)fd, file_cleanup);
- unblock_alarms();
- 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); }
-
- regex_t *pregcomp(pool *p, const char *pattern, int cflags) {
- regex_t *preg = palloc(p, sizeof(regex_t));
-
- if (regcomp(preg, pattern, cflags))
- return NULL;
-
- register_cleanup (p, (void *)preg, regex_cleanup, regex_cleanup);
-
- return preg;
- }
-
-
- void pregfree(pool *p, regex_t *reg)
- {
- block_alarms();
- regfree (reg);
- kill_cleanup (p, (void *)reg, regex_cleanup);
- 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
- */
-
- struct process_chain {
- pid_t pid;
- enum kill_conditions kill_how;
- struct process_chain *next;
- };
-
- void note_subprocess (pool *a, int pid, enum kill_conditions how)
- {
- struct process_chain *new =
- (struct process_chain *)palloc(a, sizeof(struct process_chain));
-
- new->pid = pid;
- new->kill_how = how;
- new->next = a->subprocesses;
- a->subprocesses = new;
- }
-
- int spawn_child_err (pool *p, void (*func)(void *), void *data,
- enum kill_conditions kill_how,
- FILE **pipe_in, FILE **pipe_out, FILE **pipe_err)
- {
- int pid;
- int in_fds[2];
- int out_fds[2];
- int err_fds[2];
- int save_errno;
-
- block_alarms();
-
- if (pipe_in && pipe (in_fds) < 0)
- {
- save_errno = errno;
- unblock_alarms();
- errno = save_errno;
- return 0;
- }
-
- if (pipe_out && pipe (out_fds) < 0) {
- save_errno = errno;
- if (pipe_in) {
- close (in_fds[0]); close (in_fds[1]);
- }
- unblock_alarms();
- errno = save_errno;
- return 0;
- }
-
- if (pipe_err && 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]);
- }
- unblock_alarms();
- errno = save_errno;
- return 0;
- }
-
- 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]);
- }
- unblock_alarms();
- errno = save_errno;
- return 0;
- }
-
- if (!pid) {
- /* Child process */
-
- 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);
- exit (0); /* Should never get here... */
- }
-
- /* Parent process */
-
- note_subprocess (p, pid, kill_how);
-
- if (pipe_out) {
- close (out_fds[1]);
- #ifdef __EMX__
- /* Need binary mode set for OS/2. */
- *pipe_out = fdopen (out_fds[0], "rb");
- #else
- *pipe_out = fdopen (out_fds[0], "r");
- #endif
-
- if (*pipe_out) note_cleanups_for_file (p, *pipe_out);
- }
-
- if (pipe_in) {
- close (in_fds[0]);
- #ifdef __EMX__
- /* Need binary mode set for OS/2 */
- *pipe_in = fdopen (in_fds[1], "wb");
- #else
- *pipe_in = fdopen (in_fds[1], "w");
- #endif
-
- if (*pipe_in) note_cleanups_for_file (p, *pipe_in);
- }
-
- if (pipe_err) {
- close (err_fds[1]);
- #ifdef __EMX__
- /* Need binary mode set for OS/2. */
- *pipe_err = fdopen (err_fds[0], "rb");
- #else
- *pipe_err = fdopen (err_fds[0], "r");
- #endif
-
- if (*pipe_err) note_cleanups_for_file (p, *pipe_err);
- }
-
- unblock_alarms();
- return pid;
- }
-
- 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.
- */
-
- #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) {
- /* 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);
- }
- }
-
-