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

Last change on this file since 25096 was 25096, checked in by frederik, 9 years ago

Fix spelling of "throttling" in mod_tile. This will require a change in your config file if you have been unsing the old, mis-spelled version.

File size: 60.6 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_INFO, 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 xml(%s) z(%d) x(%d) y(%d)", cmd->xmlname, cmd->z, cmd->x, cmd->y);
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_DEBUG, 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_ERR, 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_INFO, 0, r, "Could not get hardlock");
375                return 0;
376            }
377        } else {
378            ap_log_rerror(APLOG_MARK, APLOG_INFO, 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    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "tile_storage_hook: handler(%s), uri(%s), filename(%s), path_info(%s)",
614                  r->handler, r->uri, r->filename, r->path_info);
615
616    if (!r->handler)
617        return DECLINED;
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_DEBUG, 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_INFO, 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_INFO, 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_INFO, 0, r, "tile_translate: 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) return DECLINED;
898
899            oob = (cmd->z < 0 || cmd->z > MAX_ZOOM);
900            if (!oob) {
901                 // valid x/y for tiles are 0 ... 2^zoom-1
902                 limit = (1 << cmd->z) - 1;
903                 oob =  (cmd->x < 0 || cmd->x > limit || cmd->y < 0 || cmd->y > limit);
904            }
905
906            if (oob) {
907                sleep(CLIENT_PENALTY);
908                //Don't increase stats counter here,
909                //As we are interested in valid tiles only
910                return HTTP_NOT_FOUND;
911            }
912
913            strcpy(cmd->xmlname, tile_config->xmlname);
914
915            // Store a copy for later
916            ap_set_module_config(r->request_config, &tile_module, cmd);
917
918            // Generate the tile filename?
919            char abs_path[PATH_MAX];
920#ifdef METATILE
921            xyz_to_meta(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
922#else
923            xyz_to_path(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
924#endif
925            r->filename = apr_pstrdup(r->pool, abs_path);
926
927            if (n == 4) {
928                if (!strcmp(option, "status")) r->handler = "tile_status";
929                else if (!strcmp(option, "dirty")) r->handler = "tile_dirty";
930                else return DECLINED;
931            } else {
932                r->handler = "tile_serve";
933            }
934
935            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);
936
937            return OK;
938        }
939    }
940    return DECLINED;
941}
942
943/*
944 * This routine is called in the parent, so we'll set up the shared
945 * memory segment and mutex here.
946 */
947
948static int mod_tile_post_config(apr_pool_t *pconf, apr_pool_t *plog,
949                             apr_pool_t *ptemp, server_rec *s)
950{
951    void *data; /* These two help ensure that we only init once. */
952    const char *userdata_key = "mod_tile_init_module";
953    apr_status_t rs;
954    stats_data *stats;
955        delaypool *delayp;
956        int i;
957
958
959    /*
960     * The following checks if this routine has been called before.
961     * This is necessary because the parent process gets initialized
962     * a couple of times as the server starts up, and we don't want
963     * to create any more mutexes and shared memory segments than
964     * we're actually going to use.
965     */
966    apr_pool_userdata_get(&data, userdata_key, s->process->pool);
967    if (!data) {
968        apr_pool_userdata_set((const void *) 1, userdata_key,
969                              apr_pool_cleanup_null, s->process->pool);
970        return OK;
971    } /* Kilroy was here */
972
973    /* Create the shared memory segment */
974
975    /*
976     * Create a unique filename using our pid. This information is
977     * stashed in the global variable so the children inherit it.
978     * TODO get the location from the environment $TMPDIR or somesuch.
979     */
980    shmfilename = apr_psprintf(pconf, "/tmp/httpd_shm.%ld", (long int)getpid());
981        shmfilename_delaypool = apr_psprintf(pconf, "/tmp/httpd_shm_delay.%ld", (long int)getpid());
982
983    /* Now create that segment */
984    rs = apr_shm_create(&stats_shm, sizeof(stats_data),
985                        (const char *) shmfilename, pconf);
986    if (rs != APR_SUCCESS) {
987        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
988                     "Failed to create shared memory segment on file %s",
989                     shmfilename);
990        return HTTP_INTERNAL_SERVER_ERROR;
991    }
992
993        rs = apr_shm_create(&delaypool_shm, sizeof(delaypool),
994                        (const char *) shmfilename_delaypool, pconf);
995    if (rs != APR_SUCCESS) {
996        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
997                     "Failed to create shared memory segment on file %s",
998                     shmfilename_delaypool);
999        return HTTP_INTERNAL_SERVER_ERROR;
1000    }
1001
1002    /* Created it, now let's zero it out */
1003    stats = (stats_data *)apr_shm_baseaddr_get(stats_shm);
1004    stats->noResp200 = 0;
1005    stats->noResp304 = 0;
1006    stats->noResp404 = 0;
1007        stats->noResp503 = 0;
1008    stats->noResp5XX = 0;
1009        for (i = 0; i <= MAX_ZOOM; i++) {
1010                stats->noRespZoom[i] = 0;
1011        }
1012    stats->noRespOther = 0;
1013    stats->noFreshCache = 0;
1014    stats->noFreshRender = 0;
1015    stats->noOldCache = 0;
1016    stats->noOldRender = 0;
1017
1018        delayp = (delaypool *)apr_shm_baseaddr_get(delaypool_shm);
1019       
1020        delayp->last_tile_fillup = apr_time_now();
1021        delayp->last_render_fillup = apr_time_now();
1022
1023        for (i = 0; i < DELAY_HASHTABLE_SIZE; i++) {
1024                delayp->users[i].ip_addr = (in_addr_t)0;
1025                delayp->users[i].available_tiles = 0;
1026                delayp->users[i].available_render_req = 0;
1027        }
1028        for (i = 0; i < DELAY_HASHTABLE_WHITELIST_SIZE; i++) {
1029                delayp->whitelist[i] = (in_addr_t)0;
1030        }
1031        /* TODO: need a way to initialise the delaypool whitelist */
1032
1033
1034    /* Create global mutex */
1035
1036    /*
1037     * Create another unique filename to lock upon. Note that
1038     * depending on OS and locking mechanism of choice, the file
1039     * may or may not be actually created.
1040     */
1041    mutexfilename = apr_psprintf(pconf, "/tmp/httpd_mutex.%ld",
1042                                 (long int) getpid());
1043
1044    rs = apr_global_mutex_create(&stats_mutex, (const char *) mutexfilename,
1045                                 APR_LOCK_DEFAULT, pconf);
1046    if (rs != APR_SUCCESS) {
1047        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1048                     "Failed to create mutex on file %s",
1049                     mutexfilename);
1050        return HTTP_INTERNAL_SERVER_ERROR;
1051    }
1052
1053#ifdef MOD_TILE_SET_MUTEX_PERMS
1054    rs = unixd_set_global_mutex_perms(stats_mutex);
1055    if (rs != APR_SUCCESS) {
1056        ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
1057                     "Parent could not set permissions on mod_tile "
1058                     "mutex: check User and Group directives");
1059        return HTTP_INTERNAL_SERVER_ERROR;
1060    }
1061#endif /* MOD_TILE_SET_MUTEX_PERMS */
1062
1063    /*
1064     * Create another unique filename to lock upon. Note that
1065     * depending on OS and locking mechanism of choice, the file
1066     * may or may not be actually created.
1067     */
1068    mutexfilename = apr_psprintf(pconf, "/tmp/httpd_mutex_delay.%ld",
1069                                 (long int) getpid());
1070
1071    rs = apr_global_mutex_create(&delay_mutex, (const char *) mutexfilename,
1072                                 APR_LOCK_DEFAULT, pconf);
1073    if (rs != APR_SUCCESS) {
1074        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1075                     "Failed to create mutex on file %s",
1076                     mutexfilename);
1077        return HTTP_INTERNAL_SERVER_ERROR;
1078    }
1079
1080#ifdef MOD_TILE_SET_MUTEX_PERMS
1081    rs = unixd_set_global_mutex_perms(delay_mutex);
1082    if (rs != APR_SUCCESS) {
1083        ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
1084                     "Parent could not set permissions on mod_tile "
1085                     "mutex: check User and Group directives");
1086        return HTTP_INTERNAL_SERVER_ERROR;
1087    }
1088#endif /* MOD_TILE_SET_MUTEX_PERMS */
1089
1090    return OK;
1091}
1092
1093
1094/*
1095 * This routine gets called when a child inits. We use it to attach
1096 * to the shared memory segment, and reinitialize the mutex.
1097 */
1098
1099static void mod_tile_child_init(apr_pool_t *p, server_rec *s)
1100{
1101    apr_status_t rs;
1102
1103     /*
1104      * Re-open the mutex for the child. Note we're reusing
1105      * the mutex pointer global here.
1106      */
1107     rs = apr_global_mutex_child_init(&stats_mutex,
1108                                      (const char *) mutexfilename,
1109                                      p);
1110     if (rs != APR_SUCCESS) {
1111         ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
1112                     "Failed to reopen mutex on file %s",
1113                     shmfilename);
1114         /* There's really nothing else we can do here, since
1115          * This routine doesn't return a status. */
1116         exit(1); /* Ugly, but what else? */
1117     }
1118}
1119
1120static void register_hooks(__attribute__((unused)) apr_pool_t *p)
1121{
1122    ap_hook_post_config(mod_tile_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1123    ap_hook_child_init(mod_tile_child_init, NULL, NULL, APR_HOOK_MIDDLE);
1124    ap_hook_handler(tile_handler_serve, NULL, NULL, APR_HOOK_MIDDLE);
1125    ap_hook_handler(tile_handler_dirty, NULL, NULL, APR_HOOK_MIDDLE);
1126    ap_hook_handler(tile_handler_status, NULL, NULL, APR_HOOK_MIDDLE);
1127    ap_hook_handler(tile_handler_mod_stats, NULL, NULL, APR_HOOK_MIDDLE);
1128    ap_hook_translate_name(tile_translate, NULL, NULL, APR_HOOK_MIDDLE);
1129    ap_hook_map_to_storage(tile_storage_hook, NULL, NULL, APR_HOOK_FIRST);
1130}
1131
1132static const char *_add_tile_config(cmd_parms *cmd, void *mconfig, const char *baseuri, const char *name, int minzoom, int maxzoom)
1133{
1134    if (strlen(name) == 0) {
1135        return "ConfigName value must not be null";
1136    }
1137
1138    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1139    tile_config_rec *tilecfg = apr_array_push(scfg->configs);
1140
1141    // Ensure URI string ends with a trailing slash
1142    int urilen = strlen(baseuri);
1143
1144    if (urilen==0)
1145      snprintf(tilecfg->baseuri, PATH_MAX, "/");
1146    else if (baseuri[urilen-1] != '/')
1147      snprintf(tilecfg->baseuri, PATH_MAX, "%s/", baseuri);
1148    else
1149      snprintf(tilecfg->baseuri, PATH_MAX, "%s", baseuri);
1150
1151    strncpy(tilecfg->xmlname, name, XMLCONFIG_MAX-1);
1152    tilecfg->xmlname[XMLCONFIG_MAX-1] = 0;
1153    tilecfg->minzoom = minzoom;
1154    tilecfg->maxzoom = maxzoom;
1155
1156
1157    return NULL;
1158}
1159
1160static const char *add_tile_config(cmd_parms *cmd, void *mconfig, const char *baseuri, const char *name)
1161{
1162    return _add_tile_config(cmd, mconfig, baseuri, name, 0, MAX_ZOOM);
1163}
1164
1165static const char *load_tile_config(cmd_parms *cmd, void *mconfig, const char *conffile)
1166{
1167    FILE * hini ;
1168    char filename[PATH_MAX];
1169    char xmlname[XMLCONFIG_MAX];
1170    char line[INILINE_MAX];
1171    char key[INILINE_MAX];
1172    char value[INILINE_MAX];
1173    const char * result;
1174
1175    if (strlen(conffile) == 0) {
1176        strcpy(filename, RENDERD_CONFIG);
1177    } else {
1178        strcpy(filename, conffile);
1179    }
1180
1181    // Load the config
1182    if ((hini=fopen(filename, "r"))==NULL) {
1183        return "Unable to open config file";
1184    }
1185
1186    while (fgets(line, INILINE_MAX, hini)!=NULL) {
1187        if (line[0] == '#') continue;
1188        if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0;
1189        if (line[0] == '[') {
1190            if (strlen(line) >= XMLCONFIG_MAX){
1191                return "XML name too long";
1192            }
1193            sscanf(line, "[%[^]]", xmlname);
1194        } else if (sscanf(line, "%[^=]=%[^;#]", key, value) == 2
1195               ||  sscanf(line, "%[^=]=\"%[^\"]\"", key, value) == 2) {
1196
1197            if (!strcmp(key, "URI")){
1198                if (strlen(value) >= PATH_MAX){
1199                    return "URI too long";
1200                }
1201                result = add_tile_config(cmd, mconfig, value, xmlname);
1202                if (result != NULL) return result;
1203            }
1204        }
1205    }
1206    fclose(hini);
1207    return NULL;
1208}
1209
1210static const char *mod_tile_request_timeout_config(cmd_parms *cmd, void *mconfig, const char *request_timeout_string)
1211{
1212    int request_timeout;
1213
1214    if (sscanf(request_timeout_string, "%d", &request_timeout) != 1) {
1215        return "ModTileRequestTimeout needs integer argument";
1216    }
1217
1218    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1219    scfg->request_timeout = request_timeout;
1220    return NULL;
1221}
1222
1223static const char *mod_tile_request_timeout_missing_config(cmd_parms *cmd, void *mconfig, const char *request_timeout_string)
1224{
1225    int request_timeout;
1226
1227    if (sscanf(request_timeout_string, "%d", &request_timeout) != 1) {
1228        return "ModTileMissingRequestTimeout needs integer argument";
1229    }
1230
1231    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1232    scfg->request_timeout_priority = request_timeout;
1233    return NULL;
1234}
1235
1236static const char *mod_tile_max_load_old_config(cmd_parms *cmd, void *mconfig, const char *max_load_old_string)
1237{
1238    int max_load_old;
1239
1240    if (sscanf(max_load_old_string, "%d", &max_load_old) != 1) {
1241        return "ModTileMaxLoadOld needs integer argument";
1242    }
1243
1244    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1245    scfg->max_load_old = max_load_old;
1246    return NULL;
1247}
1248
1249static const char *mod_tile_max_load_missing_config(cmd_parms *cmd, void *mconfig, const char *max_load_missing_string)
1250{
1251    int max_load_missing;
1252
1253    if (sscanf(max_load_missing_string, "%d", &max_load_missing) != 1) {
1254        return "ModTileMaxLoadMissing needs integer argument";
1255    }
1256
1257    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1258    scfg->max_load_missing = max_load_missing;
1259    return NULL;
1260}
1261
1262static const char *mod_tile_renderd_socket_name_config(cmd_parms *cmd, void *mconfig, const char *renderd_socket_name_string)
1263{
1264    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1265    strncpy(scfg->renderd_socket_name, renderd_socket_name_string, PATH_MAX-1);
1266    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1267    return NULL;
1268}
1269
1270static const char *mod_tile_tile_dir_config(cmd_parms *cmd, void *mconfig, const char *tile_dir_string)
1271{
1272    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1273    strncpy(scfg->tile_dir, tile_dir_string, PATH_MAX-1);
1274    scfg->tile_dir[PATH_MAX-1] = 0;
1275    return NULL;
1276}
1277
1278static const char *mod_tile_cache_extended_host_name_config(cmd_parms *cmd, void *mconfig, const char *cache_extended_hostname)
1279{
1280    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1281    strncpy(scfg->cache_extended_hostname, cache_extended_hostname, PATH_MAX-1);
1282    scfg->cache_extended_hostname[PATH_MAX-1] = 0;
1283
1284    return NULL;
1285}
1286
1287static const char *mod_tile_cache_extended_duration_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1288{
1289    int cache_duration;
1290    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1291    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1292            return "ModTileCacheExtendedDuration needs integer argument";
1293    }
1294    scfg->cache_extended_duration = cache_duration;
1295
1296    return NULL;
1297}
1298
1299static const char *mod_tile_cache_lastmod_factor_config(cmd_parms *cmd, void *mconfig, const char *modified_factor_string)
1300{
1301    float modified_factor;
1302    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1303            &tile_module);
1304    if (sscanf(modified_factor_string, "%f", &modified_factor) != 1) {
1305        return "ModTileCacheLastModifiedFactor needs float argument";
1306    }
1307    scfg->cache_duration_last_modified_factor = modified_factor;
1308    return NULL;
1309}
1310
1311static const char *mod_tile_cache_duration_max_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1312{
1313    int cache_duration;
1314    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1315            &tile_module);
1316    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1317        return "ModTileCacheDurationMax needs integer argument";
1318    }
1319    scfg->cache_duration_max = cache_duration;
1320    return NULL;
1321}
1322
1323static const char *mod_tile_cache_duration_dirty_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1324{
1325    int cache_duration;
1326    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1327            &tile_module);
1328    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1329        return "ModTileCacheDurationDirty needs integer argument";
1330    }
1331    scfg->cache_duration_dirty = cache_duration;
1332    return NULL;
1333}
1334
1335static const char *mod_tile_cache_duration_minimum_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1336{
1337    int cache_duration;
1338    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1339            &tile_module);
1340    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1341        return "ModTileCacheDurationMinimum needs integer argument";
1342    }
1343    scfg->cache_duration_minimum = cache_duration;
1344    return NULL;
1345}
1346
1347static const char *mod_tile_cache_duration_low_config(cmd_parms *cmd, void *mconfig, const char *zoom_level_string, const char *cache_duration_string)
1348{
1349    int zoom_level;
1350    int cache_duration;
1351    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1352    if (sscanf(zoom_level_string, "%d", &zoom_level) != 1) {
1353            return "ModTileCacheDurationLowZoom needs integer argument";
1354    }
1355    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1356            return "ModTileCacheDurationLowZoom needs integer argument";
1357    }
1358    scfg->cache_level_low_zoom = zoom_level;
1359    scfg->cache_duration_low_zoom = cache_duration;
1360
1361    return NULL;
1362}
1363static const char *mod_tile_cache_duration_medium_config(cmd_parms *cmd, void *mconfig, const char *zoom_level_string, const char *cache_duration_string)
1364{
1365    int zoom_level;
1366    int cache_duration;
1367    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1368    if (sscanf(zoom_level_string, "%d", &zoom_level) != 1) {
1369            return "ModTileCacheDurationMediumZoom needs integer argument";
1370    }
1371    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1372            return "ModTileCacheDurationMediumZoom needs integer argument";
1373    }
1374    scfg->cache_level_medium_zoom = zoom_level;
1375    scfg->cache_duration_medium_zoom = cache_duration;
1376
1377    return NULL;
1378}
1379
1380static const char *mod_tile_enable_stats(cmd_parms *cmd, void *mconfig, int enableStats)
1381{
1382    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1383    scfg->enableGlobalStats = enableStats;
1384    return NULL;
1385}
1386
1387static const char *mod_tile_enable_throttling(cmd_parms *cmd, void *mconfig, int enableThrottling)
1388{
1389    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1390    scfg->enableTileThrottling = enableThrottling;
1391    return NULL;
1392}
1393
1394static const char *mod_tile_delaypool_tiles_config(cmd_parms *cmd, void *mconfig, const char *bucketsize_string, const char *topuprate_string)
1395{
1396    int bucketsize;
1397    float topuprate;
1398
1399    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1400    if (sscanf(bucketsize_string, "%d", &bucketsize) != 1) {
1401            return "ModTileThrottlingTiles needs two numerical arguments, the first one must be integer";
1402    }
1403    if (sscanf(topuprate_string, "%f", &topuprate) != 1) {
1404            return "ModTileThrottlingTiles needs two numerical arguments, the first one must be integer";
1405    }
1406    scfg->delaypoolTileSize = bucketsize;
1407
1408        /*Convert topup rate into microseconds per tile */
1409    scfg->delaypoolTileRate = (long)(1000000.0/topuprate);
1410
1411    return NULL;
1412}
1413
1414static const char *mod_tile_delaypool_render_config(cmd_parms *cmd, void *mconfig, const char *bucketsize_string, const char *topuprate_string)
1415{
1416    int bucketsize;
1417    float topuprate;
1418
1419    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1420    if (sscanf(bucketsize_string, "%d", &bucketsize) != 1) {
1421            return "ModTileThrottlingRenders needs two numerical arguments, the first one must be integer";
1422    }
1423    if (sscanf(topuprate_string, "%f", &topuprate) != 1) {
1424            return "ModTileThrottlingRenders needs two numerical arguments, the first one must be integer";
1425    }
1426    scfg->delaypoolRenderSize = bucketsize;
1427
1428        /*Convert topup rate into microseconds per tile */
1429    scfg->delaypoolRenderRate = (long)(1000000.0/topuprate);
1430
1431    return NULL;
1432}
1433
1434static void *create_tile_config(apr_pool_t *p, server_rec *s)
1435{
1436    tile_server_conf * scfg = (tile_server_conf *) apr_pcalloc(p, sizeof(tile_server_conf));
1437
1438    scfg->configs = apr_array_make(p, 4, sizeof(tile_config_rec));
1439    scfg->request_timeout = REQUEST_TIMEOUT;
1440    scfg->request_timeout_priority = REQUEST_TIMEOUT;
1441    scfg->max_load_old = MAX_LOAD_OLD;
1442    scfg->max_load_missing = MAX_LOAD_MISSING;
1443    strncpy(scfg->renderd_socket_name, RENDER_SOCKET, PATH_MAX-1);
1444    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1445    strncpy(scfg->tile_dir, HASH_PATH, PATH_MAX-1);
1446    scfg->tile_dir[PATH_MAX-1] = 0;
1447        memset(&(scfg->cache_extended_hostname),0,PATH_MAX);
1448        scfg->cache_extended_duration = 0;
1449    scfg->cache_duration_dirty = 15*60;
1450    scfg->cache_duration_last_modified_factor = 0.0;
1451    scfg->cache_duration_max = 7*24*60*60;
1452    scfg->cache_duration_minimum = 3*60*60;
1453    scfg->cache_duration_low_zoom = 6*24*60*60;
1454    scfg->cache_duration_medium_zoom = 1*24*60*60;
1455    scfg->cache_level_low_zoom = 0;
1456    scfg->cache_level_medium_zoom = 0;
1457    scfg->enableGlobalStats = 1;
1458        scfg->enableTileThrottling = 0;
1459        scfg->delaypoolTileSize = AVAILABLE_TILE_BUCKET_SIZE;
1460        scfg->delaypoolTileRate = RENDER_TOPUP_RATE;
1461        scfg->delaypoolRenderSize = AVAILABLE_RENDER_BUCKET_SIZE;
1462        scfg->delaypoolRenderRate = RENDER_TOPUP_RATE;
1463
1464
1465    return scfg;
1466}
1467
1468static void *merge_tile_config(apr_pool_t *p, void *basev, void *overridesv)
1469{
1470    int i;
1471    tile_server_conf * scfg = (tile_server_conf *) apr_pcalloc(p, sizeof(tile_server_conf));
1472    tile_server_conf * scfg_base = (tile_server_conf *) basev;
1473    tile_server_conf * scfg_over = (tile_server_conf *) overridesv;
1474
1475    scfg->configs = apr_array_append(p, scfg_base->configs, scfg_over->configs);
1476    scfg->request_timeout = scfg_over->request_timeout;
1477        scfg->request_timeout_priority = scfg_over->request_timeout_priority;
1478    scfg->max_load_old = scfg_over->max_load_old;
1479    scfg->max_load_missing = scfg_over->max_load_missing;
1480    strncpy(scfg->renderd_socket_name, scfg_over->renderd_socket_name, PATH_MAX-1);
1481    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1482    strncpy(scfg->tile_dir, scfg_over->tile_dir, PATH_MAX-1);
1483    scfg->tile_dir[PATH_MAX-1] = 0;
1484    strncpy(scfg->cache_extended_hostname, scfg_over->cache_extended_hostname, PATH_MAX-1);
1485    scfg->cache_extended_hostname[PATH_MAX-1] = 0;
1486    scfg->cache_extended_duration = scfg_over->cache_extended_duration;
1487    scfg->cache_duration_dirty = scfg_over->cache_duration_dirty;
1488    scfg->cache_duration_last_modified_factor = scfg_over->cache_duration_last_modified_factor;
1489    scfg->cache_duration_max = scfg_over->cache_duration_max;
1490    scfg->cache_duration_minimum = scfg_over->cache_duration_minimum;
1491    scfg->cache_duration_low_zoom = scfg_over->cache_duration_low_zoom;
1492    scfg->cache_duration_medium_zoom = scfg_over->cache_duration_medium_zoom;
1493    scfg->cache_level_low_zoom = scfg_over->cache_level_low_zoom;
1494    scfg->cache_level_medium_zoom = scfg_over->cache_level_medium_zoom;
1495    scfg->enableGlobalStats = scfg_over->enableGlobalStats;
1496        scfg->enableTileThrottling = scfg_over->enableTileThrottling;
1497        scfg->delaypoolTileSize = scfg_over->delaypoolTileSize;
1498        scfg->delaypoolTileRate = scfg_over->delaypoolTileRate;
1499        scfg->delaypoolRenderSize = scfg_over->delaypoolRenderSize;
1500        scfg->delaypoolRenderRate = scfg_over->delaypoolRenderRate;
1501
1502    //Construct a table of minimum cache times per zoom level
1503    for (i = 0; i <= MAX_ZOOM; i++) {
1504        if (i <= scfg->cache_level_low_zoom) {
1505            scfg->mincachetime[i] = scfg->cache_duration_low_zoom;
1506        } else if (i <= scfg->cache_level_medium_zoom) {
1507            scfg->mincachetime[i] = scfg->cache_duration_medium_zoom;
1508        } else {
1509            scfg->mincachetime[i] = scfg->cache_duration_minimum;
1510        }
1511    }
1512
1513    return scfg;
1514}
1515
1516static const command_rec tile_cmds[] =
1517{
1518    AP_INIT_TAKE1(
1519        "LoadTileConfigFile",            /* directive name */
1520        load_tile_config,                /* config action routine */
1521        NULL,                            /* argument to include in call */
1522        OR_OPTIONS,                      /* where available */
1523        "load an entire renderd config file"  /* directive description */
1524    ),
1525    AP_INIT_TAKE2(
1526        "AddTileConfig",                 /* directive name */
1527        add_tile_config,                 /* config action routine */
1528        NULL,                            /* argument to include in call */
1529        OR_OPTIONS,                      /* where available */
1530        "path and name of renderd config to use"  /* directive description */
1531    ),
1532    AP_INIT_TAKE1(
1533        "ModTileRequestTimeout",         /* directive name */
1534        mod_tile_request_timeout_config, /* config action routine */
1535        NULL,                            /* argument to include in call */
1536        OR_OPTIONS,                      /* where available */
1537        "Set timeout in seconds on mod_tile requests"  /* directive description */
1538    ),
1539    AP_INIT_TAKE1(
1540        "ModTileMissingRequestTimeout",         /* directive name */
1541        mod_tile_request_timeout_missing_config, /* config action routine */
1542        NULL,                            /* argument to include in call */
1543        OR_OPTIONS,                      /* where available */
1544        "Set timeout in seconds on missing mod_tile requests"  /* directive description */
1545    ),
1546    AP_INIT_TAKE1(
1547        "ModTileMaxLoadOld",             /* directive name */
1548        mod_tile_max_load_old_config,    /* config action routine */
1549        NULL,                            /* argument to include in call */
1550        OR_OPTIONS,                      /* where available */
1551        "Set max load for rendering old tiles"  /* directive description */
1552    ),
1553    AP_INIT_TAKE1(
1554        "ModTileMaxLoadMissing",         /* directive name */
1555        mod_tile_max_load_missing_config, /* config action routine */
1556        NULL,                            /* argument to include in call */
1557        OR_OPTIONS,                      /* where available */
1558        "Set max load for rendering missing tiles"  /* directive description */
1559    ),
1560    AP_INIT_TAKE1(
1561        "ModTileRenderdSocketName",      /* directive name */
1562        mod_tile_renderd_socket_name_config, /* config action routine */
1563        NULL,                            /* argument to include in call */
1564        OR_OPTIONS,                      /* where available */
1565        "Set name of unix domain socket for connecting to rendering daemon"  /* directive description */
1566    ),
1567    AP_INIT_TAKE1(
1568        "ModTileTileDir",                /* directive name */
1569        mod_tile_tile_dir_config,        /* config action routine */
1570        NULL,                            /* argument to include in call */
1571        OR_OPTIONS,                      /* where available */
1572        "Set name of tile cache directory"  /* directive description */
1573    ),
1574        AP_INIT_TAKE1(
1575        "ModTileCacheExtendedHostName",                /* directive name */
1576        mod_tile_cache_extended_host_name_config,        /* config action routine */
1577        NULL,                            /* argument to include in call */
1578        OR_OPTIONS,                      /* where available */
1579        "set hostname for extended period caching"  /* directive description */
1580    ),
1581        AP_INIT_TAKE1(
1582        "ModTileCacheExtendedDuration",                /* directive name */
1583        mod_tile_cache_extended_duration_config,        /* config action routine */
1584        NULL,                            /* argument to include in call */
1585        OR_OPTIONS,                      /* where available */
1586        "set length of extended period caching"  /* directive description */
1587    ),
1588    AP_INIT_TAKE1(
1589        "ModTileCacheDurationMax",                /* directive name */
1590        mod_tile_cache_duration_max_config,        /* config action routine */
1591        NULL,                            /* argument to include in call */
1592        OR_OPTIONS,                      /* where available */
1593        "Set the maximum cache expiry in seconds"  /* directive description */
1594    ),
1595    AP_INIT_TAKE1(
1596        "ModTileCacheDurationDirty",                    /* directive name */
1597        mod_tile_cache_duration_dirty_config,           /* config action routine */
1598        NULL,                                           /* argument to include in call */
1599        OR_OPTIONS,                                     /* where available */
1600        "Set the cache expiry for serving dirty tiles"  /* directive description */
1601    ),
1602    AP_INIT_TAKE1(
1603        "ModTileCacheDurationMinimum",          /* directive name */
1604        mod_tile_cache_duration_minimum_config, /* config action routine */
1605        NULL,                                   /* argument to include in call */
1606        OR_OPTIONS,                             /* where available */
1607        "Set the minimum cache expiry"          /* directive description */
1608    ),
1609    AP_INIT_TAKE1(
1610        "ModTileCacheLastModifiedFactor",       /* directive name */
1611        mod_tile_cache_lastmod_factor_config,   /* config action routine */
1612        NULL,                                   /* argument to include in call */
1613        OR_OPTIONS,                             /* where available */
1614        "Set the factor by which the last modified determins cache expiry" /* directive description */
1615    ),
1616    AP_INIT_TAKE2(
1617        "ModTileCacheDurationLowZoom",       /* directive name */
1618        mod_tile_cache_duration_low_config,                 /* config action routine */
1619        NULL,                            /* argument to include in call */
1620        OR_OPTIONS,                      /* where available */
1621        "Set the minimum cache duration and zoom level for low zoom tiles"  /* directive description */
1622    ),
1623    AP_INIT_TAKE2(
1624        "ModTileCacheDurationMediumZoom",       /* directive name */
1625        mod_tile_cache_duration_medium_config,                 /* config action routine */
1626        NULL,                            /* argument to include in call */
1627        OR_OPTIONS,                      /* where available */
1628        "Set the minimum cache duration and zoom level for medium zoom tiles"  /* directive description */
1629    ),
1630    AP_INIT_FLAG(
1631        "ModTileEnableStats",       /* directive name */
1632        mod_tile_enable_stats,                 /* config action routine */
1633        NULL,                            /* argument to include in call */
1634        OR_OPTIONS,                      /* where available */
1635        "On Off - enable of keeping stats about what mod_tile is serving"  /* directive description */
1636    ),
1637        AP_INIT_FLAG(
1638        "ModTileEnableTileThrottling",       /* directive name */
1639        mod_tile_enable_throttling,                 /* config action routine */
1640        NULL,                            /* argument to include in call */
1641        OR_OPTIONS,                      /* where available */
1642        "On Off - enable of throttling of IPs that excessively download tiles such as scrapers"  /* directive description */
1643    ),
1644        AP_INIT_TAKE2(
1645        "ModTileThrottlingTiles",       /* directive name */
1646        mod_tile_delaypool_tiles_config,                 /* config action routine */
1647        NULL,                            /* argument to include in call */
1648        OR_OPTIONS,                      /* where available */
1649        "Set the initial bucket size (number of tiles) and top up rate (tiles per second) for throttling tile request per IP"  /* directive description */
1650    ),
1651        AP_INIT_TAKE2(
1652        "ModTileThrottlingRenders",       /* directive name */
1653        mod_tile_delaypool_render_config,                 /* config action routine */
1654        NULL,                            /* argument to include in call */
1655        OR_OPTIONS,                      /* where available */
1656        "Set the initial bucket size (number of tiles) and top up rate (tiles per second) for throttling tile request per IP"  /* directive description */
1657    ),
1658    {NULL}
1659};
1660
1661module AP_MODULE_DECLARE_DATA tile_module =
1662{
1663    STANDARD20_MODULE_STUFF,
1664    NULL,                                /* dir config creater */
1665    NULL,                                /* dir merger --- default is to override */
1666    create_tile_config,                  /* server config */
1667    merge_tile_config,                   /* merge server config */
1668    tile_cmds,                           /* command apr_table_t */
1669    register_hooks                       /* register hooks */
1670};
1671
Note: See TracBrowser for help on using the repository browser.