Hi Dejan!
Dejan Grujin wrote:
>
> Problem besteht noch...
> Wäre nett, wenn Du das tun könntest
>
> Gruß Dejan
>
Klar, hab's sogar gefunden.
Das Ding heißt 'Enteruser' und scheint tatsächlich für
interaktiven Betrieb geschrieben zu sein. Vielleicht
reicht dein Perl ja für einen Hack, um die Datenbank als
Eingabe zu benutzen.
Das ganze müßte auf http://www.enteract.com liegen.
Was ich noch da habe , hängt an dieser Mail.
Marc
This is a pretty
output of this script. The useful, runnable perl
script lives here.
#!/usr/bin/perl # # NOTE: Install Perl5 and reference #!/usr/local/bin/perl if you have an older # version of FreeBSD # # This software is intended for distribution under the same terms and spirit # as FreeBSD. # # This script was originally authored by Dan Howard# for EnterAct, LLC, 1998. (http://www.enteract.com/) # use Fcntl ':flock'; #use Getopt::Std; # Global, user-customizeable configs my $pw_path = '/usr/sbin/pw'; my $pw_conf_path = '/etc/pw.conf'; my $sendmail_path = '/usr/sbin/sendmail'; my $welcome_message_path = '/etc/adduser.message'; my $logfile = '/var/log/enteruser.log'; my $organization = ''; my $domain = `/bin/hostname`; #my $lockfile = '/var/run/enteruser.LOCK'; ### Forward declarations ### # Main functions sub enteruser; sub queueuser; # Queueuser helpers sub queueadd; sub queuelist; sub queuedel; sub queuedo; # Common helpers sub get_user_data; sub print_user_data; sub add_user; # UI sub get_fullname; sub get_password; sub get_shell; sub get_username; #sub get_billing_id; #sub get_group; #sub get_referral; # Generic helpers sub append_file; ### START PROGRAM ### if( $< != 0 ) { print "You must be root to run $0!\n"; exit -1; } # Here's an example of how you could use GetOpt to extend functionality, but I # never was too keen on this example. One possibility is to set various # defaults for a queueuser operation, or change the path for the welcome # message. # # One possibility that has excited me on occasion has been the idea of setting # input through the command-line, which could ease the lives of advanced users # somewhat, but not enough that I've bothered to try. # # getopts('c'); # # -c is for curt, meaning no welcome message # if( $opt_c ) { # print "Will not send welcome message.\n"; # $welcome_message_path = ''; # } if( $0 =~ /queueuser/ ) { &queueuser(); } else { &enteruser(); } ### END PROGRAM ### sub enteruser { my %newuser; my $y_or_n; %newuser = &get_user_data; print "\nVerify User Information\n"; print "-----------------------\n"; &print_user_data(%newuser); print "\nIs this okay? (Y/n) "; $y_or_n = <>; if( $y_or_n !~ /^n/i ) { &add_user(%newuser); } } sub queueuser { my @userqueue; my $choice; while(1) { my $yn; print<<__MENU; Please Select Operation ----------------------- [A]dd a user to the queue [L]ist users in the queue [D]elete user from the queue [P]rocess users in queue [Q]uit this program __MENU print "What would you like to do? "; $choice = <>; if( $choice =~ /^a/i ) { LAME: { # the LAME kludge push @userqueue, &queueadd; } print "Add another? (Y/n) "; $yn = <>; goto LAME unless $yn =~ /^n/i; } elsif( $choice =~ /^l/i ) { &queuelist(@userqueue); } elsif( $choice =~ /^d/i ) { @userqueue = &queuedel(@userqueue); } elsif( $choice =~ /^p/i ) { @userqueue = &queuedo(@userqueue); } elsif( $choice =~ /^q/i ) { if( @userqueue ) { print "There are unprocessed users in your queue.\n"; print "If you quit now, they will be lost!\n"; print "Do you really want to quit? (y/N) "; $choice = <>; if( $choice =~ /^y/i ) { exit(0); } } else { exit(0); } } else { print "\nHuh?\n"; } } } # Pretty much just enteruser, only we stay within queueuser sub queueadd { my %newuser; my $y_or_n; %newuser = &get_user_data; print "\nVerify User Information\n"; print "-----------------------\n"; &print_user_data(%newuser); print "\nIs this okay? (Y/n) "; $y_or_n = <>; if( $y_or_n !~ /^n/i ) { return {%newuser}; } } sub queuelist { my @userlist = @_; my $user; for $user (@userlist) { printf("%8s %16s %10s %4s\n", $user->{username}, $user->{password}, $user->{fullname}, $user->{shell}); } } sub queuedel { my @userlist = @_; my $goner; my ($i, $confirm); &queuelist(@userlist); print "Which user do you wish to remove? "; $goner = <>; chomp $goner; for $i ( 0 .. $#userlist ) { if( $userlist[$i]->{username} eq $goner ) { print "\nConfirm User Deletion\n"; print "---------------------\n"; &print_user_data(%{$userlist[$i]}); print "Remove this user from your queue? (Y/n) "; $confirm = <>; if( $confirm !~ /^n/i ) { splice(@userlist,$i,1); print "User $goner removed from queue.\n"; } else { print "Okay then, we'll leave this one alone.\n"; } } } return @userlist; } sub queuedo { my @userlist = @_; my $i; for $user (@userlist) { if( $user->{username} ) { print "\n>>> ADDING USER ", $user->{username}, ":\n"; &add_user(%{$user}); } } return; } # Self-explanatory ... sub print_user_data { my %user = @_; print<<__EOUD; username: $user{username} fullname: $user{fullname} password: $user{password} shell: $user{shell} __EOUD } # Get each piece of user data, calling appropriate get_ function until it # returns 1 sub get_user_data { my %user; # while( $user{group} eq '' ) { $user{group} = &get_group; } while( $user{username} eq '' ) { $user{username} = &get_username; } while( $user{fullname} eq '' ) { $user{fullname} = &get_fullname; } while( $user{password} eq '' ) { $user{password} = &get_password; } while( $user{shell} eq '' ) { $user{shell} = &get_shell; } return %user; } # Here's an example of a custom function used by EnterAct. Here we ask # additionally for a 'Billing ID' to be stored in the log # sub get_billing_id { # print " Billing ID: "; # my $billing_id = <>; # chomp $billing_id; # if( $billing_id =~ /^\d+$/ ) { # return $billing_id; # } # print "I'm sorry, but I was hoping for a number.\n"; # return ''; # } sub get_fullname { print " Full Name: "; my $fullname = <>; chomp $fullname; if( $fullname eq '' ) { return "J. Doe"; } if( $fullname =~ /^[\w\s\.\&\']*$/ ) { return $fullname; } print "Names should be alphanumeric.\n"; return ''; } # Here's another example of how you might want to specify going about setting # group names. This is an EnterAct-specific example which prompts for a few # different acceptable groups. # sub get_group { # while(1) { # print "Choose from: dialin, mailbox, loyola, nologin\n"; # print " Which group? "; # my $group = <>; # if( $group =~ /^d/i ) { # return 'dialin'; # } # elsif( $group =~ /^m/i ) { # return 'mailbox'; # } # elsif( $group =~ /^l/i ) { # return 'loyola'; # } # elsif( $group =~ /^n/i ) { # return 'nologin'; # } # } # } # This one I like. It'll generate a random password if none is entered, using # an algorithm that results in something a little easier to tell a customer # over the phone than what pw generates but that should still be quite # unpredictable. # # If a password is entered, it does a very basic sanity check on it to # determine if it might be easily crack-able. sub get_password { my ($file, $confirm); my @check_files = ('/etc/passwd', '/usr/share/dict/words'); my @ary = ( 0 .. 9, 'A' .. 'Z', 'a' .. 'z', 'z', '!', '$', '%'); print " Password: [random] "; my $password = <>; chomp $password; if( $password ne '' ) { foreach $file (@check_files) { if( (system ("/usr/bin/grep", "-qw", $password, $file))/256 == 0 ) { print "Ewww, no. That password is found in $file.\n"; print "This password is inexcusably lame, do you really want it? (Y/n) "; $confirm = <>; if( $confirm !~ /^n/i ) { return $password; } else { return ''; } } } } else { my $pw_len = rand(5)+6; for(1..$pw_len) { $password .= $ary[rand(@ary)]; } } return $password; } # Another custom function used at EnterAct. This one actually got much more # sophisticated with time. Seen here is an earlier version, that could still # be interesting. # # sub get_referral { # my @ary = ( # 'Current customer', # 'Word of mouth', # 'Additional account', # 'Microcenter', # 'CNET', # 'Newsgroups', # 'Loyola', # 'Byte By Byte', # 'Chicago Computer Guide', # 'Digital Chicago', # 'National-Louis', # 'Lake Forest College', # 'Web', # 'Phone book', # ); # # foreach $n (0..@ary-1) { # print " ", $n+1, "\) $ary[$n]\n"; # } # # print "Enter referral numer or other: "; # my $referral = <>; # chomp $referral; # if( $referral =~ /^\d+$/ && $ary[$referral-1] ) { # $referral = $ary[$referral-1]; # } # return $referral; # } # This function will determine what shells are available in $pw_conf_path and # offer these as choices sub get_shell { my($i, $shell, $shellstr, $default); $shellstr = `/usr/bin/grep ^shells $pw_conf_path`; $shellstr =~ s/.*?=\W*(.*)/$1/; chomp $shellstr; my @shells = split(/\W+/, $shellstr); $default = $shells[0]; # Default shell is first choice listed. $default or die "Not enough shells in $pw_conf_path!"; print " Shell: [$default] "; $shell = <>; chomp $shell; $shell = $default unless $shell; for $i ( 0 .. $#shells ) { if( $shell eq $shells[$i] ) { return $shell; } } print "\"$shell\" is not a valid shell.\n"; print "Please select from among:\n @shells\n"; return ''; } # If you are using a more modern version of FreeBSD and want to use usernames # greater than eight characters, you need to change this function. Add/remove # checks as desired. sub get_username { print " Username: "; my $username = <>; chomp $username; $username =~ tr/[A-Z]/[a-z]/; if( length($username) > 8 ) { print "No, that username is too long.\n"; print "Usernames must be eight of fewer characters.\n"; return ''; } if( length($username) < 3 ) { print "No, that username is too short.\n"; print "Usernames must be three or more characters in length.\n"; return ''; } if( $username !~ /^[a-z0-9]*$/ ) { print "No, that username's not good.\n"; print "Usernames should consist solely of alphanumeric characters.\n"; return ''; } if( $username !~ /^[a-z]/ ) { print "I'm sorry, but usernames shouldn't start with numbers.\n"; return ''; } if( (system "/usr/bin/id $username 2> /dev/null > /dev/null")/256 == 0 ) { print "Ouch - that one's taken already.\n"; return ''; } if( (system "/usr/bin/grep -q ^$username: /etc/aliases")/256 == 0 ) { print "Ouch - that one's claimed as a mail alias.\n"; return ''; } return $username; } # Calls pw to enter a user into the system. Did you properly configure # /etc/pw.conf? sub add_user { my %user = @_; my $logline; my $username = $user{username}; my $fullname = $user{fullname}; my $password = $user{password}; my $shell = $user{shell}; # my $group = $user{group}; my $oldbuf = $|; $| = 1; # It's nice to finish what we start. local $SIG{INT} = 'IGNORE'; print "Running pw ... "; open( PW, "| $pw_path useradd $username -c \"$fullname\" -m -s $shell -h 0" ) # "| $pw_path useradd $username -c \"$fullname\" -g $group -m -s $shell -h 0" ) or die "$pw_path failure: $!"; print PW $password, "\n"; close PW or warn "$pw_path exited on $?: $!"; print "DONE!\n"; print "Creating public directories ... "; system("/bin/mkdir", "/home/$username/public_html"); system("/usr/sbin/chown", "-R", "$username.$username", "/home/$username/public_html"); # system("/usr/sbin/chown", "-R", "$username.$group", "/home/$username/public_html"); print "DONE!\n"; if( -s $welcome_message_path ) { print "Queueing welcome message ... "; open( WELCOME, $welcome_message_path ) or die "Couldn't open $welcome_message_path: $!"; open( MAIL, "| $sendmail_path -it " ) || die "Couldn't open pipe to $sendmail: $0"; local $SIG{PIPE} = sub { die "Couldn't open pipe to $sendmail: $0" }; select MAIL; print<<__MAIL; To: $username\@$domain ($fullname) Subject: Welcome to $organization! __MAIL while( ) { # This is better than exec()'ing arbitrary code as root, agreed? s/\$name/$username/g; s/\$fullname/$fullname/g; s/\$password/$password/g; print unless /^#/; } close MAIL || die "Error completing mail operation: $0"; select STDOUT; print "DONE!\n"; } print "Logging ... "; $logline = localtime() . " $0 " . "$username (" . (getpwnam($username))[2] . "/" . (getpwnam($username))[3] . ") \"$fullname\""; # $logline = localtime() . " $0 " . "$username/$group (" . (getpwnam($username))[2] . "/" . (getpwnam($username))[3] . ") \"$fullname\""; &append_file($logfile, $logline); print "DONE!\n"; $| = $oldbuf; } sub append_file { my($filename,$line) = @_; open(FH, ">>$filename") or warn "Can't open $logfile: $!"; flock(FH,LOCK_EX); seek(FH, 0, 2); print FH $line, "\n"; close(FH); flock(FH, LOCK_UN); }
Enteruser: A Replacement for AdduserCopyright © 1999 Dannyman
ABSTRACTThis article features a user-friendly script written for the purposes of adding users to a FreeBSD system. The article first explains the rationale for not using a pre-existing script - adduser. It then explains the rationale behind the design of the replacement script - enteruser, and provides an example of its use. The script is written in Perl and the article details an example of how the script could be modified to extend functionality. For this section, the reader should be comfortable hacking Perl. At the conclusion, this article touches on some advanced modifications that could be made to lower levels of the user addition section to make things faster on heavily-populated systems. ACKNOWLEDGMENTSThanks are owed to EnterAct Corp, for making the enteruser source code publicly available. Thanks also to Patrick McCormick of Tellme Networks Inc, for lending me a proof-read, and of course, to Daemon News for featuring this and my previous article. INTRODUCTION: WHY I DON'T LIKE ADDUSERI spent the summer and fall of 1998 working for a Chicago-based Internet Service Provider. On the systems team, I had the opportunity to explore first-hand how well FreeBSD could scale to handle thousands of users. One of the first problems I tackled was the way in which new users were added into the system. When the ISP first started, it was sufficient to run adduser while the customer was on the phone and voila! - they were online. However, there were two problems with running adduser. The first problem is a race condition where if adduser is run multiple times, it will eventually start duplicating user IDs, as a user ID may be chosen in one session while the system is being updated with that already-chosen ID. Adduser relies on pwd_mkdb, which can have some rather freaky race conditions of its own when invoked multiple times, which is another thing multiple sessions of adduser encourage. The second problem is that adduser was not designed with an eye toward adding extra functionality as time passed. As our organization grew, so did the number of things we wanted adduser to do. Logging was extended to help track referrals, for example, and various technical procedures for account setups were introduced. As I hustled around trying to repair the first problem, the second problem became an increasing concern. Something had to be done. And I figured that something was enteruser. ENTERUSERI had a significant advantage over adduser's author, Wolfram Schneider, in that by the time I needed to write enteruser, most of the hard stuff with modifying and rebuilding the user database had already been implemented by the pw command. Pw is designed to add users in one command-line, but also with some functionality in mind when it comes to being incorporated into a script. I talked about pw in May's Daemon News column. With the "hard stuff" out of the way, I was free to concentrate on the interface. After all, enteruser is mostly a glorified interface for pw. So why write enteruser at all when pw has the same functionality? Well, pw doesn't have all the functionality you might need - for example, it doesn't create a public_html directory, or keep a log of its activity. Also, pw is not something you want other employees or colleagues running, as it takes some time to learn and has enough power to do some really scary things to the system. The fewer people with "really scary" access the better. By simplifying the ways in which one might call pw, enteruser makes things easier, safer, and more secure. Along with the interface, I wanted enteruser to be extensible, so the next time someone asked for a feature it could be incorporated without too much fuss. I wrote it in Perl and broke things out into functions wherever possible. I'll delve into this below. Another thing that other employees wanted was a way to enter information for several users at once and then kick back and let the script do its work. Part of the reason for this is because once you have a few thousand users, the process of rebuilding the user database becomes noticeably slower. One of the first extensions I built into enteruser was an additional interface called queueuser. USING ENTERUSERThe version of enteruser featured in this article will work only if you've configured pw. If you want to try enteruser out, either review the section on pw in May's article or steal a friend's pw.conf. I'd share mine but it's unavailable at the moment. Try installing enteruser in your path somewhere, and make it executable. You'll need Perl5 installed in /usr/bin, which is no problem if your system is fairly current. Otherwise, change the shebang (top line) to point at your Perl5, probably /usr/local/bin/perl. And fire it up! 0-20:32 dannyman@stumpy ~> enteruser You must be root to run /home/dannyman/bin/enteruser! 255-20:32 dannyman@stumpy ~> su Password: # enteruser Username: dannyman Ouch - that one's taken already. Username: danny Full Name: Danny D Man Password: [random] dannyman Ewww, no. That password is found in /etc/passwd. This password is inexcusably lame, do you really want it? (Y/n) n Password: [random] Shell: [sh] tcsh Verify User Information ----------------------- username: danny fullname: Danny D Man password: 3FK$VALfmS shell: tcsh Is this okay? (Y/n) Running pw ... DONE! Creating public directories ... DONE! Logging ... DONE! # su danny %cd %ls -a . .login .mailrc .shrc .. .login_conf .profile .xsession .cshrc .mail_aliases .rhosts public_html %tail -1 /var/log/enteruser.log Tue May 4 20:33:28 1999 /home/dannyman/bin/enteruser danny (1013/1013) "Danny D Man"Rocket science it's not. You could also put a welcome message in /etc/adduser.message and it will send that along - just like adduser, only a little more secure. USING QUEUEUSERQueueuser is enteruser. If you run enteruser as a link to queueuser, enteruser will run as queueuser - same script, two different beasts. Using queueuser is almost exactly like using enteruser, because they both rely on the same functions, only queueuser has more: # queueuser Please Select Operation ----------------------- [A]dd a user to the queue [L]ist users in the queue [D]elete user from the queue [P]rocess users in queue [Q]uit this program What would you like to do? q Self-explanatory enough? The first option will prompt for information using the same function used in enteruser. List and delete are useful for finding and removing mistakes. Once you have entered a list of users and are satisfied with them, entering "p" will effectively cause enteruser to be invoked once per user. HACKING ENTERUSERI find the first step to hacking something is to get a printout of it and whatever relevant dependencies it needs, and take that and possibly a good reference book to the bathroom, or some other place where you can dwell with your own thoughts without being disturbed. Of course, if the whole world was like me, we might need more bathrooms. The point is, in an ideal world, you'll take a nice leisurely stroll through a printout of enteruser, and hopefully come to grok it with ease. The The For some fairly instant gratification, go in there and incorporate a feature for setting a user's primary group. This is pretty trivial because the functionality used to be there and I left it in there in commented form. A checklist:
See how easy it is? This checklist should be handy for doing basic feature additions to enteruser even when the subroutine isn't already written. PERFORMANCE MODIFICATIONSIf your system is growing to support several thousand users, you'll start to find that pw runs really slow. This can be modified by tweaking the calls that pw makes to pwd_mkdb. Unfortunately, this involves hacking pw's source code. The story is like this: the more users you have, the slower pwd_mkdb runs. There are two things that will make pwd_mkdb run faster.
It took me several hours of stumbling around the source code trying to figure this stuff out. Unfortunately, the machine I kept my patches on won't have net access until July. I hope to follow this last bit up in next month's issue with the patches in question. If you are eager to tackle this right away, I can say to look into the calls to pwdb() in /usr/src/usr.sbin/pw/pwupd.c. Of course, you'll want to understand how pwd_mkdb works, and this is explained in my previous column.
Dannyman, dannyman@dannyland.org
|
||
To Unsubscribe: send mail to majordomo(at)de.FreeBSD.org
with "unsubscribe de-bsd-questions" in the body of the message
Received on Mon 11 Jun 2001 - 21:44:19 CEST