view make-username.pl @ 8:542b1fb6759d

Removed debug printfs
author Eris Caffee <discordia@eldalin.com>
date Mon, 18 Jul 2011 13:08:21 -0500
parents 462ba4c70c65
children 807d649f3bc9
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 # main is a block. NO GLOBALS!
32 {
33 my $base ="";
34 my $user;
35 my $debug = 0;
36 my $maxlen = 8;
37 my $test = 0;
39 foreach my $arg (@ARGV) {
40 if ("-d" eq $arg) {
41 $debug = 1;
42 } elsif ($arg =~ /^-m/) {
43 $arg =~ s/^..//;
44 if ($arg =~ /^(\d+)$/) {
45 $maxlen = $1;
46 $debug and print "Setting maxlen to $maxlen\n";
47 } else {
48 printf(STDERR "Invalid username length specified: $arg\n");
49 }
50 } elsif ($arg eq "-h") {
51 usage();
52 exit 0;
53 } elsif ($arg eq "-t") {
54 $test = 1;
55 } else {
56 $base = $arg;
57 }
58 }
60 if (! $test) {
61 $user = make_username($base, $maxlen);
62 if (defined $user) {
63 printf("%s\n", $user);
64 } else {
65 printf(STDERR "No username available\n");
66 }
67 exit 0;
68 } else {
69 my $pwdfile = "passwd";
70 my ($base, $t1, $t2);
71 my @bases = ("", # should use default "user" as base. user and user1 taken
72 "eightchr",
73 "short",
74 "this *&% is not allowed &^$%&^m",
75 "123digits",
76 "this-is-a-domain.com",
77 '&$%#&$*&$%*&',
78 "testqwer", # testqwer taken
79 "testabc", # all testabc? taken
80 "testzx", # all testzx?? taken
81 "test", # all test??? taken
82 "t" # all z????? taken though 10435
83 );
85 foreach $base (@bases) {
86 $t1 = time;
87 $user = make_username($base, $maxlen, $pwdfile);
88 $t2 = time;
89 if (defined $user) {
90 printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
91 } else {
92 printf("%30s No username available\n", $base, $t2 - $t1);
93 }
94 }
96 $maxlen = 2;
97 $base = "z"; # all z? taken. Should return no username.
98 $t1 = time;
99 $user = make_username($base, $maxlen, $pwdfile);
100 $t2 = time;
101 if (defined $user) {
102 printf("%30s %-".$maxlen."s in %ds\n", $base, $user, $t2 - $t1);
103 } else {
104 printf("%30s No username available\n", $base, $t2 - $t1);
105 }
107 }
109 }
110 exit 0;
112 ################################################################################
113 sub make_username {
114 # Arguments:
115 # $base The string to use as the basis of the new username.
116 # Can be any string (such as an existing username or a domain name)
117 # But at most $maxlen characters will be used. Invalid characters will
118 # be removed.
119 # If the first character would be a number, an 'a' is prepended to the name.
120 # If blank, the string "user" will be used.
121 # $maxlen The maximum length of the username to create. If not specified,
122 # it defaults to 8. It must be 2 or more. (If it was 1 then there
123 # would be no way to prepend a number to it without having a digit as
124 # the first character of the new name.)
125 # $pwdfile The name of the passwd file to use for checking for new users.
126 # /etc/passwd by default. This is really a debugging option.
127 #
128 # Returns:
129 # A string containing the new username or undef if no username
130 # could be generated (unlikely)
132 my ($base, $maxlen, $pwdfile) = @_;
134 if ($base eq "") {
135 $base = "user";
136 }
138 ! defined $pwdfile and $pwdfile = "/etc/passwd";
140 if (! defined $maxlen) {
141 $maxlen = 8;
142 } else {
143 if ($maxlen !~ /^\d+$/) {
144 printf(STDERR "Error: non-numeric username length specified.\n");
145 return undef;
146 }
147 }
149 if (! -e $pwdfile) {
150 printf(STDERR "Error: non-existant passwd file specified: $pwdfile\n");
151 return undef;
152 }
154 # Remove non-alpha-numeric characters and make sure the first character
155 # is a letter.
156 $base =~ s/[^A-Za-z0-9]//g;
157 if ($base !~ /^[a-z]/ ) {
158 $base = "a".$base;
159 }
161 # Use the specified parameter as the base of the new username.
162 # Print it into a string of exactly $maxlen characters.
163 # Print the number into the last portion of the new username.
164 # Delete spaces (in case the passed username candidate was short).
165 # Churn until we find an unused name.
166 my $i=1;
167 my $numstr;
168 my $newuser = sprintf("%.".$maxlen."s", $base);
169 while ( (user_exists($newuser, $pwdfile)) && ($i < 10**($maxlen-1)) ) {
170 $numstr = sprintf("%d", $i);
171 $newuser = sprintf("%-".$maxlen."s", $base);
172 substr($newuser, -length($numstr), length($numstr), $numstr);
173 $newuser =~ s/ //g;
174 $i++;
175 }
177 $i >= 10**($maxlen-1) and return undef;
178 return $newuser;
179 }
181 ################################################################################
182 sub user_exists {
183 my ($user, $pwdfile) = @_;
185 my $retval = 0;
186 my $i;
187 my $buffer;
189 open my $handle, "<", $pwdfile;
190 while (sysread($handle, $buffer, 65536, 0)) {
191 if ($buffer =~ /(^|\n)${user}:/) {
192 $retval = 1;
193 last;
194 }
195 }
196 close $handle;
197 return $retval;
198 }
200 ################################################################################
201 sub usage {
202 printf(
203 "Usage: ".basename($PROGRAM_NAME)." [-h] [-d] [-mM] [-t] [basename]
205 Generate a new unused username suitable for assigning to a new account.
207 basename If specified this will be used as the basis of the new username
209 The basename will be stripped of all characters other than letters
210 and digits, and it will be truncated to no more than the maximum
211 allowed username length (8 by default, but settable with the
212 -m option).
213 In the event that a user already exists by the given name a number
214 will be inserted at the end of the username and sequentially
215 increased until an available name is found.
216 If basename is not specified, then \"user\" will be used as the base.
217 -h Print these instructions.
218 -d Enable debug messages.
219 -mM Set maximum allowed username length to M (default is 8).
220 -t Perform test code. You must have a suitable passwd file in the
221 current directory in order to run this.
222 ");
223 }