#-*- Mode: perl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- # Users account manager. Designed to be architecture and distribution independent. # # Copyright (C) 2000-2001 Ximian, Inc. # # Authors: Hans Petter Jansson , # Arturo Espinosa , # Tambet Ingo . # Grzegorz Golawski (PLD Support), # Milan Bouchet-Valat . # # 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. # pw: modifying users/groups and user/group data on FreeBSD. package Users::Users; use Utils::Util; use Utils::Report; use Utils::File; use Utils::Backend; use Utils::Replace; # --- 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" ); @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 = &Utils::File::locate_tool ("usermod"); $cmd_userdel = &Utils::File::locate_tool ("userdel"); $cmd_useradd = &Utils::File::locate_tool ("useradd"); $cmd_adduser = &Utils::File::locate_tool ("adduser"); $cmd_deluser = &Utils::File::locate_tool ("deluser"); $cmd_chfn = &Utils::File::locate_tool ("chfn"); $cmd_pw = &Utils::File::locate_tool ("pw"); $cmd_passwd = &Utils::File::locate_tool ("passwd"); $cmd_chpasswd = &Utils::File::locate_tool ("chpasswd"); # enum like for verbose group array positions my $i = 0; my $LOGIN = $i++; my $PASSWD = $i++; my $UID = $i++; my $GID = $i++; my $COMMENT = $i++; my $HOME = $i++; my $SHELL = $i++; my $PASSWD_STATUS = $i++; my $ENC_HOME = $i++; my $HOME_FLAGS = $i++; my $LOCALE = $i++; my $LOCATION = $i++; my $FACE = $i++; %login_defs_prop_map = (); %profiles_prop_map = (); sub get_login_defs_prop_array { my @prop_array; my @login_defs_prop_array_default = ( "QMAIL_DIR", "qmail_dir", "MAIL_DIR", "mailbox_dir", "MAIL_FILE", "mailbox_file", "PASS_MAX_DAYS", "pwd_maxdays", "PASS_MIN_DAYS", "pwd_mindays", "PASS_MIN_LEN", "pwd_min_length", "PASS_WARN_AGE", "pwd_warndays", "UID_MIN", "umin", "UID_MAX", "umax", "GID_MIN", "gmin", "GID_MAX", "gmax", "USERDEL_CMD", "del_user_additional_command", "CREATE_HOME", "create_home", "", ""); my @login_defs_prop_array_suse = ( "QMAIL_DIR", "qmail_dir", "MAIL_DIR", "mailbox_dir", "MAIL_FILE", "mailbox_file", "PASS_MAX_DAYS", "pwd_maxdays", "PASS_MIN_DAYS", "pwd_mindays", "PASS_MIN_LEN", "pwd_min_length", "PASS_WARN_AGE", "pwd_warndays", "UID_MIN", "umin", "UID_MAX", "umax", "SYSTEM_GID_MIN", "gmin", "GID_MAX", "gmax", "USERDEL_CMD", "del_user_additional_command", "CREATE_HOME", "create_home", "", ""); if ($Utils::Backend::tool{"platform"} =~ /^suse/) { @prop_array = @login_defs_prop_array_suse; } else { @prop_array = @login_defs_prop_array_default; } for ($i = 0; $prop_array [$i] ne ""; $i += 2) { $login_defs_prop_map {$prop_array [$i]} = $prop_array [$i + 1]; $login_defs_prop_map {$prop_array [$i + 1]} = $prop_array [$i]; } } sub get_profiles_prop_array { my @prop_array; my @profiles_prop_array_default = ( "NAME" , "name", "COMMENT", "comment", "LOGINDEFS", "login_defs", "HOME_PREFFIX", "home_prefix", "SHELL", "shell", "GROUP", "group", "SKEL_DIR", "skel_dir", "QMAIL_DIR" , "qmail_dir", "MAIL_DIR" , "mailbox_dir", "MAIL_FILE" , "mailbox_file", "PASS_RANDOM", "pwd_random", "PASS_MAX_DAYS" , "pwd_maxdays", "PASS_MIN_DAYS" , "pwd_mindays", "PASS_MIN_LEN" , "pwd_min_length", "PASS_WARN_AGE" , "pwd_warndays", "UID_MIN" , "umin", "UID_MAX" , "umax", "GID_MIN" , "gmin", "GID_MAX" , "gmax", "USERDEL_CMD" , "del_user_additional_command", "CREATE_HOME" , "create_home", "", ""); my @profiles_prop_array_suse = ( "NAME" , "name", "COMMENT", "comment", "LOGINDEFS", "login_defs", "HOME_PREFFIX", "home_prefix", "SHELL", "shell", "GROUP", "group", "SKEL_DIR", "skel_dir", "QMAIL_DIR" , "qmail_dir", "MAIL_DIR" , "mailbox_dir", "MAIL_FILE" , "mailbox_file", "PASS_RANDOM", "pwd_random", "PASS_MAX_DAYS" , "pwd_maxdays", "PASS_MIN_DAYS" , "pwd_mindays", "PASS_MIN_LEN" , "pwd_min_length", "PASS_WARN_AGE" , "pwd_warndays", "UID_MIN" , "umin", "UID_MAX" , "umax", "GID_MIN" , "gmin", "GID_MAX" , "gmax", "USERDEL_CMD" , "del_user_additional_command", "CREATE_HOME" , "create_home", "", ""); if ($Utils::Backend::tool{"platform"} =~ /suse/) { @prop_array = @profiles_prop_array_suse; } else { @prop_array = @profiles_prop_array_default; } for ($i = 0; $prop_array[$i] ne ""; $i += 2) { $profiles_prop_map {$prop_array [$i]} = $prop_array [$i + 1]; $profiles_prop_map {$prop_array [$i + 1]} = $prop_array [$i]; } } #FIXME: do not hardcode GIDs like that my $rh_logindefs_defaults = { 'shell' => '/bin/bash', 'group' => -1, 'skel_dir' => '/etc/skel/', }; my $gentoo_logindefs_defaults = { 'shell' => '/bin/bash', 'group' => 100, 'skel_dir' => '/etc/skel/', }; my $freebsd_logindefs_defaults = { 'shell' => '/bin/sh', 'group' => -1, 'skel_dir' => '/etc/skel/', }; my $logindefs_dist_map = { 'redhat-6.2' => $rh_logindefs_defaults, 'redhat-7.0' => $rh_logindefs_defaults, 'redhat-7.1' => $rh_logindefs_defaults, 'redhat-7.2' => $rh_logindefs_defaults, 'redhat-7.3' => $rh_logindefs_defaults, 'redhat-8.0' => $rh_logindefs_defaults, 'mandrake-9.0' => $rh_logindefs_defaults, 'pld-1.0' => $rh_logindefs_defaults, 'fedora-1' => $rh_logindefs_defaults, 'debian' => $rh_logindefs_defaults, 'vine-3.0' => $rh_logindefs_defaults, 'gentoo' => $gentoo_logindefs_defaults, 'archlinux' => $gentoo_logindefs_defaults, 'slackware-9.1.0' => $gentoo_logindefs_defaults, 'freebsd-5' => $freebsd_logindefs_defaults, 'suse-9.0' => $gentoo_logindefs_defaults, 'solaris-2.11' => $gentoo_logindefs_defaults, }; # Add reporting table. &Utils::Report::add ({ 'users_read_profiledb_success' => ['info', 'Profiles read successfully.'], 'users_read_profiledb_fail' => ['warn', 'Profiles read failed.'], 'users_read_users_success' => ['info', 'Users read successfully.'], 'users_read_users_fail' => ['warn', 'Users read failed.'], 'users_read_users_invalid' => ['warn', 'Invalid user found while reading (missing fields).'], 'users_read_groups_success' => ['info', 'Groups read successfully.'], 'users_read_groups_fail' => ['warn', 'Groups read failed.'], 'users_read_shells_success' => ['info', 'Shells read successfully.'], 'users_read_shells_fail' => ['warn', 'Reading shells failed.'], 'users_write_profiledb_success' => ['info', 'Profiles written successfully.'], 'users_write_profiledb_fail' => ['warn', 'Writing profiles failed.'], 'users_write_users_success' => ['info', 'Users written successfully.'], 'users_write_users_fail' => ['warn', 'Writing users failed.'], 'users_write_groups_success' => ['info', 'Groups written successfully.'], 'users_write_groups_fail' => ['warn', 'Writing groups failed.'], }); sub logindefs_add_defaults { # Common for all distros my $logindefs = { 'home_prefix' => '/home/', }; &get_profiles_prop_array (); # Distro specific my $dist_specific = $logindefs_dist_map->{$Utils::Backend::tool{"platform"}}; # Just to be 100% sure SOMETHING gets filled: unless ($dist_specific) { $dist_specific = $rh_logindefs_defaults; } foreach my $key (keys %$dist_specific) { # Make sure there's no crappy entries if (exists ($profiles_prop_map{$key}) || $key eq "groups") { $logindefs->{$key} = $dist_specific->{$key}; } } return $logindefs; } sub get_logindefs { my $logindefs; &get_login_defs_prop_array (); $logindefs = &logindefs_add_defaults (); # Get new data in case someone has changed login_defs manually. my $fh = &Utils::File::open_read_from_names (@login_defs_names); if ($fh) { while (<$fh>) { next if &Utils::Util::ignore_line ($_); chomp; my @line = split /[ \t]+/; if (exists $login_defs_prop_map{$line[0]}) { $logindefs->{$login_defs_prop_map{$line[0]}} = $line[1]; } } close $fh; } else { # Put safe defaults for distros/OS that don't have any defaults file $logindefs->{"umin"} = '1000'; $logindefs->{"umax"} = '60000'; $logindefs->{"gmin"} = '1000'; $logindefs->{"gmax"} = '60000'; } return $logindefs; } sub get { my ($ifh, @users, %users_hash, $fd, @passwd_status); my (@line, @users); # Find the passwd file. $ifh = &Utils::File::open_read_from_names(@passwd_names); return unless ($ifh); while (<$ifh>) { chomp; # FreeBSD allows comments in the passwd file. next if &Utils::Util::ignore_line ($_); @line = split ':', $_, -1; $login = $line[$LOGIN]; # skip invalid users, else they will create troubles if (($line[$LOGIN] eq "") || ($line[$UID] eq "")) { &Utils::Report::do_report ("users_read_users_invalid"); next; } @comment = split ',', $line[$COMMENT], 5; # we need to make sure that there are 5 elements push @comment, "" while (scalar (@comment) < 5); $line[$COMMENT] = [@comment]; # always return empty string - anyway, passwd should be in /etc/shadow $line[$PASSWD] = ""; $users_hash{$login} = [@line]; # Detect lock status of password # We run 'passwd' instead of reading /etc/shadow directly # to avoid leaving sensitive data in memory (hard to clear in perl) $fd = &Utils::File::run_pipe_read ("passwd -S $login"); @passwd_status = split ' ', <$fd>; &Utils::File::close_file ($fd); if ($passwd_status[1] eq "P") { $users_hash{$login}[$PASSWD_STATUS] = 0; } elsif ($passwd_status[1] eq "NP") { $users_hash{$login}[$PASSWD_STATUS] = 1; } else # "L", means locked password { $users_hash{$login}[$PASSWD_STATUS] = 1 << 1; } # max value for an unsigned 32 bits integer means no main group $users_hash{$login}[$GID] = 0xFFFFFFFF if (!$users_hash{$login}[$GID]); # TODO: read actual values $users_hash{$login}[$ENC_HOME] = 0; $users_hash{$login}[$HOME_FLAGS] = 0; $users_hash{$login}[$LOCALE] = ""; $users_hash{$login}[$LOCATION] = ""; $users_hash{$login}[$FACE] = ""; } &Utils::File::close_file ($ifh); # transform the hash into an array foreach $login (keys %users_hash) { push @users, $users_hash{$login}; } return \@users; } sub del_user { my ($user) = @_; my (@command, $remove_home); $remove_home = $$user[$HOME_FLAGS] & 1; if ($Utils::Backend::tool{"system"} eq "FreeBSD") { if ($remove_home) { @command = ($cmd_pw, "userdel", "-r", "-n", $$user[$LOGIN]); } else { @command = ($cmd_pw, "userdel", "-n", $$user[$LOGIN]); } } elsif ($cmd_deluser) # use deluser (preferred method) { if ($remove_home) { @command = ($cmd_deluser, "--remove-home", $$user[$LOGIN]); } else { @command = ($cmd_deluser, $$user[$LOGIN]); } } else # use userdel { if ($remove_home) { @command = ($cmd_userdel, "--remove", $$user[$LOGIN]); } else { @command = ($cmd_userdel, $$user[$LOGIN]); } } &Utils::File::run (@command); } sub change_user_chfn { my ($login, $old_comment, $comment) = @_; my ($fname, $office, $office_phone, $home_phone); my (@command, $str); return if !$login; # Compare old and new data return if (Utils::Util::struct_eq ($old_comment, $comment)); $str = join (",", @$comment); if ($Utils::Backend::tool{"system"} eq "FreeBSD") { @command = ($cmd_pw, "usermod", "-n", $login, "-c", $str); } else { @command = ($cmd_usermod, "-c", $str, $login); } &Utils::File::run (@command); } sub set_passwd { my ($login, $password, $passwd_status) = @_; my ($pwdpipe); # handle empty password via passwd, as all tools don't support it if ($passwd_status & 1) { &Utils::File::run ("passwd", "-d", $login); return; } if ($Utils::Backend::tool{"system"} eq "FreeBSD") { my ($command); $command = "$cmd_pw usermod \'$login\' -h 0"; $pwdpipe = &Utils::File::run_pipe_write ($command); print $pwdpipe $password; &Utils::File::close_file ($pwdpipe); } elsif ($Utils::Backend::tool{"system"} eq "SunOS") { my ($command); $command = "$cmd_passwd --stdin \'$login\'"; $pwdpipe = &Utils::File::run_pipe_write ($command); print $pwdpipe $password; &Utils::File::close_file ($pwdpipe); } else { $pwdpipe = &Utils::File::run_pipe_write ($cmd_chpasswd); print $pwdpipe "$login:$password"; &Utils::File::close_file ($pwdpipe); } } # Enable/disable password, only call if value has changed sub set_lock { my ($login, $passwd_status) = @_; my ($pwdpipe); if ($passwd_status & (1 << 1)) { &Utils::File::run ("passwd", "-l", $login); } else { &Utils::File::run ("passwd", "-u", $login); } } # This function allows empty values to be passed, in which cas # the platform's tools will choose the default. sub add_user { my ($user) = @_; my ($tool_mkdir, $chown_home, $real_uid, $real_gid); $tool_mkdir = &Utils::File::locate_tool ("mkdir"); # If directory is specified, ensure its parents exist. # When using default prefix, we assume the directory exists. if ($$user[$HOME]) { my $home_parents, $erase_home; $home_parents = $$user[$HOME]; $home_parents =~ s/\/+[^\/]+\/*$//; &Utils::File::run ($tool_mkdir, "-p", $home_parents); $erase_home = $$user[$HOME_FLAGS] & (1 << 3); # Remove home if asked, it will be created from scratch by platform tools if ($erase_home && -e $$user[$HOME] && $$user[$HOME] ne "/") { # Remove trailing slash(es) to avoid issues with rm on symlinks $$user[$HOME] =~ s|/*$||; @command = ("rm", "-Rf", $$user[$HOME]); &Utils::File::run (@command); } } # max value means default UID or GID here $real_uid = ($$user[$UID] != 0xFFFFFFFF); $real_gid = ($$user[$GID] != 0xFFFFFFFF); if ($Utils::Backend::tool{"system"} eq "FreeBSD") { my $logindefs; # FreeBSD doesn't create the home directory if (!$$user[$HOME]) { $logindefs = &get_logindefs (); $$user[$HOME] = "$$logindefs{'home_prefix'}/$$user[$LOGIN]"; } &Utils::File::run ($tool_mkdir, "-p", $$user[$HOME]); @command = ($cmd_pw, "useradd", "-n", $$user[$LOGIN], "-h", "-"); # disable login until password is set push (@command, ("-s", $$user[$HOME])) if ($$user[$HOME]); push (@command, ("-s", $$user[$SHELL])) if ($$user[$SHELL]); push (@command, ("-u", $$user[$UID])) if ($real_uid); push (@command, ("-g", $$user[$GID])) if ($real_gid); &Utils::File::run (@command); } elsif ($Utils::Backend::tool{"system"} eq "SunOS") { @command = ($cmd_useradd); push (@command, ("-d", $$user[$HOME])) if ($$user[$HOME]); push (@command, ("-s", $$user[$SHELL])) if ($$user[$SHELL]); push (@command, ("-u", $$user[$UID])) if ($real_uid); push (@command, ("-g", $$user[$GID])) if ($real_gid); push (@command, $$user[$LOGIN]); &Utils::File::run (@command); } else { if ($cmd_adduser && $Utils::Backend::tool{"platform"} !~ /^slackware/ && $Utils::Backend::tool{"platform"} !~ /^archlinux/ && $Utils::Backend::tool{"platform"} !~ /^redhat/ && $Utils::Backend::tool{"platform"} !~ /^gentoo/) { # use adduser if available and valid (slackware one is b0rk) # set empty gecos fields and password, they will be filled out later @command = ($cmd_adduser, "--gecos", "", "--disabled-password"); push (@command, ("--home", $$user[$HOME])) if ($$user[$HOME]); push (@command, ("--shell", $$user[$SHELL])) if ($$user[$SHELL]); push (@command, ("--uid", $$user[$UID])) if ($real_uid); push (@command, ("--gid", $$user[$GID])) if ($real_gid); # Allow encrypted home if the tool is present if ($$user[$ENC_HOME] && &Utils::File::locate_tool ("mount.ecryptfs")) { push (@command, "--encrypt-home"); } push (@command, $$user[$LOGIN]); &Utils::File::run (@command); } else { # fallback to useradd @command = ($cmd_useradd, "-m"); push (@command, ("-d", $$user[$HOME])) if ($$user[$HOME]); push (@command, ("-s", $$user[$SHELL])) if ($$user[$SHELL]); push (@command, ("-u", $$user[$UID])) if ($real_uid); push (@command, ("-g", $$user[$GID])) if ($real_gid); push (@command, $$user[$LOGIN]); &Utils::File::run (@command); } } &change_user_chfn ($$user[$LOGIN], undef, $$user[$COMMENT]); &set_passwd ($$user[$LOGIN], $$user[$PASSWD], $$user[$PASSWD_STATUS]); &set_lock ($$user[$LOGIN], $$user[$PASSWD_STATUS]); $chown_home = $$user[$HOME_FLAGS] & (1 << 1); # update user to get values that were filled $user = &get_user ($$user[$LOGIN]); # ensure user owns its home dir if asked if ($chown_home && $$user[$HOME] ne "/") { @command = ("chown", "-R", "$$user[$LOGIN]:$$user[$GID]", $$user[$HOME]); &Utils::File::run (@command); } # Return the new user with default values filled. # Returns NULL if user doesn't exist, which means failure. return $user; } sub change_user { my ($old_user, $new_user) = @_; my $chown_home, $move_home, $copy_home, $erase_home; if ($Utils::Backend::tool{"system"} eq "FreeBSD") { @command = ($cmd_pw, "usermod", $$old_user[$LOGIN], "-l", $$new_user[$LOGIN], "-u", $$new_user[$UID], "-d", $$new_user[$HOME], "-g", $$new_user[$GID], "-s", $$new_user[$SHELL]); &Utils::File::run (@command); } else { @command = ($cmd_usermod, "-d", $$new_user[$HOME], "-g", $$new_user[$GID], "-l", $$new_user[$LOGIN], "-s", $$new_user[$SHELL], "-u", $$new_user[$UID], $$old_user[$LOGIN]); &Utils::File::run (@command); } &change_user_chfn ($$new_user[$LOGIN], $$old_user[$COMMENT], $$new_user[$COMMENT]); &set_passwd ($$new_user[$LOGIN], $$new_user[$PASSWD], $$user[$PASSWD_STATUS]); # Only change lock status if status has changed if (($$new_user[$PASSWD_STATUS] & (1 << 1)) != ($$old_user[$PASSWD_STATUS] & (1 << 1))) { &set_lock ($$new_user[$LOGIN], $$new_user[$PASSWD_STATUS]); } # Home directory handling if ($$new_user[$HOME] ne $$old_user[$HOME]) { # remove old home dir $remove_home = $$new_user[$HOME_FLAGS] & (1 << 0); # ensure user owns home dir $chown_home = $$new_user[$HOME_FLAGS] & (1 << 1); # copy old home files to new dir $copy_home = $$new_user[$HOME_FLAGS] & (1 << 2); # remove files present in path to new home $erase_home = $$new_user[$HOME_FLAGS] & (1 << 3); # Remove trailing slash(es) to avoid issues with rm on symlinks # '/' becomes empty, which is easier to check for security below $$new_user[$HOME] =~ s|/*$||; $$old_user[$HOME] =~ s|/*$||; if ($erase_home && $$new_user[$HOME] && -e $$new_user[$HOME]) { @command = ("rm", "-Rf", $$new_user[$HOME]); &Utils::File::run (@command); } if ($copy_home && $$new_user[$HOME] && $$old_user[$HOME]) { # Remove new directory if present, to avoid troubles when merging. # GUIs should ask the user before passing this flag anyway! if (-e $$new_user[$HOME]) { @command = ("rm", "-Rf", $$new_user[$HOME]); &Utils::File::run (@command); } if (-e $$old_user[$HOME]) { if ($remove_home) { @command = ("mv", "-f", $$old_user[$HOME], $$new_user[$HOME]); } else { if ($Utils::Backend::tool{"system"} eq "SunOS") { @command = ("cp", "-RPpf", $$old_user[$HOME], $$new_user[$HOME]); } else { @command = ("cp", "-af", $$old_user[$HOME], $$new_user[$HOME]); } } &Utils::File::run (@command); } } elsif ($remove_home && $$old_user[$HOME] && -e $$old_user[$HOME] ) { @command = ("rm", "-Rf", $$old_user[$HOME]); &Utils::File::run (@command); } # Create home directory owned by user if not present # If a file with this name exists, skip if (!-e $$new_user[$HOME] && $$new_user[$HOME]) { @command = ("mkdir", "-p", $$new_user[$HOME]); &Utils::File::run (@command) if (!-d $$new_user[$HOME]); @command = ("chown", "-f", "$$new_user[$LOGIN]:$$new_user[$GID]", $$new_user[$HOME]); &Utils::File::run (@command); } elsif ($chown_home && $$new_user[$HOME]) { @command = ("chown", "-Rf", "$$new_user[$LOGIN]:$$new_user[$GID]", $$new_user[$HOME]); &Utils::File::run (@command); } } # Erase password string to avoid it from staying in memory $$new_user[$PASSWD] = '0' x length ($$new_user[$PASSWD]); } sub set_logindefs { my ($config) = @_; my ($logindefs, $key, $file); return unless $config; &get_login_defs_prop_array (); foreach $key (@login_defs_names) { if (-f $key) { $file = $key; last; } } unless ($file) { &Utils::Report::do_report ("file_open_read_failed", join (", ", @login_defs_names)); return; } foreach $key (keys (%$config)) { # Write ONLY login.defs values. if (exists ($login_defs_prop_map{$key})) { &Utils::Replace::split ($file, $login_defs_prop_map{$key}, "[ \t]+", $$config{$key}); } } } sub get_self { my ($uid) = @_; my ($users) = &get (); foreach $user (@$users) { next if ($uid != $$user[$UID]); return ($$user[$COMMENT], $$user[$LOCALE]); } return ([""], ""); } sub get_user { my ($login) = @_; my ($users) = &get (); foreach $user (@$users) { next if ($login ne $$user[$LOGIN]); return $user; } return NULL; } sub set_user { my ($new_user) = @_; my ($users) = &get (); # Make backups manually, otherwise they don't get backed up. &Utils::File::do_backup ($_) foreach (@passwd_names); &Utils::File::do_backup ($_) foreach (@shadow_names); foreach $user (@$users) { if ($$new_user[$LOGIN] eq $$user[$LOGIN]) { &change_user ($user, $new_user); return; } } } sub set_self { my ($uid, @comments, $locale, $location) = @_; my ($users) = &get (); # Make backups manually, otherwise they don't get backed up. &Utils::File::do_backup ($_) foreach (@passwd_names); &Utils::File::do_backup ($_) foreach (@shadow_names); foreach $user (@$users) { if ($uid == $$user[$UID]) { &change_user_chfn ($$user[$LOGIN], $$user[$COMMENT], @comments); return; } } # TODO: change locale and location } sub get_files { my ($arr); push @$arr, @passwd_names; push @$arr, @shadow_names; return $arr; } 1;