#!/usr/bin/perl -wT my $ID = q$Id: localuserinfo,v 1.7 2007-02-07 15:08:09 jonas Exp $; # # localuserinfo -- List fullname for each user # # Written by Jonas Smedegaard # Copyright 2003-2007 Jonas Smedegaard # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # TODO: Options to suppress infochunks # use strict; use Getopt::Long; use User::pwent; use User::grent; our $verbose = 1; # should we be verbose? my $ignore_badname = 0; # should we ignore bad names? my $include_username = 1; my $include_fullname = 1; my $include_office = 0; my $include_officehints = 1; my $include_workphone = 1; my $include_homephone = 1; my $include_other = 0; my $include_addresshints = 1; my $include_mail = 0; my $include_groupname = 0; my $include_members = 0; my $include_groups = 0; my $include_grouphints = 1; our $custom_template; our @names; # Resolve version number from CVS id my $version = join (' ', (split (' ', $ID))[1..3]); $version =~ s/,v\b//; $version =~ s/(\S+)$/($1)/; our $maildomain_path = '/etc/mailname'; our $maildomain = &get_maildomain; # Parse options, sanity checks unless ( GetOptions ( "quiet|q" => sub { $verbose = 0 }, "username" => \$include_username, "fullname|n" => \$include_fullname, "office" => \$include_office, "officehints" => \$include_officehints, "workphone" => \$include_workphone, "homephone" => \$include_homephone, "other" => \$include_other, "addresshints" => \$include_addresshints, "mail|m" => \$include_mail, "groupname" => \$include_groupname, "members" => \$include_members, "groups" => \$include_groups, "grouphints" => \$include_grouphints, "custom=s" => \$custom_template, "ignore-badname" => \$ignore_badname, "help|h" => sub { &usage(); exit 0 }, "version|v" => sub { &version(); exit 0 }, "debug" => sub { $verbose = 2 } ) ) { &usage(); exit 1; } while (defined(my $arg = shift(@ARGV))) { push (@names, $arg); } # TODO: Support custom ordering and custom delimiter my @infochunks; push (@infochunks, 'username') if ($include_username); push (@infochunks, 'fullname') if ($include_fullname); push (@infochunks, 'office') if ($include_office); push (@infochunks, 'officehints') if ($include_officehints); push (@infochunks, 'workphone') if ($include_workphone); push (@infochunks, 'homephone') if ($include_homephone); push (@infochunks, 'other') if ($include_other); push (@infochunks, 'addresshints') if ($include_addresshints); push (@infochunks, 'mailaddress') if ($include_mail); push (@infochunks, 'groupname') if ($include_groupname); push (@infochunks, 'members') if ($include_members); push (@infochunks, 'groups') if ($include_groups); push (@infochunks, 'grouphints') if ($include_grouphints); my $template = $custom_template ? $custom_template : '%' . join('% %', @infochunks) . '%'; my %groups; while (my $gr = getgrent()) { foreach my $member (@{$gr->members}) { push @{$groups{$member}}, $gr->name unless ($member eq $gr->name); } } # TODO: Rewrite to batch-resolve userinfo for all users before using any while (my $username = shift @names) { my $string; my ($fullname, $office, $workphone, $homephone, $other, $groupname, $members, $groups, $officehints, $addresshints, $grouphints) = &getuserinfo($username); my $mailaddress = "$username\@$maildomain"; $string = $template; $string =~ s/\%username\%/$username/g; $string =~ s/\%fullname\%/$fullname/g; $string =~ s/\%office\%/$office/g; $string =~ s/\%workphone\%/$workphone/g; $string =~ s/\%homephone\%/$homephone/g; $string =~ s/\%other\%/$other/g; $string =~ s/\%officehints\%/@$officehints/g; $string =~ s/\%addresshints\%/@$addresshints/g; $string =~ s/\%mailaddress\%/$mailaddress/g; $string =~ s/\%groupname\%/$groupname/g; $string =~ s/\%members\%/$members/g; $string =~ s/\%groups\%/$groups/g; $string =~ s/\%grouphints\%/@$grouphints/g; print "$string\n"; } sub getuserinfo($) { my $username = shift; my $pw = getpwnam($username) || die "Failed locating user \"$username\"."; my $gr = getgrgid($pw->gid) || die "Failed locating primary group of user \"$username\"."; my ($fullname, $office, $workphone, $homephone, $other) = split /\s*,\s*/, $pw->gecos; $office .= ''; $workphone .= ''; $homephone .= ''; $other .= ''; my @officehints = grep {s/^([\.[:alnum:]_-]+)$/%$1/} split /\s+/, $office; my @addresshints = grep {/^([\.[:alnum:]_-]+|\+)@([\.[:alnum:]_-]+)?$/} split /\s+/, $other; my $groupname = $gr->name; my $members = $gr->members; my $groups = join ' ', defined($groups{$username}) ? @{$groups{$username}} : ''; my @grouphints = grep {s/^([\.[:alnum:]_-]+)$/\@$1/} split /\s+/, $groups; return ($fullname, $office, $workphone, $homephone, $other, $groupname, $members, $groups, \@officehints, \@addresshints, \@grouphints); } sub get_maildomain { open (MAILDOMAINFILE, "<" . $maildomain_path) || die "can’t open $maildomain_path: $!"; my $string = readline(MAILDOMAINFILE) || die "can’t read $maildomain_path: $!"; close (MAILDOMAINFILE); chomp($string); # FIXME: Do some sanity check of the string - ensure a single word, FQDN syntax etc. return $string; } sub version { printf ("localuserinfo version %s\n\n", $version); print <<'EOF'; List fullname and/or other info for each user. Copyright (C) 2003-2007 Jonas Smedegaard EOF print <<'EOF'; This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, /usr/share/common-licenses/GPL, for more details. EOF } sub usage { print <<"EOF"; localuserinfo USER [USER...] List fullname and/or other info for each user general options: --quiet | -q don't give process information to stdout --ignore-badname ignore non-existing usernames instead of failing --help | -h usage message --username include username --fullname | -n include fullname --office include office --officehints include office hints: words in "office" field with "%" appended --workphone include work phone --homephone include home phone --other include full "other" field --addresshints include address hints: words in "other" field containing "@" --mail | -m include email address: USERNAME\@MAILDOMAIN --groupname include primary group --members include members of primary group --groups include secondary groups --grouphints include group hints: secondary groups with "@" appended --custom=TEMPLATE custom template, e.g. '%username% (%fullname%)' available infochunks: * username * fullname office * officehints * workphone * homephone other * addresshints * mailaddress groupname members groups * grouphint (all marked infosnippets are included by default, in the order listed) --version | -v version number and copyright EOF }