changeset 3:fa7be81d0e33

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
author Eris Caffee <discordia@eldalin.com>
date Mon, 18 Jul 2011 07:01:16 -0500
parents 706c20861682
children
files make-username.pl passwd-huge.gz passwd.gz
diffstat 3 files changed, 191 insertions(+), 59 deletions(-) [+]
line diff
     1.1 --- a/make-username.pl	Sat Jul 16 13:23:21 2011 -0500
     1.2 +++ b/make-username.pl	Mon Jul 18 07:01:16 2011 -0500
     1.3 @@ -1,72 +1,123 @@
     1.4  #!/usr/bin/env perl
     1.5  
     1.6 +################################################################################
     1.7 +#
     1.8 +# Creates a new unused username for use on a cPanel server.
     1.9 +#
    1.10 +################################################################################
    1.11 +
    1.12 +use 5.010;
    1.13 +
    1.14  use warnings;
    1.15  use strict;
    1.16 +use English;
    1.17 +use File::Basename;
    1.18  
    1.19 -my $base;
    1.20 -my $user ;
    1.21 +my $base ="";
    1.22 +my $user;
    1.23 +my $debug = 0;
    1.24 +my $maxlen = 8;
    1.25 +my $test = 0;
    1.26  
    1.27 -$base = "";
    1.28 -$user = make_username($base);
    1.29 -printf("%30s %s\n", $base, $user);
    1.30 +foreach my $arg (@ARGV) {
    1.31 +    if ("-d" eq $arg) {
    1.32 +	$debug = 1;
    1.33 +    } elsif ($arg =~ /^-m/) {
    1.34 +	$arg =~ s/^..//;
    1.35 +	if ($arg =~ /^(\d+)$/) {
    1.36 +	    $maxlen = $1;
    1.37 +	    $debug and print "Setting maxlen to $maxlen\n";
    1.38 +	} else {
    1.39 +	    printf(STDERR "Invalid username length specified: $arg\n");
    1.40 +	}
    1.41 +    } elsif ($arg eq "-h") {
    1.42 +	usage();
    1.43 +	exit 0;
    1.44 +    } elsif ($arg eq "-t") {
    1.45 +	$test = 1;
    1.46 +    } else {
    1.47 +	$base = $arg;
    1.48 +    }
    1.49 +}
    1.50  
    1.51 -$base = "eightchr";
    1.52 -$user = make_username($base);
    1.53 -printf("%30s %s\n", $base, $user);
    1.54 +if (! $test) {
    1.55 +    $user = make_username($base);
    1.56 +    defined $user and printf("%s\n", $user);
    1.57 +    exit 0;
    1.58 +} else {
    1.59 +    my $pwdfile = "passwd";
    1.60 +    $base = "";
    1.61 +    my $t1 = time;
    1.62 +    $user = make_username($base, $pwdfile);
    1.63 +    my $t2 = time;
    1.64 +    defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
    1.65 +    
    1.66 +    $base = "eightchr";
    1.67 +    $t1 = time;
    1.68 +    $user = make_username($base, $pwdfile);
    1.69 +    $t2 = time;
    1.70 +    defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
    1.71 +    
    1.72 +    $base = "short";
    1.73 +    $t1 = time;
    1.74 +    $user = make_username($base, $pwdfile);
    1.75 +    $t2 = time;
    1.76 +    defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
    1.77 +    
    1.78 +    $base = "this *&% is not allowed &^$%&^m";
    1.79 +    $t1 = time;
    1.80 +    $user = make_username($base, $pwdfile);
    1.81 +    $t2 = time;
    1.82 +    defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
    1.83  
    1.84 -$base = "short";
    1.85 -$user = make_username($base);
    1.86 -printf("%30s %s\n", $base, $user);
    1.87 +    $base = "123digits";
    1.88 +    $t1 = time;
    1.89 +    $user = make_username($base, $pwdfile);
    1.90 +    $t2 = time;
    1.91 +    defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
    1.92  
    1.93 -$base = "this *&% is not allowed &^$%&^m";
    1.94 -$user = make_username($base);
    1.95 -printf("%30s %s\n", $base, $user);
    1.96 +    $base = "this-is-a-domain.com";
    1.97 +    $t1 = time;
    1.98 +    $user = make_username($base, $pwdfile);
    1.99 +    $t2 = time;
   1.100 +    defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
   1.101  
   1.102 -$base = "123digits";
   1.103 -$user = make_username($base);
   1.104 -printf("%30s %s\n", $base, $user);
   1.105 +    $base = '&$%#&$*&$%*&';
   1.106 +    $t1 = time;
   1.107 +    $user = make_username($base, $pwdfile);
   1.108 +    $t2 = time;
   1.109 +    defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
   1.110  
   1.111 -$base = "this-is-a-domain.com";
   1.112 -$user = make_username($base);
   1.113 -printf("%30s %s\n", $base, $user);
   1.114 +    $base = "testqwer";
   1.115 +    $t1 = time;
   1.116 +    $user = make_username($base, $pwdfile);
   1.117 +    $t2 = time;
   1.118 +    defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
   1.119  
   1.120 -$base = '&$%#&$*&$%*&';
   1.121 -$user = make_username($base);
   1.122 -printf("%30s %s\n", $base, $user);
   1.123 +    $base = "testabc";
   1.124 +    $t1 = time;
   1.125 +    $user = make_username($base, $pwdfile);
   1.126 +    $t2 = time;
   1.127 +    defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
   1.128  
   1.129 -# For the next test, run this first in the shell as root (not on a production system, obviously!):
   1.130 -#
   1.131 -# [ ! -d /var/cpanel/users ] && mkdir -p /var/cpanel/users
   1.132 -# touch /var/cpanel/users/testqwer
   1.133 -# touch /var/cpanel/users/testabc
   1.134 -# i=1 ; while [ $i -lt 10 ] ; do touch /var/cpanel/users/testabc${i} ; let i=$i+1 ; done
   1.135 -# touch /var/cpanel/users/testzx
   1.136 -# i=1 ; while [ $i -lt 100 ] ; do touch /var/cpanel/users/testzx${i} ; let i=$i+1 ; done
   1.137 -# touch /var/cpanel/users/test
   1.138 -# i=1 ; while [ $i -lt 100 ] ; do touch /var/cpanel/users/test${i} ; let i=$i+1 ; done
   1.139 -# touch /var/cpanel/users/t
   1.140 -# i=1 ; while [ $i -lt 10346 ] ; do touch /var/cpanel/users/t${i} ; let i=$i+1 ; done
   1.141 +    $base = "testzx";
   1.142 +    $t1 = time;
   1.143 +    $user = make_username($base, $pwdfile);
   1.144 +    $t2 = time;
   1.145 +    defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
   1.146  
   1.147 -$base = "testqwer";
   1.148 -$user = make_username($base);
   1.149 -printf("%30s %s\n", $base, $user);
   1.150 +    $base = "test";
   1.151 +    $t1 = time;
   1.152 +    $user = make_username($base, $pwdfile);
   1.153 +    $t2 = time;
   1.154 +    defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
   1.155  
   1.156 -$base = "testabc";
   1.157 -$user = make_username($base);
   1.158 -printf("%30s %s\n", $base, $user);
   1.159 -
   1.160 -$base = "testzx";
   1.161 -$user = make_username($base);
   1.162 -printf("%30s %s\n", $base, $user);
   1.163 -
   1.164 -$base = "test";
   1.165 -$user = make_username($base);
   1.166 -printf("%30s %s\n", $base, $user);
   1.167 -
   1.168 -$base = "t";
   1.169 -$user = make_username($base);
   1.170 -printf("%30s %s\n", $base, $user);
   1.171 -
   1.172 +    $base = "t";
   1.173 +    $t1 = time;
   1.174 +    $user = make_username($base, $pwdfile);
   1.175 +    $t2 = time;
   1.176 +    defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
   1.177 +}
   1.178  
   1.179  sub make_username {
   1.180      # Arguments:
   1.181 @@ -81,12 +132,19 @@
   1.182      # A string containing the new username or undef if no username 
   1.183      # could be generated  (unlikely)
   1.184  
   1.185 -    my ($base) = @_;
   1.186 +    my ($base, $pwdfile) = @_;
   1.187  
   1.188      if ($base eq "") {
   1.189  	$base = "user";
   1.190      }
   1.191  
   1.192 +    ! defined $pwdfile and $pwdfile = "/etc/passwd";
   1.193 +
   1.194 +    if (! -e $pwdfile) {
   1.195 +	printf(STDERR "Error: non-existant passwd file specified: $pwdfile\n");
   1.196 +	return undef;
   1.197 +    }
   1.198 +
   1.199      # Remove non-alpha-numeric characters and make sure the first character is a letter.
   1.200      $base =~ s/[^A-Za-z0-9]//g;
   1.201      if ($base !~ /^[a-z]/ ) { 
   1.202 @@ -98,17 +156,91 @@
   1.203      # Print the number into the last portion of the new username.
   1.204      # Delete spaces (in case the passed username candidate was short).
   1.205      # Churn until we find an unused name.
   1.206 +    my $tries=0;
   1.207      my $i=1;
   1.208      my $numstr;
   1.209 -    my $newuser = sprintf("%.8s", $base);
   1.210 -    while ( (-e "/var/cpanel/users/$newuser") && ($i < 10000000) ) { 
   1.211 +    my $newuser = sprintf("%.".$maxlen."s", $base);
   1.212 +
   1.213 +
   1.214 +    # ----- Times: 88s, 89s, 85s -----
   1.215 +    # open(F,$pwdfile);
   1.216 +    # my @passwd=<F>;
   1.217 +    # close F;
   1.218 +    # while ((grep {/^${newuser}:/ } @passwd) && ($i < 10000000) ) {
   1.219 +
   1.220 +    # ----- Times: 67s, 67s, 67s -----
   1.221 +    # while ( (! system('grep -qsP "^'.$newuser.':" '.$pwdfile)) && ($i < 10000000) ) { 
   1.222 +
   1.223 +    # ----- with user_exists1() Times: 47s, 50s, 50s -----
   1.224 +    # ----- with user_exists() using sysread Times: 4s, 4s, 4s -----
   1.225 +    while ( (user_exists($newuser, $pwdfile)) && ($i < 10000000) ) { 
   1.226 +	$tries = 1;
   1.227  	$numstr = sprintf("%d", $i);
   1.228 -	$newuser = sprintf("%-8s", $base);
   1.229 +	$newuser = sprintf("%-".$maxlen."s", $base);
   1.230  	substr($newuser, -length($numstr), length($numstr), $numstr);
   1.231  	$newuser =~ s/ //g;
   1.232          $i++;
   1.233 +	$debug and printf(STDERR "Checking for $newuser\n");
   1.234      }
   1.235  
   1.236 -    $i == 10000000 && return undef;
   1.237 +    $i == 10000000 and return undef;
   1.238 +    $debug and print "final is $newuser in $tries attempts\n";
   1.239      return $newuser;
   1.240  }
   1.241 +
   1.242 +sub user_exists {
   1.243 +    my ($user, $pwdfile) = @_;
   1.244 +
   1.245 +    my $retval = 0;
   1.246 +    my $i;
   1.247 +    my $buffer;
   1.248 +
   1.249 +    open my $handle, "<", $pwdfile;
   1.250 +    while (sysread($handle, $buffer, 65536, 0)) {
   1.251 +	if ($buffer =~ /(^|\n)${user}:/) {
   1.252 +	    $retval = 1;
   1.253 +	    last;
   1.254 +	}
   1.255 +    }
   1.256 +    close $handle;
   1.257 +    return $retval;
   1.258 +}
   1.259 +
   1.260 +sub user_exists1 {
   1.261 +    my ($user, $pwdfile) = @_;
   1.262 +    my $retval = 0;
   1.263 +    open my $handle, "<", $pwdfile;
   1.264 +    while (<$handle>) {
   1.265 +	if ($_ =~ /^${user}:/) {
   1.266 +	    $retval = 1;
   1.267 +	    last;
   1.268 +	}
   1.269 +    }
   1.270 +    close $handle;
   1.271 +    return $retval;
   1.272 +}
   1.273 +
   1.274 +################################################################################
   1.275 +sub usage {
   1.276 +    printf( 
   1.277 +"Usage: ".basename($PROGRAM_NAME)." [-h] [-d] [-mM] [-t] [basename]
   1.278 +
   1.279 +Generate a new unused username suitable for assigning to a new account.
   1.280 +
   1.281 +basename    If specified this will be used as the basis of the new username
   1.282 +
   1.283 +            The basename will be stripped of all characters other than letters 
   1.284 +            and digits, and it will be truncated to no more than the maximum 
   1.285 +            allowed username length (8 by default, but settable with the 
   1.286 +            -m option).
   1.287 +            In the event that a user already exists by the given name a number 
   1.288 +            will be inserted at the end of the username and sequentially 
   1.289 +            increased until an available name is found.
   1.290 +            If basename is not specified, then \"user\" will be used as the base.
   1.291 +-h          Print these instructions.
   1.292 +-d          Enable debug messages.
   1.293 +-mM         Set maximum allowed username length to M (default is 8).
   1.294 +-t          Perform test code.  You must have a suitable passwd file in the 
   1.295 +            current directory in order to run this.
   1.296 +");
   1.297 +}
     2.1 Binary file passwd-huge.gz has changed
     3.1 Binary file passwd.gz has changed