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

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

[mod_tile] Output some stats on what mod_tile returns

This patch reports two properties of the served tiles. First it reports how many tiles are returned with which response code, i.e.
how many tiles get return with http code 200, 304, 404 or 500. Secondly, it reports if tiles are returned fresh from disk, fresh
after being rendered on the spot, old without attempting to render, old after timing out waiting for the tile to be rendered.

The stats get reported through a special page at "http://servername/mod_tile".

Caveat: In order to report the stats of a global counter, a mutex is needed that serialises access to that counter. This can have
some performance effects, although it is attempted to hold this lock as short as possible. Measuring the effect with apache bench,
running for 1 million tile requests at a concurrency level of 10, this gave about a slowdown of 1%. This should be pretty much
worst case, as apache bench always requests the same tile, so that is all in cache. With this, it still achieves more than 5800
requests per second at 50Mb/s on a dual core laptop. On different hardware, this can obviously differ.

Stats reporting can be disabled with a config setting in mod_tile.conf

The locking code is based on mod_example_ipc.c

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