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

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

mod_tile: Allow URI strings with or without a trailing slash. Previously a missing slash would cause the rendering to fail. Closes trac ticket 2428

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