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

Last change on this file since 20343 was 20343, checked in by apmon, 10 years ago

[mod_tile] Add a config parameter to overwrite caching-header heuristics based on hostname

This allows to have hostname aliases (e.g. cache.tile.openstreetmap.org) to become more (or less)
cache friendly, to tune the trade off between up-to-dateness and minimizing network resources

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