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

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

[mod_tile renderd munin] report mod_tile / renderd usage by zoom level via munin

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