Perl for Systems Administrators
Simon Cozens
IT Support Staff Conference, 2001
(page 1)
Who Am I?
- Perl 5 Unicode Maintainer
- Author of "Beginning Perl"
- Other books and articles on Perl, Linux and Open Source
- "That annoying bastard from Pembroke"
(page 2)
What is Perl?
- A byte-compiled, interpreted programming language
- A text manipulation language
- A cleaned-up, portable version of Unix
(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
- Hold a "thing" - number, string, etc.
$item = "3com 4-port hub";
$units = 10;
- Automatic conversion between number and string
print "We have " . $units . " " . $item;
print "s" if $units > 1;
- Interpolation into strings
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
- "Dictionaries", "associative arrays"
%quota = (
chris => 1024 * 10,
steve => 1024 * 5,
jill => 1024 * 8
);
for $user (@users) {
if ($disk_used{$user} > $quota{$user}) {
...
}
}
(page 7)
Perl Idioms
- $_ is the "default variable"
- while () {... reads from a filehandle into $_
- while (<>) {... reads from standard input or @ARGV
(page 8)
Regular Expressions
- "Pattern Matching" - shell globs on steroids
- /something/ tests if $_ contains "something"
- Classes: /\w/, /\s/, /\d/, /\W/, /\S/, /\D/, /[a-z]/, /[^a-z]/
- Repetitions: /\w+/, /\s*/, /\d{3,5}/
(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
- <> for read, as we've seen.
- open (FILEHANDLE, $filename)
- ... or die $!
- open (OUTPUT, ">$filename") or die $!;
- open (PIPE, "$command|") or die $!;
- open (PIPEOUT, "|$command") or die $!;
(page 11)
Unix Systems Functions
- gethostbyname/gethostbyaddr/etc.
(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
- Not re-inventing the wheel
(page 14)
Sending Mail
- Mail::Mailer (mail, sendmail and SMTP)
- Mail::Send (Simpler interface, abstraction)
(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
- Flat-file text: open, <>, split, join
- DBM: Key-value pairs - hashes on disk
- DBI: Interface to RDBMS database
(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
- libwin32
- Win32::OLE
- Win32::IPC
- Win32::Clipboard
- Win32::EventLog
- Win32::Service
- ...
(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?
- Books
- "Camel Book" - Programming Perl
- "Llama Book" - Learning Perl
- "Perl for Systems Administrators"
- Beginning Perl
- Mailing Lists
- beginners@perl.org
- beginners-cgi@perl.org
(page 23)