ACC SHELL
/**
* File:
* Backup.ycp
*
* Module:
* Backup module
*
* Authors:
* Ladislav Slezak <lslezak@suse.cz>
*
* Internal
*
* $Id: Backup.ycp 59901 2009-12-02 15:03:27Z locilka $
*
* Main file for backup module
*
*/
{
module "Backup";
textdomain "backup";
import "Progress";
import "Report";
import "Nfs";
import "Popup";
import "FileUtils";
import "String";
import "Service";
include "backup/functions.ycp";
// include "hwinfo/classnames.ycp";
// do not include (requires installed yast2-tune)
// just use that part of the ClassNames from classnames.ycp
map<integer,map> ClassNames = $[
0x106 : $[
// TRANSLATORS: name of device (the same as in yast2-tune) - the first device
"name" : _("Mass storage device"),
0x00 : _("Disk"),
0x01 : _("Tape"),
0x02 : _("CD-ROM"),
0x03 : _("Floppy disk"),
// TRANSLATORS: name of device (the same as in yast2-tune) - the last device
0x80 : _("Storage device")
]
];
// maximum cron file index
integer max_cron_index = 0;
global string script_store_ext2_area = "/sbin/e2image";
global string script_get_partition_table = "/sbin/fdisk -l";
global string script_get_files = "/usr/lib/YaST2/bin/backup_search.pl";
global string script_create_archive = "/usr/lib/YaST2/bin/backup_archive.pl";
// day names, key is integer used in crontab
global map<integer,string> daynames = $[
1 : _("Monday"),
2 : _("Tuesday"),
3 : _("Wednesday"),
4 : _("Thursday"),
5 : _("Friday"),
6 : _("Saturday"),
7 : _("Sunday")
];
global map<integer,string> ordinal_numbers = $[
1 : _("1st"),
2 : _("2nd"),
3 : _("3rd"),
4 : _("4th"),
5 : _("5th"),
6 : _("6th"),
7 : _("7th"),
8 : _("8th"),
9 : _("9th"),
10 : _("10th"),
11 : _("11th"),
12 : _("12th"),
13 : _("13th"),
14 : _("14th"),
15 : _("15th"),
16 : _("16th"),
17 : _("17th"),
18 : _("18th"),
19 : _("19th"),
20 : _("20th"),
21 : _("21st"),
22 : _("22nd"),
23 : _("23rd"),
24 : _("24th"),
25 : _("25th"),
26 : _("26th"),
27 : _("27th"),
28 : _("28th"),
29 : _("29th"),
30 : _("30th"),
31 : _("31st")
];
// global settings
global map<string, map> backup_profiles = $[] ; // map of all available profiles
// global defaults
global string default_archive_name = ""; // archive file name
global string default_description = ""; // user comment
global symbol default_archive_type = `tgz; // archive type
global boolean default_multi_volume = false;
global symbol default_volume_size = `fd144;
global string default_user_volume_size = "";
global symbol default_user_volume_unit = nil;
global boolean default_search = true; // search files which do not belong to any package
global boolean default_all_rpms_content = false; // by default only changed RPM-files are backed up
global boolean default_system = false; // backup system areas
global boolean default_display = false; // display files before creating archive
global boolean default_do_md5_test = true;
global boolean default_perms = true; // store RPM file if owner/permissions were changed
global list<string> default_default_dir = [ "/media", "/tmp", "/var/lock", "/var/run", "/var/tmp", "/var/cache", "/sys", "/windows", "/mnt" ]; // default excluded directoried from search
global list<string> default_dir_list = default_default_dir; // selected directoried to exclude
global string default_include_dir = "/";
global list<string> default_regexp_list = [];
// iso9660 is used on CDROM, ntfs read-only
global list<string> default_fs_exclude = [ "iso9660", "ntfs" ]; // selected filesystems to exclude from search
global list<string> default_detected_fs = nil; // detected filesystems
global list< map<string,any> > default_detected_ext2 = nil; // detected mounted ext2 filesystems
global list<term> default_ext2_backup = []; // selected ext2 filesystems to backup
global boolean default_backup_pt = true; // backup partition table
global boolean default_backup_all_ext2 = false; // backup all mounted ext2 partitions
global boolean default_backup_none_ext2 = true; // backup none ext2 partitions
global boolean default_backup_selected_ext2 = false; // backup selected ext2 partitions
global string default_tmp_dir = "/tmp";
//global list default_all_entered_dirs = [];
global map<string,map> default_backup_files = $[ ]; // all found files to backup
global map<string,map> default_selected_files = nil; // selected files to backup
global list<string> default_unselected_files = [ ]; // files, which user explicitly unselected
//global list default_selected_directories = []; // default directories to backup
//global boolean default_LVMsnapshot = true;
//global boolean default_testonly = false;
global boolean default_autoprofile = true;
//global boolean default_systembackup = true;
global symbol default_target_type = `file;
//global string default_target_device = nil;
//global map default_target_devices_options = $[];
global string default_temporary_dir = "/var/lib/YaST2/backup/tmp";
global string default_nfsserver = "";
global string default_nfsexport = "";
global boolean default_mail_summary = true;
// global variables initialized to default values:
global string archive_name = default_archive_name; // archive file name
global string description = default_description; // user comment
global symbol archive_type = default_archive_type; // archive type
global boolean profile_is_new_one = false; // newly created archive
global boolean multi_volume = default_multi_volume;
global symbol volume_size = default_volume_size;
global string user_volume_size = default_user_volume_size;
global symbol user_volume_unit = default_user_volume_unit;
global integer user_vol_size = 0;
global string temporary_dir = default_temporary_dir;
global boolean mail_summary = default_mail_summary;
global boolean do_search = default_search; // search files which do not belong to any package
global boolean backup_all_rpms_content = default_all_rpms_content; // backup content of all packages
global boolean system = default_system; // backup system areas
global boolean display = default_display; // display files before creating archive
global boolean do_md5_test = default_do_md5_test;
global boolean perms = default_perms;
global symbol target_type = default_target_type;
//global string target_device = default_target_device;
//global map target_devices_options = default_target_devices_options;
global list<string> default_dir = default_default_dir; // default excluded directoried from search
global list<string> dir_list = default_dir_list; // selected directoried to exclude
global list<string> include_dirs = [default_include_dir]; // selected included directories
global list<string> regexp_list = default_regexp_list;
global list<string> fs_exclude = default_fs_exclude; // selected filesystems to exclude from search
global list<string> detected_fs = default_detected_fs; // detected filesystems
global list< map<string,any> > detected_ext2 = default_detected_ext2; // detected mounted ext2 filesystems
global list<term> ext2_backup = default_ext2_backup; // selected ext2 filesystems to backup
global boolean backup_pt = default_backup_pt; // backup partition table
global boolean backup_all_ext2 = default_backup_all_ext2; // backup all mounted ext2 partitions
global boolean backup_none_ext2 = default_backup_none_ext2; // backup none ext2 partitions
global boolean backup_selected_ext2 = default_backup_selected_ext2; // backup selected ext2 partitions
global string tmp_dir = default_tmp_dir;
// archive target dir used in functions
global string target_dir = "";
global boolean cron_mode = false;
global string cron_profile = "";
global list <map> backup_helper_scripts = [];
//global boolean LVMsnapshot = default_LVMsnapshot;
//global boolean testonly = default_testonly;
global boolean autoprofile = default_autoprofile;
//global boolean systembackup = default_systembackup;
global string nfsserver = default_nfsserver;
global string nfsexport = default_nfsexport;
global string nfsmount = nil; // NFS mount point, remember for unmounting
global map<string,map> backup_files = (map<string,map>)eval( default_backup_files ); // all found files to backup
global map<string,map> selected_files = (map<string,map>)eval( default_selected_files ); // selected files to backup
global list<string> unselected_files = default_unselected_files; // files, which user explicitly unselected
//global list selected_directories = default_selected_directories;
//global list all_entered_dirs = default_all_entered_dirs;
global boolean no_interactive = false; // whether the user should setup configuration manually
global string selected_profile = nil; // name of the selected profile, nil for no selected profile (default settings)
// default volume size if it wasn't detected
global integer undetected_volume_size = 1024*1024*1024;
global list<string> installable_packages = [];
global list<string> complete_backup = [];
// list of files to be deleted finishing the backup editation
global list<string> remove_cron_files = [];
// result of removing old archives
global map remove_result = $[];
// cached detected mount points
map detected_mpoints = nil;
// end of global settings
map cron_settings = $[];
// media description - capacity is maximum file size which fits
// to formatted medium using widely used file system (FAT on floppies)
// just archiving
global boolean just_creating_archive = false;
global list< map<string,any> > cd_media_descriptions = [
$[
"label" : _("CD-R/RW 650 MB (74 min.)"),
"symbol" : `cd650,
"capacity" : 649*1024*1024 // exact size is 650.4 MB - remaining space is for ISO fs
],
$[
"label" : _("CD-R/RW 700 MB (80 min.)"),
"symbol" : `cd700,
"capacity" : 702*1024*1024 // exact size is 703.1 MB - remaining space is for ISO fs
]
];
global list< map<string,any> > floppy_media_descriptions = [
$[
"label" : _("Floppy 1.44 MB"),
"symbol" : `fd144,
"capacity" : 1423*1024 // 1457664B is exact size for FAT fs
],
$[
"label" : _("Floppy 1.2 MB"),
"symbol" : `fd12,
"capacity" : 1185*1024 // 1213952B is exact size for FAT fs
]
];
global list< map<string,any> > zip_media_descriptions = [
$[
"label" : _("ZIP 100 MB"),
"symbol" : `zip100,
"capacity" : 95*1024*1024 // exact size is 96MiB (64 heads, 32 sectors, 96 cylinders, 512B sector)
]
/* $[
"label" : _("ZIP 250 MB"),
"symbol" : `zip250,
"capacity" : ?????
],*/
];
global list< map<string,any> > misc_descriptions = [
/* $[
"label" : _("Default Volume Size"),
"symbol" : `default_size,
"capacity" : 1024*1024*1024
]*/
];
global list< map<string,any> > media_descriptions = (list< map<string,any> >) merge(merge(merge(cd_media_descriptions, floppy_media_descriptions), zip_media_descriptions), misc_descriptions);
global list< map<string,any> > units_description = [
$[
"label" : _("bytes"),
"capacity" : 1,
"symbol" : `B
],
$[
// 10^3 bytes
"label" : _("kB"),
"capacity" : 1000,
"symbol" : `kB
],
$[
// 2^10 bytes
"label" : _("KiB"),
"capacity" : 1024,
"symbol" : `kiB
],
$[
// 10^6 bytes
"label" : _("MB"),
"capacity" : 1000000,
"symbol" : `MB
],
$[
// 2^20 bytes
"label" : _("MiB"),
"capacity" : 1024*1024,
"symbol" : `MiB
],
];
/* File where configuration is stored */
string configuration_filename = "/var/adm/YaST/backup/profiles";
global string backup_scripts_dir = "/var/adm/YaST/backup/scripts/";
/**
* Return capacity of required medium
* @param media Medium descriptions
* @param m Identification of required medium
* @return integer Size of medium in bytes
*/
global define integer GetCapacity(list<map<string,any> > media, symbol m) ``{
integer result = nil;
if (media != nil)
{
foreach(map<string,any> val, media,
``{
if (((symbol)(val["symbol"]:nil)) == m)
{
result = (integer)(val["capacity"]:nil);
}
}
);
}
return result;
}
/**
* Return backup_search.pl script parameters according to state of variables
* @return string String with command line parameters
*/
global define string get_search_script_parameters() ``{
string script_options = " --start-dir / --output-progress"; // required parameter for YaST2 frontend
if (backup_all_rpms_content) {
// see bnc #344643
y2milestone ("Backup all RPMs content...");
script_options = script_options + " --all-rpms-content";
}
if (do_search)
{
script_options = script_options + " --search";
}
// Include Dirs
include_dirs = toset(include_dirs);
y2milestone("Directories to include: %1", include_dirs);
if (size(include_dirs) == 0) {
include_dirs = [default_include_dir];
}
foreach(string d, include_dirs, {
if (d != nil) script_options = script_options + sformat(" --include-dir '%1'", String::Quote(d));
});
// Exclude Dirs
y2milestone("Directories to exclude: %1", dir_list);
if (size(dir_list) > 0) {
foreach(string d, dir_list, {
if (d != nil) script_options = script_options + sformat(" --exclude-dir '%1'", String::Quote(d));
});
}
// Exclude Files
y2milestone("Files to exclude: %1", regexp_list);
if (size(regexp_list) > 0) {
foreach(string r, regexp_list, {
if (r != nil) script_options = script_options + sformat(" --exclude-files '%1'", String::Quote(r));
});
}
// Exclude FileSystems
y2milestone("Filesystems to exclude: %1", fs_exclude);
if (size(fs_exclude) > 0) {
foreach(string i, fs_exclude, {
script_options = script_options + sformat(" --exclude-fs '%1'", String::Quote(i));
});
}
// save list of installable packages and pass it to the search script
if (size(installable_packages) > 0)
{
string content = mergestring(installable_packages, "\n");
string listfile = (string)SCR::Read(.target.tmpdir) + "/packagelist";
SCR::Write(.target.string, listfile, content);
script_options = script_options + " --inst-src-packages " + listfile;
}
if (!do_md5_test)
{
script_options = script_options + " --no-md5";
}
// if (display files before archiving them)
if (display) {
y2milestone ("Files files will be displayed before archiving");
// add widget file option
script_options = script_options + " --widget-file " + ((string)SCR::Read(.target.tmpdir)) + "/items.ycp";
// add items list option
script_options = script_options + " --list-file " + ((string)SCR::Read(.target.tmpdir)) + "/items-list.ycp";
} else {
y2milestone ("Displaying files will be skipped");
}
// add package verification option
script_options = script_options + " --pkg-verification";
y2milestone("Search script options: %1", script_options);
return script_options;
}
/**
* Pre-backup function - mount NFS share if required
* @return boolean true on success
*/
global define boolean PrepareBackup() ``{
if (target_type == `nfs && nfsmount == nil)
{
nfsmount = Nfs::Mount(nfsserver, nfsexport, nil, "", "");
return nfsmount != nil;
}
return true;
}
/**
* Post-backup function - unmount mounted NFS share
* @return boolean true on success
*/
global define boolean PostBackup() ``{
if (target_type == `nfs && nfsmount != nil)
{
boolean ret = Nfs::Unmount(nfsmount);
nfsmount = nil;
return ret;
}
return true;
}
/**
* Return backup_search.pl script parameters according to state of variables
* @param file_list Where is list of files to backup stored
* @param file_comment Where is comment stored
* @return string String with command line parameters
*/
global define string get_archive_script_parameters(string file_list, string file_comment) ``{
string archive_options = " --verbose --files-info " + file_list + " --comment-file " + file_comment;
if (size(complete_backup) > 0)
{
// store list of completely backed up files into a file
string complete_string = mergestring(complete_backup, "\n");
string tmpdir = (string)SCR::Read(.target.tmpdir);
SCR::Write(.target.string, tmpdir + "/complete_backup", complete_string);
archive_options = archive_options + " --complete-backup " + tmpdir + "/complete_backup";
}
else
{
y2debug("complete_backup is empty");
}
y2debug("nfsmount: %1, archive_name: %2", nfsmount, archive_name);
archive_options = archive_options + " --archive-name " + ((target_type == `file) ? archive_name :
sformat("%1/%2", nfsmount, archive_name));
if (system)
{
// add partition tabel option
if (backup_pt)
{
archive_options = archive_options + " --store-ptable";
}
list<string> tmp_selected_pt = [];
foreach(term sel_tmp_pt, ext2_backup, // get device names from `item(`id(XYZ), "XYZ")
``{
string tmp = (string)(sel_tmp_pt[1]:nil);
if (tmp != nil)
{
tmp_selected_pt = add(tmp_selected_pt, tmp);
}
}
);
list<string> detected_ext2_strings = [];
foreach(map<string, any> info, detected_ext2,
``{
string part = (string)(info["partition"]:nil);
if (part != nil)
{
detected_ext2_strings = add(detected_ext2_strings, part);
}
}
);
list<string> partitions = (backup_all_ext2) ? detected_ext2_strings : (backup_none_ext2 ? [] : tmp_selected_pt);
y2milestone("Backup Ext2 partitions: %1", partitions);
foreach(string spt, partitions, ``{archive_options = archive_options + " --store-ext2 " + spt;});
}
map typemap = $[
`tgz : "tgz",
`tbz : "tbz2",
`tar : "tar",
`stgz : "stgz",
`stbz : "stbz2",
`star : "star",
`txt : "txt"
];
archive_options = archive_options + " --archive-type " + typemap[archive_type]:"tgz";
if (multi_volume)
{
if (volume_size == `user_defined)
{
// compute volume size (in kiB)
integer vol_size = tointeger( ((tofloat(user_volume_size)) * (tofloat(GetCapacity(units_description, user_volume_unit)))) / 1024.0 );
y2debug("Volume size is %1 kiB", vol_size);
if (vol_size > 0)
{
archive_options = archive_options + " --multi-volume " + sformat("%1", vol_size);
}
else
{
y2warning("Bad volume size: %1", user_volume_size);
}
}
else
{
archive_options = archive_options + " --multi-volume " + tointeger(tofloat(GetCapacity(media_descriptions, volume_size)) / 1024.0);
}
}
if (size(tmp_dir) > 0)
{
archive_options = archive_options + " --tmp-dir " + tmp_dir;
}
y2milestone("Archive script options: %1", archive_options);
return archive_options;
}
/**
* Exclude file systems without device
*/
global define void ExcludeNodevFS() ``{
map<string,string> filesystems = (map<string,string>)SCR::Read(.proc.filesystems);
if (filesystems == nil)
{
return;
}
foreach(string k, string v, filesystems, ``{
if (v == "nodev")
{
fs_exclude = add(fs_exclude, k);
}
}
);
fs_exclude = toset(fs_exclude);
y2milestone("Detected nodev filesystems: %1", fs_exclude);
}
/**
* Write autoinstallation profile to file autoinst.xml to the same directory as archive
* @param volumes list of created archives (it is written to the XML profile as restoration source)
* @return map map $[ "result" : boolean (true on success), "profile" : string (profile file name) ]
*/
global define map WriteProfile(list<string> volumes) ``{
string archive = ((target_type == `nfs && nfsmount != nil) ? nfsmount + "/" : "") + archive_name;
integer pos = findlastof(archive, "/");
string dir = "";
string file = archive;
if (pos != nil && pos > 0)
{
dir = substring(archive, 0, pos) + "/";
file = substring(archive, pos + 1);
}
string directory = dir;
y2debug("dir: %1, file: %2", dir, file);
string prefix = "file://";
// change prefix according to volume size or archive destination
// check if file is written to NFS file system
list<map> fs = (list<map>)SCR::Read(.proc.mounts);
fs = filter(map info, fs, ``{return ((string)(info["vfstype"]:"")) == "nfs";});
foreach(map info, fs, ``{
string mountpoint = info["file"]:"";
string spec = info["spec"]:"";
string server = substring(spec, 0, findfirstof(spec, ":"));
string remdir = substring(spec, findfirstof(spec, ":") + 1);
if (mountpoint != "" && spec != "")
{
if (substring(archive, 0, size(mountpoint)) == mountpoint)
{
y2milestone("NFS server: %1, directory: %2", server, remdir);
prefix = "nfs://";
dir = server + ":" + remdir + "/";
}
}
}
);
// set prefix according to volume size
if (prefix == "" && multi_volume == true)
{
if (volume_size == `fd144 || volume_size == `fd12)
{
prefix = "fd://";
dir = "/";
}
else if (volume_size == `cd700 || volume_size == `cd650)
{
prefix = "cd://";
dir = "/";
}
}
y2debug("backup write profile: prefix=%1, dir=%2", prefix, dir);
list volumestrings = [];
if (size(volumes) > 0)
{
foreach(string volfile, volumes, ``{
string f = volfile;
integer pos = findlastof(volfile, "/");
if (pos != nil && pos > 0)
{
f = substring(volfile, pos + 1);
}
volumestrings = add(volumestrings, prefix + dir + f);
}
);
}
else
{
volumestrings = [ prefix + dir + file ];
}
map restore = $[ "archives" : volumestrings ];
// add default selection - select all packages to restore
map packages_sel = $[];
foreach(string pkg, map info, selected_files, ``{
// get package base name
if (pkg != "")
{
pkg = regexpsub(pkg, "(.*)-.*-.*", "\\1");
}
else
{
pkg = "_NoPackage_";
}
packages_sel[pkg] = $["sel_type" : "X"];
}
);
if (directory == "")
{
directory = "/";
}
// store profile to this file
string profilefile = directory + GetBaseName(archive_name) + ".xml";
// (tapes)
boolean removable_device = false;
if (regexpmatch (archive, "^/dev/")) {
// save xml to a temporary file
removable_device = true;
profilefile = (string) SCR::Read (.target.tmpdir) + "/backup-profile.xml";
}
y2debug("Profile location: %1", profilefile);
// create and save autoinstallation profile
boolean res = CloneSystem(profilefile, ["lan"], "restore", restore);
y2milestone("Clone result: %1", res);
// tar that temporary file to a device
if (removable_device) {
string command = sformat ("cd '%1'; /bin/tar -cf '%2' 'backup-profile.xml'",
String::Quote ((string) SCR::Read (.target.tmpdir)), String::Quote (archive_name));
map run = (map) SCR::Execute (.target.bash_output, command);
y2milestone ("Running command %1 -> %2", command, run);
if (run["exit"]:-1 != 0) res = false;
profilefile = archive_name;
}
if (target_type == `nfs)
{
pos = findlastof(archive_name, "/");
string nm = (pos != nil && pos > 0) ? substring(archive_name, 0, pos) : "";
y2debug("pos: %1, nm: %2", pos, nm);
// update XML location if it was stored on NFS
profilefile = nfsserver + ":" + nfsexport + "/" + nm + (size(nm) > 0 ? "/" : "") + GetBaseName(archive_name) + ".xml";
y2debug("Updated profile location: %1", profilefile);
}
return $[ "result" : res, "profile" : profilefile ];
}
/**
* Parse cron file
* @param filename File to parse
* @return map parsed values: $["auto":boolean, "day":integer, "hour":integer,
* "minute":integer, "weekday":integer, "every":symbol] or empty map if
* parse error occured
*/
global define map ReadCronSetting(string filename) ``{
map ret = $[];
if (filename == nil || filename == "")
{
return ret;
}
string filecontent = (string)SCR::Read(.target.string, filename);
list<string> lines = splitstring(filecontent, "\n");
// filter out comments
lines = filter(string l, lines, ``(!regexpmatch(l, "^[ \t]*#")));
string line = lines[0]:"";
if (line == nil || line == "")
{
return ret;
}
string regex = "^([^ \t]*)[ \t]*([^ \t]*)[ \t]([^ \t]*)[ \t]([^ \t]*)[ \t]([^ \t]*)[ \t]*[^ \t]*[ \t]*/usr/lib/YaST2/bin/backup_cron[ \t]*\"*[ \t]*profile[ \t]*=[ \t]*([^\"]*)\"*";
symbol every = `none;
map cronsettings = $[];
string profilename = "";
// is cron setting supported (ranges, lists and steps are NOT supported)
boolean unknown_settings = false;
boolean bad_settings = false;
if (regexpmatch(line, regex))
{
string minute_str = regexpsub(line, regex, "\\1");
string hour_str = regexpsub(line, regex, "\\2");
y2milestone("minute_str: %1, hour_str: %2", minute_str, hour_str);
if (!regexpmatch(minute_str, "^[0-9]*$") || !regexpmatch(hour_str, "^[0-9]*$"))
{
unknown_settings = true;
}
y2milestone("unknown_settings: %1", unknown_settings);
integer minute = tointeger(minute_str);
integer hour = tointeger(hour_str);
if (hour > 23 || hour < 0 || minute > 59 || minute < 0)
{
bad_settings = true;
}
string day = regexpsub(line, regex, "\\3");
string month = regexpsub(line, regex, "\\4");
string weekday = regexpsub(line, regex, "\\5");
y2milestone("line: %1", line);
y2milestone("day: %1", day);
integer intday = 1;
integer intweekday = 0;
profilename = regexpsub(line, regex, "\\6");
y2milestone("profilename: %1", profilename);
if (month != "*")
{
// error
unknown_settings = true;
}
if (day == "*" && weekday == "*")
{
// start every day
every = `day;
}
else if (day == "*")
{
every = `week;
if (!regexpmatch(weekday, "^[0-9]*$"))
{
unknown_settings = true;
}
intweekday = tointeger(weekday);
if (intweekday > 7 || intweekday < 0)
{
bad_settings = true;
}
}
else if (weekday == "*")
{
every = `month;
if (!regexpmatch(day, "^[0-9]*$"))
{
unknown_settings = true;
}
intday = tointeger(day);
if (intday > 31 || intday < 1)
{
bad_settings = true;
}
}
else
{
unknown_settings = true;
}
cronsettings = $["auto":true, "day":intday, "hour":hour, "minute":minute, "weekday":intweekday, "every":every];
y2milestone("cronsettings: %1", cronsettings);
}
else
{
unknown_settings = true;
}
if (unknown_settings == true)
{
// %1 is profile name, %2 is filename
Report::Warning(sformat(_("cron settings for profile %1
in file %2
are not fully supported.
"), profilename, filename));
}
if (bad_settings == true)
{
//%1 is profile name, %2 is file name
Report::Error(sformat(_("Some time values for profile %1\nin file %2\nare out of range."), profilename, filename));
}
return (every != `none) ? $["profilename":profilename, "cronsettings": cronsettings] : $[];
}
/**
* Parse all /etc/cron.d/yast2-backup-* files and update profiles
*/
global define void ReadCronSettings() ``{
string crondir = "/etc/cron.d";
list<string> files = (list<string>)SCR::Read(.target.dir, crondir);
// reset cron setings
foreach(string name, map opts, backup_profiles, ``{
map tmp = (map)eval(opts);
map cr = (map)eval(opts[`cron_settings]:$[]);
cr["cronfile"] = "";
cr["cron_changed"] = false;
tmp[`cron_settings] = (map)eval(cr);
backup_profiles[name] = (map)eval(tmp);
}
);
if (files != nil && size(files) > 0)
{
// parse all /etc/cron.d/yast2-backup-* files
foreach(string file, files, ``{
if (regexpmatch(file, "^yast2-backup-[0-9]*$") == true)
{
integer cron_index = tointeger(regexpsub(file, "yast2-backup-([0-9]*)", "\\1"));
y2milestone("cron_index: %1", cron_index);
if (cron_index > max_cron_index)
{
max_cron_index = cron_index;
}
// parse cron file
map cron = ReadCronSetting(crondir + "/" + file);
y2milestone("parsed cron config: %1", cron);
if (cron != $[] && cron != nil)
{
string profilename = cron["profilename"]:"";
map cronsettings = (map)eval(cron["cronsettings"]:$[]);
// update profile
if (profilename != "" && cronsettings != $[])
{
map profile = (map)eval(backup_profiles[profilename]:$[]);
cronsettings["cronfile"] = crondir + "/" + file;
// merge maps - include old backup settings from read profile
cronsettings = union((map)eval(profile[`cron_settings]:$[]), cronsettings);
profile[`cron_settings] = (map)eval(cronsettings);
backup_profiles[profilename] = (map)eval(profile);
}
}
}
}
);
}
y2milestone("max_cron_index: %1", max_cron_index);
}
/**
* Read backup profiles from file, do not set any global settings, just
* @see backup_profiles. The profiles are stored in hardcoded place (configuration_filename variable).
* @return boolean true if there are some profiles available
*/
global define boolean ReadBackupProfiles() ``{
if (FileUtils::Exists(configuration_filename)) {
y2milestone("Reading configuration from %1", configuration_filename);
backup_profiles = (map<string, map>) SCR::Read( .target.ycp, configuration_filename );
} else {
y2milestone("Configuration file %1 doesn't exist yet", configuration_filename);
backup_profiles = nil;
}
// if the list is empty or the file does not exists, set empty map
if( backup_profiles == nil ) backup_profiles = $[];
foreach(string profname, map opts, backup_profiles, ``{
y2debug("Read profile %1: %2", profname, opts);
if (opts[`cron_settings, "auto"]:false == true)
{
y2debug("Deactivating profile %1", profname);
opts[`cron_settings, "auto"] = false;
backup_profiles[profname] = eval(opts);
}
}
);
// add cron settings
ReadCronSettings();
return ( backup_profiles != $[] ) ;
}
/**
* Create cron file content for selected profile.
* @param profilename Name of the profile
* @return string Cron content or empty string if profile has
* disabled automatic start
*/
global define string CreateCronSetting(string profilename) ``{
map input = backup_profiles[profilename, `cron_settings]:$[];
string ret = "";
// return empty string if cron setting was not changed
if (input == nil || input == $[] || input["cron_changed"]:false == false)
{
return ret;
}
if (input["auto"]:false == true)
{
integer hour = input["hour"]:0;
integer minute = input["minute"]:0;
integer day = input["day"]:1;
integer weekday = input["weekday"]:0;
symbol every = input["every"]:`unknown;
if (every == `day)
{
ret = sformat("%1 %2 * * * root /usr/lib/YaST2/bin/backup_cron \"profile=%3\"\n", minute, hour, profilename);
}
else if (every == `week)
{
ret = sformat("%1 %2 * * %3 root /usr/lib/YaST2/bin/backup_cron \"profile=%4\"\n", minute, hour, weekday, profilename);
}
else if (every == `month)
{
ret = sformat("%1 %2 %3 * * root /usr/lib/YaST2/bin/backup_cron \"profile=%4\"\n", minute, hour, day, profilename);
}
// add comment to the first line
ret = "# Please do not edit this file manually, use YaST2 backup module instead\n" + ret;
}
return ret;
}
/**
* Write cron settings from profiles to /etc/cron.d/yast2-backup-* files
*/
global define void WriteCronSettings() ``{
y2milestone("backup_profiles: %1", backup_profiles);
boolean cron_settings_changed = false;
boolean cron_is_needed = false;
// write cron files
foreach(string name, map opts, backup_profiles, ``{
// cron file content
string setting = CreateCronSetting(name);
string cron_file = opts[`cron_settings, "cronfile"]:"";
y2milestone("name: %1", name);
y2milestone("setting: %1", setting);
y2milestone("cron_settings: %1", opts[`cron_settings]:$[]);
if (setting != "" && setting != nil)
{
// is already cron file existing?
if (size(cron_file) == 0)
{
// no, create new file
max_cron_index = max_cron_index + 1;
cron_file = sformat("/etc/cron.d/yast2-backup-%1", max_cron_index);
// remember new cron file name
backup_profiles[name, `cron_settings, "cronfile"] = cron_file;
}
SCR::Write(.target.string, cron_file, setting);
y2milestone("Created file: %1", cron_file);
cron_settings_changed = true;
cron_is_needed = true;
}
else if (size(cron_file) > 0 && opts[`cron_settings, "auto"]:false == false)
{
// remove existing cron file
SCR::Execute(.target.bash, "/bin/rm -f " + cron_file);
y2milestone("removed old cron file: %1", cron_file);
cron_settings_changed = true;
}
// mark saved value as unchanged
map prof = (map)eval(backup_profiles[name]:$[]);
map cron_s = (map)eval(prof[`cron_settings]:$[]);
cron_s["cron_changed"] = false;
prof[`cron_settings] = (map)eval(cron_s);
backup_profiles[name] = (map)eval(prof);
}
);
// Cron needs to be restarted for changes to take effect
// bugzilla #285442
if (cron_settings_changed) {
// running
if (Service::Status("cron") == 0) {
// restart it only
Service::Restart ("cron");
// not running but needed
} else if (cron_is_needed) {
// not enabled, enable it
if (! Service::Enabled ("cron")) Service::Enable ("cron");
// and start it
Service::Start ("cron");
}
}
}
/**
* Write the backup profiles to a file - hardcoded configuration_filename.
* @return boolean true if the write operation was successful.
*/
global define boolean WriteBackupProfiles() ``{
// update cron setting
WriteCronSettings();
string profiles_file = configuration_filename;
if( !SCR::Write (.target.ycp, profiles_file, backup_profiles ) )
{
y2error( "Unable to write profiles into a file" );
// TRANSLATORS: An error popup message
// %1 is the file name
Popup::Error( sformat(_("Could not store profiles to the file %1.
The profile changes will be lost."), profiles_file) );
return false;
}
foreach (string filename, remove_cron_files, {
if (filename != "") {
y2milestone("Removing file: '%1'", filename);
if ( ! (boolean) SCR::Execute (.target.remove, filename) ) {
y2warning("Cannot remove cron file '%1'", filename);
}
}
});
return true;
}
/**
* Take the current profile information and store it into a given profile.
* If the profile already exists, it will be overwritten.
* @param profile_name name of a profile to be stored into
*/
global define void StoreSettingsToBackupProfile( string profile_name ) ``{
map new_profile = $[
`archive_name : archive_name,
`description : description,
`archive_type : archive_type,
`multi_volume : multi_volume,
`volume_size : volume_size,
`user_volume_size : user_volume_size,
`user_volume_unit : user_volume_unit,
`search : do_search,
`all_rpms_content : backup_all_rpms_content,
`system : system,
`display : display,
`do_md5_test : do_md5_test,
`default_dir : default_dir,
`dir_list : dir_list,
`fs_exclude : fs_exclude,
`regexp_list : regexp_list,
`include_dirs : include_dirs,
`detected_fs : detected_fs,
`detected_ext2 : detected_ext2,
`ext2_backup : ext2_backup,
`backup_pt : backup_pt,
`backup_all_ext2 : backup_all_ext2,
`backup_none_ext2 : backup_none_ext2,
`backup_selected_ext2 : backup_selected_ext2,
`unselected_files : unselected_files,
// `all_entered_dirs : all_entered_dirs,
// `selected_directories : selected_directories,
// `LVMsnapshot : LVMsnapshot,
// `testonly : testonly,
`autoprofile : autoprofile,
// `systembackup : systembackup,
`perms : perms,
`nfsserver : nfsserver,
`nfsexport : nfsexport,
`mail_summary : mail_summary,
`tmp_dir : tmp_dir,
`target_type : target_type,
// `target_device : target_device,
// `target_devices_options : target_devices_options,
`backup_helper_scripts : backup_helper_scripts,
`cron_settings : cron_settings,
];
// add the new profile
backup_profiles[profile_name] = new_profile;
}
/**
* Restore the global settings from a given backup profile.
* @param profile_name name of a profile to be used
* @return If the name of the profile cannot be found, return false, otherwise return true.
*/
global define boolean RestoreSettingsFromBackupProfile( string profile_name ) ``{
// return false, is there is no such profile
if( !haskey( backup_profiles, profile_name ) ) return false;
// get the profile data
map profile = (map) (backup_profiles[profile_name]:nil);
// editing archive instead of adding new one
profile_is_new_one = false;
// setup global settings according to profile
// TODO: check, if all settings are valid
archive_name = profile[ `archive_name ]: default_archive_name;
description = profile[ `description ]: default_description;
archive_type = profile[ `archive_type ]: default_archive_type;
multi_volume = profile[ `multi_volume ]: default_multi_volume;
volume_size = profile[ `volume_size ]: default_volume_size;
user_volume_size = profile[ `user_volume_size ]: default_user_volume_size;
user_volume_unit = profile[ `user_volume_unit ]: default_user_volume_unit;
do_search = profile[ `search ]: default_search;
backup_all_rpms_content = profile[ `all_rpms_content ]: default_all_rpms_content;
system = profile[ `system ]: default_system;
display = profile[ `display ]: default_display;
do_md5_test = profile[ `do_md5_test ]: default_do_md5_test;
default_dir = profile[ `default_dir ]: default_default_dir;
// dir_list = profile[ `dir_list ]: default_dir_list;
any read_dir_list = profile[ `dir_list ]: default_dir_list;
// convert list of items to list of strings
if (is(read_dir_list, list<string>))
{
dir_list = (list<string>) read_dir_list;
}
else if (is(read_dir_list, list<term>))
{
// convert dir list from the old format
list<string> new_dir_list = [];
foreach(term i, (list<term>)read_dir_list,
``{
term tmp_id = (term)i[0]:nil;
if (tmp_id != nil)
{
string tmp_d = (string)tmp_id[0]:nil;
if (tmp_d != nil)
{
new_dir_list = add(new_dir_list, tmp_d);
}
}
}
);
dir_list = new_dir_list;
}
else
{
y2warning("Excluded directories - unsupported data type, value is %1", read_dir_list);
}
fs_exclude = profile[ `fs_exclude ]: default_fs_exclude;
detected_fs = profile[ `detected_fs ]: default_detected_fs;
detected_ext2 = profile[ `detected_ext2 ]: default_detected_ext2;
ext2_backup = profile[ `ext2_backup ]: default_ext2_backup;
backup_pt = profile[ `backup_pt ]: default_backup_pt;
backup_all_ext2 = profile[ `backup_all_ext2 ]: default_backup_all_ext2;
backup_none_ext2 = profile[ `backup_none_ext2 ]: default_backup_none_ext2;
backup_selected_ext2 = profile[ `backup_selected_ext2 ]: default_backup_selected_ext2;
unselected_files = profile[ `unselected_files ]: default_unselected_files;
// all_entered_dirs = profile[ `all_entered_dirs ]: default_all_entered_dirs;
// selected_directories = profile[ `selected_directories ]: default_selected_directories;
// LVMsnapshot = profile[ `LVMsnapshot ]: default_LVMsnapshot;
// testonly = profile[ `testonly ]: default_testonly;
autoprofile = profile[ `autoprofile ]: default_autoprofile;
// systembackup = profile[ `systembackup ]: default_systembackup;
perms = profile[ `perms ]: default_perms;
nfsserver = profile[ `nfsserver ]: default_nfsserver;
nfsexport = profile[ `nfsexport ]: default_nfsexport;
target_type = profile[ `target_type ]: default_target_type;
// target_device = profile[ `target_device ]: default_target_device;
// target_devices_options = profile[ `target_devices_options ]: default_target_devices_options;
mail_summary = profile[ `mail_summary ]: default_mail_summary;
tmp_dir = profile[ `tmp_dir ]: default_tmp_dir;
regexp_list = profile[ `regexp_list ]: default_regexp_list;
include_dirs = profile[ `include_dirs ]: [default_include_dir];
selected_files = default_selected_files;
backup_files = default_backup_files;
selected_profile = profile_name;
backup_helper_scripts = profile[ `backup_helper_scripts ]:[];
cron_settings = profile[ `cron_settings ]: $[];
return true;
}
/**
* Restore the default global settings.
*/
global define void RestoreDefaultSettings() ``{
// setup global settings according to defaults
archive_name = default_archive_name;
description = default_description;
archive_type = default_archive_type;
multi_volume = default_multi_volume;
volume_size = default_volume_size;
user_volume_size = default_user_volume_size;
user_volume_unit = default_user_volume_unit;
do_search = default_search;
backup_all_rpms_content = default_all_rpms_content;
system = default_system;
display = default_display;
do_md5_test = default_do_md5_test;
default_dir = default_default_dir;
dir_list = default_dir_list;
fs_exclude = default_fs_exclude;
detected_fs = default_detected_fs;
detected_ext2 = default_detected_ext2;
ext2_backup = default_ext2_backup;
backup_pt = default_backup_pt;
backup_all_ext2 = default_backup_all_ext2;
backup_none_ext2 = default_backup_none_ext2;
backup_selected_ext2 = default_backup_selected_ext2;
unselected_files = default_unselected_files;
// all_entered_dirs = eval( default_all_entered_dirs );
// selected_directories = eval( default_selected_directories );
// LVMsnapshot = default_LVMsnapshot;
// testonly = default_testonly;
autoprofile = default_autoprofile;
// systembackup = default_systembackup;
perms = default_perms;
nfsserver = default_nfsserver;
nfsexport = default_nfsexport;
target_type = default_target_type;
// target_devices_options = eval(default_target_devices_options);
mail_summary = default_mail_summary;
tmp_dir = default_tmp_dir;
regexp_list = default_regexp_list;
include_dirs = [default_include_dir];
selected_files = (map<string,map>)eval(default_selected_files);
backup_files = (map<string,map>)eval(default_backup_files);
backup_helper_scripts = [];
selected_profile = nil;
cron_settings = $[];
}
/**
* Get a sorted list of profile names currently available.
* @return the list of strings (possibly empty).
*/
global define list<string> BackupProfileNames() ``{
list<string> result = (list<string>)maplist(string key, any value, backup_profiles, ``(key) );
if( result == nil ) return [];
else return sort( result );
}
/**
* Create description of automatic backup.
* @param profilename Name of the profile
* @return string description string or empty string if profile has
* disabled automatic start
*/
global define string CreateCronDescription(string profilename) ``{
map input = backup_profiles[profilename, `cron_settings]:$[];
string ret = "";
if (input == nil || input == $[])
{
return ret;
}
if (input["auto"]:false == true)
{
integer hour = input["hour"]:0;
integer minute = input["minute"]:0;
integer day = input["day"]:1;
integer weekday = input["weekday"]:0;
symbol every = input["every"]:`unknown;
// hour/minutes time format - set according your local used format
// usually used conversion specificators:
// %H - hour (0..23), %I - hour (0..12)
// %M - minute (0..59), %p - `AM' or `PM'
// (see man date for more details)
string timeformat = _("%I:%M %p");
string bashcommand = sformat("/bin/date --date '%1:%2' '+%3'", hour, minute, timeformat);
// convert hour and minutes to localized time string - use date utility
map result = (map)SCR::Execute(.target.bash_output, bashcommand);
string ltime = mergestring(splitstring((string)(result["stdout"]:""), "\n"), "");
if (ltime == "")
{
// table item - specified time is invalid
ret = _("Invalid time");
}
else if (every == `day)
{
// table item - start backup every day (%1 is time)
ret = sformat(_("Back up daily at %1"), ltime);
}
else if (every == `week)
{
// table item - start backup every week (%1 is day name, %2 is time)
ret = sformat(_("Back up weekly (%1 at %2)"), daynames[weekday]:"?", ltime);
}
else if (every == `month)
{
// table item - start backup once a month (%1 is day (ordinal number, e.g. 5th), %2 is time)
ret = sformat(_("Back up monthly (%1 day at %2)"), ordinal_numbers[day]:"?", ltime);
}
}
return ret;
}
/**
* Helper function to extract the list of currently available profiles
* @return list List of item used in the table widget
*/
global define list<term> BackupProfileDescriptions() ``{
list result = maplist( string key, map value, backup_profiles,
``{
// description can be multiline - merge lines
string descr = mergestring(splitstring(value[`description]:default_description, "\n"), " ");
map displayinfo = UI::GetDisplayInfo();
// limit size of description shown in the table
// maximum length half of width in ncurses UI
integer maxsize = (displayinfo["TextMode"]:false) ? (displayinfo["Width"]:80 / 2) : 40;
if (size(descr) > maxsize)
{
// use only the beginning of the description, add dots
// BNC #446996: substring for localized strings -> lsubstring
descr = lsubstring (descr, 0, maxsize) + "...";
}
return `item( `id( key ), key, descr, CreateCronDescription(key) );
}
);
if (result == nil) return [];
else return result;
}
/**
* Remove given profile.
* @param profile_name name of a profile to be removed
* @param boolean remove_cronfile defines whether also the cron settings (stored in file) should be removed
* @return If the name of the profile cannot be found, return false, otherwise return true.
*/
global define boolean RemoveBackupProfile( string profile_name, boolean remove_cronfile ) ``{
// return false, is there is no such profile
if( !haskey( backup_profiles, profile_name ) ) return false;
// If there is some cronfile assigned to the profile, remove it too
if (remove_cronfile && backup_profiles[profile_name,`cron_settings,"cronfile"]:nil != nil) {
string filename = backup_profiles[profile_name,`cron_settings,"cronfile"]:"";
y2milestone("File '%1' has been marked to be removed", filename);
remove_cron_files = add(remove_cron_files, filename);
}
backup_profiles = (map<string, map>) remove( backup_profiles, profile_name );
return true;
}
/**
* Try to detect all removable devices present in the system
* @param only_writable return only writable devices (e.g. exclude CD-ROMs)
* @return map Removable devices info
*/
global define map RemovableDevices(boolean only_writable) ``{
map ret = $[];
// detect SCSI, IDE and floppy devices
list<map> devs = (list<map>)merge(merge((list<map>)SCR::Read(.probe.scsi), (list<map>)SCR::Read(.probe.ide)), (list<map>)SCR::Read(.probe.floppy));
if (size(devs) > 0)
{
foreach(map dev, devs,
``{
if (dev["class_id"]:nil == 262 && dev["sub_class_id"]:0 != 0) // Mass storage device, but not a disk
{
string dev_name = dev["dev_name"]:"";
string model = dev["model"]:"";
string bus = dev["bus"]:"";
integer sub_class_id = dev["sub_class_id"]:128; // default is "Storage device"
symbol type_symbol = `unknown;
// use non-rewinding tape device
if (size(dev_name) > 0 && sub_class_id == 1) // check if device is tape
{
list<string> parts = splitstring(dev_name, "/");
// add 'n' to the device name if it is missing
// e.g. /dev/st0 (rewinding) -> /dev/nst0 (non-rewinding)
if (!regexpmatch(parts[size(parts) - 1]:"", "^n"))
{
parts[size(parts) - 1] = "n" + parts[size(parts) - 1]:"";
dev_name = mergestring(parts, "/");
dev["dev_name"] = dev_name;
}
type_symbol = `tape;
}
// type of device (cdrom, disk, tape...) was not detected
string type = ClassNames[262, sub_class_id]:_("Unknown device type");
// remove read only devices if it was requested
// remove CD/DVD-ROM devices, other devices are considered as writable,
// it doesn't check if inserted medium is writable!
if (sub_class_id == 2 && only_writable)
{
// CD-ROM sub class, only writable devices are requested
// if CD device is not CD-R/RW or DVD-R/RW/RAM it is read only
if (!(dev["cdr"]:false || dev["cdrw"]:false
|| dev["dvdram"]:false || dev["dvdr"]:false))
{
dev_name = "";
}
type_symbol = `cd;
}
// predefined media sizes for device - initialize to all types
list media = media_descriptions;
symbol preselected = nil;
integer user_size = 0;
if (dev["dvd"]:false)
{
type = "DVD-ROM";
type_symbol = `dvd;
if (only_writable)
{
dev_name = "";
}
}
else if (dev["cdr"]:false || dev["cdrw"]:false)
{
// CD-R or CD-RW writer device
type = _("CD Writer");
type_symbol = (dev["cdr"]:false) ? `cdr : `cdrw;
media = cd_media_descriptions;
preselected = `cd700;
}
else if (dev["dvdr"]:false)
{
// DVD-R, DVD+R... writer device
type = _("DVD Writer");
type_symbol = `dvdr;
}
else if (dev["dvdram"]:false)
{
type = "DVD-RAM";
type_symbol = `dvdram;
}
else if (dev["zip"]:false && dev["sub_class_id"]:0 == 3)
{
type = "ZIP";
type_symbol = `zip;
media = zip_media_descriptions;
// get medium size
map geometry = dev["resource", "disk_log_geo"]:$[];
integer sz = geometry["cylinders"]:0 * geometry["heads"]:0 * geometry["sectors"]:0;
integer sect_sz = (dev["size", "unit"]:"" == "sectors") ? (dev["size", "y"]:512) : 0;
integer raw_size = sz * sect_sz;
// preselect medium size
if (raw_size == 96*64*32*512)
{
// this is ZIP-100
preselected = `zip100;
}
else if (raw_size > 0)
{
// unknown medium, use raw size minus 1MB for file system
preselected = `user;
user_size = raw_size - 1024*1024;
}
}
// floppy
else if (dev["sub_class_id"]:0 == 3)
{
type_symbol = `floppy;
media = floppy_media_descriptions;
list<map> sizes = dev["resource", "size"]:[];
integer sect_sz = 0;
foreach(map m, sizes,
``{
string unit = m["unit"]:"";
if (unit == "sectors")
{
sect_sz = m["x"]:0 * m["y"]:512;
}
}
);
y2milestone("sect_sz: %1", sect_sz);
if (sect_sz > 0)
{
if (sect_sz == 2880*512)
{
// 1.44 floppy
preselected = `fd144;
}
/* else if (sect_sz == 1186*512)
{
// 1.2 floppy
preselected = `fd12;
}*/
}
}
// volume size was'nt detected, use default value
if (preselected == nil)
{
preselected = `user;
user_size = undetected_volume_size;
}
if (size(dev_name) > 0)
{
ret = add(ret, dev_name, $[ "model" : model, "type" : type, "bus" : bus, "media" : media, "preselected" : preselected, "user_size" : user_size, "type_symbol" : type_symbol ]);
}
}
}
);
}
return ret;
}
/**
* Read all packages available on the installation sources
*/
global define void ReadInstallablePackages() ``{
installable_packages = GetInstallPackages();
y2debug("installable_packages: %1", installable_packages);
}
/**
* Returns detected mount points
* @return map detected mount points
*/
global define map DetectedMountPoints() ``{
// return cached value if available
if (detected_mpoints == nil)
{
detected_mpoints = DetectMountpoints();
}
return detected_mpoints;
}
/**
* Returns local archive name (required if NFS target is used)
* @return string local archive name
*/
global define string GetLocalArchiveName() ``{
string ret = archive_name;
if (target_type == `nfs && nfsmount != nil)
{
ret = nfsmount + "/" + archive_name;
}
return ret;
}
/**
* Writes file using the .backup.file_append SCR agent. This file
* is accepted by backup_archive.pl script. Used global variables:
* selected_files, backup_files.
*
* @return map with keys
* "sel_files" (integer - number of selected files),
* "sel_packages" (integer: number of selected packages),
* "ret_file_list_stored" (boolean: whether the filelist has been completely stored)
* @see <a href="../backup_specification.html">Backup module specification</a>
*/
global define map MapFilesToString () {
integer num_files = 0;
integer num_pack = 0;
if (Backup::selected_files == nil) {
return $[];
}
UI::OpenDialog(
`Left(`Label(
// busy message
_("Creating the list of files for the backup...")
))
);
y2milestone("Storing filenames list...");
string filelist_tmpfile = (string) SCR::Read(.target.tmpdir) + "/filelist";
boolean ret_file_list_stored = true;
boolean flist_appended = nil;
foreach(string pkg, map info, Backup::selected_files, {
if (pkg != "") {
flist_appended = SCR::Write(.backup.file_append, [
filelist_tmpfile,
"Package: " + pkg + "\n" +
"Installed: " + info["install_prefixes"]:"(none)" + "\n" +
mergestring(info["changed_files"]:[], "\n") + "\n"
]);
if (!flist_appended) {
ret_file_list_stored = false;
// a popup error, %1 is as file name
Report::Error(sformat(_("Cannot write the list of selected files to file %1."), filelist_tmpfile));
break;
}
num_files = num_files + size(info["changed_files"]:[]);
num_pack = num_pack + 1;
}
});
// huge amount of files, write by one (or using a buffer)
flist_appended = SCR::Write(.backup.file_append,[
filelist_tmpfile,
"Nopackage:\n"
]);
foreach (string changed_file, Backup::selected_files["", "changed_files"]:[], {
flist_appended = SCR::Write(.backup.file_append,[
filelist_tmpfile,
changed_file + "\n"
]);
if (!flist_appended) {
ret_file_list_stored = false;
// a popup error, %1 is as file name
Report::Error(sformat(_("Cannot write the list of selected files to file %1."), filelist_tmpfile));
break;
}
num_files = num_files + 1;
});
num_pack = num_pack + 1;
y2milestone("Filename stored");
// free the lizard
Backup::selected_files = $[];
UI::CloseDialog();
return $[ "sel_files" : num_files, "sel_packages" : num_pack, "file_list_stored" : ret_file_list_stored ];
}
/**
* Remove and/or rename old existing single archives
* @param name Archive name
* @param max Maximum count of existing archives
* @return map result
*/
global define map RemoveOldSingleArchives(string name, integer max) ``{
list removed = [];
map renamed = $[];
if (name == "" || name == nil)
{
return $[];
}
// check whether archive already exists
integer sz = (integer)SCR::Read(.target.size, name);
if (sz < 0)
{
// file doesn't exist, success
y2milestone("Archive doesn't exist");
return $[];
}
// check wheter older archives exist
list<string> parts = splitstring(name, "/");
string fname = parts[size(parts) - 1]:"";
string dir = mergestring(remove(parts, size(parts) - 1), "/");
if (size(fname) == 0)
{
return $[];
}
string command = "/bin/ls -1 -t " + dir + "/*-" + fname + " 2> /dev/null";
map result = (map)SCR::Execute(.target.bash_output, command);
list<string> files = splitstring(result["stdout"]:"", "\n");
list mv_dates = [];
// filter files with date - use regexp
files = filter(string file, files, ``(regexpmatch(file, "^" + dir + "/[0-9]{14}-" + fname + "$")));
y2milestone("Old archives: %1", files);
if (size(files) > 0 && size(files) >= max && max >= 0)
{
// remove the old archives
while(size(files) > 0 && size(files) >= max)
{
string oldarchive = files[size(files) - 1]:"__DUMMY__";
// remove old archive
command = "/bin/rm -f " + oldarchive;
y2milestone("Removing old archive: %1", oldarchive);
result = (map)SCR::Execute(.target.bash_output, command);
string removedoldarchive = oldarchive;
// update NFS archive name
if (Backup::target_type == `nfs)
{
removedoldarchive = Backup::nfsserver + ":" + Backup::nfsexport + substring(oldarchive, size(Backup::nfsmount));
}
removed = add(removed, removedoldarchive);
// remove old XML profile
string oldXML = dir + "/" + GetBaseName(oldarchive) + ".xml";
command = "/bin/rm -f " + oldXML;
result = (map)SCR::Execute(.target.bash_output, command);
// update NFS archive name
if (Backup::target_type == `nfs)
{
oldXML = Backup::nfsserver + ":" + Backup::nfsexport + substring(oldXML, size(Backup::nfsmount));
}
removed = add(removed, oldXML);
files = remove(files, size(files) - 1);
}
}
map stat = (map)SCR::Read(.target.stat, name);
integer ctime = stat["ctime"]:0;
string ctime_str = SecondsToDateString(ctime);
// rename existing archive
command = "/bin/mv -f " + name + " " + dir + "/" + ctime_str + "-" + fname;
result = (map)SCR::Execute(.target.bash_output, command);
string old_name = name;
string new_name = dir + "/" + ctime_str + "-" + fname;
// update NFS archive name
if (Backup::target_type == `nfs)
{
old_name = Backup::nfsserver + ":" + Backup::nfsexport + substring(name, size(Backup::nfsmount));
new_name = Backup::nfsserver + ":" + Backup::nfsexport + substring(new_name, size(Backup::nfsmount));
y2debug("NFS archive, old_name: %1, new_name: %2", old_name, new_name);
}
// renamed[name] = dir + "/" + ctime_str + "-" + fname;
renamed[old_name] = new_name;
// rename autoinstallation profile
string oldXML = dir + "/" + GetBaseName(name) + ".xml";
string newXML = dir + "/" + ctime_str + "-" + GetBaseName(fname) + ".xml";
command = "/bin/mv -f " + oldXML + " " + newXML;
result = (map)SCR::Execute(.target.bash_output, command);
// update NFS archive name
if (Backup::target_type == `nfs)
{
oldXML = Backup::nfsserver + ":" + Backup::nfsexport + substring(oldXML, size(Backup::nfsmount));
newXML = Backup::nfsserver + ":" + Backup::nfsexport + substring(newXML, size(Backup::nfsmount));
y2debug("NFS archive, oldXML: %1, newXML: %2", oldXML, newXML);
}
renamed[oldXML] = newXML;
return $[ "removed" : removed, "renamed" : renamed ];
}
/**
* Remove and/or rename old existing multivolume archives
* @param name Archive name
* @param max Maximum count of existing archives
* @return map result
*/
global define map RemoveOldMultiArchives(string name, integer max) ``{
list removed = [];
map renamed = $[];
if (name == "" || name == nil)
{
return $[];
}
// check wheter older archives exist
list<string> parts = splitstring(name, "/");
string fname = parts[size(parts) - 1]:"";
string dir = mergestring(remove(parts, size(parts) - 1), "/");
if (size(fname) == 0)
{
return $[];
}
// check whether first archive already exists
integer sz = (integer)SCR::Read(.target.size, dir + "/" + "01_" + fname);
if (sz < 0)
{
// file doesn't exist, success
y2milestone("First multivolume archive doesn't exist");
return $[];
}
else
{
y2milestone("First multivolume archive already exists");
}
string command = "/bin/ls -1 -t " + dir + "/*-*_" + fname + " 2> /dev/null";
map result = (map)SCR::Execute(.target.bash_output, command);
list<string> files = splitstring(result["stdout"]:"", "\n");
list mv_dates = [];
// filter files with date - use regexp
list<string> multi = [];
foreach(string file, files, ``{
if (regexpmatch(file, "^" + dir + "/[0-9]{14}-[0-9][0-9]+_" + fname + "$"))
{
multi = add(multi, file);
string date = regexpsub(file, "^" + dir + "/([0-9]{14})-[0-9][0-9]+_" + fname + "$", "\\1");
if (!contains(mv_dates, date))
{
mv_dates = add(mv_dates, date);
}
}
}
);
files = multi;
y2milestone("Old archives: %1", files);
y2milestone("Old archive dates: %1", mv_dates);
if (size(mv_dates) >= max && max >= 0)
{
// remove the old archives
while(size(mv_dates) >= max)
{
string oldarchivedate = mv_dates[size(mv_dates) - 1]:"__DUMMY__";
y2milestone("removing archives with date %1", oldarchivedate);
foreach(string fn, files,
``{
if (regexpmatch(fn, "^" + dir + "/" + oldarchivedate + "-[0-9]+_" + fname + "$"))
{
// remove old archive
command = "/bin/rm -f " + fn;
y2milestone("Removing old volume: %1", fn);
// update NFS archive name
if (Backup::target_type == `nfs)
{
fn = Backup::nfsserver + ":" + Backup::nfsexport + substring(fn, size(Backup::nfsmount));
}
removed = add(removed, fn);
SCR::Execute(.target.bash_output, command);
}
}
);
// remove old XML profile
string oldXML = dir + "/" + oldarchivedate + "-" + GetBaseName(fname) + ".xml";
command = "/bin/rm -f " + oldXML;
// update NFS archive name
if (Backup::target_type == `nfs)
{
oldXML = Backup::nfsserver + ":" + Backup::nfsexport + substring(oldXML, size(Backup::nfsmount));
}
removed = add(removed, oldXML);
result = (map)SCR::Execute(.target.bash_output, command);
mv_dates = remove(mv_dates, size(mv_dates) - 1);
}
}
// get creation time of the first part of the archive
map stat = (map)SCR::Read(.target.stat, dir + "/" + "01_" + fname);
integer ctime = stat["ctime"]:0;
string ctime_str = SecondsToDateString(ctime);
command = "/bin/ls -1 -t " + dir + "/*_" + fname + " 2> /dev/null";
result = (map)SCR::Execute(.target.bash_output, command);
files = splitstring(result["stdout"]:"", "\n");
files = filter(string file, files, ``(regexpmatch(file, "^" + dir + "/[0-9]+_" + fname + "$")));
y2milestone("Existing volumes: %1", files);
foreach(string volume, files, ``{
list<string> vol_parts = splitstring(volume, "/");
string vol_fname = vol_parts[size(vol_parts) - 1]:"";
string vol_dir = mergestring(remove(vol_parts, size(vol_parts) - 1), "/");
// rename existing archive
string from = vol_dir + "/" + vol_fname;
string to = vol_dir + "/" + ctime_str + "-" + vol_fname;
command = "/bin/mv -f " + from + " " + to;
result = (map)SCR::Execute(.target.bash_output, command);
// update NFS archive name
if (Backup::target_type == `nfs)
{
from = Backup::nfsserver + ":" + Backup::nfsexport + substring(from, size(Backup::nfsmount));
to = Backup::nfsserver + ":" + Backup::nfsexport + substring(to, size(Backup::nfsmount));
y2debug("NFS archive, from: %1, to: %2", from, to);
}
renamed[from] = to;
y2milestone("renamed volume %1", volume);
}
);
// rename autoinstallation profile
string oldXML = dir + "/" + GetBaseName(name) + ".xml";
string newXML = dir + "/" + ctime_str + "-" + GetBaseName(fname) + ".xml";
command = "/bin/mv -f " + oldXML + " " + newXML;
result = (map)SCR::Execute(.target.bash_output, command);
// update NFS archive name
if (Backup::target_type == `nfs)
{
oldXML = Backup::nfsserver + ":" + Backup::nfsexport + substring(oldXML, size(Backup::nfsmount));
newXML = Backup::nfsserver + ":" + Backup::nfsexport + substring(newXML, size(Backup::nfsmount));
y2debug("NFS archive, oldXML: %1, newXML: %2", oldXML, newXML);
}
renamed[oldXML] = newXML;
return $[ "removed" : removed, "renamed" : renamed ];
}
/**
* Remove and/or rename old existing archives
* @param name Archive name
* @param max Maximum count of existing archives
* @param multivolume Is archive archive multivolume?
* @return map result
*/
global define map RemoveOldArchives(string name, integer max, boolean multivolume) ``{
return (multivolume == true) ? RemoveOldMultiArchives(name, max)
: RemoveOldSingleArchives(name, max);
}
}
ACC SHELL 2018