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