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