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

Last change on this file since 28750 was 28714, checked in by apmon, 7 years ago

[mod_tile] Add some configure compatibility fixes for solaris

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