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

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

[mod_tile] Fixup configuration handling of the cache-expire settings

The initialisation of the per zoom level cache expire table was done in the wrong
section of the configuration parsing. This meant the results were order dependent in statements in mod_tile.conf,
as some of the values might not have been correctly set at that time of calculating the table.

File size: 35.2 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
50typedef struct {
51    char xmlname[XMLCONFIG_MAX];
52    char baseuri[PATH_MAX];
53    int minzoom;
54    int maxzoom;
55} tile_config_rec;
56
57typedef struct {
58    apr_array_header_t *configs;
59    int request_timeout;
60    int max_load_old;
61    int max_load_missing;
62    int cache_duration_dirty;
63    int cache_duration_max;
64    int cache_duration_minimum;
65    int cache_duration_low_zoom;
66    int cache_level_low_zoom;
67    int cache_duration_medium_zoom;
68    int cache_level_medium_zoom;
69    double cache_duration_last_modified_factor;
70    char renderd_socket_name[PATH_MAX];
71    char tile_dir[PATH_MAX];
72    int mincachetime[MAX_ZOOM + 1];
73} tile_server_conf;
74
75enum tileState { tileMissing, tileOld, tileCurrent };
76
77static int error_message(request_rec *r, const char *format, ...)
78                 __attribute__ ((format (printf, 2, 3)));
79
80static int error_message(request_rec *r, const char *format, ...)
81{
82    va_list ap;
83    va_start(ap, format);
84    int len;
85    char *msg;
86
87    len = vasprintf(&msg, format, ap);
88
89    if (msg) {
90        //ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "%s", msg);
91        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "%s", msg);
92        r->content_type = "text/plain";
93        if (!r->header_only)
94            ap_rputs(msg, r);
95        free(msg);
96    }
97
98    return OK;
99}
100
101
102int socket_init(request_rec *r)
103{
104    ap_conf_vector_t *sconf = r->server->module_config;
105    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
106
107    int fd;
108    struct sockaddr_un addr;
109
110    fd = socket(PF_UNIX, SOCK_STREAM, 0);
111    if (fd < 0) {
112        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "failed to create unix socket");
113        return FD_INVALID;
114    }
115
116    bzero(&addr, sizeof(addr));
117    addr.sun_family = AF_UNIX;
118    strncpy(addr.sun_path, scfg->renderd_socket_name, sizeof(addr.sun_path));
119
120    if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
121        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "socket connect failed for: %s", scfg->renderd_socket_name);
122        close(fd);
123        return FD_INVALID;
124    }
125    return fd;
126}
127
128static pthread_key_t key;
129static pthread_once_t key_once = PTHREAD_ONCE_INIT;
130
131static void pfd_free(void *ptr)
132{
133    int *pfd = ptr;
134
135    if (*pfd != FD_INVALID)
136        close(*pfd);
137    free(pfd);
138}
139
140static void make_key(void)
141{
142    (void) pthread_key_create(&key, pfd_free);
143}
144
145int request_tile(request_rec *r, struct protocol *cmd, int dirtyOnly)
146{
147    int *pfd;
148    int ret = 0;
149    int retry = 1;
150    struct protocol resp;
151
152    ap_conf_vector_t *sconf = r->server->module_config;
153    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
154
155    (void) pthread_once(&key_once, make_key);
156    if ((pfd = pthread_getspecific(key)) == NULL) {
157        pfd = malloc(sizeof(*pfd));
158        if (!pfd)
159            return 0;
160        *pfd = FD_INVALID;
161        (void) pthread_setspecific(key, pfd);
162    }
163
164    if (*pfd == FD_INVALID) {
165        *pfd = socket_init(r);
166
167        if (*pfd == FD_INVALID) {
168            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Failed to connected to renderer");
169            return 0;
170        } else {
171            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Connected to renderer");
172        }
173    }
174
175    // cmd has already been partial filled, fill in the rest
176    cmd->ver = PROTO_VER;
177    cmd->cmd = dirtyOnly ? cmdDirty : cmdRender;
178
179    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);
180    do {
181        ret = send(*pfd, cmd, sizeof(struct protocol), 0);
182
183        if (ret == sizeof(struct protocol))
184            break;
185
186        if (errno != EPIPE)
187            return 0;
188
189         close(*pfd);
190         *pfd = socket_init(r);
191         if (*pfd == FD_INVALID)
192             return 0;
193         ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Reconnected to renderer");
194    } while (retry--);
195
196    if (!dirtyOnly) {
197        struct timeval tv = {scfg->request_timeout, 0 };
198        fd_set rx;
199        int s;
200
201        while (1) {
202            FD_ZERO(&rx);
203            FD_SET(*pfd, &rx);
204            s = select((*pfd)+1, &rx, NULL, NULL, &tv);
205            if (s == 1) {
206                bzero(&resp, sizeof(struct protocol));
207                ret = recv(*pfd, &resp, sizeof(struct protocol), 0);
208                if (ret != sizeof(struct protocol)) {
209                    if (errno == EPIPE) {
210                        close(*pfd);
211                        *pfd = FD_INVALID;
212                    }
213                    //perror("recv error");
214                    break;
215                }
216
217                if (cmd->x == resp.x && cmd->y == resp.y && cmd->z == resp.z && !strcmp(cmd->xmlname, resp.xmlname)) {
218                    if (resp.cmd == cmdDone)
219                        return 1;
220                    else
221                        return 0;
222                } else {
223                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
224                       "Response does not match request: xml(%s,%s) z(%d,%d) x(%d,%d) y(%d,%d)", cmd->xmlname,
225                       resp.xmlname, cmd->z, resp.z, cmd->x, resp.x, cmd->y, resp.y);
226                }
227            } else if (s == 0) {
228                break;
229            } else {
230                if (errno == EPIPE) {
231                    close(*pfd);
232                    *pfd = FD_INVALID;
233                    break;
234                }
235            }
236        }
237    }
238    return 0;
239}
240
241static apr_time_t getPlanetTime(request_rec *r)
242{
243    static apr_time_t last_check;
244    static apr_time_t planet_timestamp;
245    static pthread_mutex_t planet_lock = PTHREAD_MUTEX_INITIALIZER;
246    apr_time_t now = r->request_time;
247    struct apr_finfo_t s;
248
249    pthread_mutex_lock(&planet_lock);
250    // Only check for updates periodically
251    if (now < last_check + apr_time_from_sec(300)) {
252        pthread_mutex_unlock(&planet_lock);
253        return planet_timestamp;
254    }
255
256    ap_conf_vector_t *sconf = r->server->module_config;
257    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
258    char filename[PATH_MAX];
259    snprintf(filename, PATH_MAX-1, "%s/%s", scfg->tile_dir, PLANET_TIMESTAMP);
260
261    last_check = now;
262    if (apr_stat(&s, filename, APR_FINFO_MIN, r->pool) != APR_SUCCESS) {
263        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Planet timestamp file (%s) is missing", filename);
264        // Make something up
265        planet_timestamp = now - apr_time_from_sec(3 * 24 * 60 * 60);
266    } else {
267        if (s.mtime != planet_timestamp) {
268            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Planet file updated");
269            planet_timestamp = s.mtime;
270        }
271    }
272    pthread_mutex_unlock(&planet_lock);
273    return planet_timestamp;
274}
275
276static enum tileState tile_state_once(request_rec *r)
277{
278    apr_status_t rv;
279    apr_finfo_t *finfo = &r->finfo;
280
281    if (!(finfo->valid & APR_FINFO_MTIME)) {
282        rv = apr_stat(finfo, r->filename, APR_FINFO_MIN, r->pool);
283        if (rv != APR_SUCCESS)
284            return tileMissing;
285    }
286
287    if (finfo->mtime < getPlanetTime(r))
288        return tileOld;
289
290    return tileCurrent;
291}
292
293static enum tileState tile_state(request_rec *r, struct protocol *cmd)
294{
295    enum tileState state = tile_state_once(r);
296#ifdef METATILEFALLBACK
297    if (state == tileMissing) {
298        ap_conf_vector_t *sconf = r->server->module_config;
299        tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
300
301        // Try fallback to plain PNG
302        char path[PATH_MAX];
303        xyz_to_path(path, sizeof(path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
304        r->filename = apr_pstrdup(r->pool, path);
305        state = tile_state_once(r);
306        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "png fallback %d/%d/%d",x,y,z);
307
308        if (state == tileMissing) {
309            // PNG not available either, if it gets rendered, it'll now be a .meta
310            xyz_to_meta(path, sizeof(path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
311            r->filename = apr_pstrdup(r->pool, path);
312        }
313    }
314#endif
315    return state;
316}
317
318static void add_expiry(request_rec *r, struct protocol * cmd)
319{
320    apr_time_t holdoff;
321    apr_table_t *t = r->headers_out;
322    enum tileState state = tile_state(r, cmd);
323    apr_finfo_t *finfo = &r->finfo;
324    char *timestr;
325    long int planetTimestamp, maxAge, minCache, lastModified;
326
327    ap_conf_vector_t *sconf = r->server->module_config;
328    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
329
330    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "expires(%s), uri(%s), filename(%s), path_info(%s)\n",
331                  r->handler, r->uri, r->filename, r->path_info);
332
333    /* Test if the tile we are serving is out of date, then set a low maxAge*/
334    if (state == tileOld) {
335        holdoff = (scfg->cache_duration_dirty /2) * (rand() / (RAND_MAX + 1.0));
336        maxAge = scfg->cache_duration_dirty + holdoff;
337    } else {
338        // cache heuristic based on zoom level
339        if (cmd->z > MAX_ZOOM) {
340            minCache = 0;
341            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
342                                    "z (%i) is larger than MAXZOOM %i\n",
343                                    cmd->z, MAX_ZOOM);
344        } else {
345            minCache = scfg->mincachetime[cmd->z];
346        }
347        // Time to the next known complete rerender
348        planetTimestamp = apr_time_sec(getPlanetTime(r) + apr_time_from_sec(PLANET_INTERVAL) - r->request_time) ;
349        // Time since the last render of this tile
350        lastModified = (int)(((double)apr_time_sec(r->request_time - finfo->mtime)) * scfg->cache_duration_last_modified_factor);
351        // Add a random jitter of 3 hours to space out cache expiry
352        holdoff = (3 * 60 * 60) * (rand() / (RAND_MAX + 1.0));
353
354        maxAge = MAX(minCache, planetTimestamp);
355        maxAge = MAX(maxAge,lastModified);
356        maxAge += holdoff;
357
358        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
359                        "caching heuristics: next planet render %ld; zoom level based %ld; last modified %ld\n",
360                        planetTimestamp, minCache, lastModified);
361    }
362
363    maxAge = MIN(maxAge, scfg->cache_duration_max);
364
365    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Setting tiles maxAge to %ld\n", maxAge);
366
367    apr_table_mergen(t, "Cache-Control",
368                     apr_psprintf(r->pool, "max-age=%" APR_TIME_T_FMT,
369                     maxAge));
370    timestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
371    apr_rfc822_date(timestr, (apr_time_from_sec(maxAge) + r->request_time));
372    apr_table_setn(t, "Expires", timestr);
373}
374
375double get_load_avg(request_rec *r)
376{
377    double loadavg[1];
378    int n = getloadavg(loadavg, 1);
379
380    if (n < 1)
381        return 1000;
382    else
383        return loadavg[0];
384}
385
386static int tile_handler_dirty(request_rec *r)
387{
388    if(strcmp(r->handler, "tile_dirty"))
389        return DECLINED;
390
391    struct protocol * cmd = (struct protocol *)ap_get_module_config(r->request_config, &tile_module);
392    if (cmd == NULL)
393        return DECLINED;
394
395    request_tile(r, cmd, 1);
396    return error_message(r, "Tile submitted for rendering\n");
397}
398
399static int tile_storage_hook(request_rec *r)
400{
401//    char abs_path[PATH_MAX];
402    int avg;
403    enum tileState state;
404
405    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "tile_storage_hook: handler(%s), uri(%s), filename(%s), path_info(%s)",
406                  r->handler, r->uri, r->filename, r->path_info);
407
408    if (!r->handler)
409        return DECLINED;
410
411    // Any status request is OK
412    if (!strcmp(r->handler, "tile_status"))
413        return OK;
414
415    if (strcmp(r->handler, "tile_serve") && strcmp(r->handler, "tile_dirty"))
416        return DECLINED;
417
418    struct protocol * cmd = (struct protocol *)ap_get_module_config(r->request_config, &tile_module);
419    if (cmd == NULL)
420        return DECLINED;
421/*
422should already be done
423    // Generate the tile filename
424#ifdef METATILE
425    ap_conf_vector_t *sconf = r->server->module_config;
426    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
427    xyz_to_meta(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
428#else
429    xyz_to_path(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
430#endif
431    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "abs_path(%s)", abs_path);
432    r->filename = apr_pstrdup(r->pool, abs_path);
433*/
434    avg = get_load_avg(r);
435    state = tile_state(r, cmd);
436
437    ap_conf_vector_t *sconf = r->server->module_config;
438    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
439
440    switch (state) {
441        case tileCurrent:
442            return OK;
443            break;
444        case tileOld:
445            if (avg > scfg->max_load_old) {
446               // Too much load to render it now, mark dirty but return old tile
447               request_tile(r, cmd, 1);
448               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);
449               return OK;
450            }
451            break;
452        case tileMissing:
453            if (avg > scfg->max_load_missing) {
454               request_tile(r, cmd, 1);
455               ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Load larger max_load_missing (%d). Return HTTP_NOT_FOUND.", scfg->max_load_missing);
456               return HTTP_NOT_FOUND;
457            }
458            break;
459    }
460
461    if (request_tile(r, cmd, 0)) {
462        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Update file info abs_path(%s)", r->filename);
463        // Need to update fileinfo for new rendered tile
464        apr_stat(&r->finfo, r->filename, APR_FINFO_MIN, r->pool);
465        return OK;
466    }
467
468    if (state == tileOld)
469        return OK;
470
471    return HTTP_NOT_FOUND;
472}
473
474static int tile_handler_status(request_rec *r)
475{
476    enum tileState state;
477    char time_str[APR_CTIME_LEN];
478
479    if(strcmp(r->handler, "tile_status"))
480        return DECLINED;
481
482    struct protocol * cmd = (struct protocol *)ap_get_module_config(r->request_config, &tile_module);
483    if (cmd == NULL){
484        sleep(CLIENT_PENALTY);
485        return HTTP_NOT_FOUND;
486    }
487
488    state = tile_state(r, cmd);
489    if (state == tileMissing)
490        return error_message(r, "Unable to find a tile at %s\n", r->filename);
491    apr_ctime(time_str, r->finfo.mtime);
492
493    return error_message(r, "Tile is %s. Last rendered at %s\n", (state == tileOld) ? "due to be rendered" : "clean", time_str);
494}
495
496static int tile_handler_serve(request_rec *r)
497{
498    const int tile_max = MAX_SIZE;
499    unsigned char *buf;
500    int len;
501    apr_status_t errstatus;
502
503    if(strcmp(r->handler, "tile_serve"))
504        return DECLINED;
505
506    struct protocol * cmd = (struct protocol *)ap_get_module_config(r->request_config, &tile_module);
507    if (cmd == NULL){
508        sleep(CLIENT_PENALTY);
509        return HTTP_NOT_FOUND;
510    }
511
512    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);
513
514    // FIXME: It is a waste to do the malloc + read if we are fulfilling a HEAD or returning a 304.
515    buf = malloc(tile_max);
516    if (!buf)
517        return HTTP_INTERNAL_SERVER_ERROR;
518
519    len = tile_read(cmd->xmlname, cmd->x, cmd->y, cmd->z, buf, tile_max);
520    if (len > 0) {
521#if 0
522        // Set default Last-Modified and Etag headers
523        ap_update_mtime(r, r->finfo.mtime);
524        ap_set_last_modified(r);
525        ap_set_etag(r);
526#else
527        // Use MD5 hash as only cache attribute.
528        // If a tile is re-rendered and produces the same output
529        // then we can continue to use the previous cached copy
530        char *md5 = ap_md5_binary(r->pool, buf, len);
531        apr_table_setn(r->headers_out, "ETag",
532                        apr_psprintf(r->pool, "\"%s\"", md5));
533#endif
534        ap_set_content_type(r, "image/png");
535        ap_set_content_length(r, len);
536        add_expiry(r, cmd);
537        if ((errstatus = ap_meets_conditions(r)) != OK) {
538            free(buf);
539            return errstatus;
540        } else {
541            ap_rwrite(buf, len, r);
542            free(buf);
543            return OK;
544        }
545    }
546    free(buf);
547    //ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "len = %d", len);
548
549    return DECLINED;
550}
551
552static int tile_translate(request_rec *r)
553{
554    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "tile_translate: uri(%s)", r->uri);
555
556    int i,n,limit,oob;
557    char option[11];
558
559    ap_conf_vector_t *sconf = r->server->module_config;
560    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
561
562    tile_config_rec *tile_configs = (tile_config_rec *) scfg->configs->elts;
563    for (i = 0; i < scfg->configs->nelts; ++i) {
564        tile_config_rec *tile_config = &tile_configs[i];
565
566        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "tile_translate: baseuri(%s) name(%s)", tile_config->baseuri, tile_config->xmlname);
567
568        if (!strncmp(tile_config->baseuri, r->uri, strlen(tile_config->baseuri))) {
569
570            struct protocol * cmd = (struct protocol *) apr_pcalloc(r->pool, sizeof(struct protocol));
571            bzero(cmd, sizeof(struct protocol));
572
573            n = sscanf(r->uri+strlen(tile_config->baseuri), "%d/%d/%d.png/%10s", &(cmd->z), &(cmd->x), &(cmd->y), option);
574            if (n < 3) return DECLINED;
575
576            oob = (cmd->z < 0 || cmd->z > MAX_ZOOM);
577            if (!oob) {
578                 // valid x/y for tiles are 0 ... 2^zoom-1
579                 limit = (1 << cmd->z) - 1;
580                 oob =  (cmd->x < 0 || cmd->x > limit || cmd->y < 0 || cmd->y > limit);
581            }
582
583            if (oob) {
584                sleep(CLIENT_PENALTY);
585                return HTTP_NOT_FOUND;
586            }
587
588            strcpy(cmd->xmlname, tile_config->xmlname);
589
590            // Store a copy for later
591            ap_set_module_config(r->request_config, &tile_module, cmd);
592
593            // Generate the tile filename?
594            char abs_path[PATH_MAX];
595#ifdef METATILE
596            xyz_to_meta(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
597#else
598            xyz_to_path(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
599#endif
600            r->filename = apr_pstrdup(r->pool, abs_path);
601
602            if (n == 4) {
603                if (!strcmp(option, "status")) r->handler = "tile_status";
604                else if (!strcmp(option, "dirty")) r->handler = "tile_dirty";
605                else return DECLINED;
606            } else {
607                r->handler = "tile_serve";
608            }
609
610            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);
611
612            return OK;
613        }
614    }
615    return DECLINED;
616}
617
618static void register_hooks(__attribute__((unused)) apr_pool_t *p)
619{
620    ap_hook_handler(tile_handler_serve, NULL, NULL, APR_HOOK_MIDDLE);
621    ap_hook_handler(tile_handler_dirty, NULL, NULL, APR_HOOK_MIDDLE);
622    ap_hook_handler(tile_handler_status, NULL, NULL, APR_HOOK_MIDDLE);
623    ap_hook_translate_name(tile_translate, NULL, NULL, APR_HOOK_MIDDLE);
624    ap_hook_map_to_storage(tile_storage_hook, NULL, NULL, APR_HOOK_FIRST);
625}
626
627static const char *_add_tile_config(cmd_parms *cmd, void *mconfig, const char *baseuri, const char *name, int minzoom, int maxzoom)
628{
629    if (strlen(name) == 0) {
630        return "ConfigName value must not be null";
631    }
632
633    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
634    tile_config_rec *tilecfg = apr_array_push(scfg->configs);
635
636    strncpy(tilecfg->baseuri, baseuri, PATH_MAX-1);
637    tilecfg->baseuri[PATH_MAX-1] = 0;
638    strncpy(tilecfg->xmlname, name, XMLCONFIG_MAX-1);
639    tilecfg->xmlname[XMLCONFIG_MAX-1] = 0;
640    tilecfg->minzoom = minzoom;
641    tilecfg->maxzoom = maxzoom;
642
643
644    return NULL;
645}
646
647static const char *add_tile_config(cmd_parms *cmd, void *mconfig, const char *baseuri, const char *name)
648{
649    return _add_tile_config(cmd, mconfig, baseuri, name, 0, MAX_ZOOM);
650}
651
652static const char *load_tile_config(cmd_parms *cmd, void *mconfig, const char *conffile)
653{
654    FILE * hini ;
655    char filename[PATH_MAX];
656    char xmlname[XMLCONFIG_MAX];
657    char line[INILINE_MAX];
658    char key[INILINE_MAX];
659    char value[INILINE_MAX];
660    const char * result;
661
662    if (strlen(conffile) == 0) {
663        strcpy(filename, RENDERD_CONFIG);
664    } else {
665        strcpy(filename, conffile);
666    }
667
668    // Load the config
669    if ((hini=fopen(filename, "r"))==NULL) {
670        return "Unable to open config file";
671    }
672
673    while (fgets(line, INILINE_MAX, hini)!=NULL) {
674        if (line[0] == '#') continue;
675        if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0;
676        if (line[0] == '[') {
677            if (strlen(line) >= XMLCONFIG_MAX){
678                return "XML name too long";
679            }
680            sscanf(line, "[%[^]]", xmlname);
681        } else if (sscanf(line, "%[^=]=%[^;#]", key, value) == 2
682               ||  sscanf(line, "%[^=]=\"%[^\"]\"", key, value) == 2) {
683
684            if (!strcmp(key, "URI")){
685                if (strlen(value) >= PATH_MAX){
686                    return "URI too long";
687                }
688                result = add_tile_config(cmd, mconfig, value, xmlname);
689                if (result != NULL) return result;
690            }
691        }
692    }
693    fclose(hini);
694    return NULL;
695}
696
697static const char *mod_tile_request_timeout_config(cmd_parms *cmd, void *mconfig, const char *request_timeout_string)
698{
699    int request_timeout;
700
701    if (sscanf(request_timeout_string, "%d", &request_timeout) != 1) {
702        return "ModTileRequestTimeout needs integer argument";
703    }
704
705    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
706    scfg->request_timeout = request_timeout;
707    return NULL;
708}
709
710static const char *mod_tile_max_load_old_config(cmd_parms *cmd, void *mconfig, const char *max_load_old_string)
711{
712    int max_load_old;
713
714    if (sscanf(max_load_old_string, "%d", &max_load_old) != 1) {
715        return "ModTileMaxLoadOld needs integer argument";
716    }
717
718    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
719    scfg->max_load_old = max_load_old;
720    return NULL;
721}
722
723static const char *mod_tile_max_load_missing_config(cmd_parms *cmd, void *mconfig, const char *max_load_missing_string)
724{
725    int max_load_missing;
726
727    if (sscanf(max_load_missing_string, "%d", &max_load_missing) != 1) {
728        return "ModTileMaxLoadMissing needs integer argument";
729    }
730
731    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
732    scfg->max_load_missing = max_load_missing;
733    return NULL;
734}
735
736static const char *mod_tile_renderd_socket_name_config(cmd_parms *cmd, void *mconfig, const char *renderd_socket_name_string)
737{
738    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
739    strncpy(scfg->renderd_socket_name, renderd_socket_name_string, PATH_MAX-1);
740    scfg->renderd_socket_name[PATH_MAX-1] = 0;
741    return NULL;
742}
743
744static const char *mod_tile_tile_dir_config(cmd_parms *cmd, void *mconfig, const char *tile_dir_string)
745{
746    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
747    strncpy(scfg->tile_dir, tile_dir_string, PATH_MAX-1);
748    scfg->tile_dir[PATH_MAX-1] = 0;
749    return NULL;
750}
751
752static const char *mod_tile_cache_lastmod_factor_config(cmd_parms *cmd, void *mconfig, const char *modified_factor_string)
753{
754    float modified_factor;
755    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
756            &tile_module);
757    if (sscanf(modified_factor_string, "%f", &modified_factor) != 1) {
758        return "ModTileCacheLastModifiedFactor needs float argument";
759    }
760    scfg->cache_duration_last_modified_factor = modified_factor;
761    return NULL;
762}
763
764static const char *mod_tile_cache_duration_max_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
765{
766    int cache_duration;
767    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
768            &tile_module);
769    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
770        return "ModTileCacheDurationMax needs integer argument";
771    }
772    scfg->cache_duration_max = cache_duration;
773    return NULL;
774}
775
776static const char *mod_tile_cache_duration_dirty_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
777{
778    int cache_duration;
779    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
780            &tile_module);
781    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
782        return "ModTileCacheDurationDirty needs integer argument";
783    }
784    scfg->cache_duration_dirty = cache_duration;
785    return NULL;
786}
787
788static const char *mod_tile_cache_duration_minimum_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
789{
790    int cache_duration;
791    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
792            &tile_module);
793    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
794        return "ModTileCacheDurationMinimum needs integer argument";
795    }
796    scfg->cache_duration_minimum = cache_duration;
797    return NULL;
798}
799
800static const char *mod_tile_cache_duration_low_config(cmd_parms *cmd, void *mconfig, const char *zoom_level_string, const char *cache_duration_string)
801{
802    int zoom_level;
803    int cache_duration;
804    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
805    if (sscanf(zoom_level_string, "%d", &zoom_level) != 1) {
806            return "ModTileCacheDurationLowZoom needs integer argument";
807    }
808    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
809            return "ModTileCacheDurationLowZoom needs integer argument";
810    }
811    scfg->cache_level_low_zoom = zoom_level;
812    scfg->cache_duration_low_zoom = cache_duration;
813
814    return NULL;
815}
816static const char *mod_tile_cache_duration_medium_config(cmd_parms *cmd, void *mconfig, const char *zoom_level_string, const char *cache_duration_string)
817{
818    int zoom_level;
819    int cache_duration;
820    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
821    if (sscanf(zoom_level_string, "%d", &zoom_level) != 1) {
822            return "ModTileCacheDurationMediumZoom needs integer argument";
823    }
824    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
825            return "ModTileCacheDurationMediumZoom needs integer argument";
826    }
827    scfg->cache_level_medium_zoom = zoom_level;
828    scfg->cache_duration_medium_zoom = cache_duration;
829
830    return NULL;
831}
832
833
834static void *create_tile_config(apr_pool_t *p, server_rec *s)
835{
836    tile_server_conf * scfg = (tile_server_conf *) apr_pcalloc(p, sizeof(tile_server_conf));
837
838    scfg->configs = apr_array_make(p, 4, sizeof(tile_config_rec));
839    scfg->request_timeout = REQUEST_TIMEOUT;
840    scfg->max_load_old = MAX_LOAD_OLD;
841    scfg->max_load_missing = MAX_LOAD_MISSING;
842    strncpy(scfg->renderd_socket_name, RENDER_SOCKET, PATH_MAX-1);
843    scfg->renderd_socket_name[PATH_MAX-1] = 0;
844    strncpy(scfg->tile_dir, HASH_PATH, PATH_MAX-1);
845    scfg->tile_dir[PATH_MAX-1] = 0;
846    scfg->cache_duration_dirty = 15*60;
847    scfg->cache_duration_last_modified_factor = 0.0;
848    scfg->cache_duration_max = 7*24*60*60;
849    scfg->cache_duration_minimum = 3*60*60;
850    scfg->cache_duration_low_zoom = 6*24*60*60;
851    scfg->cache_duration_medium_zoom = 1*24*60*60;
852    scfg->cache_level_low_zoom = 0;
853    scfg->cache_level_medium_zoom = 0;
854
855    return scfg;
856}
857
858static void *merge_tile_config(apr_pool_t *p, void *basev, void *overridesv)
859{
860    int i;
861    tile_server_conf * scfg = (tile_server_conf *) apr_pcalloc(p, sizeof(tile_server_conf));
862    tile_server_conf * scfg_base = (tile_server_conf *) basev;
863    tile_server_conf * scfg_over = (tile_server_conf *) overridesv;
864
865    scfg->configs = apr_array_append(p, scfg_base->configs, scfg_over->configs);
866    scfg->request_timeout = scfg_over->request_timeout;
867    scfg->max_load_old = scfg_over->max_load_old;
868    scfg->max_load_missing = scfg_over->max_load_missing;
869    strncpy(scfg->renderd_socket_name, scfg_over->renderd_socket_name, PATH_MAX-1);
870    scfg->renderd_socket_name[PATH_MAX-1] = 0;
871    strncpy(scfg->tile_dir, scfg_over->tile_dir, PATH_MAX-1);
872    scfg->tile_dir[PATH_MAX-1] = 0;
873    scfg->cache_duration_dirty = scfg_over->cache_duration_dirty;
874    scfg->cache_duration_last_modified_factor = scfg_over->cache_duration_last_modified_factor;
875    scfg->cache_duration_max = scfg_over->cache_duration_max;
876    scfg->cache_duration_minimum = scfg_over->cache_duration_minimum;
877    scfg->cache_duration_low_zoom = scfg_over->cache_duration_low_zoom;
878    scfg->cache_duration_medium_zoom = scfg_over->cache_duration_medium_zoom;
879    scfg->cache_level_low_zoom = scfg_over->cache_level_low_zoom;
880    scfg->cache_level_medium_zoom = scfg_over->cache_level_medium_zoom;
881
882    //Construct a table of minimum cache times per zoom level
883    for (i = 0; i <= MAX_ZOOM; i++) {
884        if (i <= scfg->cache_level_low_zoom) {
885            scfg->mincachetime[i] = scfg->cache_duration_low_zoom;
886        } else if (i <= scfg->cache_level_medium_zoom) {
887            scfg->mincachetime[i] = scfg->cache_duration_medium_zoom;
888        } else {
889            scfg->mincachetime[i] = scfg->cache_duration_minimum;
890        }
891    }
892
893    return scfg;
894}
895
896static const command_rec tile_cmds[] =
897{
898    AP_INIT_TAKE1(
899        "LoadTileConfigFile",            /* directive name */
900        load_tile_config,                /* config action routine */
901        NULL,                            /* argument to include in call */
902        OR_OPTIONS,                      /* where available */
903        "load an entire renderd config file"  /* directive description */
904    ),
905    AP_INIT_TAKE2(
906        "AddTileConfig",                 /* directive name */
907        add_tile_config,                 /* config action routine */
908        NULL,                            /* argument to include in call */
909        OR_OPTIONS,                      /* where available */
910        "path and name of renderd config to use"  /* directive description */
911    ),
912    AP_INIT_TAKE1(
913        "ModTileRequestTimeout",         /* directive name */
914        mod_tile_request_timeout_config, /* config action routine */
915        NULL,                            /* argument to include in call */
916        OR_OPTIONS,                      /* where available */
917        "Set timeout in seconds on mod_tile requests"  /* directive description */
918    ),
919    AP_INIT_TAKE1(
920        "ModTileMaxLoadOld",             /* directive name */
921        mod_tile_max_load_old_config,    /* config action routine */
922        NULL,                            /* argument to include in call */
923        OR_OPTIONS,                      /* where available */
924        "Set max load for rendering old tiles"  /* directive description */
925    ),
926    AP_INIT_TAKE1(
927        "ModTileMaxLoadMissing",         /* directive name */
928        mod_tile_max_load_missing_config, /* config action routine */
929        NULL,                            /* argument to include in call */
930        OR_OPTIONS,                      /* where available */
931        "Set max load for rendering missing tiles"  /* directive description */
932    ),
933    AP_INIT_TAKE1(
934        "ModTileRenderdSocketName",      /* directive name */
935        mod_tile_renderd_socket_name_config, /* config action routine */
936        NULL,                            /* argument to include in call */
937        OR_OPTIONS,                      /* where available */
938        "Set name of unix domain socket for connecting to rendering daemon"  /* directive description */
939    ),
940    AP_INIT_TAKE1(
941        "ModTileTileDir",                /* directive name */
942        mod_tile_tile_dir_config,        /* config action routine */
943        NULL,                            /* argument to include in call */
944        OR_OPTIONS,                      /* where available */
945        "Set name of tile cache directory"  /* directive description */
946    ),
947    AP_INIT_TAKE1(
948        "ModTileCacheDurationMax",                /* directive name */
949        mod_tile_cache_duration_max_config,        /* config action routine */
950        NULL,                            /* argument to include in call */
951        OR_OPTIONS,                      /* where available */
952        "Set the maximum cache expiry in seconds"  /* directive description */
953    ),
954    AP_INIT_TAKE1(
955        "ModTileCacheDurationDirty",                    /* directive name */
956        mod_tile_cache_duration_dirty_config,           /* config action routine */
957        NULL,                                           /* argument to include in call */
958        OR_OPTIONS,                                     /* where available */
959        "Set the cache expiry for serving dirty tiles"  /* directive description */
960    ),
961    AP_INIT_TAKE1(
962        "ModTileCacheDurationMinimum",          /* directive name */
963        mod_tile_cache_duration_minimum_config, /* config action routine */
964        NULL,                                   /* argument to include in call */
965        OR_OPTIONS,                             /* where available */
966        "Set the minimum cache expiry"          /* directive description */
967    ),
968    AP_INIT_TAKE1(
969        "ModTileCacheLastModifiedFactor",       /* directive name */
970        mod_tile_cache_lastmod_factor_config,   /* config action routine */
971        NULL,                                   /* argument to include in call */
972        OR_OPTIONS,                             /* where available */
973        "Set the factor by which the last modified determins cache expiry" /* directive description */
974    ),
975    AP_INIT_TAKE2(
976        "ModTileCacheDurationLowZoom",       /* directive name */
977        mod_tile_cache_duration_low_config,                 /* config action routine */
978        NULL,                            /* argument to include in call */
979        OR_OPTIONS,                      /* where available */
980        "Set the minimum cache duration and zoom level for low zoom tiles"  /* directive description */
981    ),
982    AP_INIT_TAKE2(
983        "ModTileCacheDurationMediumZoom",       /* directive name */
984        mod_tile_cache_duration_medium_config,                 /* config action routine */
985        NULL,                            /* argument to include in call */
986        OR_OPTIONS,                      /* where available */
987        "Set the minimum cache duration and zoom level for medium zoom tiles"  /* directive description */
988    ),
989    {NULL}
990};
991
992module AP_MODULE_DECLARE_DATA tile_module =
993{
994    STANDARD20_MODULE_STUFF,
995    NULL,                                /* dir config creater */
996    NULL,                                /* dir merger --- default is to override */
997    create_tile_config,                  /* server config */
998    merge_tile_config,                   /* merge server config */
999    tile_cmds,                           /* command apr_table_t */
1000    register_hooks                       /* register hooks */
1001};
1002
Note: See TracBrowser for help on using the repository browser.