home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 25 / CDROM25.iso / Share / linux / apache / contrib / modules / probably_obsolete / mod_simultaneous.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-06-11  |  11.6 KB  |  461 lines

  1. /*-
  2.  * Copyright (c) 1995 The Apache Group. All rights reserved.
  3.  * 
  4.  *
  5.  * Apache httpd license
  6.  * ====================
  7.  * 
  8.  *
  9.  * This is the license for the Apache Server. It covers all the
  10.  * files which come in this distribution, and should never be removed.
  11.  * 
  12.  * The "Apache Group" has based this server, called "Apache", on
  13.  * public domain code distributed under the name "NCSA httpd 1.3".
  14.  * 
  15.  * NCSA httpd 1.3 was placed in the public domain by the National Center 
  16.  * for Supercomputing Applications at the University of Illinois 
  17.  * at Urbana-Champaign.
  18.  * 
  19.  * As requested by NCSA we acknowledge,
  20.  * 
  21.  *  "Portions developed at the National Center for Supercomputing
  22.  *   Applications at the University of Illinois at Urbana-Champaign."
  23.  *
  24.  * Copyright on the sections of code added by the "Apache Group" belong
  25.  * to the "Apache Group" and/or the original authors. The "Apache Group" and
  26.  * authors hereby grant permission for their code, along with the
  27.  * public domain NCSA code, to be distributed under the "Apache" name.
  28.  * 
  29.  * Reuse of "Apache Group" code outside of the Apache distribution should
  30.  * be acknowledged with the following quoted text, to be included with any new
  31.  * work;
  32.  * 
  33.  * "Portions developed by the "Apache Group", taken with permission 
  34.  *  from the Apache Server   http://www.apache.org/apache/   "
  35.  *
  36.  *
  37.  * Permission is hereby granted to anyone to redistribute Apache under
  38.  * the "Apache" name. We do not grant permission for the resale of Apache, but
  39.  * we do grant permission for vendors to bundle Apache free with other software,
  40.  * or to charge a reasonable price for redistribution, provided it is made
  41.  * clear that Apache is free. Permission is also granted for vendors to 
  42.  * sell support for Apache. We explicitly forbid the redistribution of 
  43.  * Apache under any other name.
  44.  * 
  45.  * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND
  46.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  47.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  48.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE
  49.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  50.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  51.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  52.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  53.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  54.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  55.  * SUCH DAMAGE.
  56.  * 
  57.  */
  58.  
  59.  
  60. /*
  61.  * mod_simultaneous.c
  62.  * 
  63.  * Michael Davon 10/95 (davon@web-depot.com)
  64.  * 
  65.  * Limit the number of simultaneous accesses to files in a
  66.  * particular directory.  Allow servers to wait for access.
  67.  *
  68.  * Using this module will increase the load on the server, since
  69.  * it will do a fair amount of file access to manage the locks.
  70.  * However, this module is quite effective at limiting access.
  71.  *
  72.  * This is particularly useful if you only have so much bandwidth
  73.  * on your network connection, and don't want it all sucked up
  74.  * by people looking at pictures.
  75.  *
  76.  * Note that when servers are waiting for access, they wake up
  77.  * once per second, and try again for access.  There is no queue, so
  78.  * whichever waiting process wakes up first (when access is available)
  79.  * gets the access.  Queuing would be nice, but is hard to implement.
  80.  *
  81.  * An effective strategy to limit bandwidth consumption may be 
  82.  * as follows:
  83.  *
  84.  *  SimultaneousAccesses     1
  85.  *  SimultaneousWait        4000
  86.  *  SimultaneousValidate    10
  87.  *
  88.  * This will have the effect of allowing only 1 access to a directory at a
  89.  * time, however, it will keep the server waiting until it finally does
  90.  * get access (up to 4000 seconds), and it will validate the locks every
  91.  * 10 seconds, just to make sure the locking process is still around.
  92.  *
  93.  * Setting "SimultaneousAccesses 0" allows unchecked access.
  94.  *
  95.  * Setting "SimultaneousWait 0" will not wait, and will timeout
  96.  * immediately if there is no slot available.
  97.  *
  98.  * Setting "SimultaneousValidate 0" will never validate the slots.
  99.  *
  100.  * Setting "SimultaneousValidate 1" will validate the slots every second.
  101.  */
  102.  
  103. #include "httpd.h"
  104. #include "http_config.h"
  105. #include "http_core.h"
  106. #include "http_log.h"
  107. #include "http_protocol.h"
  108.  
  109. #define SIMUL_MAX    256             /* max simultaneous accesses */
  110. #define SIMUL_NAME    ".simultaneous"    /* name of lock file */
  111.  
  112.  
  113. /* Defaults for SimultaneousWait, SimultaneousValidate */
  114. #define SIMUL_DEF_WAIT        30
  115. #define SIMUL_DEF_VALIDATE    15
  116.  
  117.  
  118. /* Error code to return on access timeout */
  119. #define SIMUL_TIMEOUT    SERVICE_UNAVAILABLE
  120.  
  121. /* declare this module */
  122. module simultaneous_module;
  123.  
  124. typedef struct simultaneous_struct {
  125.     int     simul_max;
  126.     int        simul_wait;
  127.     int        simul_validate;
  128. } simultaneous_rec;
  129.  
  130. static void         *create_simultaneous(pool *p, char *d)
  131. {
  132.     simultaneous_rec *sr =
  133.     (simultaneous_rec *)pcalloc (p, sizeof(simultaneous_rec));
  134.  
  135.     sr->simul_max = 0;
  136.     sr->simul_wait = SIMUL_DEF_WAIT;
  137.     sr->simul_validate = SIMUL_DEF_VALIDATE;  
  138.  
  139.     return (void *)sr;
  140. }
  141.  
  142. static char                *set_accesses(cmd_parms *cmd,
  143.                       void *srp,
  144.                       char *arg)
  145. {
  146.     simultaneous_rec *sr = (simultaneous_rec *)srp;
  147.  
  148.     if(arg && *arg)
  149.     sr->simul_max = atoi (arg);
  150.     
  151.     if(sr->simul_max < 0)
  152.     sr->simul_max = 0;
  153.  
  154.     return NULL;
  155. }
  156.  
  157. static char         *set_wait(cmd_parms *cmd,
  158.                   void *srp,
  159.                   char *arg)
  160. {
  161.     simultaneous_rec *sr = (simultaneous_rec *)srp;
  162.  
  163.     if(arg && *arg)
  164.     sr->simul_wait = atoi (arg);
  165.  
  166.     if(sr->simul_wait < 0)
  167.     sr->simul_wait = 0;
  168.  
  169.     return NULL;
  170. }
  171.  
  172. static char         *set_validate(cmd_parms *cmd,
  173.                       void *srp,
  174.                       char *arg)
  175. {
  176.     simultaneous_rec *sr = (simultaneous_rec *)srp;
  177.  
  178.     if(arg && *arg)
  179.     sr->simul_validate = atoi (arg);
  180.  
  181.     if(sr->simul_validate < 0)
  182.     sr->simul_validate = 0;
  183.  
  184.     return NULL;
  185. }
  186.  
  187. static command_rec simul_cmds[] = {
  188. { "SimultaneousAccesses", set_accesses, NULL, OR_AUTHCFG, TAKE1, 
  189.       "max simultaneous accesses to this directory" },
  190. { "SimultaneousWait", set_wait, NULL, OR_AUTHCFG, TAKE1, 
  191.       "time (seconds) to wait for access" },
  192. { "SimultaneousValidate", set_validate, NULL, OR_AUTHCFG, TAKE1, 
  193.       "seconds between periodic validation of used slots" },
  194. { NULL }
  195. };
  196.  
  197. static void             mutex_on(int lockf)
  198. {
  199.     static struct flock lock_it = { F_WRLCK, 0, 0, 0 };
  200.  
  201.     int ret;
  202.     
  203.     while ((ret = fcntl(lockf,F_SETLKW, &lock_it)) < 0
  204.        && errno == EINTR)
  205.     continue;
  206.  
  207.     if (ret < 0) {
  208.     fprintf(stderr, "Unknown failure grabbing accept lock.  Exiting!");
  209.     exit(-1);
  210.     }
  211. }
  212.  
  213. static void         mutex_off(int lockf)
  214. {
  215.     static struct flock unlock_it = { F_UNLCK, 0, 0, 0 };
  216.  
  217.     fcntl(lockf, F_SETLKW, &unlock_it);
  218. }
  219.  
  220.  
  221. static int        handle_locking(int lockf,     /* fd to use */
  222.                        int max,        /* num of locks */
  223.                        int release,    /* release the lock? */
  224.                        int validate)    /* validate pids? */
  225. /*
  226.  * return 0 if successful
  227.  */
  228. {
  229.     int         pid = getpid();
  230.     int            pid_array[SIMUL_MAX];
  231.     int            did_it = 0;
  232.     int            status;
  233.     int            i;
  234.  
  235.     if(max > (sizeof(pid_array)/sizeof(pid_array[0])))
  236.     max = (sizeof(pid_array)/sizeof(pid_array[0]));
  237.  
  238.     memset(pid_array, '\0', sizeof(pid_array));
  239.  
  240.     /* Rewind File */
  241.     lseek(lockf, 0L, 0);
  242.  
  243.     /* get exclusive access to the file */
  244.     mutex_on(lockf);
  245.  
  246.     /* Read the PIDs from the file */
  247.     for(i = 0; i < max; i++) {
  248.     status = read(lockf, &pid_array[i], sizeof(pid_array[0]));
  249.     if(status == sizeof(pid_array[0])) { /* Got one */
  250.         continue;
  251.     }
  252.     if(status == 0) /* EOF */
  253.         break;
  254.     mutex_off(lockf);
  255.     perror("error reading lock file");
  256.     return (-1);
  257.     }
  258.  
  259.     if(validate) {
  260.     /* Validate all slots */
  261.     for(i = 0; i < max; i++) {
  262.         if(pid_array[i] && kill(pid_array[i], 0)) { /* No such process */
  263.         pid_array[i] = 0;
  264.         }
  265.     }
  266.     }
  267.  
  268.     if(release) {
  269.     /* Find this PID and release it */
  270.     for(i = 0; i < max; i++) {
  271.         if(pid_array[i] == pid) {
  272.         pid_array[i] = 0;
  273.         did_it = 1;
  274.         goto record_and_exit;
  275.         }
  276.     }
  277.     }
  278.  
  279.     if(!release) {
  280.  
  281.     /* Check for this process already listed */
  282.     for(i = 0; i < max; i++) {
  283.         if(pid_array[i] == pid) {
  284.         did_it = 1;
  285.         goto record_and_exit;
  286.         }
  287.     }
  288.  
  289.     /* Find empty slot and fill it */
  290.     for(i = 0; i < max; i++) {
  291.         if(!pid_array[i]) {
  292.         pid_array[i] = pid;
  293.         did_it = 1;
  294.         goto record_and_exit;
  295.         }
  296.     }
  297.     }
  298.  
  299.  record_and_exit:
  300.  
  301.     /* Rewind File */
  302.     lseek(lockf, 0L, 0);
  303.     
  304.     /* Write the PIDs */
  305.     status = write(lockf, pid_array, sizeof(pid_array[0]) * max);
  306.  
  307.     /* release the lock on the file */
  308.     mutex_off(lockf);
  309.  
  310.     if(did_it)
  311.     return 0;
  312.     else
  313.     return 1;
  314. }
  315.  
  316.  
  317. static int         get_lockfile_for_request(request_rec *r, char **namep)
  318. {
  319.     int            lock_file;
  320.     static char        lock_file_name[256];
  321.     char        *cp;
  322.  
  323.     if(namep)
  324.     *namep = lock_file_name;
  325.  
  326.     if(r->filename && *r->filename) {
  327. #if DEBUG
  328.     fprintf(stderr, "file to lock '%s'\n", r->filename);
  329. #endif
  330.     }
  331.     else {
  332.     return (-1);
  333.     }
  334.  
  335.     strcpy(lock_file_name, r->filename);
  336.  
  337.     if (S_ISDIR(r->finfo.st_mode)) {
  338.     strcat(lock_file_name, "/");
  339.     }
  340.     else {
  341.     if(!(cp = strrchr(lock_file_name, '/')))
  342.         return (-1);
  343.  
  344.     cp[1] = 0;
  345.     }
  346.  
  347.     strcat(lock_file_name, SIMUL_NAME);
  348.  
  349. #if DEBUG
  350.     fprintf(stderr, "lockfile is '%s'\n", lock_file_name);
  351. #endif
  352.  
  353.     if((lock_file = open(lock_file_name, O_RDWR|O_CREAT, 0660)) < 0) {
  354.     fprintf(stderr, "can't open datafile %s\n", lock_file_name);
  355.     perror("error");
  356.     return (-1);
  357.     }
  358.  
  359.     return lock_file;
  360. }
  361.  
  362.  
  363. static int         get_access (request_rec *r)
  364. /*
  365.  * This function always returns DECLINED so that other handlers
  366.  * will be run, except when it fails to get access.  If it fails
  367.  * to get access, it will return SIMUL_TIMEOUT.
  368.  */
  369. {
  370.     int            lock_file;
  371.     char        *lock_file_name;
  372.     char         ebuff[256];
  373.     int            i;
  374.     simultaneous_rec     *srec =
  375.     (simultaneous_rec *)get_module_config(r->per_dir_config,
  376.                           &simultaneous_module);
  377.  
  378.     if(!srec || !srec->simul_max)
  379.     return DECLINED;
  380.     
  381.     lock_file = get_lockfile_for_request(r, &lock_file_name);
  382.     if(lock_file < 0) {
  383.     sprintf(ebuff, "could not get lockfile for %s", lock_file_name);
  384.     log_error(ebuff, r->server);
  385.     return DECLINED;
  386.     }
  387.  
  388.     for (i = 0; i <= srec->simul_wait; i++) {
  389.  
  390.         int validate = 0;
  391.  
  392.     if(srec->simul_validate) {
  393.         if ((i + 1) % srec->simul_validate ==  0)
  394.         validate = 1;
  395.     }
  396.  
  397.     if(handle_locking(lock_file, srec->simul_max, 0, validate) == 0) {
  398.         /* We got a slot */
  399.         close(lock_file);
  400.         return DECLINED;
  401.     }
  402.  
  403.     if(i >= srec->simul_wait)
  404.         break;
  405. #if DEBUG
  406.     sprintf(ebuff, "waiting for access to %s", lock_file_name);
  407.     log_error(ebuff, r->server);
  408. #endif
  409.     sleep(1);
  410.     }
  411.  
  412.     close(lock_file);
  413.     log_reason("too many simultaneous users", r->filename, r);
  414.  
  415.     return SIMUL_TIMEOUT;
  416. }
  417.  
  418.  
  419. static int         release_access (request_rec *r)
  420. /*
  421.  * This function always returns DECLINED so that other handlers
  422.  * will be run.
  423.  */
  424. {
  425.     int            lock_file;
  426.     simultaneous_rec *srec =
  427.       (simultaneous_rec *)get_module_config (r->per_dir_config,
  428.                          &simultaneous_module);
  429.  
  430.     if(!srec || !srec->simul_max)
  431.     return DECLINED;
  432.  
  433.     lock_file = get_lockfile_for_request(r, NULL);
  434.     if(lock_file < 0)
  435.     return DECLINED;
  436.  
  437.     (void) handle_locking(lock_file, srec->simul_max, 1, 0);
  438.     close(lock_file);
  439.  
  440.     return DECLINED;
  441. }
  442.     
  443.  
  444. module simultaneous_module = {
  445.    STANDARD_MODULE_STUFF,
  446.    NULL,            /* initializer */
  447.    create_simultaneous,        /* dir config creater */
  448.    NULL,            /* dir merger --- default is to override */
  449.    NULL,            /* server config */
  450.    NULL,            /* merge server config */
  451.    simul_cmds,            /* command table */
  452.    NULL,            /* handlers */
  453.    NULL,            /* filename translation */
  454.    NULL,                /* check_user_id */
  455.    NULL,            /* check auth */
  456.    get_access,            /* check access */
  457.    NULL,            /* type_checker */
  458.    NULL,            /* fixups */
  459.    release_access,        /* logger */
  460. };
  461.