Projet

Général

Profil

Télécharger (17 ko) Statistiques
| Branche: | Révision:

univnautes-tools / pfPorts / dhcpleases / files / dhcpleases.c @ d2628919

1
/*-
2
 * Copyright (c) 2010 Ermal Lu?i <eri@pfsense.org>
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 *
14
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
 * SUCH DAMAGE.
25
 */
26

    
27

    
28
/* 
29
 * The parsing code is taken from dnsmasq isc.c file and modified to work
30
 * in this code. 
31
 */
32
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
33

    
34
   This program is free software; you can redistribute it and/or modify
35
   it under the terms of the GNU General Public License as published by
36
   the Free Software Foundation; version 2 dated June, 1991, or
37
   (at your option) version 3 dated 29 June, 2007.
38

    
39
   This program is distributed in the hope that it will be useful,
40
   but WITHOUT ANY WARRANTY; without even the implied warranty of
41
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
42
   GNU General Public License for more details.
43

    
44
  You should have received a copy of the GNU General Public License
45
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
46
*/
47
/* Code in this file is based on contributions by John Volpe. */
48

    
49
#include <sys/types.h>
50
#include <sys/socket.h>
51
#include <sys/event.h>
52
#include <sys/time.h>
53
#include <sys/stat.h>
54
#include <sys/queue.h>
55

    
56
#include <netinet/in.h>
57
#include <arpa/nameser.h>
58
#include <arpa/inet.h>
59

    
60
#include <syslog.h>
61
#include <stdarg.h>
62
#include <time.h>
63
#include <signal.h>
64

    
65
#define _WITH_DPRINTF
66
#include <stdio.h>
67
#include <stdlib.h>
68
#include <string.h>
69
#include <fcntl.h>
70
#include <unistd.h>
71

    
72
#include <errno.h>
73

    
74
#define MAXTOK 64
75
#define PIDFILE	"/var/run/dhcpleases.pid"
76

    
77
struct isc_lease {
78
	char *name, *fqdn;
79
	time_t expires;
80
	struct in_addr addr;
81
	LIST_ENTRY(isc_lease) next;
82
};
83

    
84
LIST_HEAD(isc_leases, isc_lease) leases =
85
	LIST_HEAD_INITIALIZER(leases);
86
static char *leasefile = NULL;
87
static char *pidfile = NULL;
88
static char *HOSTS = NULL;
89
static FILE *fp = NULL;
90
static char *domain_suffix = NULL;
91
static char *command = NULL;
92
static size_t hostssize = 0;
93
static size_t foreground = 0;
94

    
95
static int fexist(char *);
96
static int fsize(char *);
97
static int legal_char(char);
98
static int canonicalise(char *);
99
static int hostname_isequal(char *, char *);
100
static int next_token (char *, int, FILE *);
101
static time_t convert_time(struct tm);
102
static int load_dhcp(time_t);
103

    
104
static int write_status(void);
105
static void cleanup(void);
106
static void signal_process(void);
107
static void handle_signal(int);
108

    
109
/* Check if file exists */
110
static int
111
fexist(char * filename)
112
{
113
        struct stat buf;
114

    
115
        if (( stat (filename, &buf)) < 0)
116
                return (0);
117

    
118
        if (! S_ISREG(buf.st_mode))
119
                return (0);
120

    
121
        return(1);
122
}
123

    
124
static int
125
fsize(char * filename)
126
{
127
        struct stat buf;
128

    
129
        if (( stat (filename, &buf)) < 0)
130
                return (-1);
131

    
132
        if (! S_ISREG(buf.st_mode))
133
                return (-1);
134

    
135
        return(buf.st_size);
136
}
137

    
138
/*
139
 * check for legal char a-z A-Z 0-9 -
140
 * (also / , used for RFC2317 and _ used in windows queries
141
 * and space, for DNS-SD stuff)
142
 */
143
static int
144
legal_char(char c) {
145
	if ((c >= 'A' && c <= 'Z') ||
146
	    (c >= 'a' && c <= 'z') ||
147
	    (c >= '0' && c <= '9') ||
148
	    c == '-' || c == '/' || c == '_' || c == ' ')
149
		return (1);
150
	return (0);
151
}
152

    
153
/*
154
 * check for legal chars and remove trailing .
155
 * also fail empty string and label > 63 chars
156
 */
157
static int
158
canonicalise(char *s) {
159
	size_t dotgap = 0, l = strlen(s);
160
	char c;
161
	int nowhite = 0;
162

    
163
	if (l == 0 || l > MAXDNAME)
164
		return (0);
165

    
166
	if (s[l-1] == '.') {
167
		if (l == 1)
168
			return (0);
169
		s[l-1] = 0;
170
	}
171

    
172
	while ((c = *s)) {
173
		if (c == '.')
174
			dotgap = 0;
175
		else if (!legal_char(c) || (++dotgap > MAXLABEL))
176
			return (0);
177
		else if (c != ' ')
178
			nowhite = 1;
179
		s++;
180
	}
181

    
182
	return (nowhite);
183
}
184

    
185
/* don't use strcasecmp and friends here - they may be messed up by LOCALE */
186
static int
187
hostname_isequal(char *a, char *b) {
188
	unsigned int c1, c2;
189

    
190
	do {
191
		c1 = (unsigned char) *a++;
192
		c2 = (unsigned char) *b++;
193

    
194
		if (c1 >= 'A' && c1 <= 'Z')
195
			c1 += 'a' - 'A';
196
		if (c2 >= 'A' && c2 <= 'Z')
197
			c2 += 'a' - 'A';
198

    
199
		if (c1 != c2)
200
			return 0;
201
	} while (c1);
202

    
203
	return (1);
204
}
205

    
206
static int
207
next_token (char *token, int buffsize, FILE * fp)
208
{
209
	int c, count = 0;
210
	char *cp = token;
211

    
212
	while((c = getc(fp)) != EOF) {
213
		if (c == '#')
214
			do {
215
				c = getc(fp);
216
			} while (c != '\n' && c != EOF);
217

    
218
		if (c == ' ' || c == '\t' || c == '\n' || c == ';') {
219
			if (count)
220
				break;
221
		} else if ((c != '"') && (count<buffsize-1)) {
222
			*cp++ = c;
223
			count++;
224
		}
225
	}
226
	*cp = 0;
227
#if DEBUG
228
	printf("|TOEN: %s, %d\n", token, count);
229
#endif
230
	return count ? 1 : 0;
231
}
232

    
233
/*
234
 * There doesn't seem to be a universally available library function
235
 * which converts broken-down _GMT_ time to seconds-in-epoch.
236
 * The following was borrowed from ISC dhcpd sources, where
237
 * it is noted that it might not be entirely accurate for odd seconds.
238
 * Since we're trying to get the same answer as dhcpd, that's just
239
 * fine here.
240
 */
241
static time_t
242
convert_time(struct tm lease_time) {
243
	static const int months [11] = { 31, 59, 90, 120, 151, 181,
244
						212, 243, 273, 304, 334 };
245
	time_t time = ((((((365 * (lease_time.tm_year - 1970) + /* Days in years since '70 */
246
			    (lease_time.tm_year - 1969) / 4 +   /* Leap days since '70 */
247
			    (lease_time.tm_mon > 1		/* Days in months this year */
248
				? months [lease_time.tm_mon - 2]
249
				: 0) +
250
			    (lease_time.tm_mon > 2 &&		/* Leap day this year */
251
			    !((lease_time.tm_year - 1972) & 3)) +
252
			    lease_time.tm_mday - 1) * 24) +	/* Day of month */
253
			    lease_time.tm_hour) * 60) +
254
			    lease_time.tm_min) * 60) + lease_time.tm_sec;
255

    
256
	return (time);
257
}
258

    
259
static int
260
load_dhcp(time_t now) {
261
	char namebuff[256];
262
	char *hostname = namebuff, *suffix = NULL;
263
	char token[MAXTOK], *dot;
264
	struct in_addr host_address;
265
	time_t ttd, tts;
266
	struct isc_lease *lease, *tmp;
267

    
268
	rewind(fp);
269
	LIST_INIT(&leases);
270

    
271
	while ((next_token(token, MAXTOK, fp))) {
272
		if (strcmp(token, "lease") == 0) {
273
			*hostname = 0;
274
			ttd = tts = (time_t)(-1);
275
			if (next_token(token, MAXTOK, fp) && 
276
			    (inet_pton(AF_INET, token, &host_address))) {
277
				if (next_token(token, MAXTOK, fp) && *token == '{') {
278
					while (next_token(token, MAXTOK, fp) && *token != '}') {
279
#if DEBUG
280
						printf("token: %s\n", token);
281
#endif
282
						if ((strcmp(token, "client-hostname") == 0) ||
283
						    (strcmp(token, "hostname") == 0)) {
284
							if (next_token(hostname, MAXDNAME, fp)) {
285
								if (*hostname == '}') {
286
									*hostname = 0;
287
								} else if (!canonicalise(hostname)) {
288
									if (foreground)
289
										printf("bad name(%s) in %s\n", hostname, leasefile);
290
									else
291
										syslog(LOG_ERR, "bad name in %s", leasefile); 
292
									*hostname = 0;
293
								}
294
							}
295
						} else if ((strcmp(token, "ends") == 0) ||
296
							    (strcmp(token, "starts") == 0)) {
297
								struct tm lease_time;
298
								int is_ends = (strcmp(token, "ends") == 0);
299
								if (next_token(token, MAXTOK, fp) &&  /* skip weekday */
300
								    next_token(token, MAXTOK, fp) &&  /* Get date from lease file */
301
								    sscanf (token, "%d/%d/%d", 
302
									&lease_time.tm_year,
303
									&lease_time.tm_mon,
304
									&lease_time.tm_mday) == 3 &&
305
								    next_token(token, MAXTOK, fp) &&
306
								    sscanf (token, "%d:%d:%d:", 
307
									&lease_time.tm_hour,
308
									&lease_time.tm_min, 
309
									&lease_time.tm_sec) == 3) {
310
									if (is_ends)
311
										ttd = convert_time(lease_time);
312
									else
313
										tts = convert_time(lease_time);
314
								}
315
						}
316
					}
317
				}
318

    
319
				/* missing info? */
320
				if (!*hostname)
321
					continue;
322
				if (ttd == (time_t)(-1))
323
					ttd = (time_t)0;
324

    
325
				/* We use 0 as infinite in ttd */
326
				if ((tts != -1) && (ttd == tts - 1))
327
					ttd = (time_t)0;
328

    
329
				if ((dot = strchr(hostname, '.'))) {
330
					if (!domain_suffix || hostname_isequal(dot+1, domain_suffix)) {
331
						if (foreground)
332
							printf("Other suffix in DHCP lease for %s", hostname);
333
						else
334
							syslog(LOG_WARNING, "Other suffix in DHCP lease for %s", hostname);
335

    
336
						suffix = (dot + 1);
337
						*dot = 0;
338
					} else
339
						suffix = domain_suffix;
340
				} else
341
					suffix = domain_suffix;
342

    
343
				LIST_FOREACH(lease, &leases, next) {
344
					if (hostname_isequal(lease->name, hostname)) {
345
						lease->expires = ttd;
346
						lease->addr = host_address;
347
						break;
348
					}
349
				}
350

    
351
				if (!lease) {
352
					if ((lease = malloc(sizeof(struct isc_lease))) == NULL)
353
						continue;
354
					lease->expires = ttd;
355
					lease->addr = host_address;
356
					lease->fqdn = NULL;
357
					lease->name = NULL;
358
					LIST_INSERT_HEAD(&leases, lease, next); 
359
				} else if (lease->fqdn != NULL)
360
						free(lease->fqdn);
361

    
362
				if (foreground)
363
					printf("Found hostname: %s.%s\n", hostname, suffix);
364

    
365
				if (asprintf(&lease->name, "%s", hostname) < 0) {
366
					LIST_REMOVE(lease, next);
367
					if (lease->name != NULL)
368
						free(lease->name);
369
					if (lease->fqdn != NULL)
370
						free(lease->fqdn);
371
					free(lease);
372
				}
373
				if (asprintf(&lease->fqdn, "%s.%s", hostname, suffix) < 0) {
374
					LIST_REMOVE(lease, next);
375
					if (lease->name != NULL)
376
						free(lease->name);
377
					if (lease->fqdn != NULL)
378
						free(lease->fqdn);
379
					free(lease);
380
				}
381
			}
382
		}
383
	}
384
 
385
	/* prune expired leases */
386
	LIST_FOREACH_SAFE(lease, &leases, next, tmp) {
387
		if (lease->expires != (time_t)0 && difftime(now, lease->expires) > 0) {
388
			if (lease->name)
389
				free(lease->name);
390
			if (lease->fqdn)
391
				free(lease->fqdn);
392
			LIST_REMOVE(lease, next);
393
			free(lease);
394
		}
395
	}
396

    
397
	return (0);
398
}
399

    
400
static int
401
write_status() {
402
	struct isc_lease *lease;
403
	struct stat tmp;
404
	size_t tmpsize;
405
	int fd;
406
	
407
	fd = open(HOSTS, O_RDWR | O_CREAT | O_FSYNC);
408
        if (fd < 0)
409
		return 1;
410
	if (fstat(fd, &tmp) < 0)
411
		tmpsize = hostssize;
412
	else
413
		tmpsize = tmp.st_size;
414
	if (tmpsize < hostssize) {
415
		if (foreground)
416
			printf("%s changed size from original!", HOSTS);
417
		else
418
			syslog(LOG_WARNING, "%s changed size from original!", HOSTS);
419
		hostssize = tmpsize;
420
	}
421
	ftruncate(fd, hostssize);
422
	if (lseek(fd, 0, SEEK_END) < 0) {
423
		close(fd);
424
		return 2;
425
	}
426
	/* write the tmp hosts file */
427
	dprintf(fd, "\n# dhcpleases automatically entered\n"); /* put a blank line just to be on safe side */
428
	LIST_FOREACH(lease, &leases, next) {
429
		if (foreground)
430
			printf("%s\t%s %s\t\t# dynamic entry from dhcpd.leases\n", inet_ntoa(lease->addr),
431
				lease->fqdn ? lease->fqdn  : "empty", lease->name ? lease->name : "empty");
432
		else
433
			dprintf(fd, "%s\t%s %s\t\t# dynamic entry from dhcpd.leases\n", inet_ntoa(lease->addr),
434
				lease->fqdn ? lease->fqdn  : "empty", lease->name ? lease->name : "empty");
435
	}
436
	close(fd);
437

    
438
	return (0);
439
}
440

    
441
static void
442
cleanup() {
443
	struct isc_lease *lease, *tmp;
444

    
445
	LIST_FOREACH_SAFE(lease, &leases, next, tmp) {
446
		if (lease->fqdn)
447
			free(lease->fqdn);
448
		if (lease->name)
449
			free(lease->name);
450
		LIST_REMOVE(lease, next);
451
		free(lease);
452
	}
453

    
454
	return;
455
}
456

    
457
static void
458
signal_process() {
459
	FILE *fd;
460
	size_t size = 0;
461
	char *pid = NULL, *pc;
462
	int c, pidno;
463

    
464
	if (pidfile == NULL)
465
		goto error;
466
	size = fsize(pidfile);
467
	if (size < 0)
468
		goto error;
469

    
470
	fd = fopen(pidfile, "r");
471
	if (fd == NULL)
472
		goto error;
473

    
474
	pid = calloc(size, size);
475
	if (pid == NULL) {
476
		fclose(fd);
477
		goto error;
478
	}
479
	pc = pid;
480
	while ((c = getc(fd)) != EOF) {
481
		if (c == '\n')
482
			break;
483
		*pc++ = c;
484
	}
485
	fclose(fd);
486

    
487
	pidno = atoi(pid);
488
	free(pid);
489

    
490
	syslog(LOG_INFO, "Sending HUP signal to dns daemon(%u)", pidno);
491
	if (kill((pid_t)pidno, SIGHUP) < 0)
492
		goto error;
493

    
494
	return;
495
error:
496
	syslog(LOG_ERR, "Could not deliver signal HUP to process because its pidfile does not exist, %m.");
497
	return;
498
}
499

    
500
static void
501
handle_signal(int sig) {
502
	size_t size;
503

    
504
        switch(sig) {
505
        case SIGHUP:
506
		size = fsize(HOSTS);
507
		if (hostssize < 0)
508
			break; /* XXX: exit?! */
509
		else
510
			hostssize = size;
511
                break;
512
        case SIGTERM:
513
		unlink(PIDFILE);
514
		cleanup();
515
                exit(0);
516
                break;
517
        default:
518
                syslog(LOG_WARNING, "unhandled signal");
519
        }
520
}
521

    
522
int
523
main(int argc, char **argv) {
524
	struct kevent evlist;    /* events we want to monitor */
525
	struct kevent chlist;    /* events that were triggered */
526
	struct sigaction sa;
527
	time_t	now;
528
	int kq, nev, leasefd = 0, pidf, ch;
529

    
530
	if (argc != 5) {
531
	}
532

    
533
	while ((ch = getopt(argc, argv, "c:d:fp:h:l:")) != -1) {
534
		switch (ch) {
535
		case 'c':
536
			command = optarg;
537
			break;
538
		case 'd':
539
			domain_suffix = optarg;
540
			break;
541
		case 'f':
542
			foreground = 1;
543
			break;
544
		case 'p':
545
			pidfile = optarg;
546
			break;
547
		case 'h':
548
			HOSTS = optarg;
549
			break;
550
		case 'l':
551
			leasefile = optarg;
552
			break;
553
		default:
554
			perror("Wrong number of arguments given."); /* XXX: usage */
555
			exit(2);
556
			/* NOTREACHED */
557
		}
558
	}
559
	argc -= optind;
560
	argv += optind;
561

    
562
	if (leasefile == NULL) {
563
		syslog(LOG_ERR, "lease file is mandatory as parameter");	
564
		perror("lease file is mandatory as parameter");	
565
		exit(1);
566
	}
567
	if (!fexist(leasefile)) {
568
		syslog(LOG_ERR, "lease file needs to exist before starting dhcpleases");	
569
		perror("lease file needs to exist before starting dhcpleases");	
570
		exit(1);
571
	}
572
	if (domain_suffix == NULL) {
573
		syslog(LOG_ERR, "a domain suffix is not passed as argument using 'local' as suffix");
574
		domain_suffix = "local";
575
	}
576

    
577
	if (pidfile == NULL && !foreground) {
578
		syslog(LOG_ERR, "pidfile argument not passed it is mandatory");
579
		perror("pidfile argument not passed it is mandatory");
580
		exit(1);
581
	}
582

    
583
	if (!foreground) {
584
		if (HOSTS == NULL) {
585
			syslog(LOG_ERR, "You need to specify the hosts file path.");
586
			perror("You need to specify the hosts file path.");
587
			exit(8);
588
		}
589
		if (!fexist(HOSTS)) {
590
			syslog(LOG_ERR, "Hosts file %s does not exist!", HOSTS);
591
			perror("Hosts file passed as parameter does not exist");
592
			exit(8);
593
		}
594

    
595
		if ((hostssize = fsize(HOSTS)) < 0) {
596
			syslog(LOG_ERR, "Error while getting %s file size.", HOSTS);
597
			perror("Error while getting /etc/hosts file size.");
598
			exit(6);
599
		}
600

    
601
		closefrom(3);
602

    
603
		if (daemon(0, 0) < 0) {
604
			syslog(LOG_ERR, "Could not daemonize");
605
			perror("Could not daemonize");
606
			exit(4);
607
		}
608
	}
609

    
610
reopen:
611
	leasefd = open(leasefile, O_RDONLY);
612
	if (leasefd < 0) {
613
		syslog(LOG_ERR, "Could not get descriptor");
614
		perror("Could not get descriptor");
615
		exit(6);
616
	}
617

    
618
	fp = fdopen(leasefd, "r");
619
	if (fp == NULL) {
620
		syslog(LOG_ERR, "could not open leases file");
621
		perror("could not open leases file");
622
		exit(5);
623
	}
624

    
625
	if (!foreground) {
626
		pidf = open(PIDFILE, O_RDWR | O_CREAT | O_FSYNC);
627
		if (pidf < 0)
628
			syslog(LOG_ERR, "could not write pid file, %m");
629
		else {
630
			ftruncate(pidf, 0);
631
			dprintf(pidf, "%u\n", getpid());
632
			close(pidf);
633
		}
634

    
635
		/*
636
		 * Catch SIGHUP in order to reread configuration file.
637
		 */
638
		sa.sa_handler = handle_signal;
639
		sa.sa_flags = SA_SIGINFO|SA_RESTART;
640
		sigemptyset(&sa.sa_mask);
641
		if (sigaction(SIGHUP, &sa, NULL) < 0) {
642
			syslog(LOG_ERR, "unable to set signal handler, %m");
643
			exit(9);
644
		}
645
		if (sigaction(SIGTERM, &sa, NULL) < 0) {
646
			syslog(LOG_ERR, "unable to set signal handler, %m");
647
			exit(10);
648
		}
649

    
650
		/* Create a new kernel event queue */
651
		if ((kq = kqueue()) == -1)
652
			exit(1);
653
	}
654

    
655
	now = time(NULL);
656
	if (command == NULL) {
657
		load_dhcp(now);
658

    
659
		write_status();
660
		//syslog(LOG_INFO, "written temp hosts file after modification event.");
661

    
662
		cleanup();
663
		//syslog(LOG_INFO, "Cleaned up.");
664

    
665
		if (!foreground)
666
			signal_process();
667
	}
668

    
669
	if (!foreground) {
670
		/* Initialise kevent structure */
671
		EV_SET(&chlist, leasefd, EVFILT_VNODE, EV_ADD | EV_CLEAR | EV_ENABLE | EV_ONESHOT,
672
			NOTE_WRITE | NOTE_ATTRIB | NOTE_DELETE | NOTE_RENAME | NOTE_LINK, 0, NULL);
673
		/* Loop forever */
674
		for (;;) {
675
			nev = kevent(kq, &chlist, 1, &evlist, 1, NULL);
676
			if (nev == -1) {
677
				syslog(LOG_ERR, "kqueue error: unkown");
678
				close(leasefd);
679
				goto reopen;
680
			} else if (nev > 0) {
681
				if (evlist.flags & EV_ERROR) {
682
					syslog(LOG_ERR, "EV_ERROR: %s\n", strerror(evlist.data));
683
					close(leasefd);
684
					goto reopen;
685
				}
686
				if ((evlist.fflags & NOTE_DELETE) || (evlist.fflags & NOTE_RENAME)) {
687
					close(leasefd);
688
					goto reopen;
689
				}
690
				now = time(NULL);
691
				if (command != NULL)
692
					system(command);
693
				else {
694
					load_dhcp(now);
695

    
696
					write_status();
697
					//syslog(LOG_INFO, "written temp hosts file after modification event.");
698

    
699
					cleanup();
700
					//syslog(LOG_INFO, "Cleaned up.");
701

    
702
					signal_process();
703
				}
704
			}
705
		}
706
	}
707

    
708
	fclose(fp);
709
	unlink(PIDFILE);
710
	return (0);
711
}
    (1-1/1)