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

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

Splitt out timeouts in mod_tile for standard and priority rendering

Allow for a longer timeout on requests that are missing and thus would cause a 404,
than for tiles that would just be served out of date

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