#!/usr/bin/env perl #-*- Mode: perl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- # Time configurator. Designed to be architecture- and distribution independent. # # Copyright (C) 2000-2001 Ximian, Inc. # # Authors: Hans Petter Jansson # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Library General Public License as published # by the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Library General Public License for more details. # # You should have received a copy of the GNU Library General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # Best viewed with 100 columns of width. # Configuration files affected/used: # # /usr/share/zoneinfo/zone.tab # /etc/ntp.conf # /etc/ntp/step-tickers # /etc/localtime # Running programs affected/used: # # date use File::Copy; BEGIN { $SCRIPTSDIR = "___scriptsdir___"; if ($SCRIPTSDIR =~ /^___scriptsdir__[_]/) { $SCRIPTSDIR = "."; $DOTIN = ".in"; } require "$SCRIPTSDIR/general.pl$DOTIN"; require "$SCRIPTSDIR/platform.pl$DOTIN"; require "$SCRIPTSDIR/util.pl$DOTIN"; require "$SCRIPTSDIR/file.pl$DOTIN"; require "$SCRIPTSDIR/xml.pl$DOTIN"; require "$SCRIPTSDIR/service.pl$DOTIN"; require "$SCRIPTSDIR/parse.pl$DOTIN"; require "$SCRIPTSDIR/replace.pl$DOTIN"; } # --- Tool information --- # $name = "time"; $version = "___version___"; @platforms = ("redhat-5.2", "redhat-6.0", "redhat-6.1", "redhat-6.2", "redhat-7.0", "redhat-7.1", "redhat-7.2", "mandrake-7.1", "mandrake-7.2", "debian-2.2", "debian-woody", "suse-7.0", "turbolinux-7.0"); $description =<<"end_of_description;"; Configures your system clock, timezone and time server list. end_of_description; $progress_max = 365; # --- XML parsing --- # # Scan XML from standard input to an internal tree. sub xml_parse { my ($tree, %hash); # Scan XML to tree. $tree = &xst_xml_scan; # Walk the tree recursively and extract configuration parameters. # This is the top level - find and enter the "time" tag. while (@$tree) { if ($$tree[0] eq "time") { &xml_parse_time($$tree[1], \%hash); } shift @$tree; shift @$tree; } return(\%hash); } # sub xml_parse_time { my $tree = $_[0]; my $hash = $_[1]; shift @$tree; # Skip attributes. while (@$tree) { if ($$tree[0] eq "local_time"){ $$hash{"local_time"} = &xml_parse_local_time ($$tree[1]); } elsif ($$tree[0] eq "timezone") { $$hash{"timezone"} = &xst_xml_get_word ($$tree[1]); } elsif ($$tree[0] eq "sync") { $$hash{"sync"} = &xml_parse_sync ($$tree[1], $hash); } shift @$tree; shift @$tree; } } sub xml_parse_sync { my $tree = $_[0]; my $hash = $_[1]; my @sync; $$hash{"sync_active"} = &xst_util_read_boolean($$tree[0]->{active}); shift @$tree; while (@$tree) { if ($$tree[0] eq "server") { push (@sync, &xst_xml_get_word($$tree[1])); } shift @$tree; shift @$tree; } return \@sync; } sub xml_parse_local_time { my $tree = $_[0]; my $hash; shift @$tree; while (@$tree) { if ($$tree[0] eq "year") { $$hash{"year"} = &xst_xml_get_word($$tree[1]); } elsif ($$tree[0] eq "month") { $$hash{"month"} = &xst_xml_get_word($$tree[1]); } elsif ($$tree[0] eq "monthday") { $$hash{"monthday"} = &xst_xml_get_word($$tree[1]); } elsif ($$tree[0] eq "hour") { $$hash{"hour"} = &xst_xml_get_word($$tree[1]); } elsif ($$tree[0] eq "minute") { $$hash{"minute"} = &xst_xml_get_word($$tree[1]); } elsif ($$tree[0] eq "second") { $$hash{"second"} = &xst_xml_get_word($$tree[1]); } shift @$tree; shift @$tree; } return $hash; } # --- XML printing --- # sub xml_print { my $h = $_[0]; my @sync; my @scalar_keys = qw (timezone ntpinstalled); $sync = $$h{"sync"}; &xst_xml_print_begin (); &xst_xml_print_vspace (); &xst_xml_print_hash ($$h{"local_time"}, "local_time"); &xst_xml_print_vspace (); &xst_xml_print_vspace (); &xst_xml_print_line ("\n"); &xst_xml_enter (); foreach $server (@$sync) { &xst_xml_print_line ("$server\n"); } &xst_xml_leave (); &xst_xml_print_line ("\n"); &xst_xml_print_vspace (); &xst_xml_print_scalars ($h, @scalar_keys); &xst_xml_print_vspace (); &xst_xml_print_end (); } # Main operations sub get { my $hash; $hash = &conf_get (); &xst_report_end (); &xml_print ($hash); } sub set { my $hash; $hash = &xml_parse (); &conf_set ($hash); &xst_report_end (); } # --- Filter config: XML in, XML out --- # sub filter { my $hash; $hash = &xml_parse (); &xst_report_end (); &xml_print ($hash); } # --- Main --- # # get, set and filter are special cases that don't need more parameters than a ref to their function. # Read general.pl.in:xst_run_directive to know about the format of this hash. $directives = { "get" => [ \&get, [], "" ], "set" => [ \&set, [], "" ], "filter" => [ \&filter, [], "" ] }; $tool = &xst_init ($name, $version, $description, $directives, @ARGV); &xst_platform_ensure_supported ($tool, @platforms); &xst_run ($tool); # Portable code. sub time_get_local_time { my (%h, $trash); ($h{"second"}, $h{"minute"}, $h{"hour"}, $h{"monthday"}, $h{"month"}, $h{"year"}, $trash, $trash, $trash) = localtime (time); $h{"year"} += 1900; return \%h; } sub time_get_rh62_zone { my ($local_time_file, $zoneinfo_dir) = @_; local *TZLIST; my $zone; my $size_search; my $size_test; *TZLIST = &xst_file_open_read_from_names($zoneinfo_dir . "/zone.tab"); if (not *TZLIST) { return; } &xst_report ("time_timezone_scan"); # Get the filesize for /etc/localtime so that we don't have to execute # a diff for every file, only for file with the correct size. This speeds # up loading $size_search = (stat ($local_time_file))[7]; while () { if (/^\#/) { next; } # Skip comments. ($d, $d, $zone) = split /[\t ]+/, $_; # Get 3rd column. chomp $zone; # Remove linefeeds. # See if this zone file matches the installed one. &xst_report ("time_timezone_cmp", $zone); &xst_print_progress(); $size_test = (stat("$zoneinfo_dir/$zone"))[7]; if ($size_test eq $size_search) { if (!&xst_file_run ("diff $zoneinfo_dir/$zone $local_time_file")) { # Found a match. last; } } $zone = ""; } return $zone; close (TZLIST); } sub conf_get { my %dist_attrib; my $hash; %dist_attrib = &conf_get_parse_table (); $hash = &xst_parse_from_table ($dist_attrib{"fn"}, $dist_attrib{"table"}); return $hash; } sub time_set_local_time { my ($time) = @_; my ($res, $xscreensaver_owner, $xscreensaver_running); &xst_report_enter (); # Kill screensaver, so it doesn't confuse the user. # FIXME: This should handle multiple processes owned by different users. $xscreensaver_owner = &xst_service_proc_get_owner ("xscreensaver"); $xscreensaver_running = &xst_service_proc_stop ("xscreensaver"); $command = sprintf ("date %02d%02d%02d%02d%04d.%02d", $$time{"month"}, $$time{"monthday"}, $$time{"hour"}, $$time{"minute"}, $$time{"year"}, $$time{"second"}); &xst_report ("time_localtime_set", $command); $res = &xst_file_run ($command); # Restart screensaver. # FIXME: This should handle multiple processes owned by different users. if ($xscreensaver_running) { &xst_service_proc_start ("xscreensaver -no-splash", $xscreensaver_owner); } &xst_report_leave (); return -1 if $res; return 0; } sub time_set_rh62_zone { my ($localtime, $zonebase, $timezone) = @_; &xst_report_enter (); &xst_report ("time_timezone_set", $timezone); $tz = "$zonebase/$timezone"; if (stat($tz) ne "") { unlink $localtime; # Important, since it might be a symlink. &xst_report_enter (); $res = copy ($tz, $localtime); &xst_report_leave (); return -1 unless $res; return 0; } &xst_report_leave (); return -1; } sub time_sync_hw_from_sys { &xst_file_run ("hwclock --systohc"); return 0; } sub conf_set { my $values_hash = $_[0]; my %dist_attrib; %dist_attrib = &conf_get_replace_table (); $res = &xst_replace_from_table ($dist_attrib{"fn"}, $dist_attrib{"table"}, $values_hash); &time_sync_hw_from_sys (); return $res; } sub conf_get_parse_table { my %dist_map = ( "redhat-6.0" => "redhat-6.2", "redhat-6.1" => "redhat-6.2", "redhat-6.2" => "redhat-6.2", "redhat-7.0" => "redhat-7.0", "redhat-7.1" => "redhat-7.0", "redhat-7.2" => "redhat-7.0", "mandrake-7.1" => "redhat-7.0", "mandrake-7.2" => "redhat-7.0", "debian-2.2" => "debian-2.2", "debian-woody" => "debian-2.2", "suse-7.0" => "suse-7.0", "turbolinux-7.0" => "redhat-7.0" ); my %dist_tables = ( "redhat-6.2" => { fn => { NTP_CONF => "/etc/ntp.conf", STEP_TICKERS => "/etc/ntp/step-tickers", ZONEINFO => "/usr/share/zoneinfo", LOCAL_TIME => "/etc/localtime" }, table => [ [ "local_time", \&time_get_local_time ], [ "timezone", \&time_get_rh62_zone, [LOCAL_TIME, ZONEINFO] ], [ "sync", \&xst_parse_split_all_hash_comment, NTP_CONF, "server", "[ \t]+" ], [ "sync_active", \&xst_service_sysv_get_status, "xntpd" ], [ "ntpinstalled", \&xst_service_sysv_installed, "xntpd" ], ] }, "redhat-7.0" => { fn => { NTP_CONF => "/etc/ntp.conf", ZONEINFO => "/usr/share/zoneinfo", LOCAL_TIME => "/etc/localtime" }, table => [ [ "local_time", \&time_get_local_time ], [ "timezone", \&time_get_rh62_zone, [LOCAL_TIME, ZONEINFO] ], [ "sync", \&xst_parse_split_all_hash_comment, NTP_CONF, "server", "[ \t]+" ], [ "sync_active", \&xst_service_sysv_get_status, "ntpd" ], [ "ntpinstalled", \&xst_service_sysv_installed, "ntpd" ], ] }, "debian-2.2" => { fn => { NTP_CONF => "/etc/ntp.conf", ZONEINFO => "/usr/share/zoneinfo", LOCAL_TIME => "/etc/localtime" }, table => [ [ "local_time", \&time_get_local_time ], [ "timezone", \&time_get_rh62_zone, [LOCAL_TIME, ZONEINFO] ], [ "sync", \&xst_parse_split_all_hash_comment, NTP_CONF, "server", "[ \t]+" ], [ "sync_active", \&xst_service_sysv_get_status, "ntpd" ], [ "ntpinstalled", \&xst_service_sysv_installed, "ntp" ], ] }, "suse-7.0" => { fn => { NTP_CONF => "/etc/ntp.conf", ZONEINFO => "/usr/share/zoneinfo", LOCAL_TIME => "/etc/localtime" }, table => [ [ "local_time", \&time_get_local_time ], [ "timezone", \&time_get_rh62_zone, [LOCAL_TIME, ZONEINFO] ], [ "sync", \&xst_parse_split_all_hash_comment, NTP_CONF, "server", "[ \t]+" ], [ "sync_active", \&xst_service_sysv_get_status, "xntpd" ], [ "ntpinstalled", \&xst_service_sysv_installed, "xntpd" ], ] } ); my $dist = $dist_map {$xst_dist}; return %{$dist_tables{$dist}} if $dist; &xst_report ("platform_no_table", $xst_dist); return undef; } sub conf_get_replace_table { my %dist_map = ( "redhat-6.0" => "redhat-6.2", "redhat-6.1" => "redhat-6.2", "redhat-6.2" => "redhat-6.2", "redhat-7.0" => "redhat-7.0", "redhat-7.1" => "redhat-7.0", "redhat-7.2" => "redhat-7.0", "mandrake-7.1" => "redhat-7.0", "mandrake-7.2" => "redhat-7.0", "debian-2.2" => "debian-2.2", "debian-woody" => "debian-2.2", "suse-7.0" => "suse-7.0", "turbolinux-7.0" => "redhat-7.0" ); my %dist_tables = ( "redhat-6.2" => { fn => { NTP_CONF => "/etc/ntp.conf", STEP_TICKERS => "/etc/ntp/step-tickers", ZONEINFO => "/usr/share/zoneinfo", LOCAL_TIME => "/etc/localtime" }, table => [ [ "timezone", \&time_set_rh62_zone, [LOCAL_TIME, ZONEINFO] ], [ "local_time", \&time_set_local_time ], [ "sync", \&xst_replace_join_all, NTP_CONF, "server", "[ \t]+" ], [ "sync_active", \&xst_service_sysv_set_status, 90, "xntpd" ], ] }, "redhat-7.0" => { fn => { NTP_CONF => "/etc/ntp.conf", ZONEINFO => "/usr/share/zoneinfo", LOCAL_TIME => "/etc/localtime" }, table => [ [ "timezone", \&time_set_rh62_zone, [LOCAL_TIME, ZONEINFO] ], [ "local_time", \&time_set_local_time ], [ "sync", \&xst_replace_join_all, NTP_CONF, "server", "[ \t]+" ], [ "sync_active", \&xst_service_sysv_set_status, 90, "ntpd" ], ] }, "debian-2.2" => { fn => { NTP_CONF => "/etc/ntp.conf", ZONEINFO => "/usr/share/zoneinfo", LOCAL_TIME => "/etc/localtime" }, table => [ [ "timezone", \&time_set_rh62_zone, [LOCAL_TIME, ZONEINFO] ], [ "local_time", \&time_set_local_time ], [ "sync", \&xst_replace_join_all, NTP_CONF, "server", "[ \t]+" ], [ "sync_active", \&xst_service_sysv_set_status, 23, "ntp" ], ] }, "suse-7.0" => { fn => { NTP_CONF => "/etc/ntp.conf", ZONEINFO => "/usr/share/zoneinfo", LOCAL_TIME => "/etc/localtime" }, table => [ [ "timezone", \&time_set_rh62_zone, [LOCAL_TIME, ZONEINFO] ], [ "local_time", \&time_set_local_time ], [ "sync", \&xst_replace_join_all, NTP_CONF, "server", "[ \t]+" ], [ "sync_active", \&xst_service_sysv_set_status, 90, "xntpd" ], ] } ); my $dist = $dist_map {$xst_dist}; return %{$dist_tables{$dist}} if $dist; &xst_report ("platform_no_table", $xst_dist); return undef; }