You don't need to and shouldn't fiddle with /etc/passwd - useradd will simply fail if a user with that name already exists. So you should simply attempt to create that user and check if you succeeded - that way, even multiple concurrent copies of the script won't be able to step on each other's toes.
Don't try to find a UID yourself either. According to man useradd:
The default is to use the smallest ID value greater than 99 and greater than every other user. Values between 0 and 99 are typically reserved for system accounts.
That's exactly what you want anyway.
Also, you have tons of duplicate code in there. All these $i = 0; do { ... } while $foo; loops are very similar - factor out the code! Do It Once And Only Once.
Lastly, it would make the code much easier to read if you shuffle the prompts and text out of the code's way. As a bonus, if you give the prompts and messages names, the code becomes self documenting and you can get rid of all of that commentary as well.
#!/usr/bin/perl -w
use strict;
no warnings 'once';
use vars qw(%MSG %PROMPT);
use Email::Valid;
use Term::ReadKey;
use Mail::Send;
$ENV{PATH}='';
$|++;
sub message {
my $clear = shift;
my $message = shift;
system "/usr/bin/clear" if $clear;
printf "\n$message", @_;
}
sub ask_user {
my ($prompt, $check) = @_;
local $_;
{
print "\n$prompt->[0]";
chomp($_ = <STDIN>);
print("\n$prompt->[1]\n"), redo unless $check->();
}
return $_;
}
eval do { local $/; <DATA> }; # read messages and prompts
use constant GID => 100;
use constant SHELL => '/bin/bash';
use constant LOG_FILE => "/home/newuser/downUnder.log";
my ($USERNAME, $FULL_NAME, $FIRST_NAME, $EMAIL, $PASSWD);
message(1, $MSG{BANNER});
ask_user($PROMPT{MAKE_ACCOUNT}, sub {
die "\nCiao!\n" if 'n' eq lc;
return 1 if 'y' eq lc;
return;
});
message(1, $MSG{TOS});
ask_user($PROMPT{COMPLY}, sub {
die "\nCiao!\n" if 'n' eq lc;
return 1 if 'y' eq lc;
return;
});
{
message(1, $MSG{COLLECTDETAILS});
$FULL_NAME = ask_user($PROMPT{FULL_NAME}, sub {
/^\s*(\D+)\s+\D+\b/ ? ($FIRST_NAME = $1, return 1) : 0
});
$EMAIL = ask_user($PROMPT{EMAIL}, sub {
Email::Valid->address(-address => $_, -mxcheck => 1)
});
message(0, $MSG{CASESENSITIVE});
ReadMode 2; # cooked mode,echo off
{
$PASSWD = ask_user($PROMPT{PASSWD}, sub { length > 6 });
my $passwd2 = ask_user($PROMPT{CONFIRM}, sub { 1 });
print("\n\nYou typed two different passwords!\n"), redo
if $PASSWD ne $passwd2;
}
ReadMode 0; # restore original settings
message(0, $MSG{SUMMARY}, $FIRST_NAME, $FULL_NAME, $EMAIL);
redo if 'n' eq lc ask_user($PROMPT{CORRECT}, sub { /^(y|n)$/i });
}
$USERNAME = ask_user($PROMPT{USERNAME}, sub {
return unless /\A\w+\z/;
system(
'/usr/bin/sudo',
'/usr/sbin/useradd',
'-s' => SHELL,
'-g' => GID,
'-p' => crypt($PASSWD, time()),
'-c' => $FULL_NAME,
'-m' => $_,
) and return;
return 1;
});
my $date = localtime;
printf(
{
Mail::Send->new(
Subject => 'New User Added!',
To => 'root'
)->open
}
$MSG{ADMINMAIL},
$date,
$USERNAME,
scalar getpwnam($USERNAME),
$FULL_NAME,
$EMAIL
);
{
open my $fh, ">>", LOG_FILE
or warn "Failed opening log file: $!\n";
print $fh "$USERNAME :: $FULL_NAME :: $EMAIL - $date\n";
}
message(1, $MSG{THANKSBYE});
sleep 30;
__END__
%PROMPT = (
MAKE_ACCOUNT => [
"Are you going to make a shell account now? (y/n): ",
"Not an option",
],
COMPLY => [
"I fully understand/comply and respect the system (y/n): ",
"Not an option",
],
FULL_NAME => [
"(*) Your actual full name: ",
"Full name is required!",
],
EMAIL => [
"(*) Your email address: ",
"Your *valid* email address is required!",
],
PASSWD => [
"(*) Your account password (won't echo): ",
"Your password has to be longer than 6 characters.",
],
CONFIRM => [
"(*) Confirm your password by retyping it: ",
"",
],
CORRECT => [
"Now is your last chance to abort. Is this data correct? (y/n)
+: ",
"Not an option",
],
USERNAME => [
"Please choose a username: ",
"That username is invalid or taken.",
],
);
%MSG = (
BANNER => <<"EOT",
Welcome to the official server of the IIT Linux User's Group
EOT
TOS => <<"EOT",
Ok here's the gig. I'm making this service, and everything
associated with a shell account, freely available for your
use, to do with as you please. It's my pleasure, and I
hope you learn a whole lot.
With this, comes responsibility on your part to use these
tools in accordance with US laws and general morality.
Simply, don't go hacking away at someone else's box, or
do anything that will wind me up in jail. I might add as
a side note that I log *everything* plus some, so don't do
anything over the network that will make me suspicious.
Also understand that, while this is my primary server,
there are unexpected periods of downtime (generally
attributed to the crappy network here at the school,
and the strange electricity problems we have every so
often). I promise absolute security with regard to your
data; I can't promise complete integrity of the disks.
There are occassional occurances of data loss, due to
my mucking about, or some other such cause. Just bear
this in mind.
EOT
COLLECTDETAILS => <<"EOT",
Nothing of the following will be public.
EOT
CASESENSITIVE => <<"EOT",
Note: The following items are case sensitive.
EOT
SUMMARY => <<"EOT",
Ok, %s:
Your name is %s.
Your email is %s.
EOT
THANKSBYE => <<"EOT",
OK, your brand spanking new account is ready to roll.
You will want to open a secure shell (ssh) connection
via either protocol 1 or 2 to this host and then use
your newly created username and password to log into
the system.
Enjoy! And please always remember the following golden
rules, and all will be well:
#1) Respect the privacy of others.
#2) Respect the security and integrity of this machine.
#3) Think before you type.
Love and Linux!
(You will be automagically disconnected in 30 seconds)
EOT
ADMINMAIL => <<"EOT",
Attention Administrator!
A new user has recently been added. Information follows:
Date: %s
Username: %s (UID: %s)
User: %s
Email: %s
EOT
);
Makeshifts last the longest. |