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

Last change on this file since 15860 was 15860, checked in by jochen, 10 years ago

typo fixes

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