2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-31 14:25:52 +00:00

Changes to add support for the AppArmor profile repository to the

console UI tools. (jmichael)
This commit is contained in:
Dominic Reynolds
2007-04-26 02:48:24 +00:00
parent 4419185c4d
commit d27ff99e31
5 changed files with 733 additions and 35 deletions

View File

@@ -21,19 +21,22 @@
string status = "";
map CMDS = $[ ];
CMDS["CMD_ALLOW"] = _("&Allow");
CMDS["CMD_DENY"] = _("&Deny");
CMDS["CMD_ABORT"] = _("Abo(r)t");
CMDS["CMD_FINISHED"] = _("&Finish");
CMDS["CMD_INHERIT"] = _("&Inherit");
CMDS["CMD_PROFILE"] = _("&Profile");
CMDS["CMD_UNCONFINED"] = _("&Unconfined");
CMDS["CMD_NEW"] = _("&Edit");
CMDS["CMD_GLOB"] = _("&Glob");
CMDS["CMD_GLOBEXT"] = _("Glob w/E&xt");
CMDS["CMD_ADDHAT"] = _("&Add Requested Hat");
CMDS["CMD_USEDEFAULT"] = _("&Use Default Hat");
CMDS["CMD_SCAN"] = _("&Scan system log for AppArmor events");
CMDS["CMD_ALLOW"] = _("&Allow");
CMDS["CMD_DENY"] = _("&Deny");
CMDS["CMD_ABORT"] = _("Abo(r)t");
CMDS["CMD_FINISHED"] = _("&Finish");
CMDS["CMD_INHERIT"] = _("&Inherit");
CMDS["CMD_PROFILE"] = _("&Profile");
CMDS["CMD_UNCONFINED"] = _("&Unconfined");
CMDS["CMD_NEW"] = _("&Edit");
CMDS["CMD_GLOB"] = _("&Glob");
CMDS["CMD_GLOBEXT"] = _("Glob w/E&xt");
CMDS["CMD_ADDHAT"] = _("&Add Requested Hat");
CMDS["CMD_USEDEFAULT"] = _("&Use Default Hat");
CMDS["CMD_SCAN"] = _("&Scan system log for AppArmor events");
CMDS["CMD_VIEW_REPO"] = _("&View Profile");
CMDS["CMD_USE_REPO"] = _("&Use Profile");
CMDS["CMD_CREATE_PROFILE"] = _("&Create New Profile");
if (!installAppArmorPackages()) {
return;
@@ -312,6 +315,26 @@
// tell the backend what they did
SCR::Write(.genprof, answers);
} else if (type == "dialog-view-profile") {
string user = agent_data["user"]:"MISSING USER";
string profile = agent_data["profile"]:"MISSING PROFILE";
string default_ans = agent_data["default"]:"b";
symbol focus = `focus_yes;
if (default_ans == "b") {
focus = `focus_no;
}
map answers = $[ ];
if (Popup::AnyQuestion(_("Profile created by user ") + user,
profile, _("&Use Profile"), _("&Back"), focus)) {
answers["answer"] = "u";
} else {
answers["answer"] = "b";
}
// write the answers for the last dialog
boolean written = SCR::Write(.genprof, answers);
} else if (type == "dialog-yesno") {
string question = agent_data["question"]:"MISSING QUESTION";

View File

@@ -21,19 +21,22 @@
string status = "";
map CMDS = $[ ];
CMDS["CMD_ALLOW"] = _("&Allow");
CMDS["CMD_DENY"] = _("&Deny");
CMDS["CMD_ABORT"] = _("Abo(r)t");
CMDS["CMD_FINISHED"] = _("&Finish");
CMDS["CMD_INHERIT"] = _("&Inherit");
CMDS["CMD_PROFILE"] = _("&Profile");
CMDS["CMD_UNCONFINED"] = _("&Unconfined");
CMDS["CMD_NEW"] = _("&Edit");
CMDS["CMD_GLOB"] = _("&Glob");
CMDS["CMD_GLOBEXT"] = _("Glob w/E&xt");
CMDS["CMD_ADDHAT"] = _("&Add Requested Hat");
CMDS["CMD_USEDEFAULT"] = _("&Use Default Hat");
CMDS["CMD_SCAN"] = _("&Scan system log for AppArmor events");
CMDS["CMD_ALLOW"] = _("&Allow");
CMDS["CMD_DENY"] = _("&Deny");
CMDS["CMD_ABORT"] = _("Abo&rt");
CMDS["CMD_FINISHED"] = _("&Finish");
CMDS["CMD_INHERIT"] = _("&Inherit");
CMDS["CMD_PROFILE"] = _("&Profile");
CMDS["CMD_UNCONFINED"] = _("&Unconfined");
CMDS["CMD_NEW"] = _("&Edit");
CMDS["CMD_GLOB"] = _("&Glob");
CMDS["CMD_GLOBEXT"] = _("Glob w/E&xt");
CMDS["CMD_ADDHAT"] = _("&Add Requested Hat");
CMDS["CMD_USEDEFAULT"] = _("&Use Default Hat");
CMDS["CMD_SCAN"] = _("&Scan system log for AppArmor events");
CMDS["CMD_VIEW_REPO"] = _("&View Profile");
CMDS["CMD_USE_REPO"] = _("&Use Profile");
CMDS["CMD_CREATE_PROFILE"] = _("&Create New Profile");
if (!installAppArmorPackages()) {
return;
@@ -311,6 +314,27 @@
// tell the backend what they did
SCR::Write(.logprof, answers);
} else if (type == "dialog-view-profile") {
string user = agent_data["user"]:"MISSING USER";
string profile = agent_data["profile"]:"MISSING PROFILE";
string default_ans = agent_data["default"]:"b";
symbol focus = `focus_yes;
if (default_ans == "b") {
focus = `focus_no;
}
map answers = $[ ];
if (Popup::AnyQuestion(_("Profile created by user ") + user,
profile, _("&Use Profile"), _("&Back"), focus)) {
answers["answer"] = "u";
} else {
answers["answer"] = "b";
}
// write the answers for the last dialog
boolean written = SCR::Write(.logprof, answers);
} else if (type == "dialog-yesno") {
string question = agent_data["question"]:"MISSING QUESTION";

View File

@@ -27,10 +27,15 @@ use warnings;
use Carp;
use Cwd qw(cwd realpath);
use File::Basename;
use File::Temp qw/ tempfile tempdir /;
use Data::Dumper;
use Locale::gettext;
use POSIX;
use RPC::XML;
use RPC::XML::Client;
use Storable qw(dclone);
use Term::ReadKey;
use Immunix::Severity;
@@ -94,6 +99,8 @@ our @EXPORT = qw(
our $confdir = "/etc/apparmor";
our $repo_client;
our $running_under_genprof = 0;
our $DEBUGGING;
@@ -105,6 +112,8 @@ our $UI_Mode = "text";
our $sevdb;
our %uid2login;
# initialize Term::ReadLine if it's available
our $term;
eval {
@@ -133,6 +142,7 @@ our $seenevents = 0;
# behaviour tweaking
our $cfg;
our $repo_cfg;
# these are globs that the user specifically entered. we'll keep track of
# them so that if one later matches, we'll suggest it again.
@@ -142,6 +152,7 @@ our @userglobs;
our %t;
our %transitions;
our %sd; # we keep track of the original profiles in %sd
our %original_sd;
my @log;
my %pid;
@@ -290,6 +301,7 @@ sub setup_yast {
# something's broken, die a horrible, painful death
fatal_error "Yast frontend is out of sync from backend agent.";
}
$DEBUGGING && debug "Initial handshake ok";
# the yast connection seems to be working okay
return 1;
@@ -578,6 +590,140 @@ sub handle_binfmt ($$) {
}
}
sub get_profile_from_repo {
my $fqdbin = shift;
my $profile_data;
my $distro = $cfg->{settings}{distro};
my $repository = $cfg->{settings}{repository};
if ($repo_client) {
my $res = $repo_client->send_request('FindProfiles', $distro, $fqdbin, "");
if (did_result_succeed($res)) {
my @profiles;
my @profile_list = @{$res->value};
if (@profile_list) {
my @uids;
for my $p (@profile_list) {
my $uid = $p->{user_id};
my $username = $uid2login{$uid};
if ($username) {
$p->{username} = $username;
} else {
push @uids, $uid;
}
}
if (@uids) {
# LoginNamesFromUserIds currently returns the list of uids in sorted
# order no matter how you pass them to it - that's a bug, but let's
# explictly sort the list to work around that.
@uids = sort @uids;
my $res = $repo_client->send_request('LoginNamesFromUserIds', [ @uids ]);
if (did_result_succeed($res)) {
my @usernames = @{$res->value};
for my $uid (@uids) {
my $username = shift @usernames;
$uid2login{$uid} = $username;
}
}
}
for my $p (@profile_list) {
next if $p->{username};
my $uid = $p->{user_id};
my $username = $uid2login{$uid};
if ($username) {
$p->{username} = $username;
} else {
$p->{username} = "unknown-$uid";
}
}
my @options = map { $_->{username} } @profile_list;
my $q = { };
$q->{headers} = [ ];
push @{$q->{headers}}, gettext("Profile"), $fqdbin;
$q->{functions} = [
"CMD_VIEW_REPO", "CMD_USE_REPO", "CMD_CREATE_PROFILE",
"CMD_ABORT", "CMD_FINISHED"
];
$q->{default} = "CMD_VIEW_REPO";
$q->{options} = [ @options ];
$q->{selected} = 0;
my ($p, $ans, $arg);
do {
($ans, $arg) = UI_PromptUser($q);
for (my $i = 0; $i < scalar(@profile_list); $i++) {
if ($profile_list[$i]->{username} eq $options[$arg]) {
$p = $profile_list[$i];
$q->{selected} = $i;
}
}
if ($ans eq "CMD_VIEW_REPO") {
if ($UI_Mode eq "yast") {
SendDataToYast( { type => "dialog-view-profile", user => $arg,
profile => $p->{profile} } );
my ($ypath, $yarg) = GetDataFromYast();
$ans = $yarg->{answer} || "b";
if ( $ans eq "u" ) {
$profile_data = use_repo_profile($fqdbin, $repository, $p);
$ans = "CMD_USE_REPO";
}
} else {
open(PAGER, "| less");
print PAGER gettext("Profile submitted by") . " $options[$arg]:\n\n$p->{profile}\n\n";;
close(PAGER);
}
} elsif ($ans eq "CMD_USE_REPO") {
$profile_data = use_repo_profile($fqdbin, $repository, $p );
}
} until ($ans =~ /^CMD_(USE_REPO|CREATE_PROFILE)$/);
}
}
}
return $profile_data;
}
sub set_repo_info {
my ($profile_data, $repo_url, $username, $id) = @_;
# save repository metadata
$profile_data->{repo}{url} = $repo_url;
$profile_data->{repo}{user} = $username;
$profile_data->{repo}{id} = $id;
}
sub use_repo_profile {
my ($fqdbin, $repo_url, $profile) = @_;
my $profile_data = eval {
parse_profile_data($profile->{profile}, "repository profile");
};
if ($@) {
$profile_data = undef;
}
if ($profile_data) {
set_repo_info($profile_data->{$fqdbin}{$fqdbin}, $repo_url,
$profile->{username}, $profile->{id});
}
return $profile_data;
}
sub create_new_profile {
my $fqdbin = shift;
@@ -585,7 +731,7 @@ sub create_new_profile {
$fqdbin => {
flags => "complain",
include => { "abstractions/base" => 1 },
path => { $fqdbin => "mr" }
path => { $fqdbin => "mr" },
}
};
@@ -621,6 +767,17 @@ sub create_new_profile {
sub autodep ($) {
my $bin = shift;
unless ($repo_cfg) {
$repo_cfg = read_config("repository.conf");
unless ($repo_cfg->{repository}{enabled}) {
ask_to_enable_repo();
}
}
if (repo_is_enabled()) {
setup_repo_client();
}
# findexecutable() might fail if we're running on a different system
# than the logs were collected on. ugly. we'll just hope for the best.
my $fqdbin = findexecutable($bin) || $bin;
@@ -631,10 +788,19 @@ sub autodep ($) {
# ignore directories
return if -d $fqdbin;
my $profile_data = create_new_profile($fqdbin);
my $profile_data;
if (repo_is_enabled()) {
$profile_data = eval { get_profile_from_repo($fqdbin) };
}
unless ($profile_data) {
$profile_data = create_new_profile($fqdbin);
}
# stick the profile into our data structure.
attach_profile_data(\%sd, $profile_data);
# and store a "clean" version also so we can display the changes we've
# made during this run
attach_profile_data(\%original_sd, $profile_data);
if (-f "$profiledir/tunables/global") {
my $file = getprofilefilename($fqdbin);
@@ -913,6 +1079,18 @@ my %CMDS = (
CMD_USEDEFAULT => "(U)se Default Hat",
CMD_SCAN => "(S)can system log for SubDomain events",
CMD_HELP => "(H)elp",
CMD_VIEW_REPO => "(V)iew Profile",
CMD_USE_REPO => "(U)se Profile",
CMD_CREATE_PROFILE => "(C)reate New Profile",
CMD_UPDATE_PROFILE => "(U)pdate Profile",
CMD_IGNORE_UPDATE => "(I)gnore Update",
CMD_SAVE_CHANGES => "(S)ave Changes",
CMD_UPLOAD_CHANGES => "(U)pload Changes",
CMD_VIEW_CHANGES => "(V)iew Changes",
CMD_ENABLE_REPO => "(E)nable Repository",
CMD_DISABLE_REPO => "(D)isable Repository",
CMD_ASK_NEVER => "(N)ever Ask Again",
CMD_ASK_LATER => "Ask Me (L)ater",
);
sub UI_PromptUser ($) {
@@ -1647,6 +1825,77 @@ sub read_log {
close(LOG);
}
sub check_repo_for_newer {
my $profile = shift;
my $distro = $cfg->{settings}{distro};
my $url = $sd{$profile}{$profile}{repo}{url};
my $user = $sd{$profile}{$profile}{repo}{user};
my $id = $sd{$profile}{$profile}{repo}{id};
return unless ($distro && $url && $user && $id);
my $p;
if ($repo_client) {
my $res = $repo_client->send_request('FindProfiles', $distro, $profile, $user);
if (did_result_succeed($res)) {
my @profiles;
my @profile_list = @{$res->value};
if (@profile_list) {
if ($profile_list[0]->{id} > $id) {
$p = $profile_list[0];
}
}
}
}
if ($p) {
my $q = { };
$q->{headers} = [
"Profile", $profile,
"User", $user,
"Old Revision", $id,
"New Revision", $p->{id},
];
$q->{explanation} = "An updated version of this profile has been found in the profile repository. Would you like to use it?";
$q->{functions} = [
"CMD_VIEW_CHANGES", "CMD_UPDATE_PROFILE", "CMD_IGNORE_UPDATE",
"CMD_ABORT", "CMD_FINISHED"
];
my $ans;
do {
$ans = UI_PromptUser($q);
if ($ans eq "CMD_VIEW_CHANGES") {
my $oldprofile = serialize_profile($sd{$profile}, $profile);
my $newprofile = $p->{profile};
display_changes($oldprofile, $newprofile);
}
} until $ans =~ /^CMD_(UPDATE_PROFILE|IGNORE_UPDATE)/;
if ($ans eq "CMD_UPDATE_PROFILE") {
eval {
my $profile_data = parse_profile_data($p->{profile}, "repository profile");
if ($profile_data) {
attach_profile_data(\%sd, $profile_data);
attach_profile_data(\%original_sd, $profile_data);
$changed{$profile} = 1;
}
set_repo_info($sd{$profile}{$profile}, $url, $user, $p->{id});
UI_Info("Updated profile $profile to revision $p->{id}.");
};
if ($@) {
UI_Info("Error parsing repository profile.");
}
}
}
}
sub ask_the_questions {
my $found;
@@ -1668,6 +1917,8 @@ sub ask_the_questions {
for my $profile (sort keys %{ $log{$sdmode} }) {
check_repo_for_newer($profile);
$found++;
# this sorts the list of hats, but makes sure that the containing
@@ -2047,6 +2298,122 @@ sub ask_the_questions {
}
}
sub repo_is_enabled {
my $enabled;
if ($repo_cfg &&
$repo_cfg->{repository}{enabled} &&
$repo_cfg->{repository}{enabled} eq "yes") {
$enabled = 1;
}
return $enabled;
}
sub ask_to_enable_repo {
my $q = { };
$q->{headers} = [
"Repository", $cfg->{settings}{repository},
];
$q->{explanation} = "Would you like to enable access to the profile repository?";
$q->{functions} = [
"CMD_ENABLE_REPO", "CMD_DISABLE_REPO", "CMD_ASK_LATER",
"CMD_ABORT", "CMD_FINISHED",
];
my $cmd;
do {
$cmd = UI_PromptUser($q);
} until $cmd =~ /^CMD_(ENABLE_REPO|DISABLE_REPO|LATER_REPO)/;
if ($cmd eq "CMD_ENABLE_REPO") {
$repo_cfg->{repository}{enabled} = "yes";
} elsif ($cmd eq "CMD_DISABLE_REPO") {
$repo_cfg->{repository}{enabled} = "no";
} elsif ($cmd eq "CMD_LATER_REPO") {
$repo_cfg->{repository}{enabled} = "later";
}
write_config("repository.conf", $repo_cfg);
}
sub get_repo_user_pass {
my ($user, $pass);
if ($repo_cfg) {
$user = $repo_cfg->{repository}{user};
$pass = $repo_cfg->{repository}{pass};
}
unless ($user && $pass) {
($user, $pass) = ask_signup_info();
}
return ($user, $pass);
}
sub setup_repo_client {
unless ($repo_client) {
$repo_client = new RPC::XML::Client $cfg->{settings}{repository};
}
}
sub did_result_succeed {
my $result = shift;
my $ref = ref $result;
return ($ref && $ref ne "RPC::XML::fault") ? 1 : 0;
}
sub get_result_error {
my $result = shift;
if (ref $result) {
if (ref $result eq "RPC::XML::fault") {
$result = $result->string;
} else {
$result = $$result;
}
}
return $result;
}
sub ask_signup_info {
my ($user, $pass, $email, $signup_okay);
if ($repo_client) {
do {
$user = UI_GetString("Username: ", $user);
$pass = UI_GetString("Password: ", $pass);
$email = UI_GetString("Email Addr: ", $email);
my $res = $repo_client->send_request('LoginConfirm', $user, $pass);
if (did_result_succeed($res)) {
$signup_okay = 1;
} else {
$res = $repo_client->send_request('Signup', $user, $pass, $email);
if (did_result_succeed($res)) {
$signup_okay = 1;
} else {
my $error = get_result_error($res);
print STDERR "$error\n";
}
}
} until $signup_okay;
}
$repo_cfg->{repository}{user} = $user;
$repo_cfg->{repository}{pass} = $pass;
$repo_cfg->{repository}{email} = $email;
write_config("repository.conf", $repo_cfg);
return ($user, $pass);
}
sub do_logprof_pass {
my $logmark = shift || "";
@@ -2075,6 +2442,17 @@ sub do_logprof_pass {
# we need to be able to break all the way out of deep into subroutine calls
# if they select "Finish" so we can take them back out to the genprof prompt
eval {
unless ($repo_cfg) {
$repo_cfg = read_config("repository.conf");
unless ($repo_cfg->{repository}{enabled}) {
ask_to_enable_repo();
}
}
if (repo_is_enabled()) {
setup_repo_client();
}
read_log($logmark);
for my $root (@log) {
@@ -2119,6 +2497,13 @@ sub do_logprof_pass {
save_profiles();
if (repo_is_enabled()) {
unless ($repo_cfg->{repository}{neversubmit}) {
submit_created_profiles();
submit_changed_profiles();
}
}
# if they hit "Finish" we need to tell the caller that so we can exit
# all the way instead of just going back to the genprof prompt
return $finishing ? "FINISHED" : "NORMAL";
@@ -2126,12 +2511,251 @@ sub do_logprof_pass {
sub save_profiles {
# make sure the profile changes we've made are saved to disk...
for my $profile (sort keys %changed) {
writeprofile($profile);
reload($profile);
my @changed = sort keys %changed;
if (@changed) {
my $q = { };
$q->{title} = "Changed Profiles";
$q->{headers} = [ ];
$q->{explanation} = "The following profiles were changed. Would you like to save them?";
$q->{functions} = [
"CMD_SAVE_CHANGES", "CMD_VIEW_CHANGES",
"CMD_ABORT",
];
$q->{default} = "CMD_VIEW_CHANGES";
$q->{options} = [ @changed ];
$q->{selected} = 0;
my ($p, $ans, $arg);
do {
($ans, $arg) = UI_PromptUser($q);
if ($ans eq "CMD_VIEW_CHANGES") {
my $which = $changed[$arg];
my $oldprofile = serialize_profile($original_sd{$which},
$which);
my $newprofile = serialize_profile($sd{$which}, $which);
display_changes($oldprofile, $newprofile);
}
} until $ans =~ /^CMD_SAVE_CHANGES/;
for my $profile (sort keys %changed) {
writeprofile($profile);
reload($profile);
}
}
}
sub is_repo_profile {
my $profile_data = shift;
return $profile_data->{repo}{url} &&
$profile_data->{repo}{user} &&
$profile_data->{repo}{id};
}
sub submit_created_profiles {
my $url = $cfg->{settings}{repository};
my @new_profiles;
for my $profile (sort keys %sd) {
unless (is_repo_profile($sd{$profile}{$profile})) {
push @new_profiles, $profile;
}
}
if ($repo_client && @new_profiles) {
my $q = { };
$q->{title} = "Submit New Profiles";
$q->{headers} = [
"Repository", $url,
];
$q->{explanation} = "Would you like to upload the following newly created profiles?";
$q->{functions} = [
"CMD_UPLOAD_CHANGES", "CMD_VIEW_CHANGES", "CMD_ASK_NEVER",
"CMD_ABORT",
];
$q->{default} = "CMD_VIEW_CHANGES";
$q->{options} = [ @new_profiles ];
$q->{selected} = 0;
my ($ans, $arg);
do {
($ans, $arg) = UI_PromptUser($q);
if ($ans eq "CMD_VIEW_CHANGES") {
my $which = $new_profiles[$arg];
my $newprofile = serialize_profile($sd{$which}, $which);
display_text($which, $newprofile);
}
} until $ans =~ /^CMD_UPLOAD_CHANGES/;
if ($ans eq "CMD_UPLOAD_CHANGES") {
my $changelog = UI_GetString("Changelog Entry: ", "");
my ($user, $pass) = get_repo_user_pass();
if ($user && $pass) {
for my $profile (@new_profiles) {
my $profile_string = serialize_profile($sd{$profile},
$profile);
my @args = (
'Create', $user, $pass, $cfg->{settings}{distro},
$profile, $profile_string, $changelog
);
my $res = $repo_client->send_request(@args);
if (ref $res) {
my $newprofile = $res->{value};
my $newid = $newprofile->{id};
set_repo_info($sd{$profile}{$profile},
$url,
$user,
$newid);
writeprofile($profile);
UI_Info("Uploaded $profile to repository.");
} else {
print "Error: $res\n";
}
}
}
}
}
}
sub submit_changed_profiles {
my $url = $cfg->{settings}{repository};
my @repo_profiles;
for my $profile (sort keys %sd) {
if (is_repo_profile($sd{$profile}{$profile})) {
push @repo_profiles, $profile;
}
}
if (@repo_profiles) {
if ($repo_client) {
my @changed_profiles;
for my $profile (@repo_profiles) {
my $id = $sd{$profile}{$profile}{repo}{id};
my $res = $repo_client->send_request('Show', $id);
if (did_result_succeed($res)) {
my $p = $res->value;
my $profile_string = serialize_profile($sd{$profile},
$profile);
if ($p->{profile} ne $profile_string) {
push @changed_profiles, [ $profile, $p ];
}
}
}
if (@changed_profiles) {
my $q = { };
$q->{title} = "Submit Profiles";
$q->{headers} = [
"Repository", $url,
];
$q->{explanation} = "The following profiles from the repository were changed. Would you like to upload your changes?";
$q->{functions} = [
"CMD_UPLOAD_CHANGES", "CMD_VIEW_CHANGES", "CMD_ASK_NEVER",
"CMD_ABORT",
];
$q->{default} = "CMD_VIEW_CHANGES";
$q->{options} = [ map { $_->[0] } @changed_profiles ];
$q->{selected} = 0;
my ($ans, $arg);
do {
($ans, $arg) = UI_PromptUser($q);
if ($ans eq "CMD_VIEW_CHANGES") {
my $which = $changed_profiles[$arg]->[0];
my $repo_profile = $changed_profiles[$arg]->[1]->{profile};
my $newprofile = serialize_profile($sd{$which}, $which);
display_changes($repo_profile, $newprofile);
}
} until $ans =~ /^CMD_UPLOAD_CHANGES/;
print "UPLOAD CHANGES\n";
if ($ans eq "CMD_UPLOAD_CHANGES") {
my $changelog = UI_GetString("Changelog Entry: ", "");
my ($user, $pass) = get_repo_user_pass();
if ($user && $pass) {
for my $profile (map { $_->[0] } @changed_profiles) {
my $profile_string =
serialize_profile($sd{$profile}, $profile);
my @args = (
'Create', $user, $pass, $cfg->{settings}{distro},
$profile, $profile_string, $changelog
);
my $res = $repo_client->send_request(@args);
if (ref $res) {
my $newprofile = $res->{value};
my $newid = $newprofile->{id};
set_repo_info($sd{$profile}{$profile},
$url,
$user,
$newid);
writeprofile($profile);
UI_Info("Uploaded $profile to repository.");
} else {
print "Error: $res\n";
}
}
}
}
}
}
}
}
sub display_text {
my ($header, $body) = @_;
if (open(PAGER, "| less")) {
print PAGER "$header\n\n$body";
close(PAGER);
}
}
sub display_changes {
my ($oldprofile, $newprofile) = @_;
my $oldtmp = new File::Temp( UNLINK => 0 );
print $oldtmp $oldprofile;
close($oldtmp);
my $newtmp = new File::Temp( UNLINK => 0 );
print $newtmp $newprofile;
close($newtmp);
system("diff -uw $oldtmp $newtmp | less");
unlink($oldtmp);
unlink($newtmp);
}
sub setprocess ($$) {
my ($pid, $profile) = @_;
@@ -2340,6 +2964,7 @@ sub readprofile ($$) {
my $profile_data = parse_profile_data($data, $file);
if ($profile_data) {
attach_profile_data(\%sd, $profile_data);
attach_profile_data(\%original_sd, $profile_data);
}
};
@@ -2356,15 +2981,17 @@ sub readprofile ($$) {
sub attach_profile_data {
my ($profiles, $profile_data) = @_;
# make deep copies of the profile data so that if we change one set of
# profile data, we're not changing others because of sharing references
for my $p ( keys %$profile_data) {
$profiles->{$p} = $profile_data->{$p};
$profiles->{$p} = dclone($profile_data->{$p});
}
}
sub parse_profile_data {
my ($data, $file) = @_;
my ($profile_data, $profile, $hat, $in_contained_hat);
my ($profile_data, $profile, $hat, $in_contained_hat, $repo_data);
my $initial_comment = "";
for (split(/\n/, $data)) {
chomp;
@@ -2413,6 +3040,13 @@ sub parse_profile_data {
$profile_data->{$profile}{$hat}{initial_comment} = $initial_comment if $initial_comment;
$initial_comment = "";
if ($repo_data) {
$profile_data->{$profile}{$profile}{repo}{url} = $repo_data->{url};
$profile_data->{$profile}{$profile}{repo}{user} = $repo_data->{user};
$profile_data->{$profile}{$profile}{repo}{id} = $repo_data->{id};
$repo_data = undef;
}
} elsif (m/^\s*\}\s*$/) { # end of a profile...
# if we hit the end of a profile when we're not in one, something's
@@ -2554,7 +3188,11 @@ sub parse_profile_data {
next if /^\s*\# vim:syntax/;
# ignore Last Modified: lines
next if /^\s*\# Last Modified:/;
$initial_comment .= "$_\n";
if (/^\s*\# REPOSITORY: (\S+) (\S+) (\S+)$/) {
$repo_data = { url => $1, user => $2, id => $3 };
} else {
$initial_comment .= "$_\n";
}
}
} else {
@@ -2702,6 +3340,15 @@ sub serialize_profile {
if ($include_metadata) {
# keep track of when the file was last updated
$string .= "# Last Modified: " . localtime(time) . "\n";
# print out repository metadata
if ($profile_data->{$name}{repo} &&
$profile_data->{$name}{repo}{url} &&
$profile_data->{$name}{repo}{user} &&
$profile_data->{$name}{repo}{id}) {
my $repo = $profile_data->{$name}{repo};
$string .= "# REPOSITORY: $repo->{url} $repo->{user} $repo->{id}\n";
}
}
# print out initial comment
@@ -2753,6 +3400,7 @@ sub writeprofile ($) {
# mark the profile as up-to-date
delete $changed{$profile};
$original_sd{$profile} = dclone($sd{$profile});
}
sub getprofileflags {

View File

@@ -42,7 +42,7 @@ Requires: perl-DateManip
%else
Requires: perl-TimeDate
%endif
Requires: perl-DBI perl-DBD-SQLite perl-File-Tail perl-gettext
Requires: perl-DBI perl-DBD-SQLite perl-File-Tail perl-gettext perl-RPC-XML
Obsoletes: subdomain-utils
Provides: subdomain-utils

View File

@@ -16,6 +16,9 @@
parser = /sbin/apparmor_parser /sbin/subdomain_parser
ldd = /usr/bin/ldd
distro = opensuse
repository = http://localhost:3000/backend/api
# custom directory locations to look for #includes
#
# each name should be a valid directory containing possible #include