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

Last change on this file since 28629 was 28561, checked in by Dirk Stoecker, 7 years ago

typos

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