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