#!/usr/bin/env perl #-*- Mode: perl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- # Users account mannager. Designed to be architecture and distribution independent. # # Copyright (C) 2000-2001 Ximian, Inc. # # Authors: Hans Petter Jansson and Arturo Espinosa # # 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: # # /etc/passwd # /etc/group # /etc/shadow # /etc/login.defs # /etc/shells # /etc/skel/ # NIS support will come later. # Running programs affected/used: # # adduser: creating users. # usermod: modifying user data. # passwd: assigning or changing passwords. (Un)locking users. # chfn: modifying finger information - Name, Office, Office phone, Home phone. require "___scriptsdir___/general.pl"; require "___scriptsdir___/platform.pl"; require "___scriptsdir___/util.pl"; require "___scriptsdir___/file.pl"; require "___scriptsdir___/xml.pl"; require "___scriptsdir___/replace.pl"; $progress_max = 4; $DEBUG = 1; # 1 == command; no debug. 2 == no commands, just report. 3 == report and command. $TEST_NIS = 0; # --- Tool information --- # $name = "users"; $version = "0.1.0"; @platforms = ("redhat-5.2", "redhat-6.0", "redhat-6.1", "redhat-6.2", "redhat-7.0", "redhat-7.1", "mandrake-7.2", "debian-2.2", "debian-woody", "suse-7.0"); #, "freebsd-4", "freebsd-5"); $description =<<"end_of_description;"; Manages system users. end_of_description; # --- System config file locations --- # # We list each config file type with as many alternate locations as possible. # They are tried in array order. First found = used. @passwd_names = ( "/etc/passwd" ); @shadow_names = ( "/etc/shadow", "/etc/master.passwd" ); @group_names = ( "/etc/group" ); @login_defs_names = ( "/etc/login.defs", "/etc/adduser.conf" ); @shell_names = ( "/etc/shells" ); @skel_dir = ( "/usr/share/skel", "/etc/skel" ); # Where are the tools? $cmd_usermod = &xst_file_locate_tool ("usermod"); $cmd_userdel = &xst_file_locate_tool ("userdel"); $cmd_useradd = &xst_file_locate_tool ("useradd"); $cmd_groupdel = &xst_file_locate_tool ("groupdel"); $cmd_groupadd = &xst_file_locate_tool ("groupadd"); $cmd_groupmod = &xst_file_locate_tool ("groupmod"); $cmd_gpasswd = &xst_file_locate_tool ("gpasswd"); $cmd_chfn = &xst_file_locate_tool ("chfn"); # --- Internal configuration variables --- # # Configuration is parsed/read to, and printed/written from, these temporary global variables. %users_hash = (); @users = (); %groups_hash = (); @groups = (); %login_defs = (); @shells = (); $parse_tree = 0; $passwd_last_modified = 0; # For /etc/passwd, since the epoch. $group_last_modified = 0; # For /etc/group, since the epoch. $profiles = 0; # --- Mapping constants --- # %users_prop_map = (); @users_prop_array = ( "key", 0, "login", 1, "password", 2, "uid", 3, "gid", 4, "comment", 5, "home", 6, "shell", 7, "last_mod", 8, # Read shadow (5) for these. "passwd_min_life", 9, "passwd_max_life", 10, "passwd_exp_warn", 11, "passwd_exp_disable", 12, "passwd_disable", 13, "reserved", 14, "is_shadow", 15, "", ""); for ($i = 0; $users_prop_array[$i] ne ""; $i += 2) { $users_prop_map {$users_prop_array[$i]} = $users_prop_array[$i + 1]; $users_prop_map {$users_prop_array[$i + 1]} = $users_prop_array[$i]; } %groups_prop_map = (); @groups_prop_array = ( "key", 0, "name", 1, "password", 2, "gid", 3, "users", 4, "", ""); for ($i = 0; $groups_prop_array[$i] ne ""; $i += 2) { $groups_prop_map {$groups_prop_array[$i]} = $groups_prop_array[$i + 1]; $groups_prop_map {$groups_prop_array[$i + 1]} = $groups_prop_array[$i]; } %login_defs_prop_map = (); @login_defs_prop_array = ( "QMAIL_DIR" , "qmail_dir", "MAIL_DIR" , "mailbox_dir", "MAIL_FILE" , "mailbox_file", "PASS_MAX_DAYS" , "passwd_max_day_use", "PASS_MIN_DAYS" , "passwd_min_day_use", "PASS_MIN_LEN" , "passwd_min_length", "PASS_WARN_AGE" , "passwd_warning_advance_days", "UID_MIN" , "new_user_min_id", "UID_MAX" , "new_user_max_id", "GID_MIN" , "new_group_min_id", "GID_MAX" , "new_group_max_id", "USERDEL_CMD" , "del_user_additional_command", "CREATE_HOME" , "create_home", "", ""); for ($i = 0; $login_defs_prop_array[$i] ne ""; $i += 2) { $login_defs_prop_map {$login_defs_prop_array[$i]} = $login_defs_prop_array[$i + 1]; $login_defs_prop_map {$login_defs_prop_array[$i + 1]} = $login_defs_prop_array[$i]; } # --- Utility stuff --- # sub my_system { print STDERR "$_[0]\n"; print "$_[0]\n" if ($DEBUG & 2); system ($_[0]) if ($DEBUG & 1); } sub max { return $_[0] > $_[1]? $_[0]: $_[1]; } sub arr_cmp_recurse { my ($a1, $a2) = @_; my $i; return -1 if ($#$a1 != $#$a2); for ($i = 0; $i <= $#$a1; $i++) { if (ref ($$a1[$i]) eq "ARRAY") { # see if this is a reference. return -1 if &arr_cmp_recurse ($$a1[$i], $$a2[$i]); # we assume it is a ref to an array. } elsif ($$a1[$i] ne $$a2[$i]) { return -1; } } return 0; } # --- Configuration manipulation --- # sub read_skel_dir { my (@files, $file, $name); my $fname = ""; &xst_report_enter (); foreach $name (@skel_dir) { if (opendir (DIR, $name)) { $fname = $name; last; } } if ($fname eq "") { &xst_report ("read_skel_dir_failed", "@skel_dir"); return undef; } foreach $file (readdir (DIR)) { next if ($file eq "." || $file eq ".."); push (@files, $file); } closedir DIR; &xst_report ("read_skel_dir_success", $fname); &xst_report_leave (); return \@files } sub read_login_defs { my $ifh; local *FILE; my @line; $ifh = &xst_file_open_read_from_names(@login_defs_names); if (not $ifh) { return; } # We didn't find it. *FILE = $ifh; while () { next if &xst_ignore_line ($_); chomp; $_ = &xst_xml_unquote ($_); @line = split (/[ \t]+/, $_); $login_defs{$login_defs_prop_map{$line[0]}} = $line[1]; } $login_defs{"files"} = &read_skel_dir (); close (FILE); } sub read_passwd_shadow { my $ifh; local *FILE; my (@line, $copy, %tmphash); my $login_pos = $users_prop_map{"login"}; my $i = 0; # Find the passwd file. $ifh = &xst_file_open_read_from_names(@passwd_names); if (not $ifh) { return; } # We didn't find it. *FILE = $ifh; $passwd_last_modified = (stat (FILE))[9]; # &get the mtime. # Parse the file. @users = (); %users_hash = (); while () { chomp; # FreeBSD allows comments in the passwd file. */ next if ($_ =~ /\#.*/); $_ = &xst_xml_unquote ($_); @line = split ':', $_, -1; unshift @line, sprintf ("%06d", $i); $copy = [@line]; $users_hash{sprintf ("%06d", $i)} = $copy; $tmphash{$line[$login_pos]} = $copy; push (@users, $copy); $i ++; } close (FILE); # Find the shadow file. *FILE = &xst_file_open_read_from_names(@shadow_names); if (*FILE) { my ($login, $passwd); my $passwd_pos = $users_prop_map{"password"}; while () { chomp; # FreeBSD allows comments in the shadow passwd file. */ next if ($_ =~ /\#.*/); @line = split ':', $_, -1; push @line, 1; $login = shift @line; $passwd = shift @line; push @{$tmphash{$login}}, @line; @{$tmphash{$login}}[$passwd_pos] = $passwd; } close (FILE); } } sub read_group { my $ifh; local *FILE; my (@line, $copy, @a); my $i = 0; # Find the file. $ifh = &xst_file_open_read_from_names(@group_names); if (not $ifh) { return; } # We didn't find it. *FILE = $ifh; $group_last_modified = (stat (FILE))[9]; # &get the mtime. # Parse the file. @groups = (); %groups_hash = (); while () { chomp; $_ = &xst_xml_unquote ($_); @line = split ':', $_, -1; unshift @line, sprintf ("%06d", $i); @a = split ',', pop @line; push @line, [@a]; $copy = [@line]; $groups_hash{sprintf ("%06d", $i)} = $copy; push (@groups, $copy); $i ++; } close (FILE); } sub read_profiles { my $path; $path = &xst_file_get_data_path . "/users/profiles.xml"; $profiles = &xst_file_buffer_load ($path); } sub read_shells { my $ifh; local *FILE; # Init @shells, I think every *nix has /bin/false. push (@shells, "/bin/false") if (stat ("/bin/false") ne ""); $ifh = &xst_file_open_read_from_names(@shell_names); return if not $ifh; *FILE = $ifh; while () { next if &xst_ignore_line ($_); chomp; push (@shells, $_) if (stat ($_) ne ""); } close (FILE); } sub write_logindefs { my ($key); local *FILE; my $file = ""; foreach $key (@login_defs_names) { if (open (FILE, $key)) { &xst_debug_print_line ("write_logindefs:$key"); $file = $key; last; } } if ($file eq "") { &xst_report_warning (99, "Could not read \[@login_defs_names\]"); return; } foreach $key (keys (%login_defs)) { &xst_replace_split ($file, $login_defs_prop_map{$key}, "[ \t]+", $login_defs{$key}); } } sub write_group_passwd { my ($i, $j, $k); my (%users_all, %parse_users_hash, @parse_users, $parse_passwd_last_modified); my (%groups_all, %parse_groups_hash, @parse_groups, $parse_group_last_modified); %parse_users_hash = %users_hash; @parse_users = @users; $parse_passwd_last_modified = $passwd_last_modified; &read_passwd_shadow; &xst_progress(10); %parse_groups_hash = %groups_hash; @parse_groups = @groups; $parse_group_last_modified = $group_last_modified; &read_group; &xst_progress(20); # if ($passwd_last_modified > $parse_passwd_last_modified) # { # print STDERR "Password file may be inconsistent! No changes made.\n"; # return; # } foreach $i (keys (%users_hash)) { $users_all{$i} |= 1; } foreach $i (keys (%parse_users_hash)) { $users_all{$i} |= 2; } foreach $i (keys (%groups_hash)) { $groups_all{$i} |= 1; } foreach $i (keys (%parse_groups_hash)) { $groups_all{$i} |= 2; } &xst_progress(25); foreach $i (sort (keys (%users_all))) { &del_user ($users_hash{$i}) if ($users_all{$i} == 1); } &xst_progress(35); foreach $i (sort (keys (%groups_all))) { &del_group ($groups_hash{$i}) if ($groups_all{$i} == 1); } &xst_progress(45); foreach $i (sort (keys (%groups_all))) { &add_group ($parse_groups_hash{$i}) if ($groups_all{$i} == 2); } &xst_progress(55); foreach $i (sort (keys (%users_all))) { &add_user ($parse_users_hash{$i}) if ($users_all{$i} == 2); } &xst_progress(70); foreach $i (sort (keys (%groups_all))) { if ($groups_all{$i} == 3 && &arr_cmp_recurse ($groups_hash{$i}, $parse_groups_hash{$i})) { &change_group ($groups_hash{$i}, $parse_groups_hash{$i}); } } &xst_progress(85); foreach $i (sort (keys (%users_all))) { if ($users_all{$i} == 3 && &arr_cmp_recurse ($users_hash{$i}, $parse_users_hash{$i})) { &change_user ($users_hash{$i}, $parse_users_hash{$i}); } } &xst_progress(95); } sub del_user { my $data = $_[0]; $command = "$cmd_userdel -r \'" . $$data[$users_prop_map{"login"}] . "\'"; &my_system ($command); } sub change_user_chfn { my ($comment, $username) = @_; my ($fname, $office, $office_phone, $home_phone); my $command, @line; return if !$username; @line = split /\,/, $comment; $fname = shift @line; $office = shift @line; $office_phone = shift @line; $home_phone = shift @line; $fname = "-f \'" . $fname . "\'"; $home_phone = "-h \'" . $home_phone . "\'"; if ($xst_dist =~ /debian/) { $office = "-r \'" . $office . "\'"; $office_phone = "-w \'" . $office_phone . "\'"; } else { $office = "-o \'" . $office . "\'"; $office_phone = "-p \'" . $office_phone . "\'"; } $command = "$cmd_chfn $fname $office $office_phone $home_phone $username"; &my_system ($command); } sub add_user { my $data = $_[0]; $command = "$cmd_useradd" . " -d \'" . $$data[$users_prop_map{"home"}] . "\' -g \'" . $$data[$users_prop_map{"gid"}] . "\' -m -p \'" . $$data[$users_prop_map{"password"}] . "\' -s \'" . $$data[$users_prop_map{"shell"}] . "\' -u \'" . $$data[$users_prop_map{"uid"}] . "\' \'" . $$data[$users_prop_map{"login"}] . "\'"; &my_system ($command); &change_user_chfn ($$data[$users_prop_map{"comment"}], $$data[$users_prop_map{"login"}]); } sub change_user { my $old_data = $_[0]; my $new_data = $_[1]; $command = "$cmd_usermod" . " -d \'" . $$new_data[$users_prop_map{"home"}] . "\' -g \'" . $$new_data[$users_prop_map{"gid"}] . "\' -l \'" . $$new_data[$users_prop_map{"login"}] . "\' -p \'" . $$new_data[$users_prop_map{"password"}] . "\' -s \'" . $$new_data[$users_prop_map{"shell"}] . "\' -u \'" . $$new_data[$users_prop_map{"uid"}] . "\' \'" . $$old_data[$users_prop_map{"login"}] . "\'"; &my_system ($command); &change_user_chfn ($$new_data[$users_prop_map{"comment"}], $$new_data[$users_prop_map{"login"}]); } sub del_group { my $data = $_[0]; $command = "$cmd_groupdel \'" . $$data[$groups_prop_map{"name"}] . "\'"; &my_system ($command); } sub add_group { my $data = $_[0]; $command = "$cmd_groupadd -g \'" . $$data[$groups_prop_map{"gid"}] . "\' " . $$data[$groups_prop_map{"name"}]; &my_system ($command); } sub change_group { my $old_data = $_[0]; my $new_data = $_[1]; my ($n, $o, $i, $j, $max_n, $max_o, $r, @tmp); # for iterations $command = "$cmd_groupmod -g \'" . $$new_data[$groups_prop_map{"gid"}] . "\' -n \'" . $$new_data[$groups_prop_map{"name"}] . "\'" . " \'" . $$old_data[$groups_prop_map{"name"}] . "\'"; &my_system ($command); # Let's see if the users that compose the group have changed. if (&arr_cmp_recurse ($$new_data[$groups_prop_map{"users"}], $$old_data[$groups_prop_map{"users"}])) { $n = [ @{$$new_data[$groups_prop_map{"users"}]} ]; sort @$n; $o = [ @{$$old_data[$groups_prop_map{"users"}]} ]; sort @$o; $max_n = $#$n; $max_o = $#$o; for ($i = 0, $j = 0; $i <= &max ($max_n, $max_o); ) { $r = $$n[$i] cmp $$o[$j]; $r *= -1 if (($$o[$j] eq "") || ($$n[$i] eq "")); if ($r < 0) { # add this user to the group. $command = "$cmd_gpasswd -a \'" . $$n[$i] . "\' \'" . $$new_data[$groups_prop_map{"name"}] . "\'"; $i ++; &my_system ($command); } elsif ($r > 0) { # delete the user from the group. $command = "$cmd_gpasswd -d \'" . $$o[$j] . "\' \'" . $$new_data[$groups_prop_map{"name"}] . "\'"; $j ++; &my_system ($command); } else { # The information is the same. Go to next tuple. $i ++; $j ++; } } } } sub save_profiles { if ($xml_doc_copy =~ /(.*)<\/profiles>/ms) { my $path; $path = &xst_file_get_data_path . "/users/profiles.xml"; &xst_file_buffer_save ($1, $path); } } # --- XML parsing --- # sub xml_scan { my $doc; my @tree; my $i; if ($xst_input_file eq "") { $doc .= $i while ($i = ); } else { open INPUT_FILE, $xst_input_file; $doc .= $i while ($i = ); close INPUT_FILE; } &xst_debug_print_string_to_file ("in.xml", $doc); $xml_doc_copy = $doc; @xst_xml_scan_list = ($doc =~ /([^\<]*)(\<[^\>]*\>)[ \t\n\r]*/mg); # pcdata, tag, pcdata, tag, ... $tree = &xst_xml_scan_recurse; return $tree; } # Scan XML from standard input to an internal tree. sub xml_parse { my $tree; # Scan XML to tree. $parse_tree = $tree = &xml_scan; # Save profiles &save_profiles (); # Walk the tree recursively and extract configuration parameters. # This is the top level - find and enter the "users" tag. while (@$tree) { if ($$tree[0] eq "users") { &xml_parse_users($$tree[1]); } shift @$tree; shift @$tree; } return($tree); } sub xml_parse_users { my $tree = $_[0]; shift @$tree; # Skip attributes. while (@$tree) { if ($$tree[0] eq "logindefs") { &xml_parse_login_defs ($$tree[1]); } elsif ($$tree[0] eq "passwd_last_modified") { &xml_parse_passwd_last_modified ($$tree[1]); } elsif ($$tree[0] eq "group_last_modified") { &xml_parse_group_last_modified ($$tree[1]); } elsif ($$tree[0] eq "userdb") { &xml_parse_userdb ($$tree[1]); } elsif ($$tree[0] eq "groupdb") { &xml_parse_groupdb ($$tree[1]); } else { &xst_report ("xml_unexp_tag", $$tree[0]); } shift @$tree; shift @$tree; } } sub xml_parse_login_defs { my $tree = $_[0]; shift @$tree; # Skip attributes. while (@$tree) { if ($login_defs_prop_map{$$tree[0]} ne undef) { $login_defs{$$tree[0]} = $$tree[1][2]; } else { &xst_report ("xml_unexp_tag", $$tree[0]); } shift @$tree; shift @$tree; } } sub xml_parse_passwd_last_modified { my $tree = $_[0]; shift @$tree; # Skip attributes. &xst_report ("xml_unexp_arg", "", "passwd_last_modified") if ($$tree[0] ne "0"); $passwd_last_modified = $$tree[1]; } sub xml_parse_group_last_modified { my $tree = $_[0]; shift @$tree; # Skip attributes. &xst_report ("xml_unexp_arg", "", "group_last_modified") if ($$tree[0] ne "0"); $group_last_modified = $$tree[1]; } sub xml_parse_userdb { my $tree = $_[0]; shift @$tree; # Skip attributes. while (@$tree) { if ($$tree[0] eq "user") { &xml_parse_user ($$tree[1]); } else { &xst_report ("xml_unexp_tag", $$tree[0]); } shift @$tree; shift @$tree; } } sub xml_parse_user { my $tree = $_[0]; my @line = (); shift @$tree; # Skip attributes. while (@$tree) { if ($users_prop_map{$$tree[0]} ne undef) { $line[$users_prop_map{$$tree[0]}] = &xst_xml_unquote($$tree[1][2]); } else { &xst_report ("xml_unexp_tag", $$tree[0]); } shift @$tree; shift @$tree; } $users_hash{sprintf ("%06d", $line[0])} = [@line]; push (@users, [@line]); } sub xml_parse_groupdb { my $tree = $_[0]; shift @$tree; # Skip attributes. while (@$tree) { if ($$tree[0] eq "group") { &xml_parse_group ($$tree[1]); } else { &xst_report ("xml_unexp_tag", $$tree[0]); } shift @$tree; shift @$tree; } } sub xml_parse_group { my $tree = $_[0]; my (@line, $copy, $a, @u); shift @$tree; # Skip attributes. while (@$tree) { if ($groups_prop_map{$$tree[0]} ne undef) { if ($$tree[0] eq "users") { $line[$groups_prop_map{$$tree[0]}] = $$tree[1]; } else { $line[$groups_prop_map{$$tree[0]}] = $$tree[1][2]; } } else { &xst_report ("xml_unexp_tag", $$tree[0]); } shift @$tree; shift @$tree; } # @$a should be a parse tree of the array of users. $a = pop @line; shift @$a; while (@$a) { if ($$a[0] eq "user") { push @u, $$a[1][2]; } else { &xst_report ("xml_unexp_tag", $$tree[0]); } shift @$a; shift @$a; } push @line, [@u]; $copy = [@line]; $groups_hash{sprintf ("%06d", $line[0])} = $copy; push (@groups, $copy); } # --- XML printing --- # sub xml_print_profiles { my $i; return if (@$profiles < 1); &xst_xml_print_line ("\n"); &xst_xml_enter (); foreach $i (@$profiles) { &xst_xml_print_line ($i); } &xst_xml_leave (); &xst_xml_print_line ("\n"); &xst_xml_print_vspace (); } sub xml_print_shells { my $i; return if (@shells < 1); &xst_xml_print_line ("\n"); &xst_xml_enter (); foreach $i (@shells) { &xst_xml_print_line ("$i"); } &xst_xml_leave (); &xst_xml_print_line ("\n"); &xst_xml_print_vspace (); } sub xml_print_nis { my ($key, $i, $j, $k); &xst_xml_print_line ("\n"); &xst_xml_enter (); foreach $i (@users) { &xst_xml_print_vspace (); &xst_xml_print_line ("\n"); &xst_xml_enter (); for ($j = 0; $j < ($#users_prop_array - 1) / 2; $j++) { &xst_xml_print_line ("<" . $users_prop_map{"$j"} . ">" . $$i[$j] . "\n"); } &xst_xml_leave (); &xst_xml_print_line ("\n"); } &xst_xml_leave (); &xst_xml_print_vspace (); &xst_xml_print_line ("\n"); &xst_xml_print_line ("\n"); &xst_xml_print_vspace (); &xst_xml_print_line ("\n"); &xst_xml_enter (); foreach $i (@groups) { &xst_xml_print_vspace (); &xst_xml_print_line ("\n"); &xst_xml_enter (); for ($j = 0; $j < ($#groups_prop_array - 1) / 2 - 1; $j++) { &xst_xml_print_line ("<" . $groups_prop_map{$j} . ">" . $$i[$j] . "\n"); } &xst_xml_print_line ("\n"); &xst_xml_enter (); $k = $$i[$groups_prop_map{"users"}]; foreach $j (@$k) { &xst_xml_print_line ("$j\n"); } &xst_xml_leave (); &xst_xml_print_line ("\n"); &xst_xml_leave (); &xst_xml_print_line ("\n"); } &xst_xml_leave (); &xst_xml_print_vspace (); &xst_xml_print_line ("\n"); } sub xml_print { my ($key, $i, $j, $k); &xst_xml_print_begin (); &xst_xml_print_vspace (); &xst_xml_print_line ("\n"); &xst_xml_print_vspace (); &xst_xml_print_line ("\n"); &xst_xml_enter (); foreach $key (keys %login_defs) { if ($key ne "") { if ($key ne "files") { &xst_xml_print_line ("<$key>" . $login_defs{$key} . "\n"); } else { &xst_xml_print_line ("\n"); &xst_xml_enter (); foreach $j (@{$login_defs{$key}}) { &xst_xml_print_line ("" . $j . "\n"); } &xst_xml_leave (); &xst_xml_print_line (""); } } } &xst_xml_leave (); &xst_xml_print_line ("\n"); &xst_xml_print_vspace (); &xml_print_profiles (); &xml_print_shells (); &xst_xml_print_line ("\n"); &xst_xml_print_vspace (); &xst_xml_print_line ("\n"); &xst_xml_print_vspace (); &xst_xml_print_line ("$passwd_last_modified\n"); &xst_xml_print_vspace (); &xst_xml_print_line ("\n"); &xst_xml_enter (); foreach $i (@users) { &xst_xml_print_vspace (); &xst_xml_print_line ("\n"); &xst_xml_enter (); for ($j = 0; $j < ($#users_prop_array - 1) / 2; $j++) { &xst_xml_print_line ("<" . $users_prop_map{"$j"} . ">" . xst_xml_quote ($$i[$j]) . "\n"); } &xst_xml_leave (); &xst_xml_print_line ("\n"); } &xst_xml_leave (); &xst_xml_print_vspace (); &xst_xml_print_line ("\n"); &xst_xml_print_line ("\n"); &xst_xml_print_vspace (); &xst_xml_print_line ("\n"); &xst_xml_print_vspace (); &xst_xml_print_line ("$group_last_modified\n"); &xst_xml_print_vspace (); &xst_xml_print_line ("\n"); &xst_xml_enter (); foreach $i (@groups) { &xst_xml_print_vspace (); &xst_xml_print_line ("\n"); &xst_xml_enter (); for ($j = 0; $j < ($#groups_prop_array - 1) / 2 - 1; $j++) { &xst_xml_print_line ("<" . $groups_prop_map{$j} . ">" . xst_xml_quote ($$i[$j]) . "\n"); } &xst_xml_print_line ("\n"); &xst_xml_enter (); $k = $$i[$groups_prop_map{"users"}]; foreach $j (@$k) { &xst_xml_print_line ("$j\n"); } &xst_xml_leave (); &xst_xml_print_line ("\n"); &xst_xml_leave (); &xst_xml_print_line ("\n"); } &xst_xml_leave (); &xst_xml_print_vspace (); &xst_xml_print_line ("\n"); # This for test purposes only &xml_print_nis () if $TEST_NIS; &xst_xml_print_end (); } # --- Get (read) config --- # sub get { &xst_report ("users_getting_db"); &read_login_defs (); &xst_print_progress (); &read_group (); &xst_print_progress (); &read_passwd_shadow (); &xst_print_progress (); &read_profiles (); &xst_print_progress (); &read_shells (); &xst_print_progress (); &xst_end(); &xml_print (); } sub set { &xml_parse (); if ($xst_do_immediate) { &write_group_passwd (); &write_logindefs (); } &xst_end(); } # --- Filter config: XML in, XML out --- # sub filter { &xml_parse (); &xst_end(); &xml_print (); } # --- 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);