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

Last change on this file since 16255 was 15908, checked in by apmon, 11 years ago

[mod_tile] remove unnecessary code from my previous commit, noticed by jonb

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 = minzoom; i < 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.