mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 16:35:02 +01:00

perl utilities to the deprecated to directory; a couple of perl utilities remain, but they are still useful and do not depend on the Immunix module (just the LibAppArmor perl module).
821 lines
27 KiB
Perl
821 lines
27 KiB
Perl
#!/usr/bin/perl
|
|
# ----------------------------------------------------------------------
|
|
# Copyright (c) 2008 Dominic Reynolds. All Rights Reserved.
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of version 2 of the GNU General Public
|
|
# License as published by the Free Software Foundation.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
#
|
|
# ----------------------------------------------------------------------
|
|
|
|
# ----------------------------------------------------------------------
|
|
#
|
|
my $usage =
|
|
"aa-repo.pl --command args\n";
|
|
|
|
my $usage_search =
|
|
" --search [author=XXX] [prog=XXX] [id=XXX]
|
|
Search the repository for profiles matching the search criteria
|
|
and return the results.
|
|
NOTE: One --search switch per option
|
|
|
|
--verbose|v
|
|
Verbosity level. Supply either one or two switches. Two switches
|
|
adds full profile text in returned search results.\n";
|
|
|
|
my $usage_push =
|
|
|
|
" --push [--profile=XXX|all] [--changelog=XXX]
|
|
Push local profiles to repository, uses configured user and upon
|
|
overwrite of an existing profile in the repository then prompt
|
|
user with a diff for confirmation XXX the name of the application
|
|
whose profile should be uploaded or \"all\" to upload all
|
|
profiles. Multiple --profile switches may be passed to supply
|
|
multiple profile names
|
|
|
|
e.g. --push --profile /usr/sbin/mdnsd --profile /usr/sbin/ftp
|
|
e.g. --push --profile all\n";
|
|
|
|
my $usage_pull =
|
|
" --pull [--author=XXX] [--profile=XXX] or [--id=XXX] [--mode=complain]
|
|
pull remote profiles and install on local system
|
|
If operation will change local profiles then prompt user with
|
|
diff for confirmation
|
|
NOTE: One --pull switch per option and there are three acceptable
|
|
combinations
|
|
|
|
--pull --author=XXX
|
|
* pull all profiles in the repo for the author
|
|
|
|
--pull --author=XXX --profile=XXXX
|
|
* pull the profile for prog owned by author
|
|
|
|
--pull --id=XXXX
|
|
* pull the profile with id
|
|
|
|
--pull --mode=complain
|
|
* set the profile(s) to complain mode when installed
|
|
|
|
Profiles are checked for conflicts with currently installed
|
|
profiles and presented as a list to the user to confirm and view.\n";
|
|
|
|
|
|
my $usage_sync =
|
|
" --sync [--up] [--down] [--noconfirm]
|
|
Synchronize local profile set with the repository - showing
|
|
changes and allowing prompting the user with the diffs and
|
|
suggest the newest version to be activated. If the --all option
|
|
is passed then treat profiles not marked as remote as new
|
|
profiles that will be uploaded to the repository.\n";
|
|
|
|
my $usage_stat =
|
|
" --status
|
|
Show the current status of the local profile set. This operation
|
|
is similar to sync but does not prompt the user to up|down load
|
|
changes\n";
|
|
|
|
my $usage_getconfig =
|
|
" --getconfig|c
|
|
Print the current configuration for the repsository\n";
|
|
|
|
|
|
my $usage_setconfig =
|
|
" --setconfig [url=xxx] [username=xxxx] [password=xxxx] [enabled=(yes|no)]
|
|
[upload=(yes|no)]
|
|
Set the configuration options for the repository.
|
|
NOTE: One --setconfig switch per option\n";
|
|
|
|
my $usage_bottom =
|
|
" --quiet|q Don't prompt user - assume that all changes should be made.
|
|
|
|
ISSUES:
|
|
o Should changes made to the system be recorded somehow? An audit event?
|
|
o Should the tool allow a repo/distro to be passed for each operation?
|
|
|
|
";
|
|
|
|
use strict;
|
|
use Getopt::Long;
|
|
|
|
use Immunix::AppArmor;
|
|
use Immunix::Repository;
|
|
use Data::Dumper;
|
|
|
|
use Locale::gettext;
|
|
use POSIX;
|
|
|
|
# force $PATH to be sane
|
|
$ENV{PATH} = "/bin:/sbin:/usr/bin:/usr/sbin";
|
|
|
|
# initialize the local poo
|
|
setlocale(LC_MESSAGES, "");
|
|
textdomain("apparmor-utils");
|
|
|
|
# options variables
|
|
my $help = '';
|
|
my $verbose = '';
|
|
|
|
my ( $id, $author, $mode, %search, $sync, $getconfig, $push,
|
|
$pull, %setconfig, @profiles, $all, $changelog, $stat );
|
|
|
|
GetOptions(
|
|
'search=s%' => \%search,
|
|
'sync=s' => \$sync,
|
|
'status' => \$stat,
|
|
'getconfig|c' => \$getconfig,
|
|
'setconfig=s%' => \%setconfig,
|
|
'push' => \$push,
|
|
'id=s' => \$id,
|
|
'author=s' => \$author,
|
|
'profile=s' => \@profiles,
|
|
'changelog=s' => \$changelog,
|
|
'pull' => \$pull,
|
|
'all|a' => \$all,
|
|
'help|h' => \$help,
|
|
'verbose|v+' => \$verbose
|
|
);
|
|
|
|
#
|
|
# Root privs required to run the repo tool
|
|
#
|
|
if ( geteuid() != 0 ) {
|
|
print STDERR gettext(
|
|
"You must be logged in with root user privileges to use this program.\n"
|
|
);
|
|
exit;
|
|
}
|
|
|
|
# --help
|
|
# tell 'em how to use it...
|
|
&usage && exit if $help;
|
|
|
|
my $config = get_repo_config();
|
|
|
|
#
|
|
# --getconfig operation
|
|
#
|
|
&config && exit if $getconfig;
|
|
|
|
my $sd_mountpoint = check_for_subdomain();
|
|
unless ($sd_mountpoint) {
|
|
fatal_error(gettext(
|
|
"AppArmor does not appear to be started. Please enable AppArmor and try again."
|
|
)
|
|
);
|
|
}
|
|
|
|
#
|
|
# --setconfig operation
|
|
#
|
|
if ( keys %setconfig ) {
|
|
$config->{url} = $setconfig{url} if ( $setconfig{url} );
|
|
$config->{distro} = $setconfig{distro} if ( $setconfig{distro} );
|
|
$config->{enabled} = $setconfig{enabled} if ( $setconfig{enabled} );
|
|
$config->{email} = $setconfig{email} if ( $setconfig{email} );
|
|
$config->{user} = $setconfig{username} if ( $setconfig{username} );
|
|
$config->{password} = $setconfig{password} if ( $setconfig{password} );
|
|
$config->{upload} = $setconfig{upload} if ( $setconfig{upload} );
|
|
set_repo_config( $config );
|
|
}
|
|
|
|
#
|
|
# --push operation
|
|
#
|
|
if ( $push ) {
|
|
my ($conflicts, $repo_profiles, $local_profiles, @overrides);
|
|
if ( ! @profiles ) {
|
|
print STDERR gettext(
|
|
"Must supply at least one profile using \"--profile XXX\" to --push\n"
|
|
);
|
|
exit 1;
|
|
} else {
|
|
print STDERR Data::Dumper->Dump([@profiles], [qw(*profiles)]);
|
|
}
|
|
my $changelog = $changelog?$changelog:"none";
|
|
push_profiles( \@profiles, $changelog, 1 );
|
|
}
|
|
|
|
|
|
#
|
|
# --pull operation
|
|
#
|
|
if ( $pull ) {
|
|
my $type = "";
|
|
if ( $id ) {
|
|
if ( $author || @profiles ) {
|
|
print STDERR gettext(
|
|
"Option --id=XX is only allowed by itself and not in combination to
|
|
other options for the --pull command.\n"
|
|
);
|
|
exit 1;
|
|
}
|
|
$type = "id";
|
|
}
|
|
if ( @profiles && ! $author ) {
|
|
print STDERR gettext(
|
|
"Option --profile=XX requires that the --author=XX option be supplied
|
|
to distinguish a specific profile.\n"
|
|
);
|
|
exit 1;
|
|
} else {
|
|
$type = "profile";
|
|
}
|
|
|
|
my $mode = $mode eq "complain"?1:0;
|
|
pull_profiles( \@profiles, $type, $mode, 1 );
|
|
}
|
|
|
|
#
|
|
# --search operation
|
|
#
|
|
if ( keys %search ) {
|
|
if ( $search{id} ) {
|
|
my($status,$result) = fetch_profile_by_id( $config->{url},
|
|
$search{id} );
|
|
if ($status) {
|
|
my $title = sprintf(gettext( "Profile ID %s\n"), $search{id});
|
|
console_print_search_results( $title,
|
|
"profile",
|
|
{ $result->{name} => $result }
|
|
);
|
|
|
|
} else {
|
|
print STDERR "ERROR $result\n";
|
|
}
|
|
} elsif ( $search{author} && $search{prog} ) {
|
|
my($status,$result) =
|
|
fetch_profiles_by_name_and_user( $config->{url},
|
|
$config->{distro},
|
|
$search{prog},
|
|
$search{author}
|
|
);
|
|
if ( $status ) {
|
|
my $title =
|
|
sprintf(gettext("Profiles matching user: %s and program: %s\n"),
|
|
$search{author},
|
|
$search{prog}
|
|
);
|
|
console_print_search_results( $title, "profile", $result );
|
|
} else {
|
|
print STDERR "ERROR $result\n";
|
|
}
|
|
} elsif ( $search{author} ) {
|
|
my($status,$result) = fetch_profiles_by_user( $config->{url},
|
|
$config->{distro},
|
|
$search{author}
|
|
);
|
|
if ( $status ) {
|
|
my $title = sprintf(gettext( "Profiles for %s\n"), $search{author});
|
|
console_print_search_results( $title, "profile", $result );
|
|
} else {
|
|
print STDERR "ERROR $result\n";
|
|
}
|
|
} elsif ( $search{prog} ) {
|
|
my($status,$result) = fetch_profiles_by_name( $config->{url},
|
|
$config->{distro},
|
|
$search{prog},
|
|
);
|
|
if ( $status ) {
|
|
my $title = sprintf(gettext("Profiles matching program: %s\n"),
|
|
$search{prog});
|
|
console_print_search_results( $title, "user", $result );
|
|
} else {
|
|
print STDERR "ERROR $result\n";
|
|
}
|
|
} else {
|
|
print STDERR
|
|
"Unsupported search criteria. Please specify at least one of
|
|
author=XXX prog=XXX id=XXX\n";
|
|
}
|
|
}
|
|
|
|
if ( $stat ) {
|
|
my ( $local_profiles, $remote_profiles );
|
|
my $msg =
|
|
" The following profiles are stored in the repository but
|
|
are not synchronized with the copy in the repository\n";
|
|
|
|
my ($status, $result) = fetch_profiles_by_user( $config->{url},
|
|
$config->{distro},
|
|
$config->{user}
|
|
);
|
|
if ( $status ) {
|
|
$remote_profiles = $result;
|
|
} else {
|
|
print STDERR sprintf(gettext("ERROR connecting to repository: %s\n"),
|
|
$result);
|
|
exit;
|
|
}
|
|
|
|
readprofiles();
|
|
$local_profiles = serialize_local_profiles( \%sd );
|
|
my ($local_only,$unsynched,$synched,$conflicts) = ({}, {}, {});
|
|
$unsynched = find_profile_conflicts($remote_profiles, $local_profiles);
|
|
for my $p ( keys %$local_profiles ) {
|
|
if ( ! $remote_profiles->{$p} ) {
|
|
$local_only->{$p} = $local_profiles->{$p};
|
|
}
|
|
}
|
|
|
|
for my $p ( keys %$remote_profiles ) {
|
|
$synched->{$p} =
|
|
$remote_profiles->{$p}->{profile} if ( ! %$unsynched->{$p} );
|
|
}
|
|
UI_status($synched, $unsynched, $local_only);
|
|
}
|
|
|
|
######################
|
|
# Helper functions
|
|
######################
|
|
|
|
#
|
|
# Compare the local profile set with the remote profile set.
|
|
# Return a list of the conflicting profiles as a list
|
|
# { PROFILE_NAME => [LOCAL_PROFILE, REMOTE_PROFILE] ]
|
|
#
|
|
#
|
|
# remote_profiles = repository profiles as returned by one of the
|
|
# Immunix::Repository::fetch... functions
|
|
# local_profiles = hash ref containing
|
|
# { name => serialized local profile }
|
|
#
|
|
#
|
|
|
|
sub find_profile_conflicts ($$) {
|
|
my ($remote_profiles,$local_profiles) = @_;
|
|
my $conflicts = {};
|
|
for my $p ( keys(%$local_profiles) ) {
|
|
if ( $local_profiles->{$p} and $remote_profiles->{$p} ) {
|
|
my $p_local = $local_profiles->{$p};
|
|
my $p_remote = $remote_profiles->{$p}->{profile};
|
|
chomp($p_local);
|
|
chomp($p_remote);
|
|
if ( $p_remote ne $p_local ) {
|
|
$conflicts->{$p} = [ $p_local, $p_remote ];
|
|
}
|
|
}
|
|
}
|
|
return( $conflicts );
|
|
}
|
|
|
|
sub serialize_local_profiles ($) {
|
|
my $profiles = shift;
|
|
my $local_profiles = {};
|
|
for my $p ( keys %$profiles ) {
|
|
my $serialize_opts = {};
|
|
$serialize_opts->{NO_FLAGS} = 1;
|
|
my $p_local = serialize_profile( $profiles->{$p},
|
|
$p,
|
|
$serialize_opts );
|
|
$local_profiles->{$p} = $p_local;
|
|
}
|
|
return $local_profiles;
|
|
}
|
|
|
|
|
|
sub console_print_search_results ($$$) {
|
|
my ($title, $type,$result) = @_;
|
|
open(PAGER, "| less") or die "Can't open pager";
|
|
print PAGER $title;
|
|
print PAGER "Found " . values(%$result) . " profiles \n";
|
|
for my $p ( values(%$result) ) {
|
|
if ( $verbose ) {
|
|
if ( $type eq "user" ) {
|
|
print PAGER " Author [ " . $p->{username} . " ]\n";
|
|
} elsif ( $type eq "profile" ) {
|
|
print PAGER " Name [ " . $p->{name} . " ]\n";
|
|
}
|
|
print PAGER " Created [ " . $p->{created_at} . " ]\n";
|
|
print PAGER " Downloads [ " . $p->{downloaded_count} . " ]\n";
|
|
print PAGER " ID [ " . $p->{id} . " ]\n";
|
|
if ( $verbose > 1 ) {
|
|
print PAGER " Profile [ \n" . $p->{profile} . " ]\n\n";
|
|
} else {
|
|
print PAGER "\n";
|
|
}
|
|
} else {
|
|
my $data = $type eq "user"?$p->{username}:$p->{name};
|
|
print PAGER " " . $data . "\n";
|
|
}
|
|
}
|
|
close PAGER;
|
|
}
|
|
|
|
sub UI_resolve_profile_conflicts {
|
|
|
|
my ($explanation, $conflict_hash) = @_;
|
|
my $url = $config->{url};
|
|
my @conflicts = map { [ $_,
|
|
$conflict_hash->{$_}->[0],
|
|
$conflict_hash->{$_}->[1]
|
|
] }
|
|
keys %$conflict_hash;
|
|
my @commits = [];
|
|
my $title = "Profile conflicts";
|
|
my %resolution = ();
|
|
my $q = {};
|
|
$q->{title} = $title;
|
|
$q->{headers} = [ "Repository", $url, ];
|
|
|
|
$q->{explanation} = $explanation;
|
|
|
|
$q->{functions} = [ "CMD_OVERWRITE",
|
|
"CMD_KEEP",
|
|
"CMD_VIEW_CHANGES",
|
|
"CMD_ABORT",
|
|
"CMD_CONTINUE", ];
|
|
|
|
$q->{default} = "CMD_OVERWRITE";
|
|
$q->{options} = [ map { $_->[0] } @conflicts ];
|
|
$q->{selected} = 0;
|
|
|
|
my ($ans, $arg);
|
|
do {
|
|
($ans, $arg) = UI_PromptUser($q);
|
|
|
|
if ($ans eq "CMD_VIEW_CHANGES") {
|
|
display_changes($conflicts[$arg]->[2], $conflicts[$arg]->[1]);
|
|
}
|
|
if ( $ans eq "CMD_OVERWRITE") {
|
|
$q->{options} =
|
|
[ map { $_ =~ /$conflicts[$arg]->[0]( K| O)?$/?
|
|
$conflicts[$arg]->[0] . " O":
|
|
$_ }
|
|
@{$q->{options}}
|
|
];
|
|
$resolution{$conflicts[$arg]->[0]} = "O";
|
|
}
|
|
if ( $ans eq "CMD_KEEP") {
|
|
$q->{options} =
|
|
[ map { $_ =~ /$conflicts[$arg]->[0]( K| O)?$/?
|
|
$conflicts[$arg]->[0] . " K":
|
|
$_ }
|
|
@{$q->{options}}
|
|
];
|
|
$resolution{$conflicts[$arg]->[0]} = "K";
|
|
}
|
|
$q->{selected} = ($arg+1) % @conflicts;
|
|
} until $ans =~ /^CMD_CONTINUE/;
|
|
if ($ans eq "CMD_CONTINUE") {
|
|
my @results = ();
|
|
for my $p ( keys %resolution ) {
|
|
if ( $resolution{$p} eq "O" ) {
|
|
push @results, $p;
|
|
}
|
|
}
|
|
return @results;
|
|
}
|
|
}
|
|
|
|
sub UI_display_profiles {
|
|
my ($explanation, $profile_hash) = @_;
|
|
my $url = $config->{url};
|
|
my @profiles = map { [ $_, $profile_hash->{$_} ] } keys %$profile_hash;
|
|
my $title = gettext("Profiles");
|
|
my $q = {};
|
|
$q->{title} = $title;
|
|
$q->{headers} = [ "Repository", $url, ];
|
|
|
|
$q->{explanation} = $explanation;
|
|
|
|
$q->{functions} = [ "CMD_VIEW",
|
|
"CMD_CONTINUE", ];
|
|
|
|
$q->{default} = "CMD_CONTINUE";
|
|
$q->{options} = [ map { $_->[0] } @profiles ];
|
|
$q->{selected} = 0;
|
|
|
|
my ($ans, $arg);
|
|
do {
|
|
($ans, $arg) = UI_PromptUser($q);
|
|
|
|
if ($ans eq "CMD_VIEW") {
|
|
my $pager = get_pager();
|
|
open ( PAGER, "| $pager" ) or die "Can't open $pager";
|
|
print PAGER gettext("Profile: ") . $profiles[$arg]->[0] . "\n";
|
|
print PAGER $profiles[$arg]->[1];
|
|
close PAGER;
|
|
}
|
|
$q->{selected} = ($arg+1) % @profiles;
|
|
} until $ans =~ /^CMD_CONTINUE/;
|
|
return;
|
|
}
|
|
|
|
sub UI_display_profile_conflicts {
|
|
my ($explanation, $conflict_hash) = @_;
|
|
my $url = $config->{url};
|
|
my @conflicts = map { [ $_,
|
|
$conflict_hash->{$_}->[0],
|
|
$conflict_hash->{$_}->[1]
|
|
] }
|
|
keys %$conflict_hash;
|
|
my @commits = [];
|
|
my $title = gettext("Profile conflicts");
|
|
my $q = {};
|
|
$q->{title} = $title;
|
|
$q->{headers} = [ "Repository", $url, ];
|
|
|
|
$q->{explanation} = $explanation;
|
|
|
|
$q->{functions} = [ "CMD_VIEW_CHANGES",
|
|
"CMD_CONTINUE", ];
|
|
|
|
$q->{default} = "CMD_CONTINUE";
|
|
$q->{options} = [ map { $_->[0] } @conflicts ];
|
|
$q->{selected} = 0;
|
|
|
|
my ($ans, $arg);
|
|
do {
|
|
($ans, $arg) = UI_PromptUser($q);
|
|
|
|
if ($ans eq "CMD_VIEW_CHANGES") {
|
|
display_changes($conflicts[$arg]->[2], $conflicts[$arg]->[1]);
|
|
}
|
|
$q->{selected} = ($arg+1) % @conflicts;
|
|
} until $ans =~ /^CMD_CONTINUE/;
|
|
return;
|
|
}
|
|
|
|
sub usage {
|
|
if ( $help eq "push" ) {
|
|
print STDERR $usage . $usage_push ."\n";
|
|
} elsif ( $help eq "pull" ) {
|
|
print STDERR $usage . $usage_pull ."\n";
|
|
} elsif ( $help eq "sync" ) {
|
|
print STDERR $usage . $usage_sync ."\n";
|
|
} elsif ( $help eq "getconfig" ) {
|
|
print STDERR $usage . $usage_getconfig ."\n";
|
|
} elsif ( $help eq "setconfig" ) {
|
|
print STDERR $usage . $usage_setconfig ."\n";
|
|
} elsif ( $help eq "status" ) {
|
|
print STDERR $usage . $usage_stat ."\n";
|
|
} elsif ( $help eq "search" ) {
|
|
print STDERR $usage . $usage_search ."\n";
|
|
} else {
|
|
open(PAGER, "| less") or die "Can't open pager";
|
|
print PAGER $usage .
|
|
$usage_search .
|
|
$usage_push .
|
|
$usage_pull .
|
|
$usage_sync .
|
|
$usage_stat .
|
|
$usage_setconfig .
|
|
$usage_getconfig .
|
|
$usage_bottom . "\n";
|
|
close PAGER;
|
|
}
|
|
}
|
|
|
|
#
|
|
# --getconfig helper function
|
|
#
|
|
sub config {
|
|
my $configstr = gettext("Current config\n");
|
|
my $config = get_repo_config();
|
|
$configstr .= "\turl:\t\t$config->{url}\n";
|
|
$configstr .= "\tdistro:\t\t$config->{distro}\n";
|
|
$configstr .= "\tenabled:\t$config->{enabled}\n";
|
|
$configstr .= "\temail:\t\t$config->{email}\n";
|
|
$configstr .= "\tusername:\t$config->{user}\n";
|
|
$configstr .= "\tpassword:\t$config->{password}\n";
|
|
$configstr .= "\tupload:\t\t$config->{upload}\n";
|
|
print STDERR $configstr . "\n";
|
|
}
|
|
|
|
#
|
|
# helper function to push profiles to the repository
|
|
# used by --push and --sync options
|
|
#
|
|
sub push_profiles($$$) {
|
|
my ( $p_ref, $changelog, $confirm ) = @_;
|
|
my ( $conflicts, $remote_profiles, $local_profiles, @overrides );
|
|
my @profiles = @$p_ref;
|
|
|
|
my $conflict_msg =
|
|
" The following profile(s) selected for upload conflicts with a profile already
|
|
stored in the repository for your account. Please choose whether to keep the
|
|
current version or overwrite it.\n";
|
|
$all = 0;
|
|
|
|
readprofiles();
|
|
my ($status, $result) = fetch_profiles_by_user( $config->{url},
|
|
$config->{distro},
|
|
$config->{user}
|
|
);
|
|
if ( $status ) {
|
|
$remote_profiles = $result;
|
|
} else {
|
|
print STDERR sprintf(gettext("ERROR connecting to repository: %s\n"),
|
|
$result);
|
|
exit;
|
|
}
|
|
|
|
$all = 1 if ( grep(/^all$/, @profiles) );
|
|
|
|
if ( $all ) {
|
|
$local_profiles = serialize_local_profiles( \%sd );
|
|
} else {
|
|
my $local_sd = {};
|
|
for my $p ( @profiles ) {
|
|
if ( !$sd{$p} ) {
|
|
print STDERR
|
|
sprintf(gettext("Profile for [%s] does not exist\n"), $p);
|
|
exit;
|
|
}
|
|
$local_sd->{$p} = $sd{$p};
|
|
}
|
|
$local_profiles = serialize_local_profiles( $local_sd );
|
|
}
|
|
|
|
$conflicts = find_profile_conflicts($remote_profiles, $local_profiles);
|
|
|
|
if ( keys %$conflicts ) {
|
|
@overrides = UI_resolve_profile_conflicts( $conflict_msg, $conflicts );
|
|
}
|
|
|
|
if ( $local_profiles ) {
|
|
my @uploads;
|
|
for my $p ( keys %$local_profiles ) {
|
|
unless ( $conflicts->{$p} and !grep(/^$p$/, @overrides) ) {
|
|
print STDERR gettext("Uploading ") . $p . "... ";
|
|
my ($status,$result) = upload_profile( $config->{url},
|
|
$config->{user},
|
|
$config->{password},
|
|
$config->{distro},
|
|
$p,
|
|
$local_profiles->{$p},
|
|
$changelog
|
|
);
|
|
print STDERR gettext("done") . "\n";
|
|
}
|
|
if ( $status ) {
|
|
push @uploads, $p;
|
|
} else {
|
|
print STDERR gettext("Error uploading") . "$p: $result\n";
|
|
}
|
|
}
|
|
if ( @uploads ) {
|
|
#
|
|
# Currently the upload API with the repository returns the
|
|
# the current users profile set before the update so we have
|
|
# to refetch to obtain the metadata to update the local profiles
|
|
#
|
|
my $repo_p = [];
|
|
print STDERR gettext("Updating local profile metedata....\n");
|
|
my ($status,$result) = fetch_profiles_by_user( $config->{url},
|
|
$config->{distro},
|
|
$config->{user} );
|
|
if ( $status ) {
|
|
for my $p ( @uploads ) {
|
|
push( @$repo_p, [$p, $result->{$p}] ) if ( $result->{$p} );
|
|
}
|
|
activate_repo_profiles( $config->{url}, $repo_p, 0 );
|
|
print STDERR gettext(" done\n");
|
|
} else {
|
|
print STDERR gettext(
|
|
"Failed to retrieve updated profiles from the repository. Error: "
|
|
) . $result . "\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#
|
|
# Helper function for pulling profiles from the repository
|
|
# used by --pull and --sync options
|
|
#
|
|
sub pull_profiles($$$$) {
|
|
my ( $p_ref, $mode, $confirm, $opts ) = @_;
|
|
my @profiles = @$p_ref;
|
|
my ( $conflicts, $commit_list, $remote_profiles,
|
|
$local_profiles, @overrides );
|
|
|
|
my $conflict_msg =
|
|
" The following profiles selected for download conflict with profiles
|
|
already deployed on the system. Please choose whether to keep the local
|
|
version or overwrite with the version from the repository\n";
|
|
|
|
readprofiles();
|
|
|
|
if ( $opts->{id} ) {
|
|
my ($status,$newp) = fetch_profile_by_id( $config->{url}, $opts->{id} );
|
|
if ( ! $status ) {
|
|
print STDERR gettext(
|
|
sprintf("Error occured during operation\n\t[%s]\n",
|
|
$newp
|
|
)
|
|
);
|
|
exit 1;
|
|
} else {
|
|
$remote_profiles = { $newp->{name} => $newp->{profile} };
|
|
}
|
|
} elsif ( @profiles && $opts->{author} ) {
|
|
$remote_profiles = {};
|
|
for my $p ( @profiles ) {
|
|
my ($status,$profiles) =
|
|
fetch_profiles_by_name_and_user( $config->{url},
|
|
$config->{distro},
|
|
$p,
|
|
$opts->{author} );
|
|
if ( ! $status ) {
|
|
print STDERR gettext(sprintf(
|
|
"Error occured during operation\n\t[%s]\n",
|
|
$profiles
|
|
)
|
|
);
|
|
exit 1;
|
|
} else {
|
|
$remote_profiles->{$p} = $profiles->{$p};
|
|
}
|
|
}
|
|
} elsif ( $opts->{author} ) {
|
|
my ($status,$profiles) = fetch_profiles_by_user( $config->{url},
|
|
$config->{distro},
|
|
$opts->{author} );
|
|
if ( ! $status ) {
|
|
print STDERR gettext(sprintf(
|
|
"Error occured during operation\n\t[%s]\n",
|
|
$profiles
|
|
)
|
|
);
|
|
exit 1;
|
|
} else {
|
|
$remote_profiles = $profiles;
|
|
}
|
|
}
|
|
$local_profiles = serialize_local_profiles( \%sd );
|
|
$conflicts = find_profile_conflicts( $remote_profiles, $local_profiles );
|
|
if ( keys %$conflicts ) {
|
|
@overrides = UI_resolve_profile_conflicts( $conflict_msg, $conflicts );
|
|
}
|
|
for my $p ( keys %$remote_profiles ) {
|
|
unless ( $conflicts->{$p} and !grep(/^$p$/, @overrides) ) {
|
|
$remote_profiles->{$p}->{username} = $opts->{author};
|
|
push @$commit_list, [$p, $remote_profiles->{$p}];
|
|
}
|
|
}
|
|
|
|
if ( $commit_list and @$commit_list ) {
|
|
activate_repo_profiles( $config->{url}, $commit_list, $mode );
|
|
system("rcapparmor reload");
|
|
} else {
|
|
UI_Info(gettext("No changes to make"));
|
|
}
|
|
}
|
|
|
|
sub UI_status {
|
|
|
|
my ($synched, $unsynched, $local) = @_;
|
|
my $url = $config->{url};
|
|
my $synched_text = gettext("Synchronized repository profiles:\t\t") .
|
|
keys %$synched;
|
|
my $unsynched_text = gettext("Unsynchronized repository profiles:\t") .
|
|
keys %$unsynched;
|
|
my $local_text = gettext("Local only profiles :\t\t\t") . keys %$local;
|
|
my $options = [ $synched_text, $unsynched_text, $local_text ];
|
|
my $title = gettext("Profile Status");
|
|
my $explanation = gettext(
|
|
" This is the current status of active profiles on the system.
|
|
To view the profiles or unsyncronized changes select VIEW\n"
|
|
);
|
|
my $q = {};
|
|
$q->{title} = $title;
|
|
$q->{headers} = [ "Repository", $url, ];
|
|
$q->{explanation} = $explanation;
|
|
$q->{functions} = [ "CMD_VIEW", "CMD_FINISHED", ];
|
|
$q->{default} = "CMD_FINISHED";
|
|
$q->{options} = $options;
|
|
$q->{selected} = 0;
|
|
|
|
my ($ans, $arg);
|
|
do {
|
|
($ans, $arg) = UI_PromptUser($q);
|
|
|
|
if ($ans eq "CMD_VIEW") {
|
|
if ( $arg == 0 ) {
|
|
UI_display_profiles(
|
|
gettext("Profiles stored in the repository"),
|
|
$synched
|
|
);
|
|
} elsif ( $arg == 1 ) {
|
|
UI_display_profile_conflicts(
|
|
gettext("Unsyncronised profile changes"),
|
|
$unsynched
|
|
);
|
|
} elsif ( $arg == 2 ) {
|
|
UI_display_profiles(
|
|
gettext("Profiles stored in the repository"),
|
|
$local
|
|
);
|
|
}
|
|
}
|
|
} until $ans =~ /^CMD_FINSHED/;
|
|
}
|
|
|
|
|