]> arthur.barton.de Git - netatalk.git/blob - etc/atalkd/config.c
Added checking for USE_CRACKLIB
[netatalk.git] / etc / atalkd / config.c
1 /*
2  * Copyright (c) 1990,1993 Regents of The University of Michigan.
3  * All Rights Reserved. See COPYRIGHT.
4  */
5
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <sys/socket.h>
9 #include <sys/ioctl.h>
10 #include <syslog.h>
11 #include <sys/param.h>
12 #include <net/if.h>
13 #include <netatalk/at.h>
14 #include <netatalk/endian.h>
15 #include <atalk/paths.h>
16 #include <atalk/util.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <ctype.h>
21 #include <fcntl.h>
22 #include <errno.h>
23
24 #ifdef __svr4__
25 #include <sys/sockio.h>
26 #include <sys/stropts.h>
27 #endif __svr4__
28
29 #include "interface.h"
30 #include "multicast.h"
31 #include "rtmp.h"
32 #include "zip.h"
33 #include "list.h"
34
35 #ifndef IFF_SLAVE /* a little backward compatibility */
36 #define IFF_SLAVE 0
37 #endif
38
39 int     router(), dontroute(), seed(), phase(), net(), addr(), zone();
40
41 static struct param {
42     char        *p_name;
43     int         (*p_func)();
44 } params[] = {
45     { "router", router },
46     { "dontroute", dontroute },
47     { "seed",   seed },
48     { "phase",  phase },
49     { "net",    net },
50     { "addr",   addr },
51     { "zone",   zone },
52 };
53
54 static char **
55 parseline( line )
56     char        *line;
57 {
58     char        *p;
59     int         argc = 0;
60     static char *argv[ 128 ];
61     static char buf[ 1024 ];
62
63     if ( *line == '#' ) {
64         return( NULL );
65     }
66     argc = 0;
67
68     memset(argv, 0, sizeof(argv));
69     strcpy( buf, line );
70     p = buf;
71
72     /*
73      * This parser should be made more powerful -- it should
74      * handle various escapes, e.g. \" and \031.
75      */
76     while ( *p != '\0' ) {
77         while ( *p != '\0' && *p != '"' && isspace( *p )) {
78             p++;
79         }
80         if ( *p == '\0' ) {
81             argv[ argc ] = 0;
82             break;
83         }
84         if ( *p == '"' ) {
85             argv[ argc ] = ++p;
86             while ( *p != '\0' && *p != '"' ) {
87                 p++;
88             }
89         } else {
90             argv[ argc ] = p;
91             while ( *p != '\0' && !isspace( *p )) {
92                 p++;
93             }
94         }
95         *p++ = '\0';
96         argc++;
97     }
98     if ( argv[ 0 ] == '\0' || *argv[ 0 ] == '#' ) {
99         return( NULL );
100     }
101     return( argv );
102 }
103
104 writeconf( cf )
105     char        *cf;
106 {
107     struct stat         st;
108     char                *path, *p, newpath[ MAXPATHLEN ], line[ 1024 ];
109     char                **argv;
110     FILE                *conf, *newconf;
111     struct interface    *iface;
112     struct list         *l;
113     int                 mode = 0644, fd;
114
115     if ( cf == NULL ) {
116         path = _PATH_ATALKDCONF;
117     } else {
118         path = cf;
119     }
120
121     /* check if old conf is writable */
122     if ( stat( path, &st ) == 0 ) {
123         if (( st.st_mode & S_IWUSR ) == 0 ) {
124             syslog( LOG_INFO, "%s not writable, won't rewrite", path );
125             return( -1 );
126         }
127          mode = st.st_mode;
128     }
129
130     if (( p = strrchr( path, '/' )) == NULL ) {
131         strcpy( newpath, _PATH_ATALKDTMP );
132     } else {
133         sprintf( newpath, "%.*s/%s", p - path, path, _PATH_ATALKDTMP );
134     }
135     if (( fd = open( newpath, O_WRONLY|O_CREAT|O_TRUNC, mode )) < 0 ) {
136         syslog( LOG_ERR, "%s: %m", newpath );
137         return( -1 );
138     }
139     if (( newconf = fdopen( fd, "w" )) == NULL ) {
140         syslog( LOG_ERR, "fdreopen %s: %m", newpath );
141         return( -1 );
142     }
143
144     if (( conf = fopen( path, "r" )) == NULL && cf ) {
145         syslog( LOG_ERR, "%s: %m", path );
146         return( -1 );
147     }
148
149     iface = interfaces->i_next;
150
151     while ( conf == NULL || fgets( line, sizeof( line ), conf ) != NULL ) {
152         if ( conf != NULL && ( argv = parseline( line )) == NULL ) {
153             if ( fputs( line, newconf ) == EOF ) {
154                 syslog( LOG_ERR, "fputs: %m" );
155                 return( -1 );
156             }
157             continue;
158         }
159
160         /* write real lines */
161         if ( iface ) {
162             fprintf( newconf, "%s", iface->i_name );
163             if ( iface->i_flags & IFACE_RSEED ) {
164                 fprintf( newconf, " -router" );
165             } else if ( iface->i_flags & IFACE_SEED ) {
166                 fprintf( newconf, " -seed" );
167             }
168             if ( iface->i_flags & IFACE_DONTROUTE) {
169                 fprintf( newconf, " -dontroute");
170             }
171
172             fprintf( newconf, " -phase %d",
173                     ( iface->i_flags & IFACE_PHASE1 ) ? 1 : 2 );
174             fprintf( newconf, " -net %d", ntohs( iface->i_rt->rt_firstnet ));
175             if ( iface->i_rt->rt_lastnet != iface->i_rt->rt_firstnet ) {
176                 fprintf( newconf, "-%d", ntohs( iface->i_rt->rt_lastnet ));
177             }
178             fprintf( newconf, " -addr %u.%u",
179                     ntohs( iface->i_addr.sat_addr.s_net ),
180                     iface->i_addr.sat_addr.s_node );
181             for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
182                 fprintf( newconf, " -zone \"%.*s\"",
183                         ((struct ziptab *)l->l_data)->zt_len,
184                         ((struct ziptab *)l->l_data)->zt_name );
185             }
186             fprintf( newconf, "\n" );
187
188             iface = iface->i_next;
189             if ( conf == NULL && iface == NULL ) {
190                 break;
191             }
192         }
193     }
194     if ( conf != NULL ) {
195         fclose( conf );
196     }
197     fclose( newconf );
198
199     if ( rename( newpath, path ) < 0 ) {
200         syslog( LOG_ERR, "rename %s to %s: %m", newpath, path );
201         return( -1 );
202     }
203     return( 0 );
204 }
205
206 /*
207  * Read our config file. If it's not there, return -1. If it is there,
208  * but has syntax errors, exit. Format of the file is as follows:
209  *
210  *      interface [ -seed ] [ -phase number ] [ -net net-range ]
211  *      [ -addr net.node ] [ -zone zonename ]...
212  * e.g.
213  *      le0 -phase 1 -net 7938 -zone Argus
214  * or
215  *      le0 -phase 2 -net 8043-8044 -zone Argus -zone "Research Systems"
216  *      le0 -phase 1 -net 7938 -zone Argus
217  *
218  * Pretty much everything is optional. Anything that is unspecified is
219  * searched for on the network.  If -seed is not specified, the
220  * configuration is assumed to be soft, i.e. it can be overridden by
221  * another router. If -seed is specified, atalkd will exit if another
222  * router disagrees.  If the phase is unspecified, it defaults to phase
223  * 2 (the default can be overridden on the command line).  -addr can
224  * replace -net, if the network in question isn't a range.  The default
225  * zone for an interface is the first zone encountered for that
226  * interface.
227  */
228 readconf( cf )
229     char                *cf;
230 {
231     struct ifreq        ifr;
232     struct interface    *iface, *niface;
233     char                line[ 1024 ], **argv, *p;
234     int                 i, j, s, cc;
235     FILE                *conf;
236
237     if ( cf == NULL ) {
238         p = _PATH_ATALKDCONF;
239     } else {
240         p = cf;
241     }
242     if (( conf = fopen( p, "r" )) == NULL ) {
243         return( -1 );
244     }
245
246 #ifndef __svr4__
247     if (( s = socket( AF_APPLETALK, SOCK_DGRAM, 0 )) < 0 ) {
248         perror( "socket" );
249         fclose(conf);
250         return -1;
251     }
252 #endif __svr4__
253
254     while ( fgets( line, sizeof( line ), conf ) != NULL ) {
255         if (( argv = parseline( line )) == NULL ) {
256             continue;
257         }
258
259 #ifndef __svr4__
260         /*
261          * Check that av[ 0 ] is a valid interface.
262          * Not possible under sysV.
263          */
264         strcpy( ifr.ifr_name, argv[ 0 ] );
265
266         /* for devices that don't support appletalk */
267         if ((ioctl(s, SIOCGIFADDR, &ifr) < 0) && (errno == ENODEV)) {
268           perror(argv[0]);
269           goto read_conf_err;
270         }
271
272         if ( ioctl( s, SIOCGIFFLAGS, &ifr ) < 0 ) {
273             perror( argv[ 0 ] );
274             goto read_conf_err;
275         }
276
277         if (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT |IFF_SLAVE)) {
278             fprintf( stderr, "%s: can't configure.\n", ifr.ifr_name );
279             goto read_conf_err;
280         }
281
282 #ifdef IFF_MULTICAST
283         if ((ifr.ifr_flags & IFF_MULTICAST) == 0)
284             fprintf(stderr, "%s: multicast may not work properly.\n",
285                     ifr.ifr_name);
286 #endif
287
288         /* configure hw multicast for this interface. */
289         if (addmulti(ifr.ifr_name, NULL) < 0) {
290           perror(ifr.ifr_name);
291           fprintf(stderr, "Can't configure multicast.\n");
292           goto read_conf_err;
293         }
294 #endif __svr4__
295
296         if (( niface = newiface( argv[ 0 ] )) == NULL ) {
297             perror( "newiface" );
298             goto read_conf_err;
299         }
300
301         for ( i = 1; argv[ i ]; i += cc ) {
302             if ( argv[ i ][ 0 ] == '-' ) {
303                 argv[ i ]++;
304             }
305             for ( j = 0; j < sizeof( params ) / sizeof( params[ 0 ] ); j++ ) {
306                 if ( strcmp( argv[ i ], params[ j ].p_name ) == 0 ) {
307                     if ( params[ j ].p_func != NULL ) {
308                         cc = (*params[ j ].p_func)( niface, &argv[ i + 1 ] );
309                         if (cc < 0) 
310                           goto read_conf_err;
311                         break;
312                     }
313                 }
314             }
315             if ( j >= sizeof( params ) / sizeof( params[ 0 ] )) {
316                 fprintf( stderr, "%s: attribute not found.\n", argv[ i ] );
317                 goto read_conf_err;
318             }
319         }
320
321         for ( iface = interfaces; iface; iface = iface->i_next ) {
322             if ( strcmp( niface->i_name, iface->i_name ) == 0 &&
323                     ((( niface->i_flags & iface->i_flags &
324                     ( IFACE_PHASE1|IFACE_PHASE2 )) != 0 ) ||
325                     niface->i_flags == 0 || iface->i_flags == 0 )) {
326                 break;
327             }
328         }
329         if ( iface ) {  /* Already have this interface and phase */
330             fprintf( stderr, "%s already configured!\n", niface->i_name );
331             goto read_conf_err;
332         }
333
334         if ( interfaces == NULL ) {
335             interfaces = niface;
336         } else {
337             for ( iface = interfaces; iface->i_next; iface = iface->i_next )
338                 ;
339             iface->i_next = niface;
340         }
341         niface->i_next = NULL;
342     }
343
344 #ifndef __svr4__
345     close( s );
346 #endif
347
348     fclose( conf );
349
350     /*
351      * Note: we've added lo0 to the interface list previously, so we must
352      * have configured more than one interface...
353      */
354     for ( iface = interfaces, cc = 0; iface; iface = iface->i_next, cc++ )
355         ;
356     if ( cc >= IFBASE ) {
357         return( 0 );
358     } else {
359         return( -1 );
360     }
361
362 read_conf_err:
363 #ifndef __svr4__
364     close(s);
365 #endif
366     fclose(conf);
367     return -1;
368 }
369
370 /*ARGSUSED*/
371 router( iface, av )
372     struct interface    *iface;
373     char                **av;
374 {
375     /* make sure "-router" and "-dontroute" aren't both on the same line. */
376     if (iface->i_flags & IFACE_DONTROUTE) {
377         fprintf( stderr, "Can't specify both -router and -dontroute.\n");
378         return -1;
379     }
380
381     /*
382      * Check to be sure "-router" is before "-zone".
383      */
384     if ( iface->i_czt ) {
385         fprintf( stderr, "Must specify -router before -zone.\n");
386         return -1;
387     }
388
389     /* -router also implies -seed */
390     iface->i_flags |= IFACE_RSEED | IFACE_SEED | IFACE_ISROUTER;
391     return( 1 );
392 }
393
394 /*ARGSUSED*/
395 dontroute( iface, av )
396     struct interface    *iface;
397     char                **av;
398 {
399     /* make sure "-router" and "-dontroute" aren't both on the same line. */
400     if (iface->i_flags & IFACE_RSEED) {
401         fprintf( stderr, "Can't specify both -router and -dontroute.\n");
402         return -1;
403     }
404
405     iface->i_flags |= IFACE_DONTROUTE;
406     return( 1 );
407 }
408
409 /*ARGSUSED*/
410 seed( iface, av )
411     struct interface    *iface;
412     char                **av;
413 {
414     /*
415      * Check to be sure "-seed" is before "-zone". we keep the old
416      * semantics of just ignoring this in a routerless world.
417      */
418     if ( iface->i_czt ) {
419         fprintf( stderr, "Must specify -seed before -zone(%s).\n",
420                  iface->i_czt->zt_name);
421         return -1;
422     }
423
424     iface->i_flags |= IFACE_SEED;
425     return( 1 );
426 }
427
428 phase( iface, av )
429     struct interface    *iface;
430     char                **av;
431 {
432     int                 n;
433     char                *pnum;
434
435     if (( pnum = av[ 0 ] ) == NULL ) {
436         fprintf( stderr, "No phase.\n" );
437         return -1;
438     }
439
440     switch ( n = atoi( pnum )) {
441     case 1 :
442         iface->i_flags |= IFACE_PHASE1;
443         break;
444
445     case 2 :
446         iface->i_flags |= IFACE_PHASE2;
447         break;
448
449     default :
450         fprintf( stderr, "No phase %d.\n", n );
451         return -1;
452     }
453     return( 2 );
454 }
455
456 net( iface, av )
457     struct interface    *iface;
458     char                **av;
459 {
460     char                *nrange;
461     char                *stop;
462     int                 net;
463
464     if (( nrange = av[ 0 ] ) == NULL ) {
465         fprintf( stderr, "No network.\n" );
466         return -1;
467     }
468
469     if (( stop = strchr( nrange, '-' )) != 0 ) {
470         stop++;
471     }
472     net = atoi( nrange );
473     if ( net < 0 || net >= 0xffff ) {
474         fprintf( stderr, "Bad network: %d\n" );
475         return -1;
476     }
477
478     if ( iface->i_rt == NULL && ( iface->i_rt = newrt(iface)) == NULL ) {
479         perror( "newrt" );
480         return -1;
481     }
482
483     if ( iface->i_flags & IFACE_PHASE1 ) {
484         if ( stop != 0 ) {
485             fprintf( stderr, "Phase 1 doesn't use an address range.\n" );
486             return -1;
487         }
488         if ( iface->i_caddr.sat_addr.s_net != ATADDR_ANYNET &&
489                 ntohs( iface->i_caddr.sat_addr.s_net ) != net ) {
490             fprintf( stderr, "Net-range (%u) doesn't match net %u.\n",
491                     net, ntohs( iface->i_caddr.sat_addr.s_net ));
492             return -1;
493         }
494         iface->i_rt->rt_firstnet = iface->i_rt->rt_lastnet = htons( net );
495     } else if ( iface->i_flags & IFACE_PHASE2 ) {
496         iface->i_rt->rt_firstnet = htons( net );
497         if ( stop != 0 ) {
498             net = atoi( stop );
499             if ( net < 0 || net >= 0xffff ) {
500                 fprintf( stderr, "Bad network: %d\n" );
501                 return -1;
502             }
503         }
504         iface->i_rt->rt_lastnet = htons( net );
505         if ( iface->i_caddr.sat_addr.s_net != ATADDR_ANYNET &&
506                 ( ntohs( iface->i_rt->rt_firstnet ) >
507                 ntohs( iface->i_caddr.sat_addr.s_net ) ||
508                 ntohs( iface->i_rt->rt_lastnet ) <
509                 ntohs( iface->i_caddr.sat_addr.s_net ))) {
510             fprintf( stderr, "Net-range (%u-%u) doesn't contain net (%u).\n",
511                     ntohs( iface->i_rt->rt_firstnet ),
512                     ntohs( iface->i_rt->rt_lastnet ),
513                     ntohs( iface->i_caddr.sat_addr.s_net ));
514             return -1;
515         }
516         if ( iface->i_rt->rt_firstnet != iface->i_rt->rt_lastnet ) {
517             iface->i_rt->rt_flags |= RTMPTAB_EXTENDED;
518         }
519     } else {
520         fprintf( stderr, "Must specify phase before networks.\n" );
521         return -1;
522     }
523     return( 2 );
524 }
525
526 addr( iface, av )
527     struct interface    *iface;
528     char                **av;
529 {
530     if ( av[ 0 ] == NULL ) {
531         fprintf( stderr, "No address.\n" );
532         return -1;
533     }
534     if ( atalk_aton( av[ 0 ], &iface->i_caddr.sat_addr ) == 0 ) {
535         fprintf( stderr, "Bad address, %s\n", av[ 0 ] );
536         return -1;
537     }
538
539     if ( iface->i_rt ) {
540         if ( ntohs( iface->i_rt->rt_firstnet ) >
541                 ntohs( iface->i_caddr.sat_addr.s_net ) ||
542                 ntohs( iface->i_rt->rt_lastnet ) <
543                 ntohs( iface->i_caddr.sat_addr.s_net )) {
544             fprintf( stderr, "Net (%u) not in net-range (%u-%u).\n",
545                     ntohs( iface->i_caddr.sat_addr.s_net ),
546                     ntohs( iface->i_rt->rt_firstnet ),
547                     ntohs( iface->i_rt->rt_lastnet ));
548             return -1;
549         }
550     } else {
551         if (( iface->i_rt = newrt(iface)) == NULL ) {
552             perror( "newrt" );
553             return -1;
554         }
555         iface->i_rt->rt_firstnet = iface->i_rt->rt_lastnet =
556                 iface->i_caddr.sat_addr.s_net;
557     }
558
559     return( 2 );
560 }
561
562 zone( iface, av )
563     struct interface    *iface;
564     char                **av;
565 {
566     struct ziptab       *zt;
567     char                *zname;
568
569     if (( zname = av[ 0 ] ) == NULL ) {
570         fprintf( stderr, "No zone.\n" );
571         return -1;
572     }
573
574     /*
575      * Only process "-zone" if this interface has "-seed".  We keep our
576      * list of configured zones in the interface structure.  Then we can
577      * check that the network has given us good zones.
578      */
579     if ( iface->i_flags & IFACE_SEED ) {
580         if ( iface->i_rt == NULL ) {
581             fprintf( stderr, "Must specify net-range before zones.\n" );
582             return -1;
583         }
584         if (( zt = newzt( strlen( zname ), zname )) == NULL ) {
585             perror( "newzt" );
586             return -1;
587         }
588         if ( iface->i_czt == NULL ) {
589             iface->i_czt = zt;
590         } else {
591             zt->zt_next = iface->i_czt->zt_next;
592             iface->i_czt->zt_next = zt;
593         }
594     }
595     return( 2 );
596 }
597
598 /*
599  * Get the configuration from the kernel. Only called if there's no
600  * configuration.
601  */
602 getifconf()
603 {
604     struct interface    *iface, *niface;
605     struct ifreq        ifr;
606     char                **start, **list;
607     int                 s;
608
609     if (( s = socket( AF_APPLETALK, SOCK_DGRAM, 0 )) < 0 ) {
610         perror( "socket" );
611         return -1;
612     }
613
614     start = list = getifacelist();
615     while (list && *list) {
616         strncpy(ifr.ifr_name, *list, sizeof(ifr.ifr_name));
617         list++;
618
619         if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
620           continue;
621
622         if (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_SLAVE))
623           continue;
624
625         if ((ifr.ifr_flags & IFF_UP) == 0)
626           continue;
627
628         /* for devices that don't support appletalk */
629         if (ioctl(s, SIOCGIFADDR, &ifr) < 0 && (errno == ENODEV))
630           continue;
631
632         for ( iface = interfaces; iface; iface = iface->i_next ) {
633             if ( strcmp( iface->i_name, ifr.ifr_name ) == 0 ) {
634                 break;
635             }
636         }
637         if ( iface ) {  /* Already have this interface name */
638             continue;
639         }
640
641
642 #ifdef IFF_MULTICAST
643         if ((ifr.ifr_flags & IFF_MULTICAST) == 0)
644           fprintf(stderr, "%s: multicast may not work correctly.\n",
645                   ifr.ifr_name);
646 #endif
647
648         if (addmulti(ifr.ifr_name, NULL) < 0) {
649           fprintf(stderr, "%s: disabled.\n", ifr.ifr_name);
650           continue;
651         }
652         
653         if (( niface = newiface( ifr.ifr_name )) == NULL ) {
654             perror( "newiface" );
655             close(s);
656             freeifacelist(start);
657             return -1;
658         }
659         /*
660          * Could try to get the address from the kernel...
661          */
662
663         if ( interfaces == NULL ) {
664             interfaces = niface;
665         } else {
666             for ( iface = interfaces; iface->i_next; iface = iface->i_next )
667                 ;
668             iface->i_next = niface;
669         }
670         niface->i_next = NULL;
671     }
672     freeifacelist(start);
673     (void)close( s );
674     return( 0 );
675 }
676
677 /*
678  * Allocate a new interface structure.  Centralized here so we can change
679  * the interface structure and have it updated nicely.
680  */
681     struct interface *
682 newiface( name )
683     const char          *name;
684 {
685     struct interface    *niface;
686
687     if (( niface = (struct interface *)calloc(1, sizeof( struct interface )))
688             == NULL ) {
689         return( NULL );
690     }
691     strncpy( niface->i_name, name, sizeof(niface->i_name));
692 #ifdef BSD4_4
693     niface->i_addr.sat_len = sizeof( struct sockaddr_at );
694 #endif BSD4_4
695     niface->i_addr.sat_family = AF_APPLETALK;
696 #ifdef BSD4_4
697     niface->i_caddr.sat_len = sizeof( struct sockaddr_at );
698 #endif BSD4_4
699     niface->i_caddr.sat_family = AF_APPLETALK;
700     return( niface );
701 }
702
703 #ifdef __svr4__
704     int
705 plumb()
706 {
707     struct interface    *iface;
708     char                device[ MAXPATHLEN + 1], *p;
709     int                 fd, ppa;
710
711     for ( iface = interfaces; iface != NULL; iface = iface->i_next ) {
712         if ( strcmp( iface->i_name, LOOPIFACE ) == 0 ) {
713             continue;
714         }
715
716         strcpy( device, "/dev/" );
717         strcat( device, iface->i_name );
718         if (( p = strpbrk( device, "0123456789" )) == NULL ) {
719             syslog( LOG_ERR, "plumb: invalid device: %s", device );
720             return -1;
721         }
722         ppa = atoi( p );
723         *p = '\0';
724
725         if (( fd = open( device, O_RDWR, 0 )) < 0 ) {
726             syslog( LOG_ERR, "%s: %m", device );
727             return -1;
728         }
729         if ( ioctl( fd, I_PUSH, "ddp" ) < 0 ) {
730             syslog( LOG_ERR, "I_PUSH: %m" );
731             close(fd);
732             return -1;
733         }
734         if ( ioctl( fd, IF_UNITSEL, ppa ) < 0 ) {
735             syslog( LOG_ERR, "IF_UNITSEL: %m" );
736             close(fd);
737             return -1;
738         }
739
740         /* configure multicast. */
741         if (addmulti(iface->i_name, NULL) < 0) {
742           perror(iface->i_name);
743           fprintf(stderr,"Can't configure multicast.\n");
744           close(fd);
745           return -1;
746         }
747
748         syslog( LOG_INFO, "plumbed %s%d", device, ppa );
749     }
750
751     return( 0 );
752 }
753 #endif __svr4__