summaryrefslogtreecommitdiffstats
path: root/perl-install/common.pm
diff options
context:
space:
mode:
Diffstat (limited to 'perl-install/common.pm')
-rw-r--r--perl-install/common.pm731
1 files changed, 621 insertions, 110 deletions
diff --git a/perl-install/common.pm b/perl-install/common.pm
index d8072df85..508e6b31f 100644
--- a/perl-install/common.pm
+++ b/perl-install/common.pm
@@ -1,19 +1,25 @@
-package common; # $Id$
+package common;
use MDK::Common;
-use MDK::Common::System;
use diagnostics;
use strict;
+BEGIN { eval { require Locale::gettext } } #- allow common.pm to be used in drakxtools-backend without perl-Locale-gettext
+
+use log;
use run_program;
-use vars qw(@ISA @EXPORT $SECTORSIZE);
-@ISA = qw(Exporter);
-# no need to export ``_''
-@EXPORT = qw($SECTORSIZE N N_ check_for_xserver files_exist formatTime formatXiB makedev mandrake_release removeXiBSuffix require_root_capability salt setVirtual set_alternative set_permissions translate unmakedev untranslate);
+use Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT = qw($SECTORSIZE N P N_ check_for_xserver files_exist formatTime MB formatXiB get_libdir get_parent_uid is_running is_uefi kernel_uefi_type makedev mageia_release mageia_release_info mount_efivars removeXiBSuffix require_root_capability setVirtual set_alternative set_l10n_sort set_permissions to_utf8 translate uefi_type unmakedev);
# perl_checker: RE-EXPORT-ALL
push @EXPORT, @MDK::Common::EXPORT;
+=head1 SYNOPSYS
+
+B<common> re-export L<MDK::Common> and offers a couple widely used functions.
+
+=cut
$::prefix ||= ""; # no warning
@@ -22,74 +28,155 @@ $::prefix ||= ""; # no warning
#-#####################################################################################
our $SECTORSIZE = 512;
-#-#####################################################################################
-#- Functions
-#-#####################################################################################
+=head1 Functions
+=head2 Translating
-sub sprintf_fixutf8 {
- my $need_upgrade;
- $need_upgrade |= to_bool(c::is_tagged_utf8($_)) + 1 foreach @_;
- if ($need_upgrade == 3) { c::upgrade_utf8($_) foreach @_ };
- sprintf shift, @_;
-}
+=over
+
+=item N($format, ...)
+
+translate a message by calling gettext(). eg:
+
+ N("Summary")
+ N("File %s is on %s", "/etc/passwd", "/dev/sda2")
+
+It must B<not> be used called before L<ugtk3> is initialized, otherwise the gettext is not forced to UTF-8.
+
+=cut
sub N {
- $::one_message_has_been_translated ||= join(':', (caller(0))[1,2]); #- see ugtk2.pm
- my $s = shift @_; my $t = translate($s);
- sprintf_fixutf8 $t, @_;
+ my ($s, @para) = @_;
+ sprintf(translate($s), @para);
}
+
+=item N_($format, @param)
+
+Similar to C<N()>, but message is not actually translated.
+It tags the message as translatable so that it will end in the
+translation catalogs.
+
+In order for the message to be translated, C<translate()> must be called
+before using the message.
+
+=cut
+
sub N_ { $_[0] }
+=item P($s_singular, $s_plural, $nb, @extra_para)
-sub salt {
- my ($nb) = @_;
- require devices;
- open(my $F, devices::make("random")) or die "missing random";
- my $s; read $F, $s, $nb;
- $s = pack("b8" x $nb, unpack "b6" x $nb, $s);
- $s =~ tr|\0-\x3f|0-9a-zA-Z./|;
- $s;
-}
+Similar to C<N()>, but two messages are provided: one for the singular case, one for the
+plural case. At runtime, a number is provided in order to choose the message to use.
+eg:
-sub makedev { ($_[0] << 8) | $_[1] }
-sub unmakedev { $_[0] >> 8, $_[0] & 0xff }
+ P("%d package", "%d packages", $nb, $nb)
+
+=cut
+
+sub P {
+ my ($s_singular, $s_plural, $nb, @para) = @_;
+ sprintf(translate($s_singular, $s_plural, $nb), @para);
+}
sub translate_real {
- my ($s) = @_;
+ my ($s, $o_plural, $o_nb) = @_;
$s or return '';
+ my $s2;
foreach (@::textdomains, 'libDrakX') {
- my $s2 = c::dgettext($_, $s);
- return $s2 if $s ne $s2;
+ if ($o_plural) {
+ $s2 = Locale::gettext::dngettext($_, $s, $o_plural, $o_nb);
+ } else {
+ $s2 = Locale::gettext::dgettext($_, $s);
+ }
+ # when utf8 pragma is in use, Locale::gettext() returns an utf8 string not tagged as such:
+ c::set_tagged_utf8($s2) if !utf8::is_utf8($s2) && utf8::is_utf8($s);
+ return $s2 if !member($s2, $s, $o_plural);
}
+ # didn't lookup anything or locale is "C":
+ $s2;
+}
+
+=item remove_translate_context($string)
+
+Remove an ala-KDE context message. eg:
+
+ "_: this is verb\nOpen"
+ "_: Keep these entry short\nNetworking"
+
+=cut
+
+sub remove_translate_context {
+ my ($s) = @_;
+ #- translation with context, kde-like
+ $s =~ s/^_:.*(?:\n)?//g;
$s;
}
+=item translate($string)
+
+Translate the message according to the current locale.
+The string is B<not> tagged as translatable and will B<not> end in
+translations catalogs.
+
+perl_checker only extracts strings tagged by C<N()>, C<N_()> or C<P()>.
+
+=cut
+
sub translate {
my $s = translate_real(@_);
- $::need_utf8_i18n and c::set_tagged_utf8($s);
+ $::one_message_has_been_translated ||= join(':', (caller(1))[1,2]); #- see mygtk3.pm
+ remove_translate_context($s);
+}
- #- translation with context, kde-like
- $s =~ s/^_:.*\n//;
- $s;
+=back
+
+=head2 Conversion
+
+=over
+
+=item from_utf8($s)
+
+Convert an UTF-8 string into current locale's encoding.
+
+=cut
+
+sub from_utf8 {
+ my ($s) = @_;
+ Locale::gettext::iconv($s, "utf-8", undef); #- undef = locale charmap = nl_langinfo(CODESET)
}
+=item to_utf8($s)
+
+The reverse of C<from_utf8()>: convert a string from current locale's encoding to UTF-8.
+Also make sure the string is properly tagged as UTF-8 as concerning Perl
+
+=cut
-sub untranslate {
- my $s = shift || return;
- foreach (@_) { translate($_) eq $s and return $_ }
- die "untranslate failed";
+sub to_utf8 {
+ my ($s) = @_;
+ my $str = Locale::gettext::iconv($s, undef, "utf-8"); #- undef = locale charmap = nl_langinfo(CODESET)
+ c::set_tagged_utf8($str);
+ $str;
}
-BEGIN { undef *availableRamMB }
-sub availableRamMB() {
- my $s = MDK::Common::System::availableRamMB();
- #- HACK HACK: if i810 and memsize
- require detect_devices;
- return $s - 1 if $s == 128 && detect_devices::matching_driver('^Card:Intel 810$');
- $s;
+=item set_l10n_sort()
+
+This is needed because text printed by L<Gtk3> will always be encoded
+in UTF-8;
+we first check if LC_ALL is defined, because if it is, changing
+only LC_COLLATE will have no effect.
+
+=cut
+
+sub set_l10n_sort() {
+ my $collation_locale = $ENV{LC_ALL};
+ if (!$collation_locale) {
+ $collation_locale = c::setlocale(c::LC_COLLATE());
+ $collation_locale =~ /UTF-8/ or c::setlocale(c::LC_COLLATE(), "$collation_locale.UTF-8");
+ }
}
+
sub setVirtual {
my ($vt_number) = @_;
my $vt = '';
@@ -102,9 +189,51 @@ sub setVirtual {
sub nonblock {
my ($F) = @_;
- fcntl($F, c::F_SETFL(), fcntl($F, c::F_GETFL(), 0) | c::O_NONBLOCK());
+ fcntl($F, c::F_SETFL(), fcntl($F, c::F_GETFL(), 0) | c::O_NONBLOCK()) or die "cannot fcntl F_SETFL: $!";
}
+=item makedev($major, $minor)
+
+Given major and minor device IDs, makedev() combines these to return a device ID
+(eg for mknod())
+
+=cut
+
+sub makedev { ($_[0] << 8) | $_[1] }
+
+=item unmakedev($device_id)
+
+The reverse of C<makedev()>: given a device ID, it will return both major and minor device IDs.
+
+=cut
+
+sub unmakedev { $_[0] >> 8, $_[0] & 0xff }
+
+=item MB($suffixed_number)
+
+return a size in sector
+ie C<MB(1)> is 2048 sectors, which is 1MB
+
+eg:
+
+ MB("10") => 20480
+
+=cut
+
+sub MB {
+ my ($nb_MB) = @_;
+ $nb_MB * 2048;
+}
+
+=item removeXiBSuffix($string)
+
+Remove the XiB suffix and return the number. eg:
+
+ "10k" => 10240
+ "2M" => 2097152
+
+=cut
+
sub removeXiBSuffix {
local $_ = shift;
@@ -114,29 +243,49 @@ sub removeXiBSuffix {
/(\d+)\s*TB?$/i and return $1 * 1024 * 1024 * 1024 * 1024;
$_;
}
+
+=item formatXiB($number [, $base])
+
+The reverse of C<removeXiBSuffix()>, returns a nicely human size. eg:
+
+ 2097152 => "2MB"
+
+The optional parameter enables to provide the unit size (default is one).
+eg for a 2000 512 sized sectors:
+
+ formatXiB(2000, 512)
+
+=cut
+
sub formatXiB {
my ($newnb, $o_newbase) = @_;
my $newbase = $o_newbase || 1;
+ my $sign = $newnb < 0 ? -1 : 1;
+ $newnb = abs(int($newnb));
my ($nb, $base);
my $decr = sub {
($nb, $base) = ($newnb, $newbase);
$base >= 1024 ? ($newbase = $base / 1024) : ($newnb = $nb / 1024);
};
- foreach ('', N("KB"), N("MB"), N("GB")) {
+ my $suffix;
+ foreach (N("B"), N("KB"), N("MB"), N("GB"), N("TB")) {
$decr->();
if ($newnb < 1 && $newnb * $newbase < 1) {
- my $v = $nb * $base;
- my $s = $v < 10 && int(10 * $v - 10 * int($v));
- return int($v) . ($s ? ".$s" : '') . $_;
+ $suffix = $_;
+ last;
}
}
- int($newnb * $newbase) . N("TB");
+ my $v = $nb * $base;
+ my $s = $v < 10 && int(10 * $v - 10 * int($v));
+ int($v * $sign) . ($s ? "." . abs($s) : '') . ($suffix || N("TB"));
}
sub formatTime {
my ($s, $m, $h) = gmtime($_[0]);
if ($h) {
- sprintf "%02d:%02d", $h, $m;
+ sprintf
+ #-PO: here, "2:30" is remaining installation time (eg: "2:30" == 2 hour & 30 minutes)
+ N("%02d:%02d", $h, $m);
} elsif ($m > 1) {
N("%d minutes", $m);
} elsif ($m == 1) {
@@ -146,7 +295,20 @@ sub formatTime {
}
}
-sub usingRamdisk() { any { /ram3/ } cat_("/proc/mounts") }
+sub expand_symlinks_with_absolute_symlinks_in_prefix {
+ my ($prefix, $link) = @_;
+
+ my ($first, @l) = split '/', $link;
+ $first eq '' or die "expand_symlinks: $link is relative\n";
+ my ($f, $l);
+ foreach (@l) {
+ $f .= "/$_";
+ while ($l = readlink "$prefix$f") {
+ $f = $l =~ m!^/! ? $l : MDK::Common::File::concat_symlink($f, "../$l");
+ }
+ }
+ "$prefix$f";
+}
sub expand_symlinks_but_simple {
my ($f) = @_;
@@ -156,52 +318,61 @@ sub expand_symlinks_but_simple {
# put back the last simple symlink
$f2 =~ s|\Q$link\E$|basename($f)|e;
}
- $f2
+ $f2;
}
sub sync { &MDK::Common::System::sync }
-BEGIN { undef *formatError };
+BEGIN { undef *formatError }
sub formatError {
my ($err) = @_;
+ ref($err) eq 'SCALAR' and $err = $$err;
log::l("error: $err");
&MDK::Common::String::formatError($err);
}
-# Group the list by n. Returns a reference of lists of length n
+=item group_by($f, @list)
+
+Group the elements of @list in array references according to the $f comparison function.
+
+=cut
+
+sub group_by(&@) {
+ my $f = shift;
+ @_ or return;
+ my $e = shift;
+ my @l = my $last_l = [$e];
+ foreach (@_) {
+ if ($f->($e, $_)) {
+ push @$last_l, $_;
+ } else {
+ push @l, $last_l = [$_];
+ $e = $_;
+ }
+ }
+ @l;
+}
+
+=item group_n_lm($n, @list)
+
+Group the list by n. Returns a reference of lists of length n
+
+See also C<group_by2(LIST)> from L<MDK::Common::DataStructure>
+
+=cut
+
sub group_n_lm {
my $n = shift;
my @l;
push @l, [ splice(@_, 0, $n) ] while @_;
- @l
+ @l;
}
-sub screenshot_dir__and_move() {
- my ($dir1, $dir2) = ("$::prefix/root", '/tmp/stage2');
- if (-e $dir1) {
- if (-e "$dir2/DrakX-screenshots") {
- cp_af("$dir2/DrakX-screenshots", $dir1);
- rm_rf("$dir2/DrakX-screenshots");
- }
- $dir1;
- } else {
- $dir2;
- }
-}
+=item join_lines(@strings)
-sub take_screenshot() {
- my $dir = screenshot_dir__and_move() . '/DrakX-screenshots';
- my $warn;
- if (!-e $dir) {
- mkdir $dir or $::o->ask_warn('', N("Can't make screenshots before partitioning")), return;
- $warn = 1;
- }
- my $nb = 1;
- $nb++ while -e "$dir/$nb.png";
- system("fb2png /dev/fb0 $dir/$nb.png 0");
+Concatenate adjacent strings if laters begin with spaces.
- $::o->ask_warn('', N("Screenshots will be available after install in %s", "/root/DrakX-screenshots")) if $warn;
-}
+=cut
sub join_lines {
my @l;
@@ -218,68 +389,408 @@ sub join_lines {
}
+sub read_alternative {
+ my ($name) = @_;
+ my $alt = readlink("$::prefix/etc/alternatives/$name");
+ $alt && $::prefix . $alt;
+}
+
sub set_alternative {
my ($command, $executable) = @_;
- #- check the existance of $executable as an alternative for $command
+ #- check the existence of $executable as an alternative for $command
#- (is this needed???)
run_program::rooted_get_stdout($::prefix, 'update-alternatives', '--display', $command) =~ /^\Q$executable /m or return;
- #- this doesn't handle relative symlink, but neither does update-alternatives ;p
+ #- this does not handle relative symlink, but neither does update-alternatives ;p
symlinkf $executable, "$::prefix/etc/alternatives/$command";
}
sub files_exist { and_(map { -f "$::prefix$_" } @_) }
+sub open_file {
+ my ($file) = @_;
+ my $F;
+ open($F, $file) ? $F : do { log::l("Cannot open $file: $!"); undef };
+}
+
+=item secured_file($f)
+
+A wrapper around c::is_secure_file().
+
+Callers should probably just use C<mkstemp()> in /tmp.
+
+=cut
+
+# FIXME: callers should just use mkstemp in /tmp instead of relying on $TMPDIR || $ENV{HOME}/tmp
+# or we should just move the choice of directory from callers to here:
+# my $tmpdir = find { -d $_ } $ENV{TMPDIR}, "$ENV{HOME}/tmp", "$::prefix/tmp";
sub secured_file {
my ($f) = @_;
- c::is_secure_file($f) or die "can't ensure a safe $f";
+ my $d = dirname($f);
+ if (! -d $d) {
+ mkdir_p($d);
+ if ($d =~ /^$ENV{HOME}/) {
+ my ($user) = grep { $_->[7] eq $ENV{HOME} } list_passwd();
+ chown($user->[2], $user->[3], $d);
+ }
+ }
+ c::is_secure_file($f) or die "cannot ensure a safe $f";
$f;
}
+sub unwind_protect {
+ my ($to_do, $cleanup) = @_;
+ my @l = eval { $to_do->() };
+ my $err = $@;
+ $cleanup->();
+ $err and die $err;
+ wantarray() ? @l : $l[0];
+}
+
+sub with_private_tmp_file {
+ my ($file, $content, $f) = @_;
+
+ my $prev_umask = umask 077;
+
+ unwind_protect(sub {
+ MDK::Common::File::secured_output($file, $content);
+ $f->($file);
+ }, sub {
+ umask $prev_umask;
+ unlink $file;
+ });
+}
+
+sub chown_ {
+ my ($b_recursive, $name, $group, @files) = @_;
+
+ my ($uid, $gid) = (getpwnam($name) || $name, getgrnam($group) || $group);
+
+ require POSIX;
+ my $chown; $chown = sub {
+ foreach (@_) {
+ POSIX::lchown($uid, $gid, $_) or die "chown of file $_ failed: $!\n";
+ ! -l $_ && -d $_ && $b_recursive and &$chown(glob_($_));
+ }
+ };
+ $chown->(@files);
+}
+
+
sub set_permissions {
my ($file, $perms, $o_owner, $o_group) = @_;
# We only need to set the permissions during installation to be able to
- # print test pages. After installation the devfsd daemon does the business
- # automatically.
+ # print test pages. After installation udev does the business automatically.
return 1 unless $::isInstall;
- if ($o_owner && $o_group) {
- run_program::rooted($::prefix, "/bin/chown", "$o_owner.$o_group", $file)
- or die "Could not start chown!";
- } elsif ($o_owner) {
- run_program::rooted($::prefix, "/bin/chown", $o_owner, $file)
- or die "Could not start chown!";
- } elsif ($o_group) {
- run_program::rooted($::prefix, "/bin/chgrp", $o_group, $file)
- or die "Could not start chgrp!";
+ if ($o_owner || $o_group) {
+ $o_owner ||= (lstat($file))[4];
+ $o_group ||= (lstat($file))[5];
+ chown_(0, $o_owner, $o_group, $file);
}
- run_program::rooted($::prefix, "/bin/chmod", $perms, $file)
- or die "Could not start chmod!";
+ chmod(oct($perms), $file) or die "chmod of file $file failed: $!\n";
+}
+
+sub is_running {
+ my ($name, $o_user) = @_;
+ my $user = $o_user || $ENV{USER};
+ foreach (`ps -o '%P %p %c' -u $user`) {
+ my ($ppid, $pid, $n) = /^\s*(\d+)\s+(\d+)\s+(.*)/;
+ return $pid if $ppid != 1 && $pid != $$ && $n eq $name;
+ }
+}
+
+=back
+
+=head2 Release files
+
+=over
+
+=item parse_release_file($prefix, $f, $part)
+
+Parses the $f release file in $prefix from the $part device.
+
+Returns a hash containing C<release>, C<version>, C<release_file>, C<part> & C<arch> fields.
+
+=cut
+
+sub parse_release_file {
+ my ($prefix, $f, $part) = @_;
+ chomp(my $s = cat_("$prefix$f"));
+ my $version = $s =~ s/\s+release\s+(\S+)// && $1;
+ my $arch = $s =~ s/\s+for\s+(\S+)// && $1;
+ log::l("find_root_parts found $part->{device}: $s for $arch" . ($f !~ m!/etc/! ? " in special release file $f" : ''));
+ { release => $s, version => $version,
+ release_file => $f, part => $part, 'arch' => $arch };
+}
+
+=item release_file($o_dir)
+
+Returns the release file name.
+
+=cut
+
+sub release_file {
+ my ($o_dir) = @_;
+ my @names = ('mageia-release', 'mandriva-release', 'mandrakelinux-release', 'mandrake-release', 'conectiva-release', 'release', 'redhat-release', 'fedora-release', 'SuSE-release');
+ find { -r "$o_dir$_" } (
+ (map { "/root/drakx/$_.upgrading" } @names),
+ (map { "/etc/$_" } @names),
+ );
+}
+
+=item mageia_release_info()
+
+Parses C</etc/product.id> and returns a hash.
+
+=cut
+
+sub mageia_release_info() {
+ parse_LDAP_namespace_structure(cat_('/etc/product.id'));
}
-sub mandrake_release() {
- chomp_(cat_("/etc/mandrake-release"))
+sub parse_LDAP_namespace_structure {
+ my ($s) = @_;
+ my %h = map { if_(/(.*?)=(.*)/, $1 => $2) } split(',', $s);
+ \%h;
}
+=item mageia_release($o_dir)
+
+returns the content of Mageia release file from given directory (optional).
+
+=cut
+
+sub mageia_release {
+ my ($o_dir) = @_;
+ my $f = release_file($o_dir);
+ $f && chomp_(cat_("$o_dir$f"));
+}
+
+=back
+
+=head2 Misc
+
+=over
+
+
+=item get_parent_uid()
+
+Returns UID of the parent process.
+
+=cut
+
+sub get_parent_uid() {
+ cat_('/proc/' . getppid() . '/status') =~ /Uid:\s*(\d+)/ ? $1 : undef;
+}
+
+=item require_root_capability()
+
+Ensure we are running as root.
+
+=cut
+
sub require_root_capability() {
return if $::testing || !$>; # we're already root
- if (check_for_xserver()) {
- if (fuzzy_pidofs(qr/\bkwin\b/) > 0) {
- exec("kdesu", "--ignorebutton", "-c", "$0 @ARGV") or die N("kdesu missing");
- }
- }
- exec { 'consolehelper' } $0, @ARGV or die N("consolehelper missing");
- # still not root ?
- die "you must be root to run this program" if $>;
+ die "you must be root to run this program";
}
+=item check_for_xserver()
+
+Test if we have access to X11.
+This eg enables interactive to use the proper backend (either text mode or
+GUI)
+
+=cut
+
sub check_for_xserver() {
if (!defined $::xtest) {
- $::xtest = $ENV{DISPLAY} && system('/usr/X11R6/bin/xtest') == 0;
+ $::xtest = 0;
+ eval {
+ require Gtk3;
+ Gtk3->import;
+ $::xtest = Gtk3::init_check();
+ } if $ENV{DISPLAY};
}
return $::xtest;
}
+=item unpack_with_refs($format, $s)
+
+special unpack:
+
+=over 4
+
+=item * returning an array refs for each element like C<s10>
+
+=item * handling things like C<s10*> at the end of the format
+
+=back
+
+=cut
+
+sub unpack_with_refs {
+ my ($format, $s) = @_;
+ my $initial_format = $format;
+ my @r;
+ while ($format =~ s/\s*(\w(\d*))(\*?)\s*//) {
+ my ($sub_format, $nb, $many) = ($1, $2, $3);
+ $many && $format and internal_error("bad * in the middle of format in $initial_format");
+
+ my $done = $many && !length($s);
+ while (!$done) {
+ my @l = unpack("$sub_format a*", $s);
+ $s = pop @l;
+ push @r, $nb ? \@l : @l;
+ $done = !$many || !length($s);
+ }
+ }
+ @r;
+}
+
+=item md5file(@files)
+
+Returns the MD5 signatures of @files in list context or signature of the first one in scalar context.
+
+=cut
+
+#- used in userdrake and mdkonline
+sub md5file {
+ require Digest::MD5;
+ my @md5 = map {
+ my $sum;
+ if (open(my $FILE, $_)) {
+ binmode($FILE);
+ $sum = Digest::MD5->new->addfile($FILE)->hexdigest;
+ close($FILE);
+ }
+ $sum;
+ } @_;
+ return wantarray() ? @md5 : $md5[0];
+}
+
+sub load_modules_from_base {
+ my ($base) = @_;
+ $base =~ s|::|/|g;
+ my $base_file = $base . ".pm";
+ require $base_file;
+ my ($inc_path) = substr($INC{$base_file}, 0, -length($base_file));
+ my @files = map { substr($_, length($inc_path)) } glob_($inc_path . $base . '/*.pm');
+ require $_ foreach @files;
+ #- return the matching modules list
+ map { local $_ = $_; s|/|::|g; s|\.pm$||g; $_ } @files;
+}
+
+sub get_alternatives {
+ my ($name) = @_;
+
+ my $dir = $::prefix . '/var/lib/rpm/alternatives';
+ my ($state, $main_link, @l) = chomp_(cat_("$dir/$name")) or return;
+ my @slaves;
+ while (@l && $l[0] ne '') {
+ my ($name, $link) = splice(@l, 0, 2);
+ push @slaves, { name => $name, link => $link };
+ }
+ shift @l; #- empty line
+ my @alternatives;
+ while (@l && $l[0] ne '') {
+ my ($file, $weight, @slave_files) = splice(@l, 0, 2 + @slaves);
+
+ push @alternatives, { file => $file, weight => $weight, slave_files => \@slave_files };
+ }
+ { name => $name, link => $main_link, state => $state, slaves => \@slaves, alternatives => \@alternatives };
+}
+
+sub symlinkf_update_alternatives {
+ my ($name, $wanted_file) = @_;
+ run_program::rooted($::prefix, 'update-alternatives', '--set', $name, $wanted_file);
+}
+
+sub update_gnomekderc_no_create {
+ my ($file, $category, %subst_) = @_;
+ if (-e $file) {
+ update_gnomekderc($file, $category, %subst_);
+ }
+}
+
+sub get_libdir() {
+ arch() =~ /x86_64/ ? "lib64" : "lib";
+}
+
+=item is_uefi()
+
+Checks if we are on an uefi system
+
+=cut
+
+sub is_uefi() { -e "/sys/firmware/efi" }
+
+=item kernel_uefi_type()
+
+Returns the short name of the UEFI machine type supported by the kernel stub loader
+
+=cut
+
+sub kernel_uefi_type() {
+ my $arch = arch();
+ if ($arch eq 'aarch64') {
+ return 'aa64';
+ }
+ $arch =~ /i.86/ ? 'ia32' : 'x64';
+}
+
+=item uefi_type()
+
+Returns the UEFI machine type short name
+
+=cut
+
+sub uefi_type() {
+ if (arch() eq 'aarch64') {
+ return 'aa64';
+ }
+ if (-e '/sys/firmware/efi/fw_platform_size') {
+ cat_('/sys/firmware/efi/fw_platform_size') =~ /32/ ? 'ia32' : 'x64';
+ } else {
+ 'none';
+ }
+}
+
+=item mount_efivars()
+
+Ensures the efivarfs pseudo-filesystem is mounted in the default location
+and returns the mount point and whether it was already mounted.
+
+=cut
+
+sub mount_efivars {
+ my $mount_point = "$::prefix/sys/firmware/efi/efivars";
+ my $already_mounted = cat_("$::prefix/proc/mounts") =~ /\s$mount_point\s/;
+ run_program::run('mount', '-t', 'efivarfs', 'none', $mount_point) if !$already_mounted;
+ ($mount_point, $already_mounted);
+}
+
+=item cmp_kernel_versions($va, $vb)
+
+Compare two kernel versions
+
+=cut
+
+sub cmp_kernel_versions {
+ my ($va, $vb) = @_;
+ my $rel_a = $va =~ s/-(.*)$// && $1;
+ my $rel_b = $vb =~ s/-(.*)$// && $1;
+ ($va, $vb) = map { [ split /[.-]/ ] } $va, $vb;
+ my $r = 0;
+ mapn_ {
+ $r ||= $_[0] <=> $_[1];
+ } $va, $vb;
+ $r || $rel_a <=> $rel_b || $rel_a cmp $rel_b;
+}
+
+=back
+
+=cut
1;