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

Last change on this file since 26347 was 26347, checked in by apmon, 8 years ago

Compile fix my previous checkin

File size: 61.2 KB
Line 
1#include "apr.h"
2#include "apr_strings.h"
3#include "apr_thread_proc.h"    /* for RLIMIT stuff */
4#include "apr_optional.h"
5#include "apr_buckets.h"
6#include "apr_lib.h"
7#include "apr_poll.h"
8
9#define APR_WANT_STRFUNC
10#define APR_WANT_MEMFUNC
11#include "apr_want.h"
12
13#include "util_filter.h"
14#include "ap_config.h"
15#include "httpd.h"
16#include "http_config.h"
17#include "http_request.h"
18#include "http_core.h"
19#include "http_protocol.h"
20#include "http_main.h"
21#include "http_log.h"
22#include "util_script.h"
23#include "ap_mpm.h"
24#include "mod_core.h"
25#include "mod_cgi.h"
26#include "util_md5.h"
27
28module AP_MODULE_DECLARE_DATA tile_module;
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <stdarg.h>
34#include <sys/types.h>
35#include <sys/socket.h>
36#include <sys/un.h>
37#include <unistd.h>
38#include <fcntl.h>
39#include <errno.h>
40#include <limits.h>
41#include <time.h>
42#include <netinet/in.h>
43#include <arpa/inet.h>
44
45
46#include "gen_tile.h"
47#include "protocol.h"
48#include "render_config.h"
49#include "store.h"
50#include "dir_utils.h"
51#include "mod_tile.h"
52
53
54
55#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
56#include "unixd.h"
57#define MOD_TILE_SET_MUTEX_PERMS /* XXX Apache should define something */
58#endif
59
60
61
62apr_shm_t *stats_shm;
63apr_shm_t *delaypool_shm;
64char *shmfilename;
65char *shmfilename_delaypool;
66apr_global_mutex_t *stats_mutex;
67apr_global_mutex_t *delay_mutex;
68char *mutexfilename;
69
70static int error_message(request_rec *r, const char *format, ...)
71                 __attribute__ ((format (printf, 2, 3)));
72
73static int error_message(request_rec *r, const char *format, ...)
74{
75    va_list ap;
76    va_start(ap, format);
77    int len;
78    char *msg;
79
80    len = vasprintf(&msg, format, ap);
81
82    if (msg) {
83        //ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "%s", msg);
84        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "%s", msg);
85        r->content_type = "text/plain";
86        if (!r->header_only)
87            ap_rputs(msg, r);
88        free(msg);
89    }
90
91    return OK;
92}
93
94int socket_init(request_rec *r)
95{
96    ap_conf_vector_t *sconf = r->server->module_config;
97    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
98
99    int fd;
100    struct sockaddr_un addr;
101
102    fd = socket(PF_UNIX, SOCK_STREAM, 0);
103    if (fd < 0) {
104        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "failed to create unix socket");
105        return FD_INVALID;
106    }
107
108    bzero(&addr, sizeof(addr));
109    addr.sun_family = AF_UNIX;
110    strncpy(addr.sun_path, scfg->renderd_socket_name, sizeof(addr.sun_path));
111
112    if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
113        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "socket connect failed for: %s", scfg->renderd_socket_name);
114        close(fd);
115        return FD_INVALID;
116    }
117    return fd;
118}
119
120int request_tile(request_rec *r, struct protocol *cmd, int renderImmediately)
121{
122    int fd;
123    int ret = 0;
124    int retry = 1;
125    struct protocol resp;
126
127    ap_conf_vector_t *sconf = r->server->module_config;
128    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
129
130    fd = socket_init(r);
131
132    if (fd == FD_INVALID) {
133        ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, "Failed to connect to renderer");
134        return 0;
135    }
136
137    // cmd has already been partial filled, fill in the rest
138    cmd->ver = PROTO_VER;
139    switch (renderImmediately) {
140    case 0: { cmd->cmd = cmdDirty; break;}
141    case 1: { cmd->cmd = cmdRender; break;}
142    case 2: { cmd->cmd = cmdRenderPrio; break;}
143    }
144
145    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Requesting style(%s) z(%d) x(%d) y(%d) from renderer with priority %d", cmd->xmlname, cmd->z, cmd->x, cmd->y, cmd->cmd);
146    do {
147        ret = send(fd, cmd, sizeof(struct protocol), 0);
148
149        if (ret == sizeof(struct protocol))
150            break;
151
152        close(fd);
153        if (errno != EPIPE)
154            return 0;
155
156        fd = socket_init(r);
157        if (fd == FD_INVALID)
158            return 0;
159    } while (retry--);
160
161    if (renderImmediately) {
162        struct timeval tv = {(renderImmediately > 1?scfg->request_timeout_priority:scfg->request_timeout), 0 };
163        fd_set rx;
164        int s;
165
166        while (1) {
167            FD_ZERO(&rx);
168            FD_SET(fd, &rx);
169            s = select(fd+1, &rx, NULL, NULL, &tv);
170            if (s == 1) {
171                bzero(&resp, sizeof(struct protocol));
172                ret = recv(fd, &resp, sizeof(struct protocol), 0);
173                if (ret != sizeof(struct protocol)) {
174                    //perror("recv error");
175                    break;
176                }
177
178                if (cmd->x == resp.x && cmd->y == resp.y && cmd->z == resp.z && !strcmp(cmd->xmlname, resp.xmlname)) {
179                    close(fd);
180                    if (resp.cmd == cmdDone)
181                        return 1;
182                    else
183                        return 0;
184                } else {
185                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
186                       "Response does not match request: xml(%s,%s) z(%d,%d) x(%d,%d) y(%d,%d)", cmd->xmlname,
187                       resp.xmlname, cmd->z, resp.z, cmd->x, resp.x, cmd->y, resp.y);
188                }
189            } else {
190                break;
191            }
192        }
193    }
194
195    close(fd);
196    return 0;
197}
198
199static apr_time_t getPlanetTime(request_rec *r)
200{
201    static apr_time_t last_check;
202    static apr_time_t planet_timestamp;
203    static pthread_mutex_t planet_lock = PTHREAD_MUTEX_INITIALIZER;
204    apr_time_t now = r->request_time;
205    struct apr_finfo_t s;
206
207    pthread_mutex_lock(&planet_lock);
208    // Only check for updates periodically
209    if (now < last_check + apr_time_from_sec(300)) {
210        pthread_mutex_unlock(&planet_lock);
211        return planet_timestamp;
212    }
213
214    ap_conf_vector_t *sconf = r->server->module_config;
215    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
216    char filename[PATH_MAX];
217    snprintf(filename, PATH_MAX-1, "%s/%s", scfg->tile_dir, PLANET_TIMESTAMP);
218
219    last_check = now;
220    if (apr_stat(&s, filename, APR_FINFO_MIN, r->pool) != APR_SUCCESS) {
221        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Planet timestamp file (%s) is missing", filename);
222        // Make something up
223        planet_timestamp = now - apr_time_from_sec(3 * 24 * 60 * 60);
224    } else {
225        if (s.mtime != planet_timestamp) {
226            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Planet file updated");
227            planet_timestamp = s.mtime;
228        }
229    }
230    pthread_mutex_unlock(&planet_lock);
231    return planet_timestamp;
232}
233
234static enum tileState tile_state_once(request_rec *r)
235{
236    apr_status_t rv;
237    apr_finfo_t *finfo = &r->finfo;
238
239    if (!(finfo->valid & APR_FINFO_MTIME)) {
240        rv = apr_stat(finfo, r->filename, APR_FINFO_MIN, r->pool);
241        if (rv != APR_SUCCESS)
242            return tileMissing;
243    }
244
245    if (finfo->mtime < getPlanetTime(r))
246        return tileOld;
247
248    return tileCurrent;
249}
250
251static enum tileState tile_state(request_rec *r, struct protocol *cmd)
252{
253    enum tileState state = tile_state_once(r);
254#ifdef METATILEFALLBACK
255    if (state == tileMissing) {
256        ap_conf_vector_t *sconf = r->server->module_config;
257        tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
258
259        // Try fallback to plain PNG
260        char path[PATH_MAX];
261        xyz_to_path(path, sizeof(path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
262        r->filename = apr_pstrdup(r->pool, path);
263        state = tile_state_once(r);
264        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "png fallback %d/%d/%d",x,y,z);
265
266        if (state == tileMissing) {
267            // PNG not available either, if it gets rendered, it'll now be a .meta
268            xyz_to_meta(path, sizeof(path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
269            r->filename = apr_pstrdup(r->pool, path);
270        }
271    }
272#endif
273    return state;
274}
275
276static void add_expiry(request_rec *r, struct protocol * cmd)
277{
278    apr_time_t holdoff;
279    apr_table_t *t = r->headers_out;
280    enum tileState state = tile_state(r, cmd);
281    apr_finfo_t *finfo = &r->finfo;
282    char *timestr;
283    long int planetTimestamp, maxAge, minCache, lastModified;
284
285    ap_conf_vector_t *sconf = r->server->module_config;
286    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
287
288    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "expires(%s), uri(%s), filename(%s), path_info(%s)\n",
289                  r->handler, r->uri, r->filename, r->path_info);
290
291    /* If the hostname matches the "extended caching hostname" then set the cache age accordingly */
292    if ((scfg->cache_extended_hostname[0] != 0) && (strstr(r->hostname,
293            scfg->cache_extended_hostname) != NULL)) {
294        maxAge = scfg->cache_extended_duration;
295    } else {
296
297        /* Test if the tile we are serving is out of date, then set a low maxAge*/
298        if (state == tileOld) {
299            holdoff = (scfg->cache_duration_dirty / 2) * (rand() / (RAND_MAX
300                    + 1.0));
301            maxAge = scfg->cache_duration_dirty + holdoff;
302        } else {
303            // cache heuristic based on zoom level
304            if (cmd->z > MAX_ZOOM) {
305                minCache = 0;
306                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
307                        "z (%i) is larger than MAXZOOM %i\n", cmd->z, MAX_ZOOM);
308            } else {
309                minCache = scfg->mincachetime[cmd->z];
310            }
311            // Time to the next known complete rerender
312            planetTimestamp = apr_time_sec(getPlanetTime(r)
313                    + apr_time_from_sec(PLANET_INTERVAL) - r->request_time);
314            // Time since the last render of this tile
315            lastModified = (int) (((double) apr_time_sec(r->request_time
316                    - finfo->mtime))
317                    * scfg->cache_duration_last_modified_factor);
318            // Add a random jitter of 3 hours to space out cache expiry
319            holdoff = (3 * 60 * 60) * (rand() / (RAND_MAX + 1.0));
320
321            maxAge = MAX(minCache, planetTimestamp);
322            maxAge = MAX(maxAge, lastModified);
323            maxAge += holdoff;
324
325            ap_log_rerror(
326                    APLOG_MARK,
327                    APLOG_DEBUG,
328                    0,
329                    r,
330                    "caching heuristics: next planet render %ld; zoom level based %ld; last modified %ld\n",
331                    planetTimestamp, minCache, lastModified);
332        }
333
334        maxAge = MIN(maxAge, scfg->cache_duration_max);
335    }
336
337    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Setting tiles maxAge to %ld\n", maxAge);
338
339    apr_table_mergen(t, "Cache-Control",
340                     apr_psprintf(r->pool, "max-age=%" APR_TIME_T_FMT,
341                     maxAge));
342    timestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
343    apr_rfc822_date(timestr, (apr_time_from_sec(maxAge) + r->request_time));
344    apr_table_setn(t, "Expires", timestr);
345}
346
347double get_load_avg(request_rec *r)
348{
349    double loadavg[1];
350    int n = getloadavg(loadavg, 1);
351
352    if (n < 1)
353        return 1000;
354    else
355        return loadavg[0];
356}
357
358static int get_global_lock(request_rec *r, apr_global_mutex_t * mutex) {
359    apr_status_t rs;
360    int camped;
361
362    for (camped = 0; camped < MAXCAMP; camped++) {
363        rs = apr_global_mutex_trylock(mutex);
364        if (APR_STATUS_IS_EBUSY(rs)) {
365            apr_sleep(CAMPOUT);
366        } else if (rs == APR_SUCCESS) {
367            return 1;
368        } else if (APR_STATUS_IS_ENOTIMPL(rs)) {
369            /* If it's not implemented, just hang in the mutex. */
370            rs = apr_global_mutex_lock(mutex);
371            if (rs == APR_SUCCESS) {
372                return 1;
373            } else {
374                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Could not get hardlock");
375                return 0;
376            }
377        } else {
378            ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, "Unknown return status from trylock");
379            return 0;
380        }
381    }
382    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Timedout trylock");
383    return 0;
384}
385
386static int incRespCounter(int resp, request_rec *r, struct protocol * cmd) {
387    stats_data *stats;
388
389    ap_conf_vector_t *sconf = r->server->module_config;
390    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
391
392    if (!scfg->enableGlobalStats) {
393        /* If tile stats reporting is not enable
394         * pretend we correctly updated the counter to
395         * not fill the logs with warnings about failed
396         * stats
397         */
398        return 1;
399    }
400
401    if (get_global_lock(r, stats_mutex) != 0) {
402        stats = (stats_data *)apr_shm_baseaddr_get(stats_shm);
403        switch (resp) {
404        case OK: {
405            stats->noResp200++;
406                        if (cmd != NULL) {
407                                stats->noRespZoom[cmd->z]++;
408                        }
409            break;
410        }
411        case HTTP_NOT_MODIFIED: {
412            stats->noResp304++;
413                        if (cmd != NULL) {
414                                stats->noRespZoom[cmd->z]++;
415                        }
416            break;
417        }
418        case HTTP_NOT_FOUND: {
419            stats->noResp404++;
420            break;
421        }
422                case HTTP_SERVICE_UNAVAILABLE: {
423            stats->noResp503++;
424            break;
425        }
426        case HTTP_INTERNAL_SERVER_ERROR: {
427            stats->noResp5XX++;
428            break;
429        }
430        default: {
431            stats->noRespOther++;
432        }
433
434        }
435        apr_global_mutex_unlock(stats_mutex);
436        /* Swallowing the result because what are we going to do with it at
437         * this stage?
438         */
439        return 1;
440    } else {
441        return 0;
442    }
443}
444
445static int incFreshCounter(int status, request_rec *r) {
446    stats_data *stats;
447
448    ap_conf_vector_t *sconf = r->server->module_config;
449    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
450
451    if (!scfg->enableGlobalStats) {
452        /* If tile stats reporting is not enable
453         * pretend we correctly updated the counter to
454         * not fill the logs with warnings about failed
455         * stats
456         */
457        return 1;
458    }
459
460    if (get_global_lock(r, stats_mutex) != 0) {
461        stats = (stats_data *)apr_shm_baseaddr_get(stats_shm);
462        switch (status) {
463        case FRESH: {
464            stats->noFreshCache++;
465            break;
466        }
467        case FRESH_RENDER: {
468            stats->noFreshRender++;
469            break;
470        }
471        case OLD: {
472            stats->noOldCache++;
473            break;
474        }
475        case OLD_RENDER: {
476            stats->noOldRender++;
477            break;
478        }
479        }
480        apr_global_mutex_unlock(stats_mutex);
481        /* Swallowing the result because what are we going to do with it at
482         * this stage?
483         */
484        return 1;
485    } else {
486        return 0;
487    }
488}
489
490static int delay_allowed(request_rec *r, enum tileState state) {
491        delaypool * delayp;
492        int delay = 0;
493        int i,j;
494
495    ap_conf_vector_t *sconf = r->server->module_config;
496        tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
497        delayp = (delaypool *)apr_shm_baseaddr_get(delaypool_shm);
498
499        /* TODO: fix IPv6 compatibility */
500        in_addr_t ip = inet_addr(r->connection->remote_ip);
501       
502        int hashkey = ip % DELAY_HASHTABLE_WHITELIST_SIZE;
503        if (delayp->whitelist[hashkey] == ip) {
504                return 1;
505        }
506
507        /* If a delaypool fillup is ongoing, just skip accounting to not block on a lock */
508        if (delayp->locked) {
509                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "skipping delay pool accounting, during fillup procedure\n");
510                return 1;
511        }
512
513       
514        hashkey = ip % DELAY_HASHTABLE_SIZE;
515       
516        if (get_global_lock(r,delay_mutex) == 0) {
517                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Could not acquire lock, skipping delay pool accounting\n");
518                return 1;
519        };
520        if (delayp->users[hashkey].ip_addr == ip) {
521                /* Repeat the process to determin if we have tockens in the bucket, as the fillup only runs once a client hits an empty bucket,
522                   so in the mean time, the bucket might have been filled */
523                for (j = 0; j < 3; j++) {
524                        //ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Checking delays: Current poolsize: %i tiles and %i renders\n", delayp->users[hashkey].available_tiles, delayp->users[hashkey].available_render_req);
525                        delay = 0;
526                        if (delayp->users[hashkey].available_tiles > 0) {
527                                delayp->users[hashkey].available_tiles--;
528                        } else {
529                                delay = 1;
530                        }
531                        if (state == tileMissing) {
532                                if (delayp->users[hashkey].available_render_req > 0) {
533                                        delayp->users[hashkey].available_render_req--;
534                                } else {
535                                        delay = 2;
536                                }
537                        }
538
539                        if (delay > 0) {
540                                /* If we are on the second round, we really  hit an empty delaypool, timeout for a while to slow down clients */
541                                if (j > 0) {
542                                        apr_global_mutex_unlock(delay_mutex);
543                                        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Delaypool: Client %s has hit its limits, throttling (%i)\n", r->connection->remote_ip, delay);
544                                        sleep(CLIENT_PENALTY);
545                                        if (get_global_lock(r,delay_mutex) == 0) {
546                                                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Could not acquire lock, but had to delay\n");
547                                                return 0;
548                                        };
549                                }
550                                /* We hit an empty bucket, so run the bucket fillup procedure to check if new tokens should have arrived in the mean time. */
551                                apr_time_t now = apr_time_now();
552                                int tiles_topup = (now - delayp->last_tile_fillup) / scfg->delaypoolTileRate;
553                                int render_topup = (now - delayp->last_render_fillup) / scfg->delaypoolRenderRate;
554                                //ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Filling up pools with %i tiles and %i renders\n", tiles_topup, render_topup);
555                                if ((tiles_topup > 0) || (render_topup > 0)) {
556                                        delayp->locked = 1;
557                                        for (i = 0; i < DELAY_HASHTABLE_SIZE; i++) {
558                                                delayp->users[i].available_tiles += tiles_topup;
559                                                delayp->users[i].available_render_req += render_topup;
560                                                if (delayp->users[i].available_tiles > scfg->delaypoolTileSize) {
561                                                        delayp->users[i].available_tiles = scfg->delaypoolTileSize;
562                                                }
563                                                if (delayp->users[i].available_render_req > scfg->delaypoolRenderSize) {
564                                                        delayp->users[i].available_render_req = scfg->delaypoolRenderSize;
565                                                }
566                                        }
567                                        delayp->locked = 0;
568                                }
569                                delayp->last_tile_fillup += scfg->delaypoolTileRate*tiles_topup;
570                                delayp->last_render_fillup += scfg->delaypoolRenderRate*render_topup;                           
571                               
572                        } else {
573                                break;
574                        }
575                }
576        } else {
577                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Creating a new delaypool for ip %s, overwriting %s\n", r->connection->remote_ip, inet_ntoa(inet_makeaddr(delayp->users[hashkey].ip_addr,delayp->users[hashkey].ip_addr)));
578                delayp->users[hashkey].ip_addr = ip;
579                delayp->users[hashkey].available_tiles = scfg->delaypoolTileSize;
580            delayp->users[hashkey].available_render_req = scfg->delaypoolRenderSize;
581                delay = 0;
582        }
583        apr_global_mutex_unlock(delay_mutex);
584
585        if (delay > 0) {
586                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Delaypool: Client %s has hit its limits, rejecting (%i)\n", r->connection->remote_ip, delay);
587                return 0;
588        } else {
589                return 1;
590        }
591}
592
593static int tile_handler_dirty(request_rec *r)
594{
595    if(strcmp(r->handler, "tile_dirty"))
596        return DECLINED;
597
598    struct protocol * cmd = (struct protocol *)ap_get_module_config(r->request_config, &tile_module);
599    if (cmd == NULL)
600        return DECLINED;
601
602    request_tile(r, cmd, 0);
603    return error_message(r, "Tile submitted for rendering\n");
604}
605
606static int tile_storage_hook(request_rec *r)
607{
608//    char abs_path[PATH_MAX];
609    int avg;
610    int renderPrio = 0;
611    enum tileState state;
612
613    if (!r->handler)
614        return DECLINED;
615
616        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "tile_storage_hook: handler(%s), uri(%s), filename(%s), path_info(%s)",
617                  r->handler, r->uri, r->filename, r->path_info);
618
619    // Any status request is OK. tile_dirty also doesn't need to be handled, as tile_handler_dirty will take care of it
620    if (!strcmp(r->handler, "tile_status") || !strcmp(r->handler, "tile_dirty") || !strcmp(r->handler, "tile_mod_stats"))
621        return OK;
622
623    if (strcmp(r->handler, "tile_serve"))
624        return DECLINED;
625
626    struct protocol * cmd = (struct protocol *)ap_get_module_config(r->request_config, &tile_module);
627    if (cmd == NULL)
628        return DECLINED;
629
630/*
631should already be done
632    // Generate the tile filename
633#ifdef METATILE
634    ap_conf_vector_t *sconf = r->server->module_config;
635    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
636    xyz_to_meta(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
637#else
638    xyz_to_path(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
639#endif
640    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "abs_path(%s)", abs_path);
641    r->filename = apr_pstrdup(r->pool, abs_path);
642*/
643    avg = get_load_avg(r);
644    state = tile_state(r, cmd);
645
646    ap_conf_vector_t *sconf = r->server->module_config;
647    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
648
649        if (scfg->enableTileThrottling && !delay_allowed(r, state)) {
650                if (!incRespCounter(HTTP_SERVICE_UNAVAILABLE, r, cmd)) {
651                   ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
652                        "Failed to increase response stats counter");
653        }
654        return HTTP_SERVICE_UNAVAILABLE;               
655        }
656
657    switch (state) {
658        case tileCurrent:
659            if (!incFreshCounter(FRESH, r)) {
660                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
661                    "Failed to increase fresh stats counter");
662            }
663            return OK;
664            break;
665        case tileOld:
666            if (avg > scfg->max_load_old) {
667               // Too much load to render it now, mark dirty but return old tile
668               request_tile(r, cmd, 0);
669               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);
670               if (!incFreshCounter(OLD, r)) {
671                   ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
672                        "Failed to increase fresh stats counter");
673               }
674               return OK;
675            }
676            renderPrio = 1;
677            break;
678        case tileMissing:
679            if (avg > scfg->max_load_missing) {
680               request_tile(r, cmd, 0);
681               ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Load larger max_load_missing (%d). Return HTTP_NOT_FOUND.", scfg->max_load_missing);
682               if (!incRespCounter(HTTP_NOT_FOUND, r, cmd)) {
683                   ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
684                        "Failed to increase response stats counter");
685               }
686               return HTTP_NOT_FOUND;
687            }
688            renderPrio = 2;
689            break;
690    }
691
692    if (request_tile(r, cmd, renderPrio)) {
693        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Update file info abs_path(%s)", r->filename);
694        // Need to update fileinfo for new rendered tile
695        apr_stat(&r->finfo, r->filename, APR_FINFO_MIN, r->pool);
696        if (!incFreshCounter(FRESH_RENDER, r)) {
697            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
698                    "Failed to increase fresh stats counter");
699        }
700        return OK;
701    }
702
703    if (state == tileOld) {
704        if (!incFreshCounter(OLD_RENDER, r)) {
705            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
706                    "Failed to increase fresh stats counter");
707        }
708        return OK;
709    }
710    if (!incRespCounter(HTTP_NOT_FOUND, r, cmd)) {
711        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
712                "Failed to increase response stats counter");
713    }
714
715    return HTTP_NOT_FOUND;
716}
717
718static int tile_handler_status(request_rec *r)
719{
720    enum tileState state;
721    char time_str[APR_CTIME_LEN];
722
723    if(strcmp(r->handler, "tile_status"))
724        return DECLINED;
725
726    struct protocol * cmd = (struct protocol *)ap_get_module_config(r->request_config, &tile_module);
727    if (cmd == NULL){
728        sleep(CLIENT_PENALTY);
729        return HTTP_NOT_FOUND;
730    }
731
732    state = tile_state(r, cmd);
733    if (state == tileMissing)
734        return error_message(r, "Unable to find a tile at %s\n", r->filename);
735    apr_ctime(time_str, r->finfo.mtime);
736
737    return error_message(r, "Tile is %s. Last rendered at %s\n", (state == tileOld) ? "due to be rendered" : "clean", time_str);
738}
739
740static int tile_handler_mod_stats(request_rec *r)
741{
742    stats_data * stats;
743    stats_data local_stats;
744        int i;
745
746    if (strcmp(r->handler, "tile_mod_stats"))
747        return DECLINED;
748
749    ap_conf_vector_t *sconf = r->server->module_config;
750    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
751
752    if (!scfg->enableGlobalStats) {
753        return error_message(r, "Stats are not enabled for this server");
754    }
755
756    if (get_global_lock(r,stats_mutex) != 0) {
757        //Copy over the global counter variable into
758        //local variables, that we can immediately
759        //release the lock again
760        stats = (stats_data *) apr_shm_baseaddr_get(stats_shm);
761        memcpy(&local_stats, stats, sizeof(stats_data));
762        apr_global_mutex_unlock(stats_mutex);
763    } else {
764        return error_message(r, "Failed to acquire lock, can't display stats");
765    }
766
767    ap_rprintf(r, "NoResp200: %li\n", local_stats.noResp200);
768    ap_rprintf(r, "NoResp304: %li\n", local_stats.noResp304);
769    ap_rprintf(r, "NoResp404: %li\n", local_stats.noResp404);
770        ap_rprintf(r, "NoResp503: %li\n", local_stats.noResp503);
771    ap_rprintf(r, "NoResp5XX: %li\n", local_stats.noResp5XX);
772    ap_rprintf(r, "NoRespOther: %li\n", local_stats.noRespOther);
773    ap_rprintf(r, "NoFreshCache: %li\n", local_stats.noFreshCache);
774    ap_rprintf(r, "NoOldCache: %li\n", local_stats.noOldCache);
775    ap_rprintf(r, "NoFreshRender: %li\n", local_stats.noFreshRender);
776    ap_rprintf(r, "NoOldRender: %li\n", local_stats.noOldRender);
777        for (i = 0; i <= MAX_ZOOM; i++) {
778                ap_rprintf(r, "NoRespZoom%02i: %li\n", i, local_stats.noRespZoom[i]);
779        }
780
781
782
783    return OK;
784}
785
786static int tile_handler_serve(request_rec *r)
787{
788    const int tile_max = MAX_SIZE;
789    unsigned char *buf;
790    int len;
791    apr_status_t errstatus;
792
793    if(strcmp(r->handler, "tile_serve"))
794        return DECLINED;
795
796    struct protocol * cmd = (struct protocol *)ap_get_module_config(r->request_config, &tile_module);
797    if (cmd == NULL){
798        sleep(CLIENT_PENALTY);
799        if (!incRespCounter(HTTP_NOT_FOUND, r, cmd)) {
800            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
801                    "Failed to increase response stats counter");
802        }
803        return HTTP_NOT_FOUND;
804    }
805
806    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_handler_serve: xml(%s) z(%d) x(%d) y(%d)", cmd->xmlname, cmd->z, cmd->x, cmd->y);
807
808    // FIXME: It is a waste to do the malloc + read if we are fulfilling a HEAD or returning a 304.
809    buf = malloc(tile_max);
810    if (!buf) {
811        if (!incRespCounter(HTTP_INTERNAL_SERVER_ERROR, r, cmd)) {
812            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
813                    "Failed to increase response stats counter");
814        }
815        return HTTP_INTERNAL_SERVER_ERROR;
816    }
817
818    len = tile_read(cmd->xmlname, cmd->x, cmd->y, cmd->z, buf, tile_max);
819    if (len > 0) {
820#if 0
821        // Set default Last-Modified and Etag headers
822        ap_update_mtime(r, r->finfo.mtime);
823        ap_set_last_modified(r);
824        ap_set_etag(r);
825#else
826        // Use MD5 hash as only cache attribute.
827        // If a tile is re-rendered and produces the same output
828        // then we can continue to use the previous cached copy
829        char *md5 = ap_md5_binary(r->pool, buf, len);
830        apr_table_setn(r->headers_out, "ETag",
831                        apr_psprintf(r->pool, "\"%s\"", md5));
832#endif
833        ap_set_content_type(r, "image/png");
834        ap_set_content_length(r, len);
835        add_expiry(r, cmd);
836        if ((errstatus = ap_meets_conditions(r)) != OK) {
837            free(buf);
838            if (!incRespCounter(errstatus, r, cmd)) {
839                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
840                        "Failed to increase response stats counter");
841            }
842            return errstatus;
843        } else {
844            ap_rwrite(buf, len, r);
845            free(buf);
846            if (!incRespCounter(errstatus, r, cmd)) {
847                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
848                        "Failed to increase response stats counter");
849            }
850            return OK;
851        }
852    }
853    free(buf);
854    //ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "len = %d", len);
855    if (!incRespCounter(HTTP_NOT_FOUND, r, cmd)) {
856        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
857                "Failed to increase response stats counter");
858    }
859    return DECLINED;
860}
861
862static int tile_translate(request_rec *r)
863{
864    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: uri(%s)", r->uri);
865
866    int i,n,limit,oob;
867    char option[11];
868
869    ap_conf_vector_t *sconf = r->server->module_config;
870    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
871
872    tile_config_rec *tile_configs = (tile_config_rec *) scfg->configs->elts;
873
874    /*
875     * The page /mod_tile returns global stats about the number of tiles
876     * handled and in what state those tiles were.
877     * This should probably not be hard coded
878     */
879    if (!strncmp("/mod_tile", r->uri, strlen("/mod_tile"))) {
880        r->handler = "tile_mod_stats";
881        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
882                "tile_translate: retrieving global mod_tile stats");
883        return OK;
884    }
885
886    for (i = 0; i < scfg->configs->nelts; ++i) {
887        tile_config_rec *tile_config = &tile_configs[i];
888
889        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: testing baseuri(%s) name(%s)", tile_config->baseuri, tile_config->xmlname);
890
891        if (!strncmp(tile_config->baseuri, r->uri, strlen(tile_config->baseuri))) {
892
893            struct protocol * cmd = (struct protocol *) apr_pcalloc(r->pool, sizeof(struct protocol));
894            bzero(cmd, sizeof(struct protocol));
895
896            n = sscanf(r->uri+strlen(tile_config->baseuri), "%d/%d/%d.png/%10s", &(cmd->z), &(cmd->x), &(cmd->y), option);
897            if (n < 3) {
898                                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Invalid URL for tilelayer %s", tile_config->xmlname);
899                                return DECLINED;
900                        }
901
902            oob = (cmd->z < 0 || cmd->z > MAX_ZOOM);
903            if (!oob) {
904                 // valid x/y for tiles are 0 ... 2^zoom-1
905                 limit = (1 << cmd->z) - 1;
906                 oob =  (cmd->x < 0 || cmd->x > limit || cmd->y < 0 || cmd->y > limit);
907            }
908
909            if (oob) {
910                                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: request for %s was outside of allowed bounds", tile_config->xmlname);
911                sleep(CLIENT_PENALTY);
912                //Don't increase stats counter here,
913                //As we are interested in valid tiles only
914                return HTTP_NOT_FOUND;
915            }
916
917            strcpy(cmd->xmlname, tile_config->xmlname);
918
919            // Store a copy for later
920            ap_set_module_config(r->request_config, &tile_module, cmd);
921
922            // Generate the tile filename?
923            char abs_path[PATH_MAX];
924#ifdef METATILE
925            xyz_to_meta(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
926#else
927            xyz_to_path(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
928#endif
929            r->filename = apr_pstrdup(r->pool, abs_path);
930
931            if (n == 4) {
932                if (!strcmp(option, "status")) r->handler = "tile_status";
933                else if (!strcmp(option, "dirty")) r->handler = "tile_dirty";
934                else return DECLINED;
935            } else {
936                r->handler = "tile_serve";
937            }
938
939            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);
940
941            return OK;
942        }
943    }
944        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: No suitable tile layer found");
945    return DECLINED;
946}
947
948/*
949 * This routine is called in the parent, so we'll set up the shared
950 * memory segment and mutex here.
951 */
952
953static int mod_tile_post_config(apr_pool_t *pconf, apr_pool_t *plog,
954                             apr_pool_t *ptemp, server_rec *s)
955{
956    void *data; /* These two help ensure that we only init once. */
957    const char *userdata_key = "mod_tile_init_module";
958    apr_status_t rs;
959    stats_data *stats;
960        delaypool *delayp;
961        int i;
962
963
964    /*
965     * The following checks if this routine has been called before.
966     * This is necessary because the parent process gets initialized
967     * a couple of times as the server starts up, and we don't want
968     * to create any more mutexes and shared memory segments than
969     * we're actually going to use.
970     */
971    apr_pool_userdata_get(&data, userdata_key, s->process->pool);
972    if (!data) {
973        apr_pool_userdata_set((const void *) 1, userdata_key,
974                              apr_pool_cleanup_null, s->process->pool);
975        return OK;
976    } /* Kilroy was here */
977
978    /* Create the shared memory segment */
979
980    /*
981     * Create a unique filename using our pid. This information is
982     * stashed in the global variable so the children inherit it.
983     * TODO get the location from the environment $TMPDIR or somesuch.
984     */
985    shmfilename = apr_psprintf(pconf, "/tmp/httpd_shm.%ld", (long int)getpid());
986        shmfilename_delaypool = apr_psprintf(pconf, "/tmp/httpd_shm_delay.%ld", (long int)getpid());
987
988    /* Now create that segment */
989    rs = apr_shm_create(&stats_shm, sizeof(stats_data),
990                        (const char *) shmfilename, pconf);
991    if (rs != APR_SUCCESS) {
992        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
993                     "Failed to create shared memory segment on file %s",
994                     shmfilename);
995        return HTTP_INTERNAL_SERVER_ERROR;
996    }
997
998        rs = apr_shm_create(&delaypool_shm, sizeof(delaypool),
999                        (const char *) shmfilename_delaypool, pconf);
1000    if (rs != APR_SUCCESS) {
1001        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1002                     "Failed to create shared memory segment on file %s",
1003                     shmfilename_delaypool);
1004        return HTTP_INTERNAL_SERVER_ERROR;
1005    }
1006
1007    /* Created it, now let's zero it out */
1008    stats = (stats_data *)apr_shm_baseaddr_get(stats_shm);
1009    stats->noResp200 = 0;
1010    stats->noResp304 = 0;
1011    stats->noResp404 = 0;
1012        stats->noResp503 = 0;
1013    stats->noResp5XX = 0;
1014        for (i = 0; i <= MAX_ZOOM; i++) {
1015                stats->noRespZoom[i] = 0;
1016        }
1017    stats->noRespOther = 0;
1018    stats->noFreshCache = 0;
1019    stats->noFreshRender = 0;
1020    stats->noOldCache = 0;
1021    stats->noOldRender = 0;
1022
1023        delayp = (delaypool *)apr_shm_baseaddr_get(delaypool_shm);
1024       
1025        delayp->last_tile_fillup = apr_time_now();
1026        delayp->last_render_fillup = apr_time_now();
1027
1028        for (i = 0; i < DELAY_HASHTABLE_SIZE; i++) {
1029                delayp->users[i].ip_addr = (in_addr_t)0;
1030                delayp->users[i].available_tiles = 0;
1031                delayp->users[i].available_render_req = 0;
1032        }
1033        for (i = 0; i < DELAY_HASHTABLE_WHITELIST_SIZE; i++) {
1034                delayp->whitelist[i] = (in_addr_t)0;
1035        }
1036        /* TODO: need a way to initialise the delaypool whitelist */
1037
1038
1039    /* Create global mutex */
1040
1041    /*
1042     * Create another unique filename to lock upon. Note that
1043     * depending on OS and locking mechanism of choice, the file
1044     * may or may not be actually created.
1045     */
1046    mutexfilename = apr_psprintf(pconf, "/tmp/httpd_mutex.%ld",
1047                                 (long int) getpid());
1048
1049    rs = apr_global_mutex_create(&stats_mutex, (const char *) mutexfilename,
1050                                 APR_LOCK_DEFAULT, pconf);
1051    if (rs != APR_SUCCESS) {
1052        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1053                     "Failed to create mutex on file %s",
1054                     mutexfilename);
1055        return HTTP_INTERNAL_SERVER_ERROR;
1056    }
1057
1058#ifdef MOD_TILE_SET_MUTEX_PERMS
1059    rs = unixd_set_global_mutex_perms(stats_mutex);
1060    if (rs != APR_SUCCESS) {
1061        ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
1062                     "Parent could not set permissions on mod_tile "
1063                     "mutex: check User and Group directives");
1064        return HTTP_INTERNAL_SERVER_ERROR;
1065    }
1066#endif /* MOD_TILE_SET_MUTEX_PERMS */
1067
1068    /*
1069     * Create another unique filename to lock upon. Note that
1070     * depending on OS and locking mechanism of choice, the file
1071     * may or may not be actually created.
1072     */
1073    mutexfilename = apr_psprintf(pconf, "/tmp/httpd_mutex_delay.%ld",
1074                                 (long int) getpid());
1075
1076    rs = apr_global_mutex_create(&delay_mutex, (const char *) mutexfilename,
1077                                 APR_LOCK_DEFAULT, pconf);
1078    if (rs != APR_SUCCESS) {
1079        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1080                     "Failed to create mutex on file %s",
1081                     mutexfilename);
1082        return HTTP_INTERNAL_SERVER_ERROR;
1083    }
1084
1085#ifdef MOD_TILE_SET_MUTEX_PERMS
1086    rs = unixd_set_global_mutex_perms(delay_mutex);
1087    if (rs != APR_SUCCESS) {
1088        ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
1089                     "Parent could not set permissions on mod_tile "
1090                     "mutex: check User and Group directives");
1091        return HTTP_INTERNAL_SERVER_ERROR;
1092    }
1093#endif /* MOD_TILE_SET_MUTEX_PERMS */
1094
1095    return OK;
1096}
1097
1098
1099/*
1100 * This routine gets called when a child inits. We use it to attach
1101 * to the shared memory segment, and reinitialize the mutex.
1102 */
1103
1104static void mod_tile_child_init(apr_pool_t *p, server_rec *s)
1105{
1106    apr_status_t rs;
1107
1108     /*
1109      * Re-open the mutex for the child. Note we're reusing
1110      * the mutex pointer global here.
1111      */
1112     rs = apr_global_mutex_child_init(&stats_mutex,
1113                                      (const char *) mutexfilename,
1114                                      p);
1115     if (rs != APR_SUCCESS) {
1116         ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
1117                     "Failed to reopen mutex on file %s",
1118                     shmfilename);
1119         /* There's really nothing else we can do here, since
1120          * This routine doesn't return a status. */
1121         exit(1); /* Ugly, but what else? */
1122     }
1123}
1124
1125static void register_hooks(__attribute__((unused)) apr_pool_t *p)
1126{
1127    ap_hook_post_config(mod_tile_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1128    ap_hook_child_init(mod_tile_child_init, NULL, NULL, APR_HOOK_MIDDLE);
1129    ap_hook_handler(tile_handler_serve, NULL, NULL, APR_HOOK_MIDDLE);
1130    ap_hook_handler(tile_handler_dirty, NULL, NULL, APR_HOOK_MIDDLE);
1131    ap_hook_handler(tile_handler_status, NULL, NULL, APR_HOOK_MIDDLE);
1132    ap_hook_handler(tile_handler_mod_stats, NULL, NULL, APR_HOOK_MIDDLE);
1133    ap_hook_translate_name(tile_translate, NULL, NULL, APR_HOOK_MIDDLE);
1134    ap_hook_map_to_storage(tile_storage_hook, NULL, NULL, APR_HOOK_FIRST);
1135}
1136
1137static const char *_add_tile_config(cmd_parms *cmd, void *mconfig, const char *baseuri, const char *name, int minzoom, int maxzoom)
1138{
1139    if (strlen(name) == 0) {
1140        return "ConfigName value must not be null";
1141    }
1142
1143
1144    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1145    tile_config_rec *tilecfg = apr_array_push(scfg->configs);
1146
1147    // Ensure URI string ends with a trailing slash
1148    int urilen = strlen(baseuri);
1149
1150    if (urilen==0)
1151      snprintf(tilecfg->baseuri, PATH_MAX, "/");
1152    else if (baseuri[urilen-1] != '/')
1153      snprintf(tilecfg->baseuri, PATH_MAX, "%s/", baseuri);
1154    else
1155      snprintf(tilecfg->baseuri, PATH_MAX, "%s", baseuri);
1156
1157    strncpy(tilecfg->xmlname, name, XMLCONFIG_MAX-1);
1158    tilecfg->xmlname[XMLCONFIG_MAX-1] = 0;
1159    tilecfg->minzoom = minzoom;
1160    tilecfg->maxzoom = maxzoom;
1161
1162        ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, cmd->server,
1163                    "Loading tile config %s at %s for zooms %i - %i from tile directory %s",
1164                                 name, baseuri, minzoom, maxzoom, scfg->tile_dir);
1165
1166
1167    return NULL;
1168}
1169
1170static const char *add_tile_config(cmd_parms *cmd, void *mconfig, const char *baseuri, const char *name)
1171{
1172    return _add_tile_config(cmd, mconfig, baseuri, name, 0, MAX_ZOOM);
1173}
1174
1175static const char *load_tile_config(cmd_parms *cmd, void *mconfig, const char *conffile)
1176{
1177    FILE * hini ;
1178    char filename[PATH_MAX];
1179    char xmlname[XMLCONFIG_MAX];
1180    char line[INILINE_MAX];
1181    char key[INILINE_MAX];
1182    char value[INILINE_MAX];
1183    const char * result;
1184
1185    if (strlen(conffile) == 0) {
1186        strcpy(filename, RENDERD_CONFIG);
1187    } else {
1188        strcpy(filename, conffile);
1189    }
1190
1191    // Load the config
1192    if ((hini=fopen(filename, "r"))==NULL) {
1193        return "Unable to open config file";
1194    }
1195
1196    while (fgets(line, INILINE_MAX, hini)!=NULL) {
1197        if (line[0] == '#') continue;
1198        if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0;
1199        if (line[0] == '[') {
1200            if (strlen(line) >= XMLCONFIG_MAX){
1201                return "XML name too long";
1202            }
1203            sscanf(line, "[%[^]]", xmlname);
1204        } else if (sscanf(line, "%[^=]=%[^;#]", key, value) == 2
1205               ||  sscanf(line, "%[^=]=\"%[^\"]\"", key, value) == 2) {
1206
1207            if (!strcmp(key, "URI")){
1208                if (strlen(value) >= PATH_MAX){
1209                    return "URI too long";
1210                }
1211                result = add_tile_config(cmd, mconfig, value, xmlname);
1212                if (result != NULL) return result;
1213            }
1214        }
1215    }
1216    fclose(hini);
1217    return NULL;
1218}
1219
1220static const char *mod_tile_request_timeout_config(cmd_parms *cmd, void *mconfig, const char *request_timeout_string)
1221{
1222    int request_timeout;
1223
1224    if (sscanf(request_timeout_string, "%d", &request_timeout) != 1) {
1225        return "ModTileRequestTimeout needs integer argument";
1226    }
1227
1228    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1229    scfg->request_timeout = request_timeout;
1230    return NULL;
1231}
1232
1233static const char *mod_tile_request_timeout_missing_config(cmd_parms *cmd, void *mconfig, const char *request_timeout_string)
1234{
1235    int request_timeout;
1236
1237    if (sscanf(request_timeout_string, "%d", &request_timeout) != 1) {
1238        return "ModTileMissingRequestTimeout needs integer argument";
1239    }
1240
1241    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1242    scfg->request_timeout_priority = request_timeout;
1243    return NULL;
1244}
1245
1246static const char *mod_tile_max_load_old_config(cmd_parms *cmd, void *mconfig, const char *max_load_old_string)
1247{
1248    int max_load_old;
1249
1250    if (sscanf(max_load_old_string, "%d", &max_load_old) != 1) {
1251        return "ModTileMaxLoadOld needs integer argument";
1252    }
1253
1254    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1255    scfg->max_load_old = max_load_old;
1256    return NULL;
1257}
1258
1259static const char *mod_tile_max_load_missing_config(cmd_parms *cmd, void *mconfig, const char *max_load_missing_string)
1260{
1261    int max_load_missing;
1262
1263    if (sscanf(max_load_missing_string, "%d", &max_load_missing) != 1) {
1264        return "ModTileMaxLoadMissing needs integer argument";
1265    }
1266
1267    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1268    scfg->max_load_missing = max_load_missing;
1269    return NULL;
1270}
1271
1272static const char *mod_tile_renderd_socket_name_config(cmd_parms *cmd, void *mconfig, const char *renderd_socket_name_string)
1273{
1274    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1275    strncpy(scfg->renderd_socket_name, renderd_socket_name_string, PATH_MAX-1);
1276    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1277    return NULL;
1278}
1279
1280static const char *mod_tile_tile_dir_config(cmd_parms *cmd, void *mconfig, const char *tile_dir_string)
1281{
1282    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1283    strncpy(scfg->tile_dir, tile_dir_string, PATH_MAX-1);
1284    scfg->tile_dir[PATH_MAX-1] = 0;
1285    return NULL;
1286}
1287
1288static const char *mod_tile_cache_extended_host_name_config(cmd_parms *cmd, void *mconfig, const char *cache_extended_hostname)
1289{
1290    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1291    strncpy(scfg->cache_extended_hostname, cache_extended_hostname, PATH_MAX-1);
1292    scfg->cache_extended_hostname[PATH_MAX-1] = 0;
1293
1294    return NULL;
1295}
1296
1297static const char *mod_tile_cache_extended_duration_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1298{
1299    int cache_duration;
1300    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1301    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1302            return "ModTileCacheExtendedDuration needs integer argument";
1303    }
1304    scfg->cache_extended_duration = cache_duration;
1305
1306    return NULL;
1307}
1308
1309static const char *mod_tile_cache_lastmod_factor_config(cmd_parms *cmd, void *mconfig, const char *modified_factor_string)
1310{
1311    float modified_factor;
1312    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1313            &tile_module);
1314    if (sscanf(modified_factor_string, "%f", &modified_factor) != 1) {
1315        return "ModTileCacheLastModifiedFactor needs float argument";
1316    }
1317    scfg->cache_duration_last_modified_factor = modified_factor;
1318    return NULL;
1319}
1320
1321static const char *mod_tile_cache_duration_max_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1322{
1323    int cache_duration;
1324    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1325            &tile_module);
1326    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1327        return "ModTileCacheDurationMax needs integer argument";
1328    }
1329    scfg->cache_duration_max = cache_duration;
1330    return NULL;
1331}
1332
1333static const char *mod_tile_cache_duration_dirty_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1334{
1335    int cache_duration;
1336    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1337            &tile_module);
1338    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1339        return "ModTileCacheDurationDirty needs integer argument";
1340    }
1341    scfg->cache_duration_dirty = cache_duration;
1342    return NULL;
1343}
1344
1345static const char *mod_tile_cache_duration_minimum_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1346{
1347    int cache_duration;
1348    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1349            &tile_module);
1350    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1351        return "ModTileCacheDurationMinimum needs integer argument";
1352    }
1353    scfg->cache_duration_minimum = cache_duration;
1354    return NULL;
1355}
1356
1357static const char *mod_tile_cache_duration_low_config(cmd_parms *cmd, void *mconfig, const char *zoom_level_string, const char *cache_duration_string)
1358{
1359    int zoom_level;
1360    int cache_duration;
1361    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1362    if (sscanf(zoom_level_string, "%d", &zoom_level) != 1) {
1363            return "ModTileCacheDurationLowZoom needs integer argument";
1364    }
1365    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1366            return "ModTileCacheDurationLowZoom needs integer argument";
1367    }
1368    scfg->cache_level_low_zoom = zoom_level;
1369    scfg->cache_duration_low_zoom = cache_duration;
1370
1371    return NULL;
1372}
1373static const char *mod_tile_cache_duration_medium_config(cmd_parms *cmd, void *mconfig, const char *zoom_level_string, const char *cache_duration_string)
1374{
1375    int zoom_level;
1376    int cache_duration;
1377    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1378    if (sscanf(zoom_level_string, "%d", &zoom_level) != 1) {
1379            return "ModTileCacheDurationMediumZoom needs integer argument";
1380    }
1381    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1382            return "ModTileCacheDurationMediumZoom needs integer argument";
1383    }
1384    scfg->cache_level_medium_zoom = zoom_level;
1385    scfg->cache_duration_medium_zoom = cache_duration;
1386
1387    return NULL;
1388}
1389
1390static const char *mod_tile_enable_stats(cmd_parms *cmd, void *mconfig, int enableStats)
1391{
1392    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1393    scfg->enableGlobalStats = enableStats;
1394    return NULL;
1395}
1396
1397static const char *mod_tile_enable_throttling(cmd_parms *cmd, void *mconfig, int enableThrottling)
1398{
1399    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1400    scfg->enableTileThrottling = enableThrottling;
1401    return NULL;
1402}
1403
1404static const char *mod_tile_delaypool_tiles_config(cmd_parms *cmd, void *mconfig, const char *bucketsize_string, const char *topuprate_string)
1405{
1406    int bucketsize;
1407    float topuprate;
1408
1409    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1410    if (sscanf(bucketsize_string, "%d", &bucketsize) != 1) {
1411            return "ModTileThrottlingTiles needs two numerical arguments, the first one must be integer";
1412    }
1413    if (sscanf(topuprate_string, "%f", &topuprate) != 1) {
1414            return "ModTileThrottlingTiles needs two numerical arguments, the first one must be integer";
1415    }
1416    scfg->delaypoolTileSize = bucketsize;
1417
1418        /*Convert topup rate into microseconds per tile */
1419    scfg->delaypoolTileRate = (long)(1000000.0/topuprate);
1420
1421    return NULL;
1422}
1423
1424static const char *mod_tile_delaypool_render_config(cmd_parms *cmd, void *mconfig, const char *bucketsize_string, const char *topuprate_string)
1425{
1426    int bucketsize;
1427    float topuprate;
1428
1429    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1430    if (sscanf(bucketsize_string, "%d", &bucketsize) != 1) {
1431            return "ModTileThrottlingRenders needs two numerical arguments, the first one must be integer";
1432    }
1433    if (sscanf(topuprate_string, "%f", &topuprate) != 1) {
1434            return "ModTileThrottlingRenders needs two numerical arguments, the first one must be integer";
1435    }
1436    scfg->delaypoolRenderSize = bucketsize;
1437
1438        /*Convert topup rate into microseconds per tile */
1439    scfg->delaypoolRenderRate = (long)(1000000.0/topuprate);
1440
1441    return NULL;
1442}
1443
1444static void *create_tile_config(apr_pool_t *p, server_rec *s)
1445{
1446    tile_server_conf * scfg = (tile_server_conf *) apr_pcalloc(p, sizeof(tile_server_conf));
1447
1448    scfg->configs = apr_array_make(p, 4, sizeof(tile_config_rec));
1449    scfg->request_timeout = REQUEST_TIMEOUT;
1450    scfg->request_timeout_priority = REQUEST_TIMEOUT;
1451    scfg->max_load_old = MAX_LOAD_OLD;
1452    scfg->max_load_missing = MAX_LOAD_MISSING;
1453    strncpy(scfg->renderd_socket_name, RENDER_SOCKET, PATH_MAX-1);
1454    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1455    strncpy(scfg->tile_dir, HASH_PATH, PATH_MAX-1);
1456    scfg->tile_dir[PATH_MAX-1] = 0;
1457        memset(&(scfg->cache_extended_hostname),0,PATH_MAX);
1458        scfg->cache_extended_duration = 0;
1459    scfg->cache_duration_dirty = 15*60;
1460    scfg->cache_duration_last_modified_factor = 0.0;
1461    scfg->cache_duration_max = 7*24*60*60;
1462    scfg->cache_duration_minimum = 3*60*60;
1463    scfg->cache_duration_low_zoom = 6*24*60*60;
1464    scfg->cache_duration_medium_zoom = 1*24*60*60;
1465    scfg->cache_level_low_zoom = 0;
1466    scfg->cache_level_medium_zoom = 0;
1467    scfg->enableGlobalStats = 1;
1468        scfg->enableTileThrottling = 0;
1469        scfg->delaypoolTileSize = AVAILABLE_TILE_BUCKET_SIZE;
1470        scfg->delaypoolTileRate = RENDER_TOPUP_RATE;
1471        scfg->delaypoolRenderSize = AVAILABLE_RENDER_BUCKET_SIZE;
1472        scfg->delaypoolRenderRate = RENDER_TOPUP_RATE;
1473
1474
1475    return scfg;
1476}
1477
1478static void *merge_tile_config(apr_pool_t *p, void *basev, void *overridesv)
1479{
1480    int i;
1481    tile_server_conf * scfg = (tile_server_conf *) apr_pcalloc(p, sizeof(tile_server_conf));
1482    tile_server_conf * scfg_base = (tile_server_conf *) basev;
1483    tile_server_conf * scfg_over = (tile_server_conf *) overridesv;
1484
1485    scfg->configs = apr_array_append(p, scfg_base->configs, scfg_over->configs);
1486    scfg->request_timeout = scfg_over->request_timeout;
1487        scfg->request_timeout_priority = scfg_over->request_timeout_priority;
1488    scfg->max_load_old = scfg_over->max_load_old;
1489    scfg->max_load_missing = scfg_over->max_load_missing;
1490    strncpy(scfg->renderd_socket_name, scfg_over->renderd_socket_name, PATH_MAX-1);
1491    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1492    strncpy(scfg->tile_dir, scfg_over->tile_dir, PATH_MAX-1);
1493    scfg->tile_dir[PATH_MAX-1] = 0;
1494    strncpy(scfg->cache_extended_hostname, scfg_over->cache_extended_hostname, PATH_MAX-1);
1495    scfg->cache_extended_hostname[PATH_MAX-1] = 0;
1496    scfg->cache_extended_duration = scfg_over->cache_extended_duration;
1497    scfg->cache_duration_dirty = scfg_over->cache_duration_dirty;
1498    scfg->cache_duration_last_modified_factor = scfg_over->cache_duration_last_modified_factor;
1499    scfg->cache_duration_max = scfg_over->cache_duration_max;
1500    scfg->cache_duration_minimum = scfg_over->cache_duration_minimum;
1501    scfg->cache_duration_low_zoom = scfg_over->cache_duration_low_zoom;
1502    scfg->cache_duration_medium_zoom = scfg_over->cache_duration_medium_zoom;
1503    scfg->cache_level_low_zoom = scfg_over->cache_level_low_zoom;
1504    scfg->cache_level_medium_zoom = scfg_over->cache_level_medium_zoom;
1505    scfg->enableGlobalStats = scfg_over->enableGlobalStats;
1506        scfg->enableTileThrottling = scfg_over->enableTileThrottling;
1507        scfg->delaypoolTileSize = scfg_over->delaypoolTileSize;
1508        scfg->delaypoolTileRate = scfg_over->delaypoolTileRate;
1509        scfg->delaypoolRenderSize = scfg_over->delaypoolRenderSize;
1510        scfg->delaypoolRenderRate = scfg_over->delaypoolRenderRate;
1511
1512    //Construct a table of minimum cache times per zoom level
1513    for (i = 0; i <= MAX_ZOOM; i++) {
1514        if (i <= scfg->cache_level_low_zoom) {
1515            scfg->mincachetime[i] = scfg->cache_duration_low_zoom;
1516        } else if (i <= scfg->cache_level_medium_zoom) {
1517            scfg->mincachetime[i] = scfg->cache_duration_medium_zoom;
1518        } else {
1519            scfg->mincachetime[i] = scfg->cache_duration_minimum;
1520        }
1521    }
1522
1523    return scfg;
1524}
1525
1526static const command_rec tile_cmds[] =
1527{
1528    AP_INIT_TAKE1(
1529        "LoadTileConfigFile",            /* directive name */
1530        load_tile_config,                /* config action routine */
1531        NULL,                            /* argument to include in call */
1532        OR_OPTIONS,                      /* where available */
1533        "load an entire renderd config file"  /* directive description */
1534    ),
1535    AP_INIT_TAKE2(
1536        "AddTileConfig",                 /* directive name */
1537        add_tile_config,                 /* config action routine */
1538        NULL,                            /* argument to include in call */
1539        OR_OPTIONS,                      /* where available */
1540        "path and name of renderd config to use"  /* directive description */
1541    ),
1542    AP_INIT_TAKE1(
1543        "ModTileRequestTimeout",         /* directive name */
1544        mod_tile_request_timeout_config, /* config action routine */
1545        NULL,                            /* argument to include in call */
1546        OR_OPTIONS,                      /* where available */
1547        "Set timeout in seconds on mod_tile requests"  /* directive description */
1548    ),
1549    AP_INIT_TAKE1(
1550        "ModTileMissingRequestTimeout",         /* directive name */
1551        mod_tile_request_timeout_missing_config, /* config action routine */
1552        NULL,                            /* argument to include in call */
1553        OR_OPTIONS,                      /* where available */
1554        "Set timeout in seconds on missing mod_tile requests"  /* directive description */
1555    ),
1556    AP_INIT_TAKE1(
1557        "ModTileMaxLoadOld",             /* directive name */
1558        mod_tile_max_load_old_config,    /* config action routine */
1559        NULL,                            /* argument to include in call */
1560        OR_OPTIONS,                      /* where available */
1561        "Set max load for rendering old tiles"  /* directive description */
1562    ),
1563    AP_INIT_TAKE1(
1564        "ModTileMaxLoadMissing",         /* directive name */
1565        mod_tile_max_load_missing_config, /* config action routine */
1566        NULL,                            /* argument to include in call */
1567        OR_OPTIONS,                      /* where available */
1568        "Set max load for rendering missing tiles"  /* directive description */
1569    ),
1570    AP_INIT_TAKE1(
1571        "ModTileRenderdSocketName",      /* directive name */
1572        mod_tile_renderd_socket_name_config, /* config action routine */
1573        NULL,                            /* argument to include in call */
1574        OR_OPTIONS,                      /* where available */
1575        "Set name of unix domain socket for connecting to rendering daemon"  /* directive description */
1576    ),
1577    AP_INIT_TAKE1(
1578        "ModTileTileDir",                /* directive name */
1579        mod_tile_tile_dir_config,        /* config action routine */
1580        NULL,                            /* argument to include in call */
1581        OR_OPTIONS,                      /* where available */
1582        "Set name of tile cache directory"  /* directive description */
1583    ),
1584        AP_INIT_TAKE1(
1585        "ModTileCacheExtendedHostName",                /* directive name */
1586        mod_tile_cache_extended_host_name_config,        /* config action routine */
1587        NULL,                            /* argument to include in call */
1588        OR_OPTIONS,                      /* where available */
1589        "set hostname for extended period caching"  /* directive description */
1590    ),
1591        AP_INIT_TAKE1(
1592        "ModTileCacheExtendedDuration",                /* directive name */
1593        mod_tile_cache_extended_duration_config,        /* config action routine */
1594        NULL,                            /* argument to include in call */
1595        OR_OPTIONS,                      /* where available */
1596        "set length of extended period caching"  /* directive description */
1597    ),
1598    AP_INIT_TAKE1(
1599        "ModTileCacheDurationMax",                /* directive name */
1600        mod_tile_cache_duration_max_config,        /* config action routine */
1601        NULL,                            /* argument to include in call */
1602        OR_OPTIONS,                      /* where available */
1603        "Set the maximum cache expiry in seconds"  /* directive description */
1604    ),
1605    AP_INIT_TAKE1(
1606        "ModTileCacheDurationDirty",                    /* directive name */
1607        mod_tile_cache_duration_dirty_config,           /* config action routine */
1608        NULL,                                           /* argument to include in call */
1609        OR_OPTIONS,                                     /* where available */
1610        "Set the cache expiry for serving dirty tiles"  /* directive description */
1611    ),
1612    AP_INIT_TAKE1(
1613        "ModTileCacheDurationMinimum",          /* directive name */
1614        mod_tile_cache_duration_minimum_config, /* config action routine */
1615        NULL,                                   /* argument to include in call */
1616        OR_OPTIONS,                             /* where available */
1617        "Set the minimum cache expiry"          /* directive description */
1618    ),
1619    AP_INIT_TAKE1(
1620        "ModTileCacheLastModifiedFactor",       /* directive name */
1621        mod_tile_cache_lastmod_factor_config,   /* config action routine */
1622        NULL,                                   /* argument to include in call */
1623        OR_OPTIONS,                             /* where available */
1624        "Set the factor by which the last modified determins cache expiry" /* directive description */
1625    ),
1626    AP_INIT_TAKE2(
1627        "ModTileCacheDurationLowZoom",       /* directive name */
1628        mod_tile_cache_duration_low_config,                 /* config action routine */
1629        NULL,                            /* argument to include in call */
1630        OR_OPTIONS,                      /* where available */
1631        "Set the minimum cache duration and zoom level for low zoom tiles"  /* directive description */
1632    ),
1633    AP_INIT_TAKE2(
1634        "ModTileCacheDurationMediumZoom",       /* directive name */
1635        mod_tile_cache_duration_medium_config,                 /* config action routine */
1636        NULL,                            /* argument to include in call */
1637        OR_OPTIONS,                      /* where available */
1638        "Set the minimum cache duration and zoom level for medium zoom tiles"  /* directive description */
1639    ),
1640    AP_INIT_FLAG(
1641        "ModTileEnableStats",       /* directive name */
1642        mod_tile_enable_stats,                 /* config action routine */
1643        NULL,                            /* argument to include in call */
1644        OR_OPTIONS,                      /* where available */
1645        "On Off - enable of keeping stats about what mod_tile is serving"  /* directive description */
1646    ),
1647        AP_INIT_FLAG(
1648        "ModTileEnableTileThrottling",       /* directive name */
1649        mod_tile_enable_throttling,                 /* config action routine */
1650        NULL,                            /* argument to include in call */
1651        OR_OPTIONS,                      /* where available */
1652        "On Off - enable of throttling of IPs that excessively download tiles such as scrapers"  /* directive description */
1653    ),
1654        AP_INIT_TAKE2(
1655        "ModTileThrottlingTiles",       /* directive name */
1656        mod_tile_delaypool_tiles_config,                 /* config action routine */
1657        NULL,                            /* argument to include in call */
1658        OR_OPTIONS,                      /* where available */
1659        "Set the initial bucket size (number of tiles) and top up rate (tiles per second) for throttling tile request per IP"  /* directive description */
1660    ),
1661        AP_INIT_TAKE2(
1662        "ModTileThrottlingRenders",       /* directive name */
1663        mod_tile_delaypool_render_config,                 /* config action routine */
1664        NULL,                            /* argument to include in call */
1665        OR_OPTIONS,                      /* where available */
1666        "Set the initial bucket size (number of tiles) and top up rate (tiles per second) for throttling tile request per IP"  /* directive description */
1667    ),
1668    {NULL}
1669};
1670
1671module AP_MODULE_DECLARE_DATA tile_module =
1672{
1673    STANDARD20_MODULE_STUFF,
1674    NULL,                                /* dir config creater */
1675    NULL,                                /* dir merger --- default is to override */
1676    create_tile_config,                  /* server config */
1677    merge_tile_config,                   /* merge server config */
1678    tile_cmds,                           /* command apr_table_t */
1679    register_hooks                       /* register hooks */
1680};
1681
Note: See TracBrowser for help on using the repository browser.