view make-username.pl @ 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
line source
1 #!/usr/bin/env perl
3 ################################################################################
4 #
5 # Creates a new unused username for use on a cPanel server.
6 #
7 ################################################################################
9 use 5.010;
11 use warnings;
12 use strict;
13 use English;
14 use File::Basename;
16 my $base ="";
17 my $user;
18 my $debug = 0;
19 my $maxlen = 8;
20 my $test = 0;
22 foreach my $arg (@ARGV) {
23 if ("-d" eq $arg) {
24 $debug = 1;
25 } elsif ($arg =~ /^-m/) {
26 $arg =~ s/^..//;
27 if ($arg =~ /^(\d+)$/) {
28 $maxlen = $1;
29 $debug and print "Setting maxlen to $maxlen\n";
30 } else {
31 printf(STDERR "Invalid username length specified: $arg\n");
32 }
33 } elsif ($arg eq "-h") {
34 usage();
35 exit 0;
36 } elsif ($arg eq "-t") {
37 $test = 1;
38 } else {
39 $base = $arg;
40 }
41 }
43 if (! $test) {
44 $user = make_username($base);
45 defined $user and printf("%s\n", $user);
46 exit 0;
47 } else {
48 my $pwdfile = "passwd";
49 $base = "";
50 my $t1 = time;
51 $user = make_username($base, $pwdfile);
52 my $t2 = time;
53 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
55 $base = "eightchr";
56 $t1 = time;
57 $user = make_username($base, $pwdfile);
58 $t2 = time;
59 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
61 $base = "short";
62 $t1 = time;
63 $user = make_username($base, $pwdfile);
64 $t2 = time;
65 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
67 $base = "this *&% is not allowed &^$%&^m";
68 $t1 = time;
69 $user = make_username($base, $pwdfile);
70 $t2 = time;
71 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
73 $base = "123digits";
74 $t1 = time;
75 $user = make_username($base, $pwdfile);
76 $t2 = time;
77 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
79 $base = "this-is-a-domain.com";
80 $t1 = time;
81 $user = make_username($base, $pwdfile);
82 $t2 = time;
83 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
85 $base = '&$%#&$*&$%*&';
86 $t1 = time;
87 $user = make_username($base, $pwdfile);
88 $t2 = time;
89 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
91 $base = "testqwer";
92 $t1 = time;
93 $user = make_username($base, $pwdfile);
94 $t2 = time;
95 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
97 $base = "testabc";
98 $t1 = time;
99 $user = make_username($base, $pwdfile);
100 $t2 = time;
101 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
103 $base = "testzx";
104 $t1 = time;
105 $user = make_username($base, $pwdfile);
106 $t2 = time;
107 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
109 $base = "test";
110 $t1 = time;
111 $user = make_username($base, $pwdfile);
112 $t2 = time;
113 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
115 $base = "t";
116 $t1 = time;
117 $user = make_username($base, $pwdfile);
118 $t2 = time;
119 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
120 }
122 sub make_username {
123 # Arguments:
124 # The string to use as the basis of the new username.
125 # Can be any string (such as an existing username or a domain name)
126 # But at most 8 characters will be used. Invalid characters will
127 # be removed.
128 # If the first character would be a number, an 'a' is prepended to the name.
129 # If blank, the string "user" will be used.
130 #
131 # Returns:
132 # A string containing the new username or undef if no username
133 # could be generated (unlikely)
135 my ($base, $pwdfile) = @_;
137 if ($base eq "") {
138 $base = "user";
139 }
141 ! defined $pwdfile and $pwdfile = "/etc/passwd";
143 if (! -e $pwdfile) {
144 printf(STDERR "Error: non-existant passwd file specified: $pwdfile\n");
145 return undef;
146 }
148 # Remove non-alpha-numeric characters and make sure the first character is a letter.
149 $base =~ s/[^A-Za-z0-9]//g;
150 if ($base !~ /^[a-z]/ ) {
151 $base = "a".$base;
152 }
154 # Use the specified parameter as the base of the new username.
155 # Print it into a string of exactly 8 characters.
156 # Print the number into the last portion of the new username.
157 # Delete spaces (in case the passed username candidate was short).
158 # Churn until we find an unused name.
159 my $tries=0;
160 my $i=1;
161 my $numstr;
162 my $newuser = sprintf("%.".$maxlen."s", $base);
165 # ----- Times: 88s, 89s, 85s -----
166 # open(F,$pwdfile);
167 # my @passwd=<F>;
168 # close F;
169 # while ((grep {/^${newuser}:/ } @passwd) && ($i < 10000000) ) {
171 # ----- Times: 67s, 67s, 67s -----
172 # while ( (! system('grep -qsP "^'.$newuser.':" '.$pwdfile)) && ($i < 10000000) ) {
174 # ----- with user_exists1() Times: 47s, 50s, 50s -----
175 # ----- with user_exists() using sysread Times: 4s, 4s, 4s -----
176 while ( (user_exists($newuser, $pwdfile)) && ($i < 10000000) ) {
177 $tries = 1;
178 $numstr = sprintf("%d", $i);
179 $newuser = sprintf("%-".$maxlen."s", $base);
180 substr($newuser, -length($numstr), length($numstr), $numstr);
181 $newuser =~ s/ //g;
182 $i++;
183 $debug and printf(STDERR "Checking for $newuser\n");
184 }
186 $i == 10000000 and return undef;
187 $debug and print "final is $newuser in $tries attempts\n";
188 return $newuser;
189 }
191 sub user_exists {
192 my ($user, $pwdfile) = @_;
194 my $retval = 0;
195 my $i;
196 my $buffer;
198 open my $handle, "<", $pwdfile;
199 while (sysread($handle, $buffer, 65536, 0)) {
200 if ($buffer =~ /(^|\n)${user}:/) {
201 $retval = 1;
202 last;
203 }
204 }
205 close $handle;
206 return $retval;
207 }
209 sub user_exists1 {
210 my ($user, $pwdfile) = @_;
211 my $retval = 0;
212 open my $handle, "<", $pwdfile;
213 while (<$handle>) {
214 if ($_ =~ /^${user}:/) {
215 $retval = 1;
216 last;
217 }
218 }
219 close $handle;
220 return $retval;
221 }
223 ################################################################################
224 sub usage {
225 printf(
226 "Usage: ".basename($PROGRAM_NAME)." [-h] [-d] [-mM] [-t] [basename]
228 Generate a new unused username suitable for assigning to a new account.
230 basename If specified this will be used as the basis of the new username
232 The basename will be stripped of all characters other than letters
233 and digits, and it will be truncated to no more than the maximum
234 allowed username length (8 by default, but settable with the
235 -m option).
236 In the event that a user already exists by the given name a number
237 will be inserted at the end of the username and sequentially
238 increased until an available name is found.
239 If basename is not specified, then \"user\" will be used as the base.
240 -h Print these instructions.
241 -d Enable debug messages.
242 -mM Set maximum allowed username length to M (default is 8).
243 -t Perform test code. You must have a suitable passwd file in the
244 current directory in order to run this.
245 ");
246 }