Mirroring yum metadata
use LWP::UserAgent (); use XML::LibXML (); use Digest (); use IO::File ();
my ($url, $dir) = @ARGV;
if (not defined $dir) { die "Usage: $0 yum-remote-url local-directory\n"; }
if (-e $dir and not -d $dir) {
die "The target [$dir] exists but is not directory.\n";
}
if (not -d $dir) {
my @segments = ($dir =~ m!(.[^/]*)!g);
my $path = '';
while (@segments) {
$path .= shift(@segments);
if (not -d $path) {
print "mkdir $path\n";
mkdir $path or die "Error creating [$path]: $!\n";
}
}
}
sub check_checksum {
my ($file, $digest) = @_;
my $fh = new IO::File $file, 'r' or die "Error reading $file: $!\n";
while (<$fh>) {
$digest->add($_);
}
$fh->close();
return $digest->hexdigest;
}
my %extra_files;
@extra_files{ glob("$dir/*") } = ();
my $ua = new LWP::UserAgent();
if (not $url =~ /\/repodata\/repomd\.xml$/) {
$url .= '/repodata/repomd.xml';
}
my $repomd = $ua->mirror($url, "$dir/repomd.xml");
delete $extra_files{"$dir/repomd.xml"};
my $dom = XML::LibXML->load_xml(location => "$dir/repomd.xml");
my $xc = new XML::LibXML::XPathContext($dom);
$xc->registerNs('repo', 'http://linux.duke.edu/metadata/repo');
my $seen_error = 0;
for my $data ($xc->findnodes('/repo:repomd/repo:data')) {
my $exp_checksum = $data->getChildrenByTagName('checksum')
->string_value;
my ($exp_type) = map { $_->getAttribute('type') }
$data->getChildrenByTagName('checksum');
$exp_type =~ s/^sha/SHA-/;
my $d = new Digest($exp_type) or die "Unknown checksum $exp_type\n";
my ($file) = map { $_->getAttribute('href') }
$data->getChildrenByTagName('location');
$file =~ s!^.*/!!;
my $target_file = "$dir/$file";
delete $extra_files{$target_file};
if (-f $target_file) {
my $checksum = check_checksum($target_file, $d);
if ($checksum eq $exp_checksum) {
# print "File [$file] already exists and correct checksum, not re-retrieving.\n";
next;
}
unlink $target_file;
}
print "Will download [$file] to [$target_file]\n";
$url =~ s![^/]+$!$file!;
my $response = $ua->mirror($url, $target_file);
if (not -f $target_file) {
print "Failed to retrieve [$url]: @{[ $response->message ]}\n";
$seen_error++;
next;
}
$d->reset();
my $hex = check_checksum($target_file, $d);
if ($hex eq $exp_checksum) {
print "\tChecksum OK\n";
} else {
print "\tBad checksum [$hex]\n";
$seen_error++;
}
}
for (sort keys %extra_files) {
print "Purging [$_]\n";
unlink $_;
}
if ($seen_error) {
exit 1;
}
Adding a comment to bugzilla
use WWW::Mechanize ();
use utf8;
my $mech = new WWW::Mechanize ();
$mech->get('http://bugzilla.redhat.com/show_bug.cgi?id=1');
$mech->quiet(1);
$mech->submit_form(with_fields => {
'Bugzilla_login' => 'the-login',
'Bugzilla_password' => 'hard-to-guess-password'
});
$mech->get('http://bugzilla.redhat.com/show_bug.cgi?id=1');
# $mech->dump_forms();
$mech->form_name('changeform');
my $status = $mech->value('bug_status');
$status = ($status eq 'CLOSED') ? 'ASSIGNED' : 'CLOSED';
$mech->submit_form(with_fields => {
'bug_status' => $status,
'comment' => join '', <DATA>,
});
__DATA__
Text komentáře.
Populating database table
use DBI ();
use utf8;
my %users;
open PASSWD, 'getent passwd |' or die "Error $!\n";
while (<PASSWD>) {
my ($login, $pass, $uid) = split /:/, $_, 4;
next if $uid < 500;
$users{$login} = [ $uid ];
}
close PASSWD;
open SHADOW, 'getent shadow |' or die "Error $!\n";
while (<SHADOW>) {
my ($login, $pass) = split /:/, $_, 3;
next if not exists $users{$login};
$users{$login}[1] = $pass;
}
close SHADOW;
my $dbh = DBI->connect('dbi:Pg:dbname=perl301', 'perl301user', 'teslo',
{ RaiseError => 1, AutoCommit => 0 });
my $sth = $dbh->prepare(q!
select login, password, uid
from users
!);
$sth->execute;
my ($del_sth, $upd_sth);
while (my $row = $sth->fetchrow_hashref) {
if (not exists $users{$row->{login}}) {
$del_sth ||= $dbh->prepare(q!
delete from users where login = ?
!);
$del_sth->execute($row->{login});
print "Removing [$row->{login}]\n";
next;
}
{
no warnings 'uninitialized';
if ($row->{uid} ne $users{$row->{login}}[0]
or $row->{password} ne $users{$row->{login}}[1]) {
print "Fixing [$row->{login}]\n";
$upd_sth ||= $dbh->prepare(q!
update users set uid = ?, password = ?
where login = ?
!);
$upd_sth->execute($users{$row->{login}}[0],
$users{$row->{login}}[1],
$row->{login});
}
}
delete $users{$row->{login}};
}
my ($ins_sth);
for my $login (keys %users) {
$ins_sth ||= $dbh->prepare(q!
insert into users (login, password, uid)
values (?, ?, ?)
!);
$ins_sth->execute($login, $users{$login}[1], $users{$login}[0]);
print "Adding [$login]\n";
}
$dbh->commit;
__END__
create table users (
login varchar(64) not null primary key,
password varchar(512),
uid integer not null
);
Packages, modules
In X.pm, we have
package X;
sub x {
print "This is x in ", __PACKAGE__, "\n";
}
1;
In our script, we call function in different package:
use X; X::x();
$ perl s.pl
This is x in X
Classes, objects
In Dir.pm, we have
package Dir;
sub new {
my $class = shift;
my $self = { @_ };
return bless $self, $class;
}
sub dir {
my $self = shift;
if (@_) {
$self->{dir} = shift;
}
return $self->{dir};
}
sub parent {
my $self = shift;
my $p = $self->dir;
$p =~ s!/[^/]+$!! or return $self;
return new Dir(dir => $p);
}
sub exists {
my $self = shift;
return -e $self->dir . '/.';
}
sub mkdir {
my $self = shift;
return 1 if $self->exists();
$self->parent->mkdir();
mkdir $self->dir;
}
1;
We can then create directory in an object way:
$ls /tmp/pokus/hokusls: cannot access /tmp/pokus/hokus: No such file or directory $perl -MDir -e 'my $d = new Dir(dir => "/tmp/pokus/hokus"); $d->mkdir();'$ls /tmp/pokus/hokus$
Subclasses
Some modules are designed to be used via subclassing.
In UsersDBI.pm, we have
package UsersDBI;
use strict;
use warnings FATAL => 'all';
use base 'Class::DBI';
UsersDBI->connection('dbi:Pg:dbname=perl301', 'perl301user', 'teslo',
{ RaiseError => 1, AutoCommit => 0 });
The file Users.pm contains
package Users;
use base 'UsersDBI';
Users->table('users');
Users->columns(All => ('login', 'password', 'uid'));
So far, everything is very much a declaration.
use Users ();
for my $row (Users->retrieve_all) {
if (not exists $users{$row->login}) {
print "Removing [@{[$row->login]}]\n";
$row->delete();
next;
}
{
no warnings 'uninitialized';
my $changed = 0;
if ($row->uid ne $users{$row->login}[0]) {
$row->uid($users{$row->login}[0]);
$changed = 1;
}
if ($row->password ne $users{$row->login}[1]) {
$row->password($users{$row->login}[1]);
$changed = 1;
}
if ($changed) {
print "Fixing [@{[$row->login]}]\n";
$row->update();
}
}
delete $users{$row->login};
}
for my $login (keys %users) {
Users->insert({
login => $login,
password => $users{$login}[1],
uid => $users{$login}[0]
});
print "Adding [$login]\n";
}
Users->dbi_commit();
Plain CGI
In /var/www/cgi-bin/script.cgi:
#!/usr/bin/perl -T use strict; use warnings FATAL => 'all'; print "Content-Type: text/plain\nPragma: no-cache\n\n"; print "This is the output of the CGI script.\n";
$ GET 'http://localhost/cgi-bin/script.cgi'
This is the output of the CGI script.
CGI::Simple
#!/usr/bin/perl -T
use strict;
use warnings FATAL => 'all';
use CGI::Simple ();
my $q = new CGI::Simple();
$q->no_cache(1);
print $q->header(-status => 200, -type => 'text/plain',
'X-Header' => 'scripting');
print "This is the output of the CGI script.\n";
for ($q->param('id')) {
print "You passed in id [$_]\n";
}
1;
$ GET -Se 'http://localhost/cgi-bin/script1.cgi?id=123;id=9'
Connection: close
Date: Thu, 15 Dec 2011 13:53:25 GMT
Pragma: no-cache
Server: Apache/2.2.17 (Fedora)
Content-Type: text/plain; charset=ISO-8859-1
Expires: Thu, 15 Dec 2011 13:53:25 GMT
Client-Date: Thu, 15 Dec 2011 13:53:25 GMT
Client-Peer: 127.0.0.1:80
Client-Response-Num: 1
Client-Transfer-Encoding: chunked
X-Header: scripting
This is the output of the CGI script.
You passed in id [123]
You passed in id [9]
$ echo -n id=13 | POST 'http://localhost/cgi-bin/script1.cgi'
This is the output of the CGI script.
You passed in id [13]
mod_perl
LoadModule perl_module modules/mod_perl.so
For quick move from CGI to mod_perl, you can use
ModPerl::Registry to run unchanged script (that operate
on STDIN and STDOUT)::
Alias /perl /var/www/perl
<Directory /var/www/perl>
SetHandler perl-script
PerlResponseHandler ModPerl::Registry
PerlOptions +ParseHeaders
Options +ExecCGI
</Directory>
$ GET 'http://localhost/perl/script1.cgi?id=100&px=1'
This is the output of the CGI script.
You passed in id [100]
Having /perl-status enabled helps when debugging.
For more serious work, write handlers.
package handler;
use Apache2::RequestIO ();
use Apache2::RequestRec ();
use Apache2::Request ();
sub handler {
my $r = shift;
$r->content_type('text/plain');
$r->print("Testing handler\n");
my $req = new Apache2::Request($r);
for ($req->param('id')) {
print "You passed in id [$_]\n";
}
return Apache2::OK;
}
1;
<Location /testh> SetHandler perl-script PerlResponseHandler handler </Location>
When the modules is stored in say /var/www/perllib/handler.pm,
configure with
PerlSwitches -I/var/www/perllib
$ echo -n id=99 | POST 'http://localhost/testh?id=4'
Testing handler
You passed in id [4]
You passed in id [99]
Self-assessment
Go and create masterpieces.
Final questions?
No?
OKay. Thank you!