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

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

finally remove hardcoded tile path and replace it by config option

File size: 15.6 KB
Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <sys/types.h>
5#include <sys/socket.h>
6#include <sys/stat.h>
7#include <sys/time.h>
8#include <sys/un.h>
9#include <poll.h>
10#include <errno.h>
11#include <math.h>
12#include <getopt.h>
13#include <time.h>
14#include <sys/types.h>
15#include <dirent.h>
16#include <limits.h>
17#include <string.h>
18
19#include <pthread.h>
20
21
22#include "gen_tile.h"
23#include "protocol.h"
24#include "render_config.h"
25#include "dir_utils.h"
26
27
28char *tile_dir = HASH_PATH;
29
30#ifndef METATILE
31#warning("render_old not implemented for non-metatile mode. Feel free to submit fix")
32int main(int argc, char **argv)
33{
34    fprintf(stderr, "render_old not implemented for non-metatile mode. Feel free to submit fix!\n");
35    return -1;
36}
37#else
38
39#define INILINE_MAX 256
40static int minZoom = 0;
41static int maxZoom = MAX_ZOOM;
42static int verbose = 0;
43static int num_render = 0, num_all = 0;
44static int max_load = MAX_LOAD_OLD;
45static time_t planetTime;
46static struct timeval start, end;
47
48
49int work_complete;
50
51void display_rate(struct timeval start, struct timeval end, int num) 
52{
53    int d_s, d_us;
54    float sec;
55
56    d_s  = end.tv_sec  - start.tv_sec;
57    d_us = end.tv_usec - start.tv_usec;
58
59    sec = d_s + d_us / 1000000.0;
60
61    printf("%d tiles in %.2f seconds (%.2f tiles/s)\n", num, sec, num / sec);
62    fflush(NULL);
63}
64
65static time_t getPlanetTime(char *tile_dir)
66{
67    static time_t last_check;
68    static time_t planet_timestamp;
69    time_t now = time(NULL);
70    struct stat buf;
71    char filename[PATH_MAX];
72
73    snprintf(filename, PATH_MAX-1, "%s/%s", tile_dir, PLANET_TIMESTAMP);
74
75    // Only check for updates periodically
76    if (now < last_check + 300)
77        return planet_timestamp;
78
79    last_check = now;
80    if (stat(filename, &buf)) {
81        fprintf(stderr, "Planet timestamp file (%s) is missing\n", filename);
82        // Make something up
83        planet_timestamp = now - 3 * 24 * 60 * 60;
84    } else {
85        if (buf.st_mtime != planet_timestamp) {
86            printf("Planet file updated at %s", ctime(&buf.st_mtime));
87            planet_timestamp = buf.st_mtime;
88        }
89    }
90    return planet_timestamp;
91}
92
93int get_load_avg(void)
94{
95    double load[3];
96    int avg = 1000;
97
98    if (getloadavg(load, 3) < 0) {
99        fprintf(stderr, "failed to read loadavg");
100        return 1000;
101    }
102
103    return (int)load[0];
104}
105
106int connect_socket(const char *arg) {
107    int fd;
108    struct sockaddr_un addr;
109    const char *spath = arg;
110
111    fd = socket(PF_UNIX, SOCK_STREAM, 0);
112    if (fd < 0) {
113        fprintf(stderr, "failed to create unix socket\n");
114        exit(2);
115    }
116
117    memset(&addr, 0, sizeof(addr));
118    addr.sun_family = AF_UNIX;
119    strncpy(addr.sun_path, spath, sizeof(addr.sun_path));
120
121    if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
122        fprintf(stderr, "socket connect failed for: %s\n", spath);
123        close(fd);
124        return -1;
125    }
126    return fd;
127}
128
129
130int process_loop(int fd, char * xmlname, int x, int y, int z)
131{
132    struct protocol cmd, rsp;
133    //struct pollfd fds[1];
134    int ret = 0;
135
136    memset(&cmd, 0, sizeof(cmd));
137
138    cmd.ver = 2;
139    cmd.cmd = cmdRenderBulk;
140    cmd.z = z;
141    cmd.x = x;
142    cmd.y = y;
143    strcpy(cmd.xmlname, xmlname);
144    //strcpy(cmd.path, "/tmp/foo.png");
145    //printf("Sending request of size %i\n", sizeof(cmd));
146    ret = send(fd, &cmd, sizeof(cmd), 0);
147    if (ret != sizeof(cmd)) {
148        perror("send error");
149    }
150        //printf("Waiting for response\n");
151    memset(&rsp, 0, sizeof(rsp));
152    ret = recv(fd, &rsp, sizeof(rsp), MSG_WAITALL);
153    if (ret != sizeof(rsp)) {
154        if (ret == 0) {
155            printf("rendering socket closed, pausing\n");
156            sleep(10);
157            return 0;
158        } else {
159            perror("recv error");
160            return -1;
161        }
162    }
163        //printf("Got response\n");
164
165    if (rsp.cmd != cmdDone) {
166        printf("rendering failed, pausing\n");
167        sleep(10);
168    }
169
170    if (!ret)
171        perror("Socket send error");
172    return ret;
173}
174
175int process(const char *tilepath, int fd, const char *name)
176{
177    char xmlconfig[XMLCONFIG_MAX];
178    int x, y, z;
179    struct stat b;
180
181    if (stat(name, &b))
182        return 1;
183
184    if (path_to_xyz(tilepath, name, xmlconfig, &x, &y, &z))
185        return 1;
186
187    printf("Requesting xml(%s) x(%d) y(%d) z(%d) as last modified at %s\n", xmlconfig, x, y, z, ctime(&b.st_mtime));
188    return process_loop(fd, xmlconfig, x, y, z);
189}
190
191static void check_load(void)
192{
193    int avg = get_load_avg();
194
195    while (avg >= max_load) {
196        printf("Load average %d, sleeping\n", avg);
197        sleep(5);
198        avg = get_load_avg();
199    }
200}
201
202#define QMAX 32
203
204pthread_mutex_t qLock;
205pthread_cond_t qCondNotEmpty;
206pthread_cond_t qCondNotFull;
207
208unsigned int qLen;
209struct qItem {
210    char *path;
211    struct qItem *next;
212};
213
214struct qItem *qHead, *qTail;
215
216char *fetch(const char * socket, int * fd)
217{
218    // Fetch path to render from queue or NULL on work completion
219    // Must free() pointer after use
220    char *path;
221
222    pthread_mutex_lock(&qLock);
223
224    while (qLen == 0) {
225        /* If there is nothing to do, close the socket, as it may otherwise timeout */
226        if (fd >= 0) {
227            close(*fd);
228            *fd = -1;
229        }
230        if (work_complete) {
231            pthread_mutex_unlock(&qLock);
232            return NULL;
233        }
234        pthread_cond_wait(&qCondNotEmpty, &qLock);
235    }
236    if (*fd < 0) {
237        *fd = connect_socket(socket);
238    }
239
240    // Fetch item from queue
241    if (!qHead) {
242        fprintf(stderr, "Queue failure, null qHead with %d items in list\n", qLen);
243        exit(1);
244    }
245    path = qHead->path;
246
247    if (qHead == qTail) {
248        free(qHead);
249        qHead = NULL;
250        qTail = NULL;
251        qLen = 0;
252    } else {
253        struct qItem *e = qHead;
254        qHead = qHead->next;
255        free(e);
256        if (qLen == QMAX)
257            pthread_cond_signal(&qCondNotFull);
258        qLen--;
259    }
260    pthread_mutex_unlock(&qLock);
261    return path;
262}
263
264void enqueue(const char *path)
265{
266    // Add this path in the local render queue
267    struct qItem *e = malloc(sizeof(struct qItem));
268
269    e->path = strdup(path);
270    e->next = NULL;
271
272    if (!e->path) {
273        fprintf(stderr, "Malloc failure\n");
274        exit(1);
275    }
276
277    pthread_mutex_lock(&qLock);
278
279    while (qLen == QMAX) {
280        pthread_cond_wait(&qCondNotFull, &qLock);
281    }
282
283    // Append item to end of queue
284    if (qTail)
285        qTail->next = e;
286    else
287        qHead = e;
288    qTail = e;
289    pthread_cond_signal(&qCondNotEmpty);
290    qLen++;
291
292    pthread_mutex_unlock(&qLock);
293    sleep(10);
294}
295
296static void descend(const char *search)
297{
298    DIR *tiles = opendir(search);
299    struct dirent *entry;
300    char path[PATH_MAX];
301
302    if (!tiles) {
303        fprintf(stderr, "Unable to open directory: %s\n", search);
304        return;
305    }
306
307    while ((entry = readdir(tiles))) {
308        struct stat b;
309        char *p;
310
311        check_load();
312
313        if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
314            continue;
315        snprintf(path, sizeof(path), "%s/%s", search, entry->d_name);
316        if (stat(path, &b))
317            continue;
318        if (S_ISDIR(b.st_mode)) {
319            descend(path);
320            continue;
321        }
322        p = strrchr(path, '.');
323        if (p && !strcmp(p, ".meta")) {
324            num_all++;
325            if (planetTime > b.st_mtime) {
326                // request rendering of  old tile
327                enqueue(path);
328            }
329        }
330    }
331    closedir(tiles);
332}
333
334
335
336void *thread_main(void *arg)
337{
338    int fd;
339    char * spath = (char *) arg;
340    char *tile;
341
342    fd = connect_socket(spath);
343
344    while((tile = fetch(spath, &fd))) {
345        int ret = process(tile_dir, fd, tile);
346        if (ret == 0) {
347            printf("Reconnecting closed socket\n");
348            close(fd);
349            fd = connect_socket(spath);
350        }
351        num_render++;
352        if (!(num_render % 10)) {
353            gettimeofday(&end, NULL);
354            printf("\n");
355            printf("Meta tiles rendered: ");
356            display_rate(start, end, num_render);
357            printf("Total tiles rendered: ");
358            display_rate(start, end, num_render * METATILE * METATILE);
359            printf("Number of Metatiles tested for expiry: ");
360            display_rate(start, end, num_all);
361            printf("\n");
362        }
363        free(tile);
364    }
365
366    close(fd);
367
368    return NULL;
369}
370
371void render_layer(const char *tilepath, const char *name)
372{
373    int z;
374
375    for (z=minZoom; z<=maxZoom; z++) {
376        char path[PATH_MAX];
377        snprintf(path, PATH_MAX, "%s/%s/%d", tilepath, name, z);
378        descend(path);
379    }
380}
381
382pthread_t *workers;
383
384void spawn_workers(int num, const char *spath)
385{
386    int i;
387
388    // Setup request queue
389    pthread_mutex_init(&qLock, NULL);
390    pthread_cond_init(&qCondNotEmpty, NULL);
391    pthread_cond_init(&qCondNotFull, NULL);
392
393    printf("Starting %d rendering threads\n", num);
394    workers = calloc(sizeof(pthread_t), num);
395    if (!workers) {
396        perror("Error allocating worker memory");
397        exit(1);
398    }
399    for(i=0; i<num; i++) {
400        if (pthread_create(&workers[i], NULL, thread_main, (void *)spath)) {
401            perror("Thread creation failed");
402            exit(1);
403        }
404    }
405}
406
407void finish_workers(int num)
408{
409    int i;
410
411    printf("Waiting for rendering threads to finish\n");
412    pthread_mutex_lock(&qLock);
413    work_complete = 1;
414    pthread_mutex_unlock(&qLock);
415    pthread_cond_broadcast(&qCondNotEmpty);
416
417    for(i=0; i<num; i++)
418        pthread_join(workers[i], NULL);
419    free(workers);
420    workers = NULL;
421}
422
423
424int main(int argc, char **argv)
425{
426    char spath[PATH_MAX] = RENDER_SOCKET;
427    char *config_file = RENDERD_CONFIG;
428    char *map = NULL;
429    int c;
430    int numThreads = 1;
431    int dd, mm, yy;
432    struct tm tm;
433
434    while (1) {
435        int option_index = 0;
436        static struct option long_options[] = {
437            {"config",1,0,'c'},
438            {"min-zoom", 1, 0, 'z'},
439            {"max-zoom", 1, 0, 'Z'},
440            {"max-load", 1, 0, 'l'},
441            {"socket", 1, 0, 's'},
442            {"num-threads", 1, 0, 'n'},
443            {"tile-dir", 1, 0, 't'},
444            {"timestamp", 1, 0, 'T'},
445            {"map", 1, 0, 'm'},
446            {"verbose", 0, 0, 'v'},
447            {"help", 0, 0, 'h'},
448            {0, 0, 0, 0}
449        };
450
451        c = getopt_long(argc, argv, "hvz:Z:s:t:n:c:l:T:m:", long_options, &option_index);
452        if (c == -1)
453            break;
454
455        switch (c) {
456            case 's':   /* -s, --socket */
457                strncpy(spath, optarg, PATH_MAX-1);
458                spath[PATH_MAX-1] = 0;
459                break;
460            case 't':   /* -t, --tile-dir */
461                tile_dir=strdup(optarg);
462                break;
463
464            case 'c':   /* -c, --config */
465                config_file=strdup(optarg);
466                break;
467            case 'm':   /* -m, --map */
468                map=strdup(optarg);
469                break;
470            case 'n':   /* -n, --num-threads */
471                numThreads=atoi(optarg);
472                if (numThreads <= 0) {
473                    fprintf(stderr, "Invalid number of threads, must be at least 1\n");
474                    return 1;
475                }
476                break;
477           case 'z':   /* -z, --min-zoom */
478                        minZoom=atoi(optarg);
479                if (minZoom < 0 || minZoom > MAX_ZOOM) {
480                    fprintf(stderr, "Invalid minimum zoom selected, must be between 0 and %d\n", MAX_ZOOM);
481                    return 1;
482                }
483                break;
484            case 'Z':   /* -Z, --max-zoom */
485                maxZoom=atoi(optarg);
486                if (maxZoom < 0 || maxZoom > MAX_ZOOM) { 
487                    fprintf(stderr, "Invalid maximum zoom selected, must be between 0 and %d\n", MAX_ZOOM);
488                    return 1;
489                }
490                break;
491             case 'l':
492                 max_load = atoi(optarg);
493                 if (max_load < 0) {
494                     fprintf(stderr, "Invalid maximum load specified, must be greater than 0\n");
495                     return 1;
496                 }
497                 break;
498            case 'T':
499               
500                if (sscanf(optarg,"%d/%d/%d", &dd, &mm, &yy) < 3) {
501                    fprintf(stderr, "Invalid planet time stamp, must be in the format dd/mm/yyyy\n");
502                    return 1;
503                }
504               
505                if (yy > 100) yy -= 1900;
506                if (yy < 70) yy += 100;
507               
508                tm.tm_sec = 0; tm.tm_min = 0; tm.tm_hour = 0; tm.tm_mday = dd; tm.tm_mon = mm - 1; tm.tm_year =  yy;
509                planetTime = mktime(&tm);
510
511            case 'v':   /* -v, --verbose */
512                verbose=1;
513                break;
514            case 'h':   /* -h, --help */
515                fprintf(stderr, "Usage: render_old [OPTION] ...\n");
516                fprintf(stderr, "Search the rendered tiles and re-render tiles which are older then the last planet import\n");
517                fprintf(stderr, "  -c, --config=CONFIG  specify the renderd config file\n");
518                fprintf(stderr, "  -n, --num-threads=N  the number of parallel request threads (default 1)\n");
519                fprintf(stderr, "  -t, --tile-dir       tile cache directory (defaults to '" HASH_PATH "')\n");
520                fprintf(stderr, "  -z, --min-zoom=ZOOM  filter input to only render tiles greater or equal to this zoom level (default 0)\n");
521                fprintf(stderr, "  -Z, --max-zoom=ZOOM  filter input to only render tiles less than or equal to this zoom level (default %d)\n", MAX_ZOOM);
522                fprintf(stderr, "  -s, --socket=SOCKET  unix domain socket name for contacting renderd\n");
523                fprintf(stderr, "  -l, --max-load=LOAD  maximum system load with which requests are submitted\n");
524                fprintf(stderr, "  -T, --timestamp=DD/MM/YY  Overwrite the assumed data of the planet import\n");
525                fprintf(stderr, "  -m, --map=STYLE      Instead of going through all styls of CONFIG, only use a specific map-style\n");
526                return -1;
527            default:
528                fprintf(stderr, "unhandled char '%c'\n", c);
529                break;
530        }
531    }
532
533    if (maxZoom < minZoom) {
534        fprintf(stderr, "Invalid zoom range, max zoom must be greater or equal to minimum zoom\n");
535        return 1;
536    }
537
538    fprintf(stderr, "Rendering old tiles\n");
539
540    if (planetTime == 0) {
541        planetTime = getPlanetTime(tile_dir);
542    } else {
543        printf("Overwriting planet file update to %s", ctime(&planetTime));
544    }
545
546    gettimeofday(&start, NULL);
547
548    FILE * hini ;
549    char line[INILINE_MAX];
550    char value[INILINE_MAX];
551
552    // Load the config
553    if ((hini=fopen(config_file, "r"))==NULL) {
554        fprintf(stderr, "Config: cannot open %s\n", config_file);
555        exit(7);
556    }
557
558    spawn_workers(numThreads, spath);
559
560    if (map) {
561        render_layer(tile_dir, map);
562    } else {
563        while (fgets(line, INILINE_MAX, hini)!=NULL) {
564            if (line[0] == '[') {
565                if (strlen(line) >= XMLCONFIG_MAX){
566                    fprintf(stderr, "XML name too long: %s\n", line);
567                    exit(7);
568                }
569                sscanf(line, "[%[^]]", value);
570                // Skip mapnik & renderd sections which are config, not tile layers
571                if (strcmp(value,"mapnik") && strncmp(value, "renderd", 7))
572                    render_layer(tile_dir, value);
573            }
574        }
575    }
576    fclose(hini);
577
578    finish_workers(numThreads);
579
580    gettimeofday(&end, NULL);
581    printf("\nTotal for all tiles rendered\n");
582    printf("Meta tiles rendered: ");
583    display_rate(start, end, num_render);
584    printf("Total tiles rendered: ");
585    display_rate(start, end, num_render * METATILE * METATILE);
586    printf("Total tiles handled: ");
587    display_rate(start, end, num_all);
588
589    return 0;
590}
591#endif
Note: See TracBrowser for help on using the repository browser.