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

Last change on this file since 17661 was 17661, checked in by jonb, 10 years ago

Update mod_tile to open & close connection to renderd on each rendering request. This helps the scalability since otherwise renderd runs into select() limits at around 1000 Apache threads. Typically about 1 in 100 HTTP requests triggers a call to renderd.

File size: 47.1 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
43#include "gen_tile.h"
44#include "protocol.h"
45#include "render_config.h"
46#include "store.h"
47#include "dir_utils.h"
48
49#define INILINE_MAX 256
50
51#define FRESH 1
52#define OLD 2
53#define FRESH_RENDER 3
54#define OLD_RENDER 4
55
56typedef struct stats_data {
57    apr_uint64_t noResp200;
58    apr_uint64_t noResp304;
59    apr_uint64_t noResp404;
60    apr_uint64_t noResp5XX;
61    apr_uint64_t noRespOther;
62    apr_uint64_t noFreshCache;
63    apr_uint64_t noFreshRender;
64    apr_uint64_t noOldCache;
65    apr_uint64_t noOldRender;
66} stats_data;
67
68typedef struct {
69    char xmlname[XMLCONFIG_MAX];
70    char baseuri[PATH_MAX];
71    int minzoom;
72    int maxzoom;
73} tile_config_rec;
74
75typedef struct {
76    apr_array_header_t *configs;
77    int request_timeout;
78    int max_load_old;
79    int max_load_missing;
80    int cache_duration_dirty;
81    int cache_duration_max;
82    int cache_duration_minimum;
83    int cache_duration_low_zoom;
84    int cache_level_low_zoom;
85    int cache_duration_medium_zoom;
86    int cache_level_medium_zoom;
87    double cache_duration_last_modified_factor;
88    char renderd_socket_name[PATH_MAX];
89    char tile_dir[PATH_MAX];
90    int mincachetime[MAX_ZOOM + 1];
91    int enableGlobalStats;
92} tile_server_conf;
93
94enum tileState { tileMissing, tileOld, tileCurrent };
95
96static int error_message(request_rec *r, const char *format, ...)
97                 __attribute__ ((format (printf, 2, 3)));
98
99static int error_message(request_rec *r, const char *format, ...)
100{
101    va_list ap;
102    va_start(ap, format);
103    int len;
104    char *msg;
105
106    len = vasprintf(&msg, format, ap);
107
108    if (msg) {
109        //ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "%s", msg);
110        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "%s", msg);
111        r->content_type = "text/plain";
112        if (!r->header_only)
113            ap_rputs(msg, r);
114        free(msg);
115    }
116
117    return OK;
118}
119
120#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
121#include "unixd.h"
122#define MOD_TILE_SET_MUTEX_PERMS /* XXX Apache should define something */
123#endif
124
125/* Number of microseconds to camp out on the mutex */
126#define CAMPOUT 10
127/* Maximum number of times we camp out before giving up */
128#define MAXCAMP 10
129
130apr_shm_t *stats_shm;
131char *shmfilename;
132apr_global_mutex_t *stats_mutex;
133char *mutexfilename;
134
135int socket_init(request_rec *r)
136{
137    ap_conf_vector_t *sconf = r->server->module_config;
138    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
139
140    int fd;
141    struct sockaddr_un addr;
142
143    fd = socket(PF_UNIX, SOCK_STREAM, 0);
144    if (fd < 0) {
145        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "failed to create unix socket");
146        return FD_INVALID;
147    }
148
149    bzero(&addr, sizeof(addr));
150    addr.sun_family = AF_UNIX;
151    strncpy(addr.sun_path, scfg->renderd_socket_name, sizeof(addr.sun_path));
152
153    if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
154        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "socket connect failed for: %s", scfg->renderd_socket_name);
155        close(fd);
156        return FD_INVALID;
157    }
158    return fd;
159}
160
161int request_tile(request_rec *r, struct protocol *cmd, int dirtyOnly)
162{
163    int fd;
164    int ret = 0;
165    int retry = 1;
166    struct protocol resp;
167
168    ap_conf_vector_t *sconf = r->server->module_config;
169    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
170
171    fd = socket_init(r);
172
173    if (fd == FD_INVALID) {
174        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Failed to connect to renderer");
175        return 0;
176    }
177
178    // cmd has already been partial filled, fill in the rest
179    cmd->ver = PROTO_VER;
180    cmd->cmd = dirtyOnly ? cmdDirty : cmdRender;
181
182    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Requesting xml(%s) z(%d) x(%d) y(%d)", cmd->xmlname, cmd->z, cmd->x, cmd->y);
183    do {
184        ret = send(fd, cmd, sizeof(struct protocol), 0);
185
186        if (ret == sizeof(struct protocol))
187            break;
188
189        close(fd);
190        if (errno != EPIPE)
191            return 0;
192
193        fd = socket_init(r);
194        if (fd == FD_INVALID)
195            return 0;
196    } while (retry--);
197
198    if (!dirtyOnly) {
199        struct timeval tv = {scfg->request_timeout, 0 };
200        fd_set rx;
201        int s;
202
203        while (1) {
204            FD_ZERO(&rx);
205            FD_SET(fd, &rx);
206            s = select(fd+1, &rx, NULL, NULL, &tv);
207            if (s == 1) {
208                bzero(&resp, sizeof(struct protocol));
209                ret = recv(fd, &resp, sizeof(struct protocol), 0);
210                if (ret != sizeof(struct protocol)) {
211                    //perror("recv error");
212                    break;
213                }
214
215                if (cmd->x == resp.x && cmd->y == resp.y && cmd->z == resp.z && !strcmp(cmd->xmlname, resp.xmlname)) {
216                    close(fd);
217                    if (resp.cmd == cmdDone)
218                        return 1;
219                    else
220                        return 0;
221                } else {
222                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
223                       "Response does not match request: xml(%s,%s) z(%d,%d) x(%d,%d) y(%d,%d)", cmd->xmlname,
224                       resp.xmlname, cmd->z, resp.z, cmd->x, resp.x, cmd->y, resp.y);
225                }
226            } else {
227                break;
228            }
229        }
230    }
231
232    close(fd);
233    return 0;
234}
235
236static apr_time_t getPlanetTime(request_rec *r)
237{
238    static apr_time_t last_check;
239    static apr_time_t planet_timestamp;
240    static pthread_mutex_t planet_lock = PTHREAD_MUTEX_INITIALIZER;
241    apr_time_t now = r->request_time;
242    struct apr_finfo_t s;
243
244    pthread_mutex_lock(&planet_lock);
245    // Only check for updates periodically
246    if (now < last_check + apr_time_from_sec(300)) {
247        pthread_mutex_unlock(&planet_lock);
248        return planet_timestamp;
249    }
250
251    ap_conf_vector_t *sconf = r->server->module_config;
252    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
253    char filename[PATH_MAX];
254    snprintf(filename, PATH_MAX-1, "%s/%s", scfg->tile_dir, PLANET_TIMESTAMP);
255
256    last_check = now;
257    if (apr_stat(&s, filename, APR_FINFO_MIN, r->pool) != APR_SUCCESS) {
258        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Planet timestamp file (%s) is missing", filename);
259        // Make something up
260        planet_timestamp = now - apr_time_from_sec(3 * 24 * 60 * 60);
261    } else {
262        if (s.mtime != planet_timestamp) {
263            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Planet file updated");
264            planet_timestamp = s.mtime;
265        }
266    }
267    pthread_mutex_unlock(&planet_lock);
268    return planet_timestamp;
269}
270
271static enum tileState tile_state_once(request_rec *r)
272{
273    apr_status_t rv;
274    apr_finfo_t *finfo = &r->finfo;
275
276    if (!(finfo->valid & APR_FINFO_MTIME)) {
277        rv = apr_stat(finfo, r->filename, APR_FINFO_MIN, r->pool);
278        if (rv != APR_SUCCESS)
279            return tileMissing;
280    }
281
282    if (finfo->mtime < getPlanetTime(r))
283        return tileOld;
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    /* Test if the tile we are serving is out of date, then set a low maxAge*/
329    if (state == tileOld) {
330        holdoff = (scfg->cache_duration_dirty /2) * (rand() / (RAND_MAX + 1.0));
331        maxAge = scfg->cache_duration_dirty + holdoff;
332    } else {
333        // cache heuristic based on zoom level
334        if (cmd->z > MAX_ZOOM) {
335            minCache = 0;
336            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
337                                    "z (%i) is larger than MAXZOOM %i\n",
338                                    cmd->z, MAX_ZOOM);
339        } else {
340            minCache = scfg->mincachetime[cmd->z];
341        }
342        // Time to the next known complete rerender
343        planetTimestamp = apr_time_sec(getPlanetTime(r) + apr_time_from_sec(PLANET_INTERVAL) - r->request_time) ;
344        // Time since the last render of this tile
345        lastModified = (int)(((double)apr_time_sec(r->request_time - finfo->mtime)) * scfg->cache_duration_last_modified_factor);
346        // Add a random jitter of 3 hours to space out cache expiry
347        holdoff = (3 * 60 * 60) * (rand() / (RAND_MAX + 1.0));
348
349        maxAge = MAX(minCache, planetTimestamp);
350        maxAge = MAX(maxAge,lastModified);
351        maxAge += holdoff;
352
353        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
354                        "caching heuristics: next planet render %ld; zoom level based %ld; last modified %ld\n",
355                        planetTimestamp, minCache, lastModified);
356    }
357
358    maxAge = MIN(maxAge, scfg->cache_duration_max);
359
360    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Setting tiles maxAge to %ld\n", maxAge);
361
362    apr_table_mergen(t, "Cache-Control",
363                     apr_psprintf(r->pool, "max-age=%" APR_TIME_T_FMT,
364                     maxAge));
365    timestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
366    apr_rfc822_date(timestr, (apr_time_from_sec(maxAge) + r->request_time));
367    apr_table_setn(t, "Expires", timestr);
368}
369
370double get_load_avg(request_rec *r)
371{
372    double loadavg[1];
373    int n = getloadavg(loadavg, 1);
374
375    if (n < 1)
376        return 1000;
377    else
378        return loadavg[0];
379}
380
381static int get_global_lock(request_rec *r) {
382    apr_status_t rs;
383    int camped;
384
385    for (camped = 0; camped < MAXCAMP; camped++) {
386        rs = apr_global_mutex_trylock(stats_mutex);
387        if (APR_STATUS_IS_EBUSY(rs)) {
388            apr_sleep(CAMPOUT);
389        } else if (rs == APR_SUCCESS) {
390            return 1;
391        } else if (APR_STATUS_IS_ENOTIMPL(rs)) {
392            /* If it's not implemented, just hang in the mutex. */
393            rs = apr_global_mutex_lock(stats_mutex);
394            if (rs == APR_SUCCESS) {
395                return 1;
396            } else {
397                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Could not get hardlock");
398                return 0;
399            }
400        } else {
401            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Unknown return status from trylock");
402            return 0;
403        }
404    }
405    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Timedout trylock");
406    return 0;
407}
408
409static int incRespCounter(int resp, request_rec *r) {
410    stats_data *stats;
411
412    ap_conf_vector_t *sconf = r->server->module_config;
413    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
414
415    if (!scfg->enableGlobalStats) {
416        /* If tile stats reporting is not enable
417         * pretend we correctly updated the counter to
418         * not fill the logs with warnings about failed
419         * stats
420         */
421        return 1;
422    }
423
424    if (get_global_lock(r) != 0) {
425        stats = (stats_data *)apr_shm_baseaddr_get(stats_shm);
426        switch (resp) {
427        case OK: {
428            stats->noResp200++;
429            break;
430        }
431        case HTTP_NOT_MODIFIED: {
432            stats->noResp304++;
433            break;
434        }
435        case HTTP_NOT_FOUND: {
436            stats->noResp404++;
437            break;
438        }
439        case HTTP_INTERNAL_SERVER_ERROR: {
440            stats->noResp5XX++;
441            break;
442        }
443        default: {
444            stats->noRespOther++;
445        }
446
447        }
448        apr_global_mutex_unlock(stats_mutex);
449        /* Swallowing the result because what are we going to do with it at
450         * this stage?
451         */
452        return 1;
453    } else {
454        return 0;
455    }
456}
457
458static int incFreshCounter(int status, request_rec *r) {
459    stats_data *stats;
460
461    ap_conf_vector_t *sconf = r->server->module_config;
462    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
463
464    if (!scfg->enableGlobalStats) {
465        /* If tile stats reporting is not enable
466         * pretend we correctly updated the counter to
467         * not fill the logs with warnings about failed
468         * stats
469         */
470        return 1;
471    }
472
473    if (get_global_lock(r) != 0) {
474        stats = (stats_data *)apr_shm_baseaddr_get(stats_shm);
475        switch (status) {
476        case FRESH: {
477            stats->noFreshCache++;
478            break;
479        }
480        case FRESH_RENDER: {
481            stats->noFreshRender++;
482            break;
483        }
484        case OLD: {
485            stats->noOldCache++;
486            break;
487        }
488        case OLD_RENDER: {
489            stats->noOldRender++;
490            break;
491        }
492        }
493        apr_global_mutex_unlock(stats_mutex);
494        /* Swallowing the result because what are we going to do with it at
495         * this stage?
496         */
497        return 1;
498    } else {
499        return 0;
500    }
501}
502
503static int tile_handler_dirty(request_rec *r)
504{
505    if(strcmp(r->handler, "tile_dirty"))
506        return DECLINED;
507
508    struct protocol * cmd = (struct protocol *)ap_get_module_config(r->request_config, &tile_module);
509    if (cmd == NULL)
510        return DECLINED;
511
512    request_tile(r, cmd, 1);
513    return error_message(r, "Tile submitted for rendering\n");
514}
515
516static int tile_storage_hook(request_rec *r)
517{
518//    char abs_path[PATH_MAX];
519    int avg;
520    enum tileState state;
521
522    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "tile_storage_hook: handler(%s), uri(%s), filename(%s), path_info(%s)",
523                  r->handler, r->uri, r->filename, r->path_info);
524
525    if (!r->handler)
526        return DECLINED;
527
528    // Any status request is OK
529    if (!strcmp(r->handler, "tile_status"))
530        return OK;
531
532    if (strcmp(r->handler, "tile_serve") && strcmp(r->handler, "tile_dirty"))
533        return DECLINED;
534
535    struct protocol * cmd = (struct protocol *)ap_get_module_config(r->request_config, &tile_module);
536    if (cmd == NULL)
537        return DECLINED;
538/*
539should already be done
540    // Generate the tile filename
541#ifdef METATILE
542    ap_conf_vector_t *sconf = r->server->module_config;
543    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
544    xyz_to_meta(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
545#else
546    xyz_to_path(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
547#endif
548    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "abs_path(%s)", abs_path);
549    r->filename = apr_pstrdup(r->pool, abs_path);
550*/
551    avg = get_load_avg(r);
552    state = tile_state(r, cmd);
553
554    ap_conf_vector_t *sconf = r->server->module_config;
555    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
556
557    switch (state) {
558        case tileCurrent:
559            if (!incFreshCounter(FRESH, r)) {
560                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
561                    "Failed to increase fresh stats counter");
562            }
563            return OK;
564            break;
565        case tileOld:
566            if (avg > scfg->max_load_old) {
567               // Too much load to render it now, mark dirty but return old tile
568               request_tile(r, cmd, 1);
569               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);
570               if (!incFreshCounter(OLD, r)) {
571                   ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
572                        "Failed to increase fresh stats counter");
573               }
574               return OK;
575            }
576            break;
577        case tileMissing:
578            if (avg > scfg->max_load_missing) {
579               request_tile(r, cmd, 1);
580               ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Load larger max_load_missing (%d). Return HTTP_NOT_FOUND.", scfg->max_load_missing);
581               if (!incRespCounter(HTTP_NOT_FOUND, r)) {
582                   ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
583                        "Failed to increase response stats counter");
584               }
585               return HTTP_NOT_FOUND;
586            }
587            break;
588    }
589
590    if (request_tile(r, cmd, 0)) {
591        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Update file info abs_path(%s)", r->filename);
592        // Need to update fileinfo for new rendered tile
593        apr_stat(&r->finfo, r->filename, APR_FINFO_MIN, r->pool);
594        if (!incFreshCounter(FRESH_RENDER, r)) {
595            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
596                    "Failed to increase fresh stats counter");
597        }
598        return OK;
599    }
600
601    if (state == tileOld) {
602        if (!incFreshCounter(OLD_RENDER, r)) {
603            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
604                    "Failed to increase fresh stats counter");
605        }
606        return OK;
607    }
608    if (!incRespCounter(HTTP_NOT_FOUND, r)) {
609        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
610                "Failed to increase response stats counter");
611    }
612
613    return HTTP_NOT_FOUND;
614}
615
616static int tile_handler_status(request_rec *r)
617{
618    enum tileState state;
619    char time_str[APR_CTIME_LEN];
620
621    if(strcmp(r->handler, "tile_status"))
622        return DECLINED;
623
624    struct protocol * cmd = (struct protocol *)ap_get_module_config(r->request_config, &tile_module);
625    if (cmd == NULL){
626        sleep(CLIENT_PENALTY);
627        return HTTP_NOT_FOUND;
628    }
629
630    state = tile_state(r, cmd);
631    if (state == tileMissing)
632        return error_message(r, "Unable to find a tile at %s\n", r->filename);
633    apr_ctime(time_str, r->finfo.mtime);
634
635    return error_message(r, "Tile is %s. Last rendered at %s\n", (state == tileOld) ? "due to be rendered" : "clean", time_str);
636}
637
638static int tile_handler_mod_stats(request_rec *r)
639{
640    stats_data * stats;
641    stats_data local_stats;
642
643    if (strcmp(r->handler, "tile_mod_stats"))
644        return DECLINED;
645
646    ap_conf_vector_t *sconf = r->server->module_config;
647    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
648
649    if (!scfg->enableGlobalStats) {
650        return error_message(r, "Stats are not enabled for this server");
651    }
652
653    if (get_global_lock(r) != 0) {
654        //Copy over the global counter variable into
655        //local variables, that we can immediately
656        //release the lock again
657        stats = (stats_data *) apr_shm_baseaddr_get(stats_shm);
658        memcpy(&local_stats, stats, sizeof(stats_data));
659        apr_global_mutex_unlock(stats_mutex);
660    } else {
661        return error_message(r, "Failed to acquire lock, can't display stats");
662    }
663
664    ap_rprintf(r, "NoResp200: %li\n", local_stats.noResp200);
665    ap_rprintf(r, "NoResp304: %li\n", local_stats.noResp304);
666    ap_rprintf(r, "NoResp404: %li\n", local_stats.noResp404);
667    ap_rprintf(r, "NoResp5XX: %li\n", local_stats.noResp5XX);
668    ap_rprintf(r, "NoRespOther: %li\n", local_stats.noRespOther);
669    ap_rprintf(r, "NoFreshCache: %li\n", local_stats.noFreshCache);
670    ap_rprintf(r, "NoOldCache: %li\n", local_stats.noOldCache);
671    ap_rprintf(r, "NoFreshRender: %li\n", local_stats.noFreshRender);
672    ap_rprintf(r, "NoOldRender: %li\n", local_stats.noOldRender);
673
674
675    return OK;
676}
677
678static int tile_handler_serve(request_rec *r)
679{
680    const int tile_max = MAX_SIZE;
681    unsigned char *buf;
682    int len;
683    apr_status_t errstatus;
684
685    if(strcmp(r->handler, "tile_serve"))
686        return DECLINED;
687
688    struct protocol * cmd = (struct protocol *)ap_get_module_config(r->request_config, &tile_module);
689    if (cmd == NULL){
690        sleep(CLIENT_PENALTY);
691        if (!incRespCounter(HTTP_NOT_FOUND, r)) {
692            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
693                    "Failed to increase response stats counter");
694        }
695        return HTTP_NOT_FOUND;
696    }
697
698    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "tile_handler_serve: xml(%s) z(%d) x(%d) y(%d)", cmd->xmlname, cmd->z, cmd->x, cmd->y);
699
700    // FIXME: It is a waste to do the malloc + read if we are fulfilling a HEAD or returning a 304.
701    buf = malloc(tile_max);
702    if (!buf) {
703        if (!incRespCounter(HTTP_INTERNAL_SERVER_ERROR, r)) {
704            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
705                    "Failed to increase response stats counter");
706        }
707        return HTTP_INTERNAL_SERVER_ERROR;
708    }
709
710    len = tile_read(cmd->xmlname, cmd->x, cmd->y, cmd->z, buf, tile_max);
711    if (len > 0) {
712#if 0
713        // Set default Last-Modified and Etag headers
714        ap_update_mtime(r, r->finfo.mtime);
715        ap_set_last_modified(r);
716        ap_set_etag(r);
717#else
718        // Use MD5 hash as only cache attribute.
719        // If a tile is re-rendered and produces the same output
720        // then we can continue to use the previous cached copy
721        char *md5 = ap_md5_binary(r->pool, buf, len);
722        apr_table_setn(r->headers_out, "ETag",
723                        apr_psprintf(r->pool, "\"%s\"", md5));
724#endif
725        ap_set_content_type(r, "image/png");
726        ap_set_content_length(r, len);
727        add_expiry(r, cmd);
728        if ((errstatus = ap_meets_conditions(r)) != OK) {
729            free(buf);
730            if (!incRespCounter(errstatus, r)) {
731                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
732                        "Failed to increase response stats counter");
733            }
734            return errstatus;
735        } else {
736            ap_rwrite(buf, len, r);
737            free(buf);
738            if (!incRespCounter(errstatus, r)) {
739                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
740                        "Failed to increase response stats counter");
741            }
742            return OK;
743        }
744    }
745    free(buf);
746    //ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "len = %d", len);
747    if (!incRespCounter(HTTP_NOT_FOUND, r)) {
748        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
749                "Failed to increase response stats counter");
750    }
751    return DECLINED;
752}
753
754static int tile_translate(request_rec *r)
755{
756    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "tile_translate: uri(%s)", r->uri);
757
758    int i,n,limit,oob;
759    char option[11];
760
761    ap_conf_vector_t *sconf = r->server->module_config;
762    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
763
764    tile_config_rec *tile_configs = (tile_config_rec *) scfg->configs->elts;
765
766    /*
767     * The page /mod_tile returns global stats about the number of tiles
768     * handled and in what state those tiles were.
769     * This should probably not be hard coded
770     */
771    if (!strncmp("/mod_tile", r->uri, strlen("/mod_tile"))) {
772        r->handler = "tile_mod_stats";
773        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
774                "tile_translate: retrieving global mod_tile stats");
775        return OK;
776    }
777
778    for (i = 0; i < scfg->configs->nelts; ++i) {
779        tile_config_rec *tile_config = &tile_configs[i];
780
781        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "tile_translate: baseuri(%s) name(%s)", tile_config->baseuri, tile_config->xmlname);
782
783        if (!strncmp(tile_config->baseuri, r->uri, strlen(tile_config->baseuri))) {
784
785            struct protocol * cmd = (struct protocol *) apr_pcalloc(r->pool, sizeof(struct protocol));
786            bzero(cmd, sizeof(struct protocol));
787
788            n = sscanf(r->uri+strlen(tile_config->baseuri), "%d/%d/%d.png/%10s", &(cmd->z), &(cmd->x), &(cmd->y), option);
789            if (n < 3) return DECLINED;
790
791            oob = (cmd->z < 0 || cmd->z > MAX_ZOOM);
792            if (!oob) {
793                 // valid x/y for tiles are 0 ... 2^zoom-1
794                 limit = (1 << cmd->z) - 1;
795                 oob =  (cmd->x < 0 || cmd->x > limit || cmd->y < 0 || cmd->y > limit);
796            }
797
798            if (oob) {
799                sleep(CLIENT_PENALTY);
800                //Don't increase stats counter here,
801                //As we are interested in valid tiles only
802                return HTTP_NOT_FOUND;
803            }
804
805            strcpy(cmd->xmlname, tile_config->xmlname);
806
807            // Store a copy for later
808            ap_set_module_config(r->request_config, &tile_module, cmd);
809
810            // Generate the tile filename?
811            char abs_path[PATH_MAX];
812#ifdef METATILE
813            xyz_to_meta(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
814#else
815            xyz_to_path(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
816#endif
817            r->filename = apr_pstrdup(r->pool, abs_path);
818
819            if (n == 4) {
820                if (!strcmp(option, "status")) r->handler = "tile_status";
821                else if (!strcmp(option, "dirty")) r->handler = "tile_dirty";
822                else return DECLINED;
823            } else {
824                r->handler = "tile_serve";
825            }
826
827            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "tile_translate: op(%s) xml(%s) z(%d) x(%d) y(%d)", r->handler , cmd->xmlname, cmd->z, cmd->x, cmd->y);
828
829            return OK;
830        }
831    }
832    return DECLINED;
833}
834
835/*
836 * This routine is called in the parent, so we'll set up the shared
837 * memory segment and mutex here.
838 */
839
840static int mod_tile_post_config(apr_pool_t *pconf, apr_pool_t *plog,
841                             apr_pool_t *ptemp, server_rec *s)
842{
843    void *data; /* These two help ensure that we only init once. */
844    const char *userdata_key = "mod_tile_init_module";
845    apr_status_t rs;
846    stats_data *stats;
847
848
849    /*
850     * The following checks if this routine has been called before.
851     * This is necessary because the parent process gets initialized
852     * a couple of times as the server starts up, and we don't want
853     * to create any more mutexes and shared memory segments than
854     * we're actually going to use.
855     */
856    apr_pool_userdata_get(&data, userdata_key, s->process->pool);
857    if (!data) {
858        apr_pool_userdata_set((const void *) 1, userdata_key,
859                              apr_pool_cleanup_null, s->process->pool);
860        return OK;
861    } /* Kilroy was here */
862
863    /* Create the shared memory segment */
864
865    /*
866     * Create a unique filename using our pid. This information is
867     * stashed in the global variable so the children inherit it.
868     * TODO get the location from the environment $TMPDIR or somesuch.
869     */
870    shmfilename = apr_psprintf(pconf, "/tmp/httpd_shm.%ld", (long int)getpid());
871
872    /* Now create that segment */
873    rs = apr_shm_create(&stats_shm, sizeof(stats_data),
874                        (const char *) shmfilename, pconf);
875    if (rs != APR_SUCCESS) {
876        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
877                     "Failed to create shared memory segment on file %s",
878                     shmfilename);
879        return HTTP_INTERNAL_SERVER_ERROR;
880    }
881
882    /* Created it, now let's zero it out */
883    stats = (stats_data *)apr_shm_baseaddr_get(stats_shm);
884    stats->noResp200 = 0;
885    stats->noResp304 = 0;
886    stats->noResp404 = 0;
887    stats->noResp5XX = 0;
888    stats->noRespOther = 0;
889    stats->noFreshCache = 0;
890    stats->noFreshRender = 0;
891    stats->noOldCache = 0;
892    stats->noOldRender = 0;
893
894    /* Create global mutex */
895
896    /*
897     * Create another unique filename to lock upon. Note that
898     * depending on OS and locking mechanism of choice, the file
899     * may or may not be actually created.
900     */
901    mutexfilename = apr_psprintf(pconf, "/tmp/httpd_mutex.%ld",
902                                 (long int) getpid());
903
904    rs = apr_global_mutex_create(&stats_mutex, (const char *) mutexfilename,
905                                 APR_LOCK_DEFAULT, pconf);
906    if (rs != APR_SUCCESS) {
907        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
908                     "Failed to create mutex on file %s",
909                     mutexfilename);
910        return HTTP_INTERNAL_SERVER_ERROR;
911    }
912
913#ifdef MOD_TILE_SET_MUTEX_PERMS
914    rs = unixd_set_global_mutex_perms(stats_mutex);
915    if (rs != APR_SUCCESS) {
916        ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
917                     "Parent could not set permissions on mod_tile "
918                     "mutex: check User and Group directives");
919        return HTTP_INTERNAL_SERVER_ERROR;
920    }
921#endif /* MOD_TILE_SET_MUTEX_PERMS */
922
923    return OK;
924}
925
926
927/*
928 * This routine gets called when a child inits. We use it to attach
929 * to the shared memory segment, and reinitialize the mutex.
930 */
931
932static void mod_tile_child_init(apr_pool_t *p, server_rec *s)
933{
934    apr_status_t rs;
935
936     /*
937      * Re-open the mutex for the child. Note we're reusing
938      * the mutex pointer global here.
939      */
940     rs = apr_global_mutex_child_init(&stats_mutex,
941                                      (const char *) mutexfilename,
942                                      p);
943     if (rs != APR_SUCCESS) {
944         ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
945                     "Failed to reopen mutex on file %s",
946                     shmfilename);
947         /* There's really nothing else we can do here, since
948          * This routine doesn't return a status. */
949         exit(1); /* Ugly, but what else? */
950     }
951}
952
953static void register_hooks(__attribute__((unused)) apr_pool_t *p)
954{
955    ap_hook_post_config(mod_tile_post_config, NULL, NULL, APR_HOOK_MIDDLE);
956    ap_hook_child_init(mod_tile_child_init, NULL, NULL, APR_HOOK_MIDDLE);
957    ap_hook_handler(tile_handler_serve, NULL, NULL, APR_HOOK_MIDDLE);
958    ap_hook_handler(tile_handler_dirty, NULL, NULL, APR_HOOK_MIDDLE);
959    ap_hook_handler(tile_handler_status, NULL, NULL, APR_HOOK_MIDDLE);
960    ap_hook_handler(tile_handler_mod_stats, NULL, NULL, APR_HOOK_MIDDLE);
961    ap_hook_translate_name(tile_translate, NULL, NULL, APR_HOOK_MIDDLE);
962    ap_hook_map_to_storage(tile_storage_hook, NULL, NULL, APR_HOOK_FIRST);
963}
964
965static const char *_add_tile_config(cmd_parms *cmd, void *mconfig, const char *baseuri, const char *name, int minzoom, int maxzoom)
966{
967    if (strlen(name) == 0) {
968        return "ConfigName value must not be null";
969    }
970
971    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
972    tile_config_rec *tilecfg = apr_array_push(scfg->configs);
973
974    strncpy(tilecfg->baseuri, baseuri, PATH_MAX-1);
975    tilecfg->baseuri[PATH_MAX-1] = 0;
976    strncpy(tilecfg->xmlname, name, XMLCONFIG_MAX-1);
977    tilecfg->xmlname[XMLCONFIG_MAX-1] = 0;
978    tilecfg->minzoom = minzoom;
979    tilecfg->maxzoom = maxzoom;
980
981
982    return NULL;
983}
984
985static const char *add_tile_config(cmd_parms *cmd, void *mconfig, const char *baseuri, const char *name)
986{
987    return _add_tile_config(cmd, mconfig, baseuri, name, 0, MAX_ZOOM);
988}
989
990static const char *load_tile_config(cmd_parms *cmd, void *mconfig, const char *conffile)
991{
992    FILE * hini ;
993    char filename[PATH_MAX];
994    char xmlname[XMLCONFIG_MAX];
995    char line[INILINE_MAX];
996    char key[INILINE_MAX];
997    char value[INILINE_MAX];
998    const char * result;
999
1000    if (strlen(conffile) == 0) {
1001        strcpy(filename, RENDERD_CONFIG);
1002    } else {
1003        strcpy(filename, conffile);
1004    }
1005
1006    // Load the config
1007    if ((hini=fopen(filename, "r"))==NULL) {
1008        return "Unable to open config file";
1009    }
1010
1011    while (fgets(line, INILINE_MAX, hini)!=NULL) {
1012        if (line[0] == '#') continue;
1013        if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0;
1014        if (line[0] == '[') {
1015            if (strlen(line) >= XMLCONFIG_MAX){
1016                return "XML name too long";
1017            }
1018            sscanf(line, "[%[^]]", xmlname);
1019        } else if (sscanf(line, "%[^=]=%[^;#]", key, value) == 2
1020               ||  sscanf(line, "%[^=]=\"%[^\"]\"", key, value) == 2) {
1021
1022            if (!strcmp(key, "URI")){
1023                if (strlen(value) >= PATH_MAX){
1024                    return "URI too long";
1025                }
1026                result = add_tile_config(cmd, mconfig, value, xmlname);
1027                if (result != NULL) return result;
1028            }
1029        }
1030    }
1031    fclose(hini);
1032    return NULL;
1033}
1034
1035static const char *mod_tile_request_timeout_config(cmd_parms *cmd, void *mconfig, const char *request_timeout_string)
1036{
1037    int request_timeout;
1038
1039    if (sscanf(request_timeout_string, "%d", &request_timeout) != 1) {
1040        return "ModTileRequestTimeout needs integer argument";
1041    }
1042
1043    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1044    scfg->request_timeout = request_timeout;
1045    return NULL;
1046}
1047
1048static const char *mod_tile_max_load_old_config(cmd_parms *cmd, void *mconfig, const char *max_load_old_string)
1049{
1050    int max_load_old;
1051
1052    if (sscanf(max_load_old_string, "%d", &max_load_old) != 1) {
1053        return "ModTileMaxLoadOld needs integer argument";
1054    }
1055
1056    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1057    scfg->max_load_old = max_load_old;
1058    return NULL;
1059}
1060
1061static const char *mod_tile_max_load_missing_config(cmd_parms *cmd, void *mconfig, const char *max_load_missing_string)
1062{
1063    int max_load_missing;
1064
1065    if (sscanf(max_load_missing_string, "%d", &max_load_missing) != 1) {
1066        return "ModTileMaxLoadMissing needs integer argument";
1067    }
1068
1069    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1070    scfg->max_load_missing = max_load_missing;
1071    return NULL;
1072}
1073
1074static const char *mod_tile_renderd_socket_name_config(cmd_parms *cmd, void *mconfig, const char *renderd_socket_name_string)
1075{
1076    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1077    strncpy(scfg->renderd_socket_name, renderd_socket_name_string, PATH_MAX-1);
1078    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1079    return NULL;
1080}
1081
1082static const char *mod_tile_tile_dir_config(cmd_parms *cmd, void *mconfig, const char *tile_dir_string)
1083{
1084    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1085    strncpy(scfg->tile_dir, tile_dir_string, PATH_MAX-1);
1086    scfg->tile_dir[PATH_MAX-1] = 0;
1087    return NULL;
1088}
1089
1090static const char *mod_tile_cache_lastmod_factor_config(cmd_parms *cmd, void *mconfig, const char *modified_factor_string)
1091{
1092    float modified_factor;
1093    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1094            &tile_module);
1095    if (sscanf(modified_factor_string, "%f", &modified_factor) != 1) {
1096        return "ModTileCacheLastModifiedFactor needs float argument";
1097    }
1098    scfg->cache_duration_last_modified_factor = modified_factor;
1099    return NULL;
1100}
1101
1102static const char *mod_tile_cache_duration_max_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1103{
1104    int cache_duration;
1105    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1106            &tile_module);
1107    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1108        return "ModTileCacheDurationMax needs integer argument";
1109    }
1110    scfg->cache_duration_max = cache_duration;
1111    return NULL;
1112}
1113
1114static const char *mod_tile_cache_duration_dirty_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1115{
1116    int cache_duration;
1117    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1118            &tile_module);
1119    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1120        return "ModTileCacheDurationDirty needs integer argument";
1121    }
1122    scfg->cache_duration_dirty = cache_duration;
1123    return NULL;
1124}
1125
1126static const char *mod_tile_cache_duration_minimum_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1127{
1128    int cache_duration;
1129    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1130            &tile_module);
1131    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1132        return "ModTileCacheDurationMinimum needs integer argument";
1133    }
1134    scfg->cache_duration_minimum = cache_duration;
1135    return NULL;
1136}
1137
1138static const char *mod_tile_cache_duration_low_config(cmd_parms *cmd, void *mconfig, const char *zoom_level_string, const char *cache_duration_string)
1139{
1140    int zoom_level;
1141    int cache_duration;
1142    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1143    if (sscanf(zoom_level_string, "%d", &zoom_level) != 1) {
1144            return "ModTileCacheDurationLowZoom needs integer argument";
1145    }
1146    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1147            return "ModTileCacheDurationLowZoom needs integer argument";
1148    }
1149    scfg->cache_level_low_zoom = zoom_level;
1150    scfg->cache_duration_low_zoom = cache_duration;
1151
1152    return NULL;
1153}
1154static const char *mod_tile_cache_duration_medium_config(cmd_parms *cmd, void *mconfig, const char *zoom_level_string, const char *cache_duration_string)
1155{
1156    int zoom_level;
1157    int cache_duration;
1158    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1159    if (sscanf(zoom_level_string, "%d", &zoom_level) != 1) {
1160            return "ModTileCacheDurationMediumZoom needs integer argument";
1161    }
1162    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1163            return "ModTileCacheDurationMediumZoom needs integer argument";
1164    }
1165    scfg->cache_level_medium_zoom = zoom_level;
1166    scfg->cache_duration_medium_zoom = cache_duration;
1167
1168    return NULL;
1169}
1170
1171static const char *mod_tile_enable_stats(cmd_parms *cmd, void *mconfig, int enableStats)
1172{
1173    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1174    scfg->enableGlobalStats = enableStats;
1175    return NULL;
1176}
1177
1178static void *create_tile_config(apr_pool_t *p, server_rec *s)
1179{
1180    tile_server_conf * scfg = (tile_server_conf *) apr_pcalloc(p, sizeof(tile_server_conf));
1181
1182    scfg->configs = apr_array_make(p, 4, sizeof(tile_config_rec));
1183    scfg->request_timeout = REQUEST_TIMEOUT;
1184    scfg->max_load_old = MAX_LOAD_OLD;
1185    scfg->max_load_missing = MAX_LOAD_MISSING;
1186    strncpy(scfg->renderd_socket_name, RENDER_SOCKET, PATH_MAX-1);
1187    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1188    strncpy(scfg->tile_dir, HASH_PATH, PATH_MAX-1);
1189    scfg->tile_dir[PATH_MAX-1] = 0;
1190    scfg->cache_duration_dirty = 15*60;
1191    scfg->cache_duration_last_modified_factor = 0.0;
1192    scfg->cache_duration_max = 7*24*60*60;
1193    scfg->cache_duration_minimum = 3*60*60;
1194    scfg->cache_duration_low_zoom = 6*24*60*60;
1195    scfg->cache_duration_medium_zoom = 1*24*60*60;
1196    scfg->cache_level_low_zoom = 0;
1197    scfg->cache_level_medium_zoom = 0;
1198    scfg->enableGlobalStats = 1;
1199
1200    return scfg;
1201}
1202
1203static void *merge_tile_config(apr_pool_t *p, void *basev, void *overridesv)
1204{
1205    int i;
1206    tile_server_conf * scfg = (tile_server_conf *) apr_pcalloc(p, sizeof(tile_server_conf));
1207    tile_server_conf * scfg_base = (tile_server_conf *) basev;
1208    tile_server_conf * scfg_over = (tile_server_conf *) overridesv;
1209
1210    scfg->configs = apr_array_append(p, scfg_base->configs, scfg_over->configs);
1211    scfg->request_timeout = scfg_over->request_timeout;
1212    scfg->max_load_old = scfg_over->max_load_old;
1213    scfg->max_load_missing = scfg_over->max_load_missing;
1214    strncpy(scfg->renderd_socket_name, scfg_over->renderd_socket_name, PATH_MAX-1);
1215    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1216    strncpy(scfg->tile_dir, scfg_over->tile_dir, PATH_MAX-1);
1217    scfg->tile_dir[PATH_MAX-1] = 0;
1218    scfg->cache_duration_dirty = scfg_over->cache_duration_dirty;
1219    scfg->cache_duration_last_modified_factor = scfg_over->cache_duration_last_modified_factor;
1220    scfg->cache_duration_max = scfg_over->cache_duration_max;
1221    scfg->cache_duration_minimum = scfg_over->cache_duration_minimum;
1222    scfg->cache_duration_low_zoom = scfg_over->cache_duration_low_zoom;
1223    scfg->cache_duration_medium_zoom = scfg_over->cache_duration_medium_zoom;
1224    scfg->cache_level_low_zoom = scfg_over->cache_level_low_zoom;
1225    scfg->cache_level_medium_zoom = scfg_over->cache_level_medium_zoom;
1226    scfg->enableGlobalStats = scfg_over->enableGlobalStats;
1227
1228    //Construct a table of minimum cache times per zoom level
1229    for (i = 0; i <= MAX_ZOOM; i++) {
1230        if (i <= scfg->cache_level_low_zoom) {
1231            scfg->mincachetime[i] = scfg->cache_duration_low_zoom;
1232        } else if (i <= scfg->cache_level_medium_zoom) {
1233            scfg->mincachetime[i] = scfg->cache_duration_medium_zoom;
1234        } else {
1235            scfg->mincachetime[i] = scfg->cache_duration_minimum;
1236        }
1237    }
1238
1239    return scfg;
1240}
1241
1242static const command_rec tile_cmds[] =
1243{
1244    AP_INIT_TAKE1(
1245        "LoadTileConfigFile",            /* directive name */
1246        load_tile_config,                /* config action routine */
1247        NULL,                            /* argument to include in call */
1248        OR_OPTIONS,                      /* where available */
1249        "load an entire renderd config file"  /* directive description */
1250    ),
1251    AP_INIT_TAKE2(
1252        "AddTileConfig",                 /* directive name */
1253        add_tile_config,                 /* config action routine */
1254        NULL,                            /* argument to include in call */
1255        OR_OPTIONS,                      /* where available */
1256        "path and name of renderd config to use"  /* directive description */
1257    ),
1258    AP_INIT_TAKE1(
1259        "ModTileRequestTimeout",         /* directive name */
1260        mod_tile_request_timeout_config, /* config action routine */
1261        NULL,                            /* argument to include in call */
1262        OR_OPTIONS,                      /* where available */
1263        "Set timeout in seconds on mod_tile requests"  /* directive description */
1264    ),
1265    AP_INIT_TAKE1(
1266        "ModTileMaxLoadOld",             /* directive name */
1267        mod_tile_max_load_old_config,    /* config action routine */
1268        NULL,                            /* argument to include in call */
1269        OR_OPTIONS,                      /* where available */
1270        "Set max load for rendering old tiles"  /* directive description */
1271    ),
1272    AP_INIT_TAKE1(
1273        "ModTileMaxLoadMissing",         /* directive name */
1274        mod_tile_max_load_missing_config, /* config action routine */
1275        NULL,                            /* argument to include in call */
1276        OR_OPTIONS,                      /* where available */
1277        "Set max load for rendering missing tiles"  /* directive description */
1278    ),
1279    AP_INIT_TAKE1(
1280        "ModTileRenderdSocketName",      /* directive name */
1281        mod_tile_renderd_socket_name_config, /* config action routine */
1282        NULL,                            /* argument to include in call */
1283        OR_OPTIONS,                      /* where available */
1284        "Set name of unix domain socket for connecting to rendering daemon"  /* directive description */
1285    ),
1286    AP_INIT_TAKE1(
1287        "ModTileTileDir",                /* directive name */
1288        mod_tile_tile_dir_config,        /* config action routine */
1289        NULL,                            /* argument to include in call */
1290        OR_OPTIONS,                      /* where available */
1291        "Set name of tile cache directory"  /* directive description */
1292    ),
1293    AP_INIT_TAKE1(
1294        "ModTileCacheDurationMax",                /* directive name */
1295        mod_tile_cache_duration_max_config,        /* config action routine */
1296        NULL,                            /* argument to include in call */
1297        OR_OPTIONS,                      /* where available */
1298        "Set the maximum cache expiry in seconds"  /* directive description */
1299    ),
1300    AP_INIT_TAKE1(
1301        "ModTileCacheDurationDirty",                    /* directive name */
1302        mod_tile_cache_duration_dirty_config,           /* config action routine */
1303        NULL,                                           /* argument to include in call */
1304        OR_OPTIONS,                                     /* where available */
1305        "Set the cache expiry for serving dirty tiles"  /* directive description */
1306    ),
1307    AP_INIT_TAKE1(
1308        "ModTileCacheDurationMinimum",          /* directive name */
1309        mod_tile_cache_duration_minimum_config, /* config action routine */
1310        NULL,                                   /* argument to include in call */
1311        OR_OPTIONS,                             /* where available */
1312        "Set the minimum cache expiry"          /* directive description */
1313    ),
1314    AP_INIT_TAKE1(
1315        "ModTileCacheLastModifiedFactor",       /* directive name */
1316        mod_tile_cache_lastmod_factor_config,   /* config action routine */
1317        NULL,                                   /* argument to include in call */
1318        OR_OPTIONS,                             /* where available */
1319        "Set the factor by which the last modified determins cache expiry" /* directive description */
1320    ),
1321    AP_INIT_TAKE2(
1322        "ModTileCacheDurationLowZoom",       /* directive name */
1323        mod_tile_cache_duration_low_config,                 /* config action routine */
1324        NULL,                            /* argument to include in call */
1325        OR_OPTIONS,                      /* where available */
1326        "Set the minimum cache duration and zoom level for low zoom tiles"  /* directive description */
1327    ),
1328    AP_INIT_TAKE2(
1329        "ModTileCacheDurationMediumZoom",       /* directive name */
1330        mod_tile_cache_duration_medium_config,                 /* config action routine */
1331        NULL,                            /* argument to include in call */
1332        OR_OPTIONS,                      /* where available */
1333        "Set the minimum cache duration and zoom level for medium zoom tiles"  /* directive description */
1334    ),
1335    AP_INIT_FLAG(
1336        "ModTileEnableStats",       /* directive name */
1337        mod_tile_enable_stats,                 /* config action routine */
1338        NULL,                            /* argument to include in call */
1339        OR_OPTIONS,                      /* where available */
1340        "On Off - enable of keeping stats about what mod_tile is serving"  /* directive description */
1341    ),
1342    {NULL}
1343};
1344
1345module AP_MODULE_DECLARE_DATA tile_module =
1346{
1347    STANDARD20_MODULE_STUFF,
1348    NULL,                                /* dir config creater */
1349    NULL,                                /* dir merger --- default is to override */
1350    create_tile_config,                  /* server config */
1351    merge_tile_config,                   /* merge server config */
1352    tile_cmds,                           /* command apr_table_t */
1353    register_hooks                       /* register hooks */
1354};
1355
Note: See TracBrowser for help on using the repository browser.