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