dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.bin / pppd / asppp2pppd
blob3063864018c8f4828d9e1ac3a1b58cd39b9ed44b
1 #!/usr/bin/perl
3 # This utility translates from aspppd configuration to Solaris PPP 4.0
4 # (or ANU ppp-2.4.0; aka pppd). It can also revert to previous aspppd
5 # configuration (discarding the pppd configuration), but does not
6 # translate new configuration files into old.
8 # This script provides only a suggested translation for your existing
9 # aspppd configuration. You will need to evaluate for yourself
10 # whether the translation is appropriate for your operating
11 # environment.
13 # Copyright 2009 Sun Microsystems, Inc. All rights reserved.
14 # Use is subject to license terms.
17 # Steps in translation:
18 # - parse /etc/asppp.cf
19 # - check for aspppls in /etc/passwd (or NIS)
20 # - read in current /etc/ppp/options configuration file
21 # - read list of configured serial ports from pmadm
22 # - read in UUCP configuration files
23 # - create translated configuration
24 # - write files back out
26 # Known issues:
27 # - translation with point-to-multipoint is incomplete
29 use Getopt::Std;
30 use Fcntl;
31 use POSIX qw(tmpnam ENOSYS);
32 use Sys::Hostname;
34 # Secure the path if we're running under RBAC.
35 $ENV{PATH} = ( "/bin", "/sbin", "/usr/bin", "/usr/sbin", "/usr/ucb" )
36 if $< != $>;
38 # General path names that can be configured.
39 local($rootetc) = "/etc/";
40 local($passwd) = $rootetc . "passwd";
41 local($passwdlck) = $rootetc . ".pwd.lock";
42 local($asfile) = $rootetc . "asppp.cf";
43 local($astemp) = $rootetc . "asppp.temp.cf";
44 local($asmoved) = $rootetc . "asppp.saved.cf";
45 local($uucpdir) = $rootetc . "uucp/";
46 local($Devices) = $uucpdir . "Devices";
47 local($Devconfig) = $uucpdir . "Devconfig";
48 local($Dialers) = $uucpdir . "Dialers";
49 local($Dialcodes) = $uucpdir . "Dialcodes";
50 local($Limits) = $uucpdir . "Limits";
51 local($Sysfiles) = $uucpdir . "Sysfiles";
52 local($Systems) = $uucpdir . "Systems";
53 local($pppdir) = $rootetc . "ppp/";
54 local($options) = $pppdir . "options";
55 local($ttyprefix) = $pppdir . "options.";
56 local($peersdir) = $pppdir . "peers/";
57 local($initd) = $rootetc . "init.d/";
58 local($asctl) = $initd . "asppp";
59 local($pppdctl) = $initd . "pppd";
60 local($sedpasswd) = "/tmp/sed-passwd";
62 # Fake asppp keyword used to keep track of dial-in paths.
63 local($isdialin) = "-is-dial-in";
65 # Limits and Sysfiles are keyed on "service=".
66 # Devconfig is keyed on "service=" and "device=".
67 # Dialcodes, Dialers, Systems, and Devices are single keyword files.
68 # Devices alone may have multiple entries for a given key.
70 # Internal data structures
71 local(@sysfiles,@limits,@devconfig);
72 local(@sysdefault) = ( "systems=" . $Systems, "dialers=" . $Dialers,
73 "devices=" . $Devices );
74 local(@limitdefault) = ( "max=-1" );
75 local(@devdefault) = ( "pop=", "push=", "connecttime=-1", "expecttime=-1",
76 "msgtime=-1" );
78 # List of keywords for which ifconfig takes an additional parameter.
79 local($ifconfigtakes) = (
80 addif => 1,
81 removeif => 1,
82 auth_algs => 1,
83 encr_algs => 1,
84 encr_auth_algs => 1,
85 broadcast => 1,
86 destination => 1,
87 index => 1,
88 metric => 1,
89 modinsert => 1,
90 modremove => 1,
91 mtu => 1,
92 netmask => 1,
93 set => 1,
94 subnet => 1,
95 tdst => 1,
96 tsrc => 1,
97 wait => 1,
99 # These are keywords, but do not take an additional parameter.
100 ether => 0,
101 inet => 0,
102 inet6 => 0,
103 arp => 0,
104 -arp => 0,
105 auto-revarp => 0,
106 modlist => 0,
107 plumb => 0,
108 unplumb => 0,
109 private => 0,
110 -private => 0,
111 nud => 0,
112 -nud => 0,
113 trailers => 0,
114 -trailers => 0,
115 up => 0,
116 down => 0,
117 xmit => 0,
118 -xmit => 0,
119 auto-dhcp => 0,
120 dhcp => 0,
121 primary => 0,
122 drop => 0,
123 extend => 0,
124 inform => 0,
125 ping => 0,
126 release => 0,
127 start => 0,
128 status => 0
131 # print number of something in English.
132 sub nof
134 local($num, $item, @rest) = @_;
135 print "No ", $item, "s", @rest if $num == 0;
136 print "1 ", $item, @rest if $num == 1;
137 print $num, " ", $item, "s", @rest if $num > 1;
140 # ask a yes or no question.
141 sub yesno
143 local ($query, $default) = @_;
144 local ($ans, $defans);
146 return $default unless (-t STDIN) && (-t STDOUT) && !$opt_n;
147 $defans = $default ? "Yn" : "yN";
148 while (1) {
149 print $query, " [", $defans, "]? ";
150 chomp($ans = <STDIN>);
151 return $default unless $ans;
152 return 1 if $ans =~ /^[Yy1Tt]/;
153 return 0 if $ans =~ /^[Nn0Ff]/;
154 print "Please enter 'y' or 'n'.\n";
158 # Put quotes around a string, if necessary.
159 # The tests here aren't perfect -- they think that \\\' isn't an
160 # escaped quote -- but they're good enough.
161 sub requote
163 local($_) = @_;
164 if (/^$/) {
165 "\"\"";
166 } elsif (/^'/ || /[^\\]'/ || /\\\\'/) {
167 # Has unescaped quotes; must use " or redo quoting.
168 if (/^"/ || /[^\\]"/ || /\\\\"/) {
169 # Both kinds of quotes; redo the quoting.
170 s/^"/\\"/;
171 s/([^\\]|\\\\)"/$1\\"/g;
173 "\"" . $_ . "\"";
174 } elsif (/^"/ || /[^\\]"/ || /\\\\"/) {
175 "'" . $_ . "'";
176 } elsif (/\s/) {
177 "\"" . $_ . "\"";
178 } else {
183 # Get a single line from a UUCP configuration file and return as a
184 # reference to an array of words. Removes comments and escapes.
185 # (This is a modified version of the standard Perl shellwords function
186 # that understands C escape sequences and continuation lines.)
187 # Optionally returns lead-in, source text, and trailing component
188 # for editing.
189 sub uucpline
191 local($input, $file, $triplet) = @_;
192 local(@words,$snippet,$field,$havefield,$cont,@triparray,$maytrail);
194 $cont = "";
195 $maytrail = "";
196 while (<$input>) {
197 # remove leading whitespace
198 if (s/^(\s+)//) {
199 $maytrail .= $1;
201 if ($cont eq "") {
202 if (s/^(\#(.|\n)*)$//) {
203 $triparray[0] .= $maytrail . $1;
204 $maytrail = "";
205 next;
207 if (s/^(\\?\n?)$//) {
208 $triparray[0] .= $maytrail . $1;
209 $maytrail = "";
210 next;
212 $triparray[0] .= $maytrail;
213 $maytrail = "";
215 $snippet = $_;
216 if (s/^(([^\#\\]|\\.)*)\\\n$//) {
217 $maytrail .= $snippet;
218 $cont .= $1;
219 next;
221 if (/^(([^\\\#]|\\[^\#])*)(\#?(.|\n)*)$/) {
222 $_ = $maytrail . $1;
223 $maytrail = $3;
224 if (s/((\s|\n)*)$//) {
225 $maytrail = $1 . $maytrail;
227 $triparray[1] = $_;
229 $_ = $cont . $snippet;
230 $cont = "";
231 s/\n$//;
232 while ($_ ne '') {
233 for (;;) {
234 if (s/^#.*//) {
235 last;
236 } elsif (s/^"(([^"\\]|\\.)*)"//) {
237 $snippet = $1;
238 } elsif (s/^"//) {
239 warn "Unmatched double quote in $file: \"$_\n";
240 } elsif (s/^'(([^'\\]|\\.)*)'//) {
241 $snippet = $1;
242 } elsif (s/^'//) {
243 warn "Unmatched single quote in $file: '$_\n";
244 } elsif (s/^\\s//) {
245 # \s works in chat, but not in the pppd option files
246 $snippet = " ";
247 } elsif (s/^(\\.)//) {
248 $snippet = $1;
249 } elsif (s/^([^\s\\'"#]+)//) {
250 $snippet = $1;
251 } else {
252 s/^\s+//;
253 last;
255 $havefield = 1;
256 $field .= $snippet;
258 push(@words, $field) if $havefield;
259 $havefield = 0;
260 $field = '';
262 last;
264 $triparray[2] .= $maytrail;
265 @$triplet = @triparray;
266 warn "Bad continuation line in $file: $cont\n" if $cont ne '';
267 \@words;
270 # Given a logical UUCP file name, return a list of all of the files
271 # that should be read.
272 sub uucpfiles
274 local ($file) = @_;
275 local (@flist, $value) = ();
277 for $value (@sysfiles, @sysdefault) {
278 if ($value =~ /^$file=/i) {
279 $value =~ s/^$file=//i;
280 for $file (split /:/, $value) {
281 $file = $uucpdir . $file if $file !~ /^\//;
282 push @flist, $file;
284 last;
287 @flist;
290 # Given a file name and some key words, parse the contents of the file
291 # and return a reference to a hash constructed from this. All keys
292 # except the last must match exactly. The last is used to order the
293 # hash.
294 sub uucpkeyfile
296 local($file,@keylist) = @_;
297 local($lastkey,$keyval,$words,$i,$flag,%byservice);
299 open(SVCFILE, '<' . $file) || return undef;
300 $lastkey = pop @keylist;
301 while (@{$words = uucpline(SVCFILE, $file)}) {
302 $flag = 1;
303 foreach $keyval (@keylist) {
304 $flag = 0;
305 $i = 0;
306 while ($i < @$words) {
307 if ($$words[$i] eq $keyval) {
308 splice @$words, $i, 1;
309 $flag = 1;
310 last;
312 $i++;
314 last unless $flag;
316 next unless $flag;
317 foreach $i (0 .. @{$words}) {
318 if (@{$words}[$i] =~ /^$lastkey(.*)/i) {
319 splice @{$words}, $i, 1;
320 $byservice{$1} = $words;
321 last;
325 close SVCFILE;
326 \%byservice;
329 # This reads a UUCP file that is keyed on the first token on each
330 # line. Duplicates are not permitted; the first encountered is used
331 # and the rest are silently discarded. A hash indexed on the first
332 # token and containing an array of tokens in each bucket is returned.
333 # Used for Dialcodes, Dialers, and Systems.
334 sub uucpposfiles
336 local(@files) = @_;
337 local($keyval,$words,%keyeddata);
339 foreach $file (@files) {
340 if (!open(POSFILE, '<' . $file)) {
341 warn "$file: $!\n";
342 next;
344 while (@{$words = uucpline(POSFILE, $file)}) {
345 $keyval = shift @{$words};
346 next if $keyeddata{$keyval};
347 $keyeddata{$keyval} = $words;
349 close POSFILE;
351 \%keyeddata;
354 # This reads a UUCP file that is keyed on the first token on each line
355 # and may have duplicate entries. Each entry of the hash contains an
356 # array. Each entry of that array points to an array of tokens
357 # representing one parsed line. Used for the Devices file.
358 sub uucpdevices
360 local(@files) = @_;
361 local($keyval,$words,%keyeddata);
363 foreach $file (@files) {
364 if (!open(POSFILE, '<' . $file)) {
365 warn "$file: $!\n";
366 next;
368 while (@{$words = uucpline(POSFILE, $file)}) {
369 $keyval = shift @{$words};
370 push @{$keyeddata{$keyval}}, $words;
372 close POSFILE;
374 \%keyeddata;
377 # For a path defined in asppp.cf, copy over defaults, validate the
378 # required options, and save in the hash to be returned.
379 sub savepath
381 local($paths, $options, $defref) = @_;
382 local($peer,$intf);
384 return if $options == $defref;
385 foreach $key (keys %$defref) {
386 $$options{$key} = $$defref{$key} unless defined($$options{$key});
388 $peer = $$options{"peer_system_name"};
389 warn("Discarded path with no peer system name.\n"), return
390 unless defined($peer);
391 $intf = $$options{"interface"};
392 warn("Missing interface on path to peer \"$peer\".\n"), return
393 unless defined($intf);
394 warn("Bad interface $intf on path to peer \"$peer\".\n"), return
395 unless $intf =~ /^ipd([0-9]+|ptp[0-9]+|ptp\*)$/;
396 warn("Missing peer IP address for point-to-multipoint path to \"",
397 $peer, "\".\n"), return
398 if $intf =~ /^ipd[0-9]+$/ && !defined($$options{"peer_ip_address"});
399 warn "Multiple definitions of path to peer \"$peer\".\n",
400 if defined($paths{$peer});
401 warn "Odd version number ", $$options{"version"},
402 " encountered in path to peer \"", $peer, "\" (ignored).\n"
403 if defined($$options{"version"}) && $$options{"version"} != 1;
404 $paths{$peer} = $options;
407 # Parse through asppp.cf. Unlike the UUCP files, lines don't matter
408 # here. The parsing is modal, with "path" introducing a PPP session
409 # description and "defaults" reverting back to global definitions.
410 sub readaspppcf
412 local($aspppcf) = @_;
413 local(%aspppdkey) = (
414 chap_name => 1,
415 chap_peer_secret => 1,
416 chap_peer_name => 1,
417 chap_secret => 1,
418 debug_level => 1,
419 default_route => 0,
420 ifconfig => -1,
421 inactivity_timeout => 1,
422 interface => 1,
423 # sic; aspppd is seriously confused! ACCM isn't in IPCP.
424 ipcp_async_map => 1,
425 ipcp_compression => 1,
426 lcp_compression => 1,
427 lcp_mru => 1,
428 negotiate_address => 1,
429 pap_id => 1,
430 pap_password => 1,
431 pap_peer_id => 1,
432 pap_peer_password => 1,
433 peer_ip_address => 1,
434 peer_system_name => 1,
435 require_authentication => 2,
436 version => 1,
437 will_do_authentication => 2
439 local($words,$word,$prevword,$i,$errors,%defaults,%ifconfig,%paths);
440 local($options);
442 open ASPPPD, "<" . $aspppcf || die "$aspppcf: $!\n";
443 print "Reading configuration from $aspppcf\n" if $opt_v;
444 $defaults{inactivity_timeout} = 120;
445 $defaults{ipcp_compression} = "vj";
446 $defaults{lcp_compression} = "on";
447 $options = \%defaults;
448 while (@{$words = uucpline(ASPPPD, $aspppcf)}) {
449 if ($$words[0] =~ /^ifconfig$/i) {
450 warn "$prevword with missing argument ignored.\n"
451 if defined($prevword);
452 undef $prevword;
453 shift @$words; # discard 'ifconfig' keyword
454 $word = shift @$words;
455 warn("Bad interface on ifconfig $word.\n"), next
456 unless $word =~ /^ipd([0-9]+|ptp[0-9]+)$/;
457 $ifconfig{$word} = \@$words;
458 next;
460 unshift @{$words}, $prevword if defined($prevword);
461 undef $prevword;
462 while ($word = lc(shift @{$words})) {
463 $_ = $word;
464 if (/^defaults$/i) {
465 savepath(\%paths, $options, \%defaults);
466 $options = \%defaults;
467 next ;
469 if (/^path$/i) {
470 local(%pathopts);
471 savepath(\%paths, $options, \%defaults);
472 $options = \%pathopts;
473 next;
475 if (!defined($i = $aspppdkey{$word})) {
476 die "Too many errors in $aspppcf; aborting.\n"
477 if ++$errors > 5;
478 warn "Ignoring unknown keyword $word in $aspppcf\n";
479 next;
481 warn("$_ unexpected; remainder of line ignored.\n"),
482 last if $i == -1;
483 warn("Duplicate $_ in path ignored.\n"), next
484 if $options != \%defaults && defined($$options{$_});
485 $$options{$_} = 1 if $i == 0;
486 next if $i == 0;
487 $prevword = $_, last unless defined($word = shift @{$words});
488 $$options{$_} = $word if $i == 1;
489 if ($i == 2) {
490 undef $$options{$_}, next if $word =~ /^off$/;
491 $$options{$_} = $word;
492 if ($word = shift @{$words}) {
493 if ($word =~ /^(p|ch)ap$/) {
494 $$options{$_} .= " " . $word;
495 } else {
496 unshift @{$words}, $word;
502 warn "Odd trailing keyword \"$prevword\" ignored in $aspppcf\n"
503 if $prevword;
504 savepath(\%paths, $options, \%defaults);
505 die "No paths defined for aspppd.\n" if 0+(keys %paths) == 0;
506 die "No interfaces defined for aspppd.\n" if 0+(keys %ifconfig) == 0;
507 if ($opt_v) {
508 nof 0+(keys %paths), "path", " and ";
509 nof 0+(keys %ifconfig), "interface", " defined for aspppd.\n";
511 close ASPPPD;
512 ( \%ifconfig, \%paths );
515 # Read /etc/passwd (or NIS) and return hash of users for whom
516 # the default shell is aspppls. Each hash entry contains the user's
517 # home directory path.
518 sub readpasswd
520 local(%users,@pwe);
522 setpwent();
523 while (@pwe = getpwent()) {
524 $users{$pwe[0]} = $pwe[7] if $pwe[8] =~ /\/aspppls$/;
526 endpwent();
527 nof 0+(keys %users), "aspppd dial in user", " found.\n"
528 if $opt_v;
529 \%users;
532 # Parse through pmadm output to find enabled serial ports.
533 # Field 9 has 'I' (modem dial-out only or initialize only), 'b'
534 # (bidirectional) or is blank (modem dial-in only or terminal-hardwired).
535 # For that latter case, field 18 (software-carrier) has 'y'.
536 # Field 3 has 'x' if disabled.
537 sub getserialports
539 local(%dialin, %dialout);
541 open PMADM, "pmadm -L|" || (warn "pmadm: $!\n", return undef);
542 while (<PMADM>) {
543 split /:/;
544 if ($_[3] !~ /x/) {
545 $dialin{$_[2]} = $_[8] if $_[9] ne "I";
546 $dialout{$_[2]} = $_[8] if $_[9] ne "";
549 close PMADM;
550 ( \%dialin, \%dialout );
553 # Convert an ifconfig statement into a local and remote address pair.
554 sub ifconf_addr
556 local($ifconf) = @_;
557 local($arg, $narg, $lcladdr, $remaddr);
559 shift @$ifconf; # lose the interface name
560 while (@$ifconf) {
561 local($arg, $narg);
562 $arg = shift @$ifconf;
563 $narg = shift @$ifconf if $ifconfigtakes{$arg};
564 if (exists($ifconfigtakes{$arg})) {
565 if ($arg eq "set") {
566 $lcladdr = $narg;
567 } elsif ($arg eq "destination") {
568 $remaddr = $narg;
570 } elsif (!defined($lcladdr)) {
571 $lcladdr = $arg;
572 } elsif (!defined($remaddr)) {
573 $remaddr = $arg;
576 ( $lcladdr, $remaddr );
579 # Convert a hash of aspppd options into an array of pppd options. The
580 # third argument ($chatpath) is undef for dial-in or a path to the
581 # chat script file otherwise.
582 sub convert_options
584 local ($pppdargs, $opts, $chatpath, $user) = @_;
586 # Do the pppd option conversions.
587 push(@$pppdargs, "defaultroute") if $$opts{default_route};
588 push(@$pppdargs, "idle " . $$opts{inactivity_timeout})
589 if $$opts{inactivity_timeout} != 0;
590 push(@$pppdargs, "asyncmap " . $$opts{ipcp_async_map})
591 if $$opts{ipcp_async_map};
592 push(@$pppdargs, "novj") if !$$opts{ipcp_compression};
593 local($peer);
594 if ($$opts{require_authentication}) {
595 local (@authopts);
596 if ($$opts{require_authentication} =~ /chap/) {
597 push(@authopts, "require-chap");
598 $peer = $$opts{chap_peer_name};
599 } else {
600 push(@authopts, "require-pap");
601 $peer = $$opts{pap_peer_id};
603 push(@authopts, "remotename " . requote($peer)) if $peer;
604 push(@authopts, "auth");
605 if ($chatpath) {
606 push(@$pppdargs, @authopts);
607 } elsif ($dialin_auth == 3) {
608 # mixed authentication; must use wrapper script.
609 local($sfile) = $pppdir . "dial-in." . $user;
610 $scriptfiles{$sfile} = "#!/bin/sh\n";
611 $scriptfiles{$sfile} .= "exec /usr/bin/pppd @authopts\n";
612 $dialinshell{$user} = $sfile;
614 } elsif ($dialin_auth < 2) {
615 push(@$pppdargs, "noauth");
617 push(@$pppdargs, "noaccomp nopcomp") if !$$opts{lcp_compression};
618 push(@$pppdargs, "mru " . $$opts{lcp_mru}) if $$opts{lcp_mru};
619 push(@$pppdargs, "noipdefault ipcp-accept-local")
620 if $$opts{negotiate_address};
621 local($myname,$csecret,$psecret,$passopt);
622 if ($$opts{will_do_authentication} =~ /chap/i) {
623 $myname = $$opts{chap_name};
624 $csecret = $$opts{chap_secret};
626 $myname = "" if !$myname;
627 if ($$opts{will_do_authentication} =~ /pap/i) {
628 if ($myname ne "" && $$opts{pap_id} ne "" &&
629 $myname ne $$opts{pap_id}) {
630 warn "pppd cannot have separate local names for PAP and CHAP; using CHAP name:\n";
631 warn "\t\"$myname\"\n";
632 } else {
633 $myname = $$opts{pap_id} if $$opts{pap_id} ne "";
635 $psecret = $$opts{pap_password};
637 if ($$opts{will_do_authentication}) {
638 if ($chatpath &&
639 ($$opts{will_do_authentication} !~ /chap/i ||
640 $$opts{will_do_authentication} !~ /pap/i ||
641 $csecret eq $psecret)) {
642 push(@$pppdargs,
643 "password " . requote($csecret ? $csecret : $psecret));
644 $passopt = 1;
646 push @$pppdargs, "user " . requote($myname);
647 push(@$pppdargs, "remotename " . requote($peer))
648 if $peer && !$$opts{require_authentication};
649 } else {
650 $myname = "NeverAuthenticate";
653 push(@$pppdargs, "debug") if $$opts{debug_level} >= 8;
654 push(@$pppdargs, "kdebug 3") if $$opts{debug_level} >= 9;
655 local($lcladdr, $remaddr) = ifconf_addr($$ifconfig{$$opts{interface}});
656 if ($chatpath) {
657 if ($$opts{debug_level} >= 4) {
658 push(@pppdargs, "connect \"/usr/bin/chat -vf $chatpath\"");
659 } else {
660 push(@pppdargs, "connect \"/usr/bin/chat -f $chatpath\"");
662 push(@$pppdargs, "user " . requote($myname));
663 local($str) = $remaddr;
664 $str = $$opts{peer_ip_address} if $$opts{peer_ip_address};
665 push(@$pppdargs, $lcladdr . ":" . $str)
666 if !$opt_s && ($lcladdr || $str);
667 } else {
668 push(@$pppdargs, "user " . requote($myname))
669 if $myname eq "NeverAuthenticate";
671 if ($$opts{interface} && $opt_s) {
672 if ($$opts{interface} =~ /^ipd[0-9]+$/) {
673 push(@$pppdargs, $lcladdr . ":") if $lcladdr;
674 } elsif ($$opts{interface} =~ /^ipd(|ptp)([0-9]+)$/) {
675 push(@pppdargs, "unit " . $2);
676 } else {
677 push(@pppdargs, "plumbed");
681 # Convert the secrets
682 if ($chatpath) {
683 $addsecret = "";
684 } elsif ($$opts{peer_ip_address}) {
685 $addsecret = " " . $$opts{peer_ip_address};
686 } else {
687 $addsecret = " *";
689 if ($$opts{require_authentication}) {
690 local($lclname, $secret, $authf);
691 $lclname = $$opts{will_do_authentication} ? $myname : hostname();
692 if ($$opts{require_authentication} =~ /chap/) {
693 $secret = $$opts{chap_peer_secret};
694 $authf = \%chapsecrets;
695 } else {
696 $secret = $$opts{pap_peer_password};
697 $authf = \%papsecrets;
699 ${$$authf{$peer}}{$lclname} = requote($secret) . $addsecret;
701 if ($$opts{will_do_authentication} && !$passopt) {
702 ${$chapsecrets{$myname}}{$peer} = requote($csecret) if $csecret;
703 ${$papsecrets{$myname}}{$peer} = requote($psecret) if $psecret;
707 # Translate options for a dial-in user.
708 sub translatedialin
710 local($peer) = @_;
712 $optname = $$dialinusers{$peer};
713 $optname .= "/" if $optname !~ /\/$/;
714 $optname .= ".ppprc";
715 if (exists($optfiles{$optname})) {
716 warn "Home directories of $peer and $optuser{$optname} are the same ($optfiles{$optname}\n";
717 warn "Ignoring configuration for $peer.\n";
718 return;
720 $optuser{$optname} = $peer;
721 $dialinshell{$peer} = "/usr/bin/pppd";
722 local (@pppdargs);
723 convert_options(\@pppdargs, $$paths{$peer}, undef, $peer);
724 push @pppdargs, "nologfd";
725 $optfiles{$optname} = \@pppdargs;
728 # Translate ifconfig entries in asppp.cf into either an ifconfig
729 # script for strict translation or a set of per-port IP addresses
730 # for normal translation. Also translate ipdX interfaces into
731 # Ethernet aliases to make routing daemon happy.
732 sub translateifconfig
734 local (@ifconfiglist,$cstr,$etherif,$intf,$ifconf,$addstr,$port);
736 open IFLIST, "/usr/sbin/ifconfig -au4|" || die "cannot run ifconfig: $!\n";
737 while (<IFLIST>) {
738 $etherif = $1 if !$etherif && /^([^:]*).*UP.*BROADCAST/;
740 close IFLIST;
741 $etherif = $1 if !$etherif && glob("/etc/hostname.*") =~ /[^\.]*.(.*)/;
742 $etherif = $1 if !$etherif && glob("/etc/dhcp.*") =~ /[^\.]*.(.*)/;
743 $etherif = "hme0" if !$etherif;
745 $cstr = "";
746 foreach $intf (keys %$ifconfig) {
747 $ifconf = $$ifconfig{$intf};
748 if ($intf =~ /ipd[0-9]+/) {
749 shift @$ifconf;
750 $cstr .= "ifconfig $etherif addif @$ifconf\n";
754 $ndialin = 0+(keys %$dialin);
755 $addstr = "";
756 foreach $intf (keys %downif) {
757 $ifconf = $downif{$intf};
758 if ($intf =~ /ipdptp([0-9]+)/ && --$ndialin >= 0) {
759 push @ifconfiglist, $ifconf;
760 $addstr .= "ifconfig sppp" . $1 . " @$ifconf\n";
763 if ($ndialin > 0) {
764 if (@ifconfiglist) {
765 print "Fewer ifconfigs (", $#ifconfiglist+1,
766 ") than dial-in lines (", $ndialin+$#ifconfiglist+1, ")\n";
767 } else {
768 print "No ifconfigs for ";
769 nof $ndialin, "dial-in port", ".\n";
771 } elsif ($ndialin < 0) {
772 if (@ifconfiglist) {
773 print "Ignoring ", -$ndialin, " of ";
774 nof $#ifconfiglist-$ndialin+1, "ifconfig",
775 " (too few dial-in ports)\n";
776 } else {
777 print "Ignoring all ";
778 nof -$ndialin, "ifconfig line", " (no dial-in ports)\n";
782 if ($opt_s) {
783 # Strict translation uses pre-plumbed interfaces.
784 $cstr .= $addstr;
785 } else {
786 foreach $port (values %$dialin) {
787 last if !@ifconfiglist;
788 $port =~ s+/dev/++;
789 $port =~ s+/+.+g;
790 $optfile = $ttyprefix . $port;
791 $ifconf = pop @ifconfiglist;
792 local ($lcladdr, $remaddr) = ifconf_addr($ifconf);
793 next if !defined($lcladdr) || !defined($remaddr);
794 local (@pppdargs) = $lcladdr . ":" . $remaddr;
795 $optfiles{$optfile} = \@pppdargs;
798 $scriptfiles{$pppdir . "ifconfig"} = $cstr if $cstr;
801 # Attempt to modify global passwd file using sed script stored in
802 # the $sedpasswd temporary file.
803 sub rewrite_passwd
805 print "Updating local passwd file (if any).\n" if $opt_v;
806 if (!sysopen(PWDLCK, $passwdlck, O_WRONLY|O_CREAT, 0600)) {
807 warn "Unable to lock password file: $!\n";
808 } else {
809 $lockstr = pack "ssLLiiLLLL", F_WRLCK, 0, 0, 0, 0, 0, 0, 0, 0, 0;
810 eval {
811 local $SIG{ARLM} = sub {
812 die "alarm while locking password file\n"
814 alarm 15;
815 fcntl PWDLCK, F_SETLKW, $lockstr ||
816 die "cannot lock password file: $!\n";
817 alarm 0;
819 if ($@) {
820 warn $@;
821 } else {
822 warn "Password update failed.\n"
823 if (system("sed -f $sedpasswd < $passwd > ${passwd}.new") ||
824 !(rename "${passwd}.new", $passwd));
826 $lockstr = pack "ssLLiiLLLL", F_UNLCK, 0, 0, 0, 0, 0, 0, 0, 0, 0;
827 fcntl PWDLCK, F_SETLK, $lockstr;
828 close PWDLCK;
830 if (($ypmaster = `/usr/bin/ypwhich 2>/dev/null`) && $? == 0) {
831 $ypmaster =~ /(.*)\n/;
832 ($ypmaster) = gethostbyname($1);
833 ($thishost) = gethostbyname(hostname);
834 if ($ypmaster eq $thishost) {
835 system("cd /var/yp && make")
836 if yesno("Rebuild NIS/YP maps", $opt_y);
837 } else {
838 warn "Not running on NIS/YP master $1; unable to update user shells\n";
839 print "Use 'sed -f $sedpasswd <$passwd >${passwd}.new' on the master\n";
840 print "and then remake the NIS/YP database.\n";
841 undef $sedpasswd;
844 unlink $sedpasswd if $sedpasswd;
847 # Show usage message.
848 sub usage
850 print "Usage:\n\n";
851 print "\t$0 [-rsvy]\n\n";
852 print " -n - non-interactive mode.\n";
853 print " -r - revert back to aspppd configuration.\n";
854 print " -s - use strict translation.\n";
855 print " -v - print more detail of the operations performed.\n";
856 print " -y - assume 'yes' as default answer where reasonable.\n";
857 exit;
860 # Correct an environment variable so that it points at either a useful
861 # executable program, or nothing at all.
862 sub fixpath
864 local ($prog, $deflt) = @_;
866 $prog = $deflt if $prog eq "";
867 if ($prog !~ /^(\.\/|\/)/) {
868 local ($_) = $ENV{PATH};
869 $_ = "/bin:/usr/bin:/sbin:/usr/sbin" if $_ eq "";
870 split /:/;
871 foreach (@_) {
872 $prog = $_ . "/" . $prog, last if -x $_ . "/" . $prog;
875 $prog = "" if !(-x $prog);
876 $prog;
879 getopts('nrsvy') || usage;
881 die "Need permission to modify system files.\n"
882 unless ($> == 0 || yesno "This script should be run as root. Continue");
884 if ($opt_r) {
885 local ($intemp);
887 # Revert to previous configuration. Just rename the aspppd file back
888 # and undo changes to the passwd file.
890 die "No saved aspppd configuration exists.\n" unless -f $asmoved;
891 if (-e $astemp) {
892 die "$astemp is not a file\n" unless -f $asfile;
893 unlink $astemp || die "Cannot remove temporary $astemp: $!\n";
895 $intemp = 0;
896 if (-e $asfile) {
897 die "$asfile is not a file\n" unless -f $asfile;
898 die "Not modifying configuration.\n"
899 unless yesno "Remove existing $asfile", $opt_y;
900 rename $asfile, $astemp || die "Cannot rename existing $asfile: $!\n";
901 $intemp = 1;
904 if (rename $asmoved, $asfile) {
905 unlink $astemp || warn "$astemp: $!\n" if $intemp;
906 } else {
907 $failure = "Cannot rename $asmoved to $asfile: $!\n";
908 rename $astemp, $asfile ||
909 die "$failure\nand cannot recover: $!\n" .
910 "Saved current asppp.cf in $astemp\n"
911 if $intemp;
912 die $failure;
915 $( = $);
916 $< = $>;
918 system($pppdctl, "stop") if -x $pppdctl;
919 # remove pppd autostart files.
920 unlink $pppdir . "ifconfig";
921 unlink $pppdir . "demand";
923 system($asctl, "start") if -x $asctl;
925 open SEDFILE, ">$sedpasswd" || die "Cannot write $sedpasswd: $!\n";
926 local ($escdir) = $pppdir;
927 $escdir =~ s+/+\\/+g;
928 print SEDFILE "/${escdir}dial-in\\./s+[^:]*\$+/usr/sbin/aspppls+\n";
929 print SEDFILE "/\\/usr\\/bin\\/pppd/s+[^:]*\$+/usr/sbin/aspppls+\n";
930 close SEDFILE;
932 rewrite_passwd;
934 exit 0;
937 $aspppcf = $asfile;
938 if (!(-f $asfile)) {
939 die "No aspppd configuration exists; nothing to convert.\n"
940 unless -f $asmoved;
941 die "No changes made.\n"
942 unless yesno "Already converted; rerun anyway";
943 $aspppcf = $asmoved;
946 print "This script provides only a suggested translation for your existing aspppd\n";
947 print "configuration. You will need to evaluate for yourself whether the translation\n";
948 print "is appropriate for your operating environment.\n";
949 die "No changes made.\n"
950 unless yesno "Continue", 1;
952 # Read in the asppp.cf file first; there's no reason to continue on to
953 # the UUCP files if this file isn't readable or has no paths defined.
954 local($ifconfig, $paths) = readaspppcf($aspppcf);
956 # Loop over the ifconfigs and build a list of the down ones.
957 foreach $intf (keys %$ifconfig) {
958 local(@words) = @{$$ifconfig{$intf}};
959 while ($word = shift @words) {
960 shift @words if $ifconfigtakes{$word};
961 if ($word =~ /^down$/) {
962 warn("Why is $intf declared down?\n"), last
963 if $intf =~ /^ipd[0-9]+$/;
964 $downif{$intf} = $$ifconfig{$intf};
965 delete $$ifconfig{$intf};
966 last;
971 # Read /etc/passwd for dial-in users configured for aspppd.
972 local($dialinusers) = readpasswd;
974 # Read in existing pppd configuration. All we really care about
975 # is the setting of the "auth" option.
976 undef $authoption;
977 if (open(OPTIONS,"<" . $options)) {
978 while (@{$words = uucpline(OPTIONS, $options)}) {
979 while ($_ = pop @$words) {
980 $authoption = $_ if /auth/i;
983 close OPTIONS;
984 $authoption = "unknown" if !defined($authoption);
987 $dialin_auth = 0;
988 if ($authoption =~ /^auth$/i) {
989 $dialin_auth = 1;
990 } elsif ($authoption =~ /^noauth$/i) {
991 $dialin_auth = 2;
992 } elsif (defined($authoption)) {
993 $dialin_auth = 3;
996 # Check that there's a path for each dial in user
997 foreach $user (keys %$dialinusers) {
998 if (!defined($$paths{$user})) {
999 warn "Dial-in user ", $user,
1000 " does not have a corresponding dial-in path.\n";
1001 delete $$dialinusers{$user};
1002 next;
1004 $intf = ${$$paths{$user}}{"interface"};
1005 if ($intf eq "ipdptp*") {
1006 if (0+keys(%downif) == 0) {
1007 warn "Dial-in user $path has no available \"down\" interfaces.\n";
1008 delete $$dialinusers{$user};
1009 next;
1011 } else {
1012 if (!defined($downif{$intf}) && !defined($$ifconfig{$intf})) {
1013 warn "Dial-in path $user has undefined $intf; deleted.\n";
1014 delete $$dialinusers{$user};
1015 next;
1018 ${$$paths{$user}}{$isdialin} = 1;
1019 # 0 - no info (no options file, "noauth" on call)
1020 # 1 - all auth ("auth" in options, "noauth" on call)
1021 # 2 - all noauth ("noauth" in options)
1022 # 3 - mixed; use auth ("noauth" in options, wrapper script for "auth")
1023 if (${$$paths{$user}}{require_authentication}) {
1024 if ($dialin_auth == 2) {
1025 $dialin_auth = 3;
1026 } elsif ($dialin_auth == 0) {
1027 $dialin_auth = 1;
1029 } else {
1030 if ($dialin_auth == 1) {
1031 $dialin_auth = 3;
1032 } elsif ($dialin_auth == 0) {
1033 $dialin_auth = 2;
1038 # Get lists of usable dial-in and dial-out ports.
1039 local($dialin,$dialout) = getserialports;
1041 # Read and parse the UUCP Sysfiles, Devconfig, and Limits files.
1042 # These are keyed with the "service=" string. The Sysfiles file can
1043 # augment or override the list of files read for a given service.
1044 print "Reading UUCP configuration.\n" if $opt_v;
1045 @sysfiles = @{${uucpkeyfile($Sysfiles,"service=")}{"ppp"}};
1046 @limits = @{${uucpkeyfile($Limits,"service=")}{"ppp"}};
1047 %devconfig = %{uucpkeyfile($Devconfig,"service=ppp","device=")};
1049 # Now read in the UUCP files corresponding to this service.
1050 $systems = uucpposfiles(uucpfiles("systems"));
1051 $dialers = uucpposfiles(uucpfiles("dialers"));
1052 $dialcodes = uucpposfiles($Dialcodes);
1053 $devices = uucpdevices(uucpfiles("devices"));
1055 # just to make sure
1056 $$dialcodes{""} = ();
1058 # Loop over paths. Dial-out only paths are translated into demand-dial
1059 # configurations. Dial-in only paths are translated into appropriate
1060 # log-in entries.
1061 local (@bidirectional);
1062 foreach $peer (keys %$paths) {
1063 if (exists($$systems{$peer})) {
1064 $sline = $$systems{$peer};
1065 if ($$sline[0] eq "Never") {
1066 if (${$$paths{$peer}}{$isdialin}) {
1067 translatedialin($peer);
1068 } else {
1069 print "We never call $peer, and he never calls us.\n"
1070 if $opt_v;
1072 delete $$paths{$peer};
1073 next;
1075 push @bidirectional, $peer if ${$$paths{$peer}}{$isdialin};
1076 print "Ignoring time restriction on $peer\n"
1077 if $$sline[0] ne "Any";
1078 $dlist = $$devices{$$sline[1]};
1079 $class = $$sline[2];
1080 $i = 0;
1081 while ($i < @$dlist) {
1082 local($dev) = $$dlist[$i];
1083 if ($$dev[1] ne "-") {
1084 print "Ignoring device $$dev[0]; 801-type not supported.\n";
1085 splice @$dlist, $i, 1;
1086 next;
1088 $i++;
1090 # Make sure that classes match.
1091 next if $$dev[2] ne "Any" && $class ne "Any" && $$dev[2] ne $class;
1092 # Prepend "/dev/" if it's not present in the device name.
1093 if (exists($$dialout{$$dev[0]})) {
1094 # This just seems odd.
1095 $dname = $$dialout{$$dev[0]};
1096 $dname =~ s+/dev/term/+/dev/cua/+;
1097 } else {
1098 $dname = ($$dev[0] =~ m+^/+ ? $$dev[0] : ("/dev/" . $$dev[0]));
1100 # Skip devices that aren't supposed to be used for dial-out.
1101 next if $dname =~ m+^/dev/term/+;
1102 next if $dname =~ m+^/dev/tty[a-z]$+;
1103 # Make sure this is a character device and we have access to it.
1104 next unless -w $dname && -c $dname;
1105 warn "Dialer for $$dev[3] is missing.\n"
1106 unless exists($warned{$$dev[3]}) ||
1107 exists($$dialers{$$dev[3]});
1108 $warned{$$dev[3]} = 1;
1110 # Expand keywords from Dialcodes file. Should have \T or \D.
1111 $phone = $$sline[3];
1112 $xphone = ($$dev[4] eq "\\T" && $phone =~ /^([A-Za-z]*)(.*)$/ ?
1113 "@{$$dialcodes{$1}}" . $2 : $phone);
1115 # Make a copy of the dialing script.
1116 local(@dials) = @{$$dialers{$$dev[3]}};
1118 # Translate dial tone and wait characters from Dialers file.
1119 $_ = shift @dials;
1120 s[(.)(.)]{
1121 local($from,$to) = ($1,$2);
1122 $phone =~ s+(^|[^\\])$from+$1$to+gx;
1123 $xphone =~ s+(^|[^\\])$from+$1$to+gx;
1124 }ge;
1126 # Translate escapes in dial specification. Chat has a \T,
1127 # but uses \U instead of \D.
1128 local($needt, $needu, $isexpect, @chats) = ("", "", 1);
1129 foreach $str (@dials) {
1130 push(@chats, "") if $str eq "";
1131 local ($ostr) = "";
1132 if ($isexpect) {
1133 while ($str =~ s/([^\\]*)\\(.)//) {
1134 local($lead, $_) = ($1, $2);
1135 /[Mm]/ ? ($ostr .= $lead) :
1136 /[Ee]/ ? ($sorrye = 1, $ostr .= $lead) :
1137 ($ostr .= $lead . "\\" . $_);
1139 } else {
1140 while ($str =~ s/([^\\]*)\\(.)//) {
1141 local($lead, $_) = ($1, $2);
1142 /T/ ? ($needt = " -T '$xphone'",
1143 $ostr .= $lead . "\\T") :
1144 /D/ ? ($needu = " -U '$phone'",
1145 $ostr .= $lead . "\\U") :
1146 /M/ ? ($ostr .= $lead,
1147 ($ostr ne "" ? push(@chats, $ostr, "\\c"):0),
1148 push(@chats, "HANGUP", "OFF"), $ostr = "") :
1149 /m/ ? ($ostr .= $lead,
1150 ($ostr ne "" ? push(@chats, $ostr, "\\c"):0),
1151 push(@chats, "HANGUP", "ON"), $ostr = "") :
1152 /[Ee]/ ? ($sorrye = 1, $ostr .= $lead) :
1153 /[dp]/ ? ($ostr .= $lead . "\\" . $_ . "\\" . $_) :
1154 /c/ ? ($str eq "" ? ($ostr .= $lead . "\\c") : 0) :
1155 ($ostr .= $lead . "\\" . $_);
1158 $ostr .= $str;
1159 push @chats, $ostr if $ostr ne "";
1160 $isexpect = !$isexpect;
1163 # Pad out dial list if we're missing a "send" string and tack
1164 # on the chat list from the Systems file.
1165 if (defined $$sline[4]) {
1166 push @chats, "\\c" if !$isexpect;
1167 push @chats, (splice @$sline, 4);
1170 $chatfile = $pppdir . "chat.$peer.$$dev[3]";
1171 if (-e $chatfile) {
1172 print "$chatfile already exists.\n";
1173 if (!yesno("Should it be overwritten",$opt_y)) {
1174 if (yesno("Should it be used as-is")) {
1175 warn "Using $chatfile as-is; it may not be correct.\n";
1176 } else {
1177 for ($n = 0; ; $n++) {
1178 last if !(-e $chatfile . "." . $n);
1180 $chatfile .= "." . $n;
1181 print "Using $chatfile instead.\n";
1182 $chatfiles{$chatfile} = \@chats;
1184 } else {
1185 $overwrite{$chatfile} = 1;
1186 $chatfiles{$chatfile} = \@chats;
1188 } else {
1189 $chatfiles{$chatfile} = \@chats;
1192 push @pppdargs, $dname;
1193 push @pppdargs, $class if $class =~ /^[0-9]+$/;
1194 push @pppdargs, "demand";
1195 convert_options(\@pppdargs,$$paths{$peer},
1196 $chatfile . $needt . $needu, undef);
1198 $optname = $peersdir . $peer;
1199 if (-e $optname) {
1200 print "$optname already exists.\n";
1201 if (!yesno("Should it be overwritten", $opt_y)) {
1202 if (yesno("Should it be used as-is")) {
1203 warn "Using $optname as-is; it may not be correct.\n";
1204 } else {
1205 for ($n = 0; ; $n++) {
1206 last if !(-e $optname . "." . $n);
1208 $optname .= "." . $n;
1209 print "Using $optname instead.\n";
1210 $optfiles{$optname} = \@pppdargs;
1212 } else {
1213 $overwrite{$optname} = 1;
1214 $optfiles{$optname} = \@pppdargs;
1216 } else {
1217 $optfiles{$optname} = \@pppdargs;
1219 $scriptfiles{$pppdir . "demand"} .= "/usr/bin/pppd file $optname\n";
1220 last;
1222 } elsif (${$$paths{$peer}}{$isdialin}) {
1223 translatedialin($peer);
1224 } else {
1225 warn "Path $peer has no dial-in user nor Systems file entry.\n";
1226 delete $$paths{$peer};
1230 warn "Chat cannot do echo checking; requests for this removed.\n" if $sorrye;
1232 if (@bidirectional) {
1233 print "\nWarning: The following paths are bidirectional:\n";
1234 print "\t@bidirectional\n\n";
1235 print "Bidirectional paths (with entries in both Systems and passwd) do not translate\n";
1236 print "into Solaris PPP 4.0 semantics in an exact manner. The dial-out portion will\n";
1237 print "use the designated interface, but the dial-in portion will use any available\n";
1238 print "interface.\n";
1239 while ($peer = pop @bidirectional) {
1240 delete $ {$$paths{$peer}}{interface};
1241 translatedialin($peer);
1245 translateifconfig;
1247 # Create an /etc/ppp/options if we need to.
1248 if (!defined($authoption) && $dialin_auth > 0) {
1249 local (@pppdopts);
1250 push @pppdopts, "lock";
1251 push @pppdopts, "auth" if $dialin_auth == 1;
1252 push @pppdopts, "noauth" if $dialin_auth > 1;
1253 $optfiles{$options} = \@pppdopts;
1255 # Translate option files to plain text.
1256 foreach $file (keys %optfiles) {
1257 local ($opts) = $optfiles{$file};
1258 local ($cstr) = "";
1259 $cstr .= shift(@$opts) . "\n" while @$opts;
1260 $optfiles{$file} = $cstr;
1262 # Change "auth" to "noauth" or add "noauth" to /etc/ppp/options.
1263 if (defined($authoption) && $authoption ne "noauth" && $dialin_auth == 3) {
1264 local(@triplet, $cstr);
1265 if ($authoption eq "unknown") {
1266 warn "Adding 'noauth' to $options\n";
1267 } else {
1268 warn "Changing 'auth' in $options to 'noauth'\n";
1270 open(OPTIONS,"<" . $options) || die "$options disappeared: $!\n";
1271 while (@{$words = uucpline(OPTIONS, $options, \@triplet)}) {
1272 $cstr .= $triplet[0];
1273 if (grep(/auth/, @$words)) {
1274 local(@newwords) = map { $_ = "noauth" if /auth/; $_ } @$words;
1275 $cstr .= "@newwords";
1276 } else {
1277 $cstr .= $triplet[1];
1279 while (pop @$words) {
1280 $authoption = $_ if /auth/i;
1282 $cstr .= $triplet[2];
1284 $cstr .= $triplet[0] . $triplet[2];
1285 close OPTIONS;
1286 $cstr .= "\n" if $cstr !~ /\n$/;
1287 $cstr .= "noauth\n" if $authoption eq "unknown";
1288 $optfiles{$options} = $cstr;
1291 # Create a sed script to fix the users' shell paths.
1292 if (0+(keys %dialinshell) != 0) {
1293 $cstr = "";
1294 foreach $peer (keys %dialinshell) {
1295 $cstr .= "/^$peer:/s+[^:]*/aspppls\$+$dialinshell{$peer}+\n";
1297 $scriptfiles{$sedpasswd} = $cstr;
1300 print "\nPreparing to write out translated configuration:\n";
1302 # Enumerate the files we'll write.
1303 $nfiles = 0;
1304 if (0+(keys %chatfiles) != 0) {
1305 print " ";
1306 nof 0+(keys %chatfiles), "chat file", ":\n";
1307 foreach $file (keys %chatfiles) {
1308 $nfiles++;
1309 print "\t$nfiles. $file\n";
1310 local ($chats) = $chatfiles{$file};
1311 local ($cstr) = "";
1312 while (@$chats) {
1313 $cstr .= requote(shift(@$chats));
1314 $cstr .= " " . requote(shift(@$chats)) if @$chats;
1315 $cstr .= "\n";
1317 local (@filerec) = ( $file, $cstr );
1318 push @allfiles, \@filerec;
1321 if (0+(keys %optfiles) != 0) {
1322 print " ";
1323 nof 0+(keys %optfiles), "option file", ":\n";
1324 foreach $file (keys %optfiles) {
1325 $nfiles++;
1326 print "\t$nfiles. $file\n";
1327 local (@filerec) = ( $file, $optfiles{$file} );
1328 push @allfiles, \@filerec;
1331 if (0+(keys %scriptfiles) != 0) {
1332 print " ";
1333 nof 0+(keys %scriptfiles), "script file", ":\n";
1334 foreach $file (keys %scriptfiles) {
1335 $nfiles++;
1336 print "\t$nfiles. $file\n";
1337 local (@filerec) = ( $file, $scriptfiles{$file} );
1338 push @allfiles, \@filerec;
1342 # Merge new secrets needed with existing ones, if any.
1343 sub merge_secrets
1345 local ($addsecrets, $fname) = @_;
1346 local ($file, $cstr, @triplet, $newsecret);
1348 $nfiles++;
1349 $file = $pppdir . $fname;
1350 print "\t$nfiles. $file\n";
1351 if (open(SECRETS, '<' . $pppdir . $fname)) {
1352 while (@{$words = uucpline(SECRETS, $pppdir . $fname, \@triplet)}) {
1353 $cstr .= $triplet[0];
1354 $newsecret = $ {$$addsecrets{$$words[0]}}{$$words[1]};
1355 if (defined $newsecret) {
1356 $cstr .= requote($$words[0]) . " " . requote($$words[1]) .
1357 " " . $newsecret;
1358 delete $ {$$addsecrets{$$words[0]}}{$$words[1]};
1359 } else {
1360 $cstr .= $triplet[1];
1362 $cstr .= $triplet[2];
1364 close SECRETS;
1365 $cstr .= $triplet[0] . $triplet[2];
1367 foreach $key1 (keys (%$addsecrets)) {
1368 foreach $key2 (keys (%{$$addsecrets{$key1}})) {
1369 $cstr .= requote($key1) . " " . requote($key2) . " " .
1370 $ {$$addsecrets{$key1}}{$key2} . "\n";
1373 local (@filerec) = ( $file, $cstr );
1374 push @allfiles, \@filerec;
1377 $nchap = 0+(keys %chapsecrets) != 0;
1378 $npap = 0+(keys %papsecrets) != 0;
1379 if ($nchap != 0 || $npap != 0) {
1380 print " ";
1381 nof $nchap + $npap, "secrets file", ":\n";
1382 merge_secrets(\%chapsecrets, "chap-secrets") if $nchap != 0;
1383 merge_secrets(\%papsecrets, "pap-secrets") if $npap != 0;
1386 die "Nothing to write back; I'm done.\n" if $nfiles == 0;
1388 $PAGER = fixpath($ENV{PAGER}, "/usr/bin/less");
1389 $EDITOR = fixpath($ENV{EDITOR}, "/usr/bin/vi");
1390 $SHELL = fixpath($ENV{SHELL}, "/usr/bin/ksh");
1392 END {
1393 if ($tempname) {
1394 unlink($tempname) or
1395 die "Cannot remove temporary file $tempname: $!\n";
1399 sub show_file_options
1401 print "\nEnter option number:\n";
1402 print "\t1 - view contents of file on standard output\n";
1403 print "\t2 - view contents of file using $PAGER\n" if $PAGER ne "";
1404 print "\t3 - edit contents of file using $EDITOR\n" if $EDITOR ne "";
1405 print "\t4 - delete/undelete file from list\n";
1406 print "\t5 - rename file in list\n";
1407 print "\t6 - show file list again\n";
1408 print "\t7 - escape to shell (or \"!cmd\")\n";
1409 print "\t8 - abort without saving anything\n";
1410 print "\t9 - save all files and exit (default)\n";
1413 # If interactive, then allow user to view and modify converted data.
1414 if ((-t STDIN) && (-t STDOUT) && !$opt_n) {
1415 show_file_options();
1416 while (1) {
1417 print "Option: ";
1418 chomp($ans = <STDIN>);
1419 if ($ans eq "?" || $ans =~ /^h/i) {
1420 show_file_options();
1421 next;
1423 if ($ans eq "") {
1424 last if yesno "Saving all files. Are you sure";
1425 next;
1427 last if $ans == 9;
1428 print("Aborted.\n"), exit if $ans == 8;
1429 if ($ans =~ /^!/ || $ans == 7) {
1430 if ($ans =~ /^!(.+)/) {
1431 system($1);
1432 } else {
1433 print("Interactive shell access not permitted here.\n"), next
1434 if $< != $>;
1435 system($SHELL);
1437 } elsif ($ans == 6) {
1438 for ($i = 0; $i < $nfiles; $i++) {
1439 print "\t", $i+1, ". $allfiles[$i][0]",
1440 ($deleted[$i] ? " (deleted)" : ""), "\n";
1442 } elsif ($ans > 0 && $ans < 6) {
1443 $fnum = 0;
1444 if ($nfiles > 1) {
1445 print "File number (1 .. $nfiles): ";
1446 chomp($fnum = <STDIN>);
1447 if ($fnum < 1 || $fnum > $nfiles) {
1448 print "Unknown file (must be 1 to $nfiles).\n";
1449 next;
1451 $fnum--;
1453 if ($ans == 5) {
1454 print "Current name is $allfiles[$fnum][0]\n";
1455 print "New name: ";
1456 chomp($fname = <STDIN>);
1457 print("Unchanged\n"), next if $fname eq "";
1458 $allfiles[$fnum][0] = $fname;
1460 if ($deleted[$fnum]) {
1461 if (yesno("File " . $fnum+1 .
1462 " ($allfiles[$fnum][0]) is deleted; undelete",1)) {
1463 undef $deleted[$fnum];
1465 next;
1467 if ($ans == 1) {
1468 print $allfiles[$fnum][1];
1469 } elsif ($ans == 2 && $PAGER ne "") {
1470 $i = 0;
1471 do {
1472 if (++$i > 5) {
1473 warn "Unable to open temporary file: $!";
1474 undef $tempname;
1475 last;
1477 $tempname = tmpnam();
1478 } until sysopen(FH, $tempname, O_RDWR|O_CREAT|O_EXCL);
1479 next if !$tempname;
1480 print FH $allfiles[$fnum][1];
1481 close FH;
1482 system($PAGER, $tempname);
1483 unlink($tempname) ||
1484 warn "Trouble removing temporary file: $!";
1485 undef $tempname;
1486 } elsif ($ans == 3 && $EDITOR ne "") {
1487 $i = 0;
1488 do {
1489 if (++$i > 5) {
1490 warn "Unable to open temporary file: $!";
1491 undef $tempname;
1492 last;
1494 $tempname = tmpnam();
1495 } until sysopen(FH, $tempname, O_RDWR|O_CREAT|O_EXCL);
1496 next if !$tempname;
1497 chown $<, $(, $tempname;
1498 print FH $allfiles[$fnum][1];
1499 close FH;
1500 $i = system($EDITOR, $tempname);
1501 if ($i == 0) {
1502 if (open FH, "<" . $tempname) {
1503 read FH, $allfiles[$fnum][1], (-s $tempname);
1504 close FH;
1506 } else {
1507 print "Editor dropped core.\n" if $? & 128;
1508 print "Editor terminated on signal ", $? & 127, "\n"
1509 if $? & 127;
1510 print "Editor returned error ", $? >> 8, "\n"
1511 if $? >> 8;
1513 unlink($tempname) ||
1514 warn "Trouble removing temporary file: $!";
1515 undef $tempname;
1516 } elsif ($ans == 4) {
1517 $deleted[$fnum] = 1;
1523 print "\n";
1525 # Interactive part is over. Become real.
1526 $( = $);
1527 $< = $>;
1529 print "Stopping aspppd\n" if $opt_v;
1530 system($asctl, "stop") if -x $asctl;
1532 print "Saving all files\n" if $opt_v;
1533 for ($i = 0; $i < $nfiles; $i++) {
1534 $filerec = $allfiles[$i];
1535 if ($deleted[$i]) {
1536 delete $scriptfiles{$$filerec[0]};
1537 next;
1539 print "Saving $$filerec[0]\n" if $opt_v;
1540 $$filerec[0] =~ m+(.*)/+;
1541 if ($1 eq "") {
1542 # this is ok; just a top level file
1543 } elsif (!(-d $1)) {
1544 local ($exdir) = $1;
1545 while ($exdir && !(-d $exdir)) {
1546 $exdir =~ m+(.*)/+;
1547 $exdir = $1;
1549 if ($exdir) {
1550 local ($dir) = $1;
1551 $dir =~ m+$exdir/([^/]*)(.*)+;
1552 local ($tomake, $rest) = ($1, $2);
1553 mkdir $exdir . "/" . $tomake, 0775;
1554 if ($! == ENOSYS) {
1555 warn "Unable to make directory $exdir/$tomake; automount point.\n";
1556 next;
1558 if ($! != 0) {
1559 warn "Unable to make directory $exdir/$tomake: $!\n";
1560 next;
1562 if (system("mkdir", "-p", $dir) != 0) {
1563 warn "Failed to make $dir\n";
1564 next;
1566 } else {
1567 warn "$1 doesn't appear to have a useful path.\n";
1568 next;
1571 undef $fileerr;
1572 local ($fname) = $$filerec[0];
1573 if (-e $fname && !$overwrite{$chatfile}) {
1574 print "$fname already exists.\n"
1575 if (-t STDIN) && (-t STDOUT) && !$opt_n;
1576 if (!yesno("Should it be overwritten",$opt_y)) {
1577 warn "Using $fname as-is; it may not be correct.\n";
1578 next;
1581 if (sysopen(OUTFILE, $$filerec[0], O_WRONLY|O_CREAT|O_TRUNC, 0600)) {
1582 print OUTFILE $$filerec[1] || ($fileerr = $!);
1583 close OUTFILE || ($fileerr = $!);
1584 } else {
1585 $fileerr = $!;
1587 warn "Unable to write $$filerec[0]: $fileerr\n" if $fileerr;
1590 local(@scripts) = keys %scriptfiles;
1591 if (@scripts) {
1592 print "Making scripts executable\n" if $opt_v;
1593 system("chmod", "u+x", @scripts);
1596 rewrite_passwd if exists($scriptfiles{$sedpasswd});
1598 # clean up after a previous translation.
1599 unlink $pppdir . "ifconfig" if !$scriptfiles{$pppdir . "ifconfig"};
1600 unlink $pppdir . "demand" if !$scriptfiles{$pppdir . "demand"};
1602 (rename($asfile, $asmoved) || warn "Cannot move $asfile: $!\n")
1603 if $aspppcf ne $astemp;
1605 system($pppdctl, "start") if -x $pppdctl;
1607 # use Dumpvalue;
1608 # my $dumper = new Dumpvalue;
1609 # $dumper->set(globPrint => 1);
1610 # $dumper->dumpValue($ifconfig);