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 2009 Sun Microsystems, Inc. All rights reserved. 14# Use is subject to license terms. 15# 16 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 25 26# Known issues: 27# - translation with point-to-multipoint is incomplete 28 29use Getopt::Std; 30use Fcntl; 31use POSIX qw(tmpnam ENOSYS); 32use Sys::Hostname; 33 34# Secure the path if we're running under RBAC. 35$ENV{PATH} = ( "/bin", "/sbin", "/usr/bin", "/usr/sbin", "/usr/ucb" ) 36 if $< != $>; 37 38# General path names that can be configured. 39local($rootetc) = "/etc/"; 40local($passwd) = $rootetc . "passwd"; 41local($passwdlck) = $rootetc . ".pwd.lock"; 42local($asfile) = $rootetc . "asppp.cf"; 43local($astemp) = $rootetc . "asppp.temp.cf"; 44local($asmoved) = $rootetc . "asppp.saved.cf"; 45local($uucpdir) = $rootetc . "uucp/"; 46local($Devices) = $uucpdir . "Devices"; 47local($Devconfig) = $uucpdir . "Devconfig"; 48local($Dialers) = $uucpdir . "Dialers"; 49local($Dialcodes) = $uucpdir . "Dialcodes"; 50local($Limits) = $uucpdir . "Limits"; 51local($Sysfiles) = $uucpdir . "Sysfiles"; 52local($Systems) = $uucpdir . "Systems"; 53local($pppdir) = $rootetc . "ppp/"; 54local($options) = $pppdir . "options"; 55local($ttyprefix) = $pppdir . "options."; 56local($peersdir) = $pppdir . "peers/"; 57local($initd) = $rootetc . "init.d/"; 58local($asctl) = $initd . "asppp"; 59local($pppdctl) = $initd . "pppd"; 60local($sedpasswd) = "/tmp/sed-passwd"; 61 62# Fake asppp keyword used to keep track of dial-in paths. 63local($isdialin) = "-is-dial-in"; 64 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. 69 70# Internal data structures 71local(@sysfiles,@limits,@devconfig); 72local(@sysdefault) = ( "systems=" . $Systems, "dialers=" . $Dialers, 73 "devices=" . $Devices ); 74local(@limitdefault) = ( "max=-1" ); 75local(@devdefault) = ( "pop=", "push=", "connecttime=-1", "expecttime=-1", 76 "msgtime=-1" ); 77 78# List of keywords for which ifconfig takes an additional parameter. 79local($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, 98 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 129); 130 131# print number of something in English. 132sub nof 133{ 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; 138} 139 140# ask a yes or no question. 141sub yesno 142{ 143 local ($query, $default) = @_; 144 local ($ans, $defans); 145 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"; 155 } 156} 157 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. 161sub requote 162{ 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; 172 } 173 "\"" . $_ . "\""; 174 } elsif (/^"/ || /[^\\]"/ || /\\\\"/) { 175 "'" . $_ . "'"; 176 } elsif (/\s/) { 177 "\"" . $_ . "\""; 178 } else { 179 $_; 180 } 181} 182 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. 189sub uucpline 190{ 191 local($input, $file, $triplet) = @_; 192 local(@words,$snippet,$field,$havefield,$cont,@triparray,$maytrail); 193 194 $cont = ""; 195 $maytrail = ""; 196 while (<$input>) { 197 # remove leading whitespace 198 if (s/^(\s+)//) { 199 $maytrail .= $1; 200 } 201 if ($cont eq "") { 202 if (s/^(\#(.|\n)*)$//) { 203 $triparray[0] .= $maytrail . $1; 204 $maytrail = ""; 205 next; 206 } 207 if (s/^(\\?\n?)$//) { 208 $triparray[0] .= $maytrail . $1; 209 $maytrail = ""; 210 next; 211 } 212 $triparray[0] .= $maytrail; 213 $maytrail = ""; 214 } 215 $snippet = $_; 216 if (s/^(([^\#\\]|\\.)*)\\\n$//) { 217 $maytrail .= $snippet; 218 $cont .= $1; 219 next; 220 } 221 if (/^(([^\\\#]|\\[^\#])*)(\#?(.|\n)*)$/) { 222 $_ = $maytrail . $1; 223 $maytrail = $3; 224 if (s/((\s|\n)*)$//) { 225 $maytrail = $1 . $maytrail; 226 } 227 $triparray[1] = $_; 228 } 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; 254 } 255 $havefield = 1; 256 $field .= $snippet; 257 } 258 push(@words, $field) if $havefield; 259 $havefield = 0; 260 $field = ''; 261 } 262 last; 263 } 264 $triparray[2] .= $maytrail; 265 @$triplet = @triparray; 266 warn "Bad continuation line in $file: $cont\n" if $cont ne ''; 267 \@words; 268} 269 270# Given a logical UUCP file name, return a list of all of the files 271# that should be read. 272sub uucpfiles 273{ 274 local ($file) = @_; 275 local (@flist, $value) = (); 276 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; 283 } 284 last; 285 } 286 } 287 @flist; 288} 289 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. 294sub uucpkeyfile 295{ 296 local($file,@keylist) = @_; 297 local($lastkey,$keyval,$words,$i,$flag,%byservice); 298 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; 311 } 312 $i++; 313 } 314 last unless $flag; 315 } 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; 322 } 323 } 324 } 325 close SVCFILE; 326 \%byservice; 327} 328 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. 334sub uucpposfiles 335{ 336 local(@files) = @_; 337 local($keyval,$words,%keyeddata); 338 339 foreach $file (@files) { 340 if (!open(POSFILE, '<' . $file)) { 341 warn "$file: $!\n"; 342 next; 343 } 344 while (@{$words = uucpline(POSFILE, $file)}) { 345 $keyval = shift @{$words}; 346 next if $keyeddata{$keyval}; 347 $keyeddata{$keyval} = $words; 348 } 349 close POSFILE; 350 } 351 \%keyeddata; 352} 353 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. 358sub uucpdevices 359{ 360 local(@files) = @_; 361 local($keyval,$words,%keyeddata); 362 363 foreach $file (@files) { 364 if (!open(POSFILE, '<' . $file)) { 365 warn "$file: $!\n"; 366 next; 367 } 368 while (@{$words = uucpline(POSFILE, $file)}) { 369 $keyval = shift @{$words}; 370 push @{$keyeddata{$keyval}}, $words; 371 } 372 close POSFILE; 373 } 374 \%keyeddata; 375} 376 377# For a path defined in asppp.cf, copy over defaults, validate the 378# required options, and save in the hash to be returned. 379sub savepath 380{ 381 local($paths, $options, $defref) = @_; 382 local($peer,$intf); 383 384 return if $options == $defref; 385 foreach $key (keys %$defref) { 386 $$options{$key} = $$defref{$key} unless defined($$options{$key}); 387 } 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; 405} 406 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. 410sub readaspppcf 411{ 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 438 ); 439 local($words,$word,$prevword,$i,$errors,%defaults,%ifconfig,%paths); 440 local($options); 441 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; 459 } 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 ; 468 } 469 if (/^path$/i) { 470 local(%pathopts); 471 savepath(\%paths, $options, \%defaults); 472 $options = \%pathopts; 473 next; 474 } 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; 480 } 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; 497 } 498 } 499 } 500 } 501 } 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"; 510 } 511 close ASPPPD; 512 ( \%ifconfig, \%paths ); 513} 514 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. 518sub readpasswd 519{ 520 local(%users,@pwe); 521 522 setpwent(); 523 while (@pwe = getpwent()) { 524 $users{$pwe[0]} = $pwe[7] if $pwe[8] =~ /\/aspppls$/; 525 } 526 endpwent(); 527 nof 0+(keys %users), "aspppd dial in user", " found.\n" 528 if $opt_v; 529 \%users; 530} 531 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. 537sub getserialports 538{ 539 local(%dialin, %dialout); 540 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 ""; 547 } 548 } 549 close PMADM; 550 ( \%dialin, \%dialout ); 551} 552 553# Convert an ifconfig statement into a local and remote address pair. 554sub ifconf_addr 555{ 556 local($ifconf) = @_; 557 local($arg, $narg, $lcladdr, $remaddr); 558 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; 569 } 570 } elsif (!defined($lcladdr)) { 571 $lcladdr = $arg; 572 } elsif (!defined($remaddr)) { 573 $remaddr = $arg; 574 } 575 } 576 ( $lcladdr, $remaddr ); 577} 578 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. 582sub convert_options 583{ 584 local ($pppdargs, $opts, $chatpath, $user) = @_; 585 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}; 602 } 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; 613 } 614 } elsif ($dialin_auth < 2) { 615 push(@$pppdargs, "noauth"); 616 } 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}; 625 } 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 ""; 634 } 635 $psecret = $$opts{pap_password}; 636 } 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; 645 } 646 push @$pppdargs, "user " . requote($myname); 647 push(@$pppdargs, "remotename " . requote($peer)) 648 if $peer && !$$opts{require_authentication}; 649 } else { 650 $myname = "NeverAuthenticate"; 651 } 652 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\""); 661 } 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"; 670 } 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"); 678 } 679 } 680 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 = " *"; 688 } 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; 698 } 699 ${$$authf{$peer}}{$lclname} = requote($secret) . $addsecret; 700 } 701 if ($$opts{will_do_authentication} && !$passopt) { 702 ${$chapsecrets{$myname}}{$peer} = requote($csecret) if $csecret; 703 ${$papsecrets{$myname}}{$peer} = requote($psecret) if $psecret; 704 } 705} 706 707# Translate options for a dial-in user. 708sub translatedialin 709{ 710 local($peer) = @_; 711 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; 719 } 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; 726} 727 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. 732sub translateifconfig 733{ 734 local (@ifconfiglist,$cstr,$etherif,$intf,$ifconf,$addstr,$port); 735 736 open IFLIST, "/usr/sbin/ifconfig -au4|" || die "cannot run ifconfig: $!\n"; 737 while (<IFLIST>) { 738 $etherif = $1 if !$etherif && /^([^:]*).*UP.*BROADCAST/; 739 } 740 close IFLIST; 741 $etherif = $1 if !$etherif && glob("/etc/hostname.*") =~ /[^\.]*.(.*)/; 742 $etherif = $1 if !$etherif && glob("/etc/dhcp.*") =~ /[^\.]*.(.*)/; 743 $etherif = "hme0" if !$etherif; 744 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"; 751 } 752 } 753 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"; 761 } 762 } 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"; 770 } 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"; 779 } 780 } 781 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; 796 } 797 } 798 $scriptfiles{$pppdir . "ifconfig"} = $cstr if $cstr; 799} 800 801# Attempt to modify global passwd file using sed script stored in 802# the $sedpasswd temporary file. 803sub rewrite_passwd 804{ 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" 813 }; 814 alarm 15; 815 fcntl PWDLCK, F_SETLKW, $lockstr || 816 die "cannot lock password file: $!\n"; 817 alarm 0; 818 }; 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)); 825 } 826 $lockstr = pack "ssLLiiLLLL", F_UNLCK, 0, 0, 0, 0, 0, 0, 0, 0, 0; 827 fcntl PWDLCK, F_SETLK, $lockstr; 828 close PWDLCK; 829 } 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; 842 } 843 } 844 unlink $sedpasswd if $sedpasswd; 845} 846 847# Show usage message. 848sub usage 849{ 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; 858} 859 860# Correct an environment variable so that it points at either a useful 861# executable program, or nothing at all. 862sub fixpath 863{ 864 local ($prog, $deflt) = @_; 865 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; 873 } 874 } 875 $prog = "" if !(-x $prog); 876 $prog; 877} 878 879getopts('nrsvy') || usage; 880 881die "Need permission to modify system files.\n" 882 unless ($> == 0 || yesno "This script should be run as root. Continue"); 883 884if ($opt_r) { 885 local ($intemp); 886 887# Revert to previous configuration. Just rename the aspppd file back 888# and undo changes to the passwd file. 889 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"; 894 } 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; 902 } 903 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; 913 } 914 915 $( = $); 916 $< = $>; 917 918 system($pppdctl, "stop") if -x $pppdctl; 919 # remove pppd autostart files. 920 unlink $pppdir . "ifconfig"; 921 unlink $pppdir . "demand"; 922 923 system($asctl, "start") if -x $asctl; 924 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; 931 932 rewrite_passwd; 933 934 exit 0; 935} 936 937$aspppcf = $asfile; 938if (!(-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; 944} 945 946print "This script provides only a suggested translation for your existing aspppd\n"; 947print "configuration. You will need to evaluate for yourself whether the translation\n"; 948print "is appropriate for your operating environment.\n"; 949die "No changes made.\n" 950 unless yesno "Continue", 1; 951 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. 954local($ifconfig, $paths) = readaspppcf($aspppcf); 955 956# Loop over the ifconfigs and build a list of the down ones. 957foreach $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; 967 } 968 } 969} 970 971# Read /etc/passwd for dial-in users configured for aspppd. 972local($dialinusers) = readpasswd; 973 974# Read in existing pppd configuration. All we really care about 975# is the setting of the "auth" option. 976undef $authoption; 977if (open(OPTIONS,"<" . $options)) { 978 while (@{$words = uucpline(OPTIONS, $options)}) { 979 while ($_ = pop @$words) { 980 $authoption = $_ if /auth/i; 981 } 982 } 983 close OPTIONS; 984 $authoption = "unknown" if !defined($authoption); 985} 986 987$dialin_auth = 0; 988if ($authoption =~ /^auth$/i) { 989 $dialin_auth = 1; 990} elsif ($authoption =~ /^noauth$/i) { 991 $dialin_auth = 2; 992} elsif (defined($authoption)) { 993 $dialin_auth = 3; 994} 995 996# Check that there's a path for each dial in user 997foreach $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; 1003 } 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; 1010 } 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; 1016 } 1017 } 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; 1028 } 1029 } else { 1030 if ($dialin_auth == 1) { 1031 $dialin_auth = 3; 1032 } elsif ($dialin_auth == 0) { 1033 $dialin_auth = 2; 1034 } 1035 } 1036} 1037 1038# Get lists of usable dial-in and dial-out ports. 1039local($dialin,$dialout) = getserialports; 1040 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. 1044print "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=")}; 1048 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")); 1054 1055# just to make sure 1056$$dialcodes{""} = (); 1057 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. 1061local (@bidirectional); 1062foreach $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; 1071 } 1072 delete $$paths{$peer}; 1073 next; 1074 } 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; 1087 } 1088 $i++; 1089 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])); 1099 } 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; 1109 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); 1114 1115 # Make a copy of the dialing script. 1116 local(@dials) = @{$$dialers{$$dev[3]}}; 1117 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; 1125 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 . "\\" . $_); 1138 } 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 . "\\" . $_); 1156 } 1157 } 1158 $ostr .= $str; 1159 push @chats, $ostr if $ostr ne ""; 1160 $isexpect = !$isexpect; 1161 } 1162 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); 1168 } 1169 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); 1179 } 1180 $chatfile .= "." . $n; 1181 print "Using $chatfile instead.\n"; 1182 $chatfiles{$chatfile} = \@chats; 1183 } 1184 } else { 1185 $overwrite{$chatfile} = 1; 1186 $chatfiles{$chatfile} = \@chats; 1187 } 1188 } else { 1189 $chatfiles{$chatfile} = \@chats; 1190 } 1191 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); 1197 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); 1207 } 1208 $optname .= "." . $n; 1209 print "Using $optname instead.\n"; 1210 $optfiles{$optname} = \@pppdargs; 1211 } 1212 } else { 1213 $overwrite{$optname} = 1; 1214 $optfiles{$optname} = \@pppdargs; 1215 } 1216 } else { 1217 $optfiles{$optname} = \@pppdargs; 1218 } 1219 $scriptfiles{$pppdir . "demand"} .= "/usr/bin/pppd file $optname\n"; 1220 last; 1221 } 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}; 1227 } 1228} 1229 1230warn "Chat cannot do echo checking; requests for this removed.\n" if $sorrye; 1231 1232if (@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); 1242 } 1243} 1244 1245translateifconfig; 1246 1247# Create an /etc/ppp/options if we need to. 1248if (!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; 1254} 1255# Translate option files to plain text. 1256foreach $file (keys %optfiles) { 1257 local ($opts) = $optfiles{$file}; 1258 local ($cstr) = ""; 1259 $cstr .= shift(@$opts) . "\n" while @$opts; 1260 $optfiles{$file} = $cstr; 1261} 1262# Change "auth" to "noauth" or add "noauth" to /etc/ppp/options. 1263if (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"; 1269 } 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]; 1278 } 1279 while (pop @$words) { 1280 $authoption = $_ if /auth/i; 1281 } 1282 $cstr .= $triplet[2]; 1283 } 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; 1289} 1290 1291# Create a sed script to fix the users' shell paths. 1292if (0+(keys %dialinshell) != 0) { 1293 $cstr = ""; 1294 foreach $peer (keys %dialinshell) { 1295 $cstr .= "/^$peer:/s+[^:]*/aspppls\$+$dialinshell{$peer}+\n"; 1296 } 1297 $scriptfiles{$sedpasswd} = $cstr; 1298} 1299 1300print "\nPreparing to write out translated configuration:\n"; 1301 1302# Enumerate the files we'll write. 1303$nfiles = 0; 1304if (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"; 1316 } 1317 local (@filerec) = ( $file, $cstr ); 1318 push @allfiles, \@filerec; 1319 } 1320} 1321if (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; 1329 } 1330} 1331if (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; 1339 } 1340} 1341 1342# Merge new secrets needed with existing ones, if any. 1343sub merge_secrets 1344{ 1345 local ($addsecrets, $fname) = @_; 1346 local ($file, $cstr, @triplet, $newsecret); 1347 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]; 1361 } 1362 $cstr .= $triplet[2]; 1363 } 1364 close SECRETS; 1365 $cstr .= $triplet[0] . $triplet[2]; 1366 } 1367 foreach $key1 (keys (%$addsecrets)) { 1368 foreach $key2 (keys (%{$$addsecrets{$key1}})) { 1369 $cstr .= requote($key1) . " " . requote($key2) . " " . 1370 $ {$$addsecrets{$key1}}{$key2} . "\n"; 1371 } 1372 } 1373 local (@filerec) = ( $file, $cstr ); 1374 push @allfiles, \@filerec; 1375} 1376 1377$nchap = 0+(keys %chapsecrets) != 0; 1378$npap = 0+(keys %papsecrets) != 0; 1379if ($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; 1384} 1385 1386die "Nothing to write back; I'm done.\n" if $nfiles == 0; 1387 1388$PAGER = fixpath($ENV{PAGER}, "/usr/bin/less"); 1389$EDITOR = fixpath($ENV{EDITOR}, "/usr/bin/vi"); 1390$SHELL = fixpath($ENV{SHELL}, "/usr/bin/ksh"); 1391 1392END { 1393 if ($tempname) { 1394 unlink($tempname) or 1395 die "Cannot remove temporary file $tempname: $!\n"; 1396 } 1397} 1398 1399sub show_file_options 1400{ 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"; 1411} 1412 1413# If interactive, then allow user to view and modify converted data. 1414if ((-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; 1422 } 1423 if ($ans eq "") { 1424 last if yesno "Saving all files. Are you sure"; 1425 next; 1426 } 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); 1436 } 1437 } elsif ($ans == 6) { 1438 for ($i = 0; $i < $nfiles; $i++) { 1439 print "\t", $i+1, ". $allfiles[$i][0]", 1440 ($deleted[$i] ? " (deleted)" : ""), "\n"; 1441 } 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; 1450 } 1451 $fnum--; 1452 } 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; 1459 } 1460 if ($deleted[$fnum]) { 1461 if (yesno("File " . $fnum+1 . 1462 " ($allfiles[$fnum][0]) is deleted; undelete",1)) { 1463 undef $deleted[$fnum]; 1464 } 1465 next; 1466 } 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; 1476 } 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; 1493 } 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; 1505 } 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; 1512 } 1513 unlink($tempname) || 1514 warn "Trouble removing temporary file: $!"; 1515 undef $tempname; 1516 } elsif ($ans == 4) { 1517 $deleted[$fnum] = 1; 1518 } 1519 } 1520 } 1521} 1522 1523print "\n"; 1524 1525# Interactive part is over. Become real. 1526$( = $); 1527$< = $>; 1528 1529print "Stopping aspppd\n" if $opt_v; 1530system($asctl, "stop") if -x $asctl; 1531 1532print "Saving all files\n" if $opt_v; 1533for ($i = 0; $i < $nfiles; $i++) { 1534 $filerec = $allfiles[$i]; 1535 if ($deleted[$i]) { 1536 delete $scriptfiles{$$filerec[0]}; 1537 next; 1538 } 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; 1548 } 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; 1557 } 1558 if ($! != 0) { 1559 warn "Unable to make directory $exdir/$tomake: $!\n"; 1560 next; 1561 } 1562 if (system("mkdir", "-p", $dir) != 0) { 1563 warn "Failed to make $dir\n"; 1564 next; 1565 } 1566 } else { 1567 warn "$1 doesn't appear to have a useful path.\n"; 1568 next; 1569 } 1570 } 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; 1579 } 1580 } 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 = $!; 1586 } 1587 warn "Unable to write $$filerec[0]: $fileerr\n" if $fileerr; 1588} 1589 1590local(@scripts) = keys %scriptfiles; 1591if (@scripts) { 1592 print "Making scripts executable\n" if $opt_v; 1593 system("chmod", "u+x", @scripts); 1594} 1595 1596rewrite_passwd if exists($scriptfiles{$sedpasswd}); 1597 1598# clean up after a previous translation. 1599unlink $pppdir . "ifconfig" if !$scriptfiles{$pppdir . "ifconfig"}; 1600unlink $pppdir . "demand" if !$scriptfiles{$pppdir . "demand"}; 1601 1602(rename($asfile, $asmoved) || warn "Cannot move $asfile: $!\n") 1603 if $aspppcf ne $astemp; 1604 1605system($pppdctl, "start") if -x $pppdctl; 1606 1607# use Dumpvalue; 1608# my $dumper = new Dumpvalue; 1609# $dumper->set(globPrint => 1); 1610# $dumper->dumpValue($ifconfig); 1611