ACC SHELL
/**
* File:
* ui.ycp
*
* Module:
* Backup module
*
* Authors:
* Ladislav Slezak <lslezak@suse.cz>
*
* $Id: ui.ycp 56677 2009-04-08 09:01:19Z kmachalkova $
*
* Yast2 user interface functions - dialogs
*/
{
import "Backup";
import "Wizard";
import "Progress";
import "Report";
import "Label";
import "Popup";
import "String";
import "NetworkPopup";
import "Nfs";
import "PackageSystem";
import "Popup";
import "Confirm";
import "Mode";
include "backup/help_texts.ycp";
include "backup/functions.ycp";
textdomain "backup";
integer wait_time = 250; // loop delay (in miliseconds)
// used for the ag_process
integer backup_PID = nil;
// variables needed for backup when freezes on ZERO (or almost zero) free space
integer waiting_without_output = 0;
boolean enough_free_space_tmp = true;
boolean enough_free_space_target = true;
// wait 1 minute between checking for the free space
integer max_wait_without_output = 60000;
// wait sec between checking
integer wait_sec_check = 59;
integer next_time_check = time() + wait_sec_check;
// minimal reasonable free space is 512K
integer min_free_space = 512;
/**
* Function periodically checks the free space
*/
define void CheckFreeSpace () {
waiting_without_output = waiting_without_output + wait_time;
if (waiting_without_output >= max_wait_without_output) {
// null the waiting time now
waiting_without_output = 0;
if (time() < next_time_check) {
return;
}
// next space check - not before time() + wait_sec_check
next_time_check = time() + wait_sec_check;
// null the free space flags
waiting_without_output = 0;
enough_free_space_tmp = true;
enough_free_space_target = true;
// check the tmpdir free space
integer free_tmp = (integer) SCR::Read(.system.freespace, Backup::tmp_dir);
if (free_tmp != -1 && free_tmp < min_free_space) {
enough_free_space_tmp = false;
}
// check the targetdir free space
integer free_target = (integer) SCR::Read(.system.freespace, Backup::target_dir);
if (free_target != -1 && free_target < min_free_space) {
enough_free_space_target = false;
}
}
}
define symbol EvaluateFreeSpace (symbol ret) {
// If zero (or almost zero) free space in tmpdir
if (!enough_free_space_tmp) {
y2warning("Not enough free space in the temporary directory");
// cron mode => finish immediately
if (Backup::cron_mode) {
y2milestone("Finishing backup...");
return `abort;
// !cron mode => ask user
} else if ((boolean) SCR::Read (.process.running, backup_PID)) {
if (!Popup::YesNoHeadline(
// headline of a popup message
_("Warning"),
// a popup question - no free space, %1 is the name of the directory
sformat(_("There is not enough free space in the temporary directory %1.\nContinue anyway?"), Backup::tmp_dir)
)) {
y2milestone("Finishing backup...");
return `abort;
} else {
y2milestone("Trying co continue...");
waiting_without_output = 0;
return ret;
}
// background agent is not running
} else {
// a popup error message - no free space, %1 is the name of the directory
Report::Error(sformat(_("There is not enough free space in the backup target directory %1.\nAborting the backup."),Backup::tmp_dir));
return `abort;
}
}
// If zero (or almost zero) free space in targetdir
if (!enough_free_space_target) {
y2warning("Not enough free space in the backup target directory");
// cron mode => finish immediately
if (Backup::cron_mode) {
y2milestone("Finishing backup...");
return `abort;
// !cron mode => ask user
} else if ((boolean) SCR::Read (.process.running, backup_PID)) {
if (!Popup::YesNoHeadline(
// headline of a popup message
_("Warning"),
// a popup question - no free space, %1 is the name of the directory
sformat(_("There is not enough free space in the backup target directory %1.\nContinue anyway?"), Backup::target_dir)
)) {
y2milestone("Finishing backup...");
return `abort;
} else {
y2milestone("Trying co continue...");
waiting_without_output = 0;
return ret;
}
// background agent is not running
} else {
// a popup error message - no free space, %1 is the name of the directory
Report::Error(sformat(_("There is not enough free space in the backup target directory %1.\nAborting the backup."),Backup::target_dir));
return `abort;
}
}
// nothing changed
return ret;
}
/**
* Wait for output from subprocess or user action. If user press `abort button, then subprocess is terminated.
* @param wait Delay in miliseconds between user action checks (if no output from subprocess is available)
* @param abort_question Symbol for AbortConfirmation function - which dialog will be displayed if Abort button is pressed
* @return symbol Pressed button id or nil if some data is ready from subprocess.
*/
define symbol waitForUserOrProcess(integer wait, symbol abort_question) ``{
symbol ret = (symbol) UI::PollInput();
if (ret == `abort || ret == `cancel)
{
y2warning("Abort pressed");
ret = `abort;
if (AbortConfirmation(abort_question))
{
if (backup_PID != nil) SCR::Execute (.process.kill, backup_PID);
return ret;
}
else
{
ret = nil;
}
}
if (backup_PID != nil && (boolean) SCR::Read (.process.running, backup_PID))
{
sleep(wait);
ret = (symbol) UI::PollInput();
if (ret == `abort || ret == `cancel) {
ret = `abort;
y2warning("Abort pressed");
if (Backup::just_creating_archive) {
CheckFreeSpace();
ret = EvaluateFreeSpace (ret);
} else {
if (AbortConfirmation(abort_question)) {
SCR::Execute(.process.kill, backup_PID);
} else {
ret = nil;
}
}
}
}
return ret;
}
symbol last_input = nil;
map<integer,integer> deselected_ids = $[];
// type of item (displayed in the table)
string filesystem_text = _("File system");
// type of item (displayed in the table)
string directory_text = _("Directory");
// type of item (displayed in the table)
string regexp_text = _("Regular expression");
integer selected_pkg_num = 0; // number of packages which files are selected
integer modified_size = 0; // size
integer modified_num = 0; // number of modified files
integer nopkg_num = 0; // number of found files which do not belong to any package
integer nopkg_size = 0; // size
integer selected_files_num = 0; // number of selected files to backup
string checkmark = "X";
string nocheckmark = " ";
boolean root_warning_displayed = false;
boolean file_list_stored = false;
boolean hostname_stored = false;
boolean date_stored = false;
boolean comment_stored = false;
list<boolean> e2image_results = []; // results of storing ext2 images
integer archived_num = 0;
list<string> not_readable_files = [];
list<string> created_archive_files = [];
list<string> failed_ptables = [];
list<string> stored_ptables = [];
boolean read_ptables_info = false;
boolean stored_list = false;
integer tar_result = 1; // exit status of tar
integer total_files = 0; // total number of files in archive
integer added_files = 0;
boolean packages_list_stored = false;
map profilewritten = $[]; // result of writing profile
// -->>
// definition of variables for search dialog/functions
// trying to break huge cycle into more smaller functions
integer package_num = 0;
string actual_package = "";
list package_files = [ ];
string actual_instprefixes = "";
string line = "";
integer total_packages = 0;
// output strings from search script
string id_package = "Package: ";
string id_complete_package = "Complete package: ";
string id_file = "Size: ";
string id_nopackage = "Nopackage:";
string id_instprefixes = "Installed: ";
string id_reading_packages = "Reading installed packages";
string id_packages_num = "Packages: ";
string id_reading_files = "Reading all files";
string id_files_read = "Files read";
boolean search_no_package = false;
boolean reading_installed_packages = false;
// we are in ncurses
boolean in_ncurses = false;
// <<--
define void ResetGlobalVariables () {
deselected_ids = $[];
selected_pkg_num = 0;
modified_size = 0;
modified_num = 0;
nopkg_num = 0;
selected_files_num = 0;
archived_num = 0;
not_readable_files = [];
created_archive_files = [];
failed_ptables = [];
stored_ptables = [];
tar_result = 1;
total_files = 0;
added_files = 0;
package_num = 0;
actual_package = "";
package_files = [ ];
actual_instprefixes = "";
line = "";
total_packages = 0;
search_no_package = false;
reading_installed_packages = false;
Backup::just_creating_archive = false;
map ui_capabilities = UI::GetDisplayInfo();
in_ncurses = ui_capabilities["TextMode"]:true;
y2milestone("Running in TextMode: %1", in_ncurses);
}
/**
* Function for installing packages
*/
define boolean InstallNeededPackages (list<string> packages) {
if (size(packages)==0) {
y2error("empty list of packages to be installed");
return false;
}
if (Mode::test()) {
return true;
}
if (!PackageSystem::CheckAndInstallPackagesInteractive (packages)) {
y2debug("star package wasn't installed");
return false;
}
return true;
}
/**
* Update widget status in the dialog. Enable/disable widget according to
* checkbox/combobox value.
*/
define void update_cron_dialog() ``{
if (UI::QueryWidget(`id(`enabled), `Value) == true)
{
UI::ChangeWidget(`id(`time), `Enabled, true);
UI::ChangeWidget(`id(`weekday), `Enabled, (UI::QueryWidget(`id(`time), `Value) == `week));
UI::ChangeWidget(`id(`day), `Enabled, (UI::QueryWidget(`id(`time), `Value) == `month));
UI::ChangeWidget(`id(`hour), `Enabled, true);
UI::ChangeWidget(`id(`minute), `Enabled, true);
UI::ChangeWidget(`id(`old), `Enabled, true);
UI::ChangeWidget(`id(`mail), `Enabled, true);
}
else
{
// disable all widgets
UI::ChangeWidget(`id(`time), `Enabled, false);
UI::ChangeWidget(`id(`weekday), `Enabled, false);
UI::ChangeWidget(`id(`day), `Enabled, false);
UI::ChangeWidget(`id(`hour), `Enabled, false);
UI::ChangeWidget(`id(`minute), `Enabled, false);
UI::ChangeWidget(`id(`old), `Enabled, false);
UI::ChangeWidget(`id(`mail), `Enabled, false);
}
}
/**
* Refresh widget states in the location dialog
*/
define void update_location_dialog() ``{
boolean nfs = (UI::QueryWidget(`id(`nfs), `Value) == true);
UI::ChangeWidget(`id(`nfsserver), `Enabled, nfs);
UI::ChangeWidget(`id(`selectexport), `Enabled, nfs);
UI::ChangeWidget(`id(`nfsexport), `Enabled, nfs);
UI::ChangeWidget(`id(`selecthost), `Enabled, nfs);
}
/**
* Ask whether archive can be overwritten if it already exists
* @return boolean true when archive can be overwritten, nil when an error occured (e.g. NFS mount failed)
*/
define boolean WriteArchive() ``{
boolean ret = true;
y2milestone("target_type: %1", Backup::target_type);
boolean exists = (Backup::target_type == `file) ?
(((integer)SCR::Read(.target.size, Backup::archive_name)) >= 0) :
NFSFileExists(Backup::nfsserver, Backup::nfsexport, Backup::archive_name);
y2milestone("exists: %1", exists);
if (exists == true)
{
// For translators %1 is archive file name (e.g. /tmp/backup.tar)
ret = Popup::YesNo(sformat(_("File %1 already exists.\nOverwrite it?"), Backup::archive_name));
}
else if (exists == nil)
{
ret = nil;
}
return ret;
}
/**
* Display dialog for automatic backup - set time when backup module will
* be started at background with current selected profile.
* @return symbol User input value
*/
define symbol CronDialog() ``{
// read current settings from profile
map current_profile = (map)eval(Backup::backup_profiles[Backup::selected_profile]:$[]);
map current_settings = (map)eval(current_profile[`cron_settings]:$[]);
// current day settings
symbol cday = current_settings["every"]:`none;
integer cweekday = current_settings["weekday"]:0;
list<term> items = [];
// create combo box content
foreach(integer num, string name, Backup::daynames, ``{
items = add(items, `item(`id(num), name, cweekday == num));
}
);
// dialog header - %1 is profile name
Wizard::SetContents(sformat(_("Automatic Backup Options for Profile %1"), Backup::selected_profile),
`VBox(
`VSpacing(0.5),
// check box label
`Left(`CheckBox(`id(`enabled), `opt(`notify), _("&Start Backup Automatically"), current_settings["auto"]:false)),
`VSpacing(0.45),
`HBox(
`HSpacing(4),
`VBox(
// frame label
//rwalter please remove this frame
//`Frame(_("Frequency"), as you wish
`VBox(
`VSpacing(0.45),
`HBox(
`HSpacing(1),
`ComboBox(`id(`time), `opt(`notify), _("&Frequency"), [
`item(`id(`day), _("Daily"), cday == `day),
`item(`id(`week), _("Weekly"), cday == `week),
`item(`id(`month), _("Monthly"), cday == `month),
]
),
`HStretch()
),
`VSpacing(0.45)
),
//),
`VSpacing(0.45),
// frame label
`Frame(_("Backup Start Time"),
`VBox(
`VSpacing(0.45),
`HBox(
`HSpacing(1),
// combo box label
`ComboBox(`id(`weekday), _("Day of the &Week"), items),
`HSpacing(2),
// integer field widget label
`IntField(`id(`day), _("&Day of the Month"), 1, 31, current_settings["day"]:1),
`HStretch()
),
`VSpacing(0.45),
`HBox(
`HSpacing(1),
// integer field widget label
`IntField(`id(`hour), _("&Hour"), 0, 23, current_settings["hour"]:0),
`HSpacing(2),
// integer field widget label
`IntField(`id(`minute), _("&Minute"), 0, 59, current_settings["minute"]:0),
`HStretch()
),
`VSpacing(0.45)
)
),
`VSpacing(0.45),
//rwalter we can probably do without this one too.
//`Frame(_("Other Settings"), well, let's see
`VBox(
`VSpacing(0.45),
`HBox(
`HSpacing(1),
// integer field widget label
`IntField(`id(`old), _("Ma&ximum Number of Old Backups"), 0, 100, current_settings["old"]:3),
`HStretch()
),
`VSpacing(0.45),
// Checkbox label
`Left(`CheckBox(`id(`mail), _("S&end Summary Mail to User root"), Backup::mail_summary)),
`VSpacing(0.45)
)
//)
),
`HSpacing(2)
)
),
backup_help_cron_dialog(), true, true
);
// replace 'Next' button with 'Ok'
Wizard::SetNextButton(`next, Label::OKButton() );
UI::SetFocus (`id (`next));
// set initial widget states
update_cron_dialog();
symbol ret = (symbol)UI::UserInput();
while (!contains([`abort, `cancel, `back, `next], ret))
{
update_cron_dialog();
ret = (symbol)UI::UserInput();
}
if (ret == `cancel)
{
ret = `abort;
}
else if (ret == `next)
{
// store setting to the selected profile
if (Backup::selected_profile != nil)
{
boolean cron_changed = (current_settings["auto"]:nil != UI::QueryWidget(`id(`enabled), `Value) ||
current_settings["every"]:nil != UI::QueryWidget(`id(`time), `Value) ||
current_settings["weekday"]:nil != UI::QueryWidget(`id(`weekday), `Value) ||
current_settings["day"]:nil != UI::QueryWidget(`id(`day), `Value) ||
current_settings["hour"]:nil != UI::QueryWidget(`id(`hour), `Value) ||
current_settings["minute"]:nil != UI::QueryWidget(`id(`minute), `Value));
y2milestone("cron_changed: %1", cron_changed);
current_settings["auto"] = UI::QueryWidget(`id(`enabled), `Value);
current_settings["every"] = UI::QueryWidget(`id(`time), `Value);
current_settings["weekday"] = UI::QueryWidget(`id(`weekday), `Value);
current_settings["day"] = UI::QueryWidget(`id(`day), `Value);
current_settings["hour"] = UI::QueryWidget(`id(`hour), `Value);
current_settings["minute"] = UI::QueryWidget(`id(`minute), `Value);
current_settings["old"] = UI::QueryWidget(`id(`old), `Value);
current_settings["cron_changed"] = cron_changed;
current_profile[`cron_settings] = eval(current_settings);
current_profile[`mail_summary] = UI::QueryWidget(`id(`mail), `Value);
Backup::backup_profiles[Backup::selected_profile] = eval(current_profile);
y2milestone("Profile: %1, New settings: %2", Backup::selected_profile, current_settings);
}
}
Wizard::RestoreNextButton();
return ret;
}
/**
* Refresh widget status (enable/disable) in the displayed dialog.
* @param enable_archive_type if true enable archive selection combo box
* and option push button, select "archive" radio button.
* If enable_archive_type is false then disbale widgets, select
* "only list" radio button.
*/
define void refresh_widget_status(boolean enable_archive_type) ``{
if (enable_archive_type == false)
{
UI::ChangeWidget(`id(`type), `Enabled, false);
UI::ChangeWidget(`id(`opts), `Enabled, false);
// UI::ChangeWidget(`id(`description), `Enabled, false);
UI::ChangeWidget(`id(`rbgroup), `CurrentButton, `only_list);
}
else
{
UI::ChangeWidget(`id(`type), `Enabled, true);
UI::ChangeWidget(`id(`opts), `Enabled, true);
// UI::ChangeWidget(`id(`description), `Enabled, true);
UI::ChangeWidget(`id(`rbgroup), `CurrentButton, `archive);
}
}
/**
* Dialog asks for aborting the new profile creation,
* If aborted, profile is deleted and true returned.
*
* @param string profile name
* @return boolean whether `abort` meaning is returned
*/
define boolean AbortNewProfileCreation(string profile_name) {
// TRANSLATORS: Popup question, [Yes] means `cancel the profile creation`
boolean cancel_it = Popup::YesNo(_("Really cancel the profile creation?"));
if (cancel_it) {
// remove the profile
Backup::RemoveBackupProfile(profile_name, true);
// abort the creation
return true;
} else {
// continue creation
return false;
}
}
/**
* Dialog for setting archive options
* @return symbol Symbol for wizard sequencer - pressed button
*/
define symbol ArchDialog() ``{
// do not allow manual changes of configuration
if (Backup::no_interactive)
{
boolean cont = true;
string nfsdir = "";
// no multivolume and does not exists, continue
if (!Backup::multi_volume)
{
// check if archive can be (over)written
cont = WriteArchive();
if (cont == nil)
{
// error popup message - NFS mount failed
Popup::Error(_("Cannot mount the selected NFS share."));
cont = false;
}
}
else if (Backup::multi_volume) // test if some volume part exists
{
// list directory content
string dir = substring(Backup::archive_name, 0, findlastof(Backup::archive_name, "/"));
string fn = substring(Backup::archive_name, findlastof(Backup::archive_name, "/") + 1);
if (Backup::target_type == `nfs)
{
nfsdir = Nfs::Mount(Backup::nfsserver, Backup::nfsexport, nil, "", "");
if (nfsdir == nil)
{
// error popup message - NFS mount failed
Popup::Error(_("Cannot mount the selected NFS share."));
}
else
{
dir = nfsdir + "/" + dir;
}
}
y2debug("dir: %1", dir);
y2debug("file: %1", fn);
list<string> ls = (list<string>)SCR::Read(.target.dir, dir);
boolean matched = false;
string conflict_file = "";
// check if some volume part exists
foreach(string f, ls, ``{
if (regexpmatch(f, "\d*_" + fn) == true)
{
if (matched == false)
{
matched = true;
conflict_file = f;
}
}
}
);
y2debug("found volume part: %1", conflict_file);
// display question
if (matched == true)
{
// For translators %1 is volume file name (e.g. /tmp/01_backup.tar)
cont = Popup::YesNo(sformat(_("The existing file %1 could become part of new volume set and be overwritten.\nReally continue?"), dir + "/" + conflict_file));
}
else
{
cont = true;
}
}
// unmount mounted directory
if (size(nfsdir) > 0)
{
Nfs::Unmount(nfsdir);
}
if( cont )
{
last_input = `next;
return last_input;
}
}
// dialog header
Wizard::SetContents(_("Archive Settings"),
`VBox(
`VSpacing(0.5),
`HBox (
`InputField (`id (`filename), `opt (`hstretch), Label::FileName()),
`HSpacing(1),
`VBox (
`Label (""),
`PushButton (`id(`browse_file), Label::BrowseButton())
)
),
`VSpacing(0.5),
`Frame(_("Backup Location"),
`HBox(
`RadioButtonGroup(`id(`source), `opt(`notify),
`VBox(
`VSpacing(0.3),
// radio button label
`Left(`RadioButton(`id(`file), `opt(`notify), _("&Local File"), Backup::target_type == `file)),
`VSpacing(0.3),
// radio button label
`Left(`RadioButton(`id(`nfs), `opt(`notify), _("Network (N&FS)"), Backup::target_type == `nfs)),
`HBox(
`HSpacing(2),
// text entry label
`InputField (`id (`nfsserver), `opt (`hstretch), _("I&P Address or Name of NFS Server"), Backup::nfsserver),
`HSpacing(1),
// push button label
`VBox(
`Label(""),
// Pushbutton label
`PushButton(`id(`selecthost), _("&Select..."))
)
),
`HBox(
`HSpacing(2),
// text entry label
`InputField (`id (`nfsexport), `opt (`hstretch), _("&Remote Directory"), Backup::nfsexport),
`HSpacing(1),
// push button label
`VBox(
`Label(""),
// Pushbutton label
`PushButton(`id(`selectexport), _("S&elect..."))
)
),
`VSpacing(0.3)
)
),
`HSpacing(1)
)
),
`VSpacing(0.5),
// frame label
`Frame( _("Archive Type"),
`VBox(
`VSpacing(`opt(`hstretch), 0.5),
`RadioButtonGroup(`id(`rbgroup),
`VBox(
`Left(`RadioButton(`id(`archive), `opt(`notify), _("Create Backup Archive"))),
`HBox(
`HSpacing(4.0),
`ComboBox(`id(`type), `opt(`notify),
// combo box label
_("Archive &Type"),
// archive type - combo box item
[ `item(`id(`tgz), _("tar with tar-gzip subarchives")),
// archive type - combo box item
`item(`id(`tbz), _("tar with tar-bzip2 subarchives")),
// archive type - combo box item
`item(`id(`tar), _("tar with tar subarchives")),
// archive type - combo box item
`item(`id(`stgz), _("tar with star-gzip subarchives")),
// archive type - combo box item
`item(`id(`stbz), _("tar with star-bzip2 subarchives")),
// archive type - combo box item
`item(`id(`star), _("tar with star subarchives")),
]
),
`HSpacing(3.0),
`VBox(
`VSpacing(1.0),
// push button label
`PushButton(`id(`opts), _("&Options..."))
),
`HStretch()
),
`VSpacing(0.5),
// radiobutton label
`Left(`RadioButton(`id(`only_list), `opt(`notify), _("Only Create List of Files Found")))
)
),
`VSpacing(0.5)
)
),
`VSpacing(0.5)
),
backup_help_archive_settings(), true, true
);
if (Backup::archive_type == nil)
Backup::archive_type = `tgz;
// set values
UI::ChangeWidget(`id(`filename), `Value, Backup::archive_name);
// UI::ChangeWidget(`id(`description), `Value, Backup::description);
if (Backup::archive_type != `txt)
{
UI::ChangeWidget(`id(`type), `Value, Backup::archive_type);
}
refresh_widget_status(Backup::archive_type != `txt);
boolean cont = false;
symbol ret = nil;
map id_result = (map)SCR::Execute(.target.bash_output, "/usr/bin/id -u");
if (id_result["stdout"]:"" != "0\n" && root_warning_displayed == false)
{
// warning popup message
Popup::Warning(_("You are not logged in as root.
Some files can only be read by the user root.
Not all files will be backed up,
so it will not be possible to restore
the system completely later.
System areas on hard disks can only
be backed up by root.
"));
root_warning_displayed = true;
}
while (!cont)
{
ret = (symbol) UI::UserInput();
if (ret == `browse_file) {
// TRANSLATORS: explanatory headline for UI::AskForExistingFile pop-up
string new_filename = UI::AskForExistingFile("", "", _("Where would you like to store the backup?"));
if (new_filename != nil && new_filename != "") {
UI::ChangeWidget (`id(`filename), `Value, new_filename);
}
continue;
}
// update dialog (enable/disable widgets)
update_location_dialog();
Backup::nfsserver = (string)UI::QueryWidget(`id(`nfsserver), `Value);
Backup::nfsexport = (string)UI::QueryWidget(`id(`nfsexport), `Value);
Backup::archive_name = (string)UI::QueryWidget(`id(`filename), `Value);
symbol type = (((symbol)UI::QueryWidget(`id(`rbgroup), `CurrentButton)) == `only_list) ? `txt : ((symbol)UI::QueryWidget(`id(`type), `Value));
// add extension ".tar" if it is missing
if (type != `txt)
{
Backup::archive_name = AddMissingExtension(Backup::archive_name, ".tar");
UI::ChangeWidget(`id(`filename), `Value, Backup::archive_name);
}
if (ret == `type)
{
boolean stat = ((symbol)UI::QueryWidget(`id(`type), `Value)) != `txt;
UI::ChangeWidget(`id(`opts), `Enabled, stat);
}
else
{
if (ret == `next)
{
// check NFS server and export name
if (((boolean)UI::QueryWidget(`id(`nfs), `Value)) == true)
{
if (size((string)UI::QueryWidget(`id(`nfsserver), `Value)) == 0)
{
Popup::Warning(_("A server name is required.
Enter the server name to use.
"));
continue;
}
else if (size((string)UI::QueryWidget(`id(`nfsexport), `Value)) == 0)
{
Popup::Warning(_("A remote directory name is required.
Enter the directory name to use.
"));
continue;
}
}
// check if star is installed for star subarchive type
if ((type == `star || type == `stgz || type == `stbz)
&& (((integer)SCR::Read(.target.size, "/usr/bin/star")) < 0)) {
InstallNeededPackages( ["star"] );
}
y2milestone("Multivolume: %1", Backup::multi_volume);
if (size(Backup::archive_name) == 0)
{
// warning popup message
Popup::Warning(_("An archive filename is required.
Enter the filename to use.
"));
cont = false;
continue;
}
if (substring(Backup::archive_name, 0, 1) != "/" && Backup::target_type == `file)
{
// warning popup message
Popup::Warning(_("Enter the archive filename with\nits absolute path, as in /tmp/backup.tar."));
cont = false;
continue;
}
string dir = substring(Backup::archive_name, 0, findlastof(Backup::archive_name, "/"));
// testing if the directory exists or if it is possible to create it
string error_message = IsPossibleToCreateDirectoryOrExists(dir);
if (error_message != "") {
Popup::Error(error_message);
cont = false;
continue;
}
if (!Backup::multi_volume)
{
// check if archive can be (over)written
cont = WriteArchive();
if (cont == nil)
{
// error popup message - NFS mount failed
Popup::Error(_("Cannot mount the selected NFS share."));
cont = false;
}
}
else if (Backup::multi_volume) // test if some volume part exists
{
// list directory content
string dir = substring(Backup::archive_name, 0, findlastof(Backup::archive_name, "/"));
string fn = substring(Backup::archive_name, findlastof(Backup::archive_name, "/") + 1);
y2debug("dir: %1", dir);
y2debug("file: %1", fn);
list<string> ls = (list<string>)SCR::Read(.target.dir, dir);
boolean matched = false;
string conflict_file = "";
// check if some volume part exists
foreach(string f, ls, ``{
if (regexpmatch(f, "\d*_" + fn) == true)
{
if (matched == false)
{
matched = true;
conflict_file = f;
}
}
}
);
y2debug("found volume part: %1", conflict_file);
// display question
if (matched == true)
{
// For translators %1 is volume file name (e.g. /tmp/01_backup.tar)
cont = Popup::YesNo(sformat(_("The existing file %1 could become part of new volume set and be overwritten.\nReally continue?"), dir + "/" + conflict_file));
}
else
{
cont = true;
}
}
else
{
cont = true;
}
}
else if (ret == `back) {
// backup profile is a new profile, just created
if (Backup::profile_is_new_one) {
ret = `back;
cont = AbortNewProfileCreation(Backup::selected_profile);
} else {
ret = `back;
cont = true;
}
}
else if (ret == `abort || ret == `cancel)
{
ret = `abort;
cont = AbortConfirmation(`changed);
}
else if (ret == `only_list || ret == `archive)
{
refresh_widget_status(ret == `archive);
}
else if (ret == `file || ret == `nfs)
{
update_location_dialog();
Backup::target_type = ret;
}
// select NFS server
else if (ret == `selecthost)
{
string srv = NetworkPopup::NFSServer(Backup::nfsserver);
if (srv != nil)
{
UI::ChangeWidget(`id(`nfsserver), `Value, srv);
Backup::nfsserver = srv;
}
}
// select NFS export
else if (ret == `selectexport)
{
string server = (string)UI::QueryWidget(`id(`nfsserver), `Value);
if (size(server) > 0)
{
string exp = NetworkPopup::NFSExport(server, Backup::nfsexport);
if (exp != nil)
{
UI::ChangeWidget(`id(`nfsexport), `Value, exp);
Backup::nfsexport = exp;
}
}
else
{
Popup::Message(_("Enter a server name."));
}
}
else
{
cont = true;
}
}
}
// get values
Backup::archive_type = (((symbol)UI::QueryWidget(`id(`rbgroup), `CurrentButton)) == `archive) ? (symbol)UI::QueryWidget(`id(`type), `Value) : `txt;
if (ret == `opts)
{
if (contains([`tar, `tgz, `tbz, `stgz, `stbz, `star], Backup::archive_type))
{
ret = `tar_opt;
}
}
last_input = ret;
return ret;
}
/**
* Setting multi volume archive options
* @return symbol Symbol for wizard sequencer - pressed button
*/
define symbol TarOptionsDialog() ``{
list description_combo = MediaList2UIList(Backup::media_descriptions);
// combo box item - user defined volume size of archive
description_combo = add(description_combo, `item(`id(`user_defined), _("Custom")));
y2milestone("%1", description_combo);
list description_combo_units = MediaList2UIList(Backup::units_description);
y2milestone("%1", description_combo_units);
// dialog header
Wizard::SetContents( _("Archive File Options"),
`VBox(
`VSpacing(0.5),
// frame label
`Frame(_("Multivolume Archive"),
`VBox(
`VSpacing(`opt(`hstretch), 0.5),
// check box label
`Left(`CheckBox(`id(`multi_volume), `opt(`notify), _("&Create a Multivolume Archive"), Backup::multi_volume)),
`VSpacing(0.5),
`HBox(
`HSpacing(3.5),
`VBox(
// combo box label
`Left(`ComboBox(`id(`vol), `opt(`notify), _("&Volume Size"), description_combo)),
`VSpacing(0.5),
`Left(
`VSquash(`HBox(
// text entry label
`Bottom(`InputField (`id (`user_size), `opt (`hstretch), _("Custom &Size"))),
`HSpacing(1),
`Bottom(`ComboBox(`id(`user_unit), "", description_combo_units)),
`HStretch()
))
),
`VSpacing(0.5)
),
`HSpacing(1.0)
)
)
),
`VSpacing(1.0)
),
backup_help_archive_options(), true, true
);
// replace 'Next' button with 'Ok'
Wizard::SetNextButton(`ok, Label::OKButton() );
UI::SetFocus (`id (`ok));
// allow only digits in the text entry
UI::ChangeWidget(`id(`user_size), `ValidChars, "0123456789" );
if (Backup::volume_size == nil)
{
Backup::volume_size = `fd144;
}
UI::ChangeWidget(`id(`vol), `Value, Backup::volume_size);
string tmp_size = "";
if (Backup::user_volume_size != nil)
{
tmp_size = Backup::user_volume_size;
}
UI::ChangeWidget(`id(`user_size), `Value, tmp_size);
if (Backup::user_volume_unit == nil)
{
Backup::user_volume_unit = `B;
}
UI::ChangeWidget(`id(`user_unit), `Value, Backup::user_volume_unit);
SetMultiWidgetsState();
boolean cont = false;
symbol ret = nil;
while (!cont)
{
ret = (symbol)UI::UserInput();
if (ret == `ok)
{
if (((boolean)UI::QueryWidget(`id(`multi_volume), `Value)) == true && ((symbol)UI::QueryWidget(`id(`vol), `Value)) == `user_defined &&
(tofloat(UI::QueryWidget(`id(`user_size), `Value)) * tofloat(Backup::GetCapacity(Backup::units_description, (symbol)UI::QueryWidget(`id(`user_unit), `Value))) / 1024.0) < 10.0)
{
// warning popup message
Popup::Warning(_("Volume size must be at least 10240 bytes."));
}
else
{
cont = true;
Backup::multi_volume = (boolean)UI::QueryWidget(`id(`multi_volume), `Value);
Backup::volume_size = (symbol)UI::QueryWidget(`id(`vol), `Value);
Backup::user_volume_size = (string)UI::QueryWidget(`id(`user_size), `Value);
Backup::user_volume_unit = (symbol)UI::QueryWidget(`id(`user_unit), `Value);
}
}
else
{
if (ret == `multi_volume || ret == `vol)
{
SetMultiWidgetsState();
}
else
{
if (ret == `abort || ret == `cancel)
{
ret = `abort;
cont = AbortConfirmation(`changed);
}
else
{
cont = true;
}
}
}
}
// Restore 'next' button
Wizard::RestoreNextButton();
return ret;
}
/**
* Dialog for setting backup options
* @return symbol Symbol for wizard sequencer - pressed button
*/
define symbol BackupDialog() ``{
// do not allow manual changes of configuration
if( Backup::no_interactive ) return `next2;
// dialog header
Wizard::SetContents( _("Backup Options"),
`VBox(`VSpacing(0.5),
//rwalter consider whether this frame and the next should be eliminated to simplify the dialog
// frame label
`Frame( _("File Selection"),
`VBox(
`VSpacing(0.3),
// check box label
`Left(`CheckBox(`id(`search), `opt(`notify), _("&Back Up Files Not Belonging to Any Package"), Backup::do_search)),
// check box label
`Left(`CheckBox(`id(`all_rpms_content), `opt(`notify), _("Back Up Content of &All Packages"), Backup::backup_all_rpms_content)),
`VSpacing(0.3),
// check box label
`Left(`CheckBox(`id(`display), _("Display List of Files Before &Creating Archive"), Backup::display)),
`VSpacing(0.3)
)
),
`VSpacing(0.5),
// frame label
`Frame( _("Search Options"),
`VBox(
`VSpacing(0.3),
// frame label
`Left(`CheckBox(`id(`md5_check), _("Check MD&5 Sum instead of Time or Size"), Backup::do_md5_test)),
`VSpacing(0.3)
)
),
`VSpacing(0.3),
// multi line widget label
`MultiLineEdit(`id(`description), _("Archive &Description"), Backup::description),
`VSpacing(0.3),
`PushButton(`id(`xpert), _("E&xpert...")),
`VSpacing(0.5)
),
backup_help_backup_setting(), true, true
);
symbol ret = nil;
while (ret != `next && ret != `abort && ret != `back && ret != `xpert)
{
ret = (symbol)UI::UserInput();
if (ret == `abort || ret == `cancel)
{
ret = `abort;
if(!AbortConfirmation(`changed))
{
ret = nil; // not confirmed, unset ret
}
}
}
// get values
Backup::backup_all_rpms_content = (boolean) UI::QueryWidget(`id(`all_rpms_content), `Value);
Backup::do_search = (boolean)UI::QueryWidget(`id(`search), `Value);
Backup::display = (boolean)UI::QueryWidget(`id(`display), `Value);
Backup::do_md5_test = (boolean)UI::QueryWidget(`id(`md5_check), `Value);
Backup::description = (string)UI::QueryWidget(`id(`description), `Value);
return ret;
}
/**
* System area backup options
* @return symbol Symbol for wizard sequencer - pressed button
*/
define symbol SystemBackupDialog() ``{
if (Backup::detected_ext2 == nil)
{
// status message - label
Wizard::SetContents("", `Label(_("Detecting mounted ext2 file systems...")), "", false, false);
Backup::detected_ext2 = Ext2Filesystems();
Backup::ext2_backup = AddIdExt2(Backup::detected_ext2);
}
// dialog header
Wizard::SetContents( _("System Area Backup"),
`VBox(
`VSpacing(0.5),
// frame label
`Frame( _("Partition Table"),
`VBox(
`VSpacing(0.4),
// check box label
`CheckBox(`id(`pt), `opt(`hstretch), _("Ba&ck Up Partition Tables"), Backup::backup_pt),
`VSpacing(0.4)
)
),
`VSpacing(0.5),
// frame label
`Frame(_("Ext2 File System Critical Area Backup"),
`RadioButtonGroup(`id(`rbg),
`VBox(
`VSpacing(0.3),
// radio button label
`Left(`RadioButton(`id(`none), `opt(`notify), _("N&one"), Backup::backup_none_ext2)),
`VSpacing(0.3),
// radio button label
`Left(`RadioButton(`id(`allmounted), `opt(`notify), _("All &Mounted"), Backup::backup_all_ext2)),
`VSpacing(0.3),
// radio button label
`Left(`RadioButton(`id(`selected), `opt(`notify), _("&Selected"), Backup::backup_selected_ext2)),
`HBox(
`HSpacing(3.0),
`VBox (
// table header
`Table(`id(`par), `header(_("Ext2 Partition"), _("Mount Point")), Backup::ext2_backup),
`Left(`HBox(
// push button label
`PushButton(`id(`addnew),`opt(`key_F3), _("A&dd...") ) ,
// push button label
`PushButton(`id(`edit),`opt(`key_F4), _("&Edit...") ) ,
// push button label
`PushButton(`id(`delete),`opt(`key_F5), _("De&lete") )
))
),
`HSpacing(2.0)
),
`VSpacing(0.4)
)
)
),
`VSpacing(1.0)
),
backup_help_system_backup(), true, true
);
Wizard::SetNextButton(`finish, Label::OKButton() );
UI::SetFocus (`id (`finish));
UI::ChangeWidget(`id(`par), `Enabled, Backup::backup_selected_ext2);
UI::ChangeWidget(`id(`addnew), `Enabled, Backup::backup_selected_ext2);
// Enable the buttons only if they are of any use
boolean enable_modif_buttons = (size (Backup::ext2_backup) > 0);
UI::ChangeWidget(`id(`edit), `Enabled, enable_modif_buttons);
UI::ChangeWidget(`id(`delete), `Enabled, enable_modif_buttons);
symbol ret = nil;
while (ret != `finish && ret != `back && ret != `abort)
{
ret = (symbol)UI::UserInput();
string curr = (string)UI::QueryWidget(`id(`par), `CurrentItem);
if (curr != nil)
{
if (ret == `edit)
{
map edited = ShowEditDialog(Label::EditButton(), curr, nil, []);
if (edited["clicked"]:nil == `ok)
{
string txt = (string)(edited["text"]:"");
string dir = Ext2MountPoint(txt);
if (txt != curr)
{
if (contains(Backup::ext2_backup, `item(`id(txt), txt, dir)))
{
// error popup message, %1 is partition name (e.g. /dev/hda1)
Popup::Error(sformat(_("Partition %1 is already in the list."), txt));
}
else
{
// refresh ext2_backup content
Backup::ext2_backup = maplist(term i, Backup::ext2_backup, ``{
string tmp = i[0,0]:"";
if (tmp == curr)
{
return `item(`id(txt), txt, dir);
}
else
{
return i;
}
});
// refresh table content
UI::ChangeWidget(`id(`par), `Items, Backup::ext2_backup);
y2debug("ext2: %1", Backup::ext2_backup);
}
}
}
}
if (ret == `delete)
{
if (! Confirm::DeleteSelected()) continue;
Backup::ext2_backup = filter(term i, Backup::ext2_backup, ``{
string tmp = i[0,0]:"";
return (tmp != curr);
});
UI::ChangeWidget(`id(`par), `Items, Backup::ext2_backup);
}
}
if (ret == `addnew)
{
list<string> detected_ext2_strings = [];
foreach(map info, Backup::detected_ext2,
``{
string part = (string)(info["partition"]:nil);
if (part != nil)
{
detected_ext2_strings = add(detected_ext2_strings, part);
}
}
);
// popup dialog header
map result = ShowEditDialog(_("&Add Ext2 Partition"), "", detected_ext2_strings, []);
if (((symbol)(result["clicked"]:nil)) == `ok)
{
integer sz = size(Backup::ext2_backup);
string new_par = (string)(result["text"]:nil);
string dir = Ext2MountPoint(new_par);
// add new partition only if it's not empty a it isn't already in list
if (new_par != "" && new_par != nil)
{
if (contains(Backup::ext2_backup, `item(`id(new_par), new_par, dir)))
{
// error popup message, %1 is partition name (e.g. /dev/hda1)
Popup::Error(sformat(_("Partition %1 is already in the list."), new_par));
}
else
{
Backup::ext2_backup = add(Backup::ext2_backup, `item(`id(new_par), new_par, dir));
UI::ChangeWidget(`id(`par), `Items, Backup::ext2_backup);
y2debug("ext2: %1", Backup::ext2_backup);
}
}
}
}
// Enable the buttons only if they are of any use
enable_modif_buttons = (size (Backup::ext2_backup) > 0);
UI::ChangeWidget(`id(`edit), `Enabled, enable_modif_buttons);
UI::ChangeWidget(`id(`delete), `Enabled, enable_modif_buttons);
if (ret == `allmounted || ret == `selected || ret == `none)
{
Backup::backup_all_ext2 = (boolean)UI::QueryWidget(`id(`allmounted), `Value);
Backup::backup_none_ext2 = (boolean)UI::QueryWidget(`id(`none), `Value);
Backup::backup_selected_ext2 = (boolean)UI::QueryWidget(`id(`selected), `Value);
UI::ChangeWidget(`id(`par), `Enabled, Backup::backup_selected_ext2);
UI::ChangeWidget(`id(`addnew), `Enabled, Backup::backup_selected_ext2);
UI::ChangeWidget(`id(`edit), `Enabled, Backup::backup_selected_ext2);
UI::ChangeWidget(`id(`delete), `Enabled, Backup::backup_selected_ext2);
}
if (ret == `abort || ret == `cancel)
{
ret = `abort;
if (!AbortConfirmation(`changed))
{
ret = nil; // not confirmed, uset ret
}
}
}
Backup::backup_pt = (boolean)UI::QueryWidget(`id(`pt), `Value);
Wizard::RestoreNextButton();
return ret;
}
/**
* Check whether there is enough free space.
* @param required required space in kB
* @param available required space in kB
* @param target_type selected target archive type
* @return boolean true = there is enough free space, false = not enough free space,
* nil = may be not enough space (compression is used, impossible to tell exactly)
*/
define boolean is_space(integer required, integer available, symbol target_type) {
y2milestone("Checking free space for the backup.");
if (available < 0) {
return false;
}
if (contains( [`tgz, `stgz, `tbz, `stbz], target_type)) {
// require at least 10M extra free space (overhead)
required = required + 10240;
y2milestone("Required: %1, Available: %2", required, available);
return (required < available) ? true : nil;
} else if (target_type == `tar || target_type == `star) {
// require at least 10M extra free space (overhead)
required = required + 10240;
y2milestone("Required: %1, Available: %2", required, available);
return required < available;
} else if (target_type == `txt) {
// require at least 500k extra free space (overhead)
required = required + 500;
y2milestone("Required: %1, Available: %2", required, available);
return required < available;
} else {
y2warning("Unknown archive type: %1", target_type);
y2milestone("Required: %1, Available: %2", required, available);
}
return nil;
}
/**
* Display warning dialog - there is (may) not enough free space in the directory.
* The dialog is not displayed when cron mode is active (there is no real UI).
* @param dir directory
* @param fits if true no dialog is displayed, if false display "there is no space",
* if nil display "there may not be space"
* @return boolean false = abort backup
*/
define boolean display_free_space_warning(boolean fits, string dir) ``{
boolean cont = true;
// don't ask in cron mode - there is no UI
// always continue, summary mail with fail message
// will be sent to root user
if (Backup::cron_mode == true)
{
return cont;
}
if (fits == false)
{
// there is no enough space
cont = Popup::YesNo(sformat(_("There is not enough free space in directory %1.
Continue anyway?
"), dir));
}
else if (fits == nil)
{
// may be that there is not enough space
cont = Popup::YesNo(sformat(_("There may not be enough free space in directory %1.
Continue anyway?
"), dir));
}
return cont;
}
/**
* Check available free space and decide whether archive will fit
* @param found_size total size of found files in bytes
* @param tmp_dir selected temporary directory
* @param target_dir target archive directory
* @param target_type target archive type
* @return boolean true = archive fits, false = it doesn't fit,
* nil = may not fit (compression is used, there is no guarantee that
* archive will fit but it can be possible if compression ratio will
* be enough high
*/
define boolean check_space(integer found_size, string tmp_dir, string target_dir, symbol target_type) ``{
// required size in 1024-blocks
found_size = found_size / 1024;
map available_tmp = get_free_space(tmp_dir);
map available_target = get_free_space(target_dir);
if (size(available_target) == 0 || size(available_tmp) == 0)
{
return true;
}
boolean fits = true;
// temporary and target directories are same
if ((string)(available_tmp["device"]:nil) == (string)(available_target["device"]:nil))
{
integer free = (integer)(available_target["free"]:0);
// required space is doubled (tmpdir + target_dir)
integer required = found_size + found_size;
fits = is_space(required, free, target_type);
if (display_free_space_warning(fits, target_dir) == false)
{
return false;
}
}
else
// temporary location and target directories are different
{
fits = is_space(found_size, available_target["free"]:0, target_type);
if (display_free_space_warning(fits, target_dir) == false)
{
return false;
}
fits = is_space(found_size, available_tmp["free"]:0, target_type);
if (display_free_space_warning(fits, tmp_dir) == false)
{
return false;
}
}
return true;
}
/**
* Sets dialog contents - Searching for Modified Files
* @param integer total_packages
*/
define void SetDialogContents_SearchingForModifiedFiles(integer total_packages) {
y2milestone("Progress Packages: %1", total_packages);
Wizard::SetContents(
// dialog header
_("Searching for Modified Files"),
`VBox(
`VSpacing(0.5),
`Left(
// label text, followed by number of files found so far
`Label(`id(`numfiles), _("Modified Files: ") + sformat("%1", modified_num))
),
`VSpacing(0.5),
`Left(
// label text, followed by sizes of files
`Label(`id(`totsize), _("Total Size: ") + String::FormatSize(modified_size))
),
`VSpacing(0.5),
`Left(
// label text, followed by name of current package
`Label(`id(`package), _("Searching in Package: ") + " ")
),
`VSpacing(3.0),
`Left(
// bug #172406
`MinSize(42, 2,
`VBox (
`HStretch(),
// progress bar label
`ProgressBar(`id(`progress), `opt(`hstretch), _("Search"), total_packages)
)
)
),
`VStretch()
),
backup_help_searching_modified(), false, false
);
UI::RecalcLayout();
}
/**
* Initializes variables before the SearchingModifiedDialog
*/
define void InitSearchingModifiedDialog () {
if (!Backup::cron_mode) {
Wizard::ClearContents();
Wizard::SetContents("",
// label text
`Label(_("Reading packages available at the software repositories...")),
backup_help_searching_modified(),
false, false
);
}
// read list of available packages at the original installation sources
Backup::ReadInstallablePackages();
// initialize list of completely backed up packages
Backup::complete_backup = [];
selected_pkg_num = 0;
Backup::selected_files = $[];
}
/**
* Takes care about installed packages
*/
define void Search_ProcessInstalledPackages () {
if (substring(line, 0, size(id_packages_num)) == id_packages_num) {
reading_installed_packages = false;
total_packages = tointeger(substring(line, size(id_packages_num)));
y2milestone("Number of installed packages: %1", total_packages);
}
}
define void Search_ChangedPackageFiles () {
// store package's changed files
if (size(package_files) > 0) {
Backup::selected_files[actual_package] = $[
"changed_files" : package_files,
"install_prefixes" : actual_instprefixes
];
package_files = [];
selected_pkg_num = selected_pkg_num + 1;
}
boolean complete = (substring(line, 0, size(id_complete_package)) == id_complete_package);
actual_package = complete ? substring(line, size(id_complete_package)) : substring(line, size(id_package));
if (complete == true) {
y2debug("Complete package: %1", actual_package);
Backup::complete_backup = add(Backup::complete_backup, actual_package);
}
package_num = package_num + 1;
if (!Backup::cron_mode) {
UI::ChangeWidget(`id(`package), `Value, _("Searching in Package: ") + actual_package);
UI::ChangeWidget(`id(`progress), `Value, package_num);
// bug #172406
// Cannot be used for ncurses
if (!in_ncurses) UI::RecalcLayout();
}
}
/**
* not sure
*/
define void Search_ModifiedFiles () {
line = substring(line, size(id_file));
string size_str = substring(line, 0, findfirstof(line, " "));
modified_size = modified_size + tointeger(size_str);
modified_num = modified_num + 1;
if (!Backup::cron_mode)
{
UI::ChangeWidget(`id(`totsize), `Value, _("Total Size: ") + sformat("%1", String::FormatSize(modified_size)));
UI::ChangeWidget(`id(`numfiles), `Value, _("Modified Files: ") + sformat("%1", modified_num));
}
string found_file = substring(line, findfirstof(line, " ") + 1);
// escape newlines in file name
// double backslashes
list<string> parts = splitstring(found_file, "\\");
string escaped = mergestring(parts, "\\\\");
// change newline to \n
parts = splitstring(escaped, "\n");
escaped = mergestring(parts, "\\n");
// add file to list of found files
package_files = add(package_files, escaped);
}
define void Search_NonPackageFile () {
if (size(package_files) > 0) {
Backup::selected_files[actual_package] = $[
"changed_files" : package_files,
"install_prefixes" : actual_instprefixes
];
package_files = [];
selected_pkg_num = selected_pkg_num + 1;
}
actual_package = ""; // empty package name
search_no_package = true; // no package part of output
y2milestone("Searching files which are not in any package started");
}
/**
* Reads list of packages files, installed packages
*/
define void Search_ReadListOfFilesAndPackages () {
if (line == id_reading_files) {
if (!Backup::cron_mode) {
// label text
Wizard::SetContents("", `Label(_("Reading packages files...")), backup_help_searching_modified(), false, false);
}
} else {
if (line == id_reading_packages) {
y2milestone("Reading installed packages");
if (!Backup::cron_mode) {
// label text
Wizard::SetContents("", `Label(_("Reading list of installed packages...")), backup_help_searching_modified(), false, false);
}
reading_installed_packages = true;
} else {
if (line == id_files_read) {
if (Backup::cron_mode != true) {
SetDialogContents_SearchingForModifiedFiles(total_packages);
UI::RecalcLayout();
}
y2milestone("Searching in packages started");
} else {
y2warning("Unknown output from search script: %1", line);
}
}
}
}
/**
* Function sets the dialog contents before searching files...
*/
define void SetDialogContents_SearchingFiles () {
// dialog header
Wizard::SetContents(_("Searching Files"),
`VBox(
`HStretch(),
`VSpacing(0.5),
`Left(
// label text, followed by value
`Label(`id(`numfiles), _("Modified Files: ") + sformat("%1", nopkg_num))
),
`VSpacing(0.5),
`Left(
// label text, followed by value
`Label(`id(`totsize), _("Total Size: ") + String::FormatSize(nopkg_size))
),
`VSpacing(0.5),
`Left(
// bug #172406
`MinSize(
42, 1,
// label text, followed by current directory name
`Label(`id(`directory), _("Searching in Directory: ") + " ")
)
)
),
backup_help_searching_files(), false, false
);
UI::RecalcLayout();
}
// Function might be removed, only for testing memory spent...
define string MemorySpent () {
string command = "LC_ALL=C /bin/ps auxw | grep \"\\(y2base\\|^USER\\)\" | grep -v \"grep\"";
map <string, any> run_ps = (map <string, any>) SCR::Execute(.target.bash_output, command);
if ((integer) run_ps["exit"]:0 != 0) {
y2warning("MemorySpent Error: '%1'", (string) run_ps["stderr"]:"");
}
return (string) run_ps["stdout"]:"";
}
define boolean CallPrePostBackupScripts (list <map> scripts_to_call, string dialog_caption) {
y2milestone ("Running scripts: %1", scripts_to_call);
Wizard::SetContents (
dialog_caption,
`VBox (
`LogView (`id ("script_log"), _("User-Defined Scripts Output"), 18, 1024)
),
" ",
false,
false
);
UI::RecalcLayout();
boolean dialog_ret = true;
integer ui_wait_time = 100;
foreach (map script_to_call, scripts_to_call, {
string script = script_to_call["path"]:"";
if (script == nil || script == "") {
y2error ("Cannot run script %1", script_to_call);
return;
}
y2milestone ("Running script %1", script);
UI::ChangeWidget (`id ("script_log"), `LastLine, sformat(_("Starting script %1...\n"), script));
integer script_PID = (integer) SCR::Execute (.process.start_shell, script, $["C_locale":true]);
y2milestone ("Script started with PID %1", script_PID);
if (script_PID == nil || script_PID == 0) {
y2error ("Cannot start script %1", script);
Report::Error (sformat (_("Cannot start %1 script\n"), script));
return;
}
any ret = nil;
string line = nil;
string errline = nil;
// something might be still in the buffer(s)
boolean last_line_non_empty = nil;
while ((boolean) SCR::Read (.process.running, script_PID) || ! (boolean) SCR::Read (.process.buffer_empty, script_PID) || last_line_non_empty == true) {
last_line_non_empty = false;
line = (string) SCR::Read (.process.read_line, script_PID);
if (line != nil) {
UI::ChangeWidget (`id ("script_log"), `LastLine, line + "\n");
last_line_non_empty = true;
}
errline = (string) SCR::Read (.process.read_line_stderr, script_PID);
if (errline != nil) {
UI::ChangeWidget (`id ("script_log"), `LastLine, errline + "\n");
last_line_non_empty = true;
}
if (Backup::cron_mode) {
sleep (wait_time);
} else {
ret = UI::PollInput();
if (ret == `abort || ret == `cancel) {
y2warning("Abort pressed");
ret = `abort;
if (AbortConfirmation (`changed)) {
SCR::Execute (.process.kill, script_PID);
dialog_ret = false;
break;
}
}
if (line == nil && errline == nil)
sleep (ui_wait_time);
}
}
SCR::Execute (.process.release, script_PID);
UI::ChangeWidget (`id ("script_log"), `LastLine, "\n\n");
if (dialog_ret != true) break;
});
return dialog_ret;
}
define symbol CallScriptsBeforeBackup () {
list <map> scripts_to_call = filter (map one_script, Backup::backup_helper_scripts, {
return one_script["type"]:"before" == "before";
});
if (size (scripts_to_call) < 1) {
y2milestone ("No 'before' scripts to call...");
return `next;
}
if (! CallPrePostBackupScripts (scripts_to_call, _("User-Defined Scripts"))) {
y2milestone ("Aborting the backup");
return `abort;
}
return `next;
}
define symbol CallScriptsAfterBackup () {
list <map> scripts_to_call = filter (map one_script, Backup::backup_helper_scripts, {
return one_script["type"]:"before" != "before";
});
if (size (scripts_to_call) < 1) {
y2milestone ("No 'after' scripts to call...");
return `next;
}
if (! CallPrePostBackupScripts (scripts_to_call, _("User-Defined Scripts"))) {
y2milestone ("Aborting the backup");
return `abort;
}
return `next;
}
/**
* Display progress of searching modified files in packages
* @return symbol Symbol for wizard sequencer - pressed button
*/
define symbol SearchingModifiedDialog() {
ResetGlobalVariables();
InitSearchingModifiedDialog();
y2milestone("Search script: %1", Backup::script_get_files + Backup::get_search_script_parameters());
// starting the searching script in the background
backup_PID = (integer) SCR::Execute (.process.start_shell, Backup::script_get_files + Backup::get_search_script_parameters(), $["C_locale":true]);
list<string> script_out = [ ];
boolean started = (backup_PID != nil && backup_PID > 0);
symbol ret = nil;
y2milestone ("subprocess started: %1 (PID: %2)", started, backup_PID);
if (!started) {
// error popup message
Report::Error(_("Could not start the search script.
Aborting the backup.
"));
line = "";
while (line != nil) {
line = (string) SCR::Read (.process.read_line_stderr, backup_PID);
y2error ("Error: %1", line);
}
if (Backup::cron_mode) {
return `abort;
} else {
return `maindialog;
}
}
integer search_time = time();
// while background script runs
while ((boolean) SCR::Read (.process.running, backup_PID) || ! (boolean) SCR::Read (.process.buffer_empty, backup_PID)) {
line = (string) SCR::Read (.process.read_line, backup_PID); // read line
while (line != nil) {
// reading installed packages until the last package is read
if (reading_installed_packages) {
Search_ProcessInstalledPackages();
} else {
if (substring (line, 0, size(id_package)) == id_package || substring (line, 0, size(id_complete_package)) == id_complete_package) {
Search_ChangedPackageFiles();
} else {
if (substring(line, 0, size(id_file)) == id_file) {
Search_ModifiedFiles();
} else {
if (line == id_nopackage) {
Search_NonPackageFile();
break;
} else {
if (substring(line, 0, size(id_instprefixes)) == id_instprefixes) {
actual_instprefixes = substring(line, size(id_instprefixes));
} else {
Search_ReadListOfFilesAndPackages();
}
}
}
}
}
line = (string) SCR::Read (.process.read_line, backup_PID); // read line
}
if (search_no_package) {
break;
}
if (Backup::cron_mode) {
sleep(wait_time);
ret = nil;
} else {
ret = waitForUserOrProcess (wait_time, `changed);
}
if (ret != nil) {
return ret;
}
}
// end of the 'while' backround script runs
if (size (package_files) > 0) {
Backup::selected_files[actual_package] = $[
"changed_files" : package_files,
"install_prefixes" : actual_instprefixes
];
selected_pkg_num = selected_pkg_num + 1;
}
y2milestone("All packages verified.");
// searching files not belonging to any package
if (search_no_package) {
string actual_dir = "/";
string id_readingall = "Reading all files";
string id_readall = "Files read";
string id_dir = "Dir: ";
if (!Backup::cron_mode) {
SetDialogContents_SearchingFiles();
}
integer dircount = 0;
list package_files_part = [];
integer new_files = 0;
while ((boolean) SCR::Read (.process.running, backup_PID) || ! (boolean) SCR::Read (.process.buffer_empty, backup_PID))
// test of script_out size is needed, because previous while cycle was interrupted and script could exited with no new output...
{
line = (string) SCR::Read (.process.read_line, backup_PID); // read line
if (line == nil) continue;
// --->
if (substring(line, 0, size(id_file)) == id_file)
{
line = substring(line, size(id_file));
string size_str = substring(line, 0, findfirstof(line, " "));
integer size_line = nil;
if (size_str != nil && size_str != "") {
size_line = tointeger(size_str);
if (size_line != nil) nopkg_size = nopkg_size + size_line;
}
nopkg_num = nopkg_num + 1;
// refresh status when 10 new files was found - it's faster...
if (nopkg_num % 10 == 0 && Backup::cron_mode != true)
{
UI::ChangeWidget(`id(`totsize), `Value, _("Total Size: ") + String::FormatSize(nopkg_size));
UI::ChangeWidget(`id(`numfiles), `Value, _("Modified Files: ") + sformat("%1", nopkg_num));
}
string found_file = substring(line, findfirstof(line, " ") + 1);
// escape newlines in file name
// double backslashes
list<string> parts = splitstring(found_file, "\\");
string escaped = mergestring(parts, "\\\\");
// change newline to \n
parts = splitstring(escaped, "\n");
escaped = mergestring(parts, "\\n");
// add file to list of found files
package_files_part = add(package_files_part, escaped);
new_files = new_files + 1;
// merge more files in one step - it's faster
if (new_files == 1000)
{
package_files = merge(package_files, package_files_part);
package_files_part = [];
new_files = 0;
}
}
else
{
if (substring(line, 0, size(id_dir)) == id_dir)
{
dircount = dircount + 1;
if (dircount == 5)
{
actual_dir = substring(line, size(id_dir));
if (!Backup::cron_mode)
{
UI::ChangeWidget(`id(`directory), `Value, _("Searching in Directory: ") + actual_dir);
// bug #172406
// Cannot be used for ncurses
if (!in_ncurses) UI::RecalcLayout();
}
dircount = 0;
}
}
}
// <---
script_out = [];
if (Backup::cron_mode == true)
{
sleep(wait_time);
ret = nil;
}
else
{
ret = waitForUserOrProcess(wait_time, `changed);
}
if (ret != nil)
{
if (ret == `abort)
{
if (AbortConfirmation(`changed))
{
return ret;
}
}
}
}
Backup::selected_files[actual_package] = $[
"changed_files" : merge(package_files, package_files_part),
"install_prefixes" : actual_instprefixes
];
}
search_time = time() - search_time;
y2milestone("Searching done after %1 sec.", search_time);
list<string> dparts = splitstring(Backup::archive_name, "/");
dparts = remove(dparts, size(dparts) - 1);
string tdir = mergestring(dparts, "/");
// just for other functions
Backup::target_dir = tdir;
// check available space
if (check_space(nopkg_size + modified_size, Backup::tmp_dir, Backup::target_dir, Backup::archive_type) == false)
{
// there is no space, user selected not to continue
if (Backup::cron_mode) {
return `abort;
} else {
return `maindialog;
}
}
if (Backup::display && !Backup::no_interactive)
{
if (Backup::selected_files == $[]) {
Backup::selected_files = nil;
}
return `next;
}
else
{
// skip the files listed in Backup::unselected_files
if (Backup::unselected_files != nil) {
// try to find each file in selected files
foreach(string file, Backup::unselected_files, {
map new_selected = $[ ];
foreach(string pak, map val, Backup::selected_files, {
list<string> changed_files = ((list<string>)(val["changed_files"]:[ ]));
if( contains( changed_files , file ) ) {
changed_files = filter( string v, changed_files, ``(v != file) );
// construct new package description
Backup::selected_files[pak, "changed_files"] = changed_files;
Backup::selected_files[pak, "install_prefixes"] = val["install_prefixes"]:[];
}
});
});
y2milestone("Filtered out unselected files according to profile");
}
// count number of files
selected_files_num = 0;
foreach(string pak, map val, Backup::selected_files, {
selected_files_num = selected_files_num + size( val["changed_files"]:[] );
});
return (Backup::display && !Backup::cron_mode) ? `next : `next2; // `next2 = skip file selection dialog
}
}
/**
* Display found files, user can select files to backup
* @return symbol Symbol for wizard sequencer - pressed button
*/
define symbol FilesDialog() ``{
// busy message
Wizard::SetContents("", `Label(_("Adding files to table...")), "", false, false);
integer t1 = time();
list< list<any> > items = [];
string items_filename = Directory::tmpdir + "/items-list.ycp";
if (FileUtils::Exists (items_filename)) {
y2milestone ("Reading %1", items_filename);
items = (list< list<any> >) SCR::Read(.target.ycp, items_filename);
} else {
y2error ("File %1 doesn't exist!", items_filename);
}
// dialog header
Wizard::SetContents( _("File Selection"),
`VBox(
`VSpacing(0.5),
// label text
`Left(`Label(_("Files to Back Up"))),
`Table (
`id(`table),
`opt(`notify),
// table header
`header(" ", _("Filename"), _("Package")),
[]
),
`Left(`HBox(
// push button label
`PushButton(`id(`sel_file), _("Select or Deselect &File")),
// push button label
`PushButton(`id(`sel_all), _("&Select All")),
// push button label
`PushButton(`id(`desel_all), _("&Deselect All"))
)),
`VSpacing(1.0)
),
backup_help_file_selection(), true, true
);
// Items are pre-defined in the file
string items_filename_show = Directory::tmpdir + "/items.ycp";
UI::ChangeWidget (`id (`table), `Items,
(FileUtils::Exists (items_filename_show) ?
(list<term>) SCR::Read (.target.ycp, items_filename_show)
:
[`item (`id("none"), ["", _("Internal error"), ""])]
)
);
integer t2 = time();
y2milestone("UI finished after %1 seconds", t2 - t1);
// mark unselected files - use list from previous run
if (size(Backup::unselected_files) > 0)
{
t1 = time();
// suppose that number of unselected files is much lower than number of
// found files (otherwise we should iterate trough list of unselected
// files)
y2milestone("Found %1 unselected files", size(Backup::unselected_files));
UI::OpenDialog(
`Left(`Label(
// busy message
_("Deselecting files...")
))
);
foreach(list file_f, items, ``{
if (contains(Backup::unselected_files, (string) file_f[1]:nil) == true)
{
y2debug("Found previously unselected file: %1 (idx: %2)", file_f[1]:nil, file_f[0]:nil);
deselected_ids[(integer) file_f[0]:nil] = 1;
}
}
);
UI::CloseDialog();
y2milestone("searching unselected files was finished after %1 seconds", time() - t1);
}
if (size(deselected_ids) > 0)
{
y2milestone("Deselecting %1 files", size(deselected_ids));
// remove selection mark for deselected files
foreach(integer idx, integer dummy_value, deselected_ids, ``{
UI::ChangeWidget(`id(`table), `Item(idx, 0), " ");
}
);
}
symbol ret = nil;
boolean changed = false;
while (ret != `next && ret != `back && ret != `abort)
{
ret = (symbol)UI::UserInput();
if (ret == `abort || ret == `cancel)
{
ret = `abort;
if (!AbortConfirmation(`changed))
{
ret = nil; // not confirmed
}
else
{
break;
}
}
integer current_item = (integer)UI::QueryWidget(`id(`table), `CurrentItem);
if (ret == `sel_all || ret == `desel_all)
{
UI::OpenDialog(
`Left(`Label(
(ret == `sel_all) ?
// An informative popup label during selecting all items in the table (can consume a lot of time)
_("Selecting all items...")
:
// An informative popup label during deselecting all items in the table (can consume a lot of time)
_("Deselecting all items...")
))
);
if (ret == `sel_all)
{
Backup::unselected_files = [];
deselected_ids = $[];
}
else
{
Backup::unselected_files = [];
foreach(string pack, map info, Backup::selected_files, ``{
Backup::unselected_files = (list<string>)merge(Backup::unselected_files, info["changed_files"]:[]);
}
);
integer desel_num = size(Backup::unselected_files);
deselected_ids = $[];
while (desel_num > 0)
{
deselected_ids[desel_num] = 1;
desel_num = desel_num - 1;
}
}
UI::CloseDialog();
UI::ChangeWidget(`id(`table), `Items,
(ret == `sel_all) ?
// selecting all
(list<term>)SCR::Read(.target.ycp, (string)SCR::Read(.target.tmpdir) + "/items.ycp")
:
// deselecting all
(list<term>)SCR::Read(.target.ycp, (string)SCR::Read(.target.tmpdir) + "/items.ycp2")
);
if (current_item != nil)
{
UI::ChangeWidget(`id(`table), `CurrentItem, current_item);
}
changed = true;
}
else
{
if (ret == `table || ret == `sel_file)
{
term table_item = (term)UI::QueryWidget(`id(`table), `Item(current_item));
string current_value = (string)table_item[1]:nocheckmark;
string file_name = (string)table_item[2]:"unknown file";
string package_name = (string)table_item[3]:"unknown package";
if (current_value == checkmark)
{
current_value = nocheckmark;
// add to unselected files
if (!contains(Backup::unselected_files, file_name))
{
Backup::unselected_files = add(Backup::unselected_files, file_name);
deselected_ids[current_item] = 1;
}
// remove from selected files
map info = Backup::selected_files[package_name]:$[];
if (size(info) > 0)
{
list<string> chfiles = info["changed_files"]:[];
chfiles = filter(string fn, chfiles, ``(fn != file_name));
Backup::selected_files[package_name, "changed_files"] = chfiles;
}
y2milestone("File %1 removed from %2", file_name, package_name);
}
else
{
current_value = checkmark;
// remove from unselected
Backup::unselected_files = filter(string fn, Backup::unselected_files, ``(fn != file_name));
deselected_ids = remove(deselected_ids, current_item);
// add to selected
map info = Backup::selected_files[package_name]:$[];
list chfiles = info["changed_files"]:[];
if (!contains(chfiles, file_name))
{
chfiles = add(chfiles, file_name);
Backup::selected_files[package_name, "changed_files"] = chfiles;
}
y2milestone("File %1 added to %2", file_name, package_name);
}
// refresh table
UI::ChangeWidget(`id(`table), `Item(current_item, 0), current_value);
changed = true;
}
}
}
if (ret == `abort)
{
return ret;
}
// file selection was changed, update profile data
if (changed == true && Backup::selected_profile != nil)
{
Backup::StoreSettingsToBackupProfile(Backup::selected_profile);
}
if (ret == `back) {
return `back_from_files;
}
return ret;
}
// Variables for the ArchivingDialog() and functions beyond
string id_hostname = "Storing hostname: ";
string id_date = "Storing date: ";
string id_partab = "Storing partition table";
string id_ptstored = "Stored partition: ";
string id_ok = "Success";
string id_failed = "Failed";
string id_storing_installed_pkgs = "Storing list of installed packages";
string id_storedpkg = "Packages stored: ";
string id_ext2 = "Storing ext2 area: ";
string id_archive = "Creating archive:";
string id_tar_exit = "/Tar result:";
string id_new_volume = "/Volume created: ";
string id_not_readable = "/File not readable: ";
string id_not_stored = "Error storing partition: ";
string id_pt_read = "Partition tables info read";
string id_creating_target = "Creating target archive file...";
map selected_info = $[];
integer last_file_stage = 0;
integer this_file_stage = 0;
integer stages_for_files = 0;
define void CreateProgress_ArchivingDialog() {
// create Progress bar
// progress stage
list<string> stages1 = [ _("Store host information") ];
// progress step
list<string> stages2 = [ _("Storing host information...") ];
selected_info = Backup::MapFilesToString();
selected_files_num = selected_info["sel_files"]:0;
selected_pkg_num = selected_info["sel_packages"]:0;
// number of steps = number of selected files + nuber of selected packages (selected files added below)
// + 6 files (comment, hostname, date, files, installed_packages, packages_info)
// + 3 steps (comment, hostname, installed PRMs)
// + 3 stages (storing files, creating package archives, creating big archive)
integer num_stages = 12;
if (Backup::system) {
// progress stage
stages1 = add(stages1, _("Create system area backup"));
// progress step
stages2 = add(stages2, _("Creating system area backup..."));
if (Backup::backup_pt)
{
num_stages = num_stages + 1;
}
num_stages = num_stages + size(Backup::ext2_backup);
num_stages = num_stages + 1; // System backup stage
}
if (Backup::multi_volume) {
num_stages = num_stages + 1; // tar prints Volume label if multi volume selected, but only at first volume
}
if (Backup::do_search) {
num_stages = num_stages + 1; // add NOPACKAGE archive step
}
if (size(Backup::complete_backup) > 0) {
num_stages = num_stages + 1; // add complete list step
}
stages_for_files = num_stages * 9;
num_stages = num_stages + stages_for_files;
// last file-stage is 0%
last_file_stage = 0;
this_file_stage = 0;
// progress stage
stages1 = add(stages1, _("Create package archives"));
// progress stage
stages1 = add(stages1, _("Create target archive"));
// progress step
stages2 = add(stages2, _("Creating package archives..."));
// progress step
stages2 = add(stages2, _("Creating target archive..."));
// progress stage
stages1 = add(stages1, _("Write autoinstallation profile"));
// progress step
stages2 = add(stages1, _("Writing autoinstallation profile..."));
if (Backup::cron_mode == true) {
Progress::set(false);
} else {
y2milestone("Creating progress dialog with %1 steps", num_stages);
Progress::New(
// progress
_("Creating Archive"),
" ",
num_stages,
stages1,
stages2,
backup_help_creating_archive()
);
}
}
/**
* Display progress of creating archive
* @return symbol Symbol for wizard sequencer - pressed button
*/
define symbol ArchivingDialog() {
// Creating Progress Bar/Steps
CreateProgress_ArchivingDialog();
symbol ret = nil;
integer progress_count = selected_pkg_num + selected_files_num;
if (progress_count<1) {
progress_count = 1;
}
y2milestone("Number of selected packages: %1, selected files: %2", selected_pkg_num, selected_files_num);
file_list_stored = selected_info["file_list_stored"]:false;
string tmpfile_list = (string) SCR::Read(.target.tmpdir) + "/filelist";
// store comment to file
string tmpfile_comment = (string)SCR::Read(.target.tmpdir) + "/comment";
comment_stored = (boolean)SCR::Write(.target.string, tmpfile_comment, Backup::description);
y2debug("%1", "Comment stored to file");
Progress::NextStep();
if (!comment_stored) {
Report::Warning(sformat(_("Cannot write comment to file %1."), tmpfile_comment));
}
added_files = 0;
// prepare start
Backup::PrepareBackup();
list<string> script_out = [ ];
archived_num = 0;
boolean tar_running = false;
string id_storing_list = "Storing list";
string id_list_stored = "File list stored";
e2image_results = [];
stored_ptables = [];
created_archive_files = [];
string last_out = nil;
boolean system_stage_changed = false;
y2milestone("Creating archive...");
backup_PID = (integer) SCR::Execute(
.process.start_shell,
sformat("%1 %2", Backup::script_create_archive, Backup::get_archive_script_parameters(tmpfile_list, tmpfile_comment)),
$["C_locale":true]
);
boolean started = (backup_PID != nil && backup_PID > 0);
if (!started)
{
// error popup message
Report::Error(_("Could not start archiving script.
Aborting the backup.
"));
return `abort;
}
Backup::just_creating_archive = true;
line = "-- just -- started --";
// start while
//
// .running might return false
// but there still might be some buffer left
while ((boolean) SCR::Read (.process.running, backup_PID) || ! (boolean) SCR::Read (.process.buffer_empty, backup_PID))
{
line = (string) SCR::Read(.process.read_line, backup_PID);
if (line == nil) continue;
script_out = [line];
// script returned some lines, no check the free space
if (size(script_out)>0) {
waiting_without_output = 0;
}
while (size(script_out) > 0)
{
string line = script_out[0]:nil; // read line
script_out = remove(script_out, 0); // remove line
y2debug("Archive script output: %1", line);
if (Backup::archive_type == `txt)
{
if (line != id_storing_list)
{
if (line == id_list_stored)
{
stored_list = true;
y2debug("List of files stored");
Progress::NextStep();
}
else
{
y2warning("Unknown output from archive script: %1", line);
}
}
}
else
{
if (tar_running)
{
if (substring(line, 0, size(id_tar_exit)) == id_tar_exit)
{
tar_result = tointeger(substring(line, size(id_tar_exit)));
}
else
{
if (substring(line, 0, size(id_new_volume)) == id_new_volume)
{
string vol_name = substring(line, size(id_new_volume));
// update NFS archive name
if (Backup::target_type == `nfs)
{
y2milestone("vol_name: %1, nfsmount: %2, size(nfsmount):%3", vol_name, Backup::nfsmount, size(Backup::nfsmount));
string location = substring(vol_name, size(Backup::nfsmount));
vol_name = Backup::nfsserver + ":" + Backup::nfsexport + location;
y2debug("Volume name: %1", vol_name);
}
created_archive_files = add(created_archive_files, vol_name);
}
else if (line == id_creating_target)
{
y2debug("Creating target archive");
Progress::NextStage();
}
else if (substring(line, 0, size(id_not_readable)) == id_not_readable)
{
not_readable_files = add(not_readable_files, substring(line, size(id_not_readable)));
y2warning("File %1 can not be read.", substring(line, size(id_not_readable)));
Progress::NextStep();
}
else
{
if (Backup::multi_volume) {
if (last_out != line)
{
archived_num = archived_num + 1;
last_out = line;
}
} else {
archived_num = archived_num + 1;
y2debug("File: %1 added to archive", line);
}
this_file_stage = tointeger(archived_num * stages_for_files / progress_count);
if (this_file_stage > last_file_stage) {
Progress::NextStep();
last_file_stage = this_file_stage;
}
}
}
}
else
{
if (substring(line, 0, size(id_hostname)) == id_hostname)
{
string tmp = substring(line, size(id_hostname));
hostname_stored = (tmp == id_ok);
y2debug("Hostaneme stored: %1", hostname_stored);
Progress::NextStage();
}
else
{
if (substring(line, 0, size(id_date)) == id_date)
{
string tmp = substring(line, size(id_date));
date_stored = (tmp == id_ok);
y2debug("Date stored: %1", date_stored);
Progress::NextStep();
}
else
{
if (substring(line, 0, size(id_partab)) == id_partab)
{
if (!system_stage_changed)
{
Progress::NextStage();
system_stage_changed = true;
}
else
{
Progress::NextStep();
}
}
else
{
if (substring(line, 0, size(id_ptstored)) == id_ptstored)
{
string part_name = substring(line, size(id_ptstored));
added_files = added_files + 2;
stored_ptables = add(stored_ptables, part_name);
}
else
{
if (substring(line, 0, size(id_not_stored)) == id_not_stored)
{
y2warning("PTble was not stored: %1", line);
failed_ptables = add(failed_ptables, substring(line, size(id_not_stored)));
}
else
{
if (substring(line, 0, size(id_ext2)) == id_ext2)
{
if (!system_stage_changed)
{
Progress::NextStage();
system_stage_changed = true;
}
}
else
{
if (line == id_archive)
{
tar_running = true;
y2debug("Creating archive");
// set next stage if system backup was selected, but no partition table or ext2 image was selected
if (Backup::system && !system_stage_changed)
{
Progress::NextStage();
system_stage_changed = true;
}
Progress::NextStage();
}
else
{
if (line == id_ok || line == id_failed)
{
e2image_results = add(e2image_results, (line == id_ok));
}
else
{
if (substring(line, 0, size(id_not_readable)) == id_not_readable)
{
not_readable_files = add(not_readable_files, substring(line, size(id_not_readable)));
y2warning("File %1 can not be read.", substring(line, size(id_not_readable)));
Progress::NextStep();
}
else
{
if (line == id_pt_read)
{
read_ptables_info = true;
}
else
{
if (substring(line, 0, size(id_storing_installed_pkgs)) == id_storing_installed_pkgs)
{
y2milestone(id_storing_installed_pkgs);
}
else
{
if (substring(line, 0, size(id_storedpkg)) == id_storedpkg)
{
packages_list_stored = (substring(line, size(id_storedpkg)) == id_ok);
y2debug("Stored list of installed packages %1", packages_list_stored);
Progress::NextStep();
}
else
{
y2warning("Unknown output from archive script: %1", line);
}
}
}
}
}
}
}
}
}
}
}
}
}
}
if (Backup::cron_mode) {
ret = nil;
sleep(wait_time);
CheckFreeSpace();
} else {
ret = waitForUserOrProcess(wait_time, `changed);
CheckFreeSpace();
}
// evaluetest the free space, asks user when there not enough free space
// changes ret symbol when needed
ret = EvaluateFreeSpace(ret);
if (ret != nil)
{
// free resources when backup is finished
Backup::PostBackup();
return ret;
}
}
}
// end while
y2milestone ("DEBUG: running %1", (boolean) SCR::Read (.process.running, backup_PID));
y2milestone ("DEBUG: empty %1", (boolean) SCR::Read (.process.buffer_empty, backup_PID));
Backup::selected_files = $[];
if (ret == `abort)
{
// remove incomplete backup archive file
SCR::Execute(.target.remove, Backup::archive_name);
}
else
{
// write autoinstallation profile
Progress::NextStage();
profilewritten = Backup::WriteProfile(created_archive_files);
}
// free resources when backup is finished
Backup::PostBackup();
return `next;
}
/**
* Allow user to enter a new profile name. If the profile already exists, it allows
* to replace it. Allows to rename current profile if current_name in not nil or "".
*
* @param string current name of the profile
* @return string the name for the new profile, "" for cancel
*/
define string AskNewProfileName( string current_name ) ``{
// Translators: text of a popup dialog
string dialog_text = _("Enter a name for the new profile.");
// renaming the current profile
if (current_name != nil && current_name != "") {
// TRANSLATORS: text of a popup dialog, %1 is a profile name to be renamed
dialog_text = sformat(_("Enter a new name for the %1 profile."), current_name);
}
// double-quote in name breaks backup in several places
map new_name = ShowEditDialog(dialog_text, "", nil, ["\""]);
while( (symbol)(new_name["clicked"]:nil) == `ok && Backup::backup_profiles[ (string)(new_name["text"]:nil) ]:nil != nil )
{
// Translators: error popup, %1 is profile name
if( ! Popup::YesNo(sformat(_("A profile %1 already exists.
Replace the existing profile?
"), new_name["text"]:"")) ) {
// double-quote in name breaks backup at several places
new_name = ShowEditDialog(_("Enter a name for the new profile."), "", nil, ["\""]);
}
else
{
// yes, do replace
return new_name[ "text" ]:"";
}
}
if( (symbol)(new_name["clicked"]:nil) == `ok ) {
return (string)(new_name["text"]:"");
}
else
{
return "";
}
}
/**
* Display backup summary
* @return symbol Symbol for wizard sequencer - pressed button
*/
define symbol SummaryDialog() ``{
string br = "<BR>";
string p = "<P>";
string _p = "</P>";
string em = "<B>";
string _em = "</B>";
if (Backup::cron_mode == true)
{
br = "\n";
p = "";
_p = "\n";
em = "";
_em = "";
}
string backup_result = "";
string backup_details = "";
if (Backup::archive_type == `txt)
{
// For translators: %1 is entered archive file name (summary text)
backup_result = p + (stored_list ? sformat(_("List of files saved to file %1"), Backup::archive_name)
// part of summary text
// summary text
: (em + _("Error storing list of files") + _em)) + _p;
}
else
{
// part of summary text
backup_result = p + _("Modified Files Found: ") + modified_num + br
// part of summary text
+ _("Total Size: ") + String::FormatSize(modified_size) + _p;
if (Backup::do_search)
{
// part of summary text
backup_result = backup_result + p + _("Files Not in a Package Found: ") + nopkg_num + br
// part of summary text
+ _("Total Size: ") + String::FormatSize(nopkg_size) + _p;
}
if (Backup::display)
{
// part of summary text
backup_result = backup_result + p + _("Selected Files to Back Up: ") + selected_files_num + _p;
}
// part of summary text
backup_details = p + (hostname_stored ? _("Hostname stored successfully") :
// part of summary text
(em + _("Storing hostname failed") + _em)) + br + (date_stored ? _("Date stored successfully") :
// part of summary text
(em + _("Storing date failed") + _em)) + br + (file_list_stored ? _("File list stored successfully") :
// part of summary text
(em + _("Storing file list failed") + _em)) + br + (comment_stored ? _("Comment stored successfully") :
// part of summary text
(em + _("Storing comment failed") + _em)) + br
// part of summary text
+ (packages_list_stored ? _("List of installed packages stored successfully") :
// part of summary text
(em + _("Storing list of installed packages failed") + _em)) + _p;
if (size(not_readable_files) > 0 || !hostname_stored || !date_stored || !file_list_stored || !comment_stored || !packages_list_stored)
{
// part of summary text, 'Details' is button label
backup_result = backup_result + p + _("Some errors occurred during backup. Press Details for more information.") + _p;
}
if (size(not_readable_files) > 0)
{
// part of summary text
backup_details = backup_details + br + p + em + _("Errors Creating Archive:") + _em + br;
foreach(string f, not_readable_files,
``{
// For translators: %1 file name - part of summary text
backup_details = backup_details + em + sformat(_("Cannot read file %1"), f) + _em + br;
}
);
backup_details = backup_details + _p;
}
if (Backup::system)
{
y2debug("Ext2 backup: %1", Backup::ext2_backup);
y2debug("Ext2 results: %1", e2image_results);
if (Backup::backup_pt)
{
if (!read_ptables_info)
{
// part of summary text
backup_details = backup_details + p + em + _("Detecting disk partitions failed") + _em + br;
}
foreach(string failed_pt, failed_ptables,
``{
// For translators: %1 is device name of disk, e.g. hda - part of summary text
backup_details = backup_details + em + sformat(_("Storing partition table of disk /dev/%1 failed"), failed_pt) + _em + br;
}
);
foreach(string stored_pt, stored_ptables,
``{
// For translators: %1 is device name of disk, e.g. hda - part of summary text
backup_details = backup_details + sformat(_("Storing partition table of disk /dev/%1 was successful"), stored_pt) + br;
}
);
}
if (size(Backup::ext2_backup) > 0)
{
integer index = 0;
foreach(boolean r, e2image_results,
``{
term tmp = (term) Backup::ext2_backup[index]:nil;
tmp = (term) tmp[0]:nil;
string partition_name = (string) tmp[0]:"";
if (r)
{
backup_details = backup_details
// For translators: %1 is partition name e.g. /dev/hda1 - part of summary text
+ sformat(_("Ext2 image of %1 stored successfully"), partition_name) + br;
added_files = added_files + 1;
}
else
{
backup_details = backup_details + em
// For translators: %1 is partition name e.g. /dev/hda1 - part of summary text
+ sformat(_("Storing ext2 image of %1 failed"), partition_name) + _em + br;
}
index = index + 1;
}
);
}
backup_details = backup_details + _p;
}
if (file_list_stored)
{
added_files = added_files + 1;
}
if (hostname_stored)
{
added_files = added_files + 1;
}
if (date_stored)
{
added_files = added_files + 1;
}
if (comment_stored)
{
added_files = added_files + 1;
}
if (packages_list_stored)
{
added_files = added_files +1;
}
y2debug("selected_files_num: %1", selected_files_num);
y2debug("added_files: %1", added_files);
y2debug("total_files: %1", total_files);
backup_result = backup_result + _p
// part of summary text
+ br + ((selected_files_num + added_files < total_files) ? em + _("Warning: Some files were not backed up") + _em + br : "");
string archive_created = "";
if (!Backup::multi_volume)
{
string archname = (Backup::target_type == `file) ? Backup::archive_name : NFSfile(Backup::nfsserver, Backup::nfsexport, Backup::archive_name);
// For translators: %1 is entered archive file name - part of summary text
archive_created = sformat(_("Archive %1 created successfully"), archname);
}
else
{
// part of summary text
archive_created = _("Archive created successfully");
}
// part of summary text - %1 is file name
string profilesummary = (((boolean)(profilewritten["result"]:false)) == true) ? sformat(_("Autoinstallation profile saved to file %1."), (string)(profilewritten["profile"]:"")) : (em + _("Autoinstallation profile was not saved.") + _em);
backup_result = backup_result
// part of summary text
+ ((tar_result == 0) ? archive_created : (em + _("Archive creation failed") + _em))
+ br + profilesummary + br
+ _p;
// part of summary text
backup_details = backup_details + p + _("Total Archived Files: ") + archived_num + _p;
if (Backup::multi_volume)
{
if (size(created_archive_files) > 0)
{
// part of summary text
backup_details = backup_details + p + em + "Created archive volumes:" + _em + br;
foreach(string f, created_archive_files,
``{
backup_details = backup_details + f + br;
}
);
backup_details = backup_details + _p;
}
}
}
term buttons = `HBox(
`PushButton(`id(`details), `opt(`key_F2), _("&Details..."))
);
if( Backup::selected_profile == nil )
{
buttons = `HBox(
`PushButton(`id(`details), `opt(`key_F2), _("&Details...")),
`PushButton(`id(`profile), _("&Save as Profile..."))
);
}
// cron mode
if (Backup::cron_mode == true)
{
if (Backup::mail_summary == true)
{
SendSummary(Backup::remove_result, Backup::selected_profile, backup_result, backup_details);
}
return `finish;
}
// dialog header
Wizard::SetContents( _("Backup Summary"),
`VBox(
`VSpacing(0.5),
`RichText(backup_result),
`VSpacing(0.5),
// push button label
buttons,
`VSpacing(1.0)
),
backup_help_summary(), true, true
);
if (Backup::archive_type == `txt)
{
UI::ChangeWidget(`id(`details), `Enabled, false);
}
Wizard::SetNextButton(`finish, Label::OKButton() );
UI::SetFocus (`id (`finish));
symbol ret = (symbol)UI::UserInput();
while (ret != `finish && ret != `back)
{
if (ret == `details)
{
// popup dialog header
Popup::LongText(_("Backup Summary Details"), `RichText(backup_details), 70, 15);
}
else if (ret == `abort || ret == `cancel)
{
ret = `abort;
if (AbortConfirmation(`changed))
{
break;
}
else
{
ret = nil;
}
}
else if( ret == `profile )
{
string new_name = AskNewProfileName( nil );
//if no cancel, store
if( new_name != "" )
{
Backup::StoreSettingsToBackupProfile( new_name );
}
}
ret = (symbol)UI::UserInput();
}
Wizard::RestoreNextButton();
return ret;
}
void RedrawScriptsTable (integer selected_item) {
integer counter = -1;
list <term> items = maplist (map one_item, Backup::backup_helper_scripts, {
counter = counter + 1;
// before
return `item (
`id (counter),
(one_item["type"]:"before" == "before" ?
// Script type
_("Run before backup")
:
// Script type
_("Run after backup")
),
one_item["path"]:""
);
});
UI::ChangeWidget (`id ("table_of_scripts"), `Items, items);
if (size (Backup::backup_helper_scripts) > selected_item) {
UI::ChangeWidget (`id ("table_of_scripts"), `CurrentItem, selected_item);
}
boolean button_state = (size (items) > 0);
UI::ChangeWidget (`id (`delete_script), `Enabled, button_state);
UI::ChangeWidget (`id (`edit_script), `Enabled, button_state);
}
void InitScriptContent () {
string file = (string) UI::QueryWidget (`id ("script_name"), `Value);
// the default content is taken from UI (might be already defined)
string script_content = (string) UI::QueryWidget (`id ("script_content"), `Value);
// if a file exists, the content is taken from there
if (FileUtils::Exists (file)) {
script_content = (string) SCR::Read (.target.string, file);
if (script_content == nil) {
y2error ("Cannot read file %1", file);
script_content = "";
}
} else {
y2warning ("File %1 does not exist (yet)", file);
}
// adjust UI
UI::ChangeWidget (`id ("script_content"), `Value, script_content);
}
boolean StoreScriptContent (string filename) {
string script_content = (string) UI::QueryWidget (`id ("script_content"), `Value);
if (! FileUtils::Exists (filename)) {
integer pos = findlastof (filename, "/");
if (pos == nil) {
UI::SetFocus (`id ("script_content"));
Report::Error (_("Invalid file name."));
return false;
}
string directory = substring (filename, 0, pos + 1);
map cmd = (map) SCR::Execute (
.target.bash_output,
sformat ("mkdir -pv '%1' && touch '%2' && chmod 0700 '%2'", directory, filename)
);
if (cmd["exit"]:-1 != 0) {
Report::Error (sformat (_("Cannot create file %1.
Details: %2"), filename, cmd["stderr"]:""));
return false;
}
}
if ((boolean) SCR::Write (.target.string, filename, script_content) != true) {
Report::Error (sformat (_("Cannot write to %1 file."), filename));
return false;
}
return true;
}
boolean AddEditScriptDialog (symbol ret) {
integer current_item = -1;
boolean editing = false;
string default_run_before_backup = "";
string default_run_after_backup = "";
// edit
if (ret == `edit_script) {
current_item = (integer) UI::QueryWidget (`id ("table_of_scripts"), `CurrentItem);
editing = true;
// add
} else {
if ((boolean) SCR::Execute (.target.mkdir, Backup::backup_scripts_dir) != true) {
y2error ("Cannot create %1", Backup::backup_scripts_dir);
}
}
// >>> before backup - proposes a unique script name
map cmd = (map) SCR::Execute (
.target.bash_output,
sformat ("mktemp --dry-run -p '%1' -t run_before_backup.XXXXXXXXXXXXXXXX", String::Quote (Backup::backup_scripts_dir))
);
if (cmd["exit"]:-1 == 0) {
default_run_before_backup = splitstring (cmd["stdout"]:"", "\n")[0]:"";
} else {
y2warning ("Cannot propose a script name: %1", cmd);
}
// <<<
// >>> after backup - proposes a unique script name
cmd = (map) SCR::Execute (
.target.bash_output,
sformat ("mktemp --dry-run -p '%1' -t run_after_backup.XXXXXXXXXXXXXXXX", String::Quote (Backup::backup_scripts_dir))
);
if (cmd["exit"]:-1 == 0) {
default_run_after_backup = splitstring (cmd["stdout"]:"", "\n")[0]:"";
} else {
y2warning ("Cannot propose a script name: %1", cmd);
}
// <<<
map this_entry = Backup::backup_helper_scripts[current_item]:$[];
UI::OpenDialog (
`VBox (
`MarginBox (1, 0,
`Left (`Heading ((editing == true ?
_("Edit Backup Helper Script Options")
:
_("Add Backup Helper Script")
)))
),
`MarginBox (1, 1, `VBox (
`Frame (_("Script Type"), `RadioButtonGroup (
`id ("script_type"),
`VBox (
`Left (`RadioButton (`id ("before"), `opt (`notify), _("&Before Backup"))),
`Left (`RadioButton (`id ("after"), `opt (`notify), _("&After Backup")))
)
)),
`VSpacing (0.5),
`VSquash (`HBox (
`Bottom (`ComboBox (`id ("script_name"), `opt (`editable, `hstretch, `notify), _("Script &Path"), [])),
`Bottom (`PushButton (`id ("browse_script_name"), Label::BrowseButton()))
)),
`VSpacing (0.5),
`MinSize (80, 12, `MultiLineEdit (`id ("script_content"), _("Script &Content"), ""))
)),
`ButtonBox (
`PushButton (`id (`ok), Label::OKButton()),
`PushButton (`id (`cancel), Label::CancelButton())
)
)
);
string current_script_type = "";
string default_script_path = "";
// Adjusting the defaults
if (this_entry["type"]:"before" == "before") {
current_script_type = "before";
default_script_path = default_run_before_backup;
} else {
current_script_type = "after";
default_script_path = default_run_after_backup;
}
string initial_script_path = this_entry["path"]:default_script_path;
// Adjusting UI
UI::ChangeWidget (`id ("script_type"), `CurrentButton, current_script_type);
UI::ChangeWidget (`id ("script_name"), `Items, toset ([this_entry["path"]:default_script_path, default_script_path]));
UI::ChangeWidget (`id ("script_name"), `Value, initial_script_path);
InitScriptContent();
any ret = nil;
boolean dialog_ret = false;
boolean path_changed_by_user = false;
while (true) {
ret = UI::UserInput();
if (ret == `ok) {
this_entry["path"] = UI::QueryWidget (`id ("script_name"), `Value);
this_entry["type"] = UI::QueryWidget (`id ("script_type"), `CurrentButton);
if (this_entry["path"]:"" == "") {
UI::SetFocus (`id ("script_name"));
Report::Error (_("Script file name must be set."));
continue;
}
if (!StoreScriptContent (this_entry["path"]:""))
continue;
// a new entry
if (current_item == -1)
current_item = size (Backup::backup_helper_scripts);
Backup::backup_helper_scripts[current_item] = this_entry;
dialog_ret = true;
break;
} else if (ret == `cancel) {
break;
// Switching the script type
} else if (ret == "after" || ret == "before") {
if (ret == "before") {
current_script_type = "before";
default_script_path = default_run_before_backup;
} else {
current_script_type = "after";
default_script_path = default_run_after_backup;
}
if (path_changed_by_user != true) {
UI::ChangeWidget (`id ("script_name"), `Value, default_script_path);
}
InitScriptContent();
// Changed the script name
} else if (ret == "script_name") {
path_changed_by_user = true;
InitScriptContent();
// User pressed the [Browse] button
} else if (ret == "browse_script_name") {
string file = UI::AskForExistingFile (Backup::backup_scripts_dir, "*", _("Choose a Script File Name"));
if (file == nil) continue;
UI::ChangeWidget (`id ("script_name"), `Value, file);
InitScriptContent();
path_changed_by_user = true;
} else {
y2error ("Ret %1 not handled", ret);
}
}
UI::CloseDialog();
return dialog_ret;
}
/**
* Display dialog with expert options (e.g. system area backup,
* temporary location...)
* @return symbol user input - widget ID
*/
define symbol ExpertOptionsDialog() ``{
// dialog header
Wizard::SetContents( _("Expert Backup Options"),
`VBox (
`VSpacing(0.5),
// check box label
`CheckBoxFrame (
`id(`system), `opt(`notify), _("Back Up &Hard Disk System Areas"), Backup::system,
`VBox (
`HBox (
// text entry label
`InputField (`id (`tmp), `opt(`hstretch), _("Temporary &Location of Archive Parts"), Backup::tmp_dir),
`HStretch(),
`PushButton(`id(`set_system), _("&Options..."))
)
)
),
`VSpacing(1),
`Left(`Label (_("Pre and Post-Backup Scripts"))),
`Table (
`id ("table_of_scripts"),
`header (
// a header item
_("Script Type"),
// a header item
_("Path")
),
[]
),
`Left (`HBox (
`PushButton (`id (`add_script), `opt(`key_F3), _("&Add...")),
`PushButton (`id (`edit_script), `opt(`key_F4), _("&Edit...")),
`PushButton (`id (`delete_script), `opt(`key_F5), Label::DeleteButton())
)),
`VStretch()
),
expert_options_help(), true, true
);
RedrawScriptsTable (0);
// enable/disable widges
if (Backup::archive_type == `txt)
{
UI::ChangeWidget(`id(`system), `Enabled, false);
UI::ChangeWidget(`id(`set_system), `Enabled, false);
}
else
{
UI::ChangeWidget(`id(`set_system), `Enabled, Backup::system);
}
Wizard::SetNextButton(`finish, Label::OKButton() );
UI::SetFocus (`id (`finish));
symbol ret = nil;
integer current_item = nil;
while (ret != `finish && ret != `back && ret != `abort && ret != `set_system)
{
ret = (symbol)UI::UserInput();
current_item = (integer) UI::QueryWidget (`id ("table_of_scripts"), `CurrentItem);
if (ret == `finish || ret == `set_system) {
Backup::tmp_dir = (string)UI::QueryWidget(`id(`tmp), `Value);
Backup::system = (boolean)UI::QueryWidget(`id(`system), `Value);
} else if (ret == `system) {
UI::ChangeWidget(`id(`set_system), `Enabled, (boolean)UI::QueryWidget(`id(`system), `Value));
} else if (ret == `add_script || ret == `edit_script) {
if (AddEditScriptDialog (ret) == true)
// Redraw table and select either the new or the edited script
RedrawScriptsTable (ret == `add_script ? size(Backup::backup_helper_scripts)-1 : current_item);
} else if (ret == `delete_script) {
if (Confirm::DeleteSelected () != true) continue;
Backup::backup_helper_scripts[current_item] = nil;
Backup::backup_helper_scripts = filter (map one_item, Backup::backup_helper_scripts, {
return one_item != nil;
});
RedrawScriptsTable (0);
}
}
Wizard::RestoreNextButton();
return ret;
}
/**
* Displays a popup dialog asking for new name for the current profile.
* If nil returned, no redraw is needed, no changes are done.
*
* @param string current profile
* @param string name to be selected
*/
string RenameProfilePupupDialog(string current_name) {
Backup::RestoreSettingsFromBackupProfile( current_name );
string new_name = AskNewProfileName( current_name );
// no changes, the same name
if (current_name == new_name) {
return nil;
}
// if not cancelled
if (new_name != "" && new_name != nil) {
//map current_profile = (map)eval(Backup::backup_profiles[Backup::selected_profile]:$[]);
//map current_settings = (map)eval(current_profile[`cron_settings]:$[]);
Backup::StoreSettingsToBackupProfile( new_name );
// "Renaming", Cron settings, It seems to be a hack }8->
Backup::backup_profiles[new_name, `cron_settings]
= Backup::backup_profiles[current_name, `cron_settings]:$[];
// Setting that cron settings were changed
Backup::backup_profiles[new_name, `cron_settings, "cron_changed"] = true;
Backup::backup_profiles[current_name, `cron_settings, "cron_changed"] = true;
// Remove the backup profile, but do not remove cron file
Backup::RemoveBackupProfile( current_name, false );
return new_name;
}
return nil;
}
/**
* Dialog for selection of a profile before backup
* @return symbol Symbol for wizard sequencer - pressed button
*/
define symbol SelectProfileDialog() ``{
Wizard::SetNextButton(`finish, Label::OKButton() );
Wizard::SetAbortButton (`abort, Label::CancelButton());
Wizard::HideBackButton();
string tableheader1 = _("Name");
string tableheader2 = _("Description");
string tableheader3 = _("Automatic Backup");
Wizard::SetContents( _("System Backup"),
`VBox(
`Left(`Label(_("Available Profiles") )),
`ReplacePoint(`id(`selectionBox),
`Table(`id(`profile), `header(tableheader1, tableheader2, tableheader3), Backup::BackupProfileDescriptions() )
),
`HBox(
`PushButton(`id(`start), _("&Create Backup")),
`MenuButton(_("Profile Mana&gement"),
[
`item(`id(`add), Label::AddButton()+"..."),
`item(`id(`clone), _("&Duplicate...")),
`item(`id(`edit), Label::EditButton()+"..."),
// TRANSLATORS: Menu button selection
`item(`id(`rename), _("Re&name")+"..."),
`item(`id(`delete), Label::DeleteButton()),
`item(`id(`cron), tableheader3 + "...")
])
),
`PushButton(`id(`manual),_("Back Up &Manually...")),
`VSpacing(1.5)
),
profile_help(), false, true
);
string sel_prof = (Backup::selected_profile == nil) ? (string) Backup::BackupProfileNames()[0]:nil : Backup::selected_profile;
if (sel_prof != nil)
{
// select the first profile in the list
UI::ChangeWidget( `id(`profile), `CurrentItem, sel_prof);
}
symbol ret = nil;
Backup::no_interactive = false;
do {
// select the first profile in the list
UI::ChangeWidget( `id(`start), `Enabled, (size( Backup::backup_profiles ) != 0) );
ret = (symbol)UI::UserInput();
// load corresponding settings
if( size( Backup::backup_profiles ) == 0 )
{
Backup::RestoreDefaultSettings();
}
else
{
Backup::RestoreSettingsFromBackupProfile( (string)UI::QueryWidget(`id(`profile), `CurrentItem ) );
}
if( ret == `start )
{
Backup::no_interactive = true;
}
else if( ret == `clone || ret == `add )
{
// add a new profile with the current/default settings
if( ret == `add )
{
// restore default settings, don't care about selected profile
Backup::RestoreDefaultSettings();
}
string new_name = AskNewProfileName( nil );
// if the user didn't choose cancel
if( new_name != "" )
{
Backup::StoreSettingsToBackupProfile( new_name );
UI::ReplaceWidget( `id(`selectionBox),
`Table(`id(`profile), `header(tableheader1, tableheader2, tableheader3), Backup::BackupProfileDescriptions() )
);
// select the profile in the list
UI::ChangeWidget( `id(`profile), `CurrentItem, new_name );
if (ret == `add)
{
// start config. workflow for the new profile
ret = `edit;
Backup::RestoreSettingsFromBackupProfile((string)UI::QueryWidget(`id(`profile), `CurrentItem ) );
// adding new profile
Backup::profile_is_new_one = true;
}
}
}
else if( ret == `rename ) {
string current_name = (string) UI::QueryWidget(`id(`profile), `CurrentItem );
current_name = RenameProfilePupupDialog(current_name);
// redraw if any changes are done
if (current_name != nil) {
UI::ReplaceWidget( `id(`selectionBox),
`Table(`id(`profile), `header(tableheader1, tableheader2, tableheader3), Backup::BackupProfileDescriptions() ));
UI::ChangeWidget( `id(`profile), `CurrentItem, current_name );
}
}
else if( ret == `delete )
{
// remove the selected profile
if ( Popup::YesNo ( _("Remove the selected profile?") ) )
{
// remove backup profile also with cron file
Backup::RemoveBackupProfile( (string)UI::QueryWidget(`id(`profile), `CurrentItem ), true );
UI::ReplaceWidget( `id(`selectionBox),
`Table(`id(`profile), `header(tableheader1, tableheader2, tableheader3), Backup::BackupProfileDescriptions() ));
// select the first profile in the list
UI::ChangeWidget( `id(`profile), `CurrentItem, (string) Backup::BackupProfileNames()[0]:nil );
}
}
else if( ret == `manual )
{
// load defaults, clear selected_profile
Backup::RestoreDefaultSettings();
Backup::selected_profile = nil;
}
else if (ret == `cron)
{
string selprofile = (string)UI::QueryWidget(`id(`profile), `CurrentItem);
if (selprofile != nil)
{
Backup::selected_profile = selprofile;
}
else
{
// no selected profile, wait for next UserInput
ret = nil;
}
}
else if (ret == `cancel)
{
ret = `abort;
}
} while( ret != `finish && ret != `manual && ret != `start && ret != `edit && ret != `cron && ret != `abort);
// save profiles before quit and
// before starting backup
// (modifications would be lost when backup is aborted
if (ret == `finish || ret == `start || ret == `manual)
{
y2milestone(" *** Storing profiles ***");
Backup::WriteBackupProfiles();
}
Wizard::RestoreNextButton ();
Wizard::RestoreBackButton ();
y2debug("SelectProfileDialog result: %1", ret);
return ret;
}
/**
* Choose the next step - start searching or return to the profile dialog.
* @return symbol Symbol for wizard sequencer - `next for searching, `next2 for return to profile dialog
*/
define symbol PrepareSearching() ``{
// if the user does not use profile, start searching
if( Backup::selected_profile == nil || Backup::no_interactive)
{
return `next;
}
// there is a profile, store the data and return to profile dialog
else
{
Backup::StoreSettingsToBackupProfile( Backup::selected_profile );
return `ok;
}
}
/**
* Directory selection dialog
* @param label dialog label
* @param dir start directory
* @return map result $[ "input" : symbol (user input, `ok or `cancel), "dir" : string (selected directory) ];
*/
define map DirPopup(string label, string dir) ``{
UI::OpenDialog(
`VBox(
`VSpacing(0.5),
`HBox(
`HSpacing(1.0),
`InputField (`id(`dir), `opt (`vstretch), label, dir),
`HSpacing(1),
`VBox(
`Label(""),
`PushButton(`id(`browse), _("&Browse..."))
),
`HSpacing(1.0)
),
`VSpacing(1.0),
`HBox(
`PushButton(`id(`ok), Label::OKButton()),
`PushButton(`id(`cancel), Label::CancelButton())
),
`VSpacing(0.3)
)
);
UI::SetFocus(`id(`ok));
symbol input = (symbol)UI::UserInput();
while (input != `ok && input != `cancel)
{
if (input == `browse)
{
// title in the file selection dialog
string new = UI::AskForExistingDirectory(dir, _("Directory Selection"));
if (size(new) > 0)
{
UI::ChangeWidget(`id(`dir), `Value, new);
}
}
input = (symbol)UI::UserInput();
}
string newval = (string)UI::QueryWidget(`id(`dir), `Value);
UI::CloseDialog();
return $[ "input" : input, "dir" : newval ];
}
/**
* Convert size in bytes to B, kiB, kB, MiB or MB
* @param sz size in bytes
* @return map map $[ "string" : string (textual representation), "size" : integer (value), "unit" : symbol (unit of the value) ]
*/
define map size2map(integer sz) ``{
string ret = "";
symbol ret_unit = `B;
map<symbol,string> unit_descr = $[];
if (sz == nil)
{
return nil;
}
foreach(map m, Backup::units_description,
``{
symbol s = (symbol)(m["symbol"]:nil);
string label = (string)(m["label"]:nil);
if (s != nil && size(label) > 0)
{
unit_descr[s] = label;
}
}
);
if (sz % 1024 == 0)
{
// size in kiB
sz = sz / 1024;
if (sz % 1024 == 0)
{
// size in MiB
sz = sz / 1024;
// return MiB
ret = sformat("%1", sz) + " " + unit_descr[`MiB]:"";
ret_unit = `MiB;
}
else
{
// return kiB
ret = sformat("%1", sz) + " " + unit_descr[`kiB]:"";
ret_unit = `kiB;
}
}
else if (sz % 1000 == 0)
{
// size in kB
sz = sz / 1000;
if (sz % 1000 == 0)
{
// size in MB
sz = sz / 1000;
// return MB
ret = sformat("%1", sz) + " " + unit_descr[`MB]:"";
ret_unit = `MB;
}
else
{
// return kB
ret = sformat("%1", sz) + " " + unit_descr[`kB]:"";
ret_unit = `kB;
}
}
else
{
ret = sformat("%1", sz) + " " + unit_descr[`B]:"";
ret_unit = `B;
}
return $[ "string" : ret, "size" : sz, "unit" : ret_unit ];
}
/**
* Convert size description to string
* @param s volume size
* @param user_size user defined size
* @return string result
*/
define string symbol2string(symbol s, integer user_size) ``{
y2milestone("s: %1, user_size: %2", s, user_size);
string ret = "";
if (s == nil)
{
return ret;
}
if (s == `user)
{
map tmp = size2map(user_size);
return (string)(tmp["string"]:"");
}
foreach(map m, Backup::media_descriptions,
``{
symbol sym = (symbol)(m["symbol"]:nil);
integer cap = (integer)(m["capacity"]:nil);
string label = (string)(m["label"]:"");
y2milestone("symbol: %1", m["symbol"]:nil);
if (sym == s)
{
ret = label;
if (cap > 0)
{
map tmp = size2map(cap);
ret = ret + sformat(" (%1)", tmp["string"]:"");
}
}
}
);
return ret;
}
/**
* Convert list of strings to list of items
* @param start_id identification of the first item
* @param type description of item
* @param input input
* @return list<term> result
*/
define list<term> list2items(integer start_id, string type, list<string> input) ``{
list<term> ret = [];
integer i = start_id;
if (size(input) > 0)
{
foreach(string itm, input, ``{
ret = add(ret, `item(`id(i), itm, type));
i = i + 1;
}
);
}
return ret;
}
void RedrawIncludeTable () {
list <term> items = [];
// If no include_dir is defined, use the entire root "/"
if (Backup::include_dirs == nil || Backup::include_dirs == []) {
Backup::include_dirs = [Backup::default_include_dir];
}
boolean enable_include_table = true;
foreach (string include_dir, Backup::include_dirs, {
// The entire file system is included
if (include_dir == Backup::default_include_dir) {
// TRANSLATORS: informative text in the multiline select-box
items = [`item(`id(include_dir), _("Entire file system will be searched"))];
enable_include_table = false;
break;
// Other directories are included
} else {
items = add (items, `item(`id(include_dir), include_dir));
}
});
if (items != [] && items != nil && enable_include_table) {
UI::ChangeWidget(`id(`include), `Items, items);
UI::ChangeWidget(`id(`edit_include), `Enabled, true);
UI::ChangeWidget(`id(`delete_include), `Enabled, true);
} else {
UI::ChangeWidget(`id(`include), `Items, items);
UI::ChangeWidget(`id(`edit_include), `Enabled, false);
UI::ChangeWidget(`id(`delete_include), `Enabled, false);
}
}
/**
* Redraws contstraints table content -
* directories, regular expressions and
* file systems to exclude
*/
void RedrawConstraintsTable() {
list items = [];
items = merge(items, list2items (size (items), directory_text, Backup::dir_list));
items = merge(items, list2items (size (items), regexp_text, Backup::regexp_list));
items = merge(items, list2items (size (items), filesystem_text, Backup::fs_exclude));
if (items != [] && items != nil) {
UI::ChangeWidget(`id(`const), `Items, items);
UI::ChangeWidget(`id(`edit), `Enabled, true);
UI::ChangeWidget(`id(`delete), `Enabled, true);
} else {
UI::ChangeWidget(`id(`const), `Items, []);
UI::ChangeWidget(`id(`edit), `Enabled, false);
UI::ChangeWidget(`id(`delete), `Enabled, false);
}
}
/**
* Initialize the "Search Constraints" dialog
*/
define symbol InitConstraintDialog () {
// initialize excluded directories
if (Backup::dir_list == nil)
{
Backup::dir_list = [];
}
// initialize excluded filesystems
if (Backup::detected_fs == nil)
{
// busy message
Wizard::SetContents("", `Label(_("Detecting file system types...")), "", false, false);
Backup::detected_fs = GetMountedFilesystems();
Backup::ExcludeNodevFS();
}
// do not allow manual changes of configuration
if (Backup::no_interactive)
{
return `next;
}
// dialog header
Wizard::SetContents( _("Search Constraints"),
`VBox(
`VSquash(
`MinHeight(6,
// selection box
`SelectionBox(`id(`include), `opt(`shrinkable), _("&Directories Included in Search"), [])
)
),
`HBox(
// push button label
`PushButton(`id(`add_include), _("&Add...")),
// push button label
`PushButton(`id(`edit_include), _("&Edit...")),
// push button label
`PushButton(`id(`delete_include), _("De&lete"))
),
`VSpacing(1.0),
// table label
`Left(`Label(_("Items Excluded from Search"))),
// table header
`Table(`id(`const), `opt(`vstretch), `header(_("Value"), _("Type")), []),
`Left(`HBox(
// push button label
`MenuButton(`id(`add_menu), _("A&dd"), [
`item(`id(`dir), _("&Directory...")),
`item(`id(`fs), _("&File System...")),
`item(`id(`regexp), _("&Regular Expression..."))
]
),
// push button label
`PushButton(`id(`edit), `opt(`key_F4), _("&Edit...")),
// push button label
`PushButton(`id(`delete), `opt(`key_F5), _("De&lete"))
))
),
backup_help_constraints(), true, true
);
RedrawConstraintsTable();
RedrawIncludeTable();
if (Backup::selected_profile != nil) {
// replace 'Next' button with 'OK' if profile is configured
Wizard::SetNextButton(`next, Label::OKButton() );
UI::SetFocus (`id (`next));
}
return nil;
}
void AddExcludeItem_Regexp () {
// textentry label
map result = ShowEditDialog(_("&Add New Expression"), "", nil, []);
if (result["clicked"]:nil == `ok) {
string new_regexp = (string) (result["text"]:nil);
// add item only if it's not empty and it isn't already in list
if (new_regexp != "" && new_regexp != nil) {
if (contains(Backup::regexp_list, new_regexp)) {
// error poup message - %1 is an entered regular expression
Popup::Error(sformat(_("Expression %1 is already in the list."), new_regexp));
} else {
Backup::regexp_list = add(Backup::regexp_list, new_regexp);
RedrawConstraintsTable();
}
}
}
}
void AddExcludeItem_Dir () {
// textentry label
map result = ShowEditDialog(_("&Add New Directory"), "", nil, []);
if (result["clicked"]:nil == `ok) {
string new = (string)(result["text"]:nil);
// add item only if it's not empty and it isn't already in list
if (new != "" && new != nil) {
if (contains(Backup::dir_list, new)) {
// error poup message - %1 is a directory name
Popup::Error(sformat(_("Directory %1 is already in the list."), new));
} else {
Backup::dir_list = add(Backup::dir_list, new);
RedrawConstraintsTable();
}
}
}
}
void AddExcludeItem_Fs (list <string> & all_fss) {
// combobox label
map result = ShowEditDialog(_("&Add New File System"), "", all_fss, []);
if (result["clicked"]:nil == `ok) {
string new = (string)(result["text"]:nil);
// add item only if it's not empty and it isn't already in list
if (new != "" && new != nil) {
if (contains(Backup::fs_exclude, new)) {
// error poup message - %1 is a directory name
Popup::Error(sformat(_("File system %1 is already in the list."), new));
} else {
Backup::fs_exclude = add(Backup::fs_exclude, new);
RedrawConstraintsTable();
}
}
}
}
void ExcludeItemEdit (string value, string type, list <string> & all_fss) {
// textentry label
map result = ShowEditDialog(Label::EditButton(), value, (type == filesystem_text) ? all_fss : nil, []);
if (result["clicked"]:nil == `ok) {
string new_txt = (string)(result["text"]:nil);
if (new_txt != nil && new_txt != value) {
if (type == regexp_text) {
if (contains(Backup::regexp_list, new_txt)) {
// error popup message
Popup::Error(sformat(_("Expression %1 is already in the list."), new_txt));
} else {
// refresh regexp_list content
Backup::regexp_list = maplist(string i, Backup::regexp_list, ``((i == value) ? new_txt : i));
}
} else if (type == directory_text) {
if (contains(Backup::dir_list, new_txt)) {
// error popup message
Popup::Error(sformat(_("Directory %1 is already in the list."), new_txt));
} else {
// refresh regexp_list content
Backup::dir_list = maplist(string i, Backup::dir_list, ``((i == value) ? new_txt : i));
}
} else if (type == filesystem_text) {
if (contains(Backup::fs_exclude, new_txt)) {
// error popup message
Popup::Error(sformat(_("File system %1 is already in the list."), new_txt));
} else {
// refresh regexp_list content
Backup::fs_exclude = maplist(string i, Backup::fs_exclude, ``((i == value) ? new_txt : i));
}
}
// refresh table content
RedrawConstraintsTable();
}
}
}
void ExcludeItemDelete (string value, string type) {
// remove selected constraint
if (type == regexp_text) {
Backup::regexp_list = filter (string i, Backup::regexp_list, ``(i != value));
} else if (type == directory_text) {
Backup::dir_list = filter (string i, Backup::dir_list, ``(i != value));
} else if (type == filesystem_text) {
Backup::fs_exclude = filter (string i, Backup::fs_exclude, ``(i != value));
}
// refresh table content
RedrawConstraintsTable();
}
// Managing Include dirs -->
void AddIncludeItemNow (string new_dir) {
// Removing "/" if present
if (contains (Backup::include_dirs, Backup::default_include_dir)) {
Backup::include_dirs = filter (string one_dir, Backup::include_dirs, {
return one_dir != Backup::default_include_dir;
});
}
// Removing the last slash
if (regexpmatch(new_dir, "^.+/$")) {
new_dir = regexpsub(new_dir, "^(.+)/$", "\\1");
}
if (new_dir == Backup::default_include_dir) {
y2milestone("Selecting the whole fs '/'");
Backup::include_dirs = [Backup::default_include_dir];
} else {
// Adding new directory
Backup::include_dirs = toset (add (Backup::include_dirs, new_dir));
}
}
void AddIncludeItem () {
while (true) {
// return $[ "text" : text, "clicked" : input ];
map <string, any> new_dir = ShowEditBrowseDialog(_("&Add New Directory"), "");
if (new_dir["clicked"]:nil == `ok && new_dir["text"]:"" != "" && new_dir["text"]:nil != nil) {
// bnc #395835
if (Mode::normal() && ! FileUtils::Exists (new_dir["text"]:"") && (! Popup::AnyQuestion (
_("Warning"),
sformat (_("Directory %1 does not exist.
Use it anyway?"), new_dir["text"]:""),
_("Yes, Use It"),
Label::NoButton(),
`focus_yes
))) {
continue;
}
AddIncludeItemNow(new_dir["text"]:"");
RedrawIncludeTable();
}
break;
}
}
void DeleteIncludeItem (string delete_dir) {
if (Confirm::DeleteSelected() && contains(Backup::include_dirs, delete_dir)) {
Backup::include_dirs = filter (string one_dir, Backup::include_dirs, {
return one_dir != delete_dir;
});
RedrawIncludeTable();
}
}
void EditIncludeItem (string old_item) {
map <string, any> new_dir = ShowEditBrowseDialog(_("&Edit Included Directory"), old_item);
if (new_dir["clicked"]:nil == `ok && new_dir["text"]:"" != "" && new_dir["text"]:nil != nil) {
// item changed
if (new_dir["text"]:"" != old_item) {
DeleteIncludeItem (old_item);
AddIncludeItemNow(new_dir["text"]:"");
RedrawIncludeTable();
}
}
}
// <-- Managing Include dirs
/**
* Dialog for setting excluded directories, file systems and reg. expressions
* @return symbol Symbol for wizard sequencer - pressed button
*/
define symbol ConstraintDialog() {
symbol ret = InitConstraintDialog();
if (ret != nil) return ret;
map <string, string> foundfilesystems = (map <string, string>) SCR::Read (.proc.filesystems);
list <string> all_fss = [];
if (size (foundfilesystems) > 0) {
foreach(string fsname, string flag, foundfilesystems, {
all_fss = add(all_fss, fsname);
}
);
}
integer curr = nil;
string type = nil;
string value = nil;
term line = nil;
// --> while
while (ret != `next && ret != `back && ret != `abort) {
ret = (symbol) UI::UserInput();
y2milestone("Ret: %1", ret);
// Managing Exclude items
if (contains([`edit, `delete, `dir, `fs, `regexp], ret)) {
curr = (integer) UI::QueryWidget(`id(`const), `CurrentItem);
if (curr != nil) {
y2milestone("current item: %1", curr);
line = (term) UI::QueryWidget(`id(`const), `Item(curr));
y2milestone("current option: %1", line);
value = (string) line[1]:nil;
y2milestone("value: %1", value);
type = (string) line[2]:nil;
y2milestone("type: %1", type);
}
// Managing Include items
} else if (contains([`add_include, `edit_include, `delete_include], ret)) {
value = (string) UI::QueryWidget(`id(`include), `CurrentItem);
y2milestone("value: %1", value);
}
// Exclude item - delete
if (ret == `delete && type != nil && value != nil) {
ExcludeItemDelete(value, type);
// Exclude item - edit
} else if (ret == `edit && type != nil && value != nil) {
ExcludeItemEdit(value, type, all_fss);
// Exclude item - add regexp
} else if (ret == `regexp) {
AddExcludeItem_Regexp();
// Exclude item - add directory
} else if (ret == `dir) {
AddExcludeItem_Dir();
// Exclude item - add filesystem
} else if (ret == `fs) {
AddExcludeItem_Fs(all_fss);
// Include Directory - add
} else if (ret == `add_include) {
AddIncludeItem();
// Include Directory - delete
} else if (ret == `delete_include) {
DeleteIncludeItem(value);
// Include Directory - edit
} else if (ret == `edit_include) {
EditIncludeItem(value);
// Unknown
} else if (ret != `next && ret != `back && ret != `abort) {
y2error("Unknown ret %1", ret);
}
}
// <-- while
if (Backup::selected_profile != nil) {
// Restore 'next' button
Wizard::RestoreNextButton();
}
return ret;
}
}
ACC SHELL 2018