view make-username.pl @ 9:807d649f3bc9

Tinker, tinker, nothing major.
author Eris Caffee <discordia@eldalin.com>
date Mon, 18 Jul 2011 13:24:25 -0500
parents 542b1fb6759d
children
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 if (! defined $maxlen) {
139 $maxlen = 8;
140 } elsif ($maxlen !~ /^\d+$/) {
141 printf(STDERR "Error: non-numeric username length specified.\n");
142 return undef;
143 }
145 if (! defined $pwdfile) {
146 $pwdfile = "/etc/passwd";
147 } elsif (! -e $pwdfile) {
148 printf(STDERR "Error: non-existant passwd file specified: $pwdfile\n");
149 return undef;
150 }
152 # Remove non-alpha-numeric characters and make sure the first character
153 # is a letter.
154 $base =~ s/[^A-Za-z0-9]//g;
155 if ($base !~ /^[a-z]/ ) {
156 $base = "a".$base;
157 }
159 # Use the specified parameter as the base of the new username.
160 # Print it into a string of exactly $maxlen characters.
161 # Print the number into the last portion of the new username.
162 # Delete spaces (in case the passed username candidate was short).
163 # Churn until we find an unused name.
164 my $i=1;
165 my $numstr;
166 my $newuser = sprintf("%.".$maxlen."s", $base);
167 while ( (user_exists($newuser, $pwdfile)) && ($i < 10**($maxlen-1)) ) {
168 $numstr = sprintf("%d", $i);
169 $newuser = sprintf("%-".$maxlen."s", $base);
170 substr($newuser, -length($numstr), length($numstr), $numstr);
171 $newuser =~ s/ //g;
172 $i++;
173 }
175 $i >= 10**($maxlen-1) and return undef;
176 return $newuser;
177 }
179 ################################################################################
180 sub user_exists {
181 my ($user, $pwdfile) = @_;
183 my $retval = 0;
184 my $i;
185 my $buffer;
187 open my $handle, "<", $pwdfile;
188 while (sysread($handle, $buffer, 65536, 0)) {
189 if ($buffer =~ /(^|\n)${user}:/) {
190 $retval = 1;
191 last;
192 }
193 }
194 close $handle;
195 return $retval;
196 }
198 ################################################################################
199 sub usage {
200 printf(
201 "Usage: ".basename($PROGRAM_NAME)." [-h] [-d] [-mM] [-t] [basename]
203 Generate a new unused username suitable for assigning to a new account.
205 basename If specified this will be used as the basis of the new username
207 The basename will be stripped of all characters other than letters
208 and digits, and it will be truncated to no more than the maximum
209 allowed username length (8 by default, but settable with the
210 -m option).
211 In the event that a user already exists by the given name a number
212 will be inserted at the end of the username and sequentially
213 increased until an available name is found.
214 If basename is not specified, then \"user\" will be used as the base.
215 -h Print these instructions.
216 -d Enable debug messages.
217 -mM Set maximum allowed username length to M (default is 8).
218 -t Perform test code. You must have a suitable passwd file in the
219 current directory in order to run this.
220 ");
221 }