Perl for Systems Administrators


Simon Cozens


IT Support Staff Conference, 2001

(page 1)


Who Am I?






(page 2)


What is Perl?






(page 3)


A Sample Perl Program

use DB_File;

$db = '/tmp/userstats.db'; # where data is kept between runs

tie(%db, 'DB_File', $db) or die "Can't open DB_File $db : $!\n";

if (@ARGV) {
if ("@ARGV" eq "ALL") { @ARGV = sort keys %db; }
foreach $user (@ARGV) {
print "$user\t$db{$user}\n";
}
} else {
@who = `who`; # run who(1)
if ($?) {
die "Couldn't run who: $?\n"; # exited abnormally
}
# extract username (first thing on the line) and update
foreach $line (@who) {
$line =~ /^(\S+)/;
die "Bad line from who: $line\n" unless $1;
$db{$1}++;
}
}
untie %db;

(page 4)

Perl Variables
Scalars


$item = "3com 4-port hub";
$units = 10;

print "We have " . $units . " " . $item;
print "s" if $units > 1;

print "We have $units $item";

(page 5)

Perl Variables
Arrays


@ok_users = ("root", "operator", "simon");

print $ok_users[0]; # Prints "root"
print $ok_users[1]; # Prints "operator"
...

(page 6)

Perl Variables
Hashes


%quota = (
chris => 1024 * 10,
steve => 1024 * 5,
jill => 1024 * 8
);

for $user (@users) {
if ($disk_used{$user} > $quota{$user}) {
...
}
}

(page 7)


Perl Idioms




(page 8)


Regular Expressions







(page 9)


A Third Sample Program


use strict;
my %ftp_hits;
while(<>) {

# Jun 3 21:51:59 XXX connection attempt from hostname
# (x.x.x.x:3302->y.y.y.y:53)

my ($date, $service, $host) =
/^(...\s+\d+ \d\d:\d\d:\d\d) (\w+) connection attempt from (\S+)/
or next;

next if $host eq "127.0.0.1" or $host eq "localhost";
next if $service eq "www"; # Apache logs will track this.
$ftp_hits{$host}++ if $service eq "ftp";
print;
}

print "\n\n------------------\n";
print "FTP accesses by hostname:\n";

print "$_ : $ftp_hits{$_}\n" for keys %ftp_hits;

(page 10)


File Handling







(page 11)


Unix Systems Functions




(page 12)


Another Sample Program


use strict;

my %users;

while ($user = getgrent) {
$users{$user}++;
}

while (@group = getgrent) {
# From the man pages: ($name,$passwd,$gid,$members) = getgr*
@members = split /\s+/, $group[3];
delete $users{$_} for @members;
}

print "The following users are not in any groups:\n";
print "$_\n" for keys %users

(page 13)


Modules





(page 14)


Sending Mail





(page 15)


Another Sample Program
(without modules)

my $quotasize = 1024 * 5; # 5 Megabytes
use strict;
my @user;

while (@user = getpwent) {
# From the manpages:
# ($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell,$expire) = getpw*
my ($name, $uid, $gecos, $dir) = @user[0,1,6,7];

next if $uid < 500; # Ignore "system" users
my $size = `du -sk $dir`;
if ($size > $quotasize) {
warn "User $name is over quota: $size\n";
open MAIL, "|mail $user -s 'Quota warning'"
or die "Couldn't send mail: $!\n";

my $size_in_meg = sprintf("%.02d", $size / 1024);

print MAIL < Dear $gecos,
Your account is over quota; your quota is 5 megabytes,
and you currently have $size_in_meg megabytes in your home
directory. Please remove any extraneous files.

Love,
BOFH.
END_OF_MAIL
close MAIL;
}
}

(page 16)


Another Sample Program
(with modules)

use Mail::Send;
use User::pwent;

my $quotasize = 1024 * 5; # 5 Megabytes
use strict;
my $user;

while ($user = getpwent) {
next if $user->uid < 500; # Ignore "system" users
my $dir = $user->dir;
my $realname = $user->gecos;
my $size = `du -sk $dir`;

if ($size > $quotasize) {
warn "User $name is over quota: $size\n";

$msg = new Mail::Send(
Subject=>'Quota warning',
To =>$user->name);

my $size_in_meg = sprintf("%.02d", $size / 1024);
print $msg < Dear $gecos,
Your account is over quota; your quota is 5 megabytes,
and you currently have $size_in_meg megabytes in your home
directory. Please remove any extraneous files.

Love,
BOFH.
END_OF_MAIL
close $msg;
}
}

(page 17)


Databases





(page 18)


DBM Sample

use DB_File;

$db = '/tmp/userstats.db'; # where data is kept between runs

tie(%db, 'DB_File', $db) or die "Can't open DB_File $db : $!\n";

if (@ARGV) {
if ("@ARGV" eq "ALL") { @ARGV = sort keys %db; }
foreach $user (@ARGV) {
print "$user\t$db{$user}\n";
}
} else {
@who = `who`; # run who(1)
if ($?) {
die "Couldn't run who: $?\n"; # exited abnormally
}
# extract username (first thing on the line) and update
foreach $line (@who) {
$line =~ /^(\S+)/;
die "Bad line from who: $line\n" unless $1;
$db{$1}++;
}
}
untie %db;


(page 19)


DBI Sample

use User::pwent;

$dbh = DBI->connect('DBI:mysql:dbname:mysqlserver.domain.com:3306',
'user', 'password',
{ RaiseError => 1 })
or die "connecting : $DBI::errstr\n";

$dbh->do("CREATE TABLE users (uid INT, login CHAR(8))");

$sql_fmt = "INSERT INTO users VALUES( %d, %s )";
while ($user = getpwent) {
$sql = sprintf($sql_fmt, $user->uid, $dbh->quote($user->name));
$dbh->do($sql);
}

$sth = $dbh->prepare("SELECT * FROM users WHERE uid < 50");
$sth->execute;

while ((@row) = $sth->fetchrow) {
print join(", ", map {defined $_ ? $_ : "(null)"} @row), "\n";
}
$sth->finish;
$dbh->do("DROP TABLE users");
$dbh->disconnect;

(page 20)


Modules for the Windows People




(page 21)


Sample Windows administration

use Win32::EventLog;

$myServer="\\\\my-server"; # your servername here.
my($date)=join("-", ((split(/\s+/, scalar(localtime)))[0,1,2,4]));
my($dest);

for my $eventLog ("Application", "System", "Security") {

$handle=Win32::EventLog->new($eventLog, $myServer)
or die "Can't open Application EventLog on $myServer\n";

$dest="C:\\BackupEventLogs\\$eventLog\\$date.evt";
$handle->Backup($dest)
or warn "Could not backup and clear the $eventLog EventLog".
" on $myServer ($^E)\n";

$handle->Close;
}


(page 22)


Where Next?



(page 23)