# HG changeset patch # User Eris Caffee # Date 1310990476 18000 # Node ID fa7be81d0e336a46ab2c3c7abf42d438c4d3a481 # Parent 706c20861682d2f1885cc435fd9919fc9d83e914 Many changes: Made generic, not cPanel specific. Reads /etc/passwd using fast I/O to check for existing users. Converted to standalone command line utility with several options: -h -d -t -m diff -r 706c20861682 -r fa7be81d0e33 make-username.pl --- a/make-username.pl Sat Jul 16 13:23:21 2011 -0500 +++ b/make-username.pl Mon Jul 18 07:01:16 2011 -0500 @@ -1,72 +1,123 @@ #!/usr/bin/env perl +################################################################################ +# +# Creates a new unused username for use on a cPanel server. +# +################################################################################ + +use 5.010; + use warnings; use strict; +use English; +use File::Basename; -my $base; -my $user ; +my $base =""; +my $user; +my $debug = 0; +my $maxlen = 8; +my $test = 0; -$base = ""; -$user = make_username($base); -printf("%30s %s\n", $base, $user); +foreach my $arg (@ARGV) { + if ("-d" eq $arg) { + $debug = 1; + } elsif ($arg =~ /^-m/) { + $arg =~ s/^..//; + if ($arg =~ /^(\d+)$/) { + $maxlen = $1; + $debug and print "Setting maxlen to $maxlen\n"; + } else { + printf(STDERR "Invalid username length specified: $arg\n"); + } + } elsif ($arg eq "-h") { + usage(); + exit 0; + } elsif ($arg eq "-t") { + $test = 1; + } else { + $base = $arg; + } +} -$base = "eightchr"; -$user = make_username($base); -printf("%30s %s\n", $base, $user); +if (! $test) { + $user = make_username($base); + defined $user and printf("%s\n", $user); + exit 0; +} else { + my $pwdfile = "passwd"; + $base = ""; + my $t1 = time; + $user = make_username($base, $pwdfile); + my $t2 = time; + defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1); + + $base = "eightchr"; + $t1 = time; + $user = make_username($base, $pwdfile); + $t2 = time; + defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1); + + $base = "short"; + $t1 = time; + $user = make_username($base, $pwdfile); + $t2 = time; + defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1); + + $base = "this *&% is not allowed &^$%&^m"; + $t1 = time; + $user = make_username($base, $pwdfile); + $t2 = time; + defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1); -$base = "short"; -$user = make_username($base); -printf("%30s %s\n", $base, $user); + $base = "123digits"; + $t1 = time; + $user = make_username($base, $pwdfile); + $t2 = time; + defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1); -$base = "this *&% is not allowed &^$%&^m"; -$user = make_username($base); -printf("%30s %s\n", $base, $user); + $base = "this-is-a-domain.com"; + $t1 = time; + $user = make_username($base, $pwdfile); + $t2 = time; + defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1); -$base = "123digits"; -$user = make_username($base); -printf("%30s %s\n", $base, $user); + $base = '&$%#&$*&$%*&'; + $t1 = time; + $user = make_username($base, $pwdfile); + $t2 = time; + defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1); -$base = "this-is-a-domain.com"; -$user = make_username($base); -printf("%30s %s\n", $base, $user); + $base = "testqwer"; + $t1 = time; + $user = make_username($base, $pwdfile); + $t2 = time; + defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1); -$base = '&$%#&$*&$%*&'; -$user = make_username($base); -printf("%30s %s\n", $base, $user); + $base = "testabc"; + $t1 = time; + $user = make_username($base, $pwdfile); + $t2 = time; + defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1); -# For the next test, run this first in the shell as root (not on a production system, obviously!): -# -# [ ! -d /var/cpanel/users ] && mkdir -p /var/cpanel/users -# touch /var/cpanel/users/testqwer -# touch /var/cpanel/users/testabc -# i=1 ; while [ $i -lt 10 ] ; do touch /var/cpanel/users/testabc${i} ; let i=$i+1 ; done -# touch /var/cpanel/users/testzx -# i=1 ; while [ $i -lt 100 ] ; do touch /var/cpanel/users/testzx${i} ; let i=$i+1 ; done -# touch /var/cpanel/users/test -# i=1 ; while [ $i -lt 100 ] ; do touch /var/cpanel/users/test${i} ; let i=$i+1 ; done -# touch /var/cpanel/users/t -# i=1 ; while [ $i -lt 10346 ] ; do touch /var/cpanel/users/t${i} ; let i=$i+1 ; done + $base = "testzx"; + $t1 = time; + $user = make_username($base, $pwdfile); + $t2 = time; + defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1); -$base = "testqwer"; -$user = make_username($base); -printf("%30s %s\n", $base, $user); + $base = "test"; + $t1 = time; + $user = make_username($base, $pwdfile); + $t2 = time; + defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1); -$base = "testabc"; -$user = make_username($base); -printf("%30s %s\n", $base, $user); - -$base = "testzx"; -$user = make_username($base); -printf("%30s %s\n", $base, $user); - -$base = "test"; -$user = make_username($base); -printf("%30s %s\n", $base, $user); - -$base = "t"; -$user = make_username($base); -printf("%30s %s\n", $base, $user); - + $base = "t"; + $t1 = time; + $user = make_username($base, $pwdfile); + $t2 = time; + defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1); +} sub make_username { # Arguments: @@ -81,12 +132,19 @@ # A string containing the new username or undef if no username # could be generated (unlikely) - my ($base) = @_; + my ($base, $pwdfile) = @_; if ($base eq "") { $base = "user"; } + ! defined $pwdfile and $pwdfile = "/etc/passwd"; + + if (! -e $pwdfile) { + printf(STDERR "Error: non-existant passwd file specified: $pwdfile\n"); + return undef; + } + # Remove non-alpha-numeric characters and make sure the first character is a letter. $base =~ s/[^A-Za-z0-9]//g; if ($base !~ /^[a-z]/ ) { @@ -98,17 +156,91 @@ # Print the number into the last portion of the new username. # Delete spaces (in case the passed username candidate was short). # Churn until we find an unused name. + my $tries=0; my $i=1; my $numstr; - my $newuser = sprintf("%.8s", $base); - while ( (-e "/var/cpanel/users/$newuser") && ($i < 10000000) ) { + my $newuser = sprintf("%.".$maxlen."s", $base); + + + # ----- Times: 88s, 89s, 85s ----- + # open(F,$pwdfile); + # my @passwd=; + # close F; + # while ((grep {/^${newuser}:/ } @passwd) && ($i < 10000000) ) { + + # ----- Times: 67s, 67s, 67s ----- + # while ( (! system('grep -qsP "^'.$newuser.':" '.$pwdfile)) && ($i < 10000000) ) { + + # ----- with user_exists1() Times: 47s, 50s, 50s ----- + # ----- with user_exists() using sysread Times: 4s, 4s, 4s ----- + while ( (user_exists($newuser, $pwdfile)) && ($i < 10000000) ) { + $tries = 1; $numstr = sprintf("%d", $i); - $newuser = sprintf("%-8s", $base); + $newuser = sprintf("%-".$maxlen."s", $base); substr($newuser, -length($numstr), length($numstr), $numstr); $newuser =~ s/ //g; $i++; + $debug and printf(STDERR "Checking for $newuser\n"); } - $i == 10000000 && return undef; + $i == 10000000 and return undef; + $debug and print "final is $newuser in $tries attempts\n"; return $newuser; } + +sub user_exists { + my ($user, $pwdfile) = @_; + + my $retval = 0; + my $i; + my $buffer; + + open my $handle, "<", $pwdfile; + while (sysread($handle, $buffer, 65536, 0)) { + if ($buffer =~ /(^|\n)${user}:/) { + $retval = 1; + last; + } + } + close $handle; + return $retval; +} + +sub user_exists1 { + my ($user, $pwdfile) = @_; + my $retval = 0; + open my $handle, "<", $pwdfile; + while (<$handle>) { + if ($_ =~ /^${user}:/) { + $retval = 1; + last; + } + } + close $handle; + return $retval; +} + +################################################################################ +sub usage { + printf( +"Usage: ".basename($PROGRAM_NAME)." [-h] [-d] [-mM] [-t] [basename] + +Generate a new unused username suitable for assigning to a new account. + +basename If specified this will be used as the basis of the new username + + The basename will be stripped of all characters other than letters + and digits, and it will be truncated to no more than the maximum + allowed username length (8 by default, but settable with the + -m option). + In the event that a user already exists by the given name a number + will be inserted at the end of the username and sequentially + increased until an available name is found. + If basename is not specified, then \"user\" will be used as the base. +-h Print these instructions. +-d Enable debug messages. +-mM Set maximum allowed username length to M (default is 8). +-t Perform test code. You must have a suitable passwd file in the + current directory in order to run this. +"); +} diff -r 706c20861682 -r fa7be81d0e33 passwd-huge.gz Binary file passwd-huge.gz has changed diff -r 706c20861682 -r fa7be81d0e33 passwd.gz Binary file passwd.gz has changed