__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ V /  | |__) | __ ___   ____ _| |_ ___  | (___ | |__   ___| | |
 | |\/| | '__|> <   |  ___/ '__| \ \ / / _` | __/ _ \  \___ \| '_ \ / _ \ | |
 | |  | | |_ / . \  | |   | |  | |\ V / (_| | ||  __/  ____) | | | |  __/ | |
 |_|  |_|_(_)_/ \_\ |_|   |_|  |_| \_/ \__,_|\__\___| |_____/|_| |_|\___V 2.1
 if you need WebShell for Seo everyday contact me on Telegram
 Telegram Address : @jackleet
        
        
For_More_Tools: Telegram: @jackleet | Bulk Smtp support mail sender | Business Mail Collector | Mail Bouncer All Mail | Bulk Office Mail Validator | Html Letter private



Upload:

Command:

[email protected]: ~ $
#! /usr/bin/perl

# Copyright (C) 2000-2004 Roland Bauerschmidt <[email protected]>
#               2005-2023 Marc Haber <[email protected]>
#               2022 Benjamin Drung <[email protected]>
#               2023 Guillem Jover <[email protected]>
#               2021-2022 Jason Franklin <[email protected]>
#               2022 Matt Barry <[email protected]>
#               2016-2017 Afif Elghraoui <[email protected]>
#               2016 Dr. Helge Kreutzmann <[email protected]>
#               2005-2009 Joerg Hoh <[email protected]>
#               2006-2011 Stephen Gran <[email protected]>
#
# Original adduser:
# Copyright (C) 1997-1999 Guy Maor <[email protected]>
#
# Copyright (C) 1995 Ted Hajek <[email protected]>
#                    Ian A. Murdock <[email protected]>
#
# The general scheme of this program was adapted from the original
# Debian "adduser" program by Ian A. Murdock <[email protected]>.
#
# License: GPL-2+

use 5.32.0;
use strict;
use warnings;

use Getopt::Long;

use Debian::AdduserCommon 3.136;
use Debian::AdduserLogging 3.136;
use Debian::AdduserRetvalues 3.136;
BEGIN {
    if ( Debian::AdduserCommon->VERSION != version->declare('3.136') ||
         Debian::AdduserLogging->VERSION != version->declare('3.136') ||
         Debian::AdduserRetvalues->VERSION != version->declare('3.136') ) {
           die "wrong module version in adduser, check your packaging or path";
    }
}


my $version = "3.137ubuntu1";

BEGIN {
    local $ENV{PERL_DL_NONLAZY}=1;
    # we need to disable perlcritic here since this construct
    # needs experession form eval, see perl cookbook 12.2.3
    ## no critic
    eval "
        require POSIX;
        import POSIX qw(setlocale);
    ";
    ## use critic
    if ($@) {
        *setlocale = sub { return 1 };
    } else {
        setlocale( &POSIX::LC_MESSAGES, "" );
    }
    ## no critic
    eval "
        require I18N::Langinfo;
        import I18N::Langinfo qw(langinfo YESEXPR NOEXPR);
    ";
    ## use critic
    if ($@) {
        *langinfo = sub { return shift; };
        *YESEXPR  = sub { "^[yY]" };
        *NOEXPR   = sub { "^[nN]" };
    }
}

my $yesexpr = langinfo(YESEXPR());

my %config;			# configuration hash

my $nogroup_id = getgrnam("nogroup") || 65534;
$0 =~ s+.*/++;

our $verbose = 1;		# should we be verbose?
my $name_check_level = 0;		# should we allow bad names?
our $stderrmsglevel = "warn";
our $stdoutmsglevel = "info";
our $logmsglevel = "info";
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 @configfiles;
our @defaults = undef;
our $encrypt_home = undef;
our $found_group_opt = undef;
our $found_sys_opt = undef;
our $ingroup_name = undef;
our $new_firstgid = undef;
our $new_firstuid = undef;
our $new_comment = undef;
our $gid_option = undef;
our $primary_gid = undef;
our $new_lastgid = undef;
our $new_lastuid = undef;
our $new_uid = undef;
our $no_create_home = undef;
our $special_home = undef;
our $special_shell = undef;
our $use_extrausers = 0;
our $add_extra_groups;
our $add_extra_groups_old;

# 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 $first_gid = undef;
my $last_gid = undef;
my $dir_mode = undef;
my $perm = undef;
my %uid_pool;
my %gid_pool;

our @names;

GetOptions(
    'add-extra-groups' => \$add_extra_groups,
    'add_extra_groups' => \$add_extra_groups_old,
    'allow-all-names' => sub { $name_check_level = 2 },
    'allow-badname' => sub { $name_check_level = 1 unless $name_check_level },
    'allow-bad-names' => sub { $name_check_level = 1 unless $name_check_level },
    'comment=s' => \$new_comment,
    'conf|c=s' => \@configfiles,
    'debug' => sub { $verbose = 2 },
    'stdoutmsglevel=s' => \$stdoutmsglevel,
    'stderrmsglevel=s' => \$stderrmsglevel,
    'logmsglevel=s' => \$logmsglevel,
    'disabled-login' => sub { $disabled_login = 1; $ask_passwd = 0 },
    'disabled-password' => sub { $ask_passwd = 0 },
    'encrypt-home' => \$encrypt_home,
    'extrausers' => \$use_extrausers,
    'firstgid=i' => \$new_firstgid,
    'firstuid=i' => \$new_firstuid,
    'force-badname' => sub { $name_check_level = 1 unless $name_check_level },
    'gecos=s' => \$new_comment,
    'gid=i' => \$gid_option,
    'group' => \$found_group_opt,
    'help|h' => sub { &usage; exit },
    'home=s' => \$special_home,
    'ingroup=s' => \$ingroup_name,
    'lastgid=i' => \$new_lastgid,
    'lastuid=i' => \$new_lastuid,
    'no-create-home' => \$no_create_home,
    'quiet|q' => sub { $verbose = 0 },
    'shell=s' => \$special_shell,
    'system' => \$found_sys_opt,
    'uid=i' => \$new_uid,
    'verbose' => sub { $verbose = 1 },
    'version|v' => sub { &version; exit },
) or &usage_error;

if(!@configfiles) {
    @defaults = ("/etc/adduser.conf");
} else {
    @defaults = (@configfiles);
}

# preseed configuration data and then read the config file
preseed_config(\@defaults,\%config);

# everyone can issue "--help" and "--version", but only root can go on
if( $> != 0) {
    log_fatal( mtx("Only root may add a user or group to the system.") );
    exit( RET_ROOT_NEEDED );
}

# TODO: Handle configuration file input, allow bare input there.
if( $verbose == 0 ) {
    set_msglevel( $stderrmsglevel, "warn", $logmsglevel );
} elsif( $verbose == 1 ) {
    set_msglevel( $stderrmsglevel, "info", $logmsglevel );
} elsif( $verbose == 2 ) {
    set_msglevel( $stderrmsglevel, "debug", $logmsglevel );
} else {
    set_msglevel( $stderrmsglevel, $stdoutmsglevel, $logmsglevel );
}

# 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) {
    log_fatal( mtx("Only one or two names allowed.") );
    exit( RET_INVALID_CALL );
}


if (@names == 2) {	# must be addusertogroup
    if ($action eq "addsysuser" || $found_group_opt) {
        log_fatal( mtx("Specify only one name in this mode.") );
        exit( RET_INVALID_CALL );
    }
    if ($action eq "addgroup" or $action eq "addsysgroup") {
        log_fatal( mtx("addgroup with two arguments is an unspecified operation.") );
        exit( RET_INVALID_CALL );
    }
    $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 (!defined $add_extra_groups) {
    if( defined $add_extra_groups_old ) {
        $add_extra_groups = $add_extra_groups_old;
    } else {
        $add_extra_groups = 0;
    }
}

if ($action ne "addgroup" &&
    defined($found_group_opt) +defined($ingroup_name) +defined($gid_option) > 1 ) {
    log_fatal( mtx("The --group, --ingroup, and --gid options are mutually exclusive.") );
    exit( RET_EXCLUSIVE_PARAMETERS );
}


if ((defined($special_home)) && ($special_home !~ m+^/+ )) {
  log_fatal( mtx("The home dir must be an absolute path.") );
  exit( RET_INVALID_HOME_DIRECTORY );
}

if (defined($special_home)) {
    if (!defined($no_create_home) && -d $special_home) {
        log_info( mtx("The home dir %s you specified already exists.\n"), $special_home );
    }
    if (defined($no_create_home) && ! -d $special_home) {
        log_info( mtx("The home dir %s you specified can't be accessed: %s\n"), $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');
}


# TODO: map this to log levels
$ENV{"VERBOSE"} = $verbose;
$ENV{"DEBUG"}   = $verbose;

# preseed_config was moved from here

# read the uid and gid pool
if ($config{"uid_pool"}) {
    read_pool ($config{"uid_pool"}, "uid", \%uid_pool);
}
if ($config{"gid_pool"}) {
    read_pool ($config{"gid_pool"}, "gid", \%gid_pool);
}

&checkname($new_name) 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 | $gid_option - the group to add the user to
#    $special_home, $new_uid, $new_comment - optional overrides
# $action = "addgroup"
#    $new_name                - the name of the new group
#    $gid_option                 - optional override
# $action = "addsysgroup"
#    $new_name                - the name of the new group
#    $gid_option                 - optional override
# $action = "addsysuser"
#    $new_name                - the name of the new user
#    $make_group_also | $ingroup_name | $gid_option | 0  - which group
#    $special_home, $new_uid, $new_comment - optional overrides
# $action = "addusertogroup"
#    $existing_user           - the user to be added
#    $existing_group          - the group to add her to
#####


#################
## addsysgroup ##
#################
if ($action eq "addsysgroup") {

    acquire_lock();
    # Check if requested group already exists and we can exit safely
    my $ret = existing_group_ok($new_name, $gid_option);

    if ($ret == 3) {
        log_info( mtx("The group `%s' already exists as a system group. Exiting."), $new_name );
        exit( RET_OK );
    }

    if ($ret == 1) {
        log_err( mtx("The group `%s' already exists and is not a system group. Exiting."), $new_name );
        exit( RET_WRONG_OBJECT_PROPERTIES );
    }

    if ($ret == 2) {
        log_err( mtx("The group `%s' already exists, but has a different GID. Exiting."), $new_name );
        exit( RET_WRONG_OBJECT_PROPERTIES );
    }

    if (defined($gid_option) && defined(getgrgid($gid_option))) {
        log_fatal( mtx("The GID `%s' is already in use."), $gid_option );
        exit( RET_ID_IN_USE );
    }

    if (!defined($gid_option)) {
        $first_gid = $new_firstgid || $config{"first_system_gid"};
        $last_gid = $new_lastgid || $config{"last_system_gid"};
        $gid_option = &first_avail_gid($first_gid,
                           $last_gid,
                           $gid_pool{$new_name}{'id'});
        if ($gid_option == -1) {
            log_warn( mtx("No GID is available in the range %d-%d (FIRST_SYS_GID - LAST_SYS_GID)."),
                $first_gid, $last_gid );
            log_err( mtx("The group `%s' was not created."), $new_name );
            exit( RET_NO_ID_IN_RANGE );
        }
    }


    log_info( mtx("Adding group `%s' (GID %d) ..."), $new_name, $gid_option);
    my $groupadd = &which('groupadd');
    if ( ($use_extrausers) || ($config{"use_extrausers"}) ) {
        &systemcall($groupadd, '--extrausers', '-g', $gid_option, $new_name);
    } else {
        &systemcall($groupadd, '-g', $gid_option, $new_name);
    }
    release_lock(0);
    exit( RET_OK );
}


##############
## addgroup ##
##############
if ($action eq "addgroup") {
    acquire_lock();
    if (defined getgrnam($new_name)) {
        log_fatal( mtx("The group `%s' already exists."), $new_name);
        exit( RET_OBJECT_EXISTS );
    }
    if (defined($gid_option) && defined(getgrgid($gid_option))) {
        log_fatal( mtx("The GID `%s' is already in use."), $gid_option );
        exit( RET_ID_IN_USE );
    }
    if (!defined($gid_option)) {
        $first_gid = $new_firstgid || $config{"first_gid"};
        $last_gid = $new_lastgid || $config{"last_gid"};
        log_debug( "Searching for gid with first_gid=%s, last_gid=%s, new_name=%s, gid_pool=%s",
                $first_gid,
                $last_gid,
                $new_name,
                $gid_pool{$new_name}{'id'}
            );
        $gid_option = &first_avail_gid($first_gid,
                           $last_gid,
                           $gid_pool{$new_name}{'id'});

        if ($gid_option == -1) {
            log_warn( mtx("No GID is available in the range %d-%d (FIRST_GID - LAST_GID)."),
                $first_gid, $last_gid );
            log_fatal( mtx("The group `%s' was not created."), $new_name );
            exit( RET_NO_ID_IN_RANGE );
        }
    }

    log_info( mtx("Adding group `%s' (GID %d) ..."), $new_name, $gid_option);
    my $groupadd = &which('groupadd');
    if ( ($use_extrausers) || ($config{"use_extrausers"}) ) {
        &systemcall($groupadd, '--extrausers', '-g', $gid_option, $new_name);
    } else {
        &systemcall($groupadd, '-g', $gid_option, $new_name);
    }
    release_lock(0);
    exit( RET_OK );
}


####################
## addusertogroup ##
####################
if ($action eq 'addusertogroup') {

    if (!defined getpwnam($existing_user)) {
        log_fatal( mtx("The user `%s' does not exist."), $existing_user );
        exit( RET_OBJECT_DOES_NOT_EXIST );
    }
    if (!defined getgrnam($existing_group)) {
        log_fatal( mtx("The group `%s' does not exist."), $existing_group );
        exit( RET_OBJECT_DOES_NOT_EXIST );
    }

    if (&user_is_member($existing_user, $existing_group)) {
        log_info( mtx("The user `%s' is already a member of `%s'."), $existing_user, $existing_group );
        exit( RET_OK );
    }

    log_info( mtx("Adding user `%s' to group `%s' ..."), $existing_user, $existing_group );

    acquire_lock();
    if ( ($use_extrausers) || ($config{"use_extrausers"}) ) {
        &systemcall('/usr/sbin/usermod', '--extrausers', '-a', '-G', $existing_group, $existing_user);
    } else {
        &systemcall('/usr/sbin/usermod', '-a', '-G', $existing_group, $existing_user);
    }
    release_lock();

    exit( RET_OK );
}

################
## addsysuser ##
################
if ($action eq "addsysuser") {
    acquire_lock();
    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"})) {
            log_info( mtx("The system user `%s' already exists. Exiting.\n"), $new_name );
            exit( RET_OK );
        }
        log_fatal( mtx("The user `%s' already exists, but is not a system user. Exiting."), $new_name );
        exit( RET_WRONG_OBJECT_PROPERTIES );
    }
    if (existing_user_ok($new_name, $new_uid) == 2) {
        log_fatal( mtx("The user `%s' already exists with a different UID. Exiting."), $new_name );
        exit( RET_WRONG_OBJECT_PROPERTIES );
    }

    if (!$ingroup_name && !defined($gid_option) && !$make_group_also) {
      $gid_option = $nogroup_id;
    }
    check_user_group(1);

    if (!defined($new_uid) && $make_group_also) {
        $new_uid = &first_avail_uid($new_firstuid || $config{"first_system_uid"},
                                    $new_lastuid || $config{"last_system_uid"},
                                    $uid_pool{$new_name}{'id'});
        if ($new_uid == -1) {
            log_warn( mtx("No UID/GID pair is available in the range %d-%d (FIRST_SYS_UID - LAST_SYS_UID)."),
                $config{"first_system_uid"},
                $config{"last_system_uid"} );
            log_fatal( mtx("The user `%s' was not created."), $new_name );
            exit( RET_NO_ID_IN_RANGE );
        }
        $gid_option = &first_avail_gid($new_firstgid || $config{"first_system_gid"},
                                       $new_lastgid || $config{"last_system_gid"},
                                       $gid_pool{$new_name}{'id'});
        $ingroup_name = $new_name;
    }
    elsif (!defined($new_uid) && !$make_group_also) {
        $new_uid = &first_avail_uid($new_firstuid || $config{"first_system_uid"},
                                    $new_lastuid || $config{"last_system_uid"},
                                    $uid_pool{$new_name}{'id'});
        if ($new_uid == -1) {
            log_warn( mtx("No UID is available in the range %d-%d (FIRST_SYS_UID - LAST_SYS_UID)."),
                $config{"first_system_uid"},
                $config{"last_system_uid"} );
            log_fatal( mtx("The user `%s' was not created."), $new_name);
            exit( RET_NO_ID_IN_RANGE );
        }
        if (defined($gid_option)) {
            $ingroup_name = getgrgid($gid_option);
        } elsif ($ingroup_name) {
            $gid_option = getgrnam($ingroup_name);
        } else {
            log_fatal( mtx("Neither ingroup option nor gid given.") );
            exit( RET_NO_PRIMARY_GROUP );
        }
    }
    else {
        if (defined($gid_option)) {
            $ingroup_name = getgrgid($gid_option);
        } elsif ($ingroup_name) {
            $gid_option = getgrnam($ingroup_name);
        } elsif ($make_group_also) {
            $gid_option=$new_uid; $ingroup_name=$new_name;
        } else {
            log_fatal( mtx("Neither ingroup option nor gid given and make_group_also unset.") );
            exit( RET_NO_PRIMARY_GROUP );
        }
    }
    log_info( mtx("Adding system user `%s' (UID %d) ..."), $new_name, $new_uid );

    # if we reach this point, and the group does already exist, we can use it.
    if ($make_group_also && !getgrnam($new_name)) {
        log_info( mtx("Adding new group `%s' (GID %d) ..."), $new_name, $gid_option );
        $undogroup = $new_name;
        my $groupadd = &which('groupadd');
        if ( ($use_extrausers) || ($config{"use_extrausers"}) ) {
            &systemcall($groupadd, '--extrausers', '-g', $gid_option, $new_name);
        } else {
            &systemcall($groupadd, '-g', $gid_option, $new_name);
        }
    }

    log_info( mtx("Adding new user `%s' (UID %d) with group `%s' ..."),
        $new_name, $new_uid, $ingroup_name );
    $home_dir = $special_home || $uid_pool{$new_name}{'home'} || '/nonexistent';
    $no_create_home = $home_dir =~ /^\/+nonexistent(\/|$)/ ? 1 : $no_create_home;

    $shell = $special_shell || $uid_pool{$new_name}{'shell'} || '/usr/sbin/nologin';
    $undouser = $new_name;

    if ( ($use_extrausers) || ($config{"use_extrausers"}) ) {
        &systemcall('/usr/sbin/useradd', '--extrausers', '-r',
            '-K', sprintf('SYS_UID_MIN=%d', $config{'first_system_uid'}),
            '-K', sprintf('SYS_UID_MAX=%d', $config{'last_system_uid'}),
            '-d', $home_dir,
            '-g', $ingroup_name,
            '-s', $shell,
            '-u', $new_uid,
            $new_name);
    } else {
        &systemcall('/usr/sbin/useradd', '-r',
            '-K', sprintf('SYS_UID_MIN=%d', $config{'first_system_uid'}),
            '-K', sprintf('SYS_UID_MAX=%d', $config{'last_system_uid'}),
            '-d', $home_dir,
            '-g', $ingroup_name,
            '-s', $shell,
            '-u', $new_uid,
            $new_name);
    }

    release_lock(0);

    if (defined($new_comment)) {
        &ch_comment($new_comment);
    } elsif ($uid_pool{$new_name}{'comment'}) {
        &ch_comment($uid_pool{$new_name}{'comment'});
    }

    $primary_gid = $gid_option;
    create_homedir(0);

    exit( RET_OK );
}


#############
## adduser ##
#############
if ($action eq "adduser") {
    acquire_lock();

    $primary_gid=-1;
    my @supplemental_groups=();

    log_trace( "new_uid %s", $new_uid );
    log_trace( "ingroup_name %s, gid_option %s", $ingroup_name, $gid_option );
    log_trace( "usergroups %s", $config{"usergroups"} );
    log_trace( "users_gid %s, users_group %s", $config{"users_gid"}, $config{"users_group"} );
    log_trace( "primary_gid %s, supplemental groups %s", $primary_gid, join(", ",@supplemental_groups) );
    if( defined($config{"users_gid"}) && defined($config{"users_group"}) ) {
        log_warn ( mtx("USERS_GID and USERS_GROUP both given in configuration. This is an error.") );
        log_fatal (mtx("The user `%s' was not created."),$new_name);
        exit( RET_CONFIG_ERROR );
    }

    if ($config{"usergroups"} =~  /yes/i) {
        log_trace( "config usergroups == yes code path" );
        $make_group_also = 1;
        if( $gid_option ) {
            $make_group_also = 0;
            $primary_gid = $gid_option;
            log_trace( "gid_option defined %s, make_group_also 0, primary_gid=gid_option", $gid_option );
        }
        if( $ingroup_name ) {
            $make_group_also = 0;
            $primary_gid = getgrnam($ingroup_name);
            log_trace( "ingroup_name defined %s, make_group_also 0, primary_gid %s", $gid_option, $primary_gid );
        }
        log_trace( "make_group_also %s, primary_gid %s", $make_group_also, $primary_gid );
        if( defined( $primary_gid) && $primary_gid == -1 && $make_group_also == 0 ) {
            if (defined($config{"users_gid"}) && $config{"users_gid"} != -1) {
                my @grgid=getgrgid($config{"users_gid"});
                my $grname=$grgid[0];
                log_debug( "set primary_gid to users_gid %s %s", $config{"users_gid"}, $grname);
                $primary_gid = $config{"users_gid"};
            } elsif (defined($config{"users_gid"}) && $config{"users_gid"} == -1) {
                # nothing
            } else {
                my $primary_group="users";
                if (defined($config{"users_group"})) {
                    $primary_group=$config{"users_group"};
                }
                $primary_gid=getgrnam($primary_group);
                log_debug( "set primary_gid to users_group %s %s", $primary_gid, $primary_group);
            }
        } else {
            if (defined($config{"users_gid"}) && $config{"users_gid"} != -1) {
                my @grgid=getgrgid($config{"users_gid"});
                my $grname=$grgid[0];
                log_trace( "push users_gid %s %s to supplemental_groups", $config{"users_gid"}, $grname);
                push(@supplemental_groups, $grname);
            } elsif (defined($config{"users_gid"}) && $config{"users_gid"} == -1) {
                # nothing
            } else {
                my $supp_group="users";
                if (defined($config{"users_group"})) {
                    $supp_group=$config{"users_group"};
                }
                log_trace( "push %s to supplemental_groups", $supp_group );
                push(@supplemental_groups, $supp_group);
            }
        }
    } else {
        log_debug( "config usergroups != yes code path" );
        if( defined($ingroup_name) ) {
            $primary_gid=getgrnam($ingroup_name);
        } elsif (defined($config{"users_gid"})) {
            log_trace( "primary_gid = users_gid = %d", $primary_gid );
            $primary_gid = $config{"users_gid"};
        } else {
            if (defined($config{"users_group"})) {
                my @grgid=getgrnam($config{"users_group"});
                my $grgid=$grgid[2];
                log_trace( "primary_gid = users_group %s %s", $config{"users_group"}, $grgid);
                $primary_gid = $grgid;
            } else {
                log_trace( "primary_gid = literal 100");
                $primary_gid = 100;
            }
        }
        if( $primary_gid == -1 ) {
            log_fatal( "no primary GID for user set. User not created." );
            exit( RET_NO_PRIMARY_GID );
        }
    }
    log_debug( "primary_gid %s, supplemental groups %s", $primary_gid, join(", ",@supplemental_groups) );
    if ( defined $ingroup_name || defined $gid_option ) {
        $make_group_also = 0;
        log_trace( "set make_group_also 0, neither ingroup_name or gid_option defined" );
    }
    check_user_group(0);
    $first_uid = $new_firstuid || $config{"first_uid"};
    $last_uid = $new_lastuid || $config{"last_uid"};
    if ($config{"usergroups"} =~  /yes/i) {
        $first_gid = $first_uid;
        $last_gid = $last_uid;
    } else {
        $first_gid = $new_firstgid || $config{"first_gid"};
        $last_gid = $new_lastgid || $config{"last_gid"};
    }
    log_trace( "first_uid %s, last_uid %s, first_gid %s, last_gid %s", $first_uid, $last_uid, $first_gid, $last_gid );
    log_info (gtx("Adding user `%s' ..."),$new_name);

    if (!defined($new_uid)) {
        if ( defined $ingroup_name ) {
            $new_uid = &first_avail_uid( $first_uid,
                                             $last_uid,
                                             $uid_pool{$new_name}{'id'});
        } else {
            my $first_uidgid = ($first_uid, $first_gid)[$first_uid > $first_gid];
            my $last_uidgid  = ($last_uid, $last_gid)[$last_uid < $last_gid];
            # TODO: Check what happens when those ranges do not overlap
            $new_uid = &first_avail_uid_gid( $first_uidgid,
                                             $last_uidgid,
                                             $uid_pool{$new_name}{'id'});
            log_trace( "uidgid=%s, from first_uidgid %s, last_uidgid %s", $new_uid, $first_uidgid, $last_uidgid);
        }
        # TODO: user can specify different UID and GID here.
        # idea: split handling in uid/gid, which are equally the return value of
        # first_avail_uid_gid. If either pool_id is defined, set uid and gid from
        # distinct first_avail_uid and first_avail_gid calls.

        log_debug( "new_uid %s selected", $new_uid);
        if ($new_uid == -1) {
            log_warn( mtx("No UID/GID pair is available in the range %d-%d (FIRST_UID - LAST_UID)."), $first_uid, $last_uid);
            log_fatal( mtx("The user `%s' was not created."), $new_name );
            exit( RET_NO_ID_IN_RANGE );
        }
        log_trace( "gid_option %s, ingroup_name %s", $gid_option, $ingroup_name );
        if ( defined $gid_option && $gid_option == -1 && defined $ingroup_name && $ingroup_name == "") {
            log_warn( mtx("USERGROUPS=no, USER_GID=-1 and USERS_GROUP empty. A user needs a primary group!") );
            log_fatal( mtx("The user `%s' was not created."), $new_name );
            exit( RET_NO_PRIMARY_GROUP );
        } elsif (defined($gid_option) && $gid_option != -1) {
            $ingroup_name = getgrgid($gid_option);
            $primary_gid = $gid_option;
            log_debug( "gid_option defined and not -1, ingroup_name %s, primary_gid %d", $ingroup_name, $primary_gid );
        } elsif ($ingroup_name) {
            $primary_gid = getgrnam($ingroup_name);
            log_debug( "ingroup_name defined %s, primary_gid %d", $ingroup_name, $primary_gid );
        } elsif ( defined( $primary_gid ) ) {
            $ingroup_name = getgrgid($primary_gid);
            log_debug( "primary_gid defined %d, ingroup_name %s", $primary_gid, $primary_gid  );
        } else {
            $ingroup_name = 'users';
            $primary_gid = 100;
            log_debug( "ingroup_name hard users, primary_gid hard 100" );
        }
        if ($make_group_also) {
            $primary_gid = $new_uid;
            $ingroup_name = $new_name;
            log_trace( "make_group_also %s, primary_gid %s, ingroup_name %s", $make_group_also, $primary_gid, $ingroup_name );
        }
    } else {
        log_debug( "new_uid %s, primary_gid %s, ingroup_name %s, make_group_also %s", $new_uid, $primary_gid, $ingroup_name, $make_group_also );
        if (defined($gid_option)) {
            $ingroup_name = getgrgid($gid_option);
            $primary_gid = $gid_option;
            log_debug( "gid_option defined %s, ingroup_name %s", $gid_option, $ingroup_name );
        } elsif (defined($primary_gid) && $primary_gid != -1) {
            $ingroup_name = getgrgid($primary_gid);
            log_debug( "primary_gid defined %s, ingroup_name %s", $primary_gid, $ingroup_name );
        } elsif ($ingroup_name) {
            $primary_gid = getgrnam($ingroup_name);
            log_debug( "ingroup_name defined %s, primary_gid %s", $ingroup_name, $primary_gid );
        } elsif ($make_group_also) {
            $primary_gid=$new_uid; $ingroup_name=$new_name;
            log_debug( "make_group_also %s, primary_gid defined %s, ingroup_name %s", $make_group_also, $primary_gid, $ingroup_name );
        } else {
            log_debug( "no gid_option, no primary_gid, no ingroup_name, no make_group_also" );
            log_fatal( mtx("Internal error interpreting parameter combination") );
            exit( RET_INTERNAL );
        }
    }

    log_debug ("make_group_also %s", $make_group_also );
    if ($make_group_also) {
        $undogroup = $new_name;
        my $groupadd = &which('groupadd');
        if( defined( $primary_gid ) ) {
           log_info( mtx("Adding new group `%s' (%d) ..."), $new_name, $primary_gid);
           if ( ($use_extrausers) || ($config{"use_extrausers"}) ) {
               &systemcall($groupadd, '--extrausers', '-g', $primary_gid, $new_name);
           } else {
               &systemcall($groupadd, '-g', $primary_gid, $new_name);
           }
        } else {
           log_info( mtx("Adding new group `%s' (new group ID) ..."), $new_name);
           if ( ($use_extrausers) || ($config{"use_extrausers"}) ) {
               &systemcall($groupadd, '--extrausers', $new_name);
           } else {
               &systemcall($groupadd, $new_name);
           }
           $primary_gid = getgrnam($new_name);
           log_info( mtx("new group '%s' created with GID %d"), $new_name, $primary_gid );
        }
    }

    {
        my @grgid=getgrgid($primary_gid);
        my $grname=$grgid[0];
        log_info( mtx("Adding new user `%s' (%d) with group `%s (%d)' ..."), $new_name, $new_uid, $grname, $primary_gid );
    }
    $home_dir = $special_home || $uid_pool{$new_name}{'home'} || &homedir($new_name, $ingroup_name);
    if( !$disabled_login ) {
        $shell = $special_shell || $uid_pool{$new_name}{'shell'} || $config{"dshell"};
    } else {
        $shell = $special_shell || $uid_pool{$new_name}{'shell'} || "/usr/sbin/nologin";
    }
    log_debug( "creating new user with home_dir %s and shell %s", $home_dir, $shell );
    $undouser = $new_name;
    my $useradd = &which('useradd');
    if ( ($use_extrausers) || ($config{"use_extrausers"}) ) {
        &systemcall($useradd, '--extrausers', '-d', $home_dir, '-g', $primary_gid, '-s',
                    $shell, '-u', $new_uid, $new_name);
    } else {
        &systemcall($useradd, '-d', $home_dir, '-g', $primary_gid, '-s',
                    $shell, '-u', $new_uid, $new_name);
    }

    create_homedir (1); # copy skeleton data

    # useradd without -p has left the account disabled (password string is '!')
    my $yesexpr = langinfo(YESEXPR());
    my $noexpr = langinfo(NOEXPR());
    if ($ask_passwd) {
        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?
                log_warn( mtx("Permission denied")) if ($ok == 1);
                log_warn( mtx("invalid combination of options")) if ($ok == 2);
                log_warn( mtx("unexpected failure, nothing done")) if ($ok == 3);
                log_warn( mtx("unexpected failure, passwd file missing")) if ($ok == 4);
                log_warn( mtx("passwd file busy, try again")) if ($ok == 5);
                log_warn( mtx("invalid argument to option")) 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.
                PROMPT: for (;;) {
                    print (gtx("Try again? [y/N] "));
                    chop ($answer=<STDIN>);
                    last PROMPT if ($answer =~ m/$yesexpr/o);
                    last PASSWD if ($answer =~ m/$noexpr/o);
                    last PASSWD if (!$answer);
                }
            } else {
                last; ## passwd ok
            }
        }
    }

    if (defined($new_comment)) {
        &ch_comment($new_comment);
    } elsif ($uid_pool{$new_name}{'comment'}) {
        &ch_comment($uid_pool{$new_name}{'comment'});
    } else {
        my $noexpr = langinfo(NOEXPR());
        my $yesexpr = langinfo(YESEXPR());
        CHFN: 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.
            PROMPT: for (;;) {
                print (gtx("Is the information correct? [Y/n] "));
                chop (my $answer=<STDIN>);
                last PROMPT if ($answer =~ m/$noexpr/o);
                last CHFN if ($answer =~ m/$yesexpr/o);
                last CHFN if (!$answer);
            }
        }
    }

    if ( ( $add_extra_groups || $config{"add_extra_groups"} ) && defined($config{"extra_groups"}) ) {
        log_debug( "add extra_groups from config (%s) to supplemental_groups", $config{"extra_groups"} );
        push (@supplemental_groups, split(/\s+/,$config{"extra_groups"}));
    }
    if ( @supplemental_groups ) {
        log_info( mtx("Adding new user `%s' to supplemental / extra groups `%s' ..."), $new_name, join(", ", @supplemental_groups) );
        foreach my $newgrp ( @supplemental_groups ) {
            log_trace( "newgrp %s", $newgrp );
            if (!defined getgrnam($newgrp)) {
                log_warn( mtx("The group `%s' does not exist."), $newgrp);
                next;
            }
            if (&user_is_member($new_name, $newgrp)) {
                log_info( mtx("The user `%s' is already a member of `%s'."),
                        $new_name,$newgrp );
                next;

            }

            log_info( mtx("Adding user `%s' to group `%s' ..."), $new_name, $newgrp );
            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);
            }
        }
    }


    if ($config{"quotauser"}) {
        log_info( mtx("Setting quota for user `%s' to values of user `%s' ..."), $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,
            $primary_gid, $home_dir) if (-x "/usr/local/sbin/adduser.local");
    release_lock(0);

    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  -> do not 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 ($home_dir =~ /^\/+nonexistent(\/|$)/) {
        log_info( mtx("Not creating `%s'."), $home_dir );
    } elsif ($no_create_home) {
        log_info( mtx("Not creating home directory `%s'."), $home_dir );
    } elsif (-e $home_dir) {
        log_warn( mtx("The home directory `%s' already exists.  Not touching this directory."),
            $home_dir );
        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 != $primary_gid)) {
            log_warn( mtx("Warning: The home directory `%s' does not belong to the user you are currently creating."), $home_dir );
        }
    } else {
        log_info( mtx("Creating home directory `%s' ..."),$home_dir );
        $undohome = $home_dir;
        if( !&mktree($home_dir) ) {
           log_err( gtx("Couldn't create home directory `%s': %s."), $home_dir, $!);
           &cleanup();
        }
        if ($action eq "adduser") {
            # Mute the command
            system('sh' => ( '-c' => '"$@" >/dev/null 2>&1', '--', '/usr/sbin/zsysctl', 'userdata', 'create', $new_name, $home_dir,));
        }
        if( !chown($new_uid, $primary_gid, $home_dir) ) {
            log_err("chown %s:%s %s: %s", $new_uid, $primary_gid, $home_dir, $!);
            &cleanup();
        }
        $dir_mode = get_dir_mode();
        if( !chmod ($dir_mode, $home_dir) ) {
            log_err("chmod %s %s: %s", $dir_mode, $home_dir, $!);
            &cleanup();
        }

        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) {
            log_info( mtx("Copying files from `%s' ..."), $config{skel} );
            my $findpipe;
            if( !open($findpipe, q{-|}, "cd $config{skel}; find .  -print") ) {
                log_err( mtx("fork for `find' failed: %s"), $!);
                &cleanup();
            }
            while (<$findpipe>) {
                chop;
                next if ($_ eq ".");
                next if ($_ =~ qr/$config{skel_ignore_regex}/ );
                &copy_to_dir($config{"skel"}, $_, $home_dir, $new_uid,
                    $primary_gid, ($config{"setgid_home"} =~ /yes/i));
            }
            close ($findpipe);
        }

        if (defined($encrypt_home)) {
            &systemcall("/bin/umount", $home_dir);
        }
    }
}

# mktree: create a directory and all parent directories
# we do not 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 = oct(755);

    $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 is 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 does not exist
#   1 if the user already exists with the specified uid
#     (or $new_uid was not specified)
#   2 if the user already exists, but $new_uid does not match 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 should not be performed here
        # also, we might be checking a normal user as well here
        if( $uid >= $config{"first_system_uid"} &&
            $uid <= $config{"last_system_uid" } ) {
            return 2;
        }
    } else {
        return 0;
    }
}

# existing_group_ok: check if there is already a group which satiesfies the requirements
# parameter:
#   new_name: the name of the group
#   new_gid : the GID of the group
# return values:
#   0 if the group does not exist
#   1 if the group already exists with the specified gid
#      (or $gid_option was not specified)
#   2 if the group already exists, but $gid_option does not 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 should not  be any
        # gid outside of our allowed range anyways ...
        # also, we might be checking a normal user as well here
        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 is not a system user, 1 otherwise
# return values:
#
# todo: not sure whether global variables apply fine here.
sub check_user_group {
    my ($system) = @_;
    log_debug( "check_user_group %s called, make_group_also %s", $system, $make_group_also );
    if( !$system || !existing_user_ok($new_name, $new_uid) ) {
        if( defined getpwnam($new_name) ) {
            if( $system ) {
                log_fatal( mtx("The user `%s' already exists, and is not a system user."), $new_name);
                exit( RET_WRONG_OBJECT_PROPERTIES );
            } else {
                log_fatal( mtx("The user `%s' already exists."), $new_name);
                exit( RET_OBJECT_EXISTS );
            }
        }
        if (defined($new_uid) && getpwuid($new_uid)) {
            log_fatal( mtx("The UID %d is already in use."), $new_uid);
            exit( RET_ID_IN_USE );
        }
    }
    if ($make_group_also) {
        log_trace( "make_group_also 1, new_name %s, new_uid %s", $new_name, $new_uid );
        if( !$system || !existing_group_ok($new_name, $new_uid) ) {
            if (defined getgrnam($new_name)) {
                log_fatal( mtx("The group `%s' already exists."),$new_name );
                exit( RET_OBJECT_EXISTS );
            }
            if (defined($new_uid) && defined(getgrgid($new_uid))) {
                log_fatal( mtx("The GID %d is already in use."),$new_uid );
                exit( RET_ID_IN_USE );
            }
        }
    } else {
        if ($ingroup_name && !defined(getgrnam($ingroup_name))) {
            log_fatal( mtx("The group `%s' does not exist."), $ingroup_name);
            exit( RET_OBJECT_DOES_NOT_EXIST );
        }
        if (defined($gid_option) && !defined(getgrgid($gid_option))) {
            log_fatal( mtx("No group with GID %d found."), $gid_option);
            exit( RET_OBJECT_DOES_NOT_EXIST );
        }
    }
    log_debug( "return from check_user_group" );
}


# 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;
        if( !($target = readlink("$fromdir/$file")) ) {
            log_err( "readlink: %s", $! );
            &cleanup();
        }
        my $curgid="$)";
        my $curuid="$>";
        my $error="";
        $)="$newg";
        $>="$newu";
        symlink("$target", "$todir/$file") or $error="$!";
        $>="$curuid";
        $)="$curgid";
        if( "$error" ne "" ) {
            log_err( "symlink: %s", $!);
            &cleanup();
        }
        return;
    } elsif (-f "$fromdir/$file") {
        my $filefh;
        my $newfilefh;
        if( !open ($filefh, q{<}, "$fromdir/$file") ) {
            log_err( "open %s/%s: %s", $fromdir, $file , $!);
            &cleanup();
        }
        if( !open ($newfilefh, q{>}, "$todir/$file") ) {
            log_err( "open >%s/%s: %s", $todir, $file, $!);
            &cleanup();
        }

        if( !print $newfilefh (<$filefh>) ) {
           log_err( "print %s/%s: %s", $todir, $file, $!);
           &cleanup();
        }
        close($filefh);
        if( !close($newfilefh) ) {
            log_err( "close %s/%s: %s", $todir, $file, $!);
            &cleanup();
        }
    } elsif (-d "$fromdir/$file") {
        if( !mkdir("$todir/$file", 700) ) {
            log_err( "mkdir: %s/%s: %s", $todir, $file, $!);
            &cleanup();
        }
    } else {
        log_err( mtx("%s/%s is neither a dir, file, nor a symlink."), $fromdir, $file );
        &cleanup();
    }

    if( !chown($newu, $newg, "$todir/$file") ) {
       log_err( "chown %s:%s %s/%s: %s", $newu, $newg, $todir, $file, $! );
       &cleanup();
    }
    $perm = (stat("$fromdir/$file"))[2] & oct(7777);
    if (-d "$fromdir/$file" && ($perm & oct(10)) && $sgiddir) {
        $perm |= oct(2000);
    }
    if( !chmod($perm, "$todir/$file") ) {
       log_err( "chmod %s/%s: %s", $todir, $file, $!);
       &cleanup();
    }
}


# checkname: perform some sanity checks
# parameters:
#   name: the name to check
# return values:
#   none (exits on error)
sub checkname {
    my ($name) = @_;

    my $ieee_regex = '^[_.A-Za-z0-9][-\@_.A-Za-z0-9]*\$?$';
    my $min_regex = qr{^[^-+~:,\s/][^:,\s/]*$};
    my $name_regex_var = $found_sys_opt ? 'SYS_NAME_REGEX' : 'NAME_REGEX';
    my $name_regex = $config{lc $name_regex_var};

    if ($name =~ /^[\d]+$/) {
        # this check cannot be turned off
        log_err( mtx("To avoid ambiguity with numerical UIDs, usernames which
            consist of only digits are not allowed.") );
        exit( RET_INVALID_CHARS_IN_NAME );
    }

    if (length $name > 32) {
        # this check cannot be turned off
        log_err( mtx("Usernames must be no more than 32 bytes in length;
            note that if you are using Unicode characters, the character
            limit will be less than 32.") );
        exit( RET_INVALID_CHARS_IN_NAME );
    }

    if ($name !~ $min_regex) {
        # this check cannot be turned off
        log_err( mtx("To avoid problems, the username must not start with a
            dash, plus sign, or tilde, and it must not contain any of the
            following: colon, comma, slash, or any whitespace characters
            including spaces, tabs, and newlines.") );
        exit( RET_INVALID_CHARS_IN_NAME );
    }

    return if ($name =~ qr/$name_regex/);

    if ($name !~ $ieee_regex && $name_check_level < 2) {
        log_err( mtx("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.  (Use the `--allow-all-names' option
            to bypass this restriction.)") );
        exit( RET_INVALID_CHARS_IN_NAME );
    }

    if ($name_check_level) {
        log_info( mtx("Allowing use of questionable username.") );
    } else {
        log_err( mtx("Please enter a username matching the regular expression
            configured via the %s configuration variable.  Use the
            `--allow-bad-names' option to relax this check or reconfigure
            %s in configuration."), $name_regex_var, $name_regex_var );
        exit( RET_INVALID_CHARS_IN_NAME );
    }
}

# first_avail_uid: return the first available uid in given range
# parameters:
#   min, max: the range
#   pool_id: user id suggested from pool
# return values:
#   -1 if no free uid is available
#  otherwise the choosen uid
sub first_avail_uid {
    my ($min, $max, $pool_id) = @_;
    if (defined ($pool_id)) {
        return $pool_id if (!defined(getpwuid($pool_id)));
        return -1;
    }
    log_info( mtx("Selecting UID from range %d to %d ...\n"),$min,$max);

    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
#   pool_id: group id suggested from pool
# return values:
#   -1 if no free gid is available
#   otherwise the choosen gid
sub first_avail_gid {
    my ($min, $max, $pool_id) = @_;
    if (defined ($pool_id)) {
        return $pool_id if (!defined(getgrgid($pool_id)));
        return -1;
    }
    log_info( mtx("Selecting GID from range %d to %d ..."),$min,$max);

    my $t = $min;
    while ($t <= $max) {
       return $t if (!defined(getgrgid($t)));
       $t++;
    }
    return -1; # nothing available
}

# first_avail_uid_gid: return the first available id in given range
#     that is both available as uid and gid
# parameters:
#   min, max: the range
#   pool_id: user id suggested from pool
# return values:
#   -1 if no free id is available
#   otherwise the choosen id
sub first_avail_uid_gid {
    my ($min, $max, $pool_id) = @_;
    if (defined ($pool_id)) {
        return $pool_id if (!defined(getgrgid($pool_id)));
        return -1;
    }
    log_info( mtx("Selecting UID/GID from range %d to %d ..."), $min, $max );

    my $t = $min;
    while ($t <= $max) {
       return $t if (!defined(getgrgid($t)) && !defined(getpwuid($t)));
       $t++;
    }
    return -1; # nothing available
}

sub ch_comment {
    my $chfn = &which('chfn');
    my $comment = shift;
    if($comment =~ /,/) {
        my($comment_name,$comment_room,$comment_work,$comment_home,$comment_other)
          = split(/,/,$comment);

        if ( ($use_extrausers) || ($config{"use_extrausers"}) ) {
            &systemcall($chfn, '--extrausers', '-f', $comment_name, '-r', $comment_room, $new_name);
            &systemcall($chfn,'--extrausers','-w',$comment_work,$new_name)
                if(defined($comment_work));
            &systemcall($chfn,'--extrausers','-h',$comment_home,$new_name)
                if(defined($comment_home));
            &systemcall($chfn,'--extrausers','-o',$comment_other,$new_name)
                if(defined($comment_other));
        } else {
            &systemcall($chfn, '-f', $comment_name, '-r', $comment_room, $new_name);
            &systemcall($chfn,'-w',$comment_work,$new_name)
                if(defined($comment_work));
            &systemcall($chfn,'-h',$comment_home,$new_name)
                if(defined($comment_home));
            &systemcall($chfn,'-o',$comment_other,$new_name)
                if(defined($comment_other));
        }
    } else {
        if ( ($use_extrausers) || ($config{"use_extrausers"}) ) {
            &systemcall($chfn, '--extrausers', '-f', $comment, $new_name);
        } else {
            &systemcall($chfn, '-f', $comment, $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 {
    if ($undohome) {
        log_info( mtx("Removing directory `%s' ..."), $undohome);
        &systemcall('rm', '-rf', $undohome);
    }
    if ($undouser) {
        log_info( mtx("Removing user `%s' ..."), $undouser);
        &systemcall('userdel', $undouser);
    }
    if ($undogroup) {
        log_info( mtx("Removing group `%s' ..."), $undogroup);
        &systemcall('groupdel', $undogroup);
    }
    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.
    log_err( mtx( "Caught a SIG%s.", $sig ) );
    &cleanup();
}


sub version {
    printf( gtx("adduser version %s\n\n"), $version );
    print( gtx("Adds a user or group to the system.

For detailed copyright information, please refer to
/usr/share/doc/adduser/copyright.
\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 [--uid id] [--firstuid id] [--lastuid id]
        [--gid id] [--firstgid id] [--lastgid id] [--ingroup group]
        [--add-extra-groups] [--encrypt-home] [--shell shell]
        [--comment comment] [--home dir] [--no-create-home]
        [--allow-all-names] [--allow-bad-names]
        [--disabled-password] [--disabled-login]
        [--conf file] [--extrausers] [--quiet] [--verbose] [--debug]
        user
    Add a normal user

adduser --system
        [--uid id] [--group] [--ingroup group] [--gid id]
        [--shell shell] [--comment comment] [--home dir] [--no-create-home]
        [--conf file] [--extrausers] [--quiet] [--verbose] [--debug]
        user
   Add a system user

adduser --group
        [--gid ID] [--firstgid id] [--lastgid id]
        [--conf file] [--extrausers] [--quiet] [--verbose] [--debug]
        group
addgroup
        [--gid ID] [--firstgid id] [--lastgid id]
        [--conf file] [--extrausers] [--quiet] [--verbose] [--debug]
        group
   Add a user group

addgroup --system
        [--gid id]
        [--conf file] [--extrausers] [--quiet] [--verbose] [--debug]
        group
   Add a system group

adduser [--extrausers] USER GROUP
   Add an existing user to an existing group\n") );
}

sub usage_error {
    &usage;
    exit( RET_INVALID_CALL );
}

# get_dir_mode: return the appropriate permissions mode for a home directory
# parameters:
#   none
# return value:
#   a valid octal mode
sub get_dir_mode
  {
    my $setgid = $config{"setgid_home"} =~ /yes/i;
    my $mode = $found_sys_opt
      ? $config{"sys_dir_mode"}
      : $config{"dir_mode"};

    if(!defined($mode) || ! ($mode =~ /[0-7]{3}/ || $mode =~ /[0-7]{4}/)) {
        $mode = ($found_sys_opt) ? "750" : "0750";
    }

    if($setgid && (length($mode) == 3 || $mode =~ /^[0-1|4-5][0-7]{3}$/)) {
        $mode += 2000;
    }

    return oct($mode);
  }

# Local Variables:
# mode:cperl
# cperl-indent-level:4
# End:

# vim: tabstop=4 shiftwidth=4 expandtab

Filemanager

Name Type Size Permission Actions
addgroup File 53.9 KB 0755
adduser File 53.9 KB 0755
aspell-autobuildhash File 13.36 KB 0755
delgroup File 18.53 KB 0755
deluser File 18.53 KB 0755
dpkg-preconfigure File 4.25 KB 0755
dpkg-reconfigure File 4.43 KB 0755
escapesrc File 22.23 KB 0755
genccode File 14.59 KB 0755
gencmn File 14.59 KB 0755
gennorm2 File 62.76 KB 0755
gensprep File 26.66 KB 0755
iconvconfig File 34.47 KB 0755
icupkg File 22.95 KB 0755
ispell-autobuildhash File 15.52 KB 0755
ldconfig File 387 B 0755
ldconfig.real File 1 MB 0755
libgvc6-config-update File 14.15 KB 0755
remove-default-ispell File 2.86 KB 0755
remove-default-wordlist File 2.86 KB 0755
select-default-ispell File 3.23 KB 0755
select-default-wordlist File 3.21 KB 0755
update-default-aspell File 1 KB 0755
update-default-ispell File 9.68 KB 0755
update-default-wordlist File 7.5 KB 0755
update-dictcommon-aspell File 1 KB 0755
update-dictcommon-hunspell File 782 B 0755
update-icon-caches File 596 B 0755
zic File 66.39 KB 0755
Filemanager