diff options
Diffstat (limited to 'networking/udhcp/dhcpc.c')
-rw-r--r-- | networking/udhcp/dhcpc.c | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c new file mode 100644 index 000000000..ae40ec9c2 --- /dev/null +++ b/networking/udhcp/dhcpc.c | |||
@@ -0,0 +1,559 @@ | |||
1 | /* dhcpc.c | ||
2 | * | ||
3 | * udhcp DHCP client | ||
4 | * | ||
5 | * Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include <stdio.h> | ||
23 | #include <sys/time.h> | ||
24 | #include <sys/types.h> | ||
25 | #include <sys/file.h> | ||
26 | #include <unistd.h> | ||
27 | #include <getopt.h> | ||
28 | #include <stdlib.h> | ||
29 | #include <sys/socket.h> | ||
30 | #include <netinet/in.h> | ||
31 | #include <arpa/inet.h> | ||
32 | #include <signal.h> | ||
33 | #include <time.h> | ||
34 | #include <string.h> | ||
35 | #include <sys/ioctl.h> | ||
36 | #include <net/if.h> | ||
37 | #include <errno.h> | ||
38 | |||
39 | #include "dhcpd.h" | ||
40 | #include "dhcpc.h" | ||
41 | #include "options.h" | ||
42 | #include "clientpacket.h" | ||
43 | #include "packet.h" | ||
44 | #include "script.h" | ||
45 | #include "socket.h" | ||
46 | #include "debug.h" | ||
47 | #include "pidfile.h" | ||
48 | |||
49 | static int state; | ||
50 | static unsigned long requested_ip; /* = 0 */ | ||
51 | static unsigned long server_addr; | ||
52 | static unsigned long timeout; | ||
53 | static int packet_num; /* = 0 */ | ||
54 | static int fd; | ||
55 | static int signal_pipe[2]; | ||
56 | |||
57 | #define LISTEN_NONE 0 | ||
58 | #define LISTEN_KERNEL 1 | ||
59 | #define LISTEN_RAW 2 | ||
60 | static int listen_mode; | ||
61 | |||
62 | #define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script" | ||
63 | |||
64 | struct client_config_t client_config = { | ||
65 | /* Default options. */ | ||
66 | abort_if_no_lease: 0, | ||
67 | foreground: 0, | ||
68 | quit_after_lease: 0, | ||
69 | background_if_no_lease: 0, | ||
70 | interface: "eth0", | ||
71 | pidfile: NULL, | ||
72 | script: DEFAULT_SCRIPT, | ||
73 | clientid: NULL, | ||
74 | hostname: NULL, | ||
75 | ifindex: 0, | ||
76 | arp: "\0\0\0\0\0\0", /* appease gcc-3.0 */ | ||
77 | }; | ||
78 | |||
79 | #ifndef BB_VER | ||
80 | static void show_usage(void) | ||
81 | { | ||
82 | printf( | ||
83 | "Usage: udhcpc [OPTIONS]\n\n" | ||
84 | " -c, --clientid=CLIENTID Client identifier\n" | ||
85 | " -H, --hostname=HOSTNAME Client hostname\n" | ||
86 | " -h Alias for -H\n" | ||
87 | " -f, --foreground Do not fork after getting lease\n" | ||
88 | " -b, --background Fork to background if lease cannot be\n" | ||
89 | " immediately negotiated.\n" | ||
90 | " -i, --interface=INTERFACE Interface to use (default: eth0)\n" | ||
91 | " -n, --now Exit with failure if lease cannot be\n" | ||
92 | " immediately negotiated.\n" | ||
93 | " -p, --pidfile=file Store process ID of daemon in file\n" | ||
94 | " -q, --quit Quit after obtaining lease\n" | ||
95 | " -r, --request=IP IP address to request (default: none)\n" | ||
96 | " -s, --script=file Run file at dhcp events (default:\n" | ||
97 | " " DEFAULT_SCRIPT ")\n" | ||
98 | " -v, --version Display version\n" | ||
99 | ); | ||
100 | exit(0); | ||
101 | } | ||
102 | #endif | ||
103 | |||
104 | |||
105 | /* just a little helper */ | ||
106 | static void change_mode(int new_mode) | ||
107 | { | ||
108 | DEBUG(LOG_INFO, "entering %s listen mode", | ||
109 | new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none"); | ||
110 | close(fd); | ||
111 | fd = -1; | ||
112 | listen_mode = new_mode; | ||
113 | } | ||
114 | |||
115 | |||
116 | /* perform a renew */ | ||
117 | static void perform_renew(void) | ||
118 | { | ||
119 | LOG(LOG_INFO, "Performing a DHCP renew"); | ||
120 | switch (state) { | ||
121 | case RENEWING: | ||
122 | run_script(NULL, "deconfig"); | ||
123 | case BOUND: | ||
124 | case REBINDING: | ||
125 | change_mode(LISTEN_KERNEL); | ||
126 | state = RENEW_REQUESTED; | ||
127 | break; | ||
128 | case RENEW_REQUESTED: | ||
129 | case REQUESTING: | ||
130 | case RELEASED: | ||
131 | change_mode(LISTEN_RAW); | ||
132 | state = INIT_SELECTING; | ||
133 | break; | ||
134 | case INIT_SELECTING: | ||
135 | } | ||
136 | |||
137 | /* start things over */ | ||
138 | packet_num = 0; | ||
139 | |||
140 | /* Kill any timeouts because the user wants this to hurry along */ | ||
141 | timeout = 0; | ||
142 | } | ||
143 | |||
144 | |||
145 | /* perform a release */ | ||
146 | static void perform_release(void) | ||
147 | { | ||
148 | char buffer[16]; | ||
149 | struct in_addr temp_addr; | ||
150 | |||
151 | /* send release packet */ | ||
152 | if (state == BOUND || state == RENEWING || state == REBINDING) { | ||
153 | temp_addr.s_addr = server_addr; | ||
154 | sprintf(buffer, "%s", inet_ntoa(temp_addr)); | ||
155 | temp_addr.s_addr = requested_ip; | ||
156 | LOG(LOG_INFO, "Unicasting a release of %s to %s", | ||
157 | inet_ntoa(temp_addr), buffer); | ||
158 | send_release(server_addr, requested_ip); /* unicast */ | ||
159 | run_script(NULL, "deconfig"); | ||
160 | } | ||
161 | LOG(LOG_INFO, "Entering released state"); | ||
162 | |||
163 | change_mode(LISTEN_NONE); | ||
164 | state = RELEASED; | ||
165 | timeout = 0x7fffffff; | ||
166 | } | ||
167 | |||
168 | |||
169 | /* Exit and cleanup */ | ||
170 | static void exit_client(int retval) | ||
171 | { | ||
172 | pidfile_delete(client_config.pidfile); | ||
173 | CLOSE_LOG(); | ||
174 | exit(retval); | ||
175 | } | ||
176 | |||
177 | |||
178 | /* Signal handler */ | ||
179 | static void signal_handler(int sig) | ||
180 | { | ||
181 | if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) { | ||
182 | LOG(LOG_ERR, "Could not send signal: %s", | ||
183 | strerror(errno)); | ||
184 | } | ||
185 | } | ||
186 | |||
187 | |||
188 | static void background(void) | ||
189 | { | ||
190 | int pid_fd; | ||
191 | |||
192 | pid_fd = pidfile_acquire(client_config.pidfile); /* hold lock during fork. */ | ||
193 | while (pid_fd >= 0 && pid_fd < 3) pid_fd = dup(pid_fd); /* don't let daemon close it */ | ||
194 | if (daemon(0, 0) == -1) { | ||
195 | perror("fork"); | ||
196 | exit_client(1); | ||
197 | } | ||
198 | client_config.foreground = 1; /* Do not fork again. */ | ||
199 | pidfile_write_release(pid_fd); | ||
200 | } | ||
201 | |||
202 | |||
203 | #ifdef COMBINED_BINARY | ||
204 | int udhcpc_main(int argc, char *argv[]) | ||
205 | #else | ||
206 | int main(int argc, char *argv[]) | ||
207 | #endif | ||
208 | { | ||
209 | unsigned char *temp, *message; | ||
210 | unsigned long t1 = 0, t2 = 0, xid = 0; | ||
211 | unsigned long start = 0, lease; | ||
212 | fd_set rfds; | ||
213 | int retval; | ||
214 | struct timeval tv; | ||
215 | int c, len; | ||
216 | struct dhcpMessage packet; | ||
217 | struct in_addr temp_addr; | ||
218 | int pid_fd; | ||
219 | time_t now; | ||
220 | int max_fd; | ||
221 | int sig; | ||
222 | |||
223 | static struct option arg_options[] = { | ||
224 | {"clientid", required_argument, 0, 'c'}, | ||
225 | {"foreground", no_argument, 0, 'f'}, | ||
226 | {"background", no_argument, 0, 'b'}, | ||
227 | {"hostname", required_argument, 0, 'H'}, | ||
228 | {"hostname", required_argument, 0, 'h'}, | ||
229 | {"interface", required_argument, 0, 'i'}, | ||
230 | {"now", no_argument, 0, 'n'}, | ||
231 | {"pidfile", required_argument, 0, 'p'}, | ||
232 | {"quit", no_argument, 0, 'q'}, | ||
233 | {"request", required_argument, 0, 'r'}, | ||
234 | {"script", required_argument, 0, 's'}, | ||
235 | {"version", no_argument, 0, 'v'}, | ||
236 | {"help", no_argument, 0, '?'}, | ||
237 | {0, 0, 0, 0} | ||
238 | }; | ||
239 | |||
240 | /* get options */ | ||
241 | while (1) { | ||
242 | int option_index = 0; | ||
243 | c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:s:v", arg_options, &option_index); | ||
244 | if (c == -1) break; | ||
245 | |||
246 | switch (c) { | ||
247 | case 'c': | ||
248 | len = strlen(optarg) > 255 ? 255 : strlen(optarg); | ||
249 | if (client_config.clientid) free(client_config.clientid); | ||
250 | client_config.clientid = xmalloc(len + 2); | ||
251 | client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID; | ||
252 | client_config.clientid[OPT_LEN] = len; | ||
253 | client_config.clientid[OPT_DATA] = '\0'; | ||
254 | strncpy(client_config.clientid + OPT_DATA, optarg, len); | ||
255 | break; | ||
256 | case 'f': | ||
257 | client_config.foreground = 1; | ||
258 | break; | ||
259 | case 'b': | ||
260 | client_config.background_if_no_lease = 1; | ||
261 | break; | ||
262 | case 'h': | ||
263 | case 'H': | ||
264 | len = strlen(optarg) > 255 ? 255 : strlen(optarg); | ||
265 | if (client_config.hostname) free(client_config.hostname); | ||
266 | client_config.hostname = xmalloc(len + 2); | ||
267 | client_config.hostname[OPT_CODE] = DHCP_HOST_NAME; | ||
268 | client_config.hostname[OPT_LEN] = len; | ||
269 | strncpy(client_config.hostname + 2, optarg, len); | ||
270 | break; | ||
271 | case 'i': | ||
272 | client_config.interface = optarg; | ||
273 | break; | ||
274 | case 'n': | ||
275 | client_config.abort_if_no_lease = 1; | ||
276 | break; | ||
277 | case 'p': | ||
278 | client_config.pidfile = optarg; | ||
279 | break; | ||
280 | case 'q': | ||
281 | client_config.quit_after_lease = 1; | ||
282 | break; | ||
283 | case 'r': | ||
284 | requested_ip = inet_addr(optarg); | ||
285 | break; | ||
286 | case 's': | ||
287 | client_config.script = optarg; | ||
288 | break; | ||
289 | case 'v': | ||
290 | printf("udhcpcd, version %s\n\n", VERSION); | ||
291 | exit_client(0); | ||
292 | break; | ||
293 | default: | ||
294 | show_usage(); | ||
295 | } | ||
296 | } | ||
297 | |||
298 | OPEN_LOG("udhcpc"); | ||
299 | LOG(LOG_INFO, "udhcp client (v%s) started", VERSION); | ||
300 | |||
301 | pid_fd = pidfile_acquire(client_config.pidfile); | ||
302 | pidfile_write_release(pid_fd); | ||
303 | |||
304 | if (read_interface(client_config.interface, &client_config.ifindex, | ||
305 | NULL, client_config.arp) < 0) | ||
306 | exit_client(1); | ||
307 | |||
308 | if (!client_config.clientid) { | ||
309 | client_config.clientid = xmalloc(6 + 3); | ||
310 | client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID; | ||
311 | client_config.clientid[OPT_LEN] = 7; | ||
312 | client_config.clientid[OPT_DATA] = 1; | ||
313 | memcpy(client_config.clientid + 3, client_config.arp, 6); | ||
314 | } | ||
315 | |||
316 | /* setup signal handlers */ | ||
317 | socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe); | ||
318 | signal(SIGUSR1, signal_handler); | ||
319 | signal(SIGUSR2, signal_handler); | ||
320 | signal(SIGTERM, signal_handler); | ||
321 | |||
322 | state = INIT_SELECTING; | ||
323 | run_script(NULL, "deconfig"); | ||
324 | change_mode(LISTEN_RAW); | ||
325 | |||
326 | for (;;) { | ||
327 | |||
328 | tv.tv_sec = timeout - time(0); | ||
329 | tv.tv_usec = 0; | ||
330 | FD_ZERO(&rfds); | ||
331 | |||
332 | if (listen_mode != LISTEN_NONE && fd < 0) { | ||
333 | if (listen_mode == LISTEN_KERNEL) | ||
334 | fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface); | ||
335 | else | ||
336 | fd = raw_socket(client_config.ifindex); | ||
337 | if (fd < 0) { | ||
338 | LOG(LOG_ERR, "FATAL: couldn't listen on socket, %s", strerror(errno)); | ||
339 | exit_client(0); | ||
340 | } | ||
341 | } | ||
342 | if (fd >= 0) FD_SET(fd, &rfds); | ||
343 | FD_SET(signal_pipe[0], &rfds); | ||
344 | |||
345 | if (tv.tv_sec > 0) { | ||
346 | DEBUG(LOG_INFO, "Waiting on select...\n"); | ||
347 | max_fd = signal_pipe[0] > fd ? signal_pipe[0] : fd; | ||
348 | retval = select(max_fd + 1, &rfds, NULL, NULL, &tv); | ||
349 | } else retval = 0; /* If we already timed out, fall through */ | ||
350 | |||
351 | now = time(0); | ||
352 | if (retval == 0) { | ||
353 | /* timeout dropped to zero */ | ||
354 | switch (state) { | ||
355 | case INIT_SELECTING: | ||
356 | if (packet_num < 3) { | ||
357 | if (packet_num == 0) | ||
358 | xid = random_xid(); | ||
359 | |||
360 | /* send discover packet */ | ||
361 | send_discover(xid, requested_ip); /* broadcast */ | ||
362 | |||
363 | timeout = now + ((packet_num == 2) ? 4 : 2); | ||
364 | packet_num++; | ||
365 | } else { | ||
366 | if (client_config.background_if_no_lease) { | ||
367 | LOG(LOG_INFO, "No lease, forking to background."); | ||
368 | background(); | ||
369 | } else if (client_config.abort_if_no_lease) { | ||
370 | LOG(LOG_INFO, "No lease, failing."); | ||
371 | exit_client(1); | ||
372 | } | ||
373 | /* wait to try again */ | ||
374 | packet_num = 0; | ||
375 | timeout = now + 60; | ||
376 | } | ||
377 | break; | ||
378 | case RENEW_REQUESTED: | ||
379 | case REQUESTING: | ||
380 | if (packet_num < 3) { | ||
381 | /* send request packet */ | ||
382 | if (state == RENEW_REQUESTED) | ||
383 | send_renew(xid, server_addr, requested_ip); /* unicast */ | ||
384 | else send_selecting(xid, server_addr, requested_ip); /* broadcast */ | ||
385 | |||
386 | timeout = now + ((packet_num == 2) ? 10 : 2); | ||
387 | packet_num++; | ||
388 | } else { | ||
389 | /* timed out, go back to init state */ | ||
390 | state = INIT_SELECTING; | ||
391 | timeout = now; | ||
392 | packet_num = 0; | ||
393 | change_mode(LISTEN_RAW); | ||
394 | } | ||
395 | break; | ||
396 | case BOUND: | ||
397 | /* Lease is starting to run out, time to enter renewing state */ | ||
398 | state = RENEWING; | ||
399 | change_mode(LISTEN_KERNEL); | ||
400 | DEBUG(LOG_INFO, "Entering renew state"); | ||
401 | /* fall right through */ | ||
402 | case RENEWING: | ||
403 | /* Either set a new T1, or enter REBINDING state */ | ||
404 | if ((t2 - t1) <= (lease / 14400 + 1)) { | ||
405 | /* timed out, enter rebinding state */ | ||
406 | state = REBINDING; | ||
407 | timeout = now + (t2 - t1); | ||
408 | DEBUG(LOG_INFO, "Entering rebinding state"); | ||
409 | } else { | ||
410 | /* send a request packet */ | ||
411 | send_renew(xid, server_addr, requested_ip); /* unicast */ | ||
412 | |||
413 | t1 = (t2 - t1) / 2 + t1; | ||
414 | timeout = t1 + start; | ||
415 | } | ||
416 | break; | ||
417 | case REBINDING: | ||
418 | /* Either set a new T2, or enter INIT state */ | ||
419 | if ((lease - t2) <= (lease / 14400 + 1)) { | ||
420 | /* timed out, enter init state */ | ||
421 | state = INIT_SELECTING; | ||
422 | LOG(LOG_INFO, "Lease lost, entering init state"); | ||
423 | run_script(NULL, "deconfig"); | ||
424 | timeout = now; | ||
425 | packet_num = 0; | ||
426 | change_mode(LISTEN_RAW); | ||
427 | } else { | ||
428 | /* send a request packet */ | ||
429 | send_renew(xid, 0, requested_ip); /* broadcast */ | ||
430 | |||
431 | t2 = (lease - t2) / 2 + t2; | ||
432 | timeout = t2 + start; | ||
433 | } | ||
434 | break; | ||
435 | case RELEASED: | ||
436 | /* yah, I know, *you* say it would never happen */ | ||
437 | timeout = 0x7fffffff; | ||
438 | break; | ||
439 | } | ||
440 | } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) { | ||
441 | /* a packet is ready, read it */ | ||
442 | |||
443 | if (listen_mode == LISTEN_KERNEL) | ||
444 | len = get_packet(&packet, fd); | ||
445 | else len = get_raw_packet(&packet, fd); | ||
446 | |||
447 | if (len == -1 && errno != EINTR) { | ||
448 | DEBUG(LOG_INFO, "error on read, %s, reopening socket", strerror(errno)); | ||
449 | change_mode(listen_mode); /* just close and reopen */ | ||
450 | } | ||
451 | if (len < 0) continue; | ||
452 | |||
453 | if (packet.xid != xid) { | ||
454 | DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)", | ||
455 | (unsigned long) packet.xid, xid); | ||
456 | continue; | ||
457 | } | ||
458 | |||
459 | if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { | ||
460 | DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring"); | ||
461 | continue; | ||
462 | } | ||
463 | |||
464 | switch (state) { | ||
465 | case INIT_SELECTING: | ||
466 | /* Must be a DHCPOFFER to one of our xid's */ | ||
467 | if (*message == DHCPOFFER) { | ||
468 | if ((temp = get_option(&packet, DHCP_SERVER_ID))) { | ||
469 | memcpy(&server_addr, temp, 4); | ||
470 | xid = packet.xid; | ||
471 | requested_ip = packet.yiaddr; | ||
472 | |||
473 | /* enter requesting state */ | ||
474 | state = REQUESTING; | ||
475 | timeout = now; | ||
476 | packet_num = 0; | ||
477 | } else { | ||
478 | DEBUG(LOG_ERR, "No server ID in message"); | ||
479 | } | ||
480 | } | ||
481 | break; | ||
482 | case RENEW_REQUESTED: | ||
483 | case REQUESTING: | ||
484 | case RENEWING: | ||
485 | case REBINDING: | ||
486 | if (*message == DHCPACK) { | ||
487 | if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) { | ||
488 | LOG(LOG_ERR, "No lease time with ACK, using 1 hour lease"); | ||
489 | lease = 60 * 60; | ||
490 | } else { | ||
491 | memcpy(&lease, temp, 4); | ||
492 | lease = ntohl(lease); | ||
493 | } | ||
494 | |||
495 | /* enter bound state */ | ||
496 | t1 = lease / 2; | ||
497 | |||
498 | /* little fixed point for n * .875 */ | ||
499 | t2 = (lease * 0x7) >> 3; | ||
500 | temp_addr.s_addr = packet.yiaddr; | ||
501 | LOG(LOG_INFO, "Lease of %s obtained, lease time %ld", | ||
502 | inet_ntoa(temp_addr), lease); | ||
503 | start = now; | ||
504 | timeout = t1 + start; | ||
505 | requested_ip = packet.yiaddr; | ||
506 | run_script(&packet, | ||
507 | ((state == RENEWING || state == REBINDING) ? "renew" : "bound")); | ||
508 | |||
509 | state = BOUND; | ||
510 | change_mode(LISTEN_NONE); | ||
511 | if (client_config.quit_after_lease) | ||
512 | exit_client(0); | ||
513 | if (!client_config.foreground) | ||
514 | background(); | ||
515 | |||
516 | } else if (*message == DHCPNAK) { | ||
517 | /* return to init state */ | ||
518 | LOG(LOG_INFO, "Received DHCP NAK"); | ||
519 | run_script(&packet, "nak"); | ||
520 | if (state != REQUESTING) | ||
521 | run_script(NULL, "deconfig"); | ||
522 | state = INIT_SELECTING; | ||
523 | timeout = now; | ||
524 | requested_ip = 0; | ||
525 | packet_num = 0; | ||
526 | change_mode(LISTEN_RAW); | ||
527 | sleep(3); /* avoid excessive network traffic */ | ||
528 | } | ||
529 | break; | ||
530 | /* case BOUND, RELEASED: - ignore all packets */ | ||
531 | } | ||
532 | } else if (retval > 0 && FD_ISSET(signal_pipe[0], &rfds)) { | ||
533 | if (read(signal_pipe[0], &sig, sizeof(signal)) < 0) { | ||
534 | DEBUG(LOG_ERR, "Could not read signal: %s", | ||
535 | strerror(errno)); | ||
536 | continue; /* probably just EINTR */ | ||
537 | } | ||
538 | switch (sig) { | ||
539 | case SIGUSR1: | ||
540 | perform_renew(); | ||
541 | break; | ||
542 | case SIGUSR2: | ||
543 | perform_release(); | ||
544 | break; | ||
545 | case SIGTERM: | ||
546 | LOG(LOG_INFO, "Received SIGTERM"); | ||
547 | exit_client(0); | ||
548 | } | ||
549 | } else if (retval == -1 && errno == EINTR) { | ||
550 | /* a signal was caught */ | ||
551 | } else { | ||
552 | /* An error occured */ | ||
553 | DEBUG(LOG_ERR, "Error on select"); | ||
554 | } | ||
555 | |||
556 | } | ||
557 | return 0; | ||
558 | } | ||
559 | |||