home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / sbin / update-rc.d-insserv < prev    next >
Encoding:
Text File  |  2010-05-07  |  16.7 KB  |  594 lines

  1. #! /usr/bin/perl
  2. #
  3. # update-rc.d    Update the links in /etc/rc[0-9S].d/
  4. #
  5.  
  6. use strict;
  7. use warnings;
  8.  
  9. my $initd = "/etc/init.d";
  10. my $etcd  = "/etc/rc";
  11. my $notreally = 0;
  12.  
  13. # Save last action to this directory
  14. my $archive = "/var/lib/update-rc.d";
  15.  
  16. # Print usage message and die.
  17.  
  18. sub usage {
  19.     print STDERR "update-rc.d: error: @_\n" if ($#_ >= 0);
  20.     print STDERR <<EOF;
  21. usage: update-rc.d [-n] [-f] <basename> remove
  22.        update-rc.d [-n] <basename> defaults [NN | SS KK]
  23.        update-rc.d [-n] <basename> start|stop NN runlvl [runlvl] [...] .
  24.        update-rc.d [-n] <basename> disable|enable [S|2|3|4|5]
  25.         -n: not really
  26.         -f: force
  27.  
  28. The disable|enable API is not stable and might change in the future.
  29. EOF
  30.     exit (1);
  31. }
  32.  
  33. # Dependency based boot sequencing is the default, but upgraded
  34. # systems might keep the legacy ordering until the sysadm choose to
  35. # migrate to the new ordering method.  sysv-rc version 2.87dsf-2 will
  36. # remove /var/lib/insserv/using-insserv and this divert, thus transfering
  37. # the responsibility for dependency based update-rc.d to sysv-rc.
  38. if ( -f "/var/lib/insserv/using-insserv" && ! -f "/etc/init.d/.legacy-bootordering" ) {
  39.     info("using dependency based boot sequencing");
  40.     exit insserv_updatercd(@ARGV);
  41. }
  42.  
  43. # Check out options.
  44. my $force;
  45.  
  46. my @orig_argv = @ARGV;
  47.  
  48. while($#ARGV >= 0 && ($_ = $ARGV[0]) =~ /^-/) {
  49.     shift @ARGV;
  50.     if (/^-n$/) { $notreally++; next }
  51.     if (/^-f$/) { $force++; next }
  52.     if (/^-h|--help$/) { &usage; }
  53.     &usage("unknown option");
  54. }
  55.  
  56. sub save_last_action {
  57.     my ($script, @arguments) = @_;
  58.  
  59.     return if $notreally;
  60.  
  61.     open(FILE, ">", "$archive/${script}.new") || die "unable to write to $archive/${script}.new";
  62.     print FILE join(" ","update-rc.d",@arguments), "\n";
  63.     close(FILE);
  64.     rename "$archive/${script}.new", "$archive/${script}";
  65. }
  66.  
  67. sub remove_last_action {
  68.     my ($script) = @_;
  69.     unlink "$archive/$script";
  70. }
  71.  
  72. # Action.
  73.  
  74. &usage() if ($#ARGV < 1);
  75. my $bn = shift @ARGV;
  76.  
  77. unless ($bn =~ m/[a-zA-Z0-9+.-]+/) {
  78.     print STDERR "update-rc.d: illegal character in name '$bn'\n";
  79.     exit (1);
  80. }
  81.  
  82. if ($ARGV[0] ne 'remove') {
  83.     if (! -f "$initd/$bn") {
  84.     print STDERR "update-rc.d: $initd/$bn: file does not exist\n";
  85.     exit (1);
  86.     }
  87.     &parse_lsb_header("$initd/$bn");
  88.     &cmp_args_with_defaults($bn, $ARGV[0], @ARGV);
  89. } elsif (-f "$initd/$bn") {
  90.     if (!$force) {
  91.     printf STDERR "update-rc.d: $initd/$bn exists during rc.d purge (use -f to force)\n";
  92.     exit (1);
  93.     }
  94. }
  95.  
  96. my @startlinks;
  97. my @stoplinks;
  98.  
  99. $_ = $ARGV[0];
  100. if    (/^remove$/)       { &checklinks ("remove"); remove_last_action($bn); }
  101. elsif (/^defaults$/)     { &defaults (@ARGV); &makelinks; save_last_action($bn, @orig_argv); }
  102. elsif (/^(start|stop)$/) { &startstop (@ARGV); &makelinks; save_last_action($bn, @orig_argv); }
  103. elsif (/^(dis|en)able$/) { &toggle (@ARGV); &makelinks; save_last_action($bn, @orig_argv); }
  104. else                     { &usage; }
  105.  
  106. exit (0);
  107.  
  108. sub info {
  109.     print STDOUT "update-rc.d: @_\n";
  110. }
  111.  
  112. sub warning {
  113.     print STDERR "update-rc.d: warning: @_\n";
  114. }
  115.  
  116. sub error {
  117.     print STDERR "update-rc.d: error: @_\n";
  118.     exit (1);
  119. }
  120.  
  121. sub error_code {
  122.     my $rc = shift;
  123.     print STDERR "update-rc.d: error: @_\n";
  124.     exit ($rc);
  125. }
  126.  
  127. # Check if there are links in /etc/rc[0-9S].d/ 
  128. # Remove if the first argument is "remove" and the links 
  129. # point to $bn.
  130.  
  131. sub is_link () {
  132.     my ($op, $fn, $bn) = @_;
  133.     if (! -l $fn) {
  134.     warning "$fn is not a symbolic link\n";
  135.     return 0;
  136.     } else {
  137.     my $linkdst = readlink ($fn);
  138.     if (! defined $linkdst) {
  139.         die ("update-rc.d: error reading symbolic link: $!\n");
  140.     }
  141.     if (($linkdst ne "../init.d/$bn") && ($linkdst ne "$initd/$bn")) {
  142.         warning "$fn is not a link to ../init.d/$bn or $initd/$bn\n";
  143.         return 0;
  144.     }
  145.     }
  146.     return 1;
  147. }
  148.  
  149. sub checklinks {
  150.     my ($i, $found, $fn, $islnk);
  151.  
  152.     print " Removing any system startup links for $initd/$bn ...\n"
  153.     if (defined $_[0] && $_[0] eq 'remove');
  154.  
  155.     $found = 0;
  156.  
  157.     foreach $i (0..9, 'S') {
  158.     unless (chdir ("$etcd$i.d")) {
  159.         next if ($i =~ m/^[789S]$/);
  160.         die("update-rc.d: chdir $etcd$i.d: $!\n");
  161.     }
  162.     opendir(DIR, ".");
  163.     my $saveBN=$bn;
  164.     $saveBN =~ s/\+/\\+/g;
  165.     foreach $_ (readdir(DIR)) {
  166.         next unless (/^[SK]\d\d$saveBN$/);
  167.         $fn = "$etcd$i.d/$_";
  168.         $found = 1;
  169.         $islnk = &is_link ($_[0], $fn, $bn);
  170.         next unless (defined $_[0] and $_[0] eq 'remove');
  171.         if (! $islnk) {
  172.         print "   $fn is not a link to ../init.d/$bn; not removing\n"; 
  173.         next;
  174.         }
  175.         print "   $etcd$i.d/$_\n";
  176.         next if ($notreally);
  177.         unlink ("$etcd$i.d/$_") ||
  178.         die("update-rc.d: unlink: $!\n");
  179.     }
  180.     closedir(DIR);
  181.     }
  182.     $found;
  183. }
  184.  
  185. sub parse_lsb_header {
  186.     my $initdscript = shift;
  187.     my %lsbinfo;
  188.     my $lsbheaders = "Provides|Required-Start|Required-Stop|Default-Start|Default-Stop";
  189.     open(INIT, "<$initdscript") || die "error: unable to read $initdscript";
  190.     while (<INIT>) {
  191.         chomp;
  192.         $lsbinfo{'found'} = 1 if (m/^\#\#\# BEGIN INIT INFO\s*$/);
  193.         last if (m/\#\#\# END INIT INFO\s*$/);
  194.         if (m/^\# ($lsbheaders):\s*(\S?.*)$/i) {
  195.         $lsbinfo{lc($1)} = $2;
  196.         }
  197.     }
  198.     close(INIT);
  199.  
  200.     # Check that all the required headers are present
  201.     if (!$lsbinfo{found}) {
  202.     printf STDERR "update-rc.d: warning: $initdscript missing LSB information\n";
  203.     printf STDERR "update-rc.d: see <http://wiki.debian.org/LSBInitScripts>\n";
  204.     } else {
  205.         for my $key (split(/\|/, lc($lsbheaders))) {
  206.             if (!exists $lsbinfo{$key}) {
  207.                 warning "$initdscript missing LSB keyword '$key'\n";
  208.             }
  209.         }
  210.     }
  211. }
  212.  
  213.  
  214. # Process the arguments after the "enable" or "disable" keyword.
  215.  
  216. sub toggle {
  217.     my @argv = @_;
  218.     my ($action, %lvls, @start, @stop, @xstartlinks);
  219.  
  220.     if (!&checklinks) {
  221.     print " System start/stop links for $initd/$bn do not exist.\n";
  222.     exit (0);
  223.     }
  224.  
  225.     $action = $argv[0];
  226.     if ($#argv > 1) {
  227.     while ($#argv > 0 && shift @argv) {
  228.         if ($argv[0] =~ /^[S2-5]$/) {
  229.         $lvls{$argv[0]}++;
  230.         } else {
  231.         &usage ("expected 'S' '2' '3' '4' or '5'");
  232.         }
  233.     }
  234.     } else {
  235.     $lvls{$_}++ for ('S', '2', '3', '4', '5');
  236.     }
  237.  
  238.     push(@start, glob($etcd . '[2-5S].d/[KS][0-9][0-9]' . $bn));
  239.  
  240.     foreach (@start) {
  241.     my $islink = &is_link (undef, $_, $bn);
  242.     next if !$islink;
  243.  
  244.     next unless my ($lvl, $sk, $seq) = m/^$etcd([2-5S])\.d\/([SK])([0-9]{2})$bn$/;
  245.     $startlinks[$lvl] = $sk . $seq;
  246.  
  247.     if ($action eq 'disable' and $sk eq 'S' and $lvls{$lvl}) {
  248.         $xstartlinks[$lvl] = 'K' . sprintf "%02d", (100 - $seq);
  249.     } elsif ($action eq 'enable' and $sk eq 'K' and $lvls{$lvl}) {
  250.         $xstartlinks[$lvl] = 'S' . sprintf "%02d", -($seq - 100);
  251.     } else {
  252.         $xstartlinks[$lvl] = $sk . $seq;
  253.     }
  254.     }
  255.  
  256.     push(@stop, glob($etcd . '[016].d/[KS][0-9][0-9]' . $bn));
  257.  
  258.     foreach (@stop) {
  259.     my $islink = &is_link (undef, $_, $bn);
  260.     next if !$islink;
  261.  
  262.     next unless my ($lvl, $sk, $seq) = m/^$etcd([016])\.d\/([SK])([0-9]{2})$bn$/;
  263.     $stoplinks[$lvl] = $sk . $seq;
  264.     }
  265.  
  266.     if ($action eq 'disable') {
  267.     print " Disabling system startup links for $initd/$bn ...\n";
  268.     } elsif ($action eq 'enable') {
  269.     print " Enabling system startup links for $initd/$bn ...\n";
  270.     }
  271.  
  272.     &checklinks ("remove");
  273.     @startlinks = @xstartlinks;
  274.  
  275.     1;
  276. }
  277.  
  278. # Process the arguments after the "defaults" keyword.
  279.  
  280. sub defaults {
  281.     my @argv = @_;
  282.     my ($start, $stop) = (20, 20);
  283.  
  284.     &usage ("defaults takes only one or two codenumbers") if ($#argv > 2);
  285.     $start = $stop = $argv[1] if ($#argv >= 1);
  286.     $stop  =         $argv[2] if ($#argv >= 2);
  287.     &usage ("codenumber must be a number between 0 and 99")
  288.     if ($start !~ /^\d\d?$/ || $stop  !~ /^\d\d?$/);
  289.  
  290.     $start = sprintf("%02d", $start);
  291.     $stop  = sprintf("%02d", $stop);
  292.  
  293.     $stoplinks[$_]  = "K$stop"  for (0, 1, 6);
  294.     $startlinks[$_] = "S$start" for (2, 3, 4, 5);
  295.  
  296.     1;
  297. }
  298.  
  299. # Process the arguments after the start or stop keyword.
  300.  
  301. sub startstop {
  302.     my @argv = @_;
  303.     my($letter, $NN, $level);
  304.  
  305.     while ($#argv >= 0) {
  306.     if    ($argv[0] eq 'start') { $letter = 'S'; }
  307.     elsif ($argv[0] eq 'stop')  { $letter = 'K'; }
  308.     else {
  309.         &usage("expected start|stop");
  310.     }
  311.  
  312.     if ($argv[1] !~ /^\d\d?$/) {
  313.         &usage("expected NN after $argv[0]");
  314.     }
  315.     $NN = sprintf("%02d", $argv[1]);
  316.  
  317.     if ($argv[-1] ne '.') {
  318.         &usage("start|stop arguments not terminated by \".\"");
  319.     }
  320.  
  321.     shift @argv; shift @argv;
  322.     $level = shift @argv;
  323.     do {
  324.         if ($level !~ m/^[0-9S]$/) {
  325.         &usage(
  326.                "expected runlevel [0-9S] (did you forget \".\" ?)");
  327.         }
  328.         if (! -d "$etcd$level.d") {
  329.         print STDERR
  330.             "update-rc.d: $etcd$level.d: no such directory\n";
  331.         exit(1);
  332.         }
  333.         $level = 99 if ($level eq 'S');
  334.         $startlinks[$level] = "$letter$NN" if ($letter eq 'S');
  335.         $stoplinks[$level]  = "$letter$NN" if ($letter eq 'K');
  336.     } while (($level = shift @argv) ne '.');
  337.     }
  338.     1;
  339. }
  340.  
  341. # Create the links.
  342.  
  343. sub makelinks {
  344.     my($t, $i);
  345.     my @links;
  346.  
  347.     if (&checklinks) {
  348.     print " System start/stop links for $initd/$bn already exist.\n";
  349.     return 0;
  350.     }
  351.     print " Adding system startup for $initd/$bn ...\n";
  352.  
  353.     # nice unreadable perl mess :)
  354.  
  355.     for($t = 0; $t < 2; $t++) {
  356.     @links = $t ? @startlinks : @stoplinks;
  357.     for($i = 0; $i <= $#links; $i++) {
  358.         my $lvl = $i;
  359.         $lvl = 'S' if ($i == 99);
  360.         next if (!defined $links[$i] or $links[$i] eq '');
  361.         print "   $etcd$lvl.d/$links[$i]$bn -> ../init.d/$bn\n";
  362.         next if ($notreally);
  363.         symlink("../init.d/$bn", "$etcd$lvl.d/$links[$i]$bn")
  364.         || die("update-rc.d: symlink: $!\n");
  365.     }
  366.     }
  367.  
  368.     1;
  369. }
  370.  
  371. ## Dependency based
  372. sub insserv_updatercd {
  373.     my @args = @_;
  374.     my @opts;
  375.     my $scriptname;
  376.     my $action;
  377.     my $notreally = 0;
  378.  
  379.     my @orig_argv = @args;
  380.  
  381.     while($#args >= 0 && ($_ = $args[0]) =~ /^-/) {
  382.         shift @args;
  383.         if (/^-n$/) { push(@opts, $_); $notreally++; next }
  384.         if (/^-f$/) { push(@opts, $_); next }
  385.         if (/^-h|--help$/) { &usage; }
  386.         usage("unknown option");
  387.     }
  388.  
  389.     usage("not enough arguments") if ($#args < 1);
  390.  
  391.     $scriptname = shift @args;
  392.     $action = shift @args;
  393.     if ("remove" eq $action) {
  394.         if ( -f "/etc/init.d/$scriptname" ) {
  395.             my $rc = system("insserv", @opts, "-r", $scriptname) >> 8;
  396.             if (0 == $rc && !$notreally) {
  397.                 remove_last_action($scriptname);
  398.             }
  399.             error_code($rc, "insserv rejected the script header") if $rc;
  400.             exit $rc;
  401.         } else {
  402.             # insserv removes all dangling symlinks, no need to tell it
  403.             # what to look for.
  404.             my $rc = system("insserv", @opts) >> 8;
  405.             if (0 == $rc && !$notreally) {
  406.                 remove_last_action($scriptname);
  407.             }
  408.             error_code($rc, "insserv rejected the script header") if $rc;
  409.             exit $rc;
  410.         }
  411.     } elsif ("defaults" eq $action || "start" eq $action ||
  412.              "stop" eq $action) {
  413.         # All start/stop/defaults arguments are discarded so emit a
  414.         # message if arguments have been given and are in conflict
  415.         # with Default-Start/Default-Stop values of LSB comment.
  416.         cmp_args_with_defaults($scriptname, $action, @args);
  417.  
  418.         if ( -f "/etc/init.d/$scriptname" ) {
  419.             my $rc = system("insserv", @opts, $scriptname) >> 8;
  420.             if (0 == $rc && !$notreally) {
  421.                 save_last_action($scriptname, @orig_argv);
  422.             }
  423.             error_code($rc, "insserv rejected the script header") if $rc;
  424.             exit $rc;
  425.         } else {
  426.             error("initscript does not exist: /etc/init.d/$scriptname");
  427.         }
  428.     } elsif ("disable" eq $action || "enable" eq $action) {
  429.         insserv_toggle($notreally, $action, $scriptname, @args);
  430.         # Call insserv to resequence modified links
  431.         my $rc = system("insserv", @opts, $scriptname) >> 8;
  432.         if (0 == $rc && !$notreally) {
  433.             save_last_action($scriptname, @orig_argv);
  434.         }
  435.         error_code($rc, "insserv rejected the script header") if $rc;
  436.         exit $rc;
  437.     } else {
  438.         usage();
  439.     }
  440. }
  441.  
  442. sub parse_def_start_stop {
  443.     my $script = shift;
  444.     my (%lsb, @def_start_lvls, @def_stop_lvls);
  445.  
  446.     open my $fh, '<', $script or error("unable to read $script");
  447.     while (<$fh>) {
  448.         chomp;
  449.         if (m/^### BEGIN INIT INFO$/) {
  450.             $lsb{'begin'}++;
  451.         }
  452.         elsif (m/^### END INIT INFO$/) {
  453.             $lsb{'end'}++;
  454.             last;
  455.         }
  456.         elsif ($lsb{'begin'} and not $lsb{'end'}) {
  457.             if (m/^# Default-Start:\s*(\S?.*)$/) {
  458.                 @def_start_lvls = split(' ', $1);
  459.             }
  460.             if (m/^# Default-Stop:\s*(\S?.*)$/) {
  461.                 @def_stop_lvls = split(' ', $1);
  462.             }
  463.         }
  464.     }
  465.     close($fh);
  466.  
  467.     return (\@def_start_lvls, \@def_stop_lvls);
  468. }
  469.  
  470. sub lsb_header_for_script {
  471.     my $name = shift;
  472.  
  473.     foreach my $file ("/etc/insserv/overrides/$name", "/etc/init.d/$name",
  474.                       "/usr/share/insserv/overrides/$name") {
  475.         return $file if -s $file;
  476.     }
  477.  
  478.     error("cannot find a LSB script for $name");
  479. }
  480.  
  481. sub cmp_args_with_defaults {
  482.     my ($name, $act) = (shift, shift);
  483.     my ($lsb_start_ref, $lsb_stop_ref, $arg_str, $lsb_str);
  484.     my (@arg_start_lvls, @arg_stop_lvls, @lsb_start_lvls, @lsb_stop_lvls);
  485.  
  486.     ($lsb_start_ref, $lsb_stop_ref) = parse_def_start_stop("/etc/init.d/$name");
  487.     @lsb_start_lvls = @$lsb_start_ref;
  488.     @lsb_stop_lvls  = @$lsb_stop_ref;
  489.     return if (!@lsb_start_lvls and !@lsb_stop_lvls);
  490.  
  491.     if ($act eq 'defaults') {
  492.         @arg_start_lvls = (2, 3, 4, 5);
  493.         @arg_stop_lvls  = (0, 1, 6);
  494.     } elsif ($act eq 'start' or $act eq 'stop') {
  495.         my $start = $act eq 'start' ? 1 : 0;
  496.         my $stop = $act eq 'stop' ? 1 : 0;
  497.  
  498.         # The legacy part of this program passes arguments starting with
  499.         # "start|stop NN x y z ." but the insserv part gives argument list
  500.         # starting with sequence number (ie. strips off leading "start|stop")
  501.         # Start processing arguments immediately after the first seq number.
  502.         my $argi = $_[0] eq $act ? 2 : 1;
  503.  
  504.         while (defined $_[$argi]) {
  505.             my $arg = $_[$argi];
  506.  
  507.             # Runlevels 0 and 6 are always stop runlevels
  508.             if ($arg eq 0 or $arg eq 6) {
  509.         $start = 0; $stop = 1; 
  510.             } elsif ($arg eq 'start') {
  511.                 $start = 1; $stop = 0; $argi++; next;
  512.             } elsif ($arg eq 'stop') {
  513.                 $start = 0; $stop = 1; $argi++; next;
  514.             } elsif ($arg eq '.') {
  515.                 next;
  516.             }
  517.             push(@arg_start_lvls, $arg) if $start;
  518.             push(@arg_stop_lvls, $arg) if $stop;
  519.         } continue {
  520.             $argi++;
  521.         }
  522.     }
  523.  
  524.     if ($#arg_start_lvls != $#lsb_start_lvls or
  525.         join("\0", sort @arg_start_lvls) ne join("\0", sort @lsb_start_lvls)) {
  526.         $arg_str = @arg_start_lvls ? "@arg_start_lvls" : "none";
  527.         $lsb_str = @lsb_start_lvls ? "@lsb_start_lvls" : "none";
  528.         warning "$name start runlevel arguments ($arg_str) do not match",
  529.                 "LSB Default-Start values ($lsb_str)";
  530.     }
  531.     if ($#arg_stop_lvls != $#lsb_stop_lvls or
  532.         join("\0", sort @arg_stop_lvls) ne join("\0", sort @lsb_stop_lvls)) {
  533.         $arg_str = @arg_stop_lvls ? "@arg_stop_lvls" : "none";
  534.         $lsb_str = @lsb_stop_lvls ? "@lsb_stop_lvls" : "none";
  535.         warning "$name stop runlevel arguments ($arg_str) do not match",
  536.                 "LSB Default-Stop values ($lsb_str)";
  537.     }
  538. }
  539.  
  540. sub insserv_toggle {
  541.     my ($dryrun, $act, $name) = (shift, shift, shift);
  542.     my (@toggle_lvls, $start_lvls, $stop_lvls, @symlinks);
  543.     my $lsb_header = lsb_header_for_script($name);
  544.  
  545.     # Extra arguments to disable|enable action are runlevels. If none
  546.     # given parse LSB info for Default-Start value.
  547.     if ($#_ >= 0) {
  548.         @toggle_lvls = @_;
  549.     } else {
  550.         ($start_lvls, $stop_lvls) = parse_def_start_stop($lsb_header);
  551.         @toggle_lvls = @$start_lvls;
  552.         if ($#toggle_lvls < 0) {
  553.             error("$name Default-Start contains no runlevels, aborting.");
  554.         }
  555.     }
  556.  
  557.     # Find symlinks in rc.d directories. Refuse to modify links in runlevels
  558.     # not used for normal system start sequence.
  559.     for my $lvl (@toggle_lvls) {
  560.         if ($lvl !~ /^[S2345]$/) {
  561.             warning("$act action will have no effect on runlevel $lvl");
  562.             next;
  563.         }
  564.         push(@symlinks, $_) for glob("/etc/rc$lvl.d/[SK][0-9][0-9]$name");
  565.     }
  566.  
  567.     if (!@symlinks) {
  568.         error("no runlevel symlinks to modify, aborting!");
  569.     }
  570.  
  571.     # Toggle S/K bit of script symlink.
  572.     for my $cur_lnk (@symlinks) {
  573.         my $sk;
  574.         my @new_lnk = split(//, $cur_lnk);
  575.  
  576.         if ("disable" eq $act) {
  577.             $sk = rindex($cur_lnk, '/S') + 1;
  578.             next if $sk < 1;
  579.             $new_lnk[$sk] = 'K';
  580.         } else {
  581.             $sk = rindex($cur_lnk, '/K') + 1;
  582.             next if $sk < 1;
  583.             $new_lnk[$sk] = 'S';
  584.         }
  585.  
  586.         if ($dryrun) {
  587.             printf("rename(%s, %s)\n", $cur_lnk, join('', @new_lnk));
  588.             next;
  589.         }
  590.  
  591.         rename($cur_lnk, join('', @new_lnk)) or error($!);
  592.     }
  593. }
  594.