view make-username.pl @ 5:0979af167267

Removed commented code and timing notes left from I/O testing.
author Eris Caffee <discordia@eldalin.com>
date Mon, 18 Jul 2011 09:37:20 -0500
parents 742a3d2c99d6
children 065d2beb69a5
line source
1 #!/usr/bin/env perl
3 ################################################################################
4 #
5 # Copyright (C) 2011 Sarah Eris Horsley Caffee
6 #
7 # This is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #
20 ################################################################################
21 #
22 # Returns a new unused username for use on a *nix server.
23 #
24 ################################################################################
26 use warnings;
27 use strict;
28 use English;
29 use File::Basename;
31 my $base ="";
32 my $user;
33 my $debug = 0;
34 my $maxlen = 8;
35 my $test = 0;
37 foreach my $arg (@ARGV) {
38 if ("-d" eq $arg) {
39 $debug = 1;
40 } elsif ($arg =~ /^-m/) {
41 $arg =~ s/^..//;
42 if ($arg =~ /^(\d+)$/) {
43 $maxlen = $1;
44 $debug and print "Setting maxlen to $maxlen\n";
45 } else {
46 printf(STDERR "Invalid username length specified: $arg\n");
47 }
48 } elsif ($arg eq "-h") {
49 usage();
50 exit 0;
51 } elsif ($arg eq "-t") {
52 $test = 1;
53 } else {
54 $base = $arg;
55 }
56 }
58 if (! $test) {
59 $user = make_username($base);
60 defined $user and printf("%s\n", $user);
61 exit 0;
62 } else {
63 my $pwdfile = "passwd";
64 $base = "";
65 my $t1 = time;
66 $user = make_username($base, $pwdfile);
67 my $t2 = time;
68 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
70 $base = "eightchr";
71 $t1 = time;
72 $user = make_username($base, $pwdfile);
73 $t2 = time;
74 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
76 $base = "short";
77 $t1 = time;
78 $user = make_username($base, $pwdfile);
79 $t2 = time;
80 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
82 $base = "this *&% is not allowed &^$%&^m";
83 $t1 = time;
84 $user = make_username($base, $pwdfile);
85 $t2 = time;
86 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
88 $base = "123digits";
89 $t1 = time;
90 $user = make_username($base, $pwdfile);
91 $t2 = time;
92 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
94 $base = "this-is-a-domain.com";
95 $t1 = time;
96 $user = make_username($base, $pwdfile);
97 $t2 = time;
98 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
100 $base = '&$%#&$*&$%*&';
101 $t1 = time;
102 $user = make_username($base, $pwdfile);
103 $t2 = time;
104 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
106 $base = "testqwer";
107 $t1 = time;
108 $user = make_username($base, $pwdfile);
109 $t2 = time;
110 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
112 $base = "testabc";
113 $t1 = time;
114 $user = make_username($base, $pwdfile);
115 $t2 = time;
116 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
118 $base = "testzx";
119 $t1 = time;
120 $user = make_username($base, $pwdfile);
121 $t2 = time;
122 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
124 $base = "test";
125 $t1 = time;
126 $user = make_username($base, $pwdfile);
127 $t2 = time;
128 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
130 $base = "t";
131 $t1 = time;
132 $user = make_username($base, $pwdfile);
133 $t2 = time;
134 defined $user and printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
135 }
137 sub make_username {
138 # Arguments:
139 # The string to use as the basis of the new username.
140 # Can be any string (such as an existing username or a domain name)
141 # But at most 8 characters will be used. Invalid characters will
142 # be removed.
143 # If the first character would be a number, an 'a' is prepended to the name.
144 # If blank, the string "user" will be used.
145 #
146 # Returns:
147 # A string containing the new username or undef if no username
148 # could be generated (unlikely)
150 my ($base, $pwdfile) = @_;
152 if ($base eq "") {
153 $base = "user";
154 }
156 ! defined $pwdfile and $pwdfile = "/etc/passwd";
158 if (! -e $pwdfile) {
159 printf(STDERR "Error: non-existant passwd file specified: $pwdfile\n");
160 return undef;
161 }
163 # Remove non-alpha-numeric characters and make sure the first character
164 # is a letter.
165 $base =~ s/[^A-Za-z0-9]//g;
166 if ($base !~ /^[a-z]/ ) {
167 $base = "a".$base;
168 }
170 # Use the specified parameter as the base of the new username.
171 # Print it into a string of exactly 8 characters.
172 # Print the number into the last portion of the new username.
173 # Delete spaces (in case the passed username candidate was short).
174 # Churn until we find an unused name.
175 my $tries=0;
176 my $i=1;
177 my $numstr;
178 my $newuser = sprintf("%.".$maxlen."s", $base);
180 while ( (user_exists($newuser, $pwdfile)) && ($i < 10000000) ) {
181 $tries = 1;
182 $numstr = sprintf("%d", $i);
183 $newuser = sprintf("%-".$maxlen."s", $base);
184 substr($newuser, -length($numstr), length($numstr), $numstr);
185 $newuser =~ s/ //g;
186 $i++;
187 $debug and printf(STDERR "Checking for $newuser\n");
188 }
190 $i == 10000000 and return undef;
191 $debug and print "final is $newuser in $tries attempts\n";
192 return $newuser;
193 }
195 sub user_exists {
196 my ($user, $pwdfile) = @_;
198 my $retval = 0;
199 my $i;
200 my $buffer;
202 open my $handle, "<", $pwdfile;
203 while (sysread($handle, $buffer, 65536, 0)) {
204 if ($buffer =~ /(^|\n)${user}:/) {
205 $retval = 1;
206 last;
207 }
208 }
209 close $handle;
210 return $retval;
211 }
213 ################################################################################
214 sub usage {
215 printf(
216 "Usage: ".basename($PROGRAM_NAME)." [-h] [-d] [-mM] [-t] [basename]
218 Generate a new unused username suitable for assigning to a new account.
220 basename If specified this will be used as the basis of the new username
222 The basename will be stripped of all characters other than letters
223 and digits, and it will be truncated to no more than the maximum
224 allowed username length (8 by default, but settable with the
225 -m option).
226 In the event that a user already exists by the given name a number
227 will be inserted at the end of the username and sequentially
228 increased until an available name is found.
229 If basename is not specified, then \"user\" will be used as the base.
230 -h Print these instructions.
231 -d Enable debug messages.
232 -mM Set maximum allowed username length to M (default is 8).
233 -t Perform test code. You must have a suitable passwd file in the
234 current directory in order to run this.
235 ");
236 }