source: subversion/applications/utils/mod_tile/mod_tile.c @ 28453

Last change on this file since 28453 was 28453, checked in by frederik, 7 years ago

whitespace fixes

File size: 69.6 KB
Line 
1#include "apr.h"
2#include "apr_strings.h"
3#include "apr_thread_proc.h"    /* for RLIMIT stuff */
4#include "apr_optional.h"
5#include "apr_buckets.h"
6#include "apr_lib.h"
7#include "apr_poll.h"
8
9#define APR_WANT_STRFUNC
10#define APR_WANT_MEMFUNC
11#include "apr_want.h"
12
13#include "util_filter.h"
14#include "ap_config.h"
15#include "httpd.h"
16#include "http_config.h"
17#include "http_request.h"
18#include "http_core.h"
19#include "http_protocol.h"
20#include "http_main.h"
21#include "http_log.h"
22#include "util_script.h"
23#include "ap_mpm.h"
24#include "mod_core.h"
25#include "mod_cgi.h"
26#include "util_md5.h"
27
28module AP_MODULE_DECLARE_DATA tile_module;
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <stdarg.h>
34#include <sys/types.h>
35#include <sys/socket.h>
36#include <sys/un.h>
37#include <unistd.h>
38#include <fcntl.h>
39#include <errno.h>
40#include <limits.h>
41#include <time.h>
42#include <netinet/in.h>
43#include <arpa/inet.h>
44
45
46#include "gen_tile.h"
47#include "protocol.h"
48#include "render_config.h"
49#include "store.h"
50#include "dir_utils.h"
51#include "mod_tile.h"
52
53
54
55#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
56#include "unixd.h"
57#define MOD_TILE_SET_MUTEX_PERMS /* XXX Apache should define something */
58#endif
59
60
61apr_shm_t *stats_shm;
62apr_shm_t *delaypool_shm;
63char *shmfilename;
64char *shmfilename_delaypool;
65apr_global_mutex_t *stats_mutex;
66apr_global_mutex_t *delay_mutex;
67char *mutexfilename;
68
69static int error_message(request_rec *r, const char *format, ...)
70                 __attribute__ ((format (printf, 2, 3)));
71
72static int error_message(request_rec *r, const char *format, ...)
73{
74    va_list ap;
75    va_start(ap, format);
76    int len;
77    char *msg;
78
79    len = vasprintf(&msg, format, ap);
80
81    if (msg) {
82        //ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "%s", msg);
83        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "%s", msg);
84        r->content_type = "text/plain";
85        if (!r->header_only)
86            ap_rputs(msg, r);
87        free(msg);
88    }
89
90    return OK;
91}
92
93int socket_init(request_rec *r)
94{
95    ap_conf_vector_t *sconf = r->server->module_config;
96    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
97
98    int fd;
99    struct sockaddr_un addr;
100
101    fd = socket(PF_UNIX, SOCK_STREAM, 0);
102    if (fd < 0) {
103        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "failed to create unix socket");
104        return FD_INVALID;
105    }
106
107    bzero(&addr, sizeof(addr));
108    addr.sun_family = AF_UNIX;
109    strncpy(addr.sun_path, scfg->renderd_socket_name, sizeof(addr.sun_path));
110
111    if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
112        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "socket connect failed for: %s with reason: %s", scfg->renderd_socket_name, strerror(errno));
113        close(fd);
114        return FD_INVALID;
115    }
116    return fd;
117}
118
119int request_tile(request_rec *r, struct protocol *cmd, int renderImmediately)
120{
121    int fd;
122    int ret = 0;
123    int retry = 1;
124    struct protocol resp;
125
126    ap_conf_vector_t *sconf = r->server->module_config;
127    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
128
129    fd = socket_init(r);
130
131    if (fd == FD_INVALID) {
132        ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, "Failed to connect to renderer");
133        return 0;
134    }
135
136    // cmd has already been partial filled, fill in the rest
137    cmd->ver = PROTO_VER;
138    switch (renderImmediately) {
139    case 0: { cmd->cmd = cmdDirty; break;}
140    case 1: { cmd->cmd = cmdRender; break;}
141    case 2: { cmd->cmd = cmdRenderPrio; break;}
142    }
143
144    if (scfg->bulkMode) cmd->cmd = cmdRenderBulk; 
145
146    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Requesting style(%s) z(%d) x(%d) y(%d) from renderer with priority %d", cmd->xmlname, cmd->z, cmd->x, cmd->y, cmd->cmd);
147    do {
148        ret = send(fd, cmd, sizeof(struct protocol), 0);
149
150        if (ret == sizeof(struct protocol))
151            break;
152       
153        if (errno != EPIPE) {
154            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "request_tile: Failed to send request to renderer: %s", strerror(errno));
155            close(fd);
156            return 0;
157        }
158        close(fd);
159
160        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "request_tile: Reconnecting to rendering socket after failed request due to sigpipe");
161
162        fd = socket_init(r);
163        if (fd == FD_INVALID)
164            return 0;
165    } while (retry--);
166
167    if (renderImmediately) {
168        struct timeval tv = {(renderImmediately > 1?scfg->request_timeout_priority:scfg->request_timeout), 0 };
169        fd_set rx;
170        int s;
171
172        while (1) {
173            FD_ZERO(&rx);
174            FD_SET(fd, &rx);
175            s = select(fd+1, &rx, NULL, NULL, &tv);
176            if (s == 1) {
177                bzero(&resp, sizeof(struct protocol));
178                ret = recv(fd, &resp, sizeof(struct protocol), 0);
179                if (ret != sizeof(struct protocol)) {
180                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "request_tile: Failed to read response from rendering socket %s",
181                                  strerror(errno));
182                    break;
183                }
184
185                if (cmd->x == resp.x && cmd->y == resp.y && cmd->z == resp.z && !strcmp(cmd->xmlname, resp.xmlname)) {
186                    close(fd);
187                    if (resp.cmd == cmdDone)
188                        return 1;
189                    else
190                        return 0;
191                } else {
192                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
193                       "Response does not match request: xml(%s,%s) z(%d,%d) x(%d,%d) y(%d,%d)", cmd->xmlname,
194                       resp.xmlname, cmd->z, resp.z, cmd->x, resp.x, cmd->y, resp.y);
195                }
196            } else {
197                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
198                              "request_tile: Request xml(%s) z(%d) x(%d) y(%d) could not be rendered in %i seconds",
199                              cmd->xmlname, cmd->z, cmd->x, cmd->y,
200                              (renderImmediately > 1?scfg->request_timeout_priority:scfg->request_timeout));
201                break;
202            }
203        }
204    }
205
206    close(fd);
207    return 0;
208}
209
210static apr_time_t getPlanetTime(request_rec *r)
211{
212    static apr_time_t last_check[XMLCONFIGS_MAX];
213    static apr_time_t planet_timestamp[XMLCONFIGS_MAX];
214    static pthread_mutex_t planet_lock = PTHREAD_MUTEX_INITIALIZER;
215    apr_time_t now = r->request_time;
216    struct apr_finfo_t s;
217
218    struct tile_request_data * rdata = (struct tile_request_data *)ap_get_module_config(r->request_config, &tile_module);
219    if (rdata == NULL) {
220        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "No per request configuration data");
221        return planet_timestamp[0];
222    }
223    struct protocol * cmd = rdata->cmd;
224
225
226    pthread_mutex_lock(&planet_lock);
227    // Only check for updates periodically
228    if (now < last_check[rdata->layerNumber] + apr_time_from_sec(300)) {
229        pthread_mutex_unlock(&planet_lock);
230        return planet_timestamp[rdata->layerNumber];
231    }
232
233    ap_conf_vector_t *sconf = r->server->module_config;
234    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
235
236    char filename[PATH_MAX];
237    snprintf(filename, PATH_MAX-1, "%s/%s%s", scfg->tile_dir, cmd->xmlname, PLANET_TIMESTAMP);
238
239    last_check[rdata->layerNumber] = now;
240    if (apr_stat(&s, filename, APR_FINFO_MIN, r->pool) != APR_SUCCESS) {
241        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "per tile style planet time stamp (%s) missing, trying global one", filename);
242        snprintf(filename, PATH_MAX-1, "%s/%s", scfg->tile_dir, PLANET_TIMESTAMP);
243        if (apr_stat(&s, filename, APR_FINFO_MIN, r->pool) != APR_SUCCESS) {
244            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Global planet time stamp file (%s) is missing. Assuming 3 days old.", filename);
245            // Make something up
246            planet_timestamp[rdata->layerNumber] = now - apr_time_from_sec(3 * 24 * 60 * 60);
247        } else {
248            if (s.mtime != planet_timestamp[rdata->layerNumber]) {
249                planet_timestamp[rdata->layerNumber] = s.mtime;
250                char * timestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
251                    apr_rfc822_date(timestr, (planet_timestamp[rdata->layerNumber]));
252                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Global planet file time stamp (%s) updated to %s", filename, timestr);
253            }
254        }
255    } else {
256        if (s.mtime != planet_timestamp[rdata->layerNumber]) {
257            planet_timestamp[rdata->layerNumber] = s.mtime;
258            char * timestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
259            apr_rfc822_date(timestr, (planet_timestamp[rdata->layerNumber]));
260            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Per style planet file time stamp (%s) updated to %s", filename, timestr);
261        }
262    }
263    pthread_mutex_unlock(&planet_lock);
264    return planet_timestamp[rdata->layerNumber];
265}
266
267static enum tileState tile_state_once(request_rec *r)
268{
269    apr_status_t rv;
270    apr_finfo_t *finfo = &r->finfo;
271
272    if (!(finfo->valid & APR_FINFO_MTIME)) {
273        rv = apr_stat(finfo, r->filename, APR_FINFO_MIN, r->pool);
274        if (rv != APR_SUCCESS) {
275            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_state_once: File %s is missing", r->filename);
276            return tileMissing;
277        }
278    }
279
280    if (finfo->mtime < getPlanetTime(r)) {
281        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_state_once: File %s is old", r->filename);
282        return tileOld;
283    }
284
285    return tileCurrent;
286}
287
288static enum tileState tile_state(request_rec *r, struct protocol *cmd)
289{
290    enum tileState state = tile_state_once(r);
291#ifdef METATILEFALLBACK
292    if (state == tileMissing) {
293        ap_conf_vector_t *sconf = r->server->module_config;
294        tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
295
296        // Try fallback to plain PNG
297        char path[PATH_MAX];
298        xyz_to_path(path, sizeof(path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
299        r->filename = apr_pstrdup(r->pool, path);
300        state = tile_state_once(r);
301        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "png fallback %d/%d/%d",x,y,z);
302
303        if (state == tileMissing) {
304            // PNG not available either, if it gets rendered, it'll now be a .meta
305            xyz_to_meta(path, sizeof(path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
306            r->filename = apr_pstrdup(r->pool, path);
307        }
308    }
309#endif
310    return state;
311}
312
313static void add_expiry(request_rec *r, struct protocol * cmd)
314{
315    apr_time_t holdoff;
316    apr_table_t *t = r->headers_out;
317    enum tileState state = tile_state(r, cmd);
318    apr_finfo_t *finfo = &r->finfo;
319    char *timestr;
320    long int planetTimestamp, maxAge, minCache, lastModified;
321
322    ap_conf_vector_t *sconf = r->server->module_config;
323    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
324
325    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "expires(%s), uri(%s), filename(%s), path_info(%s)\n",
326                  r->handler, r->uri, r->filename, r->path_info);
327
328    /* If the hostname matches the "extended caching hostname" then set the cache age accordingly */
329    if ((scfg->cache_extended_hostname[0] != 0) && (strstr(r->hostname,
330            scfg->cache_extended_hostname) != NULL)) {
331        maxAge = scfg->cache_extended_duration;
332    } else {
333
334        /* Test if the tile we are serving is out of date, then set a low maxAge*/
335        if (state == tileOld) {
336            holdoff = (scfg->cache_duration_dirty / 2) * (rand() / (RAND_MAX
337                    + 1.0));
338            maxAge = scfg->cache_duration_dirty + holdoff;
339        } else {
340            // cache heuristic based on zoom level
341            if (cmd->z > MAX_ZOOM) {
342                minCache = 0;
343                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
344                        "z (%i) is larger than MAXZOOM %i\n", cmd->z, MAX_ZOOM);
345            } else {
346                minCache = scfg->mincachetime[cmd->z];
347            }
348            // Time to the next known complete rerender
349            planetTimestamp = apr_time_sec(getPlanetTime(r)
350                    + apr_time_from_sec(PLANET_INTERVAL) - r->request_time);
351            // Time since the last render of this tile
352            lastModified = (int) (((double) apr_time_sec(r->request_time
353                    - finfo->mtime))
354                    * scfg->cache_duration_last_modified_factor);
355            // Add a random jitter of 3 hours to space out cache expiry
356            holdoff = (3 * 60 * 60) * (rand() / (RAND_MAX + 1.0));
357
358            maxAge = MAX(minCache, planetTimestamp);
359            maxAge = MAX(maxAge, lastModified);
360            maxAge += holdoff;
361
362            ap_log_rerror(
363                    APLOG_MARK,
364                    APLOG_DEBUG,
365                    0,
366                    r,
367                    "caching heuristics: next planet render %ld; zoom level based %ld; last modified %ld\n",
368                    planetTimestamp, minCache, lastModified);
369        }
370
371        maxAge = MIN(maxAge, scfg->cache_duration_max);
372    }
373
374    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Setting tiles maxAge to %ld\n", maxAge);
375
376    apr_table_mergen(t, "Cache-Control",
377                     apr_psprintf(r->pool, "max-age=%" APR_TIME_T_FMT,
378                     maxAge));
379    timestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
380    apr_rfc822_date(timestr, (apr_time_from_sec(maxAge) + r->request_time));
381    apr_table_setn(t, "Expires", timestr);
382}
383
384double get_load_avg(request_rec *r)
385{
386    double loadavg[1];
387    int n = getloadavg(loadavg, 1);
388
389    if (n < 1)
390        return 1000;
391    else
392        return loadavg[0];
393}
394
395static int get_global_lock(request_rec *r, apr_global_mutex_t * mutex) {
396    apr_status_t rs;
397    int camped;
398
399    for (camped = 0; camped < MAXCAMP; camped++) {
400        rs = apr_global_mutex_trylock(mutex);
401        if (APR_STATUS_IS_EBUSY(rs)) {
402            apr_sleep(CAMPOUT);
403        } else if (rs == APR_SUCCESS) {
404            return 1;
405        } else if (APR_STATUS_IS_ENOTIMPL(rs)) {
406            /* If it's not implemented, just hang in the mutex. */
407            rs = apr_global_mutex_lock(mutex);
408            if (rs == APR_SUCCESS) {
409                return 1;
410            } else {
411                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Could not get hardlock");
412                return 0;
413            }
414        } else {
415            ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, "Unknown return status from trylock");
416            return 0;
417        }
418    }
419    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Timedout trylock");
420    return 0;
421}
422
423static int incRespCounter(int resp, request_rec *r, struct protocol * cmd, int layerNumber) {
424    stats_data *stats;
425
426    ap_conf_vector_t *sconf = r->server->module_config;
427    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
428
429    if (!scfg->enableGlobalStats) {
430        /* If tile stats reporting is not enable
431         * pretend we correctly updated the counter to
432         * not fill the logs with warnings about failed
433         * stats
434         */
435        return 1;
436    }
437
438    if (get_global_lock(r, stats_mutex) != 0) {
439        stats = (stats_data *)apr_shm_baseaddr_get(stats_shm);
440        switch (resp) {
441        case OK: {
442            stats->noResp200++;
443            if (cmd != NULL) {
444                stats->noRespZoom[cmd->z]++;
445                stats->noResp200Layer[layerNumber]++;
446            }
447            break;
448        }
449        case HTTP_NOT_MODIFIED: {
450            stats->noResp304++;
451            if (cmd != NULL) {
452                stats->noRespZoom[cmd->z]++;
453                stats->noResp200Layer[layerNumber]++;
454            }
455            break;
456        }
457        case HTTP_NOT_FOUND: {
458            stats->noResp404++;
459            stats->noResp404Layer[layerNumber]++;
460            break;
461        }
462        case HTTP_SERVICE_UNAVAILABLE: {
463            stats->noResp503++;
464            break;
465        }
466        case HTTP_INTERNAL_SERVER_ERROR: {
467            stats->noResp5XX++;
468            break;
469        }
470        default: {
471            stats->noRespOther++;
472        }
473
474        }
475        apr_global_mutex_unlock(stats_mutex);
476        /* Swallowing the result because what are we going to do with it at
477         * this stage?
478         */
479        return 1;
480    } else {
481        return 0;
482    }
483}
484
485static int incFreshCounter(int status, request_rec *r) {
486    stats_data *stats;
487
488    ap_conf_vector_t *sconf = r->server->module_config;
489    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
490
491    if (!scfg->enableGlobalStats) {
492        /* If tile stats reporting is not enable
493         * pretend we correctly updated the counter to
494         * not fill the logs with warnings about failed
495         * stats
496         */
497        return 1;
498    }
499
500    if (get_global_lock(r, stats_mutex) != 0) {
501        stats = (stats_data *)apr_shm_baseaddr_get(stats_shm);
502        switch (status) {
503        case FRESH: {
504            stats->noFreshCache++;
505            break;
506        }
507        case FRESH_RENDER: {
508            stats->noFreshRender++;
509            break;
510        }
511        case OLD: {
512            stats->noOldCache++;
513            break;
514        }
515        case OLD_RENDER: {
516            stats->noOldRender++;
517            break;
518        }
519        }
520        apr_global_mutex_unlock(stats_mutex);
521        /* Swallowing the result because what are we going to do with it at
522         * this stage?
523         */
524        return 1;
525    } else {
526        return 0;
527    }
528}
529
530static int delay_allowed(request_rec *r, enum tileState state) {
531    delaypool * delayp;
532    int delay = 0;
533    int i,j;
534
535    ap_conf_vector_t *sconf = r->server->module_config;
536    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
537    delayp = (delaypool *)apr_shm_baseaddr_get(delaypool_shm);
538
539    /* TODO: fix IPv6 compatibility */
540    in_addr_t ip = inet_addr(r->connection->remote_ip);
541   
542    int hashkey = ip % DELAY_HASHTABLE_WHITELIST_SIZE;
543    if (delayp->whitelist[hashkey] == ip) {
544        return 1;
545    }
546
547    /* If a delaypool fillup is ongoing, just skip accounting to not block on a lock */
548    if (delayp->locked) {
549        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "skipping delay pool accounting, during fillup procedure\n");
550        return 1;
551    }
552
553   
554    hashkey = ip % DELAY_HASHTABLE_SIZE;
555   
556    if (get_global_lock(r,delay_mutex) == 0) {
557        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Could not acquire lock, skipping delay pool accounting\n");
558        return 1;
559    };
560    if (delayp->users[hashkey].ip_addr == ip) {
561        /* Repeat the process to determin if we have tockens in the bucket, as the fillup only runs once a client hits an empty bucket,
562           so in the mean time, the bucket might have been filled */
563        for (j = 0; j < 3; j++) {
564            //ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Checking delays: Current poolsize: %i tiles and %i renders\n", delayp->users[hashkey].available_tiles, delayp->users[hashkey].available_render_req);
565            delay = 0;
566            if (delayp->users[hashkey].available_tiles > 0) {
567                delayp->users[hashkey].available_tiles--;
568            } else {
569                delay = 1;
570            }
571            if (state == tileMissing) {
572                if (delayp->users[hashkey].available_render_req > 0) {
573                    delayp->users[hashkey].available_render_req--;
574                } else {
575                    delay = 2;
576                }
577            }
578
579            if (delay > 0) {
580                /* If we are on the second round, we really  hit an empty delaypool, timeout for a while to slow down clients */
581                if (j > 0) {
582                    apr_global_mutex_unlock(delay_mutex);
583                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Delaypool: Client %s has hit its limits, throttling (%i)\n", r->connection->remote_ip, delay);
584                    sleep(CLIENT_PENALTY);
585                    if (get_global_lock(r,delay_mutex) == 0) {
586                        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Could not acquire lock, but had to delay\n");
587                        return 0;
588                    };
589                }
590                /* We hit an empty bucket, so run the bucket fillup procedure to check if new tokens should have arrived in the mean time. */
591                apr_time_t now = apr_time_now();
592                int tiles_topup = (now - delayp->last_tile_fillup) / scfg->delaypoolTileRate;
593                int render_topup = (now - delayp->last_render_fillup) / scfg->delaypoolRenderRate;
594                //ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Filling up pools with %i tiles and %i renders\n", tiles_topup, render_topup);
595                if ((tiles_topup > 0) || (render_topup > 0)) {
596                    delayp->locked = 1;
597                    for (i = 0; i < DELAY_HASHTABLE_SIZE; i++) {
598                        delayp->users[i].available_tiles += tiles_topup;
599                        delayp->users[i].available_render_req += render_topup;
600                        if (delayp->users[i].available_tiles > scfg->delaypoolTileSize) {
601                            delayp->users[i].available_tiles = scfg->delaypoolTileSize;
602                        }
603                        if (delayp->users[i].available_render_req > scfg->delaypoolRenderSize) {
604                            delayp->users[i].available_render_req = scfg->delaypoolRenderSize;
605                        }
606                    }
607                    delayp->locked = 0;
608                }
609                delayp->last_tile_fillup += scfg->delaypoolTileRate*tiles_topup;
610                delayp->last_render_fillup += scfg->delaypoolRenderRate*render_topup;               
611               
612            } else {
613                break;
614            }
615        }
616    } else {
617        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Creating a new delaypool for ip %s, overwriting %s\n", r->connection->remote_ip, inet_ntoa(inet_makeaddr(delayp->users[hashkey].ip_addr,delayp->users[hashkey].ip_addr)));
618        delayp->users[hashkey].ip_addr = ip;
619        delayp->users[hashkey].available_tiles = scfg->delaypoolTileSize;
620        delayp->users[hashkey].available_render_req = scfg->delaypoolRenderSize;
621        delay = 0;
622    }
623    apr_global_mutex_unlock(delay_mutex);
624
625    if (delay > 0) {
626        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Delaypool: Client %s has hit its limits, rejecting (%i)\n", r->connection->remote_ip, delay);
627        return 0;
628    } else {
629        return 1;
630    }
631}
632
633static int tile_handler_dirty(request_rec *r)
634{
635    if(strcmp(r->handler, "tile_dirty"))
636        return DECLINED;
637
638    struct tile_request_data * rdata = (struct tile_request_data *)ap_get_module_config(r->request_config, &tile_module);
639    struct protocol * cmd = rdata->cmd;
640    if (cmd == NULL)
641        return DECLINED;
642
643    ap_conf_vector_t *sconf = r->server->module_config;
644    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
645    if (scfg->bulkMode) return OK;
646
647    request_tile(r, cmd, 0);
648    return error_message(r, "Tile submitted for rendering\n");
649}
650
651static int tile_storage_hook(request_rec *r)
652{
653//    char abs_path[PATH_MAX];
654    int avg;
655    int renderPrio = 0;
656    enum tileState state;
657
658    if (!r->handler)
659        return DECLINED;
660
661    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "tile_storage_hook: handler(%s), uri(%s), filename(%s), path_info(%s)",
662                  r->handler, r->uri, r->filename, r->path_info);
663
664    // Any status request is OK. tile_dirty also doesn't need to be handled, as tile_handler_dirty will take care of it
665    if (!strcmp(r->handler, "tile_status") || !strcmp(r->handler, "tile_dirty") || !strcmp(r->handler, "tile_mod_stats"))
666        return OK;
667
668    if (strcmp(r->handler, "tile_serve"))
669        return DECLINED;
670
671    struct tile_request_data * rdata = (struct tile_request_data *)ap_get_module_config(r->request_config, &tile_module);
672    struct protocol * cmd = rdata->cmd;
673    if (cmd == NULL)
674        return DECLINED;
675
676/*
677should already be done
678    // Generate the tile filename
679#ifdef METATILE
680    ap_conf_vector_t *sconf = r->server->module_config;
681    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
682    xyz_to_meta(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
683#else
684    xyz_to_path(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
685#endif
686    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "abs_path(%s)", abs_path);
687    r->filename = apr_pstrdup(r->pool, abs_path);
688*/
689    avg = get_load_avg(r);
690    state = tile_state(r, cmd);
691
692    ap_conf_vector_t *sconf = r->server->module_config;
693    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
694
695    if (scfg->enableTileThrottling && !delay_allowed(r, state)) {
696        if (!incRespCounter(HTTP_SERVICE_UNAVAILABLE, r, cmd, rdata->layerNumber)) {
697                   ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
698                        "Failed to increase response stats counter");
699        }
700        return HTTP_SERVICE_UNAVAILABLE;       
701    }
702
703    switch (state) {
704        case tileCurrent:
705            if (!incFreshCounter(FRESH, r)) {
706                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
707                    "Failed to increase fresh stats counter");
708            }
709            return OK;
710            break;
711        case tileOld:
712            if (scfg->bulkMode) {
713                return OK;
714            } else if (avg > scfg->max_load_old) {
715               // Too much load to render it now, mark dirty but return old tile
716               request_tile(r, cmd, 0);
717               ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Load larger max_load_old (%d). Mark dirty and deliver from cache.", scfg->max_load_old);
718               if (!incFreshCounter(OLD, r)) {
719                   ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
720                        "Failed to increase fresh stats counter");
721               }
722               return OK;
723            }
724            renderPrio = 1;
725            break;
726        case tileMissing:
727            if (avg > scfg->max_load_missing) {
728               request_tile(r, cmd, 0);
729               ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Load larger max_load_missing (%d). Return HTTP_NOT_FOUND.", scfg->max_load_missing);
730               if (!incRespCounter(HTTP_NOT_FOUND, r, cmd, rdata->layerNumber)) {
731                   ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
732                        "Failed to increase response stats counter");
733               }
734               return HTTP_NOT_FOUND;
735            }
736            renderPrio = 2;
737            break;
738    }
739
740    if (request_tile(r, cmd, renderPrio)) {
741        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Update file info abs_path(%s)", r->filename);
742        // Need to update fileinfo for new rendered tile
743        apr_stat(&r->finfo, r->filename, APR_FINFO_MIN, r->pool);
744        if (!incFreshCounter(FRESH_RENDER, r)) {
745            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
746                    "Failed to increase fresh stats counter");
747        }
748        return OK;
749    }
750
751    if (state == tileOld) {
752        if (!incFreshCounter(OLD_RENDER, r)) {
753            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
754                    "Failed to increase fresh stats counter");
755        }
756        return OK;
757    }
758    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_storage_hook: Missing tile was not rendered in time. Returning File Not Found");
759    if (!incRespCounter(HTTP_NOT_FOUND, r, cmd, rdata->layerNumber)) {
760        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
761                "Failed to increase response stats counter");
762    }
763
764    return HTTP_NOT_FOUND;
765}
766
767static int tile_handler_status(request_rec *r)
768{
769    enum tileState state;
770    char time_str[APR_CTIME_LEN];
771
772    if(strcmp(r->handler, "tile_status"))
773        return DECLINED;
774
775    struct tile_request_data * rdata = (struct tile_request_data *)ap_get_module_config(r->request_config, &tile_module);
776    struct protocol * cmd = rdata->cmd;
777    if (cmd == NULL){
778        sleep(CLIENT_PENALTY);
779        return HTTP_NOT_FOUND;
780    }
781
782    state = tile_state(r, cmd);
783    if (state == tileMissing)
784        return error_message(r, "Unable to find a tile at %s\n", r->filename);
785    apr_ctime(time_str, r->finfo.mtime);
786
787    return error_message(r, "Tile is %s. Last rendered at %s\n", (state == tileOld) ? "due to be rendered" : "clean", time_str);
788}
789
790static int tile_handler_mod_stats(request_rec *r)
791{
792    stats_data * stats;
793    stats_data local_stats;
794    int i;
795
796    if (strcmp(r->handler, "tile_mod_stats"))
797        return DECLINED;
798
799    ap_conf_vector_t *sconf = r->server->module_config;
800    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
801    tile_config_rec *tile_configs = (tile_config_rec *) scfg->configs->elts;
802
803    if (!scfg->enableGlobalStats) {
804        return error_message(r, "Stats are not enabled for this server");
805    }
806
807    if (get_global_lock(r,stats_mutex) != 0) {
808        //Copy over the global counter variable into
809        //local variables, that we can immediately
810        //release the lock again
811        stats = (stats_data *) apr_shm_baseaddr_get(stats_shm);
812        memcpy(&local_stats, stats, sizeof(stats_data));
813        apr_global_mutex_unlock(stats_mutex);
814    } else {
815        return error_message(r, "Failed to acquire lock, can't display stats");
816    }
817
818    ap_rprintf(r, "NoResp200: %li\n", local_stats.noResp200);
819    ap_rprintf(r, "NoResp304: %li\n", local_stats.noResp304);
820    ap_rprintf(r, "NoResp404: %li\n", local_stats.noResp404);
821    ap_rprintf(r, "NoResp503: %li\n", local_stats.noResp503);
822    ap_rprintf(r, "NoResp5XX: %li\n", local_stats.noResp5XX);
823    ap_rprintf(r, "NoRespOther: %li\n", local_stats.noRespOther);
824    ap_rprintf(r, "NoFreshCache: %li\n", local_stats.noFreshCache);
825    ap_rprintf(r, "NoOldCache: %li\n", local_stats.noOldCache);
826    ap_rprintf(r, "NoFreshRender: %li\n", local_stats.noFreshRender);
827    ap_rprintf(r, "NoOldRender: %li\n", local_stats.noOldRender);
828    for (i = 0; i <= MAX_ZOOM; i++) {
829        ap_rprintf(r, "NoRespZoom%02i: %li\n", i, local_stats.noRespZoom[i]);
830    }
831    for (i = 0; i < scfg->configs->nelts; ++i) {
832            tile_config_rec *tile_config = &tile_configs[i];
833            ap_rprintf(r,"NoRes200Layer%s: %li\n", tile_config->xmlname, local_stats.noResp200Layer[i]);
834            ap_rprintf(r,"NoRes404Layer%s: %li\n", tile_config->xmlname, local_stats.noResp404Layer[i]);
835    }
836
837
838
839    return OK;
840}
841
842static int tile_handler_serve(request_rec *r)
843{
844    const int tile_max = MAX_SIZE;
845    unsigned char err_msg[4096];
846    unsigned char *buf;
847    int len;
848    apr_status_t errstatus;
849
850    if(strcmp(r->handler, "tile_serve"))
851        return DECLINED;
852
853    struct tile_request_data * rdata = (struct tile_request_data *)ap_get_module_config(r->request_config, &tile_module);
854    struct protocol * cmd = rdata->cmd;
855    if (cmd == NULL){
856        sleep(CLIENT_PENALTY);
857        if (!incRespCounter(HTTP_NOT_FOUND, r, cmd, rdata->layerNumber)) {
858            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
859                    "Failed to increase response stats counter");
860        }
861        return HTTP_NOT_FOUND;
862    }
863
864    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_handler_serve: xml(%s) z(%d) x(%d) y(%d)", cmd->xmlname, cmd->z, cmd->x, cmd->y);
865
866    // FIXME: It is a waste to do the malloc + read if we are fulfilling a HEAD or returning a 304.
867    buf = malloc(tile_max);
868    if (!buf) {
869        if (!incRespCounter(HTTP_INTERNAL_SERVER_ERROR, r, cmd, rdata->layerNumber)) {
870            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
871                    "Failed to increase response stats counter");
872        }
873        return HTTP_INTERNAL_SERVER_ERROR;
874    }
875
876    err_msg[0] = 0;
877    len = tile_read(cmd->xmlname, cmd->x, cmd->y, cmd->z, buf, tile_max, err_msg);
878    if (len > 0) {
879#if 0
880        // Set default Last-Modified and Etag headers
881        ap_update_mtime(r, r->finfo.mtime);
882        ap_set_last_modified(r);
883        ap_set_etag(r);
884#else
885        // Use MD5 hash as only cache attribute.
886        // If a tile is re-rendered and produces the same output
887        // then we can continue to use the previous cached copy
888        char *md5 = ap_md5_binary(r->pool, buf, len);
889        apr_table_setn(r->headers_out, "ETag",
890                        apr_psprintf(r->pool, "\"%s\"", md5));
891#endif
892        tile_server_conf *scfg = ap_get_module_config(r->server->module_config, &tile_module);
893        tile_config_rec *tile_configs = (tile_config_rec *) scfg->configs->elts;
894        ap_set_content_type(r, tile_configs[rdata->layerNumber].mimeType);
895        ap_set_content_length(r, len);
896        add_expiry(r, cmd);
897        if ((errstatus = ap_meets_conditions(r)) != OK) {
898            free(buf);
899            if (!incRespCounter(errstatus, r, cmd, rdata->layerNumber)) {
900                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
901                        "Failed to increase response stats counter");
902            }
903            return errstatus;
904        } else {
905            ap_rwrite(buf, len, r);
906            free(buf);
907            if (!incRespCounter(errstatus, r, cmd, rdata->layerNumber)) {
908                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
909                        "Failed to increase response stats counter");
910            }
911            return OK;
912        }
913    }
914    free(buf);
915    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Failed to read tile from disk: %s", err_msg);
916    if (!incRespCounter(HTTP_NOT_FOUND, r, cmd, rdata->layerNumber)) {
917        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
918                "Failed to increase response stats counter");
919    }
920    return DECLINED;
921}
922
923static int tile_translate(request_rec *r)
924{
925    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: uri(%s)", r->uri);
926
927    int i,n,limit,oob;
928    char option[11];
929
930    ap_conf_vector_t *sconf = r->server->module_config;
931    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
932
933    tile_config_rec *tile_configs = (tile_config_rec *) scfg->configs->elts;
934
935    /*
936     * The page /mod_tile returns global stats about the number of tiles
937     * handled and in what state those tiles were.
938     * This should probably not be hard coded
939     */
940    if (!strncmp("/mod_tile", r->uri, strlen("/mod_tile"))) {
941        r->handler = "tile_mod_stats";
942        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
943                "tile_translate: retrieving global mod_tile stats");
944        return OK;
945    }
946
947    for (i = 0; i < scfg->configs->nelts; ++i) {
948        tile_config_rec *tile_config = &tile_configs[i];
949
950        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: testing baseuri(%s) name(%s) extension(%s)",
951                tile_config->baseuri, tile_config->xmlname, tile_config->fileExtension );
952
953        if (!strncmp(tile_config->baseuri, r->uri, strlen(tile_config->baseuri))) {
954
955            struct tile_request_data * rdata = (struct tile_request_data *) apr_pcalloc(r->pool, sizeof(struct tile_request_data));
956            struct protocol * cmd = (struct protocol *) apr_pcalloc(r->pool, sizeof(struct protocol));
957            bzero(cmd, sizeof(struct protocol));
958            bzero(rdata, sizeof(struct tile_request_data));
959            char extension[256];
960            n = sscanf(r->uri+strlen(tile_config->baseuri),"%d/%d/%d.%[a-z]/%10s", &(cmd->z), &(cmd->x), &(cmd->y), extension, option);
961            if (n < 4) {
962                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Invalid URL for tilelayer %s", tile_config->xmlname);
963                return DECLINED;
964            }
965            if (strcmp(extension, tile_config->fileExtension) != 0) {
966                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Invalid file extension (%s) for tilelayer %s, required %s",
967                        extension, tile_config->xmlname, tile_config->fileExtension);
968                return DECLINED;
969            }
970
971            oob = (cmd->z < 0 || cmd->z > MAX_ZOOM);
972            if (!oob) {
973                 // valid x/y for tiles are 0 ... 2^zoom-1
974                 limit = (1 << cmd->z) - 1;
975                 oob =  (cmd->x < 0 || cmd->x > limit || cmd->y < 0 || cmd->y > limit);
976            }
977
978            if (oob) {
979                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: request for %s was outside of allowed bounds", tile_config->xmlname);
980                sleep(CLIENT_PENALTY);
981                //Don't increase stats counter here,
982                //As we are interested in valid tiles only
983                return HTTP_NOT_FOUND;
984            }
985
986            strcpy(cmd->xmlname, tile_config->xmlname);
987
988            // Store a copy for later
989            rdata->cmd = cmd;
990            rdata->layerNumber = i;
991            ap_set_module_config(r->request_config, &tile_module, rdata);
992
993            // Generate the tile filename?
994            char abs_path[PATH_MAX];
995#ifdef METATILE
996            xyz_to_meta(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
997#else
998            xyz_to_path(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
999#endif
1000            r->filename = apr_pstrdup(r->pool, abs_path);
1001
1002            if (n == 5) {
1003                if (!strcmp(option, "status")) r->handler = "tile_status";
1004                else if (!strcmp(option, "dirty")) r->handler = "tile_dirty";
1005                else return DECLINED;
1006            } else {
1007                r->handler = "tile_serve";
1008            }
1009
1010            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: op(%s) xml(%s) mime(%s) z(%d) x(%d) y(%d)",
1011                    r->handler , cmd->xmlname, tile_config->mimeType, cmd->z, cmd->x, cmd->y);
1012
1013            return OK;
1014        }
1015    }
1016    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: No suitable tile layer found");
1017    return DECLINED;
1018}
1019
1020/*
1021 * This routine is called in the parent, so we'll set up the shared
1022 * memory segment and mutex here.
1023 */
1024
1025static int mod_tile_post_config(apr_pool_t *pconf, apr_pool_t *plog,
1026                             apr_pool_t *ptemp, server_rec *s)
1027{
1028    void *data; /* These two help ensure that we only init once. */
1029    const char *userdata_key = "mod_tile_init_module";
1030    apr_status_t rs;
1031    stats_data *stats;
1032    delaypool *delayp;
1033    int i;
1034
1035
1036    /*
1037     * The following checks if this routine has been called before.
1038     * This is necessary because the parent process gets initialized
1039     * a couple of times as the server starts up, and we don't want
1040     * to create any more mutexes and shared memory segments than
1041     * we're actually going to use.
1042     */
1043    apr_pool_userdata_get(&data, userdata_key, s->process->pool);
1044    if (!data) {
1045        apr_pool_userdata_set((const void *) 1, userdata_key,
1046                              apr_pool_cleanup_null, s->process->pool);
1047        return OK;
1048    } /* Kilroy was here */
1049
1050    /* Create the shared memory segment */
1051
1052    /*
1053     * Create a unique filename using our pid. This information is
1054     * stashed in the global variable so the children inherit it.
1055     * TODO get the location from the environment $TMPDIR or somesuch.
1056     */
1057    shmfilename = apr_psprintf(pconf, "/tmp/httpd_shm.%ld", (long int)getpid());
1058    shmfilename_delaypool = apr_psprintf(pconf, "/tmp/httpd_shm_delay.%ld", (long int)getpid());
1059
1060    /* Now create that segment */
1061    rs = apr_shm_create(&stats_shm, sizeof(stats_data),
1062                        (const char *) shmfilename, pconf);
1063    if (rs != APR_SUCCESS) {
1064        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1065                     "Failed to create shared memory segment on file %s",
1066                     shmfilename);
1067        return HTTP_INTERNAL_SERVER_ERROR;
1068    }
1069
1070    rs = apr_shm_create(&delaypool_shm, sizeof(delaypool),
1071                        (const char *) shmfilename_delaypool, pconf);
1072    if (rs != APR_SUCCESS) {
1073        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1074                     "Failed to create shared memory segment on file %s",
1075                     shmfilename_delaypool);
1076        return HTTP_INTERNAL_SERVER_ERROR;
1077    }
1078
1079    /* Created it, now let's zero it out */
1080    stats = (stats_data *)apr_shm_baseaddr_get(stats_shm);
1081    stats->noResp200 = 0;
1082    stats->noResp304 = 0;
1083    stats->noResp404 = 0;
1084    stats->noResp503 = 0;
1085    stats->noResp5XX = 0;
1086    for (i = 0; i <= MAX_ZOOM; i++) {
1087        stats->noRespZoom[i] = 0;
1088    }
1089    stats->noRespOther = 0;
1090    stats->noFreshCache = 0;
1091    stats->noFreshRender = 0;
1092    stats->noOldCache = 0;
1093    stats->noOldRender = 0;
1094
1095    delayp = (delaypool *)apr_shm_baseaddr_get(delaypool_shm);
1096   
1097    delayp->last_tile_fillup = apr_time_now();
1098    delayp->last_render_fillup = apr_time_now();
1099
1100    for (i = 0; i < DELAY_HASHTABLE_SIZE; i++) {
1101        delayp->users[i].ip_addr = (in_addr_t)0;
1102        delayp->users[i].available_tiles = 0;
1103        delayp->users[i].available_render_req = 0;
1104    }
1105    for (i = 0; i < DELAY_HASHTABLE_WHITELIST_SIZE; i++) {
1106        delayp->whitelist[i] = (in_addr_t)0;
1107    }
1108    /* TODO: need a way to initialise the delaypool whitelist */
1109
1110
1111    /* Create global mutex */
1112
1113    /*
1114     * Create another unique filename to lock upon. Note that
1115     * depending on OS and locking mechanism of choice, the file
1116     * may or may not be actually created.
1117     */
1118    mutexfilename = apr_psprintf(pconf, "/tmp/httpd_mutex.%ld",
1119                                 (long int) getpid());
1120
1121    rs = apr_global_mutex_create(&stats_mutex, (const char *) mutexfilename,
1122                                 APR_LOCK_DEFAULT, pconf);
1123    if (rs != APR_SUCCESS) {
1124        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1125                     "Failed to create mutex on file %s",
1126                     mutexfilename);
1127        return HTTP_INTERNAL_SERVER_ERROR;
1128    }
1129
1130#ifdef MOD_TILE_SET_MUTEX_PERMS
1131    rs = unixd_set_global_mutex_perms(stats_mutex);
1132    if (rs != APR_SUCCESS) {
1133        ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
1134                     "Parent could not set permissions on mod_tile "
1135                     "mutex: check User and Group directives");
1136        return HTTP_INTERNAL_SERVER_ERROR;
1137    }
1138#endif /* MOD_TILE_SET_MUTEX_PERMS */
1139
1140    /*
1141     * Create another unique filename to lock upon. Note that
1142     * depending on OS and locking mechanism of choice, the file
1143     * may or may not be actually created.
1144     */
1145    mutexfilename = apr_psprintf(pconf, "/tmp/httpd_mutex_delay.%ld",
1146                                 (long int) getpid());
1147
1148    rs = apr_global_mutex_create(&delay_mutex, (const char *) mutexfilename,
1149                                 APR_LOCK_DEFAULT, pconf);
1150    if (rs != APR_SUCCESS) {
1151        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1152                     "Failed to create mutex on file %s",
1153                     mutexfilename);
1154        return HTTP_INTERNAL_SERVER_ERROR;
1155    }
1156
1157#ifdef MOD_TILE_SET_MUTEX_PERMS
1158    rs = unixd_set_global_mutex_perms(delay_mutex);
1159    if (rs != APR_SUCCESS) {
1160        ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
1161                     "Parent could not set permissions on mod_tile "
1162                     "mutex: check User and Group directives");
1163        return HTTP_INTERNAL_SERVER_ERROR;
1164    }
1165#endif /* MOD_TILE_SET_MUTEX_PERMS */
1166
1167    return OK;
1168}
1169
1170
1171/*
1172 * This routine gets called when a child inits. We use it to attach
1173 * to the shared memory segment, and reinitialize the mutex.
1174 */
1175
1176static void mod_tile_child_init(apr_pool_t *p, server_rec *s)
1177{
1178    apr_status_t rs;
1179
1180     /*
1181      * Re-open the mutex for the child. Note we're reusing
1182      * the mutex pointer global here.
1183      */
1184     rs = apr_global_mutex_child_init(&stats_mutex,
1185                                      (const char *) mutexfilename,
1186                                      p);
1187     if (rs != APR_SUCCESS) {
1188         ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
1189                     "Failed to reopen mutex on file %s",
1190                     shmfilename);
1191         /* There's really nothing else we can do here, since
1192          * This routine doesn't return a status. */
1193         exit(1); /* Ugly, but what else? */
1194     }
1195}
1196
1197static void register_hooks(__attribute__((unused)) apr_pool_t *p)
1198{
1199    ap_hook_post_config(mod_tile_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1200    ap_hook_child_init(mod_tile_child_init, NULL, NULL, APR_HOOK_MIDDLE);
1201    ap_hook_handler(tile_handler_serve, NULL, NULL, APR_HOOK_MIDDLE);
1202    ap_hook_handler(tile_handler_dirty, NULL, NULL, APR_HOOK_MIDDLE);
1203    ap_hook_handler(tile_handler_status, NULL, NULL, APR_HOOK_MIDDLE);
1204    ap_hook_handler(tile_handler_mod_stats, NULL, NULL, APR_HOOK_MIDDLE);
1205    ap_hook_translate_name(tile_translate, NULL, NULL, APR_HOOK_MIDDLE);
1206    ap_hook_map_to_storage(tile_storage_hook, NULL, NULL, APR_HOOK_FIRST);
1207}
1208
1209static const char *_add_tile_config(cmd_parms *cmd, void *mconfig, const char *baseuri, const char *name, int minzoom, int maxzoom, const char * fileExtension, const char *mimeType)
1210{
1211    if (strlen(name) == 0) {
1212        return "ConfigName value must not be null";
1213    }
1214
1215
1216    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1217    tile_config_rec *tilecfg = apr_array_push(scfg->configs);
1218
1219    // Ensure URI string ends with a trailing slash
1220    int urilen = strlen(baseuri);
1221
1222    if (urilen==0)
1223      snprintf(tilecfg->baseuri, PATH_MAX, "/");
1224    else if (baseuri[urilen-1] != '/')
1225      snprintf(tilecfg->baseuri, PATH_MAX, "%s/", baseuri);
1226    else
1227      snprintf(tilecfg->baseuri, PATH_MAX, "%s", baseuri);
1228
1229    strncpy(tilecfg->xmlname, name, XMLCONFIG_MAX-1);
1230    strncpy(tilecfg->fileExtension, fileExtension, XMLCONFIG_MAX-1);
1231    strncpy(tilecfg->mimeType, mimeType, XMLCONFIG_MAX-1);
1232    tilecfg->xmlname[XMLCONFIG_MAX-1] = 0;
1233    tilecfg->minzoom = minzoom;
1234    tilecfg->maxzoom = maxzoom;
1235
1236    ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, cmd->server,
1237                    "Loading tile config %s at %s for zooms %i - %i from tile directory %s with extension .%s and mime type %s",
1238                 name, baseuri, minzoom, maxzoom, scfg->tile_dir, fileExtension, mimeType);
1239
1240
1241    return NULL;
1242}
1243
1244static const char *add_tile_mime_config(cmd_parms *cmd, void *mconfig, const char *baseuri, const char *name, const char * fileExtension)
1245{
1246    if (strcmp(fileExtension,"png") == 0) {
1247        return _add_tile_config(cmd, mconfig, baseuri, name, 0, MAX_ZOOM, fileExtension, "image/png");
1248    }
1249    if (strcmp(fileExtension,"js") == 0) {
1250        return _add_tile_config(cmd, mconfig, baseuri, name, 0, MAX_ZOOM, fileExtension, "text/javascript");
1251    }
1252    return _add_tile_config(cmd, mconfig, baseuri, name, 0, MAX_ZOOM, fileExtension, "image/png");
1253}
1254
1255static const char *add_tile_config(cmd_parms *cmd, void *mconfig, const char *baseuri, const char *name)
1256{
1257    return _add_tile_config(cmd, mconfig, baseuri, name, 0, MAX_ZOOM, "png", "image/png");
1258}
1259
1260static const char *load_tile_config(cmd_parms *cmd, void *mconfig, const char *conffile)
1261{
1262    FILE * hini ;
1263    char filename[PATH_MAX];
1264    char xmlname[XMLCONFIG_MAX];
1265    char line[INILINE_MAX];
1266    char key[INILINE_MAX];
1267    char value[INILINE_MAX];
1268    const char * result;
1269    char fileExtension[INILINE_MAX];
1270    char mimeType[INILINE_MAX];
1271
1272    if (strlen(conffile) == 0) {
1273        strcpy(filename, RENDERD_CONFIG);
1274    } else {
1275        strcpy(filename, conffile);
1276    }
1277
1278    // Load the config
1279    if ((hini=fopen(filename, "r"))==NULL) {
1280        return "Unable to open config file";
1281    }
1282
1283    while (fgets(line, INILINE_MAX, hini)!=NULL) {
1284        if (line[0] == '#') continue;
1285        if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0;
1286        if (line[0] == '[') {
1287            if (strlen(line) >= XMLCONFIG_MAX){
1288                return "XML name too long";
1289            }
1290            sscanf(line, "[%[^]]", xmlname);
1291            strcpy(fileExtension,"png");
1292            strcpy(mimeType,"image/png");
1293        } else if (sscanf(line, "%[^=]=%[^;#]", key, value) == 2
1294               ||  sscanf(line, "%[^=]=\"%[^\"]\"", key, value) == 2) {
1295
1296            if (!strcmp(key, "URI")){
1297                if (strlen(value) >= PATH_MAX){
1298                    return "URI too long";
1299                }
1300                result = _add_tile_config(cmd, mconfig, value, xmlname, 0, MAX_ZOOM, fileExtension, mimeType);
1301                if (result != NULL) return result;
1302            }
1303            if (!strcmp(key, "TYPE")){
1304                if (strlen(value) >= PATH_MAX){
1305                    return "TYPE too long";
1306                }
1307                if (sscanf(value, "%[^ ] %[^;#]", fileExtension, mimeType) != 2) {
1308                    return "TYPE is not correctly parsable";
1309                }
1310            }
1311        }
1312    }
1313    fclose(hini);
1314    return NULL;
1315}
1316
1317static const char *mod_tile_request_timeout_config(cmd_parms *cmd, void *mconfig, const char *request_timeout_string)
1318{
1319    int request_timeout;
1320
1321    if (sscanf(request_timeout_string, "%d", &request_timeout) != 1) {
1322        return "ModTileRequestTimeout needs integer argument";
1323    }
1324
1325    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1326    scfg->request_timeout = request_timeout;
1327    return NULL;
1328}
1329
1330static const char *mod_tile_request_timeout_missing_config(cmd_parms *cmd, void *mconfig, const char *request_timeout_string)
1331{
1332    int request_timeout;
1333
1334    if (sscanf(request_timeout_string, "%d", &request_timeout) != 1) {
1335        return "ModTileMissingRequestTimeout needs integer argument";
1336    }
1337
1338    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1339    scfg->request_timeout_priority = request_timeout;
1340    return NULL;
1341}
1342
1343static const char *mod_tile_max_load_old_config(cmd_parms *cmd, void *mconfig, const char *max_load_old_string)
1344{
1345    int max_load_old;
1346
1347    if (sscanf(max_load_old_string, "%d", &max_load_old) != 1) {
1348        return "ModTileMaxLoadOld needs integer argument";
1349    }
1350
1351    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1352    scfg->max_load_old = max_load_old;
1353    return NULL;
1354}
1355
1356static const char *mod_tile_max_load_missing_config(cmd_parms *cmd, void *mconfig, const char *max_load_missing_string)
1357{
1358    int max_load_missing;
1359
1360    if (sscanf(max_load_missing_string, "%d", &max_load_missing) != 1) {
1361        return "ModTileMaxLoadMissing needs integer argument";
1362    }
1363
1364    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1365    scfg->max_load_missing = max_load_missing;
1366    return NULL;
1367}
1368
1369static const char *mod_tile_renderd_socket_name_config(cmd_parms *cmd, void *mconfig, const char *renderd_socket_name_string)
1370{
1371    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1372    strncpy(scfg->renderd_socket_name, renderd_socket_name_string, PATH_MAX-1);
1373    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1374    return NULL;
1375}
1376
1377static const char *mod_tile_tile_dir_config(cmd_parms *cmd, void *mconfig, const char *tile_dir_string)
1378{
1379    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1380    strncpy(scfg->tile_dir, tile_dir_string, PATH_MAX-1);
1381    scfg->tile_dir[PATH_MAX-1] = 0;
1382    return NULL;
1383}
1384
1385static const char *mod_tile_cache_extended_host_name_config(cmd_parms *cmd, void *mconfig, const char *cache_extended_hostname)
1386{
1387    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1388    strncpy(scfg->cache_extended_hostname, cache_extended_hostname, PATH_MAX-1);
1389    scfg->cache_extended_hostname[PATH_MAX-1] = 0;
1390
1391    return NULL;
1392}
1393
1394static const char *mod_tile_cache_extended_duration_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1395{
1396    int cache_duration;
1397    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1398    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1399            return "ModTileCacheExtendedDuration needs integer argument";
1400    }
1401    scfg->cache_extended_duration = cache_duration;
1402
1403    return NULL;
1404}
1405
1406static const char *mod_tile_cache_lastmod_factor_config(cmd_parms *cmd, void *mconfig, const char *modified_factor_string)
1407{
1408    float modified_factor;
1409    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1410            &tile_module);
1411    if (sscanf(modified_factor_string, "%f", &modified_factor) != 1) {
1412        return "ModTileCacheLastModifiedFactor needs float argument";
1413    }
1414    scfg->cache_duration_last_modified_factor = modified_factor;
1415    return NULL;
1416}
1417
1418static const char *mod_tile_cache_duration_max_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1419{
1420    int cache_duration;
1421    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1422            &tile_module);
1423    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1424        return "ModTileCacheDurationMax needs integer argument";
1425    }
1426    scfg->cache_duration_max = cache_duration;
1427    return NULL;
1428}
1429
1430static const char *mod_tile_cache_duration_dirty_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1431{
1432    int cache_duration;
1433    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1434            &tile_module);
1435    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1436        return "ModTileCacheDurationDirty needs integer argument";
1437    }
1438    scfg->cache_duration_dirty = cache_duration;
1439    return NULL;
1440}
1441
1442static const char *mod_tile_cache_duration_minimum_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1443{
1444    int cache_duration;
1445    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1446            &tile_module);
1447    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1448        return "ModTileCacheDurationMinimum needs integer argument";
1449    }
1450    scfg->cache_duration_minimum = cache_duration;
1451    return NULL;
1452}
1453
1454static const char *mod_tile_cache_duration_low_config(cmd_parms *cmd, void *mconfig, const char *zoom_level_string, const char *cache_duration_string)
1455{
1456    int zoom_level;
1457    int cache_duration;
1458    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1459    if (sscanf(zoom_level_string, "%d", &zoom_level) != 1) {
1460            return "ModTileCacheDurationLowZoom needs integer argument";
1461    }
1462    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1463            return "ModTileCacheDurationLowZoom needs integer argument";
1464    }
1465    scfg->cache_level_low_zoom = zoom_level;
1466    scfg->cache_duration_low_zoom = cache_duration;
1467
1468    return NULL;
1469}
1470static const char *mod_tile_cache_duration_medium_config(cmd_parms *cmd, void *mconfig, const char *zoom_level_string, const char *cache_duration_string)
1471{
1472    int zoom_level;
1473    int cache_duration;
1474    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1475    if (sscanf(zoom_level_string, "%d", &zoom_level) != 1) {
1476            return "ModTileCacheDurationMediumZoom needs integer argument";
1477    }
1478    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1479            return "ModTileCacheDurationMediumZoom needs integer argument";
1480    }
1481    scfg->cache_level_medium_zoom = zoom_level;
1482    scfg->cache_duration_medium_zoom = cache_duration;
1483
1484    return NULL;
1485}
1486
1487static const char *mod_tile_enable_stats(cmd_parms *cmd, void *mconfig, int enableStats)
1488{
1489    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1490    scfg->enableGlobalStats = enableStats;
1491    return NULL;
1492}
1493
1494static const char *mod_tile_enable_throttling(cmd_parms *cmd, void *mconfig, int enableThrottling)
1495{
1496    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1497    scfg->enableTileThrottling = enableThrottling;
1498    return NULL;
1499}
1500
1501static const char *mod_tile_bulk_mode(cmd_parms *cmd, void *mconfig, int bulkMode)
1502{
1503    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1504    scfg->bulkMode = bulkMode;
1505    return NULL;
1506}
1507
1508static const char *mod_tile_delaypool_tiles_config(cmd_parms *cmd, void *mconfig, const char *bucketsize_string, const char *topuprate_string)
1509{
1510    int bucketsize;
1511    float topuprate;
1512
1513    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1514    if (sscanf(bucketsize_string, "%d", &bucketsize) != 1) {
1515            return "ModTileThrottlingTiles needs two numerical arguments, the first one must be integer";
1516    }
1517    if (sscanf(topuprate_string, "%f", &topuprate) != 1) {
1518            return "ModTileThrottlingTiles needs two numerical arguments, the first one must be integer";
1519    }
1520    scfg->delaypoolTileSize = bucketsize;
1521
1522    /*Convert topup rate into microseconds per tile */
1523    scfg->delaypoolTileRate = (long)(1000000.0/topuprate);
1524
1525    return NULL;
1526}
1527
1528static const char *mod_tile_delaypool_render_config(cmd_parms *cmd, void *mconfig, const char *bucketsize_string, const char *topuprate_string)
1529{
1530    int bucketsize;
1531    float topuprate;
1532
1533    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1534    if (sscanf(bucketsize_string, "%d", &bucketsize) != 1) {
1535            return "ModTileThrottlingRenders needs two numerical arguments, the first one must be integer";
1536    }
1537    if (sscanf(topuprate_string, "%f", &topuprate) != 1) {
1538            return "ModTileThrottlingRenders needs two numerical arguments, the first one must be integer";
1539    }
1540    scfg->delaypoolRenderSize = bucketsize;
1541
1542    /*Convert topup rate into microseconds per tile */
1543    scfg->delaypoolRenderRate = (long)(1000000.0/topuprate);
1544
1545    return NULL;
1546}
1547
1548static void *create_tile_config(apr_pool_t *p, server_rec *s)
1549{
1550    tile_server_conf * scfg = (tile_server_conf *) apr_pcalloc(p, sizeof(tile_server_conf));
1551
1552    scfg->configs = apr_array_make(p, 4, sizeof(tile_config_rec));
1553    scfg->request_timeout = REQUEST_TIMEOUT;
1554    scfg->request_timeout_priority = REQUEST_TIMEOUT;
1555    scfg->max_load_old = MAX_LOAD_OLD;
1556    scfg->max_load_missing = MAX_LOAD_MISSING;
1557    strncpy(scfg->renderd_socket_name, RENDER_SOCKET, PATH_MAX-1);
1558    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1559    strncpy(scfg->tile_dir, HASH_PATH, PATH_MAX-1);
1560    scfg->tile_dir[PATH_MAX-1] = 0;
1561    memset(&(scfg->cache_extended_hostname),0,PATH_MAX);
1562    scfg->cache_extended_duration = 0;
1563    scfg->cache_duration_dirty = 15*60;
1564    scfg->cache_duration_last_modified_factor = 0.0;
1565    scfg->cache_duration_max = 7*24*60*60;
1566    scfg->cache_duration_minimum = 3*60*60;
1567    scfg->cache_duration_low_zoom = 6*24*60*60;
1568    scfg->cache_duration_medium_zoom = 1*24*60*60;
1569    scfg->cache_level_low_zoom = 0;
1570    scfg->cache_level_medium_zoom = 0;
1571    scfg->enableGlobalStats = 1;
1572    scfg->enableTileThrottling = 0;
1573    scfg->delaypoolTileSize = AVAILABLE_TILE_BUCKET_SIZE;
1574    scfg->delaypoolTileRate = RENDER_TOPUP_RATE;
1575    scfg->delaypoolRenderSize = AVAILABLE_RENDER_BUCKET_SIZE;
1576    scfg->delaypoolRenderRate = RENDER_TOPUP_RATE;
1577    scfg->bulkMode = 0;
1578
1579
1580    return scfg;
1581}
1582
1583static void *merge_tile_config(apr_pool_t *p, void *basev, void *overridesv)
1584{
1585    int i;
1586    tile_server_conf * scfg = (tile_server_conf *) apr_pcalloc(p, sizeof(tile_server_conf));
1587    tile_server_conf * scfg_base = (tile_server_conf *) basev;
1588    tile_server_conf * scfg_over = (tile_server_conf *) overridesv;
1589
1590    scfg->configs = apr_array_append(p, scfg_base->configs, scfg_over->configs);
1591    scfg->request_timeout = scfg_over->request_timeout;
1592    scfg->request_timeout_priority = scfg_over->request_timeout_priority;
1593    scfg->max_load_old = scfg_over->max_load_old;
1594    scfg->max_load_missing = scfg_over->max_load_missing;
1595    strncpy(scfg->renderd_socket_name, scfg_over->renderd_socket_name, PATH_MAX-1);
1596    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1597    strncpy(scfg->tile_dir, scfg_over->tile_dir, PATH_MAX-1);
1598    scfg->tile_dir[PATH_MAX-1] = 0;
1599    strncpy(scfg->cache_extended_hostname, scfg_over->cache_extended_hostname, PATH_MAX-1);
1600    scfg->cache_extended_hostname[PATH_MAX-1] = 0;
1601    scfg->cache_extended_duration = scfg_over->cache_extended_duration;
1602    scfg->cache_duration_dirty = scfg_over->cache_duration_dirty;
1603    scfg->cache_duration_last_modified_factor = scfg_over->cache_duration_last_modified_factor;
1604    scfg->cache_duration_max = scfg_over->cache_duration_max;
1605    scfg->cache_duration_minimum = scfg_over->cache_duration_minimum;
1606    scfg->cache_duration_low_zoom = scfg_over->cache_duration_low_zoom;
1607    scfg->cache_duration_medium_zoom = scfg_over->cache_duration_medium_zoom;
1608    scfg->cache_level_low_zoom = scfg_over->cache_level_low_zoom;
1609    scfg->cache_level_medium_zoom = scfg_over->cache_level_medium_zoom;
1610    scfg->enableGlobalStats = scfg_over->enableGlobalStats;
1611    scfg->enableTileThrottling = scfg_over->enableTileThrottling;
1612    scfg->delaypoolTileSize = scfg_over->delaypoolTileSize;
1613    scfg->delaypoolTileRate = scfg_over->delaypoolTileRate;
1614    scfg->delaypoolRenderSize = scfg_over->delaypoolRenderSize;
1615    scfg->delaypoolRenderRate = scfg_over->delaypoolRenderRate;
1616    scfg->bulkMode = scfg_over->bulkMode;
1617
1618    //Construct a table of minimum cache times per zoom level
1619    for (i = 0; i <= MAX_ZOOM; i++) {
1620        if (i <= scfg->cache_level_low_zoom) {
1621            scfg->mincachetime[i] = scfg->cache_duration_low_zoom;
1622        } else if (i <= scfg->cache_level_medium_zoom) {
1623            scfg->mincachetime[i] = scfg->cache_duration_medium_zoom;
1624        } else {
1625            scfg->mincachetime[i] = scfg->cache_duration_minimum;
1626        }
1627    }
1628
1629    return scfg;
1630}
1631
1632static const command_rec tile_cmds[] =
1633{
1634    AP_INIT_TAKE1(
1635        "LoadTileConfigFile",            /* directive name */
1636        load_tile_config,                /* config action routine */
1637        NULL,                            /* argument to include in call */
1638        OR_OPTIONS,                      /* where available */
1639        "load an entire renderd config file"  /* directive description */
1640    ),
1641    AP_INIT_TAKE2(
1642        "AddTileConfig",                 /* directive name */
1643        add_tile_config,                 /* config action routine */
1644        NULL,                            /* argument to include in call */
1645        OR_OPTIONS,                      /* where available */
1646        "path and name of renderd config to use"  /* directive description */
1647    ),
1648    AP_INIT_TAKE3(
1649            "AddTileMimeConfig",         /* directive name */
1650            add_tile_mime_config,        /* config action routine */
1651            NULL,                        /* argument to include in call */
1652            OR_OPTIONS,                  /* where available */
1653            "path, name and file extension of renderd config to use"  /* directive description */
1654        ),
1655    AP_INIT_TAKE1(
1656        "ModTileRequestTimeout",         /* directive name */
1657        mod_tile_request_timeout_config, /* config action routine */
1658        NULL,                            /* argument to include in call */
1659        OR_OPTIONS,                      /* where available */
1660        "Set timeout in seconds on mod_tile requests"  /* directive description */
1661    ),
1662    AP_INIT_TAKE1(
1663        "ModTileMissingRequestTimeout",         /* directive name */
1664        mod_tile_request_timeout_missing_config, /* config action routine */
1665        NULL,                            /* argument to include in call */
1666        OR_OPTIONS,                      /* where available */
1667        "Set timeout in seconds on missing mod_tile requests"  /* directive description */
1668    ),
1669    AP_INIT_TAKE1(
1670        "ModTileMaxLoadOld",             /* directive name */
1671        mod_tile_max_load_old_config,    /* config action routine */
1672        NULL,                            /* argument to include in call */
1673        OR_OPTIONS,                      /* where available */
1674        "Set max load for rendering old tiles"  /* directive description */
1675    ),
1676    AP_INIT_TAKE1(
1677        "ModTileMaxLoadMissing",         /* directive name */
1678        mod_tile_max_load_missing_config, /* config action routine */
1679        NULL,                            /* argument to include in call */
1680        OR_OPTIONS,                      /* where available */
1681        "Set max load for rendering missing tiles"  /* directive description */
1682    ),
1683    AP_INIT_TAKE1(
1684        "ModTileRenderdSocketName",      /* directive name */
1685        mod_tile_renderd_socket_name_config, /* config action routine */
1686        NULL,                            /* argument to include in call */
1687        OR_OPTIONS,                      /* where available */
1688        "Set name of unix domain socket for connecting to rendering daemon"  /* directive description */
1689    ),
1690    AP_INIT_TAKE1(
1691        "ModTileTileDir",                /* directive name */
1692        mod_tile_tile_dir_config,        /* config action routine */
1693        NULL,                            /* argument to include in call */
1694        OR_OPTIONS,                      /* where available */
1695        "Set name of tile cache directory"  /* directive description */
1696    ),
1697    AP_INIT_TAKE1(
1698        "ModTileCacheExtendedHostName",                /* directive name */
1699        mod_tile_cache_extended_host_name_config,        /* config action routine */
1700        NULL,                            /* argument to include in call */
1701        OR_OPTIONS,                      /* where available */
1702        "set hostname for extended period caching"  /* directive description */
1703    ),
1704    AP_INIT_TAKE1(
1705        "ModTileCacheExtendedDuration",                /* directive name */
1706        mod_tile_cache_extended_duration_config,        /* config action routine */
1707        NULL,                            /* argument to include in call */
1708        OR_OPTIONS,                      /* where available */
1709        "set length of extended period caching"  /* directive description */
1710    ),
1711    AP_INIT_TAKE1(
1712        "ModTileCacheDurationMax",                /* directive name */
1713        mod_tile_cache_duration_max_config,        /* config action routine */
1714        NULL,                            /* argument to include in call */
1715        OR_OPTIONS,                      /* where available */
1716        "Set the maximum cache expiry in seconds"  /* directive description */
1717    ),
1718    AP_INIT_TAKE1(
1719        "ModTileCacheDurationDirty",                    /* directive name */
1720        mod_tile_cache_duration_dirty_config,           /* config action routine */
1721        NULL,                                           /* argument to include in call */
1722        OR_OPTIONS,                                     /* where available */
1723        "Set the cache expiry for serving dirty tiles"  /* directive description */
1724    ),
1725    AP_INIT_TAKE1(
1726        "ModTileCacheDurationMinimum",          /* directive name */
1727        mod_tile_cache_duration_minimum_config, /* config action routine */
1728        NULL,                                   /* argument to include in call */
1729        OR_OPTIONS,                             /* where available */
1730        "Set the minimum cache expiry"          /* directive description */
1731    ),
1732    AP_INIT_TAKE1(
1733        "ModTileCacheLastModifiedFactor",       /* directive name */
1734        mod_tile_cache_lastmod_factor_config,   /* config action routine */
1735        NULL,                                   /* argument to include in call */
1736        OR_OPTIONS,                             /* where available */
1737        "Set the factor by which the last modified determins cache expiry" /* directive description */
1738    ),
1739    AP_INIT_TAKE2(
1740        "ModTileCacheDurationLowZoom",       /* directive name */
1741        mod_tile_cache_duration_low_config,                 /* config action routine */
1742        NULL,                            /* argument to include in call */
1743        OR_OPTIONS,                      /* where available */
1744        "Set the minimum cache duration and zoom level for low zoom tiles"  /* directive description */
1745    ),
1746    AP_INIT_TAKE2(
1747        "ModTileCacheDurationMediumZoom", /* directive name */
1748        mod_tile_cache_duration_medium_config,                 /* config action routine */
1749        NULL,                            /* argument to include in call */
1750        OR_OPTIONS,                      /* where available */
1751        "Set the minimum cache duration and zoom level for medium zoom tiles"  /* directive description */
1752    ),
1753    AP_INIT_FLAG(
1754        "ModTileEnableStats",            /* directive name */
1755        mod_tile_enable_stats,           /* config action routine */
1756        NULL,                            /* argument to include in call */
1757        OR_OPTIONS,                      /* where available */
1758        "On Off - enable of keeping stats about what mod_tile is serving"  /* directive description */
1759    ),
1760    AP_INIT_FLAG(
1761        "ModTileEnableTileThrottling",   /* directive name */
1762        mod_tile_enable_throttling,      /* config action routine */
1763        NULL,                            /* argument to include in call */
1764        OR_OPTIONS,                      /* where available */
1765        "On Off - enable of throttling of IPs that excessively download tiles such as scrapers"  /* directive description */
1766    ),
1767    AP_INIT_TAKE2(
1768        "ModTileThrottlingTiles",        /* directive name */
1769        mod_tile_delaypool_tiles_config, /* config action routine */
1770        NULL,                            /* argument to include in call */
1771        OR_OPTIONS,                      /* where available */
1772        "Set the initial bucket size (number of tiles) and top up rate (tiles per second) for throttling tile request per IP"  /* directive description */
1773    ),
1774    AP_INIT_TAKE2(
1775        "ModTileThrottlingRenders",      /* directive name */
1776        mod_tile_delaypool_render_config,/* config action routine */
1777        NULL,                            /* argument to include in call */
1778        OR_OPTIONS,                      /* where available */
1779        "Set the initial bucket size (number of tiles) and top up rate (tiles per second) for throttling tile request per IP"  /* directive description */
1780    ),
1781    AP_INIT_FLAG(
1782        "ModTileBulkMode",               /* directive name */
1783        mod_tile_bulk_mode,              /* config action routine */
1784        NULL,                            /* argument to include in call */
1785        OR_OPTIONS,                      /* where available */
1786        "On Off - make all requests to renderd with bulk render priority, never mark tiles dirty"  /* directive description */
1787    ),
1788    {NULL}
1789};
1790
1791module AP_MODULE_DECLARE_DATA tile_module =
1792{
1793    STANDARD20_MODULE_STUFF,
1794    NULL,                                /* dir config creater */
1795    NULL,                                /* dir merger --- default is to override */
1796    create_tile_config,                  /* server config */
1797    merge_tile_config,                   /* merge server config */
1798    tile_cmds,                           /* command apr_table_t */
1799    register_hooks                       /* register hooks */
1800};
1801
Note: See TracBrowser for help on using the repository browser.