File manager - Edit - /var/www/payraty/helpdesk/public/storage/branding_media/images/adduser.tar
Back
usr/sbin/adduser 0000755 00000112547 00000000000 0007640 0 ustar 00 #!/usr/bin/perl # adduser: a utility to add users to the system # addgroup: a utility to add groups to the system # Copyright (C) 1997, 1998, 1999 Guy Maor <maor@debian.org> # Copyright (C) 1995 Ted Hajek <tedhajek@boombox.micro.umn.edu> # Ian A. Murdock <imurdock@gnu.ai.mit.edu> # Bugfixes and other improvements Roland Bauerschmidt <rb@debian.org> # General scheme of the program adapted by the original debian 'adduser' # program by Ian A. Murdock <imurdock@gnu.ai.mit.edu>. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # #################### # See the usage subroutine for explanation about how the program can be called #################### use warnings; use strict; use Debian::AdduserCommon; use Getopt::Long; my $version = "3.118ubuntu5"; ################### # return values use constant RET_OK => 0; # OK use constant RET_OBJECT_ALREADY_EXISTS => 1; # the user or group does already exist, so the requested action cannot be performed use constant RET_INVALID_CHARS_IN_NAME => 1; # the provided name contains invalid characters use constant RET_ADDUSER_ABORTED => 1; # the program was aborted (eg via Ctrl+C) use constant RET_INVALID_CALL => 1; # getopt returned with "false" BEGIN { local $ENV{PERL_DL_NONLAZY}=1; eval 'use Locale::gettext'; if ($@) { *gettext = sub { shift }; *textdomain = sub { "" }; *LC_MESSAGES = sub { 5 }; } eval { require POSIX; import POSIX qw(setlocale); }; if ($@) { *setlocale = sub { return 1 }; } eval { require I18N::Langinfo; import I18N::Langinfo qw(langinfo YESEXPR NOEXPR); }; if ($@) { *langinfo = sub { return shift; }; *YESEXPR = sub { "^[yY]" }; *NOEXPR = sub { "^[nN]" }; } } setlocale(LC_MESSAGES, ""); textdomain("adduser"); my $yesexpr = langinfo(YESEXPR()); my %config; # configuration hash my @defaults = ("/etc/adduser.conf"); my $nogroup_id = getgrnam("nogroup") || 65534; $0 =~ s+.*/++; our $verbose = 1; # should we be verbose? my $allow_badname = 0; # should we allow bad names? my $ask_passwd = 1; # ask for a passwd? my $disabled_login = 0; # leave the new account disabled? our $configfile = undef; our $found_group_opt = undef; our $found_sys_opt = undef; our $ingroup_name = undef; our $new_firstuid = undef; our $new_gecos = undef; our $new_gid = undef; our $new_lastuid = undef; our $new_uid = undef; our $no_create_home = undef; our $special_home = undef; our $special_shell = undef; our $add_extra_groups = 0; our $use_extrausers = 0; our $encrypt_home = undef; # Global variables we need later my $existing_user = undef; my $existing_group = undef; my $new_name = undef; my $make_group_also = 0; my $home_dir = undef; my $undohome = undef; my $undouser = undef; my $undogroup = undef; my $shell = undef; my $first_uid = undef; my $last_uid = undef; my $dir_mode = undef; my $perm = undef; our @names; # Parse options, sanity checks unless ( GetOptions ("quiet|q" => sub { $verbose = 0 }, "force-badname" => \$allow_badname, "help|h" => sub { &usage(); exit RET_OK }, "version|v" => sub { &version(); exit RET_OK }, "system" => \$found_sys_opt, "group" => \$found_group_opt, "ingroup=s" => \$ingroup_name, "home=s" => \$special_home, "gecos=s" => \$new_gecos, "shell=s" => \$special_shell, "disabled-password" => sub { $ask_passwd = 0 }, "disabled-login" => sub { $disabled_login = 1; $ask_passwd = 0 }, "uid=i" => \$new_uid, "firstuid=i" => \$new_firstuid, "lastuid=i" => \$new_lastuid, "gid=i" => \$new_gid, "conf=s" => \$configfile, "no-create-home" => \$no_create_home, "encrypt-home" => \$encrypt_home, "add_extra_groups" => \$add_extra_groups, "extrausers" => \$use_extrausers, "debug" => sub { $verbose = 2 } ) ) { &usage(); exit RET_INVALID_CALL; } # everyone can issue "--help" and "--version", but only root can go on dief (gtx("Only root may add a user or group to the system.\n")) if ($> != 0); if( defined($configfile) ) { @defaults = ($configfile); } # detect the right mode my $action = $0 eq "addgroup" ? "addgroup" : "adduser"; if (defined($found_sys_opt)) { $action = "addsysuser" if ($action eq "adduser"); $action = "addsysgroup" if ($action eq "addgroup"); } # explicitly set PATH, because super (1) cleans up the path and makes adduser unusable; # this is also a good idea for sudo (which doesn't clean up) $ENV{"PATH"}="/bin:/usr/bin:/sbin:/usr/sbin"; $ENV{"IFS"}=" \t\n"; ############################ # checks related to @names # ############################ while (defined(my $arg = shift(@ARGV))) { push (@names, $arg); } if ( (! defined $names[0]) || length($names[0]) == 0 || @names > 2) { dief (gtx("Only one or two names allowed.\n")); } if (@names == 2) { # must be addusertogroup dief (gtx("Specify only one name in this mode.\n")) if ($action eq "addsysuser" || $found_group_opt); $action = "addusertogroup"; $existing_user = shift (@names); $existing_group = shift (@names); } else { # 1 parameter, must be adduser $new_name = shift (@names); } ################################### # check for consistent parameters # ################################### if ($action ne "addgroup" && defined($found_group_opt) +defined($ingroup_name) +defined($new_gid) > 1 ) { dief (gtx("The --group, --ingroup, and --gid options are mutually exclusive.\n")); } if ((defined($special_home)) && ($special_home !~ m+^/+ )) { dief (gtx("The home dir must be an absolute path.\n")); } if (defined($special_home) && $verbose) { printf gtx("Warning: The home dir %s you specified already exists.\n"),$special_home if (!defined($no_create_home) && -d $special_home); printf gtx("Warning: The home dir %s you specified can't be accessed: %s\n"), $special_home, $! if (defined($no_create_home) && ! -d $special_home); } if ($found_group_opt) { if ($action eq "addsysuser") { $make_group_also = 1; } elsif ($found_sys_opt) { $action = "addsysgroup"; } else { $action = "addgroup"; } } my $ecryptfs_setup_private; if (defined($encrypt_home)) { $ecryptfs_setup_private = &which('ecryptfs-setup-private'); } $ENV{"VERBOSE"} = $verbose; $ENV{"DEBUG"} = $verbose; # preseed configuration data and then read the config file preseed_config(\@defaults,\%config); &checkname($new_name, $found_sys_opt) if defined $new_name; $SIG{'INT'} = $SIG{'QUIT'} = $SIG{'HUP'} = 'handler'; ##### # OK, we've processed the arguments. $action equals one of the following, # and the appropriate variables have been set: # # $action = "adduser" # $new_name - the name of the new user. # $ingroup_name | $new_gid - the group to add the user to # $special_home, $new_uid, $new_gecos - optional overrides # $action = "addgroup" # $new_name - the name of the new group # $new_gid - optional override # $action = "addsysgroup" # $new_name - the name of the new group # $new_gid - optional override # $action = "addsysuser" # $new_name - the name of the new user # $make_group_also | $ingroup_name | $new_gid | 0 - which group # $special_home, $new_uid, $new_gecos - optional overrides # $action = "addusertogroup" # $existing_user - the user to be added # $existing_group - the group to add her to ##### ################# ## addsysgroup ## ################# if ($action eq "addsysgroup") { # Check if requested group already exists and we can exit safely my $ret = existing_group_ok($new_name, $new_gid); if ($ret == 3) { print STDERR "$0: " if $verbose; printf STDERR (gtx("The group `%s' already exists as a system group. Exiting.\n"), $new_name) if $verbose; exit RET_OK; } if ($ret == 1) { print STDERR "$0: " if $verbose; printf STDERR (gtx("The group `%s' already exists and is not a system group. Exiting.\n"), $new_name); exit RET_OBJECT_ALREADY_EXISTS; } if ($ret == 2) { print STDERR "$0: " if $verbose; printf STDERR (gtx("The group `%s' already exists, but has a different GID. Exiting.\n"), $new_name); exit RET_OBJECT_ALREADY_EXISTS; } dief (gtx("The GID `%s' is already in use.\n"),$new_gid) if (defined($new_gid) && defined(getgrgid($new_gid))); if (!defined($new_gid)) { $new_gid = &first_avail_gid($config{"first_system_gid"}, $config{"last_system_gid"}); if ($new_gid == -1) { print STDERR "$0: "; printf STDERR gtx("No GID is available in the range %d-%d (FIRST_SYS_GID - LAST_SYS_GID).\n"),$config{"first_system_gid"},$config{"last_system_gid"}; dief (gtx("The group `%s' was not created.\n"),$new_name); } } printf (gtx("Adding group `%s' (GID %d) ...\n"),$new_name,$new_gid) if $verbose; &invalidate_nscd("group"); my $groupadd = &which('groupadd'); if ( ($use_extrausers) || ($config{"use_extrausers"}) ) { &systemcall($groupadd, '--extrausers', '-g', $new_gid, $new_name); } else { &systemcall($groupadd, '-g', $new_gid, $new_name); } &invalidate_nscd("group"); print (gtx("Done.\n")) if $verbose; exit RET_OK; } ############## ## addgroup ## ############## if ($action eq "addgroup") { dief (gtx("The group `%s' already exists.\n"),$new_name) if (defined getgrnam($new_name)); dief (gtx("The GID `%s' is already in use.\n"),$new_gid) if (defined($new_gid) && defined(getgrgid($new_gid))); if (!defined($new_gid)) { $new_gid = &first_avail_gid($config{"first_gid"}, $config{"last_gid"}); if ($new_gid == -1) { print STDERR "$0: "; printf STDERR gtx("No GID is available in the range %d-%d (FIRST_GID - LAST_GID).\n"),$config{"first_gid"},$config{"last_gid"}; dief (gtx("The group `%s' was not created.\n"),$new_name); } } printf (gtx("Adding group `%s' (GID %d) ...\n"),$new_name,$new_gid) if $verbose; &invalidate_nscd("group"); my $groupadd = &which('groupadd'); if ( ($use_extrausers) || ($config{"use_extrausers"}) ) { &systemcall($groupadd, '--extrausers', '-g', $new_gid, $new_name); } else { &systemcall($groupadd, '-g', $new_gid, $new_name); } &invalidate_nscd("group"); print (gtx("Done.\n")) if $verbose; exit RET_OK; } #################### ## addusertogroup ## #################### if ($action eq "addusertogroup") { dief (gtx("The user `%s' does not exist.\n"),$existing_user) if (!defined getpwnam($existing_user)); dief (gtx("The group `%s' does not exist.\n"),$existing_group) if (!defined getgrnam($existing_group)); if (&user_is_member($existing_user, $existing_group)) { printf gtx("The user `%s' is already a member of `%s'.\n"), $existing_user,$existing_group if $verbose; exit RET_OK; # not really an error } printf gtx("Adding user `%s' to group `%s' ...\n"),$existing_user,$existing_group if $verbose; &invalidate_nscd(); my $gpasswd = &which('gpasswd'); if ( ($use_extrausers) || ($config{"use_extrausers"}) ) { &systemcall($gpasswd, '--extrausers', '-a',$existing_user,$existing_group); } else { &systemcall($gpasswd, '-a',$existing_user,$existing_group); } &invalidate_nscd(); print (gtx("Done.\n")) if $verbose; exit RET_OK; } ################ ## addsysuser ## ################ if ($action eq "addsysuser") { if (existing_user_ok($new_name, $new_uid) == 1) { # a user with this name already exists; it's a problem when it's not a system user my $tmp_u = getpwnam($new_name); if (($tmp_u >= $config{"first_system_uid"}) and ($tmp_u <= $config{"last_system_uid"})) { printf (gtx("The system user `%s' already exists. Exiting.\n"), $new_name) if $verbose; exit RET_OK } warnf (gtx("The user `%s' already exists, but is not a system user. Exiting.\n"), $new_name); exit RET_OBJECT_ALREADY_EXISTS; } if (existing_user_ok($new_name, $new_uid) == 2) { warnf (gtx("The user `%s' already exists with a different UID. Exiting.\n"), $new_name); exit RET_OBJECT_ALREADY_EXISTS; } if (!$ingroup_name && !defined($new_gid) && !$make_group_also) { $new_gid = $nogroup_id; } check_user_group(1); if (!defined($new_uid) && $make_group_also) { $new_uid = &first_avail_uid($config{"first_system_uid"}, $config{"last_system_uid"}); if ($new_uid == -1) { print STDERR "$0: "; printf STDERR gtx("No UID/GID pair is available in the range %d-%d (FIRST_SYS_UID - LAST_SYS_UID).\n"),$config{"first_system_uid"},$config{"last_system_uid"}; dief (gtx("The user `%s' was not created.\n"),$new_name); } $new_gid = &first_avail_gid($config{"first_system_gid"}, $config{"last_system_gid"}); $ingroup_name = $new_name; } elsif (!defined($new_uid) && !$make_group_also) { $new_uid = &first_avail_uid($config{"first_system_uid"}, $config{"last_system_uid"}); if ($new_uid == -1) { print STDERR "$0: "; printf STDERR gtx("No UID is available in the range %d-%d (FIRST_SYS_UID - LAST_SYS_UID).\n"),$config{"first_system_uid"},$config{"last_system_uid"}; dief (gtx("The user `%s' was not created.\n"),$new_name); } if (defined($new_gid)) { $ingroup_name = getgrgid($new_gid); } elsif ($ingroup_name) { $new_gid = getgrnam($ingroup_name); } else { dief (gtx("Internal error")); } } else { if (defined($new_gid)) { $ingroup_name = getgrgid($new_gid); } elsif ($ingroup_name) { $new_gid = getgrnam($ingroup_name); } elsif ($make_group_also){ $new_gid=$new_uid; $ingroup_name=$new_name; } else { dief (gtx("Internal error")); } } printf (gtx("Adding system user `%s' (UID %d) ...\n"),$new_name,$new_uid) if $verbose; &invalidate_nscd(); # if we reach this point, and the group does already exist, we can use it. if ($make_group_also && !getgrnam($new_name)) { printf (gtx("Adding new group `%s' (GID %d) ...\n"),$new_name,$new_gid) if $verbose; $undogroup = $new_name; my $groupadd = &which('groupadd'); if ( ($use_extrausers) || ($config{"use_extrausers"}) ) { &systemcall($groupadd, '--extrausers', '-g', $new_gid, $new_name); } else { &systemcall($groupadd, '-g', $new_gid, $new_name); } &invalidate_nscd("group"); } printf gtx("Adding new user `%s' (UID %d) with group `%s' ...\n"),$new_name,$new_uid,$ingroup_name if $verbose; $home_dir = $special_home || &homedir($new_name, $ingroup_name); $shell = $special_shell || '/usr/sbin/nologin'; $undouser = $new_name; my $useradd = &which('useradd'); if ( ($use_extrausers) || ($config{"use_extrausers"}) ) { &systemcall($useradd, '--extrausers', '-d', $home_dir, '-g', $ingroup_name, '-s', $shell, '-u', $new_uid, $new_name); } else { &systemcall($useradd, '-d', $home_dir, '-g', $ingroup_name, '-s', $shell, '-u', $new_uid, $new_name); } if(!$disabled_login) { my $usermod = &which('usermod'); &systemcall($usermod, '-p', '*', $new_name); } my $chage = &which('chage'); print "$chage -M 99999 $new_name\n" if ($verbose > 1); # do _not_ use systemcall() here, since systemcall() dies on # non-zero exit code and we need to do special handling here! if (system($chage, '-M', '99999', $new_name)) { if( ($?>>8) ne 15 ) { &cleanup(sprintf((gtx("`%s' returned error code %d. Exiting.\n")), "$chage -M 99999 $new_name", $?>>8)) if ($?>>8); &cleanup(sprintf((gtx("`%s' exited from signal %d. Exiting.\n")), "$chage -M 99999 $new_name", $?&255)); } else { printf STDERR (gtx("%s failed with return code 15, shadow not enabled, password aging cannot be set. Continuing.\n"), $chage); } } &invalidate_nscd(); if(defined($new_gecos)) { &ch_gecos($new_gecos); } create_homedir (0); exit RET_OK; } ############# ## adduser ## ############# if ($action eq "adduser") { if (!$ingroup_name && !defined($new_gid)) { if ($config{"usergroups"} =~ /yes/i) { $make_group_also = 1; } else { $new_gid = $config{"users_gid"}; } } check_user_group(0); $first_uid = $new_firstuid || $config{"first_uid"}; $last_uid = $new_lastuid || $config{"last_uid"}; printf (gtx("Adding user `%s' ...\n"),$new_name) if $verbose; if (!defined($new_uid) && $make_group_also) { $new_uid = &first_avail_uid($first_uid, $last_uid); if ($new_uid == -1) { print STDERR "$0: "; printf STDERR gtx("No UID/GID pair is available in the range %d-%d (FIRST_UID - LAST_UID).\n"),$first_uid,$last_uid; dief (gtx("The user `%s' was not created.\n"),$new_name); } $new_gid = &first_avail_gid($config{"first_gid"}, $config{"last_gid"}); $ingroup_name = $new_name; } elsif (!defined($new_uid) && !$make_group_also) { $new_uid = &first_avail_uid($first_uid, $last_uid); if ($new_uid == -1) { print STDERR "$0: "; printf STDERR gtx("No UID is available in the range %d-%d (FIRST_UID - LAST_UID).\n"),$config{"first_uid"},$config{"last_uid"}; dief (gtx("The user `%s' was not created.\n"),$new_name); } if (defined($new_gid)) { $ingroup_name = getgrgid($new_gid); } elsif ($ingroup_name) { $new_gid = getgrnam($ingroup_name); } else { dief (gtx("Internal error")); } } else { if (defined($new_gid)) { $ingroup_name = getgrgid($new_gid); } elsif ($ingroup_name) { $new_gid = getgrnam($ingroup_name); } elsif ($make_group_also){ $new_gid=$new_uid; $ingroup_name=$new_name; } else { dief (gtx("Internal error")); } } &invalidate_nscd(); if ($make_group_also) { printf (gtx("Adding new group `%s' (%d) ...\n"),$new_name,$new_gid) if $verbose; $undogroup = $new_name; my $groupadd = &which('groupadd'); if ( ($use_extrausers) || ($config{"use_extrausers"}) ) { &systemcall($groupadd, '--extrausers', '-g', $new_gid, $new_name); } else { &systemcall($groupadd, '-g', $new_gid, $new_name); } &invalidate_nscd(); } printf gtx("Adding new user `%s' (%d) with group `%s' ...\n"),$new_name,$new_uid,$ingroup_name if $verbose; $home_dir = $special_home || &homedir($new_name, $ingroup_name); $shell = $special_shell || $config{"dshell"}; $undouser = $new_name; my $useradd = &which('useradd'); if ( ($use_extrausers) || ($config{"use_extrausers"}) ) { &systemcall($useradd, '--extrausers', '-d', $home_dir, '-g', $ingroup_name, '-s', $shell, '-u', $new_uid, $new_name); } else { &systemcall($useradd, '-d', $home_dir, '-g', $ingroup_name, '-s', $shell, '-u', $new_uid, $new_name); } &invalidate_nscd(); create_homedir (1); # copy skeleton data # useradd without -p has left the account disabled (password string is '!') my $yesexpr = langinfo(YESEXPR()); if ($ask_passwd) { for (;;) { my $passwd = &which('passwd'); # do _not_ use systemcall() here, since systemcall() dies on # non-zero exit code and we need to do special handling here! system($passwd, $new_name); my $ok = $?>>8; if ($ok != 0) { my $answer; # hm, error, should we break now? print (gtx("Permission denied\n")) if ($ok == 1); print (gtx("invalid combination of options\n")) if ($ok == 2); print (gtx("unexpected failure, nothing done\n")) if ($ok == 3); print (gtx("unexpected failure, passwd file missing\n")) if ($ok == 4); print (gtx("passwd file busy, try again\n")) if ($ok == 5); print (gtx("invalid argument to option\n")) if ($ok == 6); # Translators: [y/N] has to be replaced by values defined in your # locale. You can see by running "locale noexpr" which regular # expression will be checked to find positive answer. print (gtx("Try again? [y/N] ")); chop ($answer=<STDIN>); last if ($answer !~ m/$yesexpr/o); } else { last; ## passwd ok } } } else { if(!$disabled_login) { my $usermod = &which('usermod'); &systemcall($usermod, '-p', '*', $new_name); } } if (defined($new_gecos)) { &ch_gecos($new_gecos); } else { my $noexpr = langinfo(NOEXPR()); for (;;) { my $chfn = &which('chfn'); if ( ($use_extrausers) || ($config{"use_extrausers"}) ) { &systemcall($chfn, '--extrausers', $new_name); } else { &systemcall($chfn, $new_name); } # Translators: [y/N] has to be replaced by values defined in your # locale. You can see by running "locale yesexpr" which regular # expression will be checked to find positive answer. print (gtx("Is the information correct? [Y/n] ")); chop (my $answer=<STDIN>); last if ($answer !~ m/$noexpr/o); } } if ( ( $add_extra_groups || $config{"add_extra_groups"} ) && defined($config{"extra_groups"}) ) { printf (gtx("Adding new user `%s' to extra groups ...\n"), $new_name); foreach my $newgrp ( split ' ', $config{"extra_groups"} ) { if (!defined getgrnam($newgrp)) { warnf (gtx("The group `%s' does not exist.\n"),$newgrp); next; } if (&user_is_member($new_name, $newgrp)) { printf gtx("The user `%s' is already a member of `%s'.\n"), $new_name,$newgrp if $verbose; next; } printf gtx("Adding user `%s' to group `%s' ...\n"),$new_name,$newgrp if $verbose; &invalidate_nscd(); my $gpasswd = &which('gpasswd'); if ( ($use_extrausers) || ($config{"use_extrausers"}) ) { &systemcall($gpasswd, '--extrausers', '-M', join(',', get_group_members($newgrp), $new_name), $newgrp); } else { &systemcall($gpasswd, '-M', join(',', get_group_members($newgrp), $new_name), $newgrp); } &invalidate_nscd(); } } if ($config{"quotauser"}) { printf (gtx("Setting quota for user `%s' to values of user `%s' ...\n"), $new_name, $config{quotauser}); my $edquota = &which('edquota'); &systemcall($edquota, '-p', $config{quotauser}, $new_name); } &systemcall('/usr/local/sbin/adduser.local', $new_name, $new_uid, $new_gid, $home_dir) if (-x "/usr/local/sbin/adduser.local"); exit RET_OK; } # # we never go here # # calculate home directory sub homedir { my $dir = $config{"dhome"}; $dir .= '/' . $_[1] if ($config{"grouphomes"} =~ /yes/i); $dir .= '/' . substr($_[0],0,1) if ($config{"letterhomes"} =~ /yes/i); $dir .= '/' . $_[0]; return $dir; } # create_homedir -- create the homedirectory # parameter # 1: $copy_skeleton: # if 0 -> don't copy the skeleton data # if 1 -> copy the files in /etc/skel to the newly created home directory # return values: # none sub create_homedir { my ($copy_skeleton) = @_; if ($no_create_home) { printf gtx("Not creating home directory `%s'.\n"), $home_dir if $verbose; } elsif (-e $home_dir) { printf gtx("The home directory `%s' already exists. Not copying from `%s'.\n"), $home_dir,$config{skel} if $verbose && !$no_create_home; my @homedir_stat = stat($home_dir); my $home_uid = $homedir_stat[4]; my $home_gid = $homedir_stat[5]; if (($home_uid != $new_uid) || ($home_gid != $new_gid)) { warnf gtx("Warning: The home directory `%s' does not belong to the user you are currently creating.\n"), $home_dir; } undef @homedir_stat; undef $home_uid; undef $home_gid; } else { printf gtx("Creating home directory `%s' ...\n"),$home_dir if $verbose; $undohome = $home_dir; &mktree($home_dir) || &cleanup(sprintf(gtx("Couldn't create home directory `%s': %s.\n"), $home_dir, $!)); chown($new_uid, $new_gid, $home_dir) || &cleanup("chown $new_uid:$new_gid $home_dir: $!\n"); $dir_mode = get_dir_mode($make_group_also); chmod ($dir_mode, $home_dir) || &cleanup("chmod $dir_mode $home_dir: $!\n"); if ($action eq "adduser") { # Mute the command system('sh' => ( '-c' => '"$@" >/dev/null 2>&1', '--', '/usr/sbin/zsysctl', 'userdata', 'create', $new_name, $home_dir,)); chown($new_uid, $new_gid, $home_dir) || &cleanup("chown $new_uid:$new_gid $home_dir: $!\n"); $dir_mode = get_dir_mode($make_group_also); chmod ($dir_mode, $home_dir) || &cleanup("chmod $dir_mode $home_dir: $!\n"); } if (defined($encrypt_home)) { printf gtx("Setting up encryption ...\n") if $verbose; &systemcall($ecryptfs_setup_private, '-b', '-u', $new_name); } if ($config{"skel"} && $copy_skeleton) { printf gtx("Copying files from `%s' ...\n"),$config{skel} if $verbose; open(my $FIND, "cd $config{skel}; find . -print |") || &cleanup(sprintf(gtx("fork for `find' failed: %s\n"), $!)); while (<$FIND>) { chop; next if ($_ eq "."); next if ($_ =~ qr/$config{skel_ignore_regex}/ ); ©_to_dir($config{"skel"}, $_, $home_dir, $new_uid, $new_gid, ($config{"setgid_home"} =~ /yes/i)); } } if (defined($encrypt_home)) { &systemcall("/bin/umount", $home_dir); } } } # mktree: create a directory and all parent directories, we don't care about the rights and so on # parameters: # tree: the path # return values: # none sub mktree { my($tree) = @_; my($done, @path); my $default_dir_mode = 0755; $tree =~ s:^/*(.*)/*$:$1:; # chop off leading & trailing slashes @path = split(/\//, $tree); $done = ""; while (@path) { $done .= '/' . shift(@path); -d $done || mkdir($done, $default_dir_mode) || return 0; } return 1; } # existing_user_ok: check if there's already a user present on the system which satisfies the requirements # parameter: # new_name: the name of the user to check # new_uid : the UID of the user # return values: # 0 if the the user doesn't exist # 1 if the user already exists with the specified uid (or $new_uid wasn't specified) # 2 if the user already exists, but $new_uid doesn't matches its uid sub existing_user_ok { my($new_name,$new_uid) = @_; my ($dummy1,$dummy2,$uid); if (($dummy1,$dummy2,$uid) = getpwnam($new_name)) { if( defined($new_uid) && $uid == $new_uid ) { return 1; } if (! defined($new_uid)) { return 1; } # TODO: do we really need this code? Range check shouldn't performed here if( $uid >= $config{"first_system_uid"} && $uid <= $config{"last_system_uid" } ) { return 2; } } else { return 0; } } # existing_group_ok: check if there's already a group which satiesfies the requirements # parameter: # new_name: the name of the group # new_gid : the UID of the group # return values: # 0 if the group doesn't exist # 1 if the group already exists with the specified gid (or $new_gid wasn't specified) # 2 if the group already exists, but $new_gid doesn't match its gid # 3 if the group already exists inside the system range sub existing_group_ok { my($new_name,$new_gid) = @_; my ($dummy1,$dummy2,$gid); if (($dummy1,$dummy2,$gid) = getgrnam($new_name)) { # TODO: is this check required? There shouldn't be any gid outside of our allowed range anyways ... if( $gid >= $config{"first_system_gid"} && $gid <= $config{"last_system_gid" } ) { return 3; } if (! defined($new_gid)) { return 1; } if ($gid == $new_gid) { return 1; } else { return 2; } } else { return 0; } } # check_user_group: ??? # parameters: # system: 0 if the user isn't a system user, 1 otherwise # return values: # sub check_user_group { my ($system) = @_; if( !$system || !existing_user_ok($new_name, $new_uid) ) { if( defined getpwnam($new_name) ) { if( $system ) { dief (gtx("The user `%s' already exists, and is not a system user.\n"),$new_name); } else { dief (gtx("The user `%s' already exists.\n"),$new_name); } } dief (gtx("The UID %d is already in use.\n"),$new_uid) if (defined($new_uid) && getpwuid($new_uid)); } if ($make_group_also) { if( !$system || !existing_group_ok($new_name, $new_uid) ) { dief (gtx("The group `%s' already exists.\n"),$new_name) if (defined getgrnam($new_name)); dief (gtx("The GID %d is already in use.\n"),$new_uid) if (defined($new_uid) && defined(getgrgid($new_uid))); } } else { dief (gtx("The group `%s' does not exist.\n"),$ingroup_name) if ($ingroup_name && !defined(getgrnam($ingroup_name))); dief (gtx("The GID %d does not exist.\n"),$new_gid) if (defined($new_gid) && !defined(getgrgid($new_gid))); } } # copy_to_dir : # parameters: # fromdir # file # todir # newi # newg # sgiddir # return values: # none sub copy_to_dir { my($fromdir, $file, $todir, $newu, $newg, $sgiddir) = @_; if (-l "$fromdir/$file") { my $target=readlink("$fromdir/$file") or &cleanup("readlink: $!\n"); my $curgid="$)"; my $curuid="$>"; my $error=""; $)="$newg"; $>="$newu"; symlink("$target", "$todir/$file") or $error="$!"; $>="$curuid"; $)="$curgid"; if( "$error" ne "" ) { &cleanup("symlink: $!\n"); } return; } elsif (-f "$fromdir/$file") { open (FILE, "$fromdir/$file") || &cleanup("open $fromdir/$file: $!"); open (NEWFILE, ">$todir/$file") || &cleanup("open >$todir/$file: $!"); (print NEWFILE <FILE>) || &cleanup("print $todir/$file: $!"); close FILE; close(NEWFILE) || &cleanup("close $todir/$file "); } elsif (-d "$fromdir/$file") { mkdir("$todir/$file", 700) || &cleanup("mkdir: $!"); } else { &cleanup(sprintf((gtx("Cannot deal with %s.\nIt is not a dir, file, or symlink.\n")), "$fromdir/$file")); } chown($newu, $newg, "$todir/$file") || &cleanup("chown $newu:$newg $todir/$file: $!\n"); $perm = (stat("$fromdir/$file"))[2] & 07777; $perm |= 02000 if (-d "$fromdir/$file" && ($perm & 010) && $sgiddir); chmod($perm, "$todir/$file") || &cleanup("chmod $todir/$file: $!\n"); } # checkname: perform some sanity checks # parameters: # name: the name to check # system: 0 if the user isn't a system user, 1 otherwise # return values: # none (exits on error) sub checkname { my ($name, $system) = @_; if ($name !~ /^[_.A-Za-z0-9][-\@_.A-Za-z0-9]*\$?$/) { printf STDERR (gtx("%s: To avoid problems, the username should consist only of letters, digits, underscores, periods, at signs and dashes, and not start with a dash (as defined by IEEE Std 1003.1-2001). For compatibility with Samba machine accounts \$ is also supported at the end of the username\n"), $0); exit RET_INVALID_CHARS_IN_NAME;; } if ($system ? $name !~ qr/$config{"name_regex_system"}/ : $name !~ qr/$config{"name_regex"}/) { if ($allow_badname) { print (gtx("Allowing use of questionable username.\n")) if ($verbose); } else { printf STDERR (gtx("%s: Please enter a username matching the regular expression configured via the NAME_REGEX[_SYSTEM] configuration variable. Use the `--force-badname' option to relax this check or reconfigure NAME_REGEX.\n"), $0); exit RET_INVALID_CHARS_IN_NAME; } } } # first_avail_uid: return the first available uid in given range # parameters: # min, max: the range # return values: # -1 if no free uid is available # otherwise the choosen uid sub first_avail_uid { my ($min, $max) = @_; printf (gtx("Selecting UID from range %d to %d ...\n"),$min,$max) if ($verbose > 1); my $t = $min; while ($t <= $max) { return $t if (!defined(getpwuid($t))); $t++; } return -1; # nothing available } # first_avail_gid: return the first available gid in given range # parameters: # min, max: the range # return values: # -1 if no free gid is available # otherwise the choosen gid sub first_avail_gid { my ($min, $max) = @_; printf (gtx("Selecting GID from range %d to %d ...\n"),$min,$max) if ($verbose > 1); my $t = $min; while ($t <= $max) { return $t if (!defined(getgrgid($t))); $t++; } return -1; # nothing available } sub ch_gecos { my $chfn = &which('chfn'); my $gecos = shift; if($gecos =~ /,/) { my($gecos_name,$gecos_room,$gecos_work,$gecos_home,$gecos_other) = split(/,/,$gecos); if ( ($use_extrausers) || ($config{"use_extrausers"}) ) { &systemcall($chfn, '--extrausers', '-f', $gecos_name, '-r', $gecos_room, $new_name); &systemcall($chfn,'--extrausers','-w',$gecos_work,$new_name) if(defined($gecos_work)); &systemcall($chfn,'--extrausers','-h',$gecos_home,$new_name) if(defined($gecos_home)); &systemcall($chfn,'--extrausers','-o',$gecos_other,$new_name) if(defined($gecos_other)); } else { &systemcall($chfn, '-f', $gecos_name, '-r', $gecos_room, $new_name); &systemcall($chfn,'-w',$gecos_work,$new_name) if(defined($gecos_work)); &systemcall($chfn,'-h',$gecos_home,$new_name) if(defined($gecos_home)); &systemcall($chfn,'-o',$gecos_other,$new_name) if(defined($gecos_other)); } } else { if ( ($use_extrausers) || ($config{"use_extrausers"}) ) { &systemcall($chfn, '--extrausers', '-f', $gecos, $new_name); } else { &systemcall($chfn, '-f', $gecos, $new_name); } } } # user is member of group? sub user_is_member { my($user, $group) = @_; for (split(/ /, (getgrnam($group))[3])) { return 1 if ($user eq $_); } return 0; } sub cleanup { my ($msg) = @_; printf (gtx("Stopped: %s\n"),$msg); if ($undohome) { printf (gtx("Removing directory `%s' ...\n"),$undohome); &systemcall('rm', '-rf', $undohome); } if ($undouser) { printf (gtx("Removing user `%s' ...\n"),$undouser); &systemcall('userdel', $undouser); } if ($undogroup) { printf (gtx("Removing group `%s' ...\n"),$undogroup); &systemcall('groupdel', $undogroup); } # do we need to invalidate the nscd cache here, too? exit RET_ADDUSER_ABORTED; } sub handler { my($sig) = @_; # Translators: the variable %s is INT, QUIT, or HUP. # Please do not insert a space character between SIG and %s. &cleanup(sprintf(gtx("Caught a SIG%s.\n"), $sig)); } sub version { printf (gtx("adduser version %s\n\n"), $version); print gtx("Adds a user or group to the system. Copyright (C) 1997, 1998, 1999 Guy Maor <maor\@debian.org> Copyright (C) 1995 Ian Murdock <imurdock\@gnu.ai.mit.edu>, Ted Hajek <tedhajek\@boombox.micro.umn.edu> \n"); print gtx( "This program is free software; you can redistribute it and/or modify it under the terms of the GNU 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 General Public License, /usr/share/common-licenses/GPL, for more details. "); } sub usage { printf gtx( "adduser [--home DIR] [--shell SHELL] [--no-create-home] [--uid ID] [--firstuid ID] [--lastuid ID] [--gecos GECOS] [--ingroup GROUP | --gid ID] [--disabled-password] [--disabled-login] [--add_extra_groups] [--encrypt-home] USER Add a normal user adduser --system [--home DIR] [--shell SHELL] [--no-create-home] [--uid ID] [--gecos GECOS] [--group | --ingroup GROUP | --gid ID] [--disabled-password] [--disabled-login] [--add_extra_groups] USER Add a system user adduser --group [--gid ID] GROUP addgroup [--gid ID] GROUP Add a user group addgroup --system [--gid ID] GROUP Add a system group adduser USER GROUP Add an existing user to an existing group general options: --quiet | -q don't give process information to stdout --force-badname allow usernames which do not match the NAME_REGEX[_SYSTEM] configuration variable --extrausers uses extra users as the database --help | -h usage message --version | -v version number and copyright --conf | -c FILE use FILE as configuration file\n\n"); } sub get_dir_mode { my $setgid = shift; # no longer make home directories setgid per default (closes: #64806) $setgid = 0 unless $config{"setgid_home"} =~ /yes/i; my $dir_mode = $config{"dir_mode"}; if(!defined($dir_mode) || ! ($dir_mode =~ /[0-7]{3}/ || $dir_mode =~ /[0-7]{4}/)) { $dir_mode = $setgid ? 2755 : 0755; } else { $dir_mode = $config{"dir_mode"}; if($setgid && (length($dir_mode) == 3 || $dir_mode =~ /^[0-1|4-5][0-7]{3}$/)) { $dir_mode += 2000; } } return oct($dir_mode); } # Local Variables: # mode:cperl # cperl-indent-level:4 # End: # vim:set ai et sts=4 sw=4 tw=0: adduser.conf 0000644 00000005724 00000000000 0007013 0 ustar 00 # /etc/adduser.conf: `adduser' configuration. # See adduser(8) and adduser.conf(5) for full documentation. # The DSHELL variable specifies the default login shell on your # system. DSHELL=/bin/bash # The DHOME variable specifies the directory containing users' home # directories. DHOME=/home # If GROUPHOMES is "yes", then the home directories will be created as # /home/groupname/user. GROUPHOMES=no # If LETTERHOMES is "yes", then the created home directories will have # an extra directory - the first letter of the user name. For example: # /home/u/user. LETTERHOMES=no # The SKEL variable specifies the directory containing "skeletal" user # files; in other words, files such as a sample .profile that will be # copied to the new user's home directory when it is created. SKEL=/etc/skel # FIRST_SYSTEM_[GU]ID to LAST_SYSTEM_[GU]ID inclusive is the range for UIDs # for dynamically allocated administrative and system accounts/groups. # Please note that system software, such as the users allocated by the base-passwd # package, may assume that UIDs less than 100 are unallocated. FIRST_SYSTEM_UID=100 LAST_SYSTEM_UID=999 FIRST_SYSTEM_GID=100 LAST_SYSTEM_GID=999 # FIRST_[GU]ID to LAST_[GU]ID inclusive is the range of UIDs of dynamically # allocated user accounts/groups. FIRST_UID=1000 LAST_UID=59999 FIRST_GID=1000 LAST_GID=59999 # The USERGROUPS variable can be either "yes" or "no". If "yes" each # created user will be given their own group to use as a default. If # "no", each created user will be placed in the group whose gid is # USERS_GID (see below). USERGROUPS=yes # If USERGROUPS is "no", then USERS_GID should be the GID of the group # `users' (or the equivalent group) on your system. USERS_GID=100 # If DIR_MODE is set, directories will be created with the specified # mode. Otherwise the default mode 0755 will be used. DIR_MODE=0750 # If SETGID_HOME is "yes" home directories for users with their own # group the setgid bit will be set. This was the default for # versions << 3.13 of adduser. Because it has some bad side effects we # no longer do this per default. If you want it nevertheless you can # still set it here. SETGID_HOME=no # If QUOTAUSER is set, a default quota will be set from that user with # `edquota -p QUOTAUSER newuser' QUOTAUSER="" # If SKEL_IGNORE_REGEX is set, adduser will ignore files matching this # regular expression when creating a new home directory SKEL_IGNORE_REGEX="dpkg-(old|new|dist|save)" # Set this if you want the --add_extra_groups option to adduser to add # new users to other groups. # This is the list of groups that new non-system users will be added to # Default: #EXTRA_GROUPS="dialout cdrom floppy audio video plugdev users" # If ADD_EXTRA_GROUPS is set to something non-zero, the EXTRA_GROUPS # option above will be default behavior for adding new, non-system users #ADD_EXTRA_GROUPS=1 # check user and group names also against this regular expression. #NAME_REGEX="^[a-z][-a-z0-9_]*\$" # use extrausers by default #USE_EXTRAUSERS=1 TODO 0000644 00000001014 00000000000 0005171 0 ustar 00 TODO for adduser ---------------- For adduser 3.x there is to do: * make adduser also work with super (fix #) * do not hard code /usr/share/perl5 in debian/rules (cf. perl policy) * Use --msgid-bugs-address when using po4a in debian/rules * Address lintian's "untranslatable debconf template" This is triggered by adduser/title, which is not intended for translation. The suggestion in the lintian output is to state "for internal use only" as part of the description, but this isn't the case; it's just a name. copyright 0000644 00000003713 00000000000 0006444 0 ustar 00 This package was first put together by Ian Murdock <imurdock@debian.org> and was maintained by Steve Phillips <sjp@cvfn.org> from sources written for the Debian Project by Ian Murdock, Ted Hajek <tedhajek@boombox.micro.umn.edu>, and Sven Rudolph <sr1@inf.tu-dresden.de>. Since Nov 27 1996, it was maintained by Guy Maor <maor@debian.org>. He rewrote most of it. Since May 20 2000, it is maintained by Roland Bauerschmidt <rb@debian.org>. Since March 24 2004, it is maintained by Roland Bauerschmidt <rb@debian.org>, and co-maintained by Marc Haber <mh+debian-packages@zugschlus.de> Since 23 Oct 2005, it has been maintained by Joerg Hoh <joerg@joerghoh.de> Since June 2006, it has been maintained by Stephen Gran <sgran@debian.org> deluser is Copyright (C) 2000 Roland Bauerschmidt <rb@debian.org> and based on the source code of adduser. adduser is Copyright (C) 1997, 1998, 1999 Guy Maor <maor@debian.org>. adduser is Copyright (C) 1995 Ted Hajek <tedhajek@boombox.micro.umn.edu> with portions Copyright (C) 1994 Debian Association, Inc. The examples directory has been contributed by John Zaitseff, and is GPL V2 as well. This program is free software; you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. On Debian GNU/Linux systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL-2'. examples/adduser.local.conf 0000644 00000052551 00000000000 0011722 0 ustar 00 ############################################################################ # /etc/adduser.local.conf: Configuration for /usr/local/sbin/adduser.local # ############################################################################ # [JNZ] Modified 21-Jun-2013 # This file configures the local system additions to adduser(8) and should # be modified to suit local conditions. # # adduser.local is a script that configures a user's account for various # "services". These services are simply convenient names for directories # that must be created, Unix groups to which the user must be added, files # that need to be copied and so on. # # Note that adduser(8) can now perform SOME of the tasks that adduser.local # does, particularly by using the EXTRA_GROUPS and ADD_EXTRA_GROUPS # variables in /etc/adduser.conf. However, adduser.local is far more # flexible than doing just that... # # Please see the end of this file for an explanation of its syntax. ###################### # Global Options # ###################### # The skelother variable points to the "other" (secondary) skeletal # directory. This directory is similar to /etc/skel (see the SKEL variable # in /etc/adduser.conf), except that files are not necessarily copied to the # home directory. skelother = /etc/skel.other # The dirmode variable specifies the octal mode used by chmod(1) for any # directories created by adduser.local. Note, however, that such created # directories automatically inherit the SGID (set group ID) bit from their # parent directory. dirmode = 0755 # The filemode variable specifies the octal mode used by chmod(1) for any # files created by adduser.local. filemode = 0644 ##################### # USERS service # ##################### # Add the user to the Unix group "users". Every user on this machine # should be a member of this group. This is already done if the file # /etc/adduser.conf includes the setting "USERGROUPS=no". If USERGROUPS # is set to "yes", you should uncomment the following three lines. service = users group[users] = users addtogroup[users] = true ################### # WWW service # ################### # Configure the WWW service for the user, a service that has a real UID # associated with it. Assuming the user "www" has a GID of "www" and a # home directory of "/home/www" (in actual fact, the values are taken from # the password database), the following actions are performed: # # - the user is added to the "www" group # - the directory "/home/www/doc/users/$USER" is created, owned by # the user, with group owner "www" # - the link "public_html" is created to point to this directory # - the file "/etc/skel.other/index.html" is copied to this directory # # This assumes that the system user "www" and group "www" are NOT the same # as the UID and GID of the web server ("www-data" on my system). The "www" # account is for the web administrator. service = www user[www] = www addtogroup[www] = true homedir[www] = "" subdir[www] = "doc/users" althome[www] = false mkdir[www] = true chgrpdir[www] = true mklink[www] = true linkname[www] = "public_html" skelfile[www] = "index.html" chgrpskel[www] = true # If your web server's configuration follows the "other" (more common!) # standard for personal web pages (wherein the "public_html" directory is a # real directory in the user's home directory), you might want to use # something like the following: #service = www #homedir[www] = "" #subdir[www] = "public_html" #althome[www] = true #mkdir[www] = true #skelfile[www] = "index.html" ################### # FTP service # ################### # Configure the FTP service for the user in a similar way to the WWW # service above. The only difference is that no skeleton file is copied. service = ftp user[ftp] = ftp addtogroup[ftp] = true homedir[ftp] = "" subdir[ftp] = "doc/users" althome[ftp] = false mkdir[ftp] = true chgrpdir[ftp] = true mklink[ftp] = true linkname[ftp] = "public_ftp" ############################## # Restricted FTP service # ############################## # Create the directory ~ftp/doc-restricted/users/$USER, owned by the user, # for the Restricted FTP service on the ZAP Group server. service = ftp_r user[ftp_r] = ftp homedir[ftp_r] = "" subdir[ftp_r] = "doc-restricted/users" althome[ftp_r] = false mkdir[ftp_r] = true chgrpdir[ftp_r] = true #################### # DATA service # #################### # Create the directory /data/$USER, owned by the user. This is only done # if /data exists (it is an ordinary directory, not a mount point). #service = data #homedir[data] = "/data" #subdir[data] = "" #mounted[data] = false #mkdir[data] = true ##################### # CDROM service # ##################### # Add the user to the Unix group "cdrom" (if it exists). This allows the # user to access the CD-ROM hardware on the machine. service = cdrom group[cdrom] = cdrom addtogroup[cdrom] = true ###################### # FLOPPY service # ###################### # Add the user to the Unix group "floppy" (if it exists). This allows the # user to access the floppy drive on the machine. service = floppy group[floppy] = floppy addtogroup[floppy] = true ##################### # AUDIO service # ##################### # Add the user to the Unix group "audio" (if it exists). This allows the # user to access the audio hardware on the machine. service = audio group[audio] = audio addtogroup[audio] = true ################### # DIP service # ################### # Add the user to the Unix group "dip" (if it exists). This allows the # user to dial out using the local modem. service = dip group[dip] = dip addtogroup[dip] = true ##################### # VIDEO service # ##################### # Add the user to the Unix group "video" (if it exists). This allows the # user to use video devices plugged into the computer. service = video group[video] = video addtogroup[video] = true ####################### # PLUGDEV service # ####################### # Add the user to the Unix group "plugdev" (if it exists). This allows # the user to use the pmount daemon with pluggable devices. service = plugdev group[plugdev] = plugdev addtogroup[plugdev] = true ####################### # SCANNER service # ####################### # Add the user to the Unix group "scanner" (if it exists). This allows # the user to use any attached scanners. service = scanner group[scanner] = scanner addtogroup[scanner] = true ########################### # Syntax of this file # ########################### # The syntax of this file will be familiar to anyone who has used a # scripting language before. This file is processed line by line, with each # line either being blank (and hence ignored), a comment or a configuration # variable. # # Comment lines (such as this one) begin with a hash character ("#") and # continue to the end of the line. The hash character may be preceded by # white space. Comment lines, like blank lines, are ignored. # # All lines that are not blank or are comment lines contain configuration # variables (one per line, with no comments allowed). A configuration # variable has one of two forms: # # VARIABLE = VALUE # VARIABLE[SERVICE] = VALUE # # The first form is for global variables, while the second form is for # variables associated with a particular service. Both the variable name # and the service name are alphanumeric strings and are case sensitive (ie, # the names "SKELOTHER", "skelother" and "SkelOther" refer to three # different variables). # # The value is typically a string which may or may not be case sensitive. # It may be (but usually does not need to be) surrounded by single or double # quotes, in which case everything within the quotes is part of the value. # Note that white space may surround the variable, service and value # components; such white space is discarded, unless it appears in quotes. # You may NOT use backslash to quote quote characters! # # If a value required is a boolean, "0", "false", "f", "no" and "n" are # treated as the false value, while "1", "true", "t", "yes" and "y" are # treated as the true value. In both cases, the value is case-insensitive. # # # GLOBAL VARIABLES: # ================= # # The following global variables are available: # # skelother # dirmode # filemode # # These are described in the section "Global Options" above. # # # SERVICE VARIABLES: # ================== # # The main role of adduser.local is to configure a user's account for # various "services". These services are simply convenient names for # directories that must be created, Unix groups to which the user must be # added, files that need to be copied and so on. # # adduser.local is informed of the existence of a service by the "service" # global variable: # # service = SERVICENAME # # The service name SERVICENAME may be any case-sensitive alphanumeric # string. Examples used within this file are "www" and "data". Service # names need not correspond to any real service --- they are completely # internal to adduser.local, and are only used as a key for service # variables. The "service" global variable may appear multiple times, each # time with a different service name. # # The order of the "service" global variables IS important, as that is the # order in which those services are created. This is important if one # service depends on a prior one having been set up. # # The "service" global variable must appear before any of the services # variables for that service are defined. # # The following service variables are available, and may be specified in any # order: # # user # group # addtogroup # homedir # subdir # althome # mounted # mkdir # chgrpdir # mklink # linkname # skelfile # chgrpskel # # Remember that each service variable is followed by a service name in # square brackets. In the following explanations, "SVC" is used as a # sample service name. # # # user[SVC] = USER # # Specifies that the service belongs to a real user, and that that # service user name is USER. This user name must appear in the password # database file either in the first field (ie, a user name) or in the # third (ie, a numeric UID). # # Specifying a user name or UID also sets default values for the # "group" and "homedir" service variables. These default values are # taken from the password database (the "homedir" variable is only set # if the "althome" variable is set to false). # # # group[SVC] = GROUP # # Specifies that the service's group name is GROUP. This group name # must appear in the group database file either in the first field (ie, # a group name) or in the third (ie, a numeric GID). # # If this variable is not specified, or is specified with GROUP as an # empty string "", and the user variable is specified (and points to a # valid user), the group name is taken to be the service user's default # group. For example, if "user[svc] = mail" were to be specified, and # group[svc] were not, the group used would be default group for the # user "mail" (which happens to be GID 8, ie, "mail"). # # This group is also used for the group owner of directories, links # and copied files, depending on the settings of the "chgrpdir" and # "chgrpskel" variables. # # # addtogroup[SVC] = BOOLEAN # # Instructs whether to add the user to the group specified by the # "group" variable or implied by the "user" variable. If true, # adduser.local adds the user to the group, assuming that the group, # in fact, exists. # # If this variable is not specified, false is assumed. # # # homedir[SVC] = PATH # # Specifies the service's home directory as an absolute path name (ie, # starting from "/"). The service's home directory is used to check if # it is a mount point, as well as a base directory for the "mkdir" and # "skelfile" variables. If the directory does not exist, those # variables take no effect. # # If this variable is not specified, or is specified with PATH as an # empty string "", the value used for the service's home directory is # calculated in one of two ways. The first method is to use the home # directory of the service user; the second is to use the home directory # of the user for whom adduser.local was called. # # The first method is used when the "althome" variable is set to false # and the "user" variable is specified (and points to a valid user). # For example, if "user[svc] = www" and "althome[svc] = false" were to # be specified, the default value of the "homedir" variable would be # taken from www's home directory, typically "/var/www". # # The second method is used when the "althome" variable is true. For # example, if adduser.local were to be called for the user "anna", and # "althome" were set to true, the "homedir" variable would be set to the # home directory of anna, typically "/home/anna". # # Note that neither of these methods is used if the "homedir" variable # is set to anything other than an empty string; in such a case, the # specified value for the variable is always used. # # # subdir[SVC] = PATH # # Specifies a subdirectory off the home directory. This subdirectory is # used for creating the new directory, copying the skeleton file and for # the destination of the link. # # If the "althome" variable is set to false, the subdirectory must # already exist and is used in conjunction with the home directory and # the user's name (for whom adduser.local was called). For example, if # the following were to be specified: # # homedir[svc] = /media/zip # subdir[svc] = home # althome[svc] = false # mkdir[svc] = true # # and the user's name (for whom adduser.local was called) was "james", # the directory "/media/zip/home/james" would be created. # # If, on the other hand, the "althome" variable was set to true, the # subdirectory is used only in conjunction with the home directory; it # is THAT directory that is created. For example, if the following were # to be specified: # # althome[svc] = true # subdir[svc] = "public_html" # mkdir[svc] = true # # and adduser.local were called for the user "kathy" (who had the home # directory "/home/kathy"), the directory "/home/kathy/public_html" # would be created. # # If this variable is not specified, blank is assumed. # # # althome[SVC] = BOOLEAN # # Specifies whether the default value for the "homedir" variable is to # be taken from the service's home directory or from the user's home # directory (for whom adduser.local was called). If false, the # service's home directory (implied by the "user" setting) is used. If # true, the actual user's home directory is used. # # This variable also controls whether or not the user's login name is # used as part of the directory created by the "mkdir" variable and used # by the "mklink" and "skelfile" variables. See "homedir" and "mklink" # for more details. # # If this variable is not specified, false is assumed. # # # mounted[SVC] = BOOLEAN # # Specifies whether to check if the directory specified by the # "homedir" variable (or implied by other variables) is mounted or # not. A directory is mounted if it, or any parent directory, is # mounted (excluding the root directory, which is always mounted). # For example, if the following were to be specified (and the user's # name were "alice"): # # homedir[svc] = /home/external/server/ftp # subdir[svc] = doc/users # mounted[svc] = true # mkdir[svc] = true # # then the directory "/home/external/server/ftp/doc/users/alice" would # be created only if either "/home/external/server/ftp", # "/home/external/server", "/home/external" or "/home" were mounted. # # If this variable is not specified, false is assumed (ie, the mount # check is NOT performed). # # Note that "checking for mounting" is defined as examining the contents # of /proc/mounts. It does NOT actually attempt to mount the # directories. # # # mkdir[SVC] = BOOLEAN # # Directs adduser.local whether or not to create the directory specified # by the "homedir" and "subdir" variables. If the "althome" variable is # false, the directory that is created has the user's login name at the # end. In all cases, the newly created directory belongs to that user. # For example, if adduser.local was called for the user "david", and the # following lines were to be specified: # # homedir[data1] = "/data/1" # subdir[data1] = "users" # althome[data1] = false # mkdir[data1] = true # # then the directory "/data/1/users/david" would be created, owned by # the user "david". If, on the other hand, the following were to be # specified (for the same user "david"): # # subdir[www] = "public_html" # althome[www] = true # mkdir[www] = true # # then the directory "/home/david/public_html" would be created # (assuming "/home/david" was david's home directory), owned by the # user "david". # # The mode of the directory is taken from the "dirmode" global variable # in this configuration file. See also the comment on that global # variable. # # The group owner of the directory is either the same as the user's (in # this case, if the user "david" was in the group "users" by default, # then the group owner would be "users"), or the same as the service # user's group (see the "group" variable for more information). The # "chgrpdir" variable specifies which of these two options is used. # # If this variable is not specified, false is assumed. # # # chgrpdir[SVC] = BOOLEAN # # Specifies the group owner of any directory and link created by the # "mkdir" and "mklink" variables respectively. If true is specified, # the group owner is the same as specified by the "group" variable (or # implied by the "user" variable). If false is specified, the group # owner is the same as the actual user's default group. # # If this variable is not specified, false is assumed. # # # mklink[SVC] = BOOLEAN # # Specifies whether or not to create a symbolic link to the created # directory (see "mkdir" above) in the actual user's home directory. # The name of the link is taken from the "linkname" variable below. For # example, if the following were to be specified, and adduser.local were # called for the user "mark": # # homedir[data1] = "/data/1" # subdir[data1] = "users" # althome[data1] = false # mkdir[data1] = true # mklink[data1] = true # linkname[data1] = "data1" # # then, not only would the directory "/data/1/users/mark" be created, # but the symbolic link "data1" would be created in his home directory # as well, pointing to that directory (that is, "/home/mark/data1" -> # "/data/1/users/mark"). # # If this variable is not specified, false is assumed. # # # linkname[SVC] = PATH # # Specifies the name of the symbolic link created in the user's home # directory, as demonstrated in the example above. If PATH includes # subdirectories, these subdirectories must already exist before the # symbolic link is created; these can be created by other services prior # to this one. # # If the "mklink" variable is true, and the "linkname" variable is not # specified, or is an empty string "", the name of the service is used # (as specified by the "service" global variable). # # # skelfile[SVC] = PATH # # Instructs adduser.local to copy the file PATH from the "skelother" # skeleton directory (see the global variable of that name) into the # newly-created directory specified by the "mkdir" variable. For # example, if adduser.local was called for the user "nina", and the # following lines were to be specified: # # homedir[www] = "/home/www" # subdir[www] = "doc/users" # althome[www] = false # mkdir[www] = true # skelfile[www] = "index.html" # # then the directory "/home/www/doc/users/nina" would be created and the # file "index.html" would be copied from /etc/skel.other (assuming this # is the directory specified by the "skelother" global variable) into # that newly-created directory. # # The newly-copied file will have a mode as specified by the "filemode" # global variable, and its group owner will either be the default group # of the user, or the group as specified by the "group" variable or # implied by the "user" variable. See the "chgrpskel" variable below. # # If this variable is not specified, or PATH is an empty string "", no # file is copied. If a file of that name already exists, it is NOT # overwritten. Only one file may be specified in any given service; if # more are needed, simply create additional services with matching # "homedir", "subdir", "althome" and "mkdir" variables. # # # chgrpskel[SVC] = BOOLEAN # # Determines whether or not adduser.local changes the group owner of the # copied skeleton file (specified by the "skelfile" variable above) to # the group specified by the "group" variable or implied by the "user" # variable. If this variable is false, the default group of the user # remains the group owner. # # If this variable is not specified, false is assumed. # # # End of /etc/adduser.local.conf. examples/adduser.local 0000755 00000063502 00000000000 0010777 0 ustar 00 #!/usr/bin/perl -w ######################################################################### # # # ADDUSER Local System Additions v4.9 # # Copyright (C) 1999-2013, John Zaitseff # # # ######################################################################### # Author: John Zaitseff <J.Zaitseff@zap.org.au> # Date: 21st June, 2013 # Version: 4.9 # This program, once installed as /usr/local/sbin/adduser.local, is auto- # matically called by the adduser(8) system program on a Debian system. # This script completes the creation of a user account in a system- # dependent way. # # This script is automatically called by adduser with arguments "USERNAME # UID GID HOMEDIR". See adduser(8) for more details. In addition, this # script may be called manually; run "perldoc adduser.local" for more # details. # This program, including associated files, is free software. You may # distribute it and/or modify it under the terms of the GNU 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 # General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ######################################################################### # Configuration parameters and default values use strict; # Enforce better programming habits use locale; # Allow locale-specific sorting, etc. use utf8; # This script may use the UTF-8 character set use open ":locale"; # Use locale for standard input/output use Getopt::Long; # Parse command line options use Pod::Usage; # Display command line usage (our $O = $0) =~ s,^.*/,,; # Script name (without path) our $version = "4.9"; # Script version our $copyright = # Copyright information "$O v$version: adduser(8) local system additions\n" . "Copyright (C) 1999-2013, John Zaitseff.\n"; our @adduser = ('/usr/sbin/adduser', '--quiet'); # adduser(8) our @chown = ('/bin/chown', '-h'); # chown(1) our @install = ('/usr/bin/install', '-p'); # install(1) our $procmounts = '/proc/mounts'; # List of current mounts # These default values are extensively documented in adduser.local.conf. our $d_conffile = '/etc/adduser.local.conf'; # Configuration file location our $d_skelother = '/etc/skel.other'; # Location of skeleton files our $d_dirmode = '2755'; # Octal mode for directories our $d_filemode = '0644'; # Octal mode for files our $d_user = ''; # Default service user name our $d_group = ''; # Default service group name our $d_addtogroup = 'false'; # Default for addtogroup variable our $d_homedir = ''; # Default home directory our $d_subdir = ''; # Default subdirectory our $d_althome = 'false'; # Default for use alternate home directory our $d_mounted = 'false'; # Default for checking if mounted our $d_mkdir = 'false'; # Default for creating directory our $d_chgrpdir = 'false'; # Default for chgrpdir variable our $d_mklink = 'false'; # Default for creating symbolic link our $d_linkname = ''; # Default for symbolic link name our $d_skelfile = ''; # Default for skeleton file our $d_chgrpskel = 'false'; # Default for chgrpskel variable # Strings appearing in the configuration file our $s_skelother = 'skelother'; our $s_dirmode = 'dirmode'; our $s_filemode = 'filemode'; our $s_service = 'service'; our $s_user = 'user'; our $s_group = 'group'; our $s_addtogroup = 'addtogroup'; our $s_homedir = 'homedir'; our $s_subdir = 'subdir'; our $s_althome = 'althome'; our $s_mounted = 'mounted'; our $s_mkdir = 'mkdir'; our $s_chgrpdir = 'chgrpdir'; our $s_mklink = 'mklink'; our $s_linkname = 'linkname'; our $s_skelfile = 'skelfile'; our $s_chgrpskel = 'chgrpskel'; our @s_false = ('false', 'f', 'no', 'n', '0'); our @s_true = ('true', 't', 'yes', 'y', '1'); # Strings internal to this program (as used by the %cv hash) our $s_svcuid = '.svcuid'; # Storage for UID of service's user name our $s_svcgid = '.svcgid'; # GID of service's user name or group name our $s_actualdir = '.actualdir'; # Actual dir: homedir + subdir + username our $s_actuallink = '.actuallink'; # Actual sym link: user homedir + linkname our $s_actualsrcf = '.actualsrcf'; # Actual source file: skelother + skelfile our $s_actualdstf = '.actualdstf'; # Actual dest file: actualdir + skelfile our $s_addtogroupB = '.addtogroupB'; # Boolean versions of variables our $s_althomeB = '.althomeB'; our $s_mountedB = '.mountedB'; our $s_mkdirB = '.mkdirB'; our $s_chgrpdirB = '.chgrpdirB'; our $s_mklinkB = '.mklinkB'; our $s_chgrpskelB = '.chgrpskelB'; # Function prototypes sub chkbool($$$$); sub lchown(@); sub showusage(); sub showversion(); sub showcmdlerr(@); ######################################################################### # Initialise global variables our $conffile = $d_conffile; # Default configuration file our $verbose = 1; # Be verbose by default our $dryrun = 0; # NOT a dry run by default our @services = (); # No services to install by default # %cv is a hash for all configuration variables read in from the # configuration file. Global variables are represented by their strings, # eg, $cv{"skelother"}. Service-specific variables are represented by the # service string value, a comma, then their string, eg, $cv{"www","user"}. # The %cl hash plays a similar role, but contains the line number of the # configuration. our (%cv, %cl); $cv{$s_skelother} = $d_skelother; $cl{$s_skelother} = 0; $cv{$s_dirmode} = $d_dirmode; $cl{$s_dirmode} = 0; $cv{$s_filemode} = $d_filemode; $cl{$s_filemode} = 0; # For safety's sake, initialise the PATH environment variable $ENV{PATH} = '/usr/sbin:/usr/bin:/sbin:/bin'; # Declare some global variables our $username; # Username for which adduser.local was called our $uid; # User's UID our $gid; # User's GID our $homedir; # User's home directory ######################################################################### # Process command-line arguments our $opt_help; our $opt_version; Getopt::Long::Configure("bundling"); GetOptions('help|h|?' => \$opt_help, 'version|V' => \$opt_version, 'conf=s' => \$conffile, 'dry-run|n!' => \$dryrun, 'verbose|v!' => \$verbose, 'quiet|q' => sub { $verbose = 0; }, ) or showcmdlerr(); showusage() if $opt_help; showversion() if $opt_version; ######################################################################### # Process additional command-line parameters: USERNAME [UID GID HOMEDIR] if ($#ARGV < 0) { showcmdlerr("$O: Missing USERNAME parameter"); } if ($#ARGV > 3) { showcmdlerr("$O: Too many command-line parameters"); } # Include some sanity checking. These checks are not particularly # rigorous, as root can do anything anyway... It is meant to stop silly # mistakes, not to stop thinking. In any case, this script SHOULD only # be called from adduser(8)... die "$O: Only root can execute this program\n" if ($> != 0) and (! $dryrun); if ($#ARGV == 0) { # Only a single parameter: USERNAME $username = $ARGV[0]; (my $t_name, undef, $uid, $gid, undef, undef, undef, $homedir) = getpwnam($username); die "$O: No such user: $username\n" if ! defined($t_name); } elsif ($#ARGV == 3) { # Four parameters: USERNAME UID GID HOMEDIR $username = $ARGV[0]; $uid = $ARGV[1]; $gid = $ARGV[2]; $homedir = $ARGV[3]; $homedir =~ s,/$,,; # Remove trailing '/' if present (my $t_name, undef, my $t_uid, my $t_gid) = getpwnam($username); die "$O: No such user: $username\n" if ! defined($t_name); die "$O: No such UID: $uid\n" if ! defined(getpwuid($uid)); die "$O: No such GID: $gid\n" if ! defined(getgrgid($gid)); die "$O: UID of user $username not the same as $uid\n" if $t_uid != $uid; die "$O: GID of user $username not the same as $gid\n" if $t_gid != $gid; die "$O: Directory does not exist: $homedir\n" if ! -d $homedir; } else { showcmdlerr("$O: Missing UID, GID and/or HOMEDIR parameters"); } ######################################################################### # Process the configuration file if (! -r $conffile) { warn "$O: Cannot read configuration file $conffile:\n"; warn "$O: $conffile: $!\n"; exit(0); } print "Processing configuration file $conffile\n" if $verbose; open(CONFFILE, $conffile) or die "$O: $conffile: $!\n"; while (<CONFFILE>) { my ($var, $svc, $val); chomp; # Skip comments and blank lines next if /^\s*#/ or /^\s*$/; # Try matching a global variable with or without quotes if ((($var, $val) = /^\s*(\w+)\s*=\s*(.*)$/) == 2) { # Remove trailing spaces and surrounding quotes # (Technically, doing it this way is slightly sloppy) $val =~ s/^(.*?)\s*$/$1/; $val =~ s/^\"(.*)\"$/$1/; $val =~ s/^\'(.*)\'$/$1/; # Process the global variable if ($var eq $s_service) { # Special global configuration variable "service" my $svc = $val; if (grep($_ eq $svc, @services)) { warn "$O: Service \"$val\" redefined at $conffile:$.\n"; next; } push @services, $val; # Set up default values $cv{$svc,$s_user} = $d_user; $cv{$svc,$s_group} = $d_group; $cv{$svc,$s_addtogroup} = $d_addtogroup; $cv{$svc,$s_homedir} = $d_homedir; $cv{$svc,$s_subdir} = $d_subdir; $cv{$svc,$s_althome} = $d_althome; $cv{$svc,$s_mounted} = $d_mounted; $cv{$svc,$s_mkdir} = $d_mkdir; $cv{$svc,$s_chgrpdir} = $d_chgrpdir; $cv{$svc,$s_mklink} = $d_mklink; $cv{$svc,$s_linkname} = $d_linkname; $cv{$svc,$s_skelfile} = $d_skelfile; $cv{$svc,$s_chgrpskel} = $d_chgrpskel; $cl{$svc,$s_user} = 0; $cl{$svc,$s_group} = 0; $cl{$svc,$s_addtogroup} = 0; $cl{$svc,$s_homedir} = 0; $cl{$svc,$s_subdir} = 0; $cl{$svc,$s_althome} = 0; $cl{$svc,$s_mounted} = 0; $cl{$svc,$s_mkdir} = 0; $cl{$svc,$s_chgrpdir} = 0; $cl{$svc,$s_mklink} = 0; $cl{$svc,$s_linkname} = 0; $cl{$svc,$s_skelfile} = 0; $cl{$svc,$s_chgrpskel} = 0; } else { # Ordinary global variable if (! defined($cv{$var})) { warn "$O: Unknown global variable \"$var\" at $conffile:$.\n"; next; } $cv{$var} = $val; $cl{$var} = $.; } } # Try matching a service variable with or without quotes elsif ((($var, $svc, $val) = /^\s*(\w+)\s*\[\s*(\w+)\s*\]\s*=\s*(.*)$/) == 3) { # Remove trailing spaces and surrounding quotes $val =~ s/^(.*?)\s*$/$1/; $val =~ s/^\"(.*)\"$/$1/; $val =~ s/^\'(.*)\'$/$1/; if (! grep($_ eq $svc, @services)) { warn "$O: Undefined service \"$svc\" at $conffile:$.\n"; next; } if (! defined($cv{$svc,$var})) { warn "$O: Unknown service variable \"$var\" at $conffile:$.\n"; next; } $cv{$svc,$var} = $val; $cl{$svc,$var} = $.; } # Otherwise, it is an error in the configuration file else { warn "$O: Could not parse line at $conffile:$.\n"; next; } } close(CONFFILE) or die "$O: $conffile: $!\n"; ######################################################################### # Global variables sanity checking { my $t; # Check "skelother" if (! -d $cv{$s_skelother}) { warn "$O: Directory $cv{$s_skelother} does not exist\n"; } # Check "dirmode" $t = $cv{$s_dirmode}; if (($t !~ /^[01234567]{1,4}$/) or (oct($t) == 0)) { warn "$O: Illegal value \"$t\" at $conffile:$cl{$s_dirmode}\n"; warn "$O: Global variable \"$s_dirmode\" set to $d_dirmode\n"; $cv{$s_dirmode} = $d_dirmode; } # Check "filemode" $t = $cv{$s_filemode}; if (($t !~ /^[01234567]{1,4}$/) or (oct($t) == 0)) { warn "$O: Illegal value \"$t\" at $conffile:$cl{$s_filemode}\n"; warn "$O: Global variable \"$s_filemode\" set to $d_filemode\n"; $cv{$s_filemode} = $d_filemode; } } ######################################################################### # Actually perform what is required, with appropriate error checking foreach my $svc (@services) { my ($t_user, $t_group, $t_homedir); print "Processing service \"$svc\"\n" if $verbose; # Check validity of all boolean variables and convert them to true bools chkbool($svc, $s_addtogroup, $s_addtogroupB, $d_addtogroup); chkbool($svc, $s_althome, $s_althomeB, $d_althome); chkbool($svc, $s_mounted, $s_mountedB, $d_mounted); chkbool($svc, $s_mkdir, $s_mkdirB, $d_mkdir); chkbool($svc, $s_chgrpdir, $s_chgrpdirB, $d_chgrpdir); chkbool($svc, $s_mklink, $s_mklinkB, $d_mklink); chkbool($svc, $s_chgrpskel, $s_chgrpskelB, $d_chgrpskel); # Process the "user" configuration variable if ($cv{$svc,$s_user} ne '') { # Retrieve information about the specified service's user name (my $t_user, undef, $cv{$svc,$s_svcuid}, $cv{$svc,$s_svcgid}, undef, undef, undef, my $t_homedir) = getpwnam($cv{$svc,$s_user}); if (! defined($t_user)) { warn "$O: Illegal user name \"$cv{$svc,$s_user}\" at $conffile:$cl{$svc,$s_user}\n"; } else { $cv{$svc,$s_user} = $t_user; } # Only set home directory information if not specified by user if ($cv{$svc,$s_homedir} eq '') { if ($cv{$svc,$s_althomeB}) { $cv{$svc,$s_homedir} = $homedir; # From command line } else { $cv{$svc,$s_homedir} = $t_homedir; # From service's home } } # If the group parameter is not specified, get the appropriate info # from the user information if ($cv{$svc,$s_svcgid} and ($cv{$svc,$s_group} eq '')) { ($cv{$svc,$s_group}) = getgrgid($cv{$svc,$s_svcgid}); } } # Process the "group" configuration variable if ($cv{$svc,$s_group} ne '') { # Retrieve info about the group. Yes, it may have been done # above, but specifying "group" can be done without specifying # "user". In addition, a different group can be specified from # that used by "user". ($t_group, undef, $cv{$svc,$s_svcgid}) = getgrnam($cv{$svc,$s_group}); if (! defined($t_group)) { warn "$O: Illegal group name \"$cv{$svc,$s_group}\" at $conffile:$cl{$svc,$s_group}\n"; $cv{$svc,$s_addtogroup} = 'false'; $cv{$svc,$s_addtogroupB} = 0; $cv{$svc,$s_chgrpdir} = 'false'; $cv{$svc,$s_chgrpdirB} = 0; $cv{$svc,$s_chgrpskel} = 'false'; $cv{$svc,$s_chgrpskelB} = 0; } else { $cv{$svc,$s_group} = $t_group; } } # Process the "addtogroup" configuration variable if ($cv{$svc,$s_addtogroupB} and ($cv{$svc,$s_group} ne '')) { my $t = $cv{$svc,$s_group}; (my $t_group, undef, my $t_gid, my $t_members) = getgrnam $t; # Check if the user is already a member of that group if (($t_gid == $gid) or grep($_ eq $username, split(' ', $t_members))) { print " User \"$username\" already in group \"$t\"\n" if $verbose; } else { print " Adding user \"$username\" to group \"$t\"\n" if $verbose; system(@adduser, $username, $t) if ! $dryrun; } } # Process the "mounted" configuration variable $cv{$svc,$s_homedir} =~ s,/$,,; # Remove trailing / on homedir $cv{$svc,$s_subdir} =~ s,^/,,; # Remove leading / on subdir $cv{$svc,$s_subdir} =~ s,/$,,; # Remove trailing / on subdir if (($cv{$svc,$s_homedir} ne '') and $cv{$svc,$s_mountedB}) { # Need to check for "mounted" before checking for the existence of # of the service's home directory. if (! -r $procmounts) { warn "$O: $procmounts: $!\n"; } else { my ($t_dev, $t_mntpoint, $t_type, $t_options); my $ismounted = 0; my $t_dir = $cv{$svc,$s_homedir} . '/'; # Open mounts table and process it open(MOUNTS, $procmounts) or die "$O: $procmounts: $!\n"; while (<MOUNTS>) { chomp; ($t_dev, $t_mntpoint, $t_type, $t_options) = split; if ($t_mntpoint !~ m,/$,) { $t_mntpoint .= '/'; } # Check if the service's home directory is mounted # Skip "/" as that is always mounted if (($t_mntpoint ne '/') and (substr($t_dir, 0, length($t_mntpoint)) eq $t_mntpoint)) { $ismounted = 1; } } close(MOUNTS) or die "$O: $procmounts: $!\n"; if (! $ismounted) { print " Directory $cv{$svc,$s_homedir} not mounted\n" if $verbose; $cv{$svc,$s_homedir} = ''; } } } # Process the "homedir" and "subdir" configuration variables if ($cv{$svc,$s_homedir} ne '') { if (! -d $cv{$svc,$s_homedir}) { warn "$O: No such directory: $cv{$svc,$s_homedir}\n"; $cv{$svc,$s_homedir} = ''; } elsif (($cv{$svc,$s_subdir} ne '') and (! $cv{$svc,$s_althomeB})) { my $t = $cv{$svc,$s_homedir} . '/' . $cv{$svc,$s_subdir}; if (! -d $t) { warn "$O: No such directory: $t\n"; $cv{$svc,$s_subdir} = ''; $cv{$svc,$s_homedir} = ''; } } } # Calculate the actual directory to create (if necessary) if ($cv{$svc,$s_homedir} ne '') { $cv{$svc,$s_actualdir} = $cv{$svc,$s_homedir}; if ($cv{$svc,$s_subdir} ne '') { $cv{$svc,$s_actualdir} .= '/' . $cv{$svc,$s_subdir}; } if (! $cv{$svc,$s_althomeB}) { $cv{$svc,$s_actualdir} .= '/' . $username; } } # Process the "mkdir" and "chgrpdir" configuration variables if (($cv{$svc,$s_homedir} ne '') and $cv{$svc,$s_mkdirB}) { my $t = $cv{$svc,$s_actualdir}; if (-d $t) { print " Directory $t already exists\n" if $verbose; } elsif (-e $t) { warn "$O: Not a directory: $t\n"; $cv{$svc,$s_homedir} = ''; } else { print " Directory $t created\n" if $verbose; mkdir($t, oct($cv{$s_dirmode})) if ! $dryrun; # Note that this newly-created directory will inherit the # SGID (set group ID) bit from its parent directory. This # IS desired, hence, do NOT do a separate chmod()! if ($cv{$svc,$s_chgrpdirB}) { chown($uid, $cv{$svc,$s_svcgid}, $t) if ! $dryrun; } else { chown($uid, $gid, $t) if ! $dryrun; } } } # Process the "mklink" and "linkname" configuration variables if (($cv{$svc,$s_homedir} ne '') and $cv{$svc,$s_mklinkB} and (-d $cv{$svc,$s_actualdir})) { # Calculate the actual link name $cv{$svc,$s_linkname} =~ s,/$,,; # Remove trailing '/' if ($cv{$svc,$s_linkname} eq '') { $cv{$svc,$s_actuallink} = $homedir . '/' . $svc; } else { $cv{$svc,$s_actuallink} = $homedir . '/' . $cv{$svc,$s_linkname}; } # Create the symbolic link, if needed my $t = $cv{$svc,$s_actuallink}; if (-l $t) { print " Symbolic link $t already exists\n" if $verbose; } elsif (-e $t) { warn "$O: Not a symbolic link: $t\n"; } else { print " Symbolic link $t created\n" if $verbose; symlink($cv{$svc,$s_actualdir}, $t) if ! $dryrun; if ($cv{$svc,$s_chgrpdirB}) { lchown($uid, $cv{$svc,$s_svcgid}, $t) if ! $dryrun; } else { lchown($uid, $gid, $t) if ! $dryrun; } } } # Process the "skelfile" and "chgrpskel" configuration variables if (($cv{$svc,$s_homedir} ne '') and ($cv{$svc,$s_skelfile} ne '') and (-d $cv{$svc,$s_actualdir})) { my $t = $cv{$svc,$s_skelfile}; $cv{$svc,$s_actualsrcf} = $cv{$s_skelother} . '/' . $t; $cv{$svc,$s_actualdstf} = $cv{$svc,$s_actualdir} . '/' . $t; if (-e $cv{$svc,$s_actualdstf}) { print " File $cv{$svc,$s_actualdstf} already exists\n" if $verbose; } elsif (! -r $cv{$svc,$s_actualsrcf}) { warn "$O: $cv{$svc,$s_actualsrcf}: $!\n"; } else { print " File $cv{$svc,$s_actualdstf} created\n" if $verbose; if ($cv{$svc,$s_chgrpskelB}) { system(@install, '-m', $cv{$s_filemode}, '-o', $uid, '-g', $cv{$svc,$s_svcgid}, $cv{$svc,$s_actualsrcf}, $cv{$svc,$s_actualdstf}) if ! $dryrun; } else { system(@install, '-m', $cv{$s_filemode}, '-o', $uid, '-g', $gid, $cv{$svc,$s_actualsrcf}, $cv{$svc,$s_actualdstf}) if ! $dryrun; } } } } ######################################################################### # End of program exit(0); ######################################################################### # Check that the configuration variable contains is a valid boolean value sub chkbool($$$$) { my $svc = $_[0]; # Service name my $var = $_[1]; # Partial hash key of variable to check my $new = $_[2]; # Partial hash key of new variable (true bool) my $def = $_[3]; # Default value, in case of error my $val = $cv{$svc,$var}; if (grep($_ eq $val, @s_true)) { $cv{$svc,$new} = 1; } elsif (grep($_ eq $val, @s_false)) { $cv{$svc,$new} = 0; } else { warn "$O: Illegal value \"$val\" at $conffile:$cl{$var}\n"; warn "$O: Variable \"$var\[$svc\]\" set to \"$def\"\n"; $cv{$svc,$var} = $def; chkbool($svc, $var, $new, $def); } } ######################################################################### # A chown() that works with symbolic links sub lchown(@) { # The chown() function does NOT change the ownership of symbolic links # under Linux 2.1.81 or later. Hence, make an external call to the # chown(1) program. This program MUST support the "-h" parameter. my $t_uid = shift; my $t_gid = shift; system(@chown, '-h', "$t_uid:$t_gid", @_); } ######################################################################### # Display usage information sub showusage() { pod2usage(-message => $copyright, -exitval => 0); } ######################################################################### # Display program version information sub showversion() { print "$copyright\n"; print <<"DATAEND" This program is free software that is distributed under the GNU General Public License, version 2 or later. See /usr/share/common-licenses/GPL for more information. DATAEND ; exit(0); } ######################################################################### # Show an error message relating to the command-line and terminate sub showcmdlerr(@) { map { warn "$_\n" } @_; die "Try `$O --help' for more information.\n"; } __END__ ######################################################################### # Program documentation in POD format =head1 NAME adduser.local - adduser(8) local system additions =head1 SYNOPSIS /usr/local/sbin/adduser.local [B<--dry-run>] [B<--conf> FILE] [B<--quiet>] [B<--verbose>] [B<--help>] [B<--version>] USERNAME [UID GID HOMEDIR] =head1 DESCRIPTION The B<adduser.local> script, once installed as F</usr/local/sbin/adduser.local>, is automatically called by the adduser(8) system program on a Debian system. This script completes the creation of a user account by parsing a system-dependent configuration file, F</etc/adduser.local.conf>. That configuration file lists a number of "services" to be configured, where each service is simply a convenient name for directories that must be created, Unix groups to which the user must be added, files that need to be copied, symbolic links to be created and so on. This script is automatically called by adduser(8) with arguments I<USERNAME UID GID HOMEDIR>. In addition, this script may be called manually. In this case, only I<USERNAME> needs to be passed, along with options as described later in B<OPTIONS>. Note that adduser(8) can now perform I<some> of the tasks that B<adduser.local> does, particularly by using the EXTRA_GROUPS and ADD_EXTRA_GROUPS variables in F</etc/adduser.conf>. However, B<adduser.local> is far more flexible than doing just that... =head1 OPTIONS =over 4 =item B<-n>, B<--dry-run> Pretend to fulfil everything required, without actually doing anything. =item B<-c>, B<--conf> I<FILE> Use configuration file I<FILE> instead of the default F</etc/adduser.local.conf>. =item B<-q>, B<--quiet> Don't show extraneous output. =item B<-v>, B<--verbose> Show output about what was done (default). =item B<-h>, B<--help> Show a brief command-line summary. =item B<-V>, B<--version> Show the version of the B<adduser.local> script. =back =head1 RETURN VALUE B<adduser.local> returns a successful (zero) exit status if no severe errors were detected, otherwise a non-zero exit code is returned. =head1 EXAMPLES To add the user "john" to your system: adduser john This automatically calls B<adduser.local> with the appropriate arguments. If you would like to rerun the B<adduser.local> script (such as after modifying its configuration file) for the user "john": adduser.local john =head1 FILES =over 4 =item F</etc/adduser.local.conf> Configuration for B<adduser.local>. The default configuration is extensively documented. =back =head1 FEEDBACK Your comments, suggestions, corrections and enhancements are always warmly welcomed! Please send these to: Postal: John Zaitseff, The ZAP Group, Unit 6, 116 Woodburn Road, Berala, NSW, 2141, Australia E-mail: J.Zaitseff@zap.org.au Web: http://www.zap.org.au/software/utils/adduser.local/ FTP: ftp://ftp.zap.org.au/pub/utils/adduser.local/adduser.local.tar.gz =head1 COPYRIGHT Copyright (C) 1999-2013, John Zaitseff. This program is free software; you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =head1 SEE ALSO adduser(8) =cut examples/adduser.local.conf.examples/profile 0000644 00000004316 00000000000 0015173 0 ustar 00 ######################################################################### # /etc/profile: System-wide initialisation file for Bourne shells # ######################################################################### # [JNZ] Modified 21-Jun-2013 # This script file is sourced by Bourne-compatible shells, such as sh(1), # bash(1), ksh(1) and ash(1), when those shells are run as a login shell. # # When a login shell starts, the following script files are sourced, in # this order: # # /etc/profile - this file # /etc/profile.d/*.sh - additional profile scripts # /etc/bash.bashrc - sourced by this file (only for bash(1)) # $HOME/.bash_profile - run by bash(1) # $HOME/.bashrc - sourced by the default $HOME/.bash_profile # # When a normal (non-login) bash(1) shell starts, the following files are # sourced: # # /etc/bash.bashrc - run by bash(1) # $HOME/.bashrc - run by bash(1) # # Written by John Zaitseff and released into the public domain. umask 022 # Set the default executable path, as ENV_PATH and ENV_SUPATH in # /etc/login.defs does not always seem to be consulted if [ "`id -u`" -eq 0 ]; then PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" else PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games" fi # Augment various paths as required if [ -d $HOME/bin ]; then PATH=$HOME/bin:$PATH fi if [ -d $HOME/man ]; then MANPATH=$HOME/man:$MANPATH export MANPATH fi if [ -d $HOME/lib/man ]; then MANPATH=$HOME/lib/man:$MANPATH export MANPATH fi export PATH # Set the default prompt for interactive shells if [ "$PS1" ]; then if [ "$BASH" ] && [ "$BASH" != "/bin/sh" ]; then PS1="\u@\h:\w \\\$ " else if [ "`id -u`" -eq 0 ]; then PS1='# ' else PS1='$ ' fi fi fi # Source all *.sh scripts in /etc/profile.d if [ -d /etc/profile.d ]; then for i in /etc/profile.d/*.sh; do if [ -r "$i" ]; then . "$i" fi done unset i fi # If this is a bash(1) shell, source an additional initialisation script. # This may override variables, functions and aliases set above. if [ "$BASH" ] && [ "$BASH" != "/bin/sh" ]; then if [ -r /etc/bash.bashrc ]; then . /etc/bash.bashrc fi fi examples/adduser.local.conf.examples/bash.bashrc 0000644 00000005332 00000000000 0015710 0 ustar 00 ######################################################################### # /etc/bash.bashrc: System-wide initialisation file for Bash # ######################################################################### # [JNZ] Modified 21-Jun-2013 # This script file is sourced by bash(1) for interactive shells. It is # also sourced by /etc/profile for (possibly non-interactive) login # shells. # # Written by John Zaitseff and released into the public domain. # Useful shell settings shopt -s checkwinsize expand_aliases set -P # Useful variable settings export LANG=en_AU.UTF-8 # We are in Australia export LC_ALL=en_AU.UTF-8 export TIME_STYLE=$'+%b %e %Y\n%b %e %H:%M' # As used by ls(1) # Useful aliases, defined whether or not this shell is interactive alias cls=clear alias ls="ls -v" alias ll="ls -l" alias l.="ls -A" alias dir="ls -laF" alias e="emacs -nw" alias lo=libreoffice # Set a variable identifying any Debian Chroot Compilation Environment if [ -z "$debian_chroot" -a -r /etc/debian_chroot ]; then export debian_chroot=$(cat /etc/debian_chroot) fi # Run the following only if this shell is interactive if [ "$PS1" ]; then export HISTIGNORE="&: +.*" # Forget commands starting with space unset HISTFILE # Don't save commands to history file export LESSHISTFILE=- # Don't save history for less(1) export PROMPT_DIRTRIM=2 # Trailing directory components to keep # Make less(1) more friendly for non-text input files if [ -x /usr/bin/lesspipe ]; then eval $(/usr/bin/lesspipe) fi # Allow the Debian Chroot Compilation Environment to modify the prompt if [ -z "$debian_chroot" ]; then PS1h="\h" else PS1h="($debian_chroot)" fi # Set options depending on terminal type if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then # The terminal supports colour: assume it complies with ECMA-48 # (ISO/IEC-6429). This is almost always the case... # Make ls(1) use colour in its listings if [ -x /usr/bin/dircolors ]; then alias ls="ls -v --color=auto" eval $(/usr/bin/dircolors --sh) fi # Set the terminal prompt if [ $(id -u) -ne 0 ]; then PS1="\[\e[42;30m\]\u@$PS1h\[\e[37m\]:\[\e[30m\]\w\[\e[0m\] \\\$ " else # Root user gets a nice RED prompt! PS1="\[\e[41;37;1m\]\u@$PS1h\[\e[30m\]:\[\e[37m\]\w\[\e[0m\] \\\$ " fi else # The terminal does not support colour PS1="\u@$PS1h:\w \\\$ " fi # Allow bash(1) completion in interactive shells if ! shopt -oq posix; then if [ -f /usr/share/bash-completion/bash_completion ]; then . /usr/share/bash-completion/bash_completion elif [ -f /etc/bash_completion ]; then . /etc/bash_completion fi fi unset PS1h fi examples/adduser.local.conf.examples/skel.other/index.html 0000644 00000001403 00000000000 0017655 0 ustar 00 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <!-- This document is a place-holder for the home page until the user gets around to replacing it with his or her own. [JNZ] Modified 21-Jun-2013 Written by John Zaitseff and released into the public domain. --> <head> <title>My Home Page</title> <meta charset="UTF-8" /> <meta name="author" content="The user"> <meta name="description" content="The user's default home page"> <meta name="keywords" content="home, homepage, default"> </head> <body> <h1>My Home Page</h1> <p>Welcome to my home page on the World Wide Web! This page, like so many others, is under construction. I’ll be replacing this page soon… I hope.</p> </body> </html> examples/adduser.local.conf.examples/adduser.conf 0000644 00000006105 00000000000 0016104 0 ustar 00 # /etc/adduser.conf: `adduser' configuration. # See adduser(8) and adduser.conf(5) for full documentation. # [JNZ] Modified 21-Jun-2013 # Modified from the version shipped with adduser(8) by John Zaitseff. # These modifications are released into the public domain. # The DSHELL variable specifies the default login shell on your # system. DSHELL=/bin/bash # The DHOME variable specifies the directory containing users' home # directories. DHOME=/home # If GROUPHOMES is "yes", then the home directories will be created as # /home/groupname/user. GROUPHOMES=no # If LETTERHOMES is "yes", then the created home directories will have # an extra directory - the first letter of the user name. For example: # /home/u/user. LETTERHOMES=no # The SKEL variable specifies the directory containing "skeletal" user # files; in other words, files such as a sample .profile that will be # copied to the new user's home directory when it is created. SKEL=/etc/skel # FIRST_SYSTEM_[GU]ID to LAST_SYSTEM_[GU]ID inclusive is the range for UIDs # for dynamically allocated administrative and system accounts/groups. # Please note that system software, such as the users allocated by the # base-passwd package, may assume that UIDs less than 100 are unallocated. FIRST_SYSTEM_UID=100 LAST_SYSTEM_UID=999 FIRST_SYSTEM_GID=100 LAST_SYSTEM_GID=999 # FIRST_[GU]ID to LAST_[GU]ID inclusive is the range of UIDs of dynamically # allocated user accounts/groups. FIRST_UID=1000 LAST_UID=59999 FIRST_GID=1000 LAST_GID=59999 # The USERGROUPS variable can be either "yes" or "no". If "yes" each # created user will be given their own group to use as a default. If # "no", each created user will be placed in the group whose gid is # USERS_GID (see below). USERGROUPS=yes # If USERGROUPS is "no", then USERS_GID should be the GID of the group # `users' (or the equivalent group) on your system. USERS_GID=100 # If DIR_MODE is set, directories will be created with the specified # mode. Otherwise the default mode 0755 will be used. DIR_MODE=0755 # If SETGID_HOME is "yes" home directories for users with their own # group the setgid bit will be set. This was the default for # versions << 3.13 of adduser. Because it has some bad side effects we # no longer do this per default. If you want it nevertheless you can # still set it here. SETGID_HOME=no # If QUOTAUSER is set, a default quota will be set from that user with # `edquota -p QUOTAUSER newuser' QUOTAUSER="" # If SKEL_IGNORE_REGEX is set, adduser will ignore files matching this # regular expression when creating a new home directory SKEL_IGNORE_REGEX="dpkg-(old|new|dist|save)" # Set this if you want the --add_extra_groups option to adduser to add # new users to other groups. # This is the list of groups that new non-system users will be added to # Default: #EXTRA_GROUPS="dialout cdrom floppy audio video plugdev users" # If ADD_EXTRA_GROUPS is set to something non-zero, the EXTRA_GROUPS # option above will be default behavior for adding new, non-system users #ADD_EXTRA_GROUPS=1 # check user and group names also against this regular expression. #NAME_REGEX="^[a-z][-a-z0-9_]*\$" examples/adduser.local.conf.examples/skel/dot.bashrc 0000644 00000001442 00000000000 0016515 0 ustar 00 ######################################################################### # ~/.bashrc: Personal initialisation script for Bash # ######################################################################### # [JNZ] Modified 21-Jun-2013 # This script file is sourced by interactive Bash shells (ie, shells for # which you are able to provide keyboard input). It is also sourced by # ~/.bash_profile for login shells. It is the best place to put shell # variables, functions and aliases. # # Written by John Zaitseff and released into the public domain. # Variable settings for your convenience export EDITOR=emacs # Everyone's favourite editor # Run the following only if this shell is interactive if [ "$PS1" ]; then export IGNOREEOF=5 # Disallow accidental Ctrl-D fi examples/adduser.local.conf.examples/skel/dot.bash_logout 0000644 00000000653 00000000000 0017564 0 ustar 00 ######################################################################### # ~/.bash_logout: Personal log-out script for Bash # ######################################################################### # [JNZ] Modified 21-Jun-2013 # This script file is sourced by bash(1) when the login shell terminates. # By default, no action is taken. # # Written by John Zaitseff and released into the public domain. examples/adduser.local.conf.examples/skel/dot.bash_profile 0000644 00000002412 00000000000 0017706 0 ustar 00 ######################################################################### # ~/.bash_profile: Personal initialisation script for Bash # ######################################################################### # [JNZ] Modified 21-Jun-2013 # This script file is sourced by bash(1) for login shells. # # When a login shell starts, the following script files are sourced, in # this order: # # /etc/profile - run by bash(1) # /etc/profile.d/*.sh - additional profile scripts # /etc/bash.bashrc - sourced by /etc/profile file (only for bash(1)) # $HOME/.bash_profile - this file # $HOME/.bashrc - sourced by this file (if unchanged) # # When a normal (non-login) bash(1) shell starts, the following files are # sourced: # # /etc/bash.bashrc - run by bash(1) # $HOME/.bashrc - run by bash(1) # # Written by John Zaitseff and released into the public domain. if [ -f $HOME/.bashrc ]; then . $HOME/.bashrc fi # Display a verse from the Bible if [ ! -f $HOME/.hushlogin ] && [ ! -f $HOME/.hushverse ]; then if [ $(type -p verse) ]; then echo verse echo fi fi # Turn on talk(1) messages, unless the user does not want this if [ ! -f $HOME/.hushlogin ] && [ ! -f $HOME/.hushtalk ]; then mesg y 2>/dev/null fi examples/INSTALL 0000644 00000001117 00000000000 0007354 0 ustar 00 Please read the README file for detailed information about what needs to be done to install the ADDUSER Local System Additions program. In brief: # cp --pr=t ./adduser.local /usr/local/sbin # chmod 755 /usr/local/sbin/adduser.local # cp ./adduser.local.conf /etc # editor /etc/adduser.local.conf # The important step! # mkdir /etc/skel.other # cp --pr=t ./adduser.local.conf.examples/skel.other/index.html /etc/skel.other IMPORTANT: Do NOT just perform the above steps without knowing what you are doing! In particular, the fourth step is very important for you to do correctly. examples/README.gz 0000644 00000004322 00000000000 0007623 0 ustar 00 � �Xmo�����b�~�PR���&��:��5��H��XR+�1��q�Vt����]J�-�E�!���y}f��x�>����~Q�>|�:���m�+�Z;oj:��K_������ۧ�:��+������t�����˿�J韶h�ߺ��,O��u�Dž]ݘ*��!o�����vv���uQn��d�ܨ�q^W���v4�]7�X��ee3��y�L7��R���n� ��79˧U�6C}0Y�������Z7s��|�����yg�ܴZ۾#>N:�m�x7U�ؑ&���lJ�;�m��yҝ�U����a����͒*e��/�f�4��&�*p<j�1o̊��A����W�]�me�o)��(��(�}����1�1�ܙ������9�Y����!�S�yٙ��I�G@����q�`[8^b*[ �Z�\�Rs@�E�[S�`&e37ߧ����e�{e[Z8�i��� [N�d��D�JV�UB���~:� @�p�h�w �:�m��إ�[ݬ�] P�>S�zv�DI���������y�(��`B!�_Sm��b �*:�w�ꈆ�8��)͌�w�;�c�� ���A��%���G�k��־1 ��2g�)O��V��^X�攆��4$|���u�٪� xmnc�9��t�3n(p �ZO���X���B�/��zƚ�����j�A䞧��J\l���#���ov3�.��A嫡�wC��{X�L�X ����B��bUU�r=vŖ�B;9���m�=���L&���DmD��Ҕ!����0�z|���S�ŘԱw����Ki� y��)3h11G�}�ߠ YYu���ҿ4]E1����9|�坊 �I[ړ��~H�&��;��6�Y�A�W�T (�yX#AX,�мF#X<����-�a;HFE�|LM_g��;����F�+g1���j脔<�m��Kh�='� �Ds��L� (g8�0��bB;�6WBd��~��,lUٕLƂ��qj�H��j�fH@M���4����S� �;����O����$��j%O�G��#�]o�QH �{G�$AA��������pBW�o��.� ��ݹ<֘ˮ==�o���un�Fo������Ɉ &��q�FgFSl����3Q8�T�͖n� ��r�J��l�o zI�����.�� �.3L =W�'��+ \$rG Lf�Ì� u֣ڥވB�NW}}��;�v� �]{�i��x�:\�oc��;����N��M�s=x�� �ҽ�Y���ϡ�@�����@W�`B�'��:9`R�A���d��b���f0r%�|Q �Z�����㑮��a��ů�lL�D�mK�ͼ�,���?w]�iem���� ��#I� \X�C C�]\]��_�}�PG��a�"���ƃ^h�[s��&��XS���O��ZXNe� r��T��8f�z�������<Mw���ct� 食��D��#�M�'Y7�C�H*�jJ�L�S�.kJ��Klq�vݯK��5&K#Dj�-�퀑č�锗7< aQ{l�� ��dDF>���J�FTyK)��6�t�-��m�J�G��P�S�}ܮ�'@Fݫ�'�g���b՛���7�b��j���d6!���K��{����pI��4v؇�<[�B5ѫTb)�g;����:?;9��:U���d?^��?M���({���m�����.��O��/�����4 GQ��݈m�?��oJ��$ލ�� �Fwn��ɓϗ_�.>aZt��,6� ���˦�Gq�sM��HҌ)�m&Q����9�uzg��4N�q�� ����론��3�\ P�.��e:�:��mgd�|@u!�{��VxF��<+gSYB5�SkyW��dAGcd�ɶN���Q�>A��Ln^���]�Ԙ��DͯP��@��D��� u(s��`SX�CL�V�D6;���N�g�D��?��~x|�/��]_9>���;�ri��)�v`>��6���!G��klS����Z��b�gD��d8�/o�s.-�2���7�ھ��� �?�tx�� �Y�5��g{�=��S���I�������c�6�R3P'XP8���7�N���W7&{n�f̺���3g~�e�{��7S�/��\ƿ��H��{�P�.S�uk3 changelog.gz 0000644 00000003510 00000000000 0006775 0 ustar 00 � �X�n�8}�W\t��q�R��8�8^:�ę �,=�A��X%�)R �r*_?����7�gb %q��9�^�sg���Q�$s�p�v�=*����&gV\e��%υ+wv�~�3��SeĚ՜ ]rʅ�Y�����9Y��)=�g��� ��t���狿����g������֘eZ-�5'S+�β�U-�"��~՚I��,���ua���8>�ռ�$V=<���n ����7+gxNk�����F��oip�se1��Bb�Qm��t� �K��.�1lCGQ^eLi%2&��'��|��)�c���0�d���)Nz�ģxg�=����7]0�9KW��BYD�?�FWd]UiS�R�_j����}RY]3!n6���NE)j#�.��^�����j�j]�*�>%1��̻{�&�o^�{ܥ�s�8ϟ�hU1ko��e}���q�s2~��NGR'�}#�u~� �i��7�!��d��lX���Y�R,��ج��Kͤ�yDo�-�=�V陇)>��4�%K$��i��!%-��c���Fԅv ���~JgL# �c8�5!Ǚ�f`���cK�j:��X�"�}xGS:�L��d(!�oe"�C�l�o3!�M���7 �O�B�p����>� %Ԋ��"}B��CNE������D�;"MR��r�M��[h��+V"�zIH"�,��F�[eʵ�^� ��v1He�L'wD�/�>:��쟟/��:�赌�EVj��YA����S��=R�v3[�L,�G��6��)���m�MefS���2ы.*iX=�ka�5a��ܑ�x�����A����s}� fr���:�O�uvЧ#��. �O��ە ]m�L�� ٯomq58B?���l�l-��k��z� ��L��9�bq�-?�SK���!��A_�ۂ��Η�G�5_{���tMG�?�6��]֜�J�/���@�8I�C����I�c�8*+�Yި���s&W�aډV�?5(7(�m�l��f�k�q��/0�]S�떆�㦖��V�Z_ T��ITv�����|�a�M�]Q�?:^k�Sk}��W�cyCm�W-\�f{u��s�&�f��O(��h%Q��.!'1��_!�]Ej������Tn �������p�N+�w��eYʁ�;;��D�� g��v�w�W��z���㿹�a�&$i�^��_5���Y{���bO�3V{s��b������z�δ-E�]��K���d_��&c�6�a���$mfi�d�o��4�W��<Uj��P�.ھ6�/� ��N�K�-�P��jᓅ������L��%��\m]�~h�C/3<\-|[�cjO�h�<QeުL�k��w"� -�O�ծ���ŋA( �5t^}u+h.�弯�î�B�f�9���L$d���B���w��c�:�dP�i-2���L:�<�(o����5��^D�k?�N�ݙ ��MZ|���}7^T^��p�OV���.dK��V���rĞ���dF�<��R�-k��O+��د��J�@�x���.��t���}��<���T��+U���2>����Y�r��Z����&�xBƹ�O6�^��m=��������Tb�R1��L�� ���y A����҇�[#b��>���8n��әzQ��\��ۏ5��P@�q@����<p�PJ�W�+.�%����Q�5�\�ѝ��Ƽ�:�8J~_�;�c�j`�L�x���]����a�������"���UX���o��|��ؘ,�
| ver. 1.4 |
Github
|
.
| PHP 8.3.30 | Generation time: 0.95 |
proxy
|
phpinfo
|
Settings