summaryrefslogtreecommitdiffstats
path: root/lib/MDK/Common/System.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/MDK/Common/System.pm')
-rw-r--r--lib/MDK/Common/System.pm478
1 files changed, 478 insertions, 0 deletions
diff --git a/lib/MDK/Common/System.pm b/lib/MDK/Common/System.pm
new file mode 100644
index 0000000..a947523
--- /dev/null
+++ b/lib/MDK/Common/System.pm
@@ -0,0 +1,478 @@
+package MDK::Common::System;
+
+=head1 NAME
+
+MDK::Common::System - system-related useful functions
+
+=head1 SYNOPSIS
+
+ use MDK::Common::System qw(:all);
+
+=head1 EXPORTS
+
+=over
+
+=item %compat_arch
+
+architecture compatibility mapping (eg: k6 => i586, k7 => k6 ...)
+
+=item %printable_chars
+
+7 bit ascii characters
+
+=item $sizeof_int
+
+sizeof(int)
+
+=item $bitof_int
+
+$sizeof_int * 8
+
+=item arch()
+
+return the architecture (eg: i686, ppc, ia64, k7...)
+
+=item typeFromMagic(FILENAME, LIST)
+
+find the first corresponding magic in FILENAME. eg of LIST:
+
+ [ 'empty', 0, "\0\0\0\0" ],
+ [ 'grub', 0, "\xEBG", 0x17d, "stage1 \0" ],
+ [ 'lilo', 0x2, "LILO" ],
+
+where each entry is [ magic_name, offset, string, offset, string, ... ].
+
+=item list_passwd()
+
+return the list of users as given by C<getpwent> (see perlfunc)
+
+=item list_home()
+
+return the list of home (eg: /home/foo, /home/pixel, ...)
+
+=item list_skels()
+
+return the directories where we can find dot files: homes, /root and /etc/skel
+
+=item list_users()
+
+return the list of unprivilegied users (aka those whose uid is greater
+than 500 and who are not "nobody").
+
+=item syscall_(NAME, PARA)
+
+calls the syscall NAME
+
+=item psizeof(STRING)
+
+useful to know the length of a C<pack> format string.
+
+ psizeof("I I I C C S") = 4 + 4 + 4 + 1 + 1 + 2 = 16
+
+=item availableMemory()
+
+size of swap + memory
+
+=item availableRamMB()
+
+size of RAM as reported by the BIOS (it is a round number that can be
+displayed or given as "mem=128M" to the kernel)
+
+!! "mem=..." is dangerous in 2.4 kernels
+
+=item gettimeofday()
+
+returns the epoch in microseconds
+
+=item unix2dos(STRING)
+
+takes care of CR/LF translation
+
+=item whereis_binary(STRING)
+
+return the first absolute file in $PATH (similar to which(1) and whereis(1))
+
+=item getVarsFromSh(FILENAME)
+
+returns a hash associating shell variables to their value. useful for config
+files such as /etc/sysconfig files
+
+=item setVarsInSh(FILENAME, HASH REF)
+
+write file in shell format association a shell variable + value for each
+key/value
+
+=item setVarsInSh(FILENAME, HASH REF, LIST)
+
+restrict the fields that will be printed to LIST
+
+=item setVarsInShMode(FILENAME, INT, HASH REF, LIST)
+
+like setVarsInSh with INT being the chmod value for the config file
+
+=item addVarsInSh(FILENAME, HASH REF)
+
+like setVarsInSh but keeping the entries in the file
+
+=item addVarsInSh(FILENAME, HASH REF, LIST)
+
+like setVarsInSh but keeping the entries in the file
+
+=item addVarsInShMode(FILENAME, INT, HASH REF, LIST)
+
+like addVarsInShMode but keeping the entries in the file
+
+=item setExportedVarsInCsh(FILENAME, HASH REF, LIST)
+
+same as C<setExportedVarsInSh> for csh format
+
+=item template2file(FILENAME_IN, FILENAME_OUT, HASH)
+
+read in a template file, replace keys @@@key@@@ with value, save it in out
+file
+
+=item template2userfile(PREFIX, FILENAME_IN, FILENAME_OUT, BOOL, HASH)
+
+read in a template file, replace keys @@@key@@@ with value, save it in every homes.
+If BOOL is true, overwrite existing files. FILENAME_OUT must be a relative filename
+
+=item read_gnomekderc(FILENAME, STRING)
+
+reads GNOME-like and KDE-like config files (aka windows-like).
+You must give a category. eg:
+
+ read_gnomekderc("/etc/skels/.kderc", 'KDE')
+
+=item update_gnomekderc(FILENAME, STRING, HASH)
+
+modifies GNOME-like and KDE-like config files (aka windows-like).
+If the category doesn't exist, it creates it. eg:
+
+ update_gnomekderc("/etc/skels/.kderc", 'KDE',
+ kfmIconStyle => "Large")
+
+=item fuzzy_pidofs(REGEXP)
+
+return the list of process ids matching the regexp
+
+=back
+
+=head1 OTHER
+
+=over
+
+=item better_arch(ARCH1, ARCH2)
+
+is ARCH1 compatible with ARCH2?
+
+better_arch('i386', 'ia64') and better_arch('ia64', 'i386') are false
+
+better_arch('k7', 'k6') is true and better_arch('k6', 'k7') is false
+
+=item compat_arch(STRING)
+
+test the architecture compatibility. eg:
+
+compat_arch('i386') is false on a ia64
+
+compat_arch('k6') is true on a k6 and k7 but false on a i386 and i686
+
+=back
+
+=head1 SEE ALSO
+
+L<MDK::Common>
+
+=cut
+
+
+use MDK::Common::Math;
+use MDK::Common::File;
+
+use Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(%compat_arch $printable_chars $sizeof_int $bitof_int arch distrib typeFromMagic list_passwd list_home list_skels list_users syscall_ psizeof availableMemory availableRamMB gettimeofday unix2dos whereis_binary getVarsFromSh setVarsInSh setVarsInShMode addVarsInSh addVarsInShMode setExportedVarsInSh setExportedVarsInCsh template2file template2userfile read_gnomekderc update_gnomekderc fuzzy_pidofs); #);
+our %EXPORT_TAGS = (all => [ @EXPORT_OK ]);
+
+
+our %compat_arch = ( #- compatibilty arch mapping.
+ 'noarch' => undef,
+ 'ia32' => 'noarch',
+ 'i386' => 'ia32',
+ 'i486' => 'i386',
+ 'i586' => 'i486',
+ 'i686' => 'i586',
+ 'i786' => 'i686',
+ 'k6' => 'i586',
+ 'k7' => 'k6',
+ 'k8' => 'k7',
+ 'x86_64' => 'i686',
+ 'amd64' => 'x86_64',
+ 'ia64' => 'noarch',
+ 'ppc' => 'noarch',
+ 'alpha' => 'noarch',
+ 'sparc' => 'noarch',
+ 'sparc32' => 'sparc',
+ 'sparc64' => 'sparc32',
+ 'ia64' => 'noarch',
+ );
+
+our $printable_chars = "\x20-\x7E";
+our $sizeof_int = psizeof("i");
+our $bitof_int = $sizeof_int * 8;
+
+
+sub arch() {
+ my $SYS_NMLN = 65;
+ my $format = "Z$SYS_NMLN" x 6;
+ my $t = pack $format;
+ syscall_('uname', $t);
+ (unpack($format, $t))[4];
+}
+sub better_arch {
+ my ($new, $old) = @_;
+ while ($new && $new ne $old) { $new = $compat_arch{$new} }
+ $new;
+}
+sub compat_arch { better_arch(arch(), $_[0]) }
+
+sub distrib() {
+ my $release = MDK::Common::File::cat_('/etc/release');
+ my ($real_system, $real_product) = $release =~ /(.*) release ([\d.]+)/;
+ my $oem_config = '/etc/sysconfig/oem';
+ my %oem = -f $oem_config && getVarsFromSh($oem_config);
+ my $company = $oem{COMPANY} || 'Mandriva';
+ my $system = $oem{SYSTEM} || $real_system;
+ my $product = $oem{PRODUCT} || $real_product;
+ (company => $company, system => $system, product => $product, real_system => $real_system, real_product => $real_product);
+}
+
+sub typeFromMagic {
+ my $f = shift;
+ sysopen(my $F, $f, 0) or return;
+
+ my $tmp;
+ M: foreach (@_) {
+ if (ref($_) eq 'CODE') {
+ my $name = $_->($F) or next M;
+ return $name;
+ } else {
+ my ($name, @l) = @$_;
+ while (@l) {
+ my ($offset, $signature) = splice(@l, 0, 2);
+ sysseek($F, $offset, 0) or next M;
+ sysread($F, $tmp, length $signature);
+ $tmp eq $signature or next M;
+ }
+ return $name;
+ }
+ }
+ undef;
+}
+
+
+sub list_passwd() {
+ my (@l, @e);
+ setpwent();
+ while (@e = getpwent()) { push @l, [ @e ] }
+ endpwent();
+ @l;
+}
+sub list_home() {
+ map { $_->[7] } grep { $_->[2] >= 500 } list_passwd();
+}
+sub list_skels {
+ my ($prefix, $suffix) = @_;
+ grep { -d $_ && -w $_ } map { "$prefix$_/$suffix" } '/etc/skel', '/root', list_home();
+}
+
+sub list_users() {
+ map { 500 <= $_->[2] && $_->[0] ne "nobody" ? $_->[0] : () } list_passwd();
+}
+
+
+
+sub syscall_ {
+ my $f = shift;
+
+ #- load syscall.ph in package "main". If every use of syscall.ph do the same, all will be nice
+ package main;
+ require 'syscall.ph';
+
+ syscall(&{"main::SYS_$f"}, @_) == 0;
+}
+
+
+#- return the size of the partition and its free space in KiB
+sub df {
+ my ($mntpoint) = @_;
+ my ($blocksize, $size, $free);
+ my $buf = ' ' x 20000;
+ syscall_('statfs', $mntpoint, $buf) or return;
+ (undef, $blocksize, $size, $free, undef, undef) = unpack "L!6", $buf;
+ map { $_ * ($blocksize / 1024) } $size, $free;
+}
+
+sub sync() { syscall_('sync') }
+sub psizeof { length pack $_[0] }
+sub availableMemory() { MDK::Common::Math::sum(map { /(\d+)/ } grep { /^(MemTotal|SwapTotal):/ } MDK::Common::File::cat_("/proc/meminfo")) }
+sub availableRamMB() { 4 * MDK::Common::Math::round((-s '/proc/kcore') / 1024 / 1024 / 4) }
+sub gettimeofday() { my $t = pack "LL"; syscall_('gettimeofday', $t, 0) or die "gettimeofday failed: $!\n"; unpack("LL", $t) }
+sub unix2dos { local $_ = $_[0]; s/\015$//mg; s/$/\015/mg; $_ }
+
+sub whereis_binary {
+ my ($prog, $o_prefix) = @_;
+ if ($prog =~ m!/!) {
+ warn qq(don't call whereis_binary with a name containing a "/" (the culprit is: $prog)\n);
+ return;
+ }
+ foreach (split(':', $ENV{PATH})) {
+ my $f = "$_/$prog";
+ -x "$o_prefix$f" and return $f;
+ }
+}
+
+sub getVarsFromSh {
+ my %l;
+ open(my $F, $_[0]) or return;
+ local $_;
+ while (<$F>) {
+ s/#.*//; # remove comments
+ s/^\s*//; # leading space
+ my ($v, $val) = /^(\w+)=(.*)/ or next;
+ $val = $1 if $val =~ /^"(.*)"$/ || $val =~ /^'(.*)'$/;
+ $l{$v} = $val;
+ }
+ %l;
+}
+
+sub addVarsInSh {
+ my ($file, $l, @fields) = @_;
+ addVarsInShMode($file, 0777 ^ umask(), $l, @fields);
+}
+
+sub addVarsInShMode {
+ my ($file, $mod, $l, @fields) = @_;
+ my %l = @fields ? map { $_ => $l->{$_} } @fields : %$l;
+ my %l2 = getVarsFromSh($file);
+
+ # below is add2hash_(\%l, \%l2);
+ exists $l{$_} or $l{$_} = $l2{$_} foreach keys %l2;
+
+ setVarsInShMode($file, $mod, \%l);
+}
+
+sub setVarsInSh {
+ my ($file, $l, @fields) = @_;
+ setVarsInShMode($file, 0777 ^ umask(), $l, @fields);
+}
+
+sub setVarsInShMode {
+ my ($file, $mod, $l, @fields) = @_;
+ @fields = keys %$l unless @fields;
+ my $string = join('',
+ map {
+ my $val = $l->{$_};
+ if ($val =~ /["`\$]/) {
+ $val =~ s/(')/\\$1/g;
+ $val = qq('$val');
+ } elsif ($val =~ /['|\s\\]/) {
+ $val =~ s/(["\\])/\\$1/g;
+ $val = qq("$val");
+ }
+ "$_=$val\n";
+ } grep { $l->{$_} } @fields
+ );
+ if ($file =~ m!^/home/!) {
+ MDK::Common::File::secured_output($file, $string);
+ } else {
+ MDK::Common::File::output($file, $string);
+ }
+
+ chmod $mod, $file;
+}
+
+sub setExportedVarsInSh {
+ my ($file, $l, @fields) = @_;
+ @fields = keys %$l unless @fields;
+
+ MDK::Common::File::output($file,
+ (map { $l->{$_} ? "$_=$l->{$_}\n" : () } @fields),
+ @fields ? "export " . join(" ", @fields) . "\n" : (),
+ );
+}
+
+sub setExportedVarsInCsh {
+ my ($file, $l, @fields) = @_;
+ @fields = keys %$l unless @fields;
+
+ MDK::Common::File::output($file, map { $l->{$_} ? "setenv $_ $l->{$_}\n" : () } @fields);
+}
+
+sub template2file {
+ my ($in, $out, %toreplace) = @_;
+ MDK::Common::File::output($out, map { s/@@@(.*?)@@@/$toreplace{$1}/g; $_ } MDK::Common::File::cat_($in));
+}
+sub template2userfile {
+ my ($prefix, $in, $out_rel, $force, %toreplace) = @_;
+
+ foreach (list_skels($prefix, $out_rel)) {
+ -d MDK::Common::File::dirname($_) or !-e $_ or $force or next;
+
+ template2file($in, $_, %toreplace);
+ m|/home/(.+?)/| and chown(getpwnam($1), getgrnam($1), $_);
+ }
+}
+
+sub read_gnomekderc {
+ my ($file, $category) = @_;
+ my %h;
+ foreach (MDK::Common::File::cat_($file), "[NOCATEGORY]\n") {
+ if (/^\s*\[\Q$category\E\]/i ... /^\[/) {
+ $h{$1} = $2 if /^\s*([^=]*?)=(.*)/;
+ }
+ }
+ %h;
+}
+
+sub update_gnomekderc {
+ my ($file, $category, %subst_) = @_;
+
+ my %subst = map { lc($_) => [ $_, $subst_{$_} ] } keys %subst_;
+
+ my $s;
+ defined($category) or $category = "DEFAULTCATEGORY";
+ foreach ("[DEFAULTCATEGORY]\n", MDK::Common::File::cat_($file), "[NOCATEGORY]\n") {
+ if (my $i = /^\s*\[\Q$category\E\]/i ... /^\[/) {
+ if ($i =~ /E/) { #- for last line of category
+ chomp $s; $s .= "\n";
+ $s .= "$_->[0]=$_->[1]\n" foreach values %subst;
+ %subst = ();
+ } elsif (/^\s*([^=]*?)=/) {
+ if (my $e = delete $subst{lc($1)}) {
+ $_ = "$1=$e->[1]\n";
+ }
+ }
+ }
+ $s .= $_ if !/^\[(NO|DEFAULT)CATEGORY\]/;
+ }
+
+ #- if category has not been found above (DEFAULTCATEGORY is always found).
+ if (keys %subst) {
+ chomp $s;
+ $s .= "\n[$category]\n";
+ $s .= "$_->[0]=$_->[1]\n" foreach values %subst;
+ }
+
+ MDK::Common::File::output_p($file, $s);
+
+}
+
+sub fuzzy_pidofs {
+ my ($regexp) = @_;
+ grep {
+ /^(\d+)$/ && (MDK::Common::File::cat_("/proc/$_/cmdline") || readlink("/proc/$_/exe") || '') =~ /$regexp/;
+ } MDK::Common::File::all('/proc');
+}
+
+1;