[8/9] pakfire: Add getmetadata function

Message ID 20220309225655.4472-9-robin.roevens@disroot.org
State Superseded
Headers
Series pakfire: remove dup. code + seperate ui/logic |

Commit Message

Robin Roevens March 9, 2022, 10:56 p.m. UTC
  - Added new getmetadata function for easy access to all available
  metadata of a pak without knowledge about or need to parse
  pakfire internal db files.
- Added new 'pakfire info' functionality for displaying all available
  metadata of (a) pak(s) to the user, using the new getmetadata.
- Use getmetadata function in services.cgi to determine installed
  addon services to display. Removing code duplication and intel that
  should only be known by pakfire itself.

Signed-off-by: Robin Roevens <robin.roevens@disroot.org>
---
 html/cgi-bin/services.cgi    | 81 ++++++++++++++++++------------------
 src/pakfire/lib/functions.pl | 55 ++++++++++++++++++++++++
 src/pakfire/pakfire          | 58 ++++++++++++++++++++++++++
 3 files changed, 154 insertions(+), 40 deletions(-)
  

Comments

Michael Tremer March 21, 2022, 4:32 p.m. UTC | #1
Hello,

> On 9 Mar 2022, at 22:56, Robin Roevens <robin.roevens@disroot.org> wrote:
> 
> - Added new getmetadata function for easy access to all available
>  metadata of a pak without knowledge about or need to parse
>  pakfire internal db files.
> - Added new 'pakfire info' functionality for displaying all available
>  metadata of (a) pak(s) to the user, using the new getmetadata.
> - Use getmetadata function in services.cgi to determine installed
>  addon services to display. Removing code duplication and intel that
>  should only be known by pakfire itself.
> 
> Signed-off-by: Robin Roevens <robin.roevens@disroot.org>
> ---
> html/cgi-bin/services.cgi    | 81 ++++++++++++++++++------------------
> src/pakfire/lib/functions.pl | 55 ++++++++++++++++++++++++
> src/pakfire/pakfire          | 58 ++++++++++++++++++++++++++
> 3 files changed, 154 insertions(+), 40 deletions(-)
> 
> diff --git a/html/cgi-bin/services.cgi b/html/cgi-bin/services.cgi
> index 237475735..896c95ec3 100644
> --- a/html/cgi-bin/services.cgi
> +++ b/html/cgi-bin/services.cgi
> @@ -29,6 +29,7 @@ require '/var/ipfire/general-functions.pl';
> require "${General::swroot}/lang.pl";
> require "${General::swroot}/header.pl";
> require "${General::swroot}/graphs.pl";
> +require "/opt/pakfire/lib/functions.pl";
> 
> my %color = ();
> my %mainsettings = ();
> @@ -160,51 +161,51 @@ END
> 
> 	my $lines=0; # Used to count the outputlines to make different bgcolor
> 
> -	# Generate list of installed addon pak's
> -	opendir (DIR, "/opt/pakfire/db/installed") || die "Cannot opendir /opt/pakfire/db/installed/: $!";
> -	my @pak = sort readdir DIR;
> -	closedir(DIR);
> -
> -	foreach (@pak){
> -		chomp($_);
> -		next unless (m/^meta-/);
> -		s/^meta-//;
> -
> -		# Check which of the paks are services
> -		if (-e "/etc/init.d/$_") {
> -			# blacklist some packages
> -			#
> -			# alsa has trouble with the volume saving and was not really stopped
> -			# mdadm should not stopped with webif because this could crash the system
> -			#
> -			if ( $_ eq 'squid' ) {
> -				next;
> -			}
> -			if ( ($_ ne "alsa") && ($_ ne "mdadm") ) {
> -				$lines++;
> -				if ($lines % 2){
> -					print "<tr>";
> -					$col="bgcolor='$color{'color22'}'";
> -				}else{
> -					print "<tr>";
> -					$col="bgcolor='$color{'color20'}'";
> -				}
> +	my @paks;
> +	my @addon_services;
> 
> -				print "<td align='left' $col width='31%'>$_</td> ";
> -				my $status = isautorun($_,$col);
> -				print "$status ";
> -				print "<td align='center' $col width='8%'><a href='services.cgi?$_!start'><img alt='$Lang::tr{'start'}' title='$Lang::tr{'start'}' src='/images/go-up.png' border='0' /></a></td>";
> -				print "<td align='center' $col width='8%'><a href='services.cgi?$_!stop'><img alt='$Lang::tr{'stop'}' title='$Lang::tr{'stop'}' src='/images/go-down.png' border='0' /></a></td> ";
> -				my $status = &isrunningaddon($_,$col);
> -		 		$status =~ s/\\[[0-1]\;[0-9]+m//g;
> -
> -				chomp($status);
> -				print "$status";
> -				print "</tr>";
> +	# Generate list of installed addon pak services
> +	my %paklist = &Pakfire::dblist("installed");
> +
> +	foreach my $pak (keys %paklist) {
> +		my %metadata = &Pakfire::getmetadata($pak, "installed");
> +			
> +		if ("$metadata{'Services'}") {
> +			foreach my $service (split(/ /, "$metadata{'Services'}")) {
> +				push(@addon_services, $service);
> 			}
> 		}
> 	}
> 
> +	foreach (@addon_services) {
> +		$lines++;
> +		if ($lines % 2){
> +			print "<tr>";
> +			$col="bgcolor='$color{'color22'}'";
> +		}else{
> +			print "<tr>";
> +			$col="bgcolor='$color{'color20'}'";
> +		}
> +		print "<td align='left' $col width='31%'>$_</td> ";
> +		my $status = isautorun($_,$col);
> +		print "$status ";
> +		# Don't allow user to start/stop folowing services from webui:
> +		#  - alsa has trouble with the volume saving and was not really stopped
> +		if ($_ eq "alsa") {
> +			print "<td align='center' $col width='8%'></td>";
> +			print "<td align='center' $col width='8%'></td> ";

Is this still a problem? Why do we even catch this here?

> +		} else {
> +			print "<td align='center' $col width='8%'><a href='services.cgi?$_!start'><img alt='$Lang::tr{'start'}' title='$Lang::tr{'start'}' src='/images/go-up.png' border='0' /></a></td>";
> +			print "<td align='center' $col width='8%'><a href='services.cgi?$_!stop'><img alt='$Lang::tr{'stop'}' title='$Lang::tr{'stop'}' src='/images/go-down.png' border='0' /></a></td> ";
> +		}
> +		my $status = isrunningaddon($_,$col);
> +		$status =~ s/\\[[0-1]\;[0-9]+m//g;
> +
> +		chomp($status);
> +		print "$status";
> +		print "</tr>";
> +	}
> +
> 	print "</table></div>\n";
> 	&Header::closebox();
> 
> diff --git a/src/pakfire/lib/functions.pl b/src/pakfire/lib/functions.pl
> index 028a0277e..6dda8d688 100644
> --- a/src/pakfire/lib/functions.pl
> +++ b/src/pakfire/lib/functions.pl
> @@ -115,6 +115,7 @@ sub usage {
>   &Pakfire::message("               <update> - Contacts the servers for new lists of paks.");
>   &Pakfire::message("               <upgrade> - Installs the latest version of all paks.");
>   &Pakfire::message("               <list> [installed/notinstalled/upgrade] - Outputs a list with all, installed, available or upgradeable paks.");
> +  &Pakfire::message("               <info> <pak> [<pak> ...] - Output pak metadata.");
>   &Pakfire::message("               <status> - Outputs a summary about available core upgrades, updates and a required reboot");
>   &Pakfire::message("");
>   &Pakfire::message("       Global options:");
> @@ -674,6 +675,60 @@ sub parsemetafile {
> 	return %metadata;
> }
> 
> +sub getmetadata {
> +	### This subroutine returns a hash of available info for a package
> +	#   Pass package name and type of info as argument: Pakfire::getmetadata(package, type_of_info) 
> +	#	Type_of_info can be "latest" or "installed"
> +	#   Usage is always with two argument.
> +	my ($pak, $type) = @_;
> +
> +	my %metadata = (
> +		Name => $pak, 
> +		Installed => "no",
> +		Available => "no");
> +	my %installed_metadata = ();
> +
> +	my @templine;
> +	my @file;
> +
> +	### Get available version information
> +	if ("$type" eq "latest") {
> +		### Check if package is in packages_list and get latest available version
> +		my %db = Pakfire::dblist("all");
> +		
> +		if (defined $db{$pak}) {
> +			### Get and parse latest available metadata
> +			if (getmetafile("$pak")) {
> +				%metadata = parsemetafile("$Conf::dbdir/meta/meta-$pak");
> +
> +				$metadata{'Available'} = "yes";
> +				### Rename version info fields
> +				$metadata{'AvailableProgVersion'} = delete $metadata{'ProgVersion'};
> +				$metadata{'AvailableRelease'} = delete $metadata{'Release'};

Why is the delete necessary?

> +			}
> +		}
> +	}
> +	
> +	### Parse installed pak metadata
> +	if (&isinstalled($pak) == 0) {
> +	    %installed_metadata = parsemetafile("$Conf::dbdir/installed/meta-$pak");
> +
> +		if ("$type" eq "latest" && exists($metadata{'AvailableProgVersion'})) {
> +			### Add installed version info to latest metadata
> +			$metadata{'ProgVersion'} = $installed_metadata{'ProgVersion'};
> +			$metadata{'Release'} = $installed_metadata{'Release'};
> +		} else {
> +			### Use metadata of installed pak
> +			%metadata = %installed_metadata;
> +		}
> +		$metadata{'Installed'} = 'yes';
> +	} else {
> +		$metadata{'Installed'} = 'no';
> +	}
> +
> +	return %metadata;
> +}
> +
> sub decryptpak {
> 	my $pak = shift;
> 
> diff --git a/src/pakfire/pakfire b/src/pakfire/pakfire
> index 0ed8aacd4..9935481a5 100644
> --- a/src/pakfire/pakfire
> +++ b/src/pakfire/pakfire
> @@ -387,6 +387,64 @@
> 		} else {
> 			&Pakfire::message("PAKFIRE WARN: No packages where found using filter $filter.");
> 		}
> +	} elsif ("$ARGV[0]" eq "info") {
> +		shift;
> +
> +		my @paks;
> +		my $pak;
> +		foreach $pak (@ARGV) {
> +			unless ("$pak" =~ "^-") {
> +				push(@paks,$pak);
> +			}
> +		}
> +
> +		unless ("@paks") {
> +			Pakfire::message("PAKFIRE ERROR: missing package name");
> +			Pakfire::usage;
> +			exit 1;
> +		}
> +
> +		foreach $pak (@paks) {
> +			my %metadata = Pakfire::getmetadata($pak, "latest");
> +
> +			### Check if pakfile was actually found
> +			if ($metadata{'Installed'} eq "no" && $metadata{'Available'} eq "no") {
> +				Pakfire::message("PAKFIRE WARN: Pak '$pak' not found.");
> +				last;
> +			}
> +
> +			unless (defined $metadata{'Available'}) {
> +				Pakfire::message("PAKFIRE WARN: Unable to retrieve latest metadata for $pak. Information may be outdated.")
> +			}
> +
> +			### Printout metadata in a user friendly format
> +			print "Name: $metadata{'Name'}\n";
> +			print "Summary: $metadata{'Summary'}\n";
> +			if ($metadata{'Available'} eq "yes") {
> +				print "Version: $metadata{'AvailableProgVersion'}-$metadata{'AvailableRelease'}\n";
> +			} else {
> +				print "Version: $metadata{'ProgVersion'}-$metadata{'Release'}\n";
> +			}
> +			print "Size: " . Pakfire::beautifysize("$metadata{'Size'}") . "\n";
> +			print "Dependencies: $metadata{'Dependencies'}\n";
> +			print "Pakfile: $metadata{'File'}\n";
> +			print "Service InitScripts: $metadata{'Services'}\n";
> +			print "Installed: $metadata{'Installed'}\n";
> +			### Generate a pak status message
> +			if (! defined $metadata{'Available'}) {
> +				print "Status: unknown (an error occured retrieving latest pak metadata)";
> +			} elsif ($metadata{'Available'} eq "no") {
> +				print "Status: obsolete (version $metadata{'ProgVersion'}-$metadata{'Release'} is installed)\n";
> +			} elsif ($metadata{'Installed'} eq "yes" && "$metadata{'Release'}" < "$metadata{'AvailableRelease'}") {
> +				print "Status: outdated (version $metadata{'ProgVersion'}-$metadata{'Release'} is installed)\n";
> +			} elsif ($metadata{'Installed'} eq "yes") {
> +				print "Status: up-to-date\n";
> +			} else {
> +				print "Status: not installed\n";
> +			}
> +			print "\n";
> +		}
> +
> 	} elsif ("$ARGV[0]" eq "resolvedeps") {
> 		foreach (@ARGV) {
> 			next if ("$_" eq "resolvedeps");

This looks very good.

> -- 
> 2.34.1
> 
> 
> -- 
> Dit bericht is gescanned op virussen en andere gevaarlijke
> inhoud door MailScanner en lijkt schoon te zijn.
>
  
Robin Roevens March 22, 2022, 1:28 p.m. UTC | #2
Michael Tremer schreef op ma 21-03-2022 om 16:32 [+0000]:
> Hello,
> 
> > On 9 Mar 2022, at 22:56, Robin Roevens <robin.roevens@disroot.org>
> > wrote:
> > 
> > - Added new getmetadata function for easy access to all available
> >  metadata of a pak without knowledge about or need to parse
> >  pakfire internal db files.
> > - Added new 'pakfire info' functionality for displaying all
> > available
> >  metadata of (a) pak(s) to the user, using the new getmetadata.
> > - Use getmetadata function in services.cgi to determine installed
> >  addon services to display. Removing code duplication and intel
> > that
> >  should only be known by pakfire itself.
> > 
> > Signed-off-by: Robin Roevens <robin.roevens@disroot.org>
> > ---
> > html/cgi-bin/services.cgi    | 81 ++++++++++++++++++---------------
> > ---
> > src/pakfire/lib/functions.pl | 55 ++++++++++++++++++++++++
> > src/pakfire/pakfire          | 58 ++++++++++++++++++++++++++
> > 3 files changed, 154 insertions(+), 40 deletions(-)
> > 
> > diff --git a/html/cgi-bin/services.cgi b/html/cgi-bin/services.cgi
> > index 237475735..896c95ec3 100644
> > --- a/html/cgi-bin/services.cgi
> > +++ b/html/cgi-bin/services.cgi
> > @@ -29,6 +29,7 @@ require '/var/ipfire/general-functions.pl';
> > require "${General::swroot}/lang.pl";
> > require "${General::swroot}/header.pl";
> > require "${General::swroot}/graphs.pl";
> > +require "/opt/pakfire/lib/functions.pl";
> > 
> > my %color = ();
> > my %mainsettings = ();
> > @@ -160,51 +161,51 @@ END
> > 
> >         my $lines=0; # Used to count the outputlines to make
> > different bgcolor
> > 
> > -       # Generate list of installed addon pak's
> > -       opendir (DIR, "/opt/pakfire/db/installed") || die "Cannot
> > opendir /opt/pakfire/db/installed/: $!";
> > -       my @pak = sort readdir DIR;
> > -       closedir(DIR);
> > -
> > -       foreach (@pak){
> > -               chomp($_);
> > -               next unless (m/^meta-/);
> > -               s/^meta-//;
> > -
> > -               # Check which of the paks are services
> > -               if (-e "/etc/init.d/$_") {
> > -                       # blacklist some packages
> > -                       #
> > -                       # alsa has trouble with the volume saving
> > and was not really stopped
> > -                       # mdadm should not stopped with webif
> > because this could crash the system
> > -                       #
> > -                       if ( $_ eq 'squid' ) {
> > -                               next;
> > -                       }
> > -                       if ( ($_ ne "alsa") && ($_ ne "mdadm") ) {
> > -                               $lines++;
> > -                               if ($lines % 2){
> > -                                       print "<tr>";
> > -
> >                                        $col="bgcolor='$color{'color2
> > 2'}'";
> > -                               }else{
> > -                                       print "<tr>";
> > -
> >                                        $col="bgcolor='$color{'color2
> > 0'}'";
> > -                               }
> > +       my @paks;
> > +       my @addon_services;
> > 
> > -                               print "<td align='left' $col
> > width='31%'>$_</td> ";
> > -                               my $status = isautorun($_,$col);
> > -                               print "$status ";
> > -                               print "<td align='center' $col
> > width='8%'><a href='services.cgi?$_!start'><img
> > alt='$Lang::tr{'start'}' title='$Lang::tr{'start'}'
> > src='/images/go-up.png' border='0' /></a></td>";
> > -                               print "<td align='center' $col
> > width='8%'><a href='services.cgi?$_!stop'><img
> > alt='$Lang::tr{'stop'}' title='$Lang::tr{'stop'}' src='/images/go-
> > down.png' border='0' /></a></td> ";
> > -                               my $status =
> > &isrunningaddon($_,$col);
> > -                               $status =~ s/\\[[0-1]\;[0-9]+m//g;
> > -
> > -                               chomp($status);
> > -                               print "$status";
> > -                               print "</tr>";
> > +       # Generate list of installed addon pak services
> > +       my %paklist = &Pakfire::dblist("installed");
> > +
> > +       foreach my $pak (keys %paklist) {
> > +               my %metadata = &Pakfire::getmetadata($pak,
> > "installed");
> > +                       
> > +               if ("$metadata{'Services'}") {
> > +                       foreach my $service (split(/ /,
> > "$metadata{'Services'}")) {
> > +                               push(@addon_services, $service);
> >                         }
> >                 }
> >         }
> > 
> > +       foreach (@addon_services) {
> > +               $lines++;
> > +               if ($lines % 2){
> > +                       print "<tr>";
> > +                       $col="bgcolor='$color{'color22'}'";
> > +               }else{
> > +                       print "<tr>";
> > +                       $col="bgcolor='$color{'color20'}'";
> > +               }
> > +               print "<td align='left' $col width='31%'>$_</td> ";
> > +               my $status = isautorun($_,$col);
> > +               print "$status ";
> > +               # Don't allow user to start/stop folowing services
> > from webui:
> > +               #  - alsa has trouble with the volume saving and
> > was not really stopped
> > +               if ($_ eq "alsa") {
> > +                       print "<td align='center' $col
> > width='8%'></td>";
> > +                       print "<td align='center' $col
> > width='8%'></td> ";
> 
> Is this still a problem? Why do we even catch this here?
Not sure.. Never tested it. I will try to set up a test env with a
sound card .. not sure if I will succeed. never started a vm with sound
here before .. So if someone else can test this more easily ?

> 
> > +               } else {
> > +                       print "<td align='center' $col
> > width='8%'><a href='services.cgi?$_!start'><img
> > alt='$Lang::tr{'start'}' title='$Lang::tr{'start'}'
> > src='/images/go-up.png' border='0' /></a></td>";
> > +                       print "<td align='center' $col
> > width='8%'><a href='services.cgi?$_!stop'><img
> > alt='$Lang::tr{'stop'}' title='$Lang::tr{'stop'}' src='/images/go-
> > down.png' border='0' /></a></td> ";
> > +               }
> > +               my $status = isrunningaddon($_,$col);
> > +               $status =~ s/\\[[0-1]\;[0-9]+m//g;
> > +
> > +               chomp($status);
> > +               print "$status";
> > +               print "</tr>";
> > +       }
> > +
> >         print "</table></div>\n";
> >         &Header::closebox();
> > 
> > diff --git a/src/pakfire/lib/functions.pl
> > b/src/pakfire/lib/functions.pl
> > index 028a0277e..6dda8d688 100644
> > --- a/src/pakfire/lib/functions.pl
> > +++ b/src/pakfire/lib/functions.pl
> > @@ -115,6 +115,7 @@ sub usage {
> >   &Pakfire::message("               <update> - Contacts the servers
> > for new lists of paks.");
> >   &Pakfire::message("               <upgrade> - Installs the latest
> > version of all paks.");
> >   &Pakfire::message("               <list>
> > [installed/notinstalled/upgrade] - Outputs a list with all,
> > installed, available or upgradeable paks.");
> > +  &Pakfire::message("               <info> <pak> [<pak> ...] -
> > Output pak metadata.");
> >   &Pakfire::message("               <status> - Outputs a summary
> > about available core upgrades, updates and a required reboot");
> >   &Pakfire::message("");
> >   &Pakfire::message("       Global options:");
> > @@ -674,6 +675,60 @@ sub parsemetafile {
> >         return %metadata;
> > }
> > 
> > +sub getmetadata {
> > +       ### This subroutine returns a hash of available info for a
> > package
> > +       #   Pass package name and type of info as argument:
> > Pakfire::getmetadata(package, type_of_info) 
> > +       #       Type_of_info can be "latest" or "installed"
> > +       #   Usage is always with two argument.
> > +       my ($pak, $type) = @_;
> > +
> > +       my %metadata = (
> > +               Name => $pak, 
> > +               Installed => "no",
> > +               Available => "no");
> > +       my %installed_metadata = ();
> > +
> > +       my @templine;
> > +       my @file;
> > +
> > +       ### Get available version information
> > +       if ("$type" eq "latest") {
> > +               ### Check if package is in packages_list and get
> > latest available version
> > +               my %db = Pakfire::dblist("all");
> > +               
> > +               if (defined $db{$pak}) {
> > +                       ### Get and parse latest available metadata
> > +                       if (getmetafile("$pak")) {
> > +                               %metadata =
> > parsemetafile("$Conf::dbdir/meta/meta-$pak");
> > +
> > +                               $metadata{'Available'} = "yes";
> > +                               ### Rename version info fields
> > +                               $metadata{'AvailableProgVersion'} =
> > delete $metadata{'ProgVersion'};
> > +                               $metadata{'AvailableRelease'} =
> > delete $metadata{'Release'};
> 
> Why is the delete necessary?

This is actually a rename of the key from 'ProgVersion' to
'AvailableProgVersion'; delete removes the key from the hash, and
returns its value, which is added to the hash as the new key
'Available...'. 
If I would remove the delete, the same info would be in the hash, once
in 'ProgVersion' and once in 'AvailableProgVersion'.
And in case the pak is not installed, it would be returned as such from
this function; while existence of 'ProgVersion'/'Release' sort of
implicates that the pak is installed. (Checks should be based on
'Installed' = yes/no but it could lead to confusion to keep them)

> 
> > +                       }
> > +               }
> > +       }
> > +       
> > +       ### Parse installed pak metadata
> > +       if (&isinstalled($pak) == 0) {
> > +           %installed_metadata =
> > parsemetafile("$Conf::dbdir/installed/meta-$pak");
> > +
> > +               if ("$type" eq "latest" &&
> > exists($metadata{'AvailableProgVersion'})) {
> > +                       ### Add installed version info to latest
> > metadata
> > +                       $metadata{'ProgVersion'} =
> > $installed_metadata{'ProgVersion'};
> > +                       $metadata{'Release'} =
> > $installed_metadata{'Release'};
> > +               } else {
> > +                       ### Use metadata of installed pak
> > +                       %metadata = %installed_metadata;
> > +               }
> > +               $metadata{'Installed'} = 'yes';
> > +       } else {
> > +               $metadata{'Installed'} = 'no';
> > +       }
> > +
> > +       return %metadata;
> > +}
> > +
> > sub decryptpak {
> >         my $pak = shift;
> > 
> > diff --git a/src/pakfire/pakfire b/src/pakfire/pakfire
> > index 0ed8aacd4..9935481a5 100644
> > --- a/src/pakfire/pakfire
> > +++ b/src/pakfire/pakfire
> > @@ -387,6 +387,64 @@
> >                 } else {
> >                         &Pakfire::message("PAKFIRE WARN: No
> > packages where found using filter $filter.");
> >                 }
> > +       } elsif ("$ARGV[0]" eq "info") {
> > +               shift;
> > +
> > +               my @paks;
> > +               my $pak;
> > +               foreach $pak (@ARGV) {
> > +                       unless ("$pak" =~ "^-") {
> > +                               push(@paks,$pak);
> > +                       }
> > +               }
> > +
> > +               unless ("@paks") {
> > +                       Pakfire::message("PAKFIRE ERROR: missing
> > package name");
> > +                       Pakfire::usage;
> > +                       exit 1;
> > +               }
> > +
> > +               foreach $pak (@paks) {
> > +                       my %metadata = Pakfire::getmetadata($pak,
> > "latest");
> > +
> > +                       ### Check if pakfile was actually found
> > +                       if ($metadata{'Installed'} eq "no" &&
> > $metadata{'Available'} eq "no") {
> > +                               Pakfire::message("PAKFIRE WARN: Pak
> > '$pak' not found.");
> > +                               last;
> > +                       }
> > +
> > +                       unless (defined $metadata{'Available'}) {
> > +                               Pakfire::message("PAKFIRE WARN:
> > Unable to retrieve latest metadata for $pak. Information may be
> > outdated.")
> > +                       }
> > +
> > +                       ### Printout metadata in a user friendly
> > format
> > +                       print "Name: $metadata{'Name'}\n";
> > +                       print "Summary: $metadata{'Summary'}\n";
> > +                       if ($metadata{'Available'} eq "yes") {
> > +                               print "Version:
> > $metadata{'AvailableProgVersion'}-$metadata{'AvailableRelease'}\n";
> > +                       } else {
> > +                               print "Version:
> > $metadata{'ProgVersion'}-$metadata{'Release'}\n";
> > +                       }
> > +                       print "Size: " .
> > Pakfire::beautifysize("$metadata{'Size'}") . "\n";
> > +                       print "Dependencies:
> > $metadata{'Dependencies'}\n";
> > +                       print "Pakfile: $metadata{'File'}\n";
> > +                       print "Service InitScripts:
> > $metadata{'Services'}\n";
> > +                       print "Installed:
> > $metadata{'Installed'}\n";
> > +                       ### Generate a pak status message
> > +                       if (! defined $metadata{'Available'}) {
> > +                               print "Status: unknown (an error
> > occured retrieving latest pak metadata)";
> > +                       } elsif ($metadata{'Available'} eq "no") {
> > +                               print "Status: obsolete (version
> > $metadata{'ProgVersion'}-$metadata{'Release'} is installed)\n";
> > +                       } elsif ($metadata{'Installed'} eq "yes" &&
> > "$metadata{'Release'}" < "$metadata{'AvailableRelease'}") {
> > +                               print "Status: outdated (version
> > $metadata{'ProgVersion'}-$metadata{'Release'} is installed)\n";
> > +                       } elsif ($metadata{'Installed'} eq "yes") {
> > +                               print "Status: up-to-date\n";
> > +                       } else {
> > +                               print "Status: not installed\n";
> > +                       }
> > +                       print "\n";
> > +               }
> > +
> >         } elsif ("$ARGV[0]" eq "resolvedeps") {
> >                 foreach (@ARGV) {
> >                         next if ("$_" eq "resolvedeps");
> 
> This looks very good.
> 
> > -- 
> > 2.34.1
> > 
> > 
> > -- 
> > Dit bericht is gescanned op virussen en andere gevaarlijke
> > inhoud door MailScanner en lijkt schoon te zijn.
> > 
> 
>
  
Michael Tremer March 23, 2022, 11:28 a.m. UTC | #3
Hey,

> On 22 Mar 2022, at 13:28, Robin Roevens <robin.roevens@disroot.org> wrote:
> 
> 
> Michael Tremer schreef op ma 21-03-2022 om 16:32 [+0000]:
>> Hello,
>> 
>>> On 9 Mar 2022, at 22:56, Robin Roevens <robin.roevens@disroot.org>
>>> wrote:
>>> 
>>> - Added new getmetadata function for easy access to all available
>>>  metadata of a pak without knowledge about or need to parse
>>>  pakfire internal db files.
>>> - Added new 'pakfire info' functionality for displaying all
>>> available
>>>  metadata of (a) pak(s) to the user, using the new getmetadata.
>>> - Use getmetadata function in services.cgi to determine installed
>>>  addon services to display. Removing code duplication and intel
>>> that
>>>  should only be known by pakfire itself.
>>> 
>>> Signed-off-by: Robin Roevens <robin.roevens@disroot.org>
>>> ---
>>> html/cgi-bin/services.cgi    | 81 ++++++++++++++++++---------------
>>> ---
>>> src/pakfire/lib/functions.pl | 55 ++++++++++++++++++++++++
>>> src/pakfire/pakfire          | 58 ++++++++++++++++++++++++++
>>> 3 files changed, 154 insertions(+), 40 deletions(-)
>>> 
>>> diff --git a/html/cgi-bin/services.cgi b/html/cgi-bin/services.cgi
>>> index 237475735..896c95ec3 100644
>>> --- a/html/cgi-bin/services.cgi
>>> +++ b/html/cgi-bin/services.cgi
>>> @@ -29,6 +29,7 @@ require '/var/ipfire/general-functions.pl';
>>> require "${General::swroot}/lang.pl";
>>> require "${General::swroot}/header.pl";
>>> require "${General::swroot}/graphs.pl";
>>> +require "/opt/pakfire/lib/functions.pl";
>>> 
>>> my %color = ();
>>> my %mainsettings = ();
>>> @@ -160,51 +161,51 @@ END
>>> 
>>>         my $lines=0; # Used to count the outputlines to make
>>> different bgcolor
>>> 
>>> -       # Generate list of installed addon pak's
>>> -       opendir (DIR, "/opt/pakfire/db/installed") || die "Cannot
>>> opendir /opt/pakfire/db/installed/: $!";
>>> -       my @pak = sort readdir DIR;
>>> -       closedir(DIR);
>>> -
>>> -       foreach (@pak){
>>> -               chomp($_);
>>> -               next unless (m/^meta-/);
>>> -               s/^meta-//;
>>> -
>>> -               # Check which of the paks are services
>>> -               if (-e "/etc/init.d/$_") {
>>> -                       # blacklist some packages
>>> -                       #
>>> -                       # alsa has trouble with the volume saving
>>> and was not really stopped
>>> -                       # mdadm should not stopped with webif
>>> because this could crash the system
>>> -                       #
>>> -                       if ( $_ eq 'squid' ) {
>>> -                               next;
>>> -                       }
>>> -                       if ( ($_ ne "alsa") && ($_ ne "mdadm") ) {
>>> -                               $lines++;
>>> -                               if ($lines % 2){
>>> -                                       print "<tr>";
>>> -
>>>                                        $col="bgcolor='$color{'color2
>>> 2'}'";
>>> -                               }else{
>>> -                                       print "<tr>";
>>> -
>>>                                        $col="bgcolor='$color{'color2
>>> 0'}'";
>>> -                               }
>>> +       my @paks;
>>> +       my @addon_services;
>>> 
>>> -                               print "<td align='left' $col
>>> width='31%'>$_</td> ";
>>> -                               my $status = isautorun($_,$col);
>>> -                               print "$status ";
>>> -                               print "<td align='center' $col
>>> width='8%'><a href='services.cgi?$_!start'><img
>>> alt='$Lang::tr{'start'}' title='$Lang::tr{'start'}'
>>> src='/images/go-up.png' border='0' /></a></td>";
>>> -                               print "<td align='center' $col
>>> width='8%'><a href='services.cgi?$_!stop'><img
>>> alt='$Lang::tr{'stop'}' title='$Lang::tr{'stop'}' src='/images/go-
>>> down.png' border='0' /></a></td> ";
>>> -                               my $status =
>>> &isrunningaddon($_,$col);
>>> -                               $status =~ s/\\[[0-1]\;[0-9]+m//g;
>>> -
>>> -                               chomp($status);
>>> -                               print "$status";
>>> -                               print "</tr>";
>>> +       # Generate list of installed addon pak services
>>> +       my %paklist = &Pakfire::dblist("installed");
>>> +
>>> +       foreach my $pak (keys %paklist) {
>>> +               my %metadata = &Pakfire::getmetadata($pak,
>>> "installed");
>>> +                       
>>> +               if ("$metadata{'Services'}") {
>>> +                       foreach my $service (split(/ /,
>>> "$metadata{'Services'}")) {
>>> +                               push(@addon_services, $service);
>>>                         }
>>>                 }
>>>         }
>>> 
>>> +       foreach (@addon_services) {
>>> +               $lines++;
>>> +               if ($lines % 2){
>>> +                       print "<tr>";
>>> +                       $col="bgcolor='$color{'color22'}'";
>>> +               }else{
>>> +                       print "<tr>";
>>> +                       $col="bgcolor='$color{'color20'}'";
>>> +               }
>>> +               print "<td align='left' $col width='31%'>$_</td> ";
>>> +               my $status = isautorun($_,$col);
>>> +               print "$status ";
>>> +               # Don't allow user to start/stop folowing services
>>> from webui:
>>> +               #  - alsa has trouble with the volume saving and
>>> was not really stopped
>>> +               if ($_ eq "alsa") {
>>> +                       print "<td align='center' $col
>>> width='8%'></td>";
>>> +                       print "<td align='center' $col
>>> width='8%'></td> ";
>> 
>> Is this still a problem? Why do we even catch this here?
> Not sure.. Never tested it. I will try to set up a test env with a
> sound card .. not sure if I will succeed. never started a vm with sound
> here before .. So if someone else can test this more easily ?

VMs can easily emulate sound hardware, but I wouldn’t even bother here. This should just work and if it doesn’t that should be caught in the initscript and not in some random CGI file.

> 
>> 
>>> +               } else {
>>> +                       print "<td align='center' $col
>>> width='8%'><a href='services.cgi?$_!start'><img
>>> alt='$Lang::tr{'start'}' title='$Lang::tr{'start'}'
>>> src='/images/go-up.png' border='0' /></a></td>";
>>> +                       print "<td align='center' $col
>>> width='8%'><a href='services.cgi?$_!stop'><img
>>> alt='$Lang::tr{'stop'}' title='$Lang::tr{'stop'}' src='/images/go-
>>> down.png' border='0' /></a></td> ";
>>> +               }
>>> +               my $status = isrunningaddon($_,$col);
>>> +               $status =~ s/\\[[0-1]\;[0-9]+m//g;
>>> +
>>> +               chomp($status);
>>> +               print "$status";
>>> +               print "</tr>";
>>> +       }
>>> +
>>>         print "</table></div>\n";
>>>         &Header::closebox();
>>> 
>>> diff --git a/src/pakfire/lib/functions.pl
>>> b/src/pakfire/lib/functions.pl
>>> index 028a0277e..6dda8d688 100644
>>> --- a/src/pakfire/lib/functions.pl
>>> +++ b/src/pakfire/lib/functions.pl
>>> @@ -115,6 +115,7 @@ sub usage {
>>>   &Pakfire::message("               <update> - Contacts the servers
>>> for new lists of paks.");
>>>   &Pakfire::message("               <upgrade> - Installs the latest
>>> version of all paks.");
>>>   &Pakfire::message("               <list>
>>> [installed/notinstalled/upgrade] - Outputs a list with all,
>>> installed, available or upgradeable paks.");
>>> +  &Pakfire::message("               <info> <pak> [<pak> ...] -
>>> Output pak metadata.");
>>>   &Pakfire::message("               <status> - Outputs a summary
>>> about available core upgrades, updates and a required reboot");
>>>   &Pakfire::message("");
>>>   &Pakfire::message("       Global options:");
>>> @@ -674,6 +675,60 @@ sub parsemetafile {
>>>         return %metadata;
>>> }
>>> 
>>> +sub getmetadata {
>>> +       ### This subroutine returns a hash of available info for a
>>> package
>>> +       #   Pass package name and type of info as argument:
>>> Pakfire::getmetadata(package, type_of_info) 
>>> +       #       Type_of_info can be "latest" or "installed"
>>> +       #   Usage is always with two argument.
>>> +       my ($pak, $type) = @_;
>>> +
>>> +       my %metadata = (
>>> +               Name => $pak, 
>>> +               Installed => "no",
>>> +               Available => "no");
>>> +       my %installed_metadata = ();
>>> +
>>> +       my @templine;
>>> +       my @file;
>>> +
>>> +       ### Get available version information
>>> +       if ("$type" eq "latest") {
>>> +               ### Check if package is in packages_list and get
>>> latest available version
>>> +               my %db = Pakfire::dblist("all");
>>> +               
>>> +               if (defined $db{$pak}) {
>>> +                       ### Get and parse latest available metadata
>>> +                       if (getmetafile("$pak")) {
>>> +                               %metadata =
>>> parsemetafile("$Conf::dbdir/meta/meta-$pak");
>>> +
>>> +                               $metadata{'Available'} = "yes";
>>> +                               ### Rename version info fields
>>> +                               $metadata{'AvailableProgVersion'} =
>>> delete $metadata{'ProgVersion'};
>>> +                               $metadata{'AvailableRelease'} =
>>> delete $metadata{'Release'};
>> 
>> Why is the delete necessary?
> 
> This is actually a rename of the key from 'ProgVersion' to
> 'AvailableProgVersion'; delete removes the key from the hash, and
> returns its value, which is added to the hash as the new key
> 'Available...'. 
> If I would remove the delete, the same info would be in the hash, once
> in 'ProgVersion' and once in 'AvailableProgVersion'.
> And in case the pak is not installed, it would be returned as such from
> this function; while existence of 'ProgVersion'/'Release' sort of
> implicates that the pak is installed. (Checks should be based on
> 'Installed' = yes/no but it could lead to confusion to keep them)

Hmm, okay.

> 
>> 
>>> +                       }
>>> +               }
>>> +       }
>>> +       
>>> +       ### Parse installed pak metadata
>>> +       if (&isinstalled($pak) == 0) {
>>> +           %installed_metadata =
>>> parsemetafile("$Conf::dbdir/installed/meta-$pak");
>>> +
>>> +               if ("$type" eq "latest" &&
>>> exists($metadata{'AvailableProgVersion'})) {
>>> +                       ### Add installed version info to latest
>>> metadata
>>> +                       $metadata{'ProgVersion'} =
>>> $installed_metadata{'ProgVersion'};
>>> +                       $metadata{'Release'} =
>>> $installed_metadata{'Release'};
>>> +               } else {
>>> +                       ### Use metadata of installed pak
>>> +                       %metadata = %installed_metadata;
>>> +               }
>>> +               $metadata{'Installed'} = 'yes';
>>> +       } else {
>>> +               $metadata{'Installed'} = 'no';
>>> +       }
>>> +
>>> +       return %metadata;
>>> +}
>>> +
>>> sub decryptpak {
>>>         my $pak = shift;
>>> 
>>> diff --git a/src/pakfire/pakfire b/src/pakfire/pakfire
>>> index 0ed8aacd4..9935481a5 100644
>>> --- a/src/pakfire/pakfire
>>> +++ b/src/pakfire/pakfire
>>> @@ -387,6 +387,64 @@
>>>                 } else {
>>>                         &Pakfire::message("PAKFIRE WARN: No
>>> packages where found using filter $filter.");
>>>                 }
>>> +       } elsif ("$ARGV[0]" eq "info") {
>>> +               shift;
>>> +
>>> +               my @paks;
>>> +               my $pak;
>>> +               foreach $pak (@ARGV) {
>>> +                       unless ("$pak" =~ "^-") {
>>> +                               push(@paks,$pak);
>>> +                       }
>>> +               }
>>> +
>>> +               unless ("@paks") {
>>> +                       Pakfire::message("PAKFIRE ERROR: missing
>>> package name");
>>> +                       Pakfire::usage;
>>> +                       exit 1;
>>> +               }
>>> +
>>> +               foreach $pak (@paks) {
>>> +                       my %metadata = Pakfire::getmetadata($pak,
>>> "latest");
>>> +
>>> +                       ### Check if pakfile was actually found
>>> +                       if ($metadata{'Installed'} eq "no" &&
>>> $metadata{'Available'} eq "no") {
>>> +                               Pakfire::message("PAKFIRE WARN: Pak
>>> '$pak' not found.");
>>> +                               last;
>>> +                       }
>>> +
>>> +                       unless (defined $metadata{'Available'}) {
>>> +                               Pakfire::message("PAKFIRE WARN:
>>> Unable to retrieve latest metadata for $pak. Information may be
>>> outdated.")
>>> +                       }
>>> +
>>> +                       ### Printout metadata in a user friendly
>>> format
>>> +                       print "Name: $metadata{'Name'}\n";
>>> +                       print "Summary: $metadata{'Summary'}\n";
>>> +                       if ($metadata{'Available'} eq "yes") {
>>> +                               print "Version:
>>> $metadata{'AvailableProgVersion'}-$metadata{'AvailableRelease'}\n";
>>> +                       } else {
>>> +                               print "Version:
>>> $metadata{'ProgVersion'}-$metadata{'Release'}\n";
>>> +                       }
>>> +                       print "Size: " .
>>> Pakfire::beautifysize("$metadata{'Size'}") . "\n";
>>> +                       print "Dependencies:
>>> $metadata{'Dependencies'}\n";
>>> +                       print "Pakfile: $metadata{'File'}\n";
>>> +                       print "Service InitScripts:
>>> $metadata{'Services'}\n";
>>> +                       print "Installed:
>>> $metadata{'Installed'}\n";
>>> +                       ### Generate a pak status message
>>> +                       if (! defined $metadata{'Available'}) {
>>> +                               print "Status: unknown (an error
>>> occured retrieving latest pak metadata)";
>>> +                       } elsif ($metadata{'Available'} eq "no") {
>>> +                               print "Status: obsolete (version
>>> $metadata{'ProgVersion'}-$metadata{'Release'} is installed)\n";
>>> +                       } elsif ($metadata{'Installed'} eq "yes" &&
>>> "$metadata{'Release'}" < "$metadata{'AvailableRelease'}") {
>>> +                               print "Status: outdated (version
>>> $metadata{'ProgVersion'}-$metadata{'Release'} is installed)\n";
>>> +                       } elsif ($metadata{'Installed'} eq "yes") {
>>> +                               print "Status: up-to-date\n";
>>> +                       } else {
>>> +                               print "Status: not installed\n";
>>> +                       }
>>> +                       print "\n";
>>> +               }
>>> +
>>>         } elsif ("$ARGV[0]" eq "resolvedeps") {
>>>                 foreach (@ARGV) {
>>>                         next if ("$_" eq "resolvedeps");
>> 
>> This looks very good.
>> 
>>> -- 
>>> 2.34.1
>>> 
>>> 
>>> -- 
>>> Dit bericht is gescanned op virussen en andere gevaarlijke
>>> inhoud door MailScanner en lijkt schoon te zijn.
>>> 
>> 
>> 
> 
> -- 
> Dit bericht is gescanned op virussen en andere gevaarlijke
> inhoud door MailScanner en lijkt schoon te zijn.
  

Patch

diff --git a/html/cgi-bin/services.cgi b/html/cgi-bin/services.cgi
index 237475735..896c95ec3 100644
--- a/html/cgi-bin/services.cgi
+++ b/html/cgi-bin/services.cgi
@@ -29,6 +29,7 @@  require '/var/ipfire/general-functions.pl';
 require "${General::swroot}/lang.pl";
 require "${General::swroot}/header.pl";
 require "${General::swroot}/graphs.pl";
+require "/opt/pakfire/lib/functions.pl";
 
 my %color = ();
 my %mainsettings = ();
@@ -160,51 +161,51 @@  END
 
 	my $lines=0; # Used to count the outputlines to make different bgcolor
 
-	# Generate list of installed addon pak's
-	opendir (DIR, "/opt/pakfire/db/installed") || die "Cannot opendir /opt/pakfire/db/installed/: $!";
-	my @pak = sort readdir DIR;
-	closedir(DIR);
-
-	foreach (@pak){
-		chomp($_);
-		next unless (m/^meta-/);
-		s/^meta-//;
-
-		# Check which of the paks are services
-		if (-e "/etc/init.d/$_") {
-			# blacklist some packages
-			#
-			# alsa has trouble with the volume saving and was not really stopped
-			# mdadm should not stopped with webif because this could crash the system
-			#
-			if ( $_ eq 'squid' ) {
-				next;
-			}
-			if ( ($_ ne "alsa") && ($_ ne "mdadm") ) {
-				$lines++;
-				if ($lines % 2){
-					print "<tr>";
-					$col="bgcolor='$color{'color22'}'";
-				}else{
-					print "<tr>";
-					$col="bgcolor='$color{'color20'}'";
-				}
+	my @paks;
+	my @addon_services;
 
-				print "<td align='left' $col width='31%'>$_</td> ";
-				my $status = isautorun($_,$col);
-				print "$status ";
-				print "<td align='center' $col width='8%'><a href='services.cgi?$_!start'><img alt='$Lang::tr{'start'}' title='$Lang::tr{'start'}' src='/images/go-up.png' border='0' /></a></td>";
-				print "<td align='center' $col width='8%'><a href='services.cgi?$_!stop'><img alt='$Lang::tr{'stop'}' title='$Lang::tr{'stop'}' src='/images/go-down.png' border='0' /></a></td> ";
-				my $status = &isrunningaddon($_,$col);
-		 		$status =~ s/\\[[0-1]\;[0-9]+m//g;
-
-				chomp($status);
-				print "$status";
-				print "</tr>";
+	# Generate list of installed addon pak services
+	my %paklist = &Pakfire::dblist("installed");
+
+	foreach my $pak (keys %paklist) {
+		my %metadata = &Pakfire::getmetadata($pak, "installed");
+			
+		if ("$metadata{'Services'}") {
+			foreach my $service (split(/ /, "$metadata{'Services'}")) {
+				push(@addon_services, $service);
 			}
 		}
 	}
 
+	foreach (@addon_services) {
+		$lines++;
+		if ($lines % 2){
+			print "<tr>";
+			$col="bgcolor='$color{'color22'}'";
+		}else{
+			print "<tr>";
+			$col="bgcolor='$color{'color20'}'";
+		}
+		print "<td align='left' $col width='31%'>$_</td> ";
+		my $status = isautorun($_,$col);
+		print "$status ";
+		# Don't allow user to start/stop folowing services from webui:
+		#  - alsa has trouble with the volume saving and was not really stopped
+		if ($_ eq "alsa") {
+			print "<td align='center' $col width='8%'></td>";
+			print "<td align='center' $col width='8%'></td> ";
+		} else {
+			print "<td align='center' $col width='8%'><a href='services.cgi?$_!start'><img alt='$Lang::tr{'start'}' title='$Lang::tr{'start'}' src='/images/go-up.png' border='0' /></a></td>";
+			print "<td align='center' $col width='8%'><a href='services.cgi?$_!stop'><img alt='$Lang::tr{'stop'}' title='$Lang::tr{'stop'}' src='/images/go-down.png' border='0' /></a></td> ";
+		}
+		my $status = isrunningaddon($_,$col);
+		$status =~ s/\\[[0-1]\;[0-9]+m//g;
+
+		chomp($status);
+		print "$status";
+		print "</tr>";
+	}
+
 	print "</table></div>\n";
 	&Header::closebox();
 
diff --git a/src/pakfire/lib/functions.pl b/src/pakfire/lib/functions.pl
index 028a0277e..6dda8d688 100644
--- a/src/pakfire/lib/functions.pl
+++ b/src/pakfire/lib/functions.pl
@@ -115,6 +115,7 @@  sub usage {
   &Pakfire::message("               <update> - Contacts the servers for new lists of paks.");
   &Pakfire::message("               <upgrade> - Installs the latest version of all paks.");
   &Pakfire::message("               <list> [installed/notinstalled/upgrade] - Outputs a list with all, installed, available or upgradeable paks.");
+  &Pakfire::message("               <info> <pak> [<pak> ...] - Output pak metadata.");
   &Pakfire::message("               <status> - Outputs a summary about available core upgrades, updates and a required reboot");
   &Pakfire::message("");
   &Pakfire::message("       Global options:");
@@ -674,6 +675,60 @@  sub parsemetafile {
 	return %metadata;
 }
 
+sub getmetadata {
+	### This subroutine returns a hash of available info for a package
+	#   Pass package name and type of info as argument: Pakfire::getmetadata(package, type_of_info) 
+	#	Type_of_info can be "latest" or "installed"
+	#   Usage is always with two argument.
+	my ($pak, $type) = @_;
+
+	my %metadata = (
+		Name => $pak, 
+		Installed => "no",
+		Available => "no");
+	my %installed_metadata = ();
+
+	my @templine;
+	my @file;
+
+	### Get available version information
+	if ("$type" eq "latest") {
+		### Check if package is in packages_list and get latest available version
+		my %db = Pakfire::dblist("all");
+		
+		if (defined $db{$pak}) {
+			### Get and parse latest available metadata
+			if (getmetafile("$pak")) {
+				%metadata = parsemetafile("$Conf::dbdir/meta/meta-$pak");
+
+				$metadata{'Available'} = "yes";
+				### Rename version info fields
+				$metadata{'AvailableProgVersion'} = delete $metadata{'ProgVersion'};
+				$metadata{'AvailableRelease'} = delete $metadata{'Release'};
+			}
+		}
+	}
+	
+	### Parse installed pak metadata
+	if (&isinstalled($pak) == 0) {
+	    %installed_metadata = parsemetafile("$Conf::dbdir/installed/meta-$pak");
+
+		if ("$type" eq "latest" && exists($metadata{'AvailableProgVersion'})) {
+			### Add installed version info to latest metadata
+			$metadata{'ProgVersion'} = $installed_metadata{'ProgVersion'};
+			$metadata{'Release'} = $installed_metadata{'Release'};
+		} else {
+			### Use metadata of installed pak
+			%metadata = %installed_metadata;
+		}
+		$metadata{'Installed'} = 'yes';
+	} else {
+		$metadata{'Installed'} = 'no';
+	}
+
+	return %metadata;
+}
+
 sub decryptpak {
 	my $pak = shift;
 
diff --git a/src/pakfire/pakfire b/src/pakfire/pakfire
index 0ed8aacd4..9935481a5 100644
--- a/src/pakfire/pakfire
+++ b/src/pakfire/pakfire
@@ -387,6 +387,64 @@ 
 		} else {
 			&Pakfire::message("PAKFIRE WARN: No packages where found using filter $filter.");
 		}
+	} elsif ("$ARGV[0]" eq "info") {
+		shift;
+
+		my @paks;
+		my $pak;
+		foreach $pak (@ARGV) {
+			unless ("$pak" =~ "^-") {
+				push(@paks,$pak);
+			}
+		}
+
+		unless ("@paks") {
+			Pakfire::message("PAKFIRE ERROR: missing package name");
+			Pakfire::usage;
+			exit 1;
+		}
+
+		foreach $pak (@paks) {
+			my %metadata = Pakfire::getmetadata($pak, "latest");
+
+			### Check if pakfile was actually found
+			if ($metadata{'Installed'} eq "no" && $metadata{'Available'} eq "no") {
+				Pakfire::message("PAKFIRE WARN: Pak '$pak' not found.");
+				last;
+			}
+
+			unless (defined $metadata{'Available'}) {
+				Pakfire::message("PAKFIRE WARN: Unable to retrieve latest metadata for $pak. Information may be outdated.")
+			}
+
+			### Printout metadata in a user friendly format
+			print "Name: $metadata{'Name'}\n";
+			print "Summary: $metadata{'Summary'}\n";
+			if ($metadata{'Available'} eq "yes") {
+				print "Version: $metadata{'AvailableProgVersion'}-$metadata{'AvailableRelease'}\n";
+			} else {
+				print "Version: $metadata{'ProgVersion'}-$metadata{'Release'}\n";
+			}
+			print "Size: " . Pakfire::beautifysize("$metadata{'Size'}") . "\n";
+			print "Dependencies: $metadata{'Dependencies'}\n";
+			print "Pakfile: $metadata{'File'}\n";
+			print "Service InitScripts: $metadata{'Services'}\n";
+			print "Installed: $metadata{'Installed'}\n";
+			### Generate a pak status message
+			if (! defined $metadata{'Available'}) {
+				print "Status: unknown (an error occured retrieving latest pak metadata)";
+			} elsif ($metadata{'Available'} eq "no") {
+				print "Status: obsolete (version $metadata{'ProgVersion'}-$metadata{'Release'} is installed)\n";
+			} elsif ($metadata{'Installed'} eq "yes" && "$metadata{'Release'}" < "$metadata{'AvailableRelease'}") {
+				print "Status: outdated (version $metadata{'ProgVersion'}-$metadata{'Release'} is installed)\n";
+			} elsif ($metadata{'Installed'} eq "yes") {
+				print "Status: up-to-date\n";
+			} else {
+				print "Status: not installed\n";
+			}
+			print "\n";
+		}
+
 	} elsif ("$ARGV[0]" eq "resolvedeps") {
 		foreach (@ARGV) {
 			next if ("$_" eq "resolvedeps");