summaryrefslogtreecommitdiffstats
path: root/adduser
diff options
context:
space:
mode:
Diffstat (limited to 'adduser')
-rwxr-xr-xadduser1063
1 files changed, 1063 insertions, 0 deletions
diff --git a/adduser b/adduser
new file mode 100755
index 0000000..1e1ca1b
--- /dev/null
+++ b/adduser
@@ -0,0 +1,1063 @@
+#!/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 = "VERSION";
+
+###################
+# 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;
+
+# 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,
+ "add_extra_groups" => \$add_extra_groups,
+ "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";
+ }
+}
+
+
+$ENV{"VERBOSE"} = $verbose;
+$ENV{"DEBUG"} = $verbose;
+
+
+# preseed configuration data and then read the config file
+preseed_config(\@defaults,\%config);
+
+&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 | $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');
+ &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');
+ &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');
+ &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');
+ &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');
+ &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');
+ &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');
+ &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');
+ &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');
+ &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 ($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}/ );
+ &copy_to_dir($config{"skel"}, $_, $home_dir, $new_uid,
+ $new_gid, ($config{"setgid_home"} =~ /yes/i));
+ }
+ }
+ }
+}
+
+# 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:
+# none
+# return values:
+# none (exits on error)
+sub checkname {
+ my ($name) = @_;
+ 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 ($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 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);
+
+ &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
+ {
+ &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] 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 configuration variable
+ --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: