#!/usr/bin/perl -w # rcconf # Copyright (c) 1999-2010 kamop@debian.org # =head1 NAME rcconf - Debian Runlevel configuration tool =head1 SYNOPSIS B [options] =head1 DESCRIPTION B allows you to control which services are started when the system boots up or reboots. It displays a menu of all the services which could be started at boot. The ones that are configured to do so are marked and you can toggle individual services on and off. B gets a list of services from /etc/init.d and looks in the /etc/rc?.d directories to determine whether each service is on or off. B detects ON state by existence of /etc/rc?.d/"S"NNname. If the number(NN of /etc/rc?.d/[SK]NNname) is not 20(default), B saves the service name and the number in /var/lib/rcconf/services so as to be able to restore the service to its original configuration. If you purge B package by 'dpkg --purge' or 'aptitude purge' or others, you may lose off state package due to deletion of /var/lib/rcconf/services. =head1 OPTIONS =over 3 =item B<--help> =over 2 Print out a usage message. =back =item B<--dialog> =over 2 Use dialog command to display menu =back =item B<--whiptail> =over 2 Use whiptail command to display menu =back =item B<--notermcheck> =over 2 Do not set window size by terminal property. =back =item B<--on> service[,service,...] =over 2 Set services to be on. This option enables rcconf in command line mode and no select menu will be displayed. =back =item B<--off> service[,service,...] =over 2 Set services to be off. This option enables rcconf in command line mode and no select menu will be displayed. =back =item B<--list> =over 2 List services which includes current status(on/off). This option enables rcconf in command line mode and no select menu will be displayed. Use B<--expert> option together if you want to list all services. This result can be used as I of B<--config>. =back =item B<--config> I =over 2 Set services on/off according to I. This option enables rcconf in command line mode and no select menu will be displayed. The format of this config file is "service_name on" or "service_name off" in each line. Refer to the result of B<--list>. =back =item B<--expert> =over 2 Show and select all services for experts. In default, rcconf doesn't display system default services as a candidate such as mountall.sh to hide unnecessary services for users(but very important for system). The list of which services are considered expert can be found at the line @expertonly in /usr/sbin/rcconf. =back =item B<--now> =over 2 For each service that had the links changed, call the corresponding /etc/init.d/service-name script using invoke-rc.d, so the package starts or stops immediately. If you do not use this option, the changes will only take effect the next time you reboot (or change runlevel). =back =item B<--verbose> =over 2 Output verbose messages. =back =back =head1 Guide File B can display some description(Guide) for each services with Guide File. Guide File is placed on /var/lib/rcconf/guide, and this Guide File does not exist by default. If you want to use Guide, you need to define guides for each services in this file. If you run B before B, B can use default guides derived from package description. B generates the file '/var/lib/rcconf/guide.default' from package description(only uses first line of it) using apt-cache. B refers Guides in /var/lib/rcconf/guide before /var/lib/rcconf/guide.default. If you install some packages after executed B, you need to re-create this file using B so as to refresh guide.default that includes new guides for installed new services. =head1 Updating /etc/rd?.c/ by the package(KNOWN PROBLEM) B saves /etc/rc?.d/[SK]NNname conditions into /var/lib/rcconf/services. This file is updated only when there exists /etc/rc?.d/SNNname. It means that the condition is not saved if /etc/rc?.d/SNNname doesn't exist for the package. If the old version of the package creates both /etc/rc?.d/SNNname and /etc/rc?.d/KNNname but the newer(updated) version of the package creates only /etc/rc?.d/KNNname, some stupid condition occurs. That is, B displays this package as OFF state even as the updated package doesn't have /etc/rc?.d/SNNname. That is because B can't detect disappearance of /etc/rc?.d/SNNname and previous /etc/rc?.d/SNNname condition remains in /var/lib/rcconf/services for restore. In that situation, remove the entry(corresponding package line) from /var/lib/rcconf/services. =head1 FILE =over 8 =item /var/lib/rcconf/services The service number data file. =item /var/lib/rcconf/lock Lock file. =item /var/lib/rcconf/guide.default Guide File update-rcconf-guide generates. =item /var/lib/rcconf/guide Guide File user(Administrator) can define. =back =head1 SEE ALSO update-rc.d(8) update-rcconf-guide(8) =head1 AUTHOR Atsushi KAMOSHIDA =cut use strict; use Getopt::Long; use Pod::Usage; { local ($^W) = 0; #no warnings; eval "require 'sys/ioctl.ph';"; } my $opt_now = ""; my $opt_help = ""; my $opt_dialog = ""; my $opt_whiptail = ""; my $opt_verbose = ""; my $opt_notermcheck = ""; my $opt_expertmode = ""; my $opt_onlist = ""; my $opt_offlist = ""; my $opt_showlist = ""; my $opt_showrcdf = ""; my $opt_dryrun = ""; my $opt_configfile = ""; my $opt_error = GetOptions ("help|?" => \$opt_help, "now" => \$opt_now, "verbose" => \$opt_verbose, "notermcheck" => \$opt_notermcheck, "expert" => \$opt_expertmode, "dialog" => \$opt_dialog, "on=s" => \$opt_onlist, "off=s" => \$opt_offlist, "list" => \$opt_showlist, "dump" => \$opt_showrcdf, "config=s" => \$opt_configfile, "dry-run" => \$opt_dryrun, "whiptail" => \$opt_whiptail); my $ETC_DIR = "/etc"; my $LOCK_FILE = "/var/lock/rcconf"; my $TMP_FILE = "/tmp/rc-select.$$"; my $DATA_DIR = "/var/lib/rcconf/"; my $NO_EXECUTE = ""; if ( $opt_dryrun ) { $NO_EXECUTE = "yes"; } if ( $NO_EXECUTE ne "" ) { $DATA_DIR = "/tmp/"; } my $DATA_FILE = $DATA_DIR."services"; my $UPDATE_RCD_PATH = "/usr/sbin/update-rc.d"; my $INVOKERC_BIN = "/usr/sbin/invoke-rc.d"; my $TITLE = "rcconf - Debian Runlevel Configuration tool"; my $BOX_HEIGHT = 20; my $BOX_WIDTH = 70; my $BOX_LIST_HEIGHT = 10; my $DIALOG_BIN = "/usr/bin/whiptail"; my $DIALOG_SW_ON = "1"; my $DIALOG_SW_OFF = "0"; my $DEFAULT_RCNUM = 20; my @unselects = ("\^\\\.\$", "\^\\\.\\\.\$", "\^rc\$", "\^rcS\$", "\^README\$", "\^skeleton\$", ".*\\\.dpkg-dist\$", ".*\\\.dpkg-old\$", ".*\\\.dpkg-bak\$", ".*\\\.sh\$"); my @expertonly = ("bootmisc\.sh", "bootlogd", "checkfs-loop", "checkfs.sh", "console-screen\.sh", "checkroot\.sh", "cryptdisks-early", "dns-clean", "etc-setserial", "glibc\.sh", "halt", "hibernate", "hostname\.sh", "hwclock\.sh", "hwclockfirst\.sh", "ifupdown", "ifupdown-clean", "keymap\.sh", "killprocs", "mountall-bootclean\.sh", "mountall\.sh", "mountdevsubfs\.sh", "mountkernfs\.sh", "mountnfs-bootclean\.sh", "mountnfs\.sh", "mountoverflowtmp", "nviboot", "mtab\.sh", "networking", "rc\.local", "reboot", "rmnologin", "screen-cleanup", "sendsigs", "single", "stop-bootlogd", "stop-bootlogd-single", "udev-mtab", "umountfs", "umountroot", "urandom" ); END { &remove_lock(); if ( -f $TMP_FILE ) { unlink($TMP_FILE); } } if ( ( ! $opt_error ) || $opt_help ) { pod2usage(1); } my $DEBUG = 0; if ( ( exists($ENV{'RCCONF_DEBUG'}) ) && ( $ENV{'RCCONF_DEBUG'} ne '' ) ) { $DEBUG = 1; } if ( $opt_verbose ) { $DEBUG = 1; } my $DEBUG_STRING = ""; if ( $DEBUG == 0 ) { $DEBUG_STRING = ">/dev/null 2>&1"; } my $RUN_SCRIPTS = 0; if( ( ( exists($ENV{'RCCONF_NOW'}) ) && ( $ENV{'RCCONF_NOW'} ne '' ) ) || ($opt_now) ) { $RUN_SCRIPTS = 1; } if ( ( -r $DATA_FILE ) && ( ! -w $DATA_FILE ) ) { print STDERR "Can't write $DATA_FILE.\n"; print STDERR "Check permission of $DATA_FILE before rcconf.\n"; exit 1; } my $OUTPUT_FILE = ""; if ( ( exists($ENV{'RCCONF_SAVE'}) ) && ( $ENV{'RCCONF_SAVE'} ne '' ) ) { if ( open(SAVE, "> ".$ENV{'RCCONF_SAVE'} ) ) { $OUTPUT_FILE = $ENV{'RCCONF_SAVE'}; } } my $DEFAULT_GUIDE_FILE = $DATA_DIR."guide.default"; my $GUIDE_FILE = $DATA_DIR."guide"; if ( ( exists($ENV{'RCCONF_GUIDE'}) ) && ( $ENV{'RCCONF_GUIDE'} ne '' ) ) { $GUIDE_FILE = $ENV{'RCCONF_GUIDE'}; } ## Decide dialog command if ( ( exists($ENV{'DIALOG_BIN'}) ) && ( $ENV{'DIALOG_BIN'} ne '' ) ) { $DIALOG_BIN = $ENV{'DIALOG_BIN'}; } if ( ( exists($ENV{'DIALOG_SW_ON'}) ) && ( $ENV{'DIALOG_SW_ON'} ne '' ) ) { $DIALOG_SW_ON = $ENV{'DIALOG_SW_ON'}; } if ( ( exists($ENV{'DIALOG_SW_OFF'}) ) && ( $ENV{'DIALOG_SW_OFF'} ne '' ) ) { $DIALOG_SW_OFF = $ENV{'DIALOG_SW_OFF'}; } if ( $opt_whiptail ) { $DIALOG_BIN = "/usr/bin/whiptail"; $DIALOG_SW_ON = "1"; $DIALOG_SW_OFF = "0"; } if ( $opt_dialog ) { $DIALOG_BIN = "/usr/bin/dialog"; $DIALOG_SW_ON = "on"; $DIALOG_SW_OFF = "off"; } if ( ! -x $DIALOG_BIN ) { $DIALOG_BIN = "/usr/bin/dialog"; $DIALOG_SW_ON = "on"; $DIALOG_SW_OFF = "off"; if ( ! -x $DIALOG_BIN ) { print "rcconf needs dialog or whiptail.\n"; exit 1; } } ## Try to get window size. If it seems good, set them if ( ( defined(&TIOCGWINSZ) ) && ( ! $opt_notermcheck ) ) { my $winsize = ""; my $retval = ioctl(STDOUT,&TIOCGWINSZ,$winsize); if ( $retval ) { my ($height, $width, $xpixel, $ypixel) = unpack('S4', $winsize); if ( ( $height > 4 ) && ( $width > 10 ) ) { # some console may return 0 such as serial console, ignore in this case $BOX_WIDTH = $width - 10; $BOX_HEIGHT = $height - 4; $BOX_LIST_HEIGHT = $BOX_HEIGHT - 10; } } } my $DIALOG_OPT = "--title \"$TITLE\" --separate-output --checklist \"\" $BOX_HEIGHT $BOX_WIDTH $BOX_LIST_HEIGHT "; if ( ( exists($ENV{'DIALOG_OPT'}) ) && ( $ENV{'DIALOG_OPT'} ne '' ) ) { $DIALOG_OPT = $ENV{'DIALOG_OPT'}; } ## Read guidefile and prepare package descriptions. my $guide = &read_guidefile(file=>$DEFAULT_GUIDE_FILE); my $guide_tmp = &read_guidefile(file=>$GUIDE_FILE); foreach my $key ( %{$guide_tmp} ) { $guide->{$key} = $guide_tmp->{$key}; } undef $guide_tmp; &make_lock(); my $rcdf = &read_rcd_default(root_dir=>$ETC_DIR); if ( $opt_showrcdf ) { dump_rcd(rcdf=>$rcdf); exit 0; } my $data = &read_data(file=>$DATA_FILE); my $cur_on = &select_default(rcdf=>$rcdf, data=>$data); my $initd = &read_initd_dir(root_dir=>$ETC_DIR); my $cur_off = &select_unlinked_initd(initd=>$initd, rcdf=>$rcdf, data=>$data, unselects=>\@unselects); &update_services_data_initd(initd=>$initd, data=>$data, root_dir=>$ETC_DIR, unselects=>\@unselects); undef $initd; if ( ! $opt_expertmode ) { $cur_on = &select_unmanaged_initd(initd=>$cur_on, unselects=>\@expertonly); $cur_off = &select_unmanaged_initd(initd=>$cur_off, unselects=>\@expertonly); } ## Calculate MENU width my $GUIDE_LENGTH = -1; my $ITEM_MAX_LENGTH = -1; &update_item_max_length(data=>$cur_on); &update_item_max_length(data=>$cur_off); ## GUIDE_LENGTH = BOX_WIDTH - FIXED_LENGTH[16] - max(keys) ## FIXED_LENGTH = left_edge[5] + mark[4] + key_list_gap[2] + right_edge[5] $GUIDE_LENGTH = $BOX_WIDTH - 16 - $ITEM_MAX_LENGTH; my $ret; my $res; my $skip_exec = 0; if ( ( $opt_configfile ne "" ) && ( -f $opt_configfile ) ) { ($ret, $res) = &read_config_file(file=>$opt_configfile); if ( $ret == 0 ) { ($ret, $res) = &set_config(on=>$cur_on, off=>$cur_off, set=>$res); } } elsif ( ( $opt_onlist ne "" ) || ( $opt_offlist ne "" ) ) { ## command line mode(set) ($ret, $res) = &read_config_list(onstr=>$opt_onlist, offstr=>$opt_offlist); if ( $ret == 0 ) { ($ret, $res) = &set_config(on=>$cur_on, off=>$cur_off, set=>$res); } } elsif ( $opt_showlist ) { ## command line mode(list) &show_config(on=>$cur_on, off=>$cur_off); $skip_exec = 1; $ret = 0; } else { ## TUI mode ($ret, $res) = &output_dialog(on=>$cur_on, off=>$cur_off, info=>$guide); } if ( ( $skip_exec == 0 ) && ( $ret == 0 ) ) { my($res_on, $res_off) = &diff_result(on=>$cur_on, off=>$cur_off, res=>$res); &exec_update(on=>$res_on, off=>$res_off, data=>$data); &write_data(file=>$DATA_FILE, data=>$data); } &remove_lock(); if ( $OUTPUT_FILE ne "" ) { close(SAVE); } exit $ret; ####################################################################### ####################################################################### ####################################################################### ####################################################################### ## MODULE: read_rcd_default ## DESC: Read files in rc?.d(?:=0..6) directory and generate %rcdf. ## %rcdf->{'package'} -> [0] service num in rc0.d/S??package ## [1] ## [2] ## [3] ## [4] ## [5] ## [6] ## [7] rcS.d/S??package ## [10] service num in rc0.d/K??package ## [11] rc1.d/K??package ## [12] rc2.d/K??package ## [13] rc3.d/K??package ## [14] rc4.d/K??package ## [15] rc5.d/K??package ## [16] rc6.d/K??package ## [17] rcS.d/K??package ## IN: ## OUT: ## OP: ## STATUS: ## END: sub read_rcd_default { my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $root_dir = $ref{'root_dir'}; my %rcdf = (); my $dir; ## rc?.d my($start, $end); for ( my $i = 0; $i <= 6; $i++ ) { $dir = $root_dir."/rc".$i.".d"; ($start, $end) = &read_rc_dir(dir=>$dir); &setup_rcd(rcdf=>\%rcdf, rcfile=>$start, dirnum=>$i, margin=>0); &setup_rcd(rcdf=>\%rcdf, rcfile=>$end, dirnum=>$i, margin=>10); } ## rcS.d $dir = $root_dir."/rcS.d"; ($start, $end) = &read_rc_dir(dir=>$dir); &setup_rcd(rcdf=>\%rcdf, rcfile=>$start, dirnum=>7, margin=>0); &setup_rcd(rcdf=>\%rcdf, rcfile=>$end, dirnum=>7, margin=>10); return \%rcdf; } ## read_rcd_default ####################################################################### ## MODULE: read_rc_dir ## DESC: Open directory specified in $dir, and list Start/Stop service ## @start [0] -> {file} -+- 'num' service number ## [1] +- 'name' file name ## IN: ## OUT: ## OP: ## STATUS: ## END: sub read_rc_dir{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $dir = $ref{'dir'}; ## opendir(DIR, $dir) || die "No such directory: $!"; ## my @starts = (); my @stops = (); my @dirs = readdir(DIR); foreach ( @dirs ) { if(/^S([0-9][0-9])(.*)$/){ push(@starts, &new_file(num=>$1, name=>$2)); next; } ## if if(/^K([0-9][0-9])(.*)$/){ push(@stops, &new_file(num=>$1, name=>$2)); next; } ## if } ## while() closedir(DIR); return(\@starts, \@stops); } ## read_rc_dir ####################################################################### ## MODULE: read_initd_dir ## DESC: Collect files in init.d/ directory. ## IN: ## OUT: ## OP: ## STATUS: ## END: sub read_initd_dir{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $root_dir = $ref{'root_dir'}; ## my $dir = $root_dir."/init.d"; opendir(DIR, $dir) || die "No such directory: $!"; my(@dirs) = readdir(DIR); close(DIR); ## return \@dirs; } ## read_initd_dir ####################################################################### ## MODULE: select_unlinked_initd ## DESC: Compare between %rcdf and @initd, and list file in init.d/ ## directory which is not linked to rc?.d. ## Listed files are not serviced packages. ## We creates /etc/rc*.d/KNNname when it changes to off, so we ## also need it. ## IN: ## OUT: \@new_initd := not serviced packages ## OP: ## STATUS: ## END: sub select_unlinked_initd{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $initd = $ref{'initd'}; my $rcdf = $ref{'rcdf'}; my $data = $ref{'data'}; my $unselects = $ref{'unselects'}; ## my @new_initd = (); ## foreach my $key (@{$initd}){ next if ( &check_unselect(file=>$key, unselects=>$unselects) ); if ( ! exists($rcdf->{$key}) ) { push(@new_initd, $key); } elsif ( ( $rcdf->{$key}->[0] == -1 ) && ( $rcdf->{$key}->[1] == -1 ) && ( $rcdf->{$key}->[2] == -1 ) && ( $rcdf->{$key}->[3] == -1 ) && ( $rcdf->{$key}->[4] == -1 ) && ( $rcdf->{$key}->[5] == -1 ) && ( $rcdf->{$key}->[6] == -1 ) && ( $rcdf->{$key}->[7] == -1 ) ) { if ( ( exists($data->{$key}) ) && ( $data->{$key}->{'start'} ne "x" ) ) { ## non-defaults and defaults-number packages push(@new_initd, $key); } elsif ( ( $rcdf->{$key}->[10] == $rcdf->{$key}->[11] ) && ( $rcdf->{$key}->[10] == $rcdf->{$key}->[12] ) && ( $rcdf->{$key}->[10] == $rcdf->{$key}->[13] ) && ( $rcdf->{$key}->[10] == $rcdf->{$key}->[14] ) && ( $rcdf->{$key}->[10] == $rcdf->{$key}->[15] ) && ( $rcdf->{$key}->[10] == $rcdf->{$key}->[16] ) && ( $rcdf->{$key}->[10] == 0 ) ) { ## defaults packages push(@new_initd, $key); } elsif ( ( $rcdf->{$key}->[10] == $rcdf->{$key}->[11] ) && ( $rcdf->{$key}->[10] == $rcdf->{$key}->[12] ) && ( $rcdf->{$key}->[10] == $rcdf->{$key}->[13] ) && ( $rcdf->{$key}->[10] == $rcdf->{$key}->[14] ) && ( $rcdf->{$key}->[10] == $rcdf->{$key}->[15] ) && ( $rcdf->{$key}->[10] == $rcdf->{$key}->[16] ) ) { ## off state packages by 'update-rc.d disable' push(@new_initd, $key); } } } return \@new_initd; } ## select_unlinked_initd() ####################################################################### ## MODULE: check_unselect ## DESC: Check if 'file' exists in unselects array. ## IN: ## OUT: results 0 := file is not in the array. ## 1 := file exists in the array. ## OP: ## STATUS: ## END: sub check_unselect{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $file = $ref{'file'}; my $unselects = $ref{'unselects'}; return 1 if ( ! -x $ETC_DIR . "/init.d/" . $file ); foreach my $unselect (@{$unselects}){ return 1 if($file =~ /$unselect/); } return 0; } ## check_unselect() ####################################################################### ## MODULE: select_unmanaged_initd ## DESC: Remove unmanaged initd from @initd compared with unselects list. ## IN: ## OUT: \@new_initd := not serviced packages ## OP: ## STATUS: ## END: sub select_unmanaged_initd { my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $initd = $ref{'initd'}; my $unselects = $ref{'unselects'}; ## my @new_initd = (); my $unselect; ## foreach my $key ( @{$initd} ) { next if ( &check_unselect(file=>$key, unselects=>$unselects) ); push(@new_initd, $key); } return \@new_initd; } ## select_unmanaged_initd() ####################################################################### ## MODULE: new_file ## DESC: Generate new package file ## 'num' => service number ## 'name' => package name(filename) ## IN: ## OUT: ## OP: ## STATUS: ## END: sub new_file{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my %new = (); $new{'num'} = $ref{'num'}; $new{'name'} = $ref{'name'}; return \%new; } ## new_file() ####################################################################### ## MODULE: new_rcd ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub new_rcd{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my @rcd = (); for ( my $i = 0; $i <= 7; $i++ ) { $rcd[$i] = -1; ## start $rcd[$i + 10] = -1; ## end } return \@rcd; } ## new_rc() ####################################################################### ## MODULE: setup_rcd ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub setup_rcd{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $rcdf = $ref{'rcdf'}; my $rcfile = $ref{'rcfile'}; my $dirnum = $ref{'dirnum'}; my $margin = $ref{'margin'}; ## my $package; my $num; foreach my $file ( @{$rcfile} ) { $package = $file->{'name'}; $num = $file->{'num'}; if ( $DEBUG == 1 ) { print $package." ".$num." $margin $dirnum\n"; } if(! exists($rcdf->{$package})){ $rcdf->{$package} = &new_rcd(); if ( $DEBUG == 1 ) { print "Generate ".$package."\n"; } } $rcdf->{$package}->[$dirnum+ $margin] = $num; } ## foreach } ## setup_rcd() ####################################################################### ## MODULE: dump_rcd ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub dump_rcd{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $rcdf = $ref{'rcdf'}; ## my $i; printf " Start Stop\n"; printf "%-20s ","Package Name"; print " 0 1 2 3 4 5 6 S 0 1 2 3 4 5 6 S\n"; print '-' x 71 ."\n"; foreach my $package (keys(%{$rcdf})){ printf "%-20s ", $package; for ( $i = 0; $i <= 7; $i++ ) { printf "%2d ", $rcdf->{$package}->[$i]; } print " "; for ( $i = 0; $i <= 7; $i++ ) { printf "%2d ", $rcdf->{$package}->[$i + 10]; } print "\n"; } ## foreach $package } ## dump_rcd() ####################################################################### ## MODULE: select_default ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub select_default{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $rcdf = $ref{'rcdf'}; my $data = $ref{'data'}; ## my $link ; my @select = (); my $start_num; my $stop_num; foreach my $package ( keys(%{$rcdf}) ) { $link = $rcdf->{$package}; $start_num = $link->[2]; $stop_num = $link->[10]; if ( ( $start_num != -1 ) && ( $stop_num != -1 ) && ( $link->[3] == $start_num ) && ( $link->[4] == $start_num ) && ( $link->[5] == $start_num ) && ( $link->[11] == $stop_num ) && ( $link->[16] == $stop_num ) ) { push(@select, $package); if ( ( $start_num != $DEFAULT_RCNUM ) || ( $stop_num != $DEFAULT_RCNUM ) ) { ## defaults-number packages $data->{$package}->{'start'} = $start_num; $data->{$package}->{'stop'} = $stop_num; } } else { ## check for non-defaults packages my $start_str = ""; my $stop_str = ""; my $symbol; my $num_str; my %start_hash = (); my %stop_hash = (); for ( my $i = 0; $i < 8; $i++ ) { $symbol = ($i == 7) ? "S" : $i; ## check for S:start numbers if ( $link->[$i] != -1 ) { $num_str = sprintf("%02d", $link->[$i]); if ( ( ! exists($start_hash{$num_str}) ) || ( $start_hash{$num_str} eq "" ) ) { $start_hash{$num_str} = $num_str; } $start_hash{$num_str} .= ":" . $symbol; } ## check for K:kill(stop) numbers if ( $link->[$i + 10] != -1 ) { $num_str = sprintf("%02d", $link->[$i + 10]); if ( ( ! exists($stop_hash{$num_str}) ) || ( $stop_hash{$num_str} eq "" ) ) { $stop_hash{$num_str} = $num_str; } $stop_hash{$num_str} .= ":" . $symbol; } } ## for $start_str = services_string(hash=>\%start_hash); $stop_str = services_string(hash=>\%stop_hash); if ( $start_str ne "" ) { $data->{$package}->{'start'} = $start_str; $data->{$package}->{'stop'} = ( $stop_str ne "" ) ? $stop_str : "x"; push(@select, $package); } } ## if } ## foreach return \@select; } ## select_default() ####################################################################### ## MODULE: services_string ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub services_string { my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $str = ""; my $hash = $ref{'hash'}; foreach my $key ( keys(%{$hash}) ) { $str .= "," if ( $str ne "" ); $str .= $hash->{$key}; } ## foreach return $str; ## } ## services_string() ####################################################################### ## MODULE: output_dialog ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub output_dialog{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $on = $ref{'on'}; my $off = $ref{'off'}; my $info = $ref{'info'}; ## my @res = (); my $list = " ".&generate_dialoglist(package=>$on, sw=>$DIALOG_SW_ON, info=>$info); $list .= " ".&generate_dialoglist(package=>$off, sw=>$DIALOG_SW_OFF, info=>$info); my $exec = $DIALOG_BIN." ".$DIALOG_OPT.$list; ## my $ret = system($exec." 2>$TMP_FILE"); if ( ( $ret != 0 ) && ( $ret != 1 ) ) { print STDERR "Cancelled or $DIALOG_BIN execution error($ret)\n"; return($ret, \@res); } ## 'dialog' return 0 if exit by pressing 'OK' if ( $ret == 0 ) { open(RES, $TMP_FILE) || die "Exec error:$!"; while(){ chomp; ## strip quoted string "package" or 'package' if ( /^\"(.*)\"$/ ) { $_ = $1; } elsif ( /^\'(.*)\'$/ ) { $_ = $1; } push(@res, $_); } close(RES); } unlink $TMP_FILE; return($ret, \@res); } ## output_dialog() ####################################################################### ## MODULE: generate_dialoglist ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub generate_dialoglist { my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $package = $ref{'package'}; my $sw = $ref{'sw'}; my $info = $ref{'info'}; ## my $list = ""; my $str; for my $key ( sort(@{$package}) ) { $str = ""; if ( exists($info->{$key}) ) { $str = ( $GUIDE_LENGTH > 0 ) ? substr($info->{$key}, 0, $GUIDE_LENGTH) : $info->{$key}; } $key =~ s/\'/\"/g; $str =~ s/\'/\"/g; $list .= "'".$key."' "." '".$str."' '".$sw."' "; } return $list; } ## generate_dialoglist() ####################################################################### ## MODULE: read_guidefile ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub read_guidefile{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $file = $ref{'file'}; ## my %guide = (); my $key; my $guideline; open(IN, $file) || return \%guide; while(){ chomp; /^(\S+)\s+(.*)/; $key = $1; $guideline = $2; $guide{$key} = $guideline; } ## while() return \%guide; } # read_guidefile() ####################################################################### ## MODULE: diff_result ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub diff_result{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $on = $ref{'on'}; my $off = $ref{'off'}; my $res = $ref{'res'}; ## my %hash = (); my @res_on = (); my @res_off = (); my $key; foreach $key (@{$res}){ $hash{$key} = "OK"; } foreach $key ( @{$on} ) { if ( ! exists($hash{$key}) ) { push(@res_off, $key); } else { $hash{$key} = "ON"; } } foreach $key ( @{$off} ) { if ( exists($hash{$key}) ) { push(@res_on, $key); $hash{$key} = "OFF"; } } foreach $key ( keys(%hash) ) { if ( $hash{$key} eq "OK" ) { ## ERROR print STDERR "Illegal string(".$key.") received from $DIALOG_BIN\n"; exit 1; } } return(\@res_on, \@res_off); } ## diff_result() ####################################################################### ## MODULE: exec_update ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub exec_update{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $on = $ref{'on'}; my $off = $ref{'off'}; my $data = $ref{'data'}; ## my $key; my $command; my $pn; foreach $key ( @{$on} ) { if ( ( exists($data->{$key}) ) && ( $data->{$key}->{'start'} eq "z" ) && ( $data->{$key}->{'stop'} eq "z" ) ) { $command .= $UPDATE_RCD_PATH . " " . $key . " enable "; } else { $command = $UPDATE_RCD_PATH." -f ".$key." remove $DEBUG_STRING "; if ( ( exists($data->{$key}) ) && ( ( $data->{$key}->{'start'} =~ /[:x]/ ) || ( $data->{$key}->{'stop'} =~ /[:x]/ ) ) ) { $command .= " ; " . $UPDATE_RCD_PATH." ".$key; if ( $data->{$key}->{'start'} ne "x" ) { $command .= extract_services_data(str=>$data->{$key}->{'start'}, head=>'start'); } if ( $data->{$key}->{'stop'} ne "x" ) { $command .= extract_services_data(str=>$data->{$key}->{'stop'}, head=>'stop'); } } else { $command .= " ; " . $UPDATE_RCD_PATH." ".$key." defaults"; if ( exists($data->{$key}) ) { $command .= " ".$data->{$key}->{'start'}." ".$data->{$key}->{'stop'}; } } } $command .= " $DEBUG_STRING"; if ( $NO_EXECUTE eq '' ) { if ( $DEBUG == 1 ) { print STDERR $command."\n"; } print SAVE "$key on\n" if($OUTPUT_FILE ne ''); system($command); if ( $RUN_SCRIPTS == 1 ) { system($INVOKERC_BIN." ".$key." start"); } }else{ print STDERR "DRYRUN: ".$command."\n"; } } foreach $key ( @{$off} ) { if ( exists($data->{$key}) ) { ## non-defaults and defaults-number packages if ( $data->{$key}->{'stop'} ne "x" ) { my @array = split(/:/, $data->{$key}->{'stop'}); $pn = $array[0]; } else { $pn = ""; } } else { ## defaults packages $pn = "00"; } if ( $data->{$key}->{'stop'} eq "z" ) { $command .= $UPDATE_RCD_PATH . " " . $key . " disable $DEBUG_STRING "; } else { $command = $UPDATE_RCD_PATH." -f ".$key." remove $DEBUG_STRING "; if ( $pn ne "" ) { $command .= " ; " . $UPDATE_RCD_PATH." ".$key." stop " . $pn . " 0 1 2 3 4 5 6 . $DEBUG_STRING "; } } # $command .= " $DEBUG_STRING"; if ( $NO_EXECUTE eq '' ) { if ( $DEBUG == 1 ) { print STDERR $command."\n"; } print SAVE "$key off\n" if($OUTPUT_FILE ne ''); system($command); if ( $RUN_SCRIPTS == 1 ) { system($INVOKERC_BIN." ".$key." stop"); } }else{ print STDERR "DRYRUN: ".$command."\n"; } } } ## exec_update() ####################################################################### ## MODULE: extract_services_data ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub extract_services_data { my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $str = $ref{'str'}; my $head = $ref{'head'}; $str =~ s/:/ /g; $str =~ s/,/ \. $head /g; return " " . $head . " " . $str . " . "; } ## extract_services_data() ####################################################################### ## MODULE: update_services_data_initd ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub update_services_data_initd { my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $root_dir = $ref{'root_dir'}; my $initd = $ref{'initd'}; my $data = $ref{'data'}; ## my $dir = $root_dir."/init.d"; foreach my $key (@{$initd}){ next if ( ! exists($data->{$key}) ); next if ( ! -f $dir."/".$key ); $data->{$key}->{'exists'} = 1; open(IN,$dir."/".$key) || next; while(){ if ( /\#\s*Default-Start:/ ) { if ( exists($data->{$key}->{'start'}) ) { $data->{$key}->{'start'} = 'z'; } next; } if ( /\#\s*Default-Stop:/ ) { if ( exists($data->{$key}->{'stop'}) ) { $data->{$key}->{'stop'} = 'z'; } next; } } ## while close(IN); } ## foreach $key } ## extract_services_data() ####################################################################### ## MODULE: read_data ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub read_data{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my %data = (); ## open(IN, $ref{'file'}) || return \%data; ## while(){ next if ( /^\#/ ); if ( /^([0-9,:xzS]+)\s+([0-9,:xzS]+)\s+(\S+)/ ) { $data{$3}->{'start'} = $1; $data{$3}->{'stop'} = $2; } elsif ( /^([0-9][0-9])\s+([0-9][0-9])\s+(\S+)/ ) { $data{$3}->{'start'} = $1; $data{$3}->{'stop'} = $2; } elsif ( /^([0-9][0-9])\s+(\S+)/ ) { $data{$2}->{'start'} = $1; $data{$2}->{'stop'} = $1; } else { print STDERR $ref{'file'}.": skipping unrecognized line: \"$_\"\n"; } } ## while() close(IN); return \%data; } ## read_data() ####################################################################### ## MODULE: read_config_file ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub read_config_file{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $tag; my $value; my $ret = 0; my %data = (); use FileHandle; my $rfh = FileHandle->new($ref{'file'}); if ( ! defined($rfh) ) { print STDERR "File open error".$ref{'file'}."\n"; return(1, 0); } my $lineno = 0; while(<$rfh>){ $lineno ++; s/\r?\n$//; next if(/^$/ || /^\#/); ($tag, $value) = split(/\s+/); if ( $value =~ /^on$/i ) { $value = 1; } elsif ( $value =~ /^off$/i ) { $value = 0; } else { print STDERR $ref{'file'}.": format error in line $lineno.\n"; $ret = 1; } $data{$tag} = $value; } $rfh->close(); undef($rfh); return($ret, \%data); } ## read_config_file() ####################################################################### ## MODULE: read_config_list ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub read_config_list { my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $onstr = $ref{'onstr'}; my $offstr = $ref{'offstr'}; ## my %set_hash = (); my $service; my $ret = 0; foreach $service ( split(',', $onstr) ) { if ( exists($set_hash{$service}) ) { print STDERR "service '$service' is already set on on-list.\n"; } $set_hash{$service} = 1; } foreach $service ( split(',', $offstr) ) { if ( exists($set_hash{$service}) ) { print STDERR "service '$service' is already set on on-list or off-list.\n"; } $set_hash{$service} = 0; } return($ret, \%set_hash); } ## read_config_list() ####################################################################### ## MODULE: set_config ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub set_config{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $on = $ref{'on'}; my $off = $ref{'off'}; my $set = $ref{'set'}; ## my $on_hash = &array2hash(array=>$on, value=>'1'); my $off_hash = &array2hash(array=>$off, value=>'0'); ## my $key; my $ret = 0; foreach $key ( keys(%{$set}) ) { if ( ( ! exists($off_hash->{$key}) ) && ( ! exists($on_hash->{$key}) ) ) { print STDERR "Service '$key' doesn't exist.\n"; $ret = 1; next; } if ( $set->{$key} == 1 ) { if ( exists($off_hash->{$key}) ) { $on_hash->{$key} = '1'; } else { print STDERR "Service '$key' is already on. Skipping...\n"; } } elsif ( $set->{$key} == 0 ) { if ( exists($on_hash->{$key}) ) { $on_hash->{$key} = '0'; } else { print STDERR "Service '$key' is already off. Skipping...\n"; } } } my @res = (); foreach $key ( keys(%{$on_hash}) ) { push(@res, $key) if($on_hash->{$key} == 1); } return($ret, \@res); } ## set_config() ####################################################################### ## MODULE: show_config ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub show_config { my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $on = $ref{'on'}; my $off = $ref{'off'}; my $set = $ref{'set'}; ## foreach my $key ( @{$on} ) { print $key." on\n"; } foreach my $key ( @{$off} ) { print $key." off\n"; } } ## show_config() ####################################################################### ## MODULE: array2hash ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub array2hash{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $array = $ref{'array'}; my $value = $ref{'value'}; ## my %hash = (); foreach my $key (@{$array}){ $hash{$key} = $value; } return \%hash; } ## array2hash() ####################################################################### ## MODULE: write_data ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub write_data{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $file = $ref{'file'}; my $data = $ref{'data'}; open(OUT, "> ".$file) || die "Cannot write file $file: $!"; foreach my $key (keys(%{$data})){ next if ( ! exists($data->{$key}->{'exists'}) ); print OUT $data->{$key}->{'start'}." ". $data->{$key}->{'stop'}." ". $key."\n"; } close(OUT); } ## write_data() ####################################################################### ## MODULE: update_itme_max_length ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub update_item_max_length { my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## my $package = $ref{'data'}; ## for my $key ( @{$package} ) { if ( length($key) > $ITEM_MAX_LENGTH ) { $ITEM_MAX_LENGTH = length($key); } } } ## update_item_max_length() ####################################################################### ## MODULE: make_lock ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub make_lock{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## if ( -f $LOCK_FILE ) { die "Another rcconf is running, or still remain lock file($LOCK_FILE)."; } open(LOCK, "> ".$LOCK_FILE) || die "Can't create lock($LOCK_FILE)."; close(LOCK); } ## make_lock() ####################################################################### ## MODULE: remove_lock ## DESC: ## IN: ## OUT: ## OP: ## STATUS: ## END: sub remove_lock{ my($self) = shift if(defined($_[0]) && (ref($_[0]) ne '')); my(%ref) = @_; ## unlink($LOCK_FILE); } ## remove_lock