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

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

another step towards making tile directory configurable at runtime (still some places left that use hardcoded HASH_PATH)

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