home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 8 / CDACTUAL8.iso / share / os2 / varios / apache / alloc.c~0 < prev    next >
Encoding:
Text File  |  1996-08-03  |  24.9 KB  |  1,077 lines

  1.  
  2. /* ====================================================================
  3.  * Copyright (c) 1995 The Apache Group.  All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * 1. Redistributions of source code must retain the above copyright
  10.  *    notice, this list of conditions and the following disclaimer.
  11.  *
  12.  * 2. Redistributions in binary form must reproduce the above copyright
  13.  *    notice, this list of conditions and the following disclaimer in
  14.  *    the documentation and/or other materials provided with the
  15.  *    distribution.
  16.  *
  17.  * 3. All advertising materials mentioning features or use of this
  18.  *    software must display the following acknowledgment:
  19.  *    "This product includes software developed by the Apache Group
  20.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  21.  *
  22.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  23.  *    endorse or promote products derived from this software without
  24.  *    prior written permission.
  25.  *
  26.  * 5. Redistributions of any form whatsoever must retain the following
  27.  *    acknowledgment:
  28.  *    "This product includes software developed by the Apache Group
  29.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  30.  *
  31.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  32.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  33.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  34.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  35.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  36.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  37.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  38.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  39.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  42.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  43.  * ====================================================================
  44.  *
  45.  * This software consists of voluntary contributions made by many
  46.  * individuals on behalf of the Apache Group and was originally based
  47.  * on public domain software written at the National Center for
  48.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  49.  * For more information on the Apache Group and the Apache HTTP server
  50.  * project, please see <http://www.apache.org/>.
  51.  *
  52.  */
  53.  
  54.  
  55. /*
  56.  * Resource allocation code... the code here is responsible for making
  57.  * sure that nothing leaks.
  58.  *
  59.  * rst --- 4/95 --- 6/95
  60.  */
  61.  
  62. #include "conf.h"
  63. #include "alloc.h"
  64.  
  65. #include <stdarg.h>
  66.  
  67. /*****************************************************************
  68.  *
  69.  * Managing free storage blocks...
  70.  */
  71.  
  72. union align
  73. {
  74.   /* Types which are likely to have the longest RELEVANT alignment
  75.    * restrictions...
  76.    */
  77.  
  78.   char *cp;
  79.   void (*f)();
  80.   long l;
  81.   FILE *fp;
  82.   double d;
  83. };
  84.  
  85. #define CLICK_SZ (sizeof(union align))
  86.  
  87. union block_hdr
  88. {
  89.   union align a;
  90.  
  91.   /* Actual header... */
  92.  
  93.   struct {
  94.     char *endp;
  95.     union block_hdr *next;
  96.     char *first_avail;
  97.   } h;
  98. };
  99.  
  100. union block_hdr *block_freelist = NULL;
  101.  
  102.  
  103.  
  104. /* Get a completely new block from the system pool */
  105.  
  106. union block_hdr *malloc_block (int size)
  107. {
  108.   union block_hdr *blok =
  109.     (union block_hdr *)malloc(size + sizeof(union block_hdr));
  110.  
  111.   if (blok == NULL) return NULL;
  112.  
  113.   blok->h.next = NULL;
  114.   blok->h.first_avail = (char *)(blok + 1);
  115.   blok->h.endp = size + blok->h.first_avail;
  116.  
  117.   return blok;
  118. }
  119.  
  120.  
  121.  
  122. void chk_on_blk_list (union block_hdr *blok, union block_hdr *free_blk)
  123. {
  124.   /* Debugging code.  Left in for the moment. */
  125.  
  126.   while (free_blk) {
  127.     if (free_blk == blok) {
  128.       fprintf (stderr, "Ouch!  Freeing free block\n");
  129.       exit (1);
  130.     }
  131.     free_blk = free_blk->h.next;
  132.   }
  133. }
  134.  
  135. /* Free a chain of blocks --- must be called with alarms blocked. */
  136.  
  137. void free_blocks (union block_hdr *blok)
  138. {
  139.   /* First, put new blocks at the head of the free list ---
  140.    * we'll eventually bash the 'next' pointer of the last block
  141.    * in the chain to point to the free blocks we already had.
  142.    */
  143.  
  144.   union block_hdr *old_free_list = block_freelist;
  145.  
  146.   if (blok == NULL) return; /* Sanity check --- freeing empty pool? */
  147.  
  148.   block_freelist = blok;
  149.  
  150.   /*
  151.    * Next, adjust first_avail pointers of each block --- have to do it
  152.    * sooner or later, and it simplifies the search in new_block to do it
  153.    * now.
  154.    */
  155.  
  156.   while (blok->h.next != NULL) {
  157.     chk_on_blk_list (blok, old_free_list);
  158.     blok->h.first_avail = (char *)(blok + 1);
  159.     blok = blok->h.next;
  160.   }
  161.  
  162.   chk_on_blk_list (blok, old_free_list);
  163.   blok->h.first_avail = (char *)(blok + 1);
  164.  
  165.   /* Finally, reset next pointer to get the old free blocks back */
  166.  
  167.   blok->h.next = old_free_list;
  168. }
  169.  
  170.  
  171.  
  172.  
  173. /* Get a new block, from our own free list if possible, from the system
  174.  * if necessary.  Must be called with alarms blocked.
  175.  */
  176.  
  177. union block_hdr *new_block (int min_size)
  178. {
  179.   union block_hdr **lastptr = &block_freelist;
  180.   union block_hdr *blok = block_freelist;
  181.  
  182.   /* First, see if we have anything of the required size
  183.    * on the free list...
  184.    */
  185.  
  186.   min_size += BLOCK_MINFREE;
  187.  
  188.   while (blok != NULL) {
  189.     if (min_size <= blok->h.endp - blok->h.first_avail) {
  190.  
  191.       *lastptr = blok->h.next;
  192.       blok->h.next = NULL;
  193.       return blok;
  194.     }
  195.     else {
  196.       lastptr = &blok->h.next;
  197.       blok = blok->h.next;
  198.     }
  199.   }
  200.  
  201.   /* Nope. */
  202.  
  203.   return malloc_block (min_size);
  204. }
  205.  
  206.  
  207.  
  208. /* Accounting */
  209.  
  210. long bytes_in_block_list (union block_hdr *blok)
  211. {
  212.   long size = 0;
  213.  
  214.   while (blok) {
  215.     size += blok->h.endp - (char *)(blok + 1);
  216.     blok = blok->h.next;
  217.   }
  218.  
  219.   return size;
  220. }
  221.  
  222.  
  223. /*****************************************************************
  224.  *
  225.  * Pool internals and management...
  226.  * NB that subprocesses are not handled by the generic cleanup code,
  227.  * basically because we don't want cleanups for multiple subprocesses
  228.  * to result in multiple three-second pauses.
  229.  */
  230.  
  231. struct process_chain;
  232. struct cleanup;
  233.  
  234. static void run_cleanups (struct cleanup *);
  235. static void free_proc_chain (struct process_chain *);
  236.  
  237. struct pool {
  238.   union block_hdr *first;
  239.   union block_hdr *last;
  240.   struct cleanup *cleanups;
  241.   struct process_chain *subprocesses;
  242.   struct pool *sub_pools;
  243.   struct pool *sub_next;
  244.   struct pool *sub_prev;
  245.   struct pool *parent;
  246.   char *free_first_avail;
  247. };
  248.  
  249. pool *permanent_pool;
  250.  
  251. /* Each pool structure is allocated in the start of its own first block,
  252.  * so we need to know how many bytes that is (once properly aligned...).
  253.  * This also means that when a pool's sub-pool is destroyed, the storage
  254.  * associated with it is *completely* gone, so we have to make sure it
  255.  * gets taken off the parent's sub-pool list...
  256.  */
  257.  
  258. #define POOL_HDR_CLICKS (1 + ((sizeof(struct pool) - 1) / CLICK_SZ))
  259. #define POOL_HDR_BYTES (POOL_HDR_CLICKS * CLICK_SZ)
  260.  
  261. struct pool *make_sub_pool (struct pool *p)
  262. {
  263.   union block_hdr *blok;
  264.   pool *new_pool;
  265.  
  266.   block_alarms();
  267.  
  268.   blok = new_block (0);
  269.   new_pool = (pool *)blok->h.first_avail;
  270.   blok->h.first_avail += POOL_HDR_BYTES;
  271.  
  272.   memset ((char *)new_pool, '\0', sizeof (struct pool));
  273.   new_pool->free_first_avail = blok->h.first_avail;
  274.   new_pool->first = new_pool->last = blok;
  275.  
  276.   if (p) {
  277.     new_pool->parent = p;
  278.     new_pool->sub_next = p->sub_pools;
  279.     if (new_pool->sub_next) new_pool->sub_next->sub_prev = new_pool;
  280.     p->sub_pools = new_pool;
  281.   }
  282.  
  283.   unblock_alarms();
  284.  
  285.   return new_pool;
  286. }
  287.  
  288. void init_alloc() { permanent_pool = make_sub_pool (NULL); }
  289.  
  290. void clear_pool (struct pool *a)
  291. {
  292.   block_alarms();
  293.  
  294.   while (a->sub_pools)
  295.     destroy_pool (a->sub_pools);
  296.  
  297.   a->sub_pools = NULL;
  298.  
  299.   run_cleanups (a->cleanups);        a->cleanups = NULL;
  300.   free_proc_chain (a->subprocesses); a->subprocesses = NULL;
  301.   free_blocks (a->first->h.next);    a->first->h.next = NULL;
  302.  
  303.   a->last = a->first;
  304.   a->first->h.first_avail = a->free_first_avail;
  305.  
  306.   unblock_alarms();
  307. }
  308.  
  309. void destroy_pool (pool *a)
  310. {
  311.   block_alarms();
  312.   clear_pool (a);
  313.  
  314.   if (a->parent) {
  315.     if (a->parent->sub_pools == a) a->parent->sub_pools = a->sub_next;
  316.     if (a->sub_prev) a->sub_prev->sub_next = a->sub_next;
  317.     if (a->sub_next) a->sub_next->sub_prev = a->sub_prev;
  318.   }
  319.  
  320.   free_blocks (a->first);
  321.   unblock_alarms();
  322. }
  323.  
  324. long bytes_in_pool (pool *p) { return bytes_in_block_list (p->first); }
  325. long bytes_in_free_blocks () { return bytes_in_block_list (block_freelist); }
  326.  
  327. /*****************************************************************
  328.  *
  329.  * Allocating stuff...
  330.  */
  331.  
  332.  
  333. void *palloc (struct pool *a, int reqsize)
  334. {
  335.   /* Round up requested size to an even number of alignment units (core clicks)
  336.    */
  337.  
  338.   int nclicks = 1 + ((reqsize - 1) / CLICK_SZ);
  339.   int size = nclicks * CLICK_SZ;
  340.  
  341.   /* First, see if we have space in the block most recently
  342.    * allocated to this pool
  343.    */
  344.  
  345.   union block_hdr *blok = a->last;
  346.   char *first_avail = blok->h.first_avail;
  347.   char *new_first_avail;
  348.  
  349.   if (size <= 0) size = 1;
  350.   new_first_avail = first_avail + size;
  351.  
  352.   if (new_first_avail <= blok->h.endp) {
  353.     blok->h.first_avail = new_first_avail;
  354.     return (void *)first_avail;
  355.   }
  356.  
  357.   /* Nope --- get a new one that's guaranteed to be big enough */
  358.  
  359.   block_alarms();
  360.   blok = new_block (size);
  361.   a->last->h.next = blok;
  362.   a->last = blok;
  363.   unblock_alarms();
  364.  
  365.   first_avail = blok->h.first_avail;
  366.   blok->h.first_avail += size;
  367.  
  368.   return (void *)first_avail;
  369. }
  370.  
  371. void *pcalloc(struct pool *a, int size)
  372. {
  373.   void *res = palloc (a, size);
  374.   memset (res, '\0', size);
  375.   return res;
  376. }
  377.  
  378. char *pstrdup(struct pool *a, const char *s)
  379. {
  380.   char *res;
  381.   if (s == NULL) return NULL;
  382.   res = palloc (a, strlen(s) + 1);
  383.   strcpy (res, s);
  384.   return res;
  385. }
  386.  
  387. char *pstrndup(struct pool *a, const char *s, int n)
  388. {
  389.   char *res;
  390.   if (s == NULL) return NULL;
  391.   res = palloc (a, n + 1);
  392.   strncpy (res, s, n);
  393.   res[n] = '\0';
  394.   return res;
  395. }
  396.  
  397. char *pstrcat(pool *a, ...)
  398. {
  399.   char *cp, *argp, *res;
  400.  
  401.   /* Pass one --- find length of required string */
  402.  
  403.   int len = 0;
  404.   va_list adummy;
  405.  
  406.   va_start (adummy, a);
  407.  
  408.   while ((cp = va_arg (adummy, char *)) != NULL)
  409.     len += strlen(cp);
  410.  
  411.   va_end (adummy);
  412.  
  413.   /* Allocate the required string */
  414.  
  415.   res = (char *)palloc(a, len + 1);
  416.   cp = res;
  417.  
  418.   /* Pass two --- copy the argument strings into the result space */
  419.  
  420.   va_start (adummy, a);
  421.  
  422.   while ((argp = va_arg (adummy, char *)) != NULL) {
  423.     strcpy (cp, argp);
  424.     cp += strlen(argp);
  425.   }
  426.  
  427.   va_end (adummy);
  428.  
  429.   /* Return the result string */
  430.  
  431.   return res;
  432. }
  433.  
  434.  
  435. /*****************************************************************
  436.  *
  437.  * The 'array' functions...
  438.  */
  439.  
  440. array_header *make_array (pool *p, int nelts, int elt_size)
  441. {
  442.   array_header *res = (array_header *)palloc(p, sizeof(array_header));
  443.  
  444.   if (nelts < 1) nelts = 1; /* Assure sanity if someone asks for
  445.                  * array of zero elts.
  446.                  */
  447.  
  448.   res->elts = pcalloc (p, nelts * elt_size);
  449.  
  450.   res->pool = p;
  451.   res->elt_size = elt_size;
  452.   res->nelts = 0;       /* No active elements yet... */
  453.   res->nalloc = nelts;      /* ...but this many allocated */
  454.  
  455.   return res;
  456. }
  457.  
  458. void *push_array (array_header *arr)
  459. {
  460.   if (arr->nelts == arr->nalloc) {
  461.     char *new_data = pcalloc (arr->pool, arr->nalloc * arr->elt_size * 2);
  462.  
  463.     memcpy (new_data, arr->elts, arr->nalloc * arr->elt_size);
  464.     arr->elts = new_data;
  465.     arr->nalloc *= 2;
  466.   }
  467.  
  468.   ++arr->nelts;
  469.   return arr->elts + (arr->elt_size * (arr->nelts - 1));
  470. }
  471.  
  472. void array_cat (array_header *dst, array_header *src)
  473. {
  474.   int elt_size = dst->elt_size;
  475.  
  476.   if (dst->nelts + src->nelts > dst->nalloc) {
  477.     int new_size = dst->nalloc * 2;
  478.     char *new_data;
  479.  
  480.     if (new_size == 0) ++new_size;
  481.  
  482.     while (dst->nelts + src->nelts > new_size)
  483.       new_size *= 2;
  484.  
  485.     new_data = pcalloc (dst->pool, elt_size * new_size);
  486.     memcpy (new_data, dst->elts, dst->nalloc * elt_size);
  487.  
  488.     dst->elts = new_data;
  489.     dst->nalloc = new_size;
  490.   }
  491.  
  492.   memcpy (dst->elts + dst->nelts * elt_size, src->elts, elt_size * src->nelts);
  493.   dst->nelts += src->nelts;
  494. }
  495.  
  496. array_header *copy_array (pool *p, array_header *arr)
  497. {
  498.   array_header *res = make_array (p, arr->nalloc, arr->elt_size);
  499.  
  500.   memcpy (res->elts, arr->elts, arr->elt_size * arr->nelts);
  501.   res->nelts = arr->nelts;
  502.   return res;
  503. }
  504.  
  505. /* This cute function copies the array header *only*, but arranges
  506.  * for the data section to be copied on the first push or arraycat.
  507.  * It's useful when the elements of the array being copied are
  508.  * read only, but new stuff *might* get added on the end; we have the
  509.  * overhead of the full copy only where it is really needed.
  510.  */
  511.  
  512. array_header *copy_array_hdr (pool *p, array_header *arr)
  513. {
  514.   array_header *res = (array_header *)palloc(p, sizeof(array_header));
  515.  
  516.   res->elts = arr->elts;
  517.  
  518.   res->pool = p;
  519.   res->elt_size = arr->elt_size;
  520.   res->nelts = arr->nelts;
  521.   res->nalloc = arr->nelts; /* Force overflow on push */
  522.  
  523.   return res;
  524. }
  525.  
  526. /* The above is used here to avoid consing multiple new array bodies... */
  527.  
  528. array_header *append_arrays (pool *p,
  529.                  array_header *first, array_header *second)
  530. {
  531.   array_header *res = copy_array_hdr (p, first);
  532.  
  533.   array_cat (res, second);
  534.   return res;
  535. }
  536.  
  537.  
  538. /*****************************************************************
  539.  *
  540.  * The "table" functions.
  541.  */
  542.  
  543. table *make_table (pool *p, int nelts) {
  544.     return make_array (p, nelts, sizeof (table_entry));
  545. }
  546.  
  547. table *copy_table (pool *p, table *t) {
  548.     return copy_array (p, t);
  549. }
  550.  
  551. array_header *table_elts (table *t) { return t; }
  552.  
  553. char *table_get (table *t, char *key)
  554. {
  555.     table_entry *elts = (table_entry *)t->elts;
  556.     int i;
  557.  
  558.     if (key == NULL) return NULL;
  559.  
  560.     for (i = 0; i < t->nelts; ++i)
  561.         if (!strcasecmp (elts[i].key, key))
  562.         return elts[i].val;
  563.  
  564.     return NULL;
  565. }
  566.  
  567. void table_set (table *t, const char *key, const char *val)
  568. {
  569.     table_entry *elts = (table_entry *)t->elts;
  570.     int i;
  571.  
  572.     for (i = 0; i < t->nelts; ++i)
  573.         if (!strcasecmp (elts[i].key, key)) {
  574.         elts[i].val = pstrdup (t->pool, val);
  575.         return;
  576.     }
  577.  
  578.     elts = (table_entry *)push_array(t);
  579.     elts->key = pstrdup (t->pool, key);
  580.     elts->val = pstrdup (t->pool, val);
  581. }
  582.  
  583. void table_unset( table *t, char *key )
  584. {
  585.     table_entry *elts = (table_entry *)t->elts;
  586.     int i;
  587.     int j;
  588.  
  589.     for (i = 0; i < t->nelts; ++i)
  590.         if (!strcasecmp (elts[i].key, key)) {
  591.  
  592.             /* found the element to skip over
  593.              * there are any number of ways to remove an element from
  594.              * a contiguous block of memory.  I've chosen one that
  595.              * doesn't do a memcpy/bcopy/array_delete, *shrug*...
  596.              */
  597.             j = i;
  598.             ++i;
  599.             for ( ; i < t->nelts; ) {
  600.                 elts[j].key = elts[i].key;
  601.                 elts[j].val = elts[i].val;
  602.                 ++i;
  603.                 ++j;
  604.             };
  605.             --t->nelts;
  606.  
  607.             return;
  608.         }
  609. }
  610.  
  611. void table_merge (table *t, char *key, char *val)
  612. {
  613.     table_entry *elts = (table_entry *)t->elts;
  614.     int i;
  615.  
  616.     for (i = 0; i < t->nelts; ++i)
  617.         if (!strcasecmp (elts[i].key, key)) {
  618.         elts[i].val = pstrcat (t->pool, elts[i].val, ", ", val, NULL);
  619.         return;
  620.     }
  621.  
  622.     elts = (table_entry *)push_array(t);
  623.     elts->key = pstrdup (t->pool, key);
  624.     elts->val = pstrdup (t->pool, val);
  625. }
  626.  
  627. void table_add (table *t, char *key, char *val)
  628. {
  629.     table_entry *elts = (table_entry *)t->elts;
  630.  
  631.     elts = (table_entry *)push_array(t);
  632.     elts->key = pstrdup (t->pool, key);
  633.     elts->val = pstrdup (t->pool, val);
  634. }
  635.  
  636. table* overlay_tables (pool *p, table *overlay, table *base)
  637. {
  638.     return append_arrays (p, overlay, base);
  639. }
  640.  
  641. /*****************************************************************
  642.  *
  643.  * Managing generic cleanups.
  644.  */
  645.  
  646. struct cleanup {
  647.   void *data;
  648.   void (*plain_cleanup)(void *);
  649.   void (*child_cleanup)(void *);
  650.   struct cleanup *next;
  651. };
  652.  
  653. void register_cleanup (pool *p, void *data, void (*plain_cleanup)(void *),
  654.                void (*child_cleanup)(void *))
  655. {
  656.   struct cleanup *c = (struct cleanup *)palloc(p, sizeof (struct cleanup));
  657.   c->data = data;
  658.   c->plain_cleanup = plain_cleanup;
  659.   c->child_cleanup = child_cleanup;
  660.   c->next = p->cleanups;
  661.   p->cleanups = c;
  662. }
  663.  
  664. void kill_cleanup (pool *p, void *data, void (*cleanup)(void *))
  665. {
  666.   struct cleanup *c = p->cleanups;
  667.   struct cleanup **lastp = &p->cleanups;
  668.  
  669.   while (c) {
  670.     if (c->data == data && c->plain_cleanup == cleanup) {
  671.       *lastp = c->next;
  672.       break;
  673.     }
  674.  
  675.     lastp = &c->next;
  676.     c = c->next;
  677.   }
  678. }
  679.  
  680. void run_cleanup (pool *p, void *data, void (*cleanup)(void *))
  681. {
  682.   block_alarms();       /* Run cleanup only once! */
  683.   (*cleanup)(data);
  684.   kill_cleanup (p, data, cleanup);
  685.   unblock_alarms();
  686. }
  687.  
  688. static void run_cleanups (struct cleanup *c)
  689. {
  690.   while (c) {
  691.     (*c->plain_cleanup)(c->data);
  692.     c = c->next;
  693.   }
  694. }
  695.  
  696. static void run_child_cleanups (struct cleanup *c)
  697. {
  698.   while (c) {
  699.     (*c->child_cleanup)(c->data);
  700.     c = c->next;
  701.   }
  702. }
  703.  
  704. static void cleanup_pool_for_exec (pool *p)
  705. {
  706.   run_child_cleanups (p->cleanups);
  707.   p->cleanups = NULL;
  708.  
  709.   for (p = p->sub_pools; p; p = p->sub_next)
  710.     cleanup_pool_for_exec (p);
  711. }
  712.  
  713. void cleanup_for_exec()
  714. {
  715.   block_alarms();
  716.   cleanup_pool_for_exec (permanent_pool);
  717.   unblock_alarms();
  718. }
  719.  
  720. /*****************************************************************
  721.  *
  722.  * Files and file descriptors; these are just an application of the
  723.  * generic cleanup interface.
  724.  */
  725.  
  726. static void fd_cleanup (void *fdv) { close ((int)fdv); }
  727.  
  728. void note_cleanups_for_fd (pool *p, int fd) {
  729.   register_cleanup (p, (void *)fd, fd_cleanup, fd_cleanup);
  730. }
  731.  
  732. int popenf(struct pool *a, char *name, int flg, int mode)
  733. {
  734.   int fd;
  735.  
  736.   block_alarms();
  737.   fd = open(name, flg, mode);
  738.   if (fd >= 0) note_cleanups_for_fd (a, fd);
  739.   unblock_alarms();
  740.   return fd;
  741. }
  742.  
  743. int pclosef(struct pool *a, int fd)
  744. {
  745.   int res;
  746.  
  747.   block_alarms();
  748.   res = close(fd);
  749.   kill_cleanup(a, (void *)fd, fd_cleanup);
  750.   unblock_alarms();
  751.   return res;
  752. }
  753.  
  754. /* Note that we have separate plain_ and child_ cleanups for FILE *s,
  755.  * since fclose() would flush I/O buffers, which is extremely undesirable;
  756.  * we just close the descriptor.
  757.  */
  758.  
  759. static void file_cleanup (void *fpv) { fclose ((FILE *)fpv); }
  760. static void file_child_cleanup (void *fpv) { close (fileno ((FILE *)fpv)); }
  761.  
  762. void note_cleanups_for_file (struct pool *p, FILE *fp) {
  763.   register_cleanup (p, (void *)fp, file_cleanup, file_child_cleanup);
  764. }
  765.  
  766. FILE *pfopen(struct pool *a, char *name, char *mode)
  767. {
  768.   FILE *fd;
  769.  
  770.   block_alarms();
  771.   fd = fopen(name, mode);
  772.   if (fd != NULL) note_cleanups_for_file (a, fd);
  773.   unblock_alarms();
  774.   return fd;
  775. }
  776.  
  777. FILE *pfdopen(struct pool *a,int fd,char *mode)
  778. {
  779.   FILE *f;
  780.  
  781.   block_alarms();
  782.   f=fdopen(fd,mode);
  783.   if(f != NULL)
  784.     note_cleanups_for_file(a,f);
  785.   unblock_alarms();
  786.   return f;
  787. }
  788.  
  789.  
  790. int pfclose(struct pool *a, FILE *fd)
  791. {
  792.   int res;
  793.  
  794.   block_alarms();
  795.   res = fclose(fd);
  796.   kill_cleanup(a, (void *)fd, file_cleanup);
  797.   unblock_alarms();
  798.   return res;
  799. }
  800.  
  801. /*****************************************************************
  802.  *
  803.  * More grotty system stuff... subprocesses.  Frump.  These don't use
  804.  * the generic cleanup interface because I don't want multiple
  805.  * subprocesses to result in multiple three-second pauses; the
  806.  * subprocesses have to be "freed" all at once.  If someone comes
  807.  * along with another resource they want to allocate which has the
  808.  * same property, we might want to fold support for that into the
  809.  * generic interface, but for now, it's a special case
  810.  */
  811.  
  812. struct process_chain {
  813.   pid_t pid;
  814.   enum kill_conditions kill_how;
  815.   struct process_chain *next;
  816. };
  817.  
  818. void note_subprocess (pool *a, int pid, enum kill_conditions how)
  819. {
  820.   struct process_chain *new =
  821.     (struct process_chain *)palloc(a, sizeof(struct process_chain));
  822.  
  823.   new->pid = pid;
  824.   new->kill_how = how;
  825.   new->next = a->subprocesses;
  826.   a->subprocesses = new;
  827. }
  828.  
  829. int spawn_child (pool *p, void (*func)(void *), void *data,
  830.          enum kill_conditions kill_how,
  831.          FILE **pipe_in, FILE **pipe_out)
  832. {
  833.   int pid;
  834.   int in_fds[2];
  835.   int out_fds[2];
  836.  
  837.   block_alarms();
  838.  
  839.   if (pipe_in && pipe (in_fds) < 0)
  840.   {
  841.       unblock_alarms();
  842.       return 0;
  843.   }
  844.  
  845.   if (pipe_out && pipe (out_fds) < 0) {
  846.     if (pipe_in) {
  847.       close (in_fds[0]); close (in_fds[1]);
  848.     }
  849.     unblock_alarms();
  850.     return 0;
  851.   }
  852.  
  853.   if ((pid = fork()) < 0) {
  854.     if (pipe_in) {
  855.       close (in_fds[0]); close (in_fds[1]);
  856.     }
  857.     if (pipe_out) {
  858.       close (out_fds[0]); close (out_fds[1]);
  859.     }
  860.     unblock_alarms();
  861.     return 0;
  862.   }
  863.  
  864.   if (!pid) {
  865.     /* Child process */
  866.  
  867.     if (pipe_out) {
  868.       close (out_fds[0]);
  869.       dup2 (out_fds[1], STDOUT_FILENO);
  870.       close (out_fds[1]);
  871.     }
  872.  
  873.     if (pipe_in) {
  874.       close (in_fds[1]);
  875.       dup2 (in_fds[0], STDIN_FILENO);
  876.       close (in_fds[0]);
  877.     }
  878.  
  879.     /* HP-UX SIGCHLD fix goes here, if someone will remind me what it is... */
  880.     signal (SIGCHLD, SIG_DFL);  /* Was that it? */
  881.  
  882.     func (data);
  883.     exit (0);           /* Should never get here... */
  884.   }
  885.  
  886.   /* Parent process */
  887.  
  888.   note_subprocess (p, pid, kill_how);
  889.  
  890.   if (pipe_out) {
  891.     close (out_fds[1]);
  892. #ifdef __EMX__
  893.     /* Need binary mode set for OS/2. */
  894.     *pipe_out = fdopen (out_fds[0], "rb");
  895. #else
  896.     *pipe_out = fdopen (out_fds[0], "r");
  897. #endif
  898.  
  899.     if (*pipe_out) note_cleanups_for_file (p, *pipe_out);
  900.   }
  901.  
  902.   if (pipe_in) {
  903.     close (in_fds[0]);
  904. #ifdef __EMX__
  905.     /* Need binary mode set for OS/2 */
  906.     *pipe_in = fdopen (in_fds[1], "wb");
  907. #else
  908.     *pipe_in = fdopen (in_fds[1], "w");
  909. #endif
  910.  
  911.     if (*pipe_in) note_cleanups_for_file (p, *pipe_in);
  912.   }
  913.  
  914.   unblock_alarms();
  915.   return pid;
  916. }
  917.  
  918. static void free_proc_chain (struct process_chain *procs)
  919. {
  920.   /* Dispose of the subprocesses we've spawned off in the course of
  921.    * whatever it was we're cleaning up now.  This may involve killing
  922.    * some of them off...
  923.    */
  924.  
  925.   struct process_chain *p;
  926.   int need_timeout = 0;
  927.   int status;
  928.  
  929.   if (procs == NULL) return;    /* No work.  Whew! */
  930.  
  931.   /* First, check to see if we need to do the SIGTERM, sleep, SIGKILL
  932.    * dance with any of the processes we're cleaning up.  If we've got
  933.    * any kill-on-sight subprocesses, ditch them now as well, so they
  934.    * don't waste any more cycles doing whatever it is that they shouldn't
  935.    * be doing anymore.
  936.    */
  937.  
  938. #ifndef NEED_WAITPID
  939.   /* Pick up all defunct processes */
  940.   for (p = procs; p; p = p->next) {
  941.     if (waitpid (p->pid, (int *) 0, WNOHANG) > 0) {
  942.       p->kill_how = kill_never;
  943.     }
  944.   }
  945. #endif
  946.  
  947.   for (p = procs; p; p = p->next) {
  948.     if (p->kill_how == kill_after_timeout) {
  949.       /* Subprocess may be dead already.  Only need the timeout if not. */
  950.       if (kill (p->pid, SIGTERM) != -1)
  951.     need_timeout = 1;
  952.     } else if (p->kill_how == kill_always) {
  953.       kill (p->pid, SIGKILL);
  954.     }
  955.   }
  956.  
  957.   /* Sleep only if we have to... */
  958.  
  959.   if (need_timeout) sleep (3);
  960.  
  961.   /* OK, the scripts we just timed out for have had a chance to clean up
  962.    * --- now, just get rid of them, and also clean up the system accounting
  963.    * goop...
  964.    */
  965.  
  966.   for (p = procs; p; p = p->next){
  967.  
  968.     if (p->kill_how == kill_after_timeout)
  969.       kill (p->pid, SIGKILL);
  970.  
  971.     if (p->kill_how != kill_never)
  972.       waitpid (p->pid, &status, 0);
  973.   }
  974. }
  975.  
  976. #ifdef __EMX__
  977. int spawn_child_os2 (pool *p, void (*func)(void *), void *data,
  978.          enum kill_conditions kill_how,
  979.          FILE **pipe_in, FILE **pipe_out, char *buffer, int lenp)
  980. {
  981.   int pid;
  982.   int in_fds[2];
  983.   int out_fds[2];
  984.  
  985.   block_alarms();
  986.  
  987.   if (pipe_in && pipe (in_fds) < 0)
  988.   {
  989.       unblock_alarms();
  990.       return 0;
  991.   }
  992.  
  993.   if (pipe_out && pipe (out_fds) < 0) {
  994.     if (pipe_in) {
  995.       close (in_fds[0]); close (in_fds[1]);
  996.     }
  997.     unblock_alarms();
  998.     return 0;
  999.   }
  1000.  
  1001.   if ((pid = fork()) < 0) {
  1002.     if (pipe_in) {
  1003.       close (in_fds[0]); close (in_fds[1]);
  1004.     }
  1005.     if (pipe_out) {
  1006.       close (out_fds[0]); close (out_fds[1]);
  1007.     }
  1008.     unblock_alarms();
  1009.     return 0;
  1010.   }
  1011.  
  1012.   if (!pid) {
  1013.     int stdinpipe[2];
  1014.  
  1015.     /* Due to a limitation of EMX, inheriting socket handles is not
  1016.     allowed so we need to read the input and place it in a pipe and
  1017.     then pass that handle instead of the socket. */
  1018.  
  1019.     if (lenp > 0) {
  1020.         pipe(stdinpipe);
  1021.         setmode(stdinpipe[0], O_BINARY);
  1022.         setmode(stdinpipe[1], O_BINARY);
  1023.         write(stdinpipe[1], buffer, lenp);
  1024.         close(stdinpipe[1]);
  1025.         in_fds[0] = dup(stdinpipe[0]);
  1026.         close(stdinpipe[0]);
  1027.     }
  1028.     /* Always make sure the buffer is free()ed. */
  1029.     free(buffer);
  1030.  
  1031.     /* Child process */
  1032.  
  1033.     if (pipe_out) {
  1034.       close (out_fds[0]);
  1035.       dup2 (out_fds[1], STDOUT_FILENO);
  1036.       close (out_fds[1]);
  1037.     }
  1038.  
  1039.     if (pipe_in) {
  1040.       close (in_fds[1]);
  1041.       dup2 (in_fds[0], STDIN_FILENO);
  1042.       close (in_fds[0]);
  1043.     }
  1044.  
  1045.     /* HP-UX SIGCHLD fix goes here, if someone will remind me what it is... */
  1046.     signal (SIGCHLD, SIG_DFL);    /* Was that it? */
  1047.  
  1048.     func (data);
  1049.     exit (0);            /* Should never get here... */
  1050.   }
  1051.  
  1052.   /* Parent process */
  1053.  
  1054.   note_subprocess (p, pid, kill_how);
  1055.  
  1056.   if (pipe_out) {
  1057.     close (out_fds[1]);
  1058.     /* Need binary mode set for OS/2. */
  1059.     *pipe_out = fdopen (out_fds[0], "rb");
  1060.  
  1061.     if (*pipe_out) note_cleanups_for_file (p, *pipe_out);
  1062.   }
  1063.  
  1064.   if (pipe_in) {
  1065.     close (in_fds[0]);
  1066.     /* Need binary mode set for OS/2. */
  1067.     *pipe_in = fdopen (in_fds[1], "wb");
  1068.  
  1069.     if (*pipe_in) note_cleanups_for_file (p, *pipe_in);
  1070.   }
  1071.  
  1072.   unblock_alarms();
  1073.   return pid;
  1074. }
  1075. #endif
  1076.  
  1077.