~ubuntu-branches/debian/jessie/distcc/jessie

« back to all changes in this revision

Viewing changes to .pc/r673_zeroconf-nodups.patch/source/src/zeroconf.c

  • Committer: Package Import Robot
  • Author(s): Daniel Hartwig
  • Date: 2012-05-05 17:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20120505172418-d5821mf4ys0vvh2o
Tags: 3.1-5
* new maintainer (Closes: #664497)
* update Standards-Version to 3.9.3:
  - debian/distcc.init.d: start action exits successfully if daemon
    is already running [9.3.2]; postinst will succeed on upgrades in
    this case also (Closes: #620773, LP: #822887)
* switch to dpkg-source 3.0 (quilt) format
* remove Build-Depends: dpatch
* add Build-Depends: autotools-dev
* debian/rules:
  - use debhelper compat level 9
  - reduce to "dh $@" style
  - fixes FTBFS caused by previous build-arch target (Closes: #666383)
  - enabled bindnow hardening (no PIE yet, it causes build failure)
* debian/control:
  - added Homepage field
  - distcc: description starts lowercase
* debian/distcc.config:
  - renamed from debian/config
  - use "set -e"
  - include missing #DEBHELPER# token
* debian/distcc.init.d:
  - added stop levels 0, 6
  - unset TMPDIR before starting the daemon to avoid problems with
    root's value for this being unwritable by the distccd user; to
    provide distccd with a sensible TMPDIR put it in /etc/default/distcc
    (Closes: #514556)
* debian/distcc.postinst:
  - use "set -e" instead of "#!/bin/sh -e"
  - drop extra calls to "sed ... /etc/default/distcc" for every field
    that only removed whitespace which would be removed anyway when
    updating the fields
  - always create distccd user if it does not exist (Closes: #548053)
* debian/distccmon-gnome.menu:
  - changed title to "distcc monitor"
  - dropped incorrect hint tag
* debian/patches:
  - 06_set-pythonpath-securely.patch: contains fix for #605168 which
    was previously applied directly to the source
  - 07_preferred-user.patch: starting the daemon as root causes it to
    change user, which should be to "distccd" in Debian
  - 08_gnome-data-public-dirs.patch: install desktop and icon files for
    distccmon-gnome /usr/share/applications and /usr/share/pixmaps
    respectively (LP: #512288)
  - 09_rename-pump.patch: rename the "pump" command to "distcc-pump" in
    all references such as help text, man pages, etc. (Closes: #594083)
  - 10_consecutive-preprocessor-options.patch: correctly count
    preprocessor options (Closes: #626926)
  - 11_lsdistcc-man.patch: add man page for lsdistcc utility
* debian/watch:
  - added remote watch file
* source/config.{guess,sub}:
  - update with autotools-dev during build instead of directly patching
    the source tree
* use dpkg triggers to dynamically generate/update compiler links
  based on the ccache packaging.  Thanks to Daniel Schaal
  (Closes: #651670)
* cherry-pick upstream fixes for IPv6 support (Closes: #452835):
  - r650_ipv6-zeroconf.patch: IPv6 patch for Zeroconf and IPv6 literals
    in hosts file (Closes: #481951, LP: #593047)
  - r673_zeroconf-nodups.patch: remove duplicate hosts from the Zeroconf
    list (LP: #809534)
  - r678_distcc-v6-acl-2.patch: IPv6 support for access control
* cherry-pick other upstream fixes:
  - r732_distccmon-gnome.patch: avoid client list growing indefinitely
    (LP: #521165)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- c-file-style: "java"; indent-tabs-mode: nil; tab-width: 4; fill-column: 78 -*-
 
2
 *
 
3
 * Copyright (C) 2007 Lennart Poettering
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or
 
6
 * modify it under the terms of the GNU General Public License
 
7
 * as published by the Free Software Foundation; either version 2
 
8
 * of the License, or (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 
18
 * USA.
 
19
 */
 
20
 
 
21
#include <config.h>
 
22
 
 
23
#include <assert.h>
 
24
#include <stdio.h>
 
25
#include <sys/select.h>
 
26
#include <signal.h>
 
27
#include <sys/file.h>
 
28
#include <sys/time.h>
 
29
#include <time.h>
 
30
#include <sys/stat.h>
 
31
#include <string.h>
 
32
#include <errno.h>
 
33
#include <unistd.h>
 
34
#include <stdlib.h>
 
35
#include <limits.h>
 
36
 
 
37
#include <avahi-common/domain.h>
 
38
#include <avahi-common/error.h>
 
39
#include <avahi-common/malloc.h>
 
40
#include <avahi-common/address.h>
 
41
#include <avahi-common/simple-watch.h>
 
42
#include <avahi-client/lookup.h>
 
43
 
 
44
#include "distcc.h"
 
45
#include "hosts.h"
 
46
#include "zeroconf.h"
 
47
#include "trace.h"
 
48
#include "exitcode.h"
 
49
 
 
50
/* How long shall the background daemon be idle before i terminates itself? */
 
51
#define MAX_IDLE_TIME 20
 
52
 
 
53
/* Maxium size of host file to load */
 
54
#define MAX_FILE_SIZE (1024*100)
 
55
 
 
56
/* General daemon data */
 
57
struct daemon_data {
 
58
    struct host *hosts;
 
59
    int fd;
 
60
    int n_slots;
 
61
 
 
62
    AvahiClient *client;
 
63
    AvahiServiceBrowser *browser;
 
64
    AvahiSimplePoll *simple_poll;
 
65
};
 
66
 
 
67
/* Zeroconf service wrapper */
 
68
struct host {
 
69
    struct daemon_data *daemon_data;
 
70
    struct host *next;
 
71
 
 
72
    AvahiIfIndex interface;
 
73
    AvahiProtocol protocol;
 
74
    char *service;
 
75
    char *domain;
 
76
 
 
77
    AvahiAddress address;
 
78
    uint16_t port;
 
79
    int n_cpus;
 
80
 
 
81
    AvahiServiceResolver *resolver;
 
82
};
 
83
 
 
84
/* A generic, system independant lock routine, similar to sys_lock,
 
85
 * but more powerful:
 
86
 *        rw:         if non-zero: r/w lock instead of r/o lock
 
87
 *        enable:     lock or unlock
 
88
 *        block:      block when locking */
 
89
static int generic_lock(int fd, int rw, int enable, int block) {
 
90
#if defined(F_SETLK)
 
91
    struct flock lockparam;
 
92
 
 
93
    lockparam.l_type = enable ? (rw ? F_WRLCK : F_RDLCK) : F_UNLCK;
 
94
    lockparam.l_whence = SEEK_SET;
 
95
    lockparam.l_start = 0;
 
96
    lockparam.l_len = 0;        /* whole file */
 
97
 
 
98
    return fcntl(fd, block ? F_SETLKW : F_SETLK, &lockparam);
 
99
#elif defined(HAVE_FLOCK)
 
100
    return flock(fd, (enable ? (rw ? LOCK_EX : LOCK_SH) : LOCK_UN) | (block ? LOCK_NB : 0));
 
101
#elif defined(HAVE_LOCKF)
 
102
    return lockf(fd, (enable ? (block ? F_LOCK : F_TLOCK) : F_ULOCK));
 
103
#else
 
104
#  error "No supported lock method.  Please port this code."
 
105
#endif
 
106
}
 
107
 
 
108
/* Return the number of seconds, when the specified file was last
 
109
 * read. If the atime of that file is < clip_time, use clip_time
 
110
 * instead */
 
111
static time_t fd_last_used(int fd, time_t clip_time) {
 
112
    struct stat st;
 
113
    time_t now, ft;
 
114
    assert(fd >= 0);
 
115
 
 
116
    if (fstat(fd, &st) < 0) {
 
117
        rs_log_crit("fstat() failed: %s\n", strerror(errno));
 
118
        return -1;
 
119
    }
 
120
 
 
121
    if ((now = time(NULL)) == (time_t) -1) {
 
122
        rs_log_crit("time() failed: %s\n", strerror(errno));
 
123
        return -1;
 
124
    }
 
125
 
 
126
    ft = clip_time ? (st.st_atime < clip_time ? clip_time : st.st_atime) : st.st_atime;
 
127
    assert(ft <= now);
 
128
 
 
129
    return now - ft;
 
130
}
 
131
 
 
132
/* Write host data to host file */
 
133
static int write_hosts(struct daemon_data *d) {
 
134
    struct host *h;
 
135
    int r = 0;
 
136
    assert(d);
 
137
 
 
138
    rs_log_info("writing zeroconf data.\n");
 
139
 
 
140
    if (generic_lock(d->fd, 1, 1, 1) < 0) {
 
141
        rs_log_crit("lock failed: %s\n", strerror(errno));
 
142
        return -1;
 
143
    }
 
144
 
 
145
    if (lseek(d->fd, 0, SEEK_SET) < 0) {
 
146
        rs_log_crit("lseek() failed: %s\n", strerror(errno));
 
147
        return -1;
 
148
    }
 
149
 
 
150
    if (ftruncate(d->fd, 0) < 0) {
 
151
        rs_log_crit("ftruncate() failed: %s\n", strerror(errno));
 
152
        return -1;
 
153
    }
 
154
 
 
155
    for (h = d->hosts; h; h = h->next) {
 
156
        char t[256], a[AVAHI_ADDRESS_STR_MAX];
 
157
 
 
158
        if (h->resolver)
 
159
            /* Not yet fully resolved */
 
160
            continue;
 
161
        if (h->address.proto == AVAHI_PROTO_INET6)
 
162
            snprintf(t, sizeof(t), "[%s]:%u/%i\n", avahi_address_snprint(a, sizeof(a), &h->address), h->port, d->n_slots * h->n_cpus);
 
163
        else
 
164
            snprintf(t, sizeof(t), "%s:%u/%i\n", avahi_address_snprint(a, sizeof(a), &h->address), h->port, d->n_slots * h->n_cpus);
 
165
 
 
166
        if (dcc_writex(d->fd, t, strlen(t)) != 0) {
 
167
            rs_log_crit("write() failed: %s\n", strerror(errno));
 
168
            goto finish;
 
169
        }
 
170
    }
 
171
 
 
172
    r = 0;
 
173
 
 
174
finish:
 
175
 
 
176
    generic_lock(d->fd, 1, 0, 1);
 
177
    return r;
 
178
 
 
179
};
 
180
 
 
181
/* Free host data */
 
182
static void free_host(struct host *h) {
 
183
    assert(h);
 
184
 
 
185
    if (h->resolver)
 
186
        avahi_service_resolver_free(h->resolver);
 
187
 
 
188
    free(h->service);
 
189
    free(h->domain);
 
190
    free(h);
 
191
}
 
192
 
 
193
/* Remove a service from the host list */
 
194
static void remove_service(struct daemon_data *d, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *domain) {
 
195
    struct host *h, *p = NULL;
 
196
    assert(d);
 
197
 
 
198
    for (h = d->hosts; h; h = h->next) {
 
199
        if (h->interface == interface &&
 
200
            h->protocol == protocol &&
 
201
            !strcmp(h->service, name) &&
 
202
            avahi_domain_equal(h->domain, domain)) {
 
203
 
 
204
            if (p)
 
205
                p->next = h->next;
 
206
            else
 
207
                d->hosts = h->next;
 
208
 
 
209
            free_host(h);
 
210
 
 
211
            break;
 
212
        } else
 
213
            p = h;
 
214
    }
 
215
}
 
216
 
 
217
/* Called when a resolve call completes */
 
218
static void resolve_reply(
 
219
        AvahiServiceResolver *UNUSED(r),
 
220
        AvahiIfIndex UNUSED(interface),
 
221
        AvahiProtocol UNUSED(protocol),
 
222
        AvahiResolverEvent event,
 
223
        const char *name,
 
224
        const char *UNUSED(type),
 
225
        const char *UNUSED(domain),
 
226
        const char *UNUSED(host_name),
 
227
        const AvahiAddress *a,
 
228
        uint16_t port,
 
229
        AvahiStringList *txt,
 
230
        AvahiLookupResultFlags UNUSED(flags),
 
231
        void *userdata) {
 
232
 
 
233
    struct host *h = userdata;
 
234
 
 
235
    switch (event) {
 
236
 
 
237
        case AVAHI_RESOLVER_FOUND: {
 
238
            AvahiStringList *i;
 
239
 
 
240
            /* Look for the number of CPUs in TXT RRs */
 
241
            for (i = txt; i; i = i->next) {
 
242
                char *key, *value;
 
243
 
 
244
                if (avahi_string_list_get_pair(i, &key, &value, NULL) < 0)
 
245
                    continue;
 
246
 
 
247
                if (!strcmp(key, "cpus"))
 
248
                    if ((h->n_cpus = atoi(value)) <= 0)
 
249
                        h->n_cpus = 1;
 
250
 
 
251
                avahi_free(key);
 
252
                avahi_free(value);
 
253
            }
 
254
 
 
255
            h->address = *a;
 
256
            h->port = port;
 
257
 
 
258
            avahi_service_resolver_free(h->resolver);
 
259
            h->resolver = NULL;
 
260
 
 
261
            /* Write modified hosts file */
 
262
            write_hosts(h->daemon_data);
 
263
 
 
264
            break;
 
265
        }
 
266
 
 
267
        case AVAHI_RESOLVER_FAILURE:
 
268
 
 
269
            rs_log_warning("Failed to resolve service '%s': %s\n", name,
 
270
                           avahi_strerror(avahi_client_errno(h->daemon_data->client)));
 
271
 
 
272
            free_host(h);
 
273
            break;
 
274
    }
 
275
 
 
276
}
 
277
 
 
278
/* Called whenever a new service is found or removed */
 
279
static void browse_reply(
 
280
        AvahiServiceBrowser *UNUSED(b),
 
281
        AvahiIfIndex interface,
 
282
        AvahiProtocol protocol,
 
283
        AvahiBrowserEvent event,
 
284
        const char *name,
 
285
        const char *type,
 
286
        const char *domain,
 
287
        AvahiLookupResultFlags UNUSED(flags),
 
288
        void *userdata) {
 
289
 
 
290
    struct daemon_data *d = userdata;
 
291
    assert(d);
 
292
 
 
293
    switch (event) {
 
294
        case AVAHI_BROWSER_NEW: {
 
295
            struct host *h;
 
296
 
 
297
            h = malloc(sizeof(struct host));
 
298
            assert(h);
 
299
 
 
300
            rs_log_info("new service: %s\n", name);
 
301
 
 
302
            if (!(h->resolver = avahi_service_resolver_new(d->client,
 
303
                                                           interface,
 
304
                                                           protocol,
 
305
                                                           name,
 
306
                                                           type,
 
307
                                                           domain,
 
308
                                                           AVAHI_PROTO_UNSPEC,
 
309
                                                           0,
 
310
                                                           resolve_reply,
 
311
                                                           h))) {
 
312
                rs_log_warning("Failed to create service resolver for '%s': %s\n", name,
 
313
                               avahi_strerror(avahi_client_errno(d->client)));
 
314
 
 
315
                free(h);
 
316
 
 
317
            } else {
 
318
 
 
319
                /* Fill in missing data */
 
320
                h->service = strdup(name);
 
321
                assert(h->service);
 
322
                h->domain = strdup(domain);
 
323
                assert(h->domain);
 
324
                h->daemon_data = d;
 
325
                h->interface = interface;
 
326
                h->protocol = protocol;
 
327
                h->next = d->hosts;
 
328
                h->n_cpus = 1;
 
329
                d->hosts = h;
 
330
            }
 
331
 
 
332
            break;
 
333
        }
 
334
 
 
335
        case AVAHI_BROWSER_REMOVE:
 
336
 
 
337
            rs_log_info("Removed service: %s\n", name);
 
338
 
 
339
            remove_service(d, interface, protocol, name, domain);
 
340
            write_hosts(d);
 
341
            break;
 
342
 
 
343
        case AVAHI_BROWSER_FAILURE:
 
344
            rs_log_crit("Service Browser failure '%s': %s\n", name,
 
345
                        avahi_strerror(avahi_client_errno(d->client)));
 
346
 
 
347
            avahi_simple_poll_quit(d->simple_poll);
 
348
            break;
 
349
 
 
350
        case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
351
        case AVAHI_BROWSER_ALL_FOR_NOW:
 
352
            ;
 
353
 
 
354
    }
 
355
}
 
356
 
 
357
static void client_callback(AvahiClient *client, AvahiClientState state, void *userdata) {
 
358
    struct daemon_data *d = userdata;
 
359
 
 
360
    switch (state) {
 
361
 
 
362
        case AVAHI_CLIENT_FAILURE:
 
363
            rs_log_crit("Client failure: %s\n", avahi_strerror(avahi_client_errno(client)));
 
364
            avahi_simple_poll_quit(d->simple_poll);
 
365
            break;
 
366
 
 
367
        case AVAHI_CLIENT_S_COLLISION:
 
368
        case AVAHI_CLIENT_S_REGISTERING:
 
369
        case AVAHI_CLIENT_S_RUNNING:
 
370
        case AVAHI_CLIENT_CONNECTING:
 
371
            ;
 
372
    }
 
373
}
 
374
 
 
375
/* The main function of the background daemon */
 
376
static int daemon_proc(const char *host_file, const char *lock_file, int n_slots) {
 
377
    int ret = 1;
 
378
    int lock_fd = -1;
 
379
    struct daemon_data d;
 
380
    time_t clip_time;
 
381
    int error;
 
382
    char machine[64], version[64], stype[128];
 
383
 
 
384
    rs_add_logger(rs_logger_syslog, RS_LOG_DEBUG, NULL, 0);
 
385
 
 
386
    /* Prepare daemon data structure */
 
387
    d.fd = -1;
 
388
    d.hosts = NULL;
 
389
    d.n_slots = n_slots;
 
390
    d.simple_poll = NULL;
 
391
    d.browser = NULL;
 
392
    d.client = NULL;
 
393
    clip_time = time(NULL);
 
394
 
 
395
    rs_log_info("Zeroconf daemon running.\n");
 
396
 
 
397
    /* Open daemon lock file and lock it */
 
398
    if ((lock_fd = open(lock_file, O_RDWR|O_CREAT, 0666)) < 0) {
 
399
        rs_log_crit("open('%s') failed: %s\n", lock_file, strerror(errno));
 
400
        goto finish;
 
401
    }
 
402
 
 
403
    if (generic_lock(lock_fd, 1, 1, 0) < 0) {
 
404
        /* lock failed, there's probably already another daemon running */
 
405
        goto finish;
 
406
    }
 
407
 
 
408
    /* Open host file */
 
409
    if ((d.fd = open(host_file, O_RDWR|O_CREAT, 0666)) < 0) {
 
410
        rs_log_crit("open('%s') failed: %s\n", host_file, strerror(errno));
 
411
        goto finish;
 
412
    }
 
413
 
 
414
    /* Clear host file */
 
415
    write_hosts(&d);
 
416
 
 
417
    if (!(d.simple_poll = avahi_simple_poll_new())) {
 
418
        rs_log_crit("Failed to create simple poll object.\n");
 
419
        goto finish;
 
420
    }
 
421
 
 
422
    if (!(d.client = avahi_client_new(
 
423
                  avahi_simple_poll_get(d.simple_poll),
 
424
                  0,
 
425
                  client_callback,
 
426
                  &d,
 
427
                  &error))) {
 
428
        rs_log_crit("Failed to create Avahi client object: %s\n", avahi_strerror(error));
 
429
        goto finish;
 
430
    }
 
431
 
 
432
    if (dcc_get_gcc_version(version, sizeof(version)) &&
 
433
        dcc_get_gcc_machine(machine, sizeof(machine))) {
 
434
 
 
435
        dcc_make_dnssd_subtype(stype, sizeof(stype), version, machine);
 
436
    } else {
 
437
        rs_log_warning("Warning, failed to get CC version and machine type.\n");
 
438
 
 
439
        strncpy(stype, DCC_DNS_SERVICE_TYPE, sizeof(stype));
 
440
        stype[sizeof(stype)-1] = 0;
 
441
    }
 
442
 
 
443
    rs_log_info("Browsing for '%s'.\n", stype);
 
444
 
 
445
    if (!(d.browser = avahi_service_browser_new(
 
446
                  d.client,
 
447
                  AVAHI_IF_UNSPEC,
 
448
                  AVAHI_PROTO_UNSPEC,
 
449
                  stype,
 
450
                  NULL,
 
451
                  0,
 
452
                  browse_reply,
 
453
                  &d))) {
 
454
        rs_log_crit("Failed to create service browser object: %s\n", avahi_strerror(avahi_client_errno(d.client)));
 
455
        goto finish;
 
456
    }
 
457
 
 
458
    /* Check whether the host file has been used recently */
 
459
    while (fd_last_used(d.fd, clip_time) <= MAX_IDLE_TIME) {
 
460
 
 
461
        /* Iterate the main loop for 5s */
 
462
        if (avahi_simple_poll_iterate(d.simple_poll, 5000) != 0) {
 
463
            rs_log_crit("Event loop exited abnormaly.\n");
 
464
            goto finish;
 
465
        }
 
466
    }
 
467
 
 
468
    /* Wer are idle */
 
469
    rs_log_info("Zeroconf daemon unused.\n");
 
470
 
 
471
    ret = 0;
 
472
 
 
473
finish:
 
474
 
 
475
    /* Cleanup */
 
476
    if (lock_fd >= 0) {
 
477
        generic_lock(lock_fd, 1, 0, 0);
 
478
        close(lock_fd);
 
479
    }
 
480
 
 
481
    if (d.fd >= 0)
 
482
        close(d.fd);
 
483
 
 
484
    while (d.hosts) {
 
485
        struct host *h = d.hosts;
 
486
        d.hosts = d.hosts->next;
 
487
        free_host(h);
 
488
    }
 
489
 
 
490
    if (d.client)
 
491
        avahi_client_free(d.client);
 
492
 
 
493
    if (d.simple_poll)
 
494
        avahi_simple_poll_free(d.simple_poll);
 
495
 
 
496
    rs_log_info("zeroconf daemon ended.\n");
 
497
 
 
498
    return ret;
 
499
}
 
500
 
 
501
/* Return path to the zeroconf directory in ~/.distcc */
 
502
static int get_zeroconf_dir(char **dir_ret) {
 
503
    static char *cached;
 
504
    int ret;
 
505
 
 
506
    if (cached) {
 
507
        *dir_ret = cached;
 
508
        return 0;
 
509
    } else {
 
510
        ret = dcc_get_subdir("zeroconf", dir_ret);
 
511
        if (ret == 0)
 
512
            cached = *dir_ret;
 
513
        return ret;
 
514
    }
 
515
}
 
516
 
 
517
/* Get the host list from zeroconf */
 
518
int dcc_zeroconf_add_hosts(struct dcc_hostdef **ret_list, int *ret_nhosts, int n_slots, struct dcc_hostdef **ret_prev) {
 
519
    char *host_file = NULL, *lock_file = NULL, *s = NULL;
 
520
    int lock_fd = -1, host_fd = -1;
 
521
    int fork_daemon = 0;
 
522
    int r = -1;
 
523
    char *dir;
 
524
    struct stat st;
 
525
 
 
526
    if (get_zeroconf_dir(&dir) != 0) {
 
527
        rs_log_crit("failed to get zeroconf dir.\n");
 
528
        goto finish;
 
529
    }
 
530
 
 
531
    lock_file = malloc(strlen(dir) + sizeof("/lock"));
 
532
    assert(lock_file);
 
533
    sprintf(lock_file, "%s/lock", dir);
 
534
 
 
535
    host_file = malloc(strlen(dir) + sizeof("/hosts"));
 
536
    assert(host_file);
 
537
    sprintf(host_file, "%s/hosts", dir);
 
538
 
 
539
    /* Open lock file */
 
540
    if ((lock_fd = open(lock_file, O_RDWR|O_CREAT, 0666)) < 0) {
 
541
        rs_log_crit("open('%s') failed: %s\n", lock_file, strerror(errno));
 
542
        goto finish;
 
543
    }
 
544
 
 
545
    /* Try to lock the lock file */
 
546
    if (generic_lock(lock_fd, 1, 1, 0) >= 0) {
 
547
        /* The lock succeeded => there's no daemon running yet! */
 
548
        fork_daemon = 1;
 
549
        generic_lock(lock_fd, 1, 0, 0);
 
550
    }
 
551
 
 
552
    close(lock_fd);
 
553
 
 
554
    /* Shall we fork a new daemon? */
 
555
    if (fork_daemon) {
 
556
        pid_t pid;
 
557
 
 
558
        rs_log_info("Spawning zeroconf daemon.\n");
 
559
 
 
560
        if ((pid = fork()) == -1) {
 
561
            rs_log_crit("fork() failed: %s\n", strerror(errno));
 
562
            goto finish;
 
563
        } else if (pid == 0) {
 
564
            int fd;
 
565
            /* Child */
 
566
 
 
567
            /* Close file descriptors and replace them by /dev/null */
 
568
            close(0);
 
569
            close(1);
 
570
            close(2);
 
571
            fd = open("/dev/null", O_RDWR);
 
572
            assert(fd == 0);
 
573
            fd = dup(0);
 
574
            assert(fd == 1);
 
575
            fd = dup(0);
 
576
            assert(fd == 2);
 
577
 
 
578
#ifdef HAVE_SETSID
 
579
            setsid();
 
580
#endif
 
581
 
 
582
            chdir("/");
 
583
            rs_add_logger(rs_logger_syslog, RS_LOG_DEBUG, NULL, 0);
 
584
            _exit(daemon_proc(host_file, lock_file, n_slots));
 
585
        }
 
586
 
 
587
        /* Parent */
 
588
 
 
589
        /* Wait some time for initial host gathering */
 
590
        usleep(1000000);         /* 1000 ms */
 
591
    }
 
592
 
 
593
    /* Open host list read-only */
 
594
    if ((host_fd = open(host_file, O_RDONLY)) < 0) {
 
595
        rs_log_crit("open('%s') failed: %s\n", host_file, strerror(errno));
 
596
        goto finish;
 
597
    }
 
598
 
 
599
    /* A read lock */
 
600
    if (generic_lock(host_fd, 0, 1, 1) < 0) {
 
601
        rs_log_crit("lock failed: %s\n", strerror(errno));
 
602
        goto finish;
 
603
    }
 
604
 
 
605
    /* Get file size */
 
606
    if (fstat(host_fd, &st) < 0) {
 
607
        rs_log_crit("stat() failed: %s\n", strerror(errno));
 
608
        goto finish;
 
609
    }
 
610
 
 
611
    if (st.st_size >= MAX_FILE_SIZE) {
 
612
        rs_log_crit("file too large.\n");
 
613
        goto finish;
 
614
    }
 
615
 
 
616
    /* read file data */
 
617
    s = malloc((size_t) st.st_size+1);
 
618
    assert(s);
 
619
 
 
620
    if (dcc_readx(host_fd, s, (size_t) st.st_size) != 0) {
 
621
        rs_log_crit("failed to read from file.\n");
 
622
        goto finish;
 
623
    }
 
624
    s[st.st_size] = 0;
 
625
 
 
626
    /* Parse host data */
 
627
    if (dcc_parse_hosts(s, host_file, ret_list, ret_nhosts, ret_prev) != 0) {
 
628
        rs_log_crit("failed to parse host file.\n");
 
629
        goto finish;
 
630
    }
 
631
 
 
632
    r = 0;
 
633
 
 
634
finish:
 
635
    if (host_fd >= 0) {
 
636
        generic_lock(host_fd, 0, 0, 1);
 
637
        close(host_fd);
 
638
    }
 
639
 
 
640
    free(lock_file);
 
641
    free(host_file);
 
642
    free(s);
 
643
 
 
644
    return r;
 
645
}