ACC SHELL
/**
* File:
* modules/Restore.ycp
*
* Package:
* Restore module
*
* Summary:
* Data for configuration of restore, input and output functions.
*
* Authors:
* Ladislav Slezak <lslezak@suse.cz>
*
* $Id: Restore.ycp 55625 2009-02-19 17:33:01Z locilka $
* Representation of the configuration of restore.
* Input and output routines.
*
*/
{
// This is "Restore" module
module "Restore";
import "Progress";
import "Report";
import "Mode";
import "Summary";
import "Service";
import "Package";
import "Label";
import "Popup";
import "URL";
import "Message";
import "String";
textdomain "restore";
// local file name (can be on mounted file system)
string filename = "";
// entered file name (e.g. nfs://server:/dir/archive.tar)
global string inputname = "";
global boolean completerestoration = false;
// list of volumes in URL-like syntax
list<string> inputvolumes = [];
// contents of archive
list<string> archivefiles = [];
// installed packages at backup time
map<string,string> installedpkgs = $[];
// list of installed packages
map<string,string> actualinstalledpackages = $[];
list<string> complete_backup = [];
// restoration archive and selection
// "vers" : "version", "files" : ["files"], "prefix" : "prefix", "descr" : "description", "sel_type" : "X", "sel_file" : [""]
map<string,map<string, any> > archive_info = $[];
map<string,map> autoinst_info = $[];
// information stored in archive
string date = "";
string hostname = "";
string comment = "";
// list of files
list<string> volumeparts = [];
// temporary directory
string tempdir = "";
// restore files to the selected directory
global string targetDirectory = "/";
// mount point, stored for unmounting
string mountpoint = "";
// nopackage identification stored in Exported map
// (to prevent empty tag in XML profile)
string nopackage_id = "_NoPackage_";
/**
* Run lilo after files are restored
*/
global boolean runbootloader = true;
/**
* Run SuSEconfig after files are restored
*/
global boolean runSuSEconfig = true;
/**
* Rewrite RPM db - unapack /var/lib/rpm/* files from backup if present
*/
global boolean restoreRPMdb = nil;
boolean config_modified = false;
/**
* Return modified flag
* @return true if modified
*/
global define boolean Modified() ``{
return config_modified;
};
global define void SetModified() {
config_modified = true;
}
/**
* Selected archive has more parts
* @return boolen True if archive have more than one part.
*/
global define boolean IsMultiVolume() ``{
return (size(volumeparts) > 0);
}
/**
* Return date when backup archive was created. Date is stored in archive in file info/date.
* @return string Date
*/
global define string GetArchiveDate() ``{
return date;
}
/**
* Return name of backup archive
* @return string Input name file name
*/
global define string GetInputName() ``{
return inputname;
}
/**
* Return name of backup archive
* @return string File name
*/
global define string GetArchiveName() ``{
return filename;
}
/**
* Return user comment stored in archive. Comment is stored in file info/comment.
* @return string Archive comment
*/
global define string GetArchiveComment() ``{
return comment;
}
/**
* Return host name of machine on which backup archive was created. Host name is stored in archive in file info/hostname.
* @return string Host name
*/
global define string GetArchiveHostname() ``{
return hostname;
}
/**
* Return map with packages installed at backup time (form is $["package name" : "version"]).
* @return map Installed packages at backup time
*/
global define map<string,string> GetArchiveInstalledPackages() ``{
return installedpkgs;
}
/**
* Return list of files in the backup archive
* @return list Files in the archive
*/
global define list GetArchiveFiles() ``{
return archivefiles;
}
/**
* Read installed packages.
* @return map Map $[ "packagename" : "version" ]
*/
global define map<string,string> ReadActualInstalledPackages() ``{
// do not read installed packages in autoyast config mode
if (Mode::config () == true)
{
return $[];
}
// init packager (TODO: later should be removed...)
Pkg::TargetInit("/", false);
// read info about installed packages
list<string> info = Pkg::GetPackages(`installed, false);
y2debug("ReadActualInstalledPackages: %1", sort(info));
// process version info
map<string,string> ret = $[];
foreach(string pkg, info, ``{
list<string> all = splitstring(pkg, " ");
ret = add(ret, all[0]:"unknown", all[1]:"" + "-" + all[2]:"");
}
);
return ret;
}
/**
* Return installed packages. Result is cached in Restore module, so only first use takes long time
* @return map Map $[ "packagename" : "version" ]
*/
global define map<string,string> GetActualInstalledPackages() ``{
if (actualinstalledpackages == $[] || actualinstalledpackages == nil)
{
actualinstalledpackages = ReadActualInstalledPackages();
}
return actualinstalledpackages;
}
/**
* Return missing packages (packages which were installed at backup time, but at restore time they are not installed)
* @return map Map $[ "packagename" : $[ "ver" : "version", "descr" : "Short description of the package"]], key description is present only if decription exists
*/
global define map<string, map<string, string> > GetMissingPackages() ``{
map<string,string> r = $[];
GetActualInstalledPackages();
// filter actual installed packages out
r = filter(string p, string v, installedpkgs, ``{
return (haskey(actualinstalledpackages, p) != true);
}
);
y2debug("actualinstalledpackages: %1", actualinstalledpackages);
y2debug("GetMissingPackages r: %1", r);
// add descriptions
map<string, map<string,string> > ret = mapmap(string pkg, string version, r, ``{
string descr = (Mode::test () == false) ? Pkg::PkgSummary(pkg) : "";
if (descr == nil)
{
return $[pkg: $["ver" : version]];
}
else
{
return $[pkg: $["ver" : version, "descr" : descr ]];
}
}
);
// ignore gpg-pubkey packages - they are not real RPMs
ret = filter(string pkg, map<string,string> inf, ret, ``{
return (pkg != "gpg-pubkey");
}
);
y2debug("GetMissingPackages ret: %1", ret);
return ret;
}
/**
* Return extra packages (packages which are installed at restore time, but at restore time they are installed)
* @return map Map $[ "packagename" : $[ "ver" : "version", "descr" : "Short description of the package"]], key description is present only if decription exists
*/
global define map<string,map<string,string> > GetExtraPackages() ``{
map<string,string> r = $[];
GetActualInstalledPackages();
// filter actual installed packages out
r = filter(string p, string v, actualinstalledpackages, ``{
return (haskey(installedpkgs, p) != true);
}
);
// add descriptions
map<string,map<string,string> > ret = mapmap(string pkg, string version, r, ``{
string descr = (Mode::test () == false) ? Pkg::PkgSummary(pkg) : "";
if (descr == nil)
{
return $[pkg: $["ver" : version]];
}
else
{
return $[pkg: $["ver" : version, "descr" : descr ]];
}
}
);
return ret;
}
/**
* Return packages which have different version at backup archive and in system
* @return map Map $[ "packagename" : $[ "inst": "installed version", "ver" : "version at backup time, "descr" : "Short description of the package"]], key description is present only if decription exists
*/
global define map<string, map <string, string> > GetMismatchedPackages() ``{
map<string, map <string, string> > ret = $[];
GetActualInstalledPackages();
foreach(string p, string v, actualinstalledpackages, ``{
if (haskey(installedpkgs, p) == true)
{
string backupversion = (string)(installedpkgs[p]:"");
if (backupversion != v)
{
string descr = (Mode::test () == false) ? Pkg::PkgSummary(p) : "";
ret = (descr != nil) ? add(ret, p, $["ver": backupversion, "inst" : v, "descr" : descr]) :
add(ret, p, $["ver": backupversion, "inst" : v]);
}
}
}
);
return ret;
}
/**
* Returns selected packages (even partially).
* @return map Map with same keys as map returned by GetArchiveInfo()
*/
global define map<string, map> GetSelectedPackages() ``{
map<string, map> ret = $[];
if (archive_info != nil)
{
// filter out unselected packages
ret = filter(string p, map info, archive_info, ``{
string sel_type = (string)(info["sel_type"]:" ");
if (sel_type == "X" || sel_type == "P")
{
return true;
}
else
{
return false;
}
}
);
}
return ret;
}
/**
* Clear cache of installed packages. Next use of GetActualInstalledPackages() function will reread installed packages.
*/
global define void ClearInstalledPackagesCache()
{
actualinstalledpackages = nil;
}
/**
* Umount mounted file system.
*/
global define void Umount() ``{
if (mountpoint != "" && mountpoint != nil)
{
y2milestone("Umount called - unmounting %1", mountpoint);
SCR::Execute(.target.umount, mountpoint);
mountpoint = "";
}
}
/**
* Access to file on NFS server
* @param server Name or IP adress of NFS server
* @param file File on the server
* @return map $[ "mounted" : boolena (true on success), "mountpoint" : string (mount point) , "file" : string (file name), "server_dir" : string (directory on the server) ]
*/
define map mountNFS(string server, string file) ``{
integer pos = findlastof(file, "/");
if (pos != nil)
{
string dir = substring(file, 0, pos);
string f = substring(file, pos + 1);
string tmpdir = (string) SCR::Read(.target.tmpdir);
string mpoint = tmpdir + "/nfs";
if (dir == "" || dir == nil)
{
dir = "/";
}
// create mount point directory
SCR::Execute(.target.mkdir, mpoint);
y2milestone("dir: %1", dir);
y2milestone("file: %1", f);
y2milestone("mpoint: %1", mpoint);
boolean result = (boolean) SCR::Execute(.target.mount, [server + ":" + dir, mpoint], "-t nfs -o ro");
return $[ "mounted" : result, "mountpoint" : mpoint , "file" : f, "server_dir" : dir ];
}
return $[ "mounted" : false ];
}
/**
* Access to file on CD
* @param cdindex Index of CD drive (in list SCR::Read(.probe.cdrom))
* @return map $[ "mounted" : boolena (true on success), "mpoint" : string (mount point) ]
*/
define map mountCD(integer cdindex) ``{
list drives = (list) SCR::Read(.probe.cdrom);
if (cdindex == nil || cdindex > size(drives) - 1)
{
return $[ "mounted" : false ];
}
string cddevice = drives[cdindex, "dev_name"]:"/dev/cdrom";
string tmpdir = (string) SCR::Read(.target.tmpdir);
string mpoint = tmpdir + "/cd";
// create mount point directory
SCR::Execute(.target.mkdir, mpoint);
boolean result = (boolean) SCR::Execute(.target.mount, [cddevice, mpoint], "-o ro" );
return $[ "mounted" : result, "mpoint" : mpoint ];
}
/**
* Access to file on floppy
* @param fdindex Index of floppy drive (in list SCR::Read(.probe.floppy))
* @return map $[ "mounted" : boolena (true on success), "mpoint" : string (mount point) ]
*/
define map mountFloppy(integer fdindex) ``{
list drives = (list) SCR::Read(.probe.floppy);
if (fdindex == nil || fdindex > size(drives) - 1)
{
return $[ "mounted" : false ];
}
string fddevice = drives[fdindex, "dev_name"]:"/dev/fd0";
string tmpdir = (string) SCR::Read(.target.tmpdir);
string mpoint = tmpdir + "/fd";
// create mount point directory
SCR::Execute(.target.mkdir, mpoint);
boolean result = (boolean) SCR::Execute(.target.mount, [fddevice, mpoint], "-o ro" );
return $[ "mounted" : result, "mpoint" : mpoint ];
}
/**
* Mount device
* @param device Device file name (e.g. /dev/cdrom, /dev/sda...)
* @return map Map $[ "mounted" : boolean (true on success), "mpoint" : string (mount point where device was mounted) ];
*/
define map mountDevice(string device) ``{
if (device == nil || device == "")
{
return $[ "mounted" : false ];
}
string tmpdir = (string) SCR::Read(.target.tmpdir);
string mpoint = tmpdir + "/mount";
// create mount point directory
SCR::Execute(.target.mkdir, mpoint);
// mount read-only
boolean result = (boolean) SCR::Execute(.target.mount, [device, mpoint], "-o ro" );
return $[ "mounted" : result, "mpoint" : mpoint ];
}
/**
* Mount input source
* @param input File in URl-like syntax
* @return map Map $[ "success" : boolean (true on success), "mpoint" : string (mount point), "file" : string (file name on the local system) ];
*/
global define map MountInput(string input) ``{
y2milestone("MountInput(%1)", input);
boolean success = false;
string mpoint = "";
string file = "";
//parse 'input'
string nfsprefix = "nfs://";
string fileprefix = "file://";
string devprefix = "dev://";
string cdprefix = "cd"; // cd prefix can be "cd://" (equivalent to "cd0://"), "cd1://", "cd2://", ... number of CD identification
string fdprefix = "fd";
map parsed_url = URL::Parse(input);
string scheme = parsed_url["scheme"]:"file";
if (scheme == "nfs")
{
string nfs_server = parsed_url["host"]:"";
//substring(tail, 0, pos);
string nfs_file = parsed_url["path"]:"";
//substring(tail, pos + delimiter_correction);
// check if portmapper is installed
integer result = (integer) SCR::Execute(.target.bash, "/bin/rpm -q portmap");
y2milestone("check result: rpm: %1", result);
if (result != 0)
{
y2milestone("portmapper package is not installed");
Package::DoInstall(["portmap"]);
}
// check if portmapper is running
integer portmapper_status = Service::Status("portmap");
y2milestone("portmap status: %1", portmapper_status);
if (portmapper_status != 0)
{
// start portmapper
boolean started = Service::Start("portmap");
y2milestone("portmap start result: %1", started);
}
y2milestone("NFS source - server: %1 file: %2", nfs_server, nfs_file);
map mountresult = mountNFS(nfs_server, nfs_file);
if (mountresult["mounted"]:false == false)
{
y2error("Cannot read source '%1' - NFS mount failed", input);
}
else
{
mpoint = mountresult["mountpoint"]:"";
file = mpoint + "/" + mountresult["file"]:"";
success = true;
y2milestone("mpoint: %1", mpoint);
y2milestone("file: %1", file);
}
}
else if (scheme == "dev")
{
string device = parsed_url["host"]:"";
string devfile = parsed_url["path"]:"";
y2milestone("Device source - device: %1 file: %2", device, devfile);
map mountresult = mountDevice(device);
if (mountresult["mounted"]:false == false)
{
y2error("Cannot read source '%1' - device mount failed", input);
}
else
{
mpoint = mountresult["mpoint"]:"";
file = mpoint + "/" + devfile;
success = true;
}
}
else if (scheme == "file")
{
file = substring(input, size(fileprefix));
y2milestone("FILE source: %1", file);
success = true;
}
else if (regexpmatch(scheme, "^cd[0-9]*"))
{
// get CD drive index
string cdindex = regexpsub(scheme, "^cd0*([0-9]*)", "\\1");
string cdfile = parsed_url["path"]:"";
if (cdindex == nil || cdindex == "")
{
cdindex = "0";
}
if (cdfile == nil)
{
cdfile = "";
}
y2milestone("CD source - drive: %1 file: %2", cdindex, cdfile);
// mount CD
map mountresult = mountCD(tointeger(cdindex));
if (mountresult["mounted"]:false == false)
{
y2error("Cannot read source '%1' - mount failed", input);
}
else
{
mpoint = mountresult["mpoint"]:"";
file = mpoint + "/" + cdfile;
success = true;
}
}
else if (regexpmatch(scheme, "^fd[0-9]*"))
{
// get floppy index
string fdindex = regexpsub(scheme, "fd0*([0-9]*)://(.*)", "\\1");
string fdfile = parsed_url["path"]:"";
if (fdindex == nil || fdindex == "")
{
fdindex = "0";
}
if (fdfile == nil)
{
fdfile = "";
}
y2milestone("Floppy source - drive: %1 file: %2", fdindex, fdfile);
// mount floppy
map mountresult = mountFloppy(tointeger(fdindex));
if (mountresult["mounted"]:false == false)
{
y2error("Cannot read source '%1' - mount failed", input);
}
else
{
mpoint = mountresult["mpoint"]:"";
file = mpoint + "/" + fdfile;
success = true;
}
}
else
{
y2error("Unknown prefix in input: %1", input);
}
return $[ "success" : success, "mpoint" : mpoint, "file" : file ];
}
/**
* Check if volume number in archive is equal to expected volume number
* @param filename Volume file name
* @param num Number of volume
* @return map Map $[ "success" : boolean (true on success), "lastvolume" : boolean (true if archive is last volume) ]
*/
define map CheckVolume(string filename, integer num) ``{
boolean success = false;
boolean lastvolume = true;
// test if archive is multi volume - use -v parameter to get file descriptions
map detailresult = (map) SCR::Execute(.target.bash_output, "/bin/tar -v -t -f " + filename);
list<string> stdout = splitstring(detailresult["stdout"]:"", "\n");
string firstline = stdout[0]:"";
y2debug("Test: First line: %1", firstline);
// check if first line is volume label number num
if (regexpmatch(firstline, sformat("V--------- .* YaST2 backup: Volume %1--.*--", num)) == true)
{
success = true;
}
return $[ "success" : success, "lastvolume" : (detailresult["exit"]:-1 == 0) ];
}
/**
* Copy volume to the local temporary directory
* @param filename Source file
* @param num Number of volume
* @return map Map $[ "success" : boolean (true on success), "file" : string (file name in the temporary directory) ]
*/
define map CopyVolume(string filename, integer num) ``{
boolean success = false;
string tmpfile = tempdir + "/" + sformat("%1", num) + ".tar";
// copy multi volume part to temp directory
integer exit = (integer) SCR::Execute(.target.bash, "/bin/cp " + filename + " " + tmpfile);
if (exit == 0)
{
success = true;
}
else
{
y2error("Copy failed");
}
return $[ "success" : success, "file" : tmpfile ];
}
/**
* Add next volume - check volume, copy volume to the temp. dir.
* @param file File name of volume
* @return map Map $[ "success" : boolean (true on success), "lastvolume" : boolean (true if archive is last volume) ]
*/
define map AddVolume(string file) ``{
integer vol = size(volumeparts) + 1;
boolean success = false;
// check if file is next volume
map check = CheckVolume(file, vol);
y2debug("CheckVolume(%1, %2): %3", file, vol, check);
if (check["success"]:false == true)
{
// copy file to temporary directory
map copy = CopyVolume(file, vol);
y2debug("CopyVolume(%1, %2): %3", file, vol, copy);
if (copy["success"]:false == true)
{
string partname = copy["file"]:"";
volumeparts = add (volumeparts, partname);
success = true;
}
}
return $[ "success" : success, "lastvolume" : check["lastvolume"]:true ];
}
/**
* Change restore selection of package.
* @param pkgnames Name of package
* @param selection New restore selection for package, map $[ "sel_type" : "X", "sel_file" : ["files"] ]
*/
global define void SetRestoreSelection(string pkgname, map selection) ``{
if (pkgname == nopackage_id)
{
pkgname = "";
}
if (haskey(archive_info, pkgname) == false)
{
y2warning("Package %1 is not in archive, cannot be restored!", pkgname);
}
else
{
string sel_type = selection["sel_type"]:" ";
list sel_file = [];
map<string, any> pkginfo = archive_info[pkgname]:$[];
if (pkginfo == nil)
{
pkginfo = $[];
}
if (sel_type == "P")
{
sel_file = selection["sel_file"]:[];
}
else if (sel_type != "X" && sel_type != " ")
{
y2warning("Unknown selection type '%1' for package '%2'", sel_type, pkgname);
}
pkginfo = add(pkginfo, "sel_type", sel_type);
pkginfo = add(pkginfo, "sel_file", sel_file);
archive_info = add(archive_info, pkgname, pkginfo);
}
}
/**
* Set selection in _auto client and display properties of archive
* @param settings Restoration selection
*/
global define void SetSelectionProperty(map<string,map> settings) ``{
// set selection for this archive and display archive propertyt
foreach(string package, map info, settings, ``{
y2debug("setting selection: package:%1, selection:%2)", package, info);
if (package == nopackage_id)
{
package = "";
}
SetRestoreSelection(package, info);
}
);
}
/**
* Read contents of archive
* @param input File name of backup archive. File on NFS server is 'nfs://server:/dir/file.tar', local file: 'file:///dir/file.tar' (prefix is file://, directory is /dir)
* @return boolean True if archive was succesfully read, otherwise false (file does not exist, not tar archive, broken archive, archive not created by Backup module, ...)
*/
global define boolean Read(string input) ``{
// umount old mount point
Umount();
if (tempdir == "")
{
tempdir = (string) SCR::Read(.target.tmpdir);
}
inputname = input;
if (Mode::test () == true)
{
filename = "/tmp/dummy.tar";
archivefiles = [ "info/", "info/date", "info/comment", "info/files",
"info/packages_info", "info/installed_packages", "info/hostname",
"NOPACKAGE-20020509-0.tar.gz", "kdebase3-3.0-19-20020509-0.tar.gz", "lprng-3.8.5-49-20020509-0.tar.gz",
"mozilla-0.9.8-54-20020509-0.tar.gz", "netcfg-2002.3.20-0-20020509-0.tar.gz" ];
date = "13.01.2002 14:25";
comment = "Some comments";
hostname = "linux.local";
installedpkgs = $["netcfg" : "2002.3.20-0", "lprng" : "3.8.5-49", "kdebase3" : "3.0-19", "gnome-applets" : "1.4.0.5-98", "mozilla" : "0.9.8-54"];
actualinstalledpackages = $["ggv" : "1.1.93-167", "netcfg" : "2002.3.20-0", "lprng" : "3.8.5-49", "kdebase3" : "3.0-19", "aterm" : "0.4.0"];
archive_info = $["" : $["descr" : "Files not owned by any package", "files":["/.qt/", "/dev/dvd", "/dev/cdrom"], "sel_type" : "X" ],
"kdebase3" : $["descr" : "KDE base package: base system", "files":["/etc/opt/kde3/share/config/kdm/kdmrc"], "prefix" : "", "sel_type" : "X", "vers" : "3.0-19"],
"mozilla" : $["descr" : "Open Source WWW browser", "files" : ["/opt/mozilla/chrome/installed-chrome.txt"], "prefix" : "", "sel_type" : "X", "vers" : "0.9.8-54"],
"lprng" : $["descr" : "LPRng Print Spooler", "files" : ["/etc/init.d/lpd"], "prefix" : "", "sel_type" : "X", "vers" : "3.8.5-49"],
"netcfg":$["descr" : "Network configuration files in /etc", "files" : ["/etc/HOSTNAME", "/etc/defaultdomain", "/etc/exports", "/etc/hosts"], "prefix":"", "sel_type":"X", "vers":"2002.3.20-0"]
];
// set default selection
archive_info = mapmap(string p, map<string, any> i, archive_info, ``{
i = add(i, "sel_type", (p == "") ? "X" : ((haskey(actualinstalledpackages, p)) ? "X" : " "));
return $[p: i];
}
);
return true;
}
// mount source
map mresult = MountInput(input);
y2debug("MountInput: %1", mresult);
if (mresult["success"]:false == false)
{
// error message
Report::Error(_("Cannot mount file system."));
return false;
}
filename = mresult["file"]:"";
mountpoint = mresult["mpoint"]:"";
y2milestone("filename: %1", filename);
y2milestone("mountpoint: %1", mountpoint);
// get archive contents
map result = (map) SCR::Execute(.target.bash_output, "/bin/tar -t -f " + filename);
// check tar exit value
if (result["exit"]:-1 != 0)
{
// tar failed, check whether file is first volume of multivolume archive
volumeparts = [];
map addresult = AddVolume(filename);
if (addresult["success"]:false == true)
{
// read archive info from local copy (should be faster)
filename = volumeparts[0]: "";
}
else
{
// error message
Report::Error(_("Cannot read archive file.
It is not a tar archive or it is broken.
"));
return false;
}
}
else
{
// if archive is not local or NFS file copy it to tempdir (even if it isn't multi volume),
// so removable device (e.g. CD-ROM) can be used for package installation later
if (substring(input, 0, size("file://")) != "file://" && substring(input, 0, size("nfs://")) != "nfs://")
{
// copy archive to local file
map copy = CopyVolume(filename, 0);
if (copy["success"]:false == true)
{
// set file name to local copy
filename = copy["file"]:"";
// umount file system
Umount();
}
else
{
// error message: copy failed
Report::Error(_("Cannot copy archive file
to temporary directory.
"));
return false;
}
}
}
// get list of files
archivefiles = splitstring(result["stdout"]:"", "\n");
archivefiles = filter(string f, archivefiles, ``{return f != "" && f != nil;});
boolean compressed_packages_info = contains(archivefiles, "info/packages_info.gz");
y2milestone("compressed_packages_info: %1", compressed_packages_info);
if (!(contains(archivefiles, "info/files") && (contains(archivefiles, "info/packages_info") || contains(archivefiles, "info/packages_info.gz")) && contains(archivefiles, "info/installed_packages")))
{
// archive doesn't contain files from backup - file is not backup archive or not first volume of multi volume archive
y2error("Archive does not contain 'info/files' or 'info/packages_info' or 'info/installed_packages' file!");
// error message
Report::Error(_("The archive does not contain the required files.
It was probably not created by the backup module.
"));
return false;
}
y2debug("read archivefiles: %1", archivefiles);
string infofiles = "info/comment info/hostname info/date info/installed_packages info/files info/complete_backup ";
infofiles = infofiles + (compressed_packages_info ? "info/packages_info.gz" : "info/packages_info");
// unpack info files
result = (map) SCR::Execute(.target.bash_output, "/bin/tar -C '" + String::Quote (tempdir) + "' -x -f " + filename + " " + infofiles + " 2> /dev/null");
date = (string) SCR::Read(.target.string, tempdir + "/info/date");
comment = (string) SCR::Read(.target.string, tempdir + "/info/comment");
hostname = (string) SCR::Read(.target.string, tempdir + "/info/hostname");
string complete_backup_str = (SCR::Read(.target.size, tempdir + "/info/complete_backup") > 0) ?
(string) SCR::Read(.target.string, tempdir + "/info/complete_backup")
: "";
y2debug("complete_backup_str: %1", complete_backup_str);
complete_backup = splitstring(complete_backup_str, "\n");
y2milestone("Complete backup: %1", complete_backup);
// read archive contents file
string archivefs = (string) SCR::Read(.target.string, tempdir + "/info/files");
if (archivefs != nil)
{
archivefiles = splitstring(archivefs, "\n");
archivefiles = filter(string pk, archivefiles, ``{return pk != "" && pk != nil;});
}
y2debug("final archivefiles: %1", archivefiles);
// read installed packages
string installedpkgs_str = (string) SCR::Read(.target.string, tempdir + "/info/installed_packages");
// convert string to list
list<string> installedpkgs_list = splitstring(installedpkgs_str, "\n");
installedpkgs_list = filter(string pk, installedpkgs_list, ``{return pk != "" && pk != nil;});
// convert list to map (key - package name, value - package version)
installedpkgs = $[];
foreach(string fullname, installedpkgs_list, ``{
string version = regexpsub(fullname, ".*-(.*-.*)", "\\1");
string name = substring(fullname, 0, size(fullname) - size(version) - 1);
installedpkgs = add(installedpkgs, name, version);
}
);
if (compressed_packages_info == true)
{
y2milestone("Unpacking packages_info.gz file");
// uncompress compressed package info
SCR::Execute(.target.bash, "/usr/bin/gunzip -c " + tempdir + "/info/packages_info.gz > " + tempdir + "/info/packages_info");
y2milestone("File unpacked");
}
// covert package info file to YCP structure by perl script
y2milestone("Converting package info");
SCR::Execute(.target.bash, "/usr/lib/YaST2/bin/restore_parse_pkginfo.pl < " + tempdir + "/info/packages_info > " + tempdir + "/info.ycp");
y2milestone("Reading package info");
archive_info = (map<string,map<string, any> >) SCR::Read(.target.ycp, tempdir + "/info.ycp");
y2milestone("Read %1 packages", size(archive_info));
// read actual installed packages
GetActualInstalledPackages();
map<string, map<string, string> > mismatched = GetMismatchedPackages();
// add package descriptions and default selection
archive_info = mapmap(string p, map<string, any> i, archive_info, ``{
// decription of files not owned by any package
string descr = (p == "") ? _("Files not owned by any package") : Pkg::PkgSummary(p);
if (i == nil)
{
i = $[];
}
if (descr == nil)
{
descr = "";
}
map<string, any> t = add(i, "descr", descr);
string sel_type = " ";
if (Mode::config () == false)
{
// set default selection to "X" (package is installed) or " " (package is not installed)
if (p == "")
{
// set "no package" default value to "X"
sel_type = "X";
}
else
{
sel_type = (haskey(actualinstalledpackages, p) && !haskey(mismatched, p)) ? "X" : " ";
}
}
else
{
// in autoinstall-config mode leave preselected value
sel_type = i["sel_type"]:" ";
}
t = add(t, "sel_type", sel_type);
return $[p: t];
}
);
if (Mode::config () == true)
{
// refresh file selection in autoinstall config mode
y2debug("Setting selection: %1", autoinst_info);
SetSelectionProperty(autoinst_info);
}
y2milestone("values from archive: date=%1, comment=%2, hostname=%3", date, comment, hostname);
y2debug("installed packages at backup time: %1", installedpkgs);
y2debug("actual installed packages: %1", GetActualInstalledPackages());
return true;
}
/**
* Set settings
* @param settings Map with settings: start lilo, run SuSEconfig, restore RPM db
*/
global define void Set(map settings) ``{
y2milestone("Using settings: %1", settings);
runbootloader = settings["runbootloader"]:true;
runSuSEconfig = settings["runSuSEconfig"]:true;
restoreRPMdb = settings["restoreRPMdb"]:false;
completerestoration = settings["completerestoration"]:true;
list<string> archiveslist = settings["archives"]:[];
inputname = archiveslist[0]:"";
// read archives
if (size(archiveslist) == 0)
{
// set unconfigured state
inputname = "";
archive_info = $[];
}
// set restore selection
SetSelectionProperty(settings["selection"]:$[]);
// store settings - file selection will be refreshed after archive selection
// autoinstall config mode
autoinst_info = settings["selection"]:$[];
y2debug("setting autoinst_info: %1", autoinst_info);
y2debug("archive_info: %1", archive_info);
}
/**
* Get all restore settings - for use by autoinstallation
* @param settings The YCP structure to be imported
* @return boolean True on success
*/
global define boolean Import(map settings) ``{
if (settings == nil)
{
return false;
}
Set(settings);
return true;
}
/**
* Dump the restore settings to a single map - for use by autoinstallation.
* @return map Dumped settings (later acceptable by Import ())
*/
global define map Export() ``{
map selection = $[];
if (inputname == "")
{
// unconfigured
return $[];
}
foreach(string package, map info, archive_info, ``{
string sel_type = info["sel_type"]:" ";
list sel_file = info["sel_file"]:[];
if (package == "")
{
package = nopackage_id;
}
selection = add(selection, package, $[ "sel_type" : sel_type, "sel_file" : sel_file ]);
}
);
return $[
"archives" : prepend(inputvolumes, inputname),
"runbootloader" : runbootloader,
"runSuSEconfig" : runSuSEconfig,
"restoreRPMdb" : restoreRPMdb,
"completerestoration" : completerestoration,
"selection" : selection
];
}
/**
* Return restore configuration
* @return map Map $[ "packagename" : $["vers" : "version", "files" : ["files in the archive"], "prefix" : "installprefix", "descr" : "Short description", "sel_type" : "X", "sel_file" : ["selected files to restore"] ] ], possible values for "sel_type" key are: "X" - restore all files from archive, " " - do not restore this package, "P" - partial restore, restore only selected files at "sel_file" key. Package name "" means files not owned by any package.
*/
global define map<string, map<string, any> > GetArchiveInfo() ``{
return archive_info;
}
/**
* Return number of packages which will be restored from archive
* @return integer Total selected packages
*/
global define integer TotalPackagesToRestore() ``{
integer total = 0;
// filter out unselected packages
foreach(string p, map info, archive_info, ``{
string sel_type = info["sel_type"]:" ";
if (sel_type == "X" || sel_type == "P")
{
total = total + 1;
}
}
);
return total;
}
/**
* Return number of files which will be unpacked from archive
* @return integer Total selected files
*/
global define integer TotalFilesToRestore() ``{
integer total = 0;
// filter out unselected packages and compute total restored files
foreach(string p, map info, archive_info, ``{
string sel_type = info["sel_type"]:" ";
if (sel_type == "X")
{
total = total + size(info["files"]:[]);
}
else if (sel_type == "P")
{
total = total + size(info["sel_file"]:[]);
}
}
);
return total;
}
/**
* Activate boot loader configuration if requested.
* Uses variable Restore::runbootloader
* @return boolean true on success
*/
global define boolean ActivateBootloader() ``{
boolean ret = nil;
// disable Bootloader's progress bar
Progress::off();
if (runbootloader == true && Mode::test () == false)
{
y2milestone("activating boot loader configuration");
// Bootloader function calls were moved to a client
// to break the build-dependency on yast2-bootloader
ret = (boolean) WFM::call ("restore_bootloader");
if (ret == false)
{
// error popup message
Report::Error(_("Boot loader configuration failed."));
}
y2milestone("boot loader activated: %1", ret);
}
// re-enable progress bar
Progress::on();
return ret;
}
/**
* Restore files from archive
* @param abort This block is periodically evaluated, if it evaluates to true restoration will be aborted. It should be something like ``{return UI::PollInput () == `abort;} if UI exists or ``{ return false; } if there is no UI (abort will not be possible).
* @param progress Id of progress bar or nil.
* @param targetdir Directory to which files from archive will be upacked
* @return map Map $[ "aborted" : boolean, "restored" : [ "restored file" ], "failed" : [ "failed file" ] ]
*/
global define map Write(block<boolean> abort, symbol progress, string targetdir) ``{
map<string, map> restore = archive_info;
integer total = 0;
integer restoredpackages = 0;
list<string> restoredfiles = [];
list<string> failedfiles = [];
boolean aborted = false;
if (size(targetdir) == 0 || substring(targetdir, 0, 1) != "/")
{
// error message, %1 is directory
Report::Error(sformat(_("Invalid target directory (%1)."), targetdir));
return $[ "aborted" : aborted, "restored" : restoredfiles, "failed" : failedfiles, "packages" : restoredpackages, "bootloader" : false ];
}
// create target directory if it doesn't exist
integer out = (integer)SCR::Execute(.target.bash, "/bin/mkdir -p " + targetdir);
if (out != 0)
{
// error message
Report::Error( Message::UnableToCreateDirectory(targetdir) );
}
// filter out unselected packages and compute total restored files
restore = filter(string p, map info, restore, ``{
string sel_type = info["sel_type"]:" ";
if (completerestoration) sel_type = "X";
if (sel_type == "X")
{
total = total + size(info["files"]:[]);
return true;
}
else if (sel_type == "P")
{
total = total + size(info["sel_file"]:[]);
return true;
}
else
{
return false;
}
}
);
y2milestone("%1 files will be restored from archive", total);
list<string> packages = [];
foreach(string package, map info, restore, ``{
packages = add(packages, package);
}
);
integer i = 0;
symbol ret = `next;
if (restoreRPMdb == true)
{
string rpm_backup_script = "/etc/cron.daily/suse.de-backup-rpmdb";
// backup existing RPM DB - use aaa_base script which is started from cron
if (SCR::Read(.target.size, rpm_backup_script) > 0)
{
y2milestone("Startting RPM backup script (%1)", rpm_backup_script);
integer result = (integer) SCR::Execute(.target.bash, rpm_backup_script);
y2milestone("RPM backup exit value: %1", result);
}
else
{
y2warning("RPM DB backup script (%1) was not found, DB was not backed up!", rpm_backup_script);
}
string stat = archive_info["", "sel_type"]:" ";
y2warning("NOPACKAGE status: '%1'", stat);
y2warning("NOPACKAGE files: '%1'", archive_info["", "files"]:[]);
if (stat == " " || stat == "P")
{
// RPM DB restoration is selected, but files not owned by any package are deselected
// uncompress only RPM DB files, which are in directory /var/lib/rpm
list<string> RPM = [];
// search RPM DB files
foreach(string f, archive_info["", "files"]:[], ``{
if (regexpmatch(f, "^/var/lib/rpm/") == true)
{
RPM = add(RPM, f);
}
}
);
y2warning("found RPM DB files: %1", RPM);
if (stat == " ")
{
map in = (map)eval(archive_info[""]:$[]);
in["sel_type"] = "P";
in["sel_file"] = RPM;
// set new values
restore[""] = (map)eval(in);
total = total + size(RPM);
}
else
{
// may be some files are already selected
// check status of each file
list<string> already_selected = archive_info["","sel_file"]:[];
foreach(string rpm_file, RPM, ``{
if (!contains(already_selected, rpm_file))
{
already_selected = add(already_selected, rpm_file);
total = total + 1;
}
}
);
map in = (map)eval(archive_info[""]:$[]);
in["sel_file"] = already_selected;
// set new values
restore[""] = (map)eval(in);
}
}
}
y2warning("packages: %1", packages);
while (i < size(packages))
{
string package = packages[i]:"nonexistingpackage";
map info = restore[package]:$[];
// progress bar label
string label = (package == "") ? _("Restoring files not owned by any package...") :
// progress bar label - %1 is package name
sformat(_("Restoring package %1..."), package);
string sel_type = info["sel_type"]:"";
if (progress != nil)
{
// update name of package
UI::ChangeWidget(`id(progress), `Label, label);
UI::ChangeWidget(`id(progress), `Value, i);
}
if (Mode::test () == true)
{
// delay
sleep(300);
}
else
{
// y2logs will be probably overwritten - backup them
if (package == "" && targetDirectory == "/")
{
integer copy = (integer) SCR::Execute(.target.bash, "cp -r /var/log/YaST2 /var/log/YaST2.before_restore");
y2milestone("copy y2logs to /var/log/YaST2.before_restore: ret=%1", copy);
}
// get subarchive name
string name = (package == "") ? "NOPACKAGE" : (package + "-" + info["vers"]:"");
string fileinarchive = "";
foreach(string f, archivefiles, ``{
if (regexpmatch(f, "^" + name + "-........-.\\.s{0,1}tar.*") == true)
{
y2debug("package %1 is in archive file %2", package, f);
fileinarchive = f;
}
}
);
// BNC #460674, Do not change the system locale
// It can change I18N characters in output
string locale_modifications = "";
if (fileinarchive == "")
{
y2error("Can't find subarchive for package %1", package);
}
else
{
// unpack subarchive at background
boolean started = nil;
if (IsMultiVolume() == true)
{
string param = " ";
foreach(string f, volumeparts, ``{
param = param + "-f " + f + " ";
}
);
string command = locale_modifications + "echo q | /bin/tar -C " + tempdir + " -x -M " + param + fileinarchive + " 2> /dev/null";
y2milestone ("Running command: %1", command);
started = (boolean) SCR::Execute(.background.run, command);
}
else
{
string command = locale_modifications + "/bin/tar -C " + tempdir + " -x -f " + filename + " " + fileinarchive;
y2milestone ("Running command: %1", command);
started = (boolean) SCR::Execute(.background.run, command);
}
// abort test cycle
while((boolean) SCR::Read(.background.isrunning))
{
sleep(100);
aborted = eval(abort);
if (aborted == true)
{
y2warning("Restoration aborted!");
SCR::Execute(.background.kill, nil); // kill subprocess
break;
}
}
// break all packages cycle
if (aborted == true)
{
break;
}
// set compression parameter
string compress = "";
// use star archiver
boolean star = false;
if (regexpmatch(fileinarchive, ".*\\.tar\\.gz$") == true)
{
compress = "-z";
}
else if (regexpmatch(fileinarchive, ".*\\.tar\\.bz2$") == true)
{
compress = "-j";
}
else if (
regexpmatch(fileinarchive, ".*\\.star$") ||
regexpmatch(fileinarchive, ".*\\.star\\.gz$") ||
regexpmatch(fileinarchive, ".*\\.star\\.bz2$")
== true)
{
// star can autodetect used compression
compress = "";
star = true;
}
y2debug("compress: %1", compress);
y2debug("star: %1", star);
y2debug("fileinarchive: %1", fileinarchive);
string RPMdb = (restoreRPMdb) ? "" : (star ? "-not pat=var/lib/rpm" : "--exclude var/lib/rpm");
// files to unpack, "" means all files
string unpackfiles = "";
// select files to unpack
if (sel_type == "P")
{
// strip leading '/'
foreach (string f, info["sel_file"]:[], ``{
if (size(f) > 1) {
// remove a leading slash
if (substring (f, 0, 1) == "/") f = substring (f, 1);
// every single entry must be quoted
unpackfiles = unpackfiles + " '" + String::Quote (f) + "'";
}
}
);
}
// FIXME: use list of files
// for star: list=filename
// for tar: --files-from=filename
// create (s)tar command
string tarcommand = (star == false) ? (locale_modifications + "/bin/tar -C " + targetdir + " "
+ compress + " -x -v -f " + tempdir + "/" + fileinarchive + " " + RPMdb
+ " " + unpackfiles + " 2> " + tempdir + "/tar.stderr > " + tempdir
+ "/tar.stdout")
: (locale_modifications + "/usr/bin/star -C " + targetdir + " "
+ compress + " -x -v -U -f " + tempdir + "/" + fileinarchive + " " + RPMdb
+ " " + unpackfiles + " 2> " + tempdir + "/tar.stderr > " + tempdir
+ "/tar.stdout");
// -U option: replace existing files
y2milestone("tarcommand: %1", tarcommand);
// check whether star is installed
if (SCR::Read(.target.size, "/usr/bin/star") < 0 && star == true)
{
// remove short
string cont = Label::ContinueButton();
cont = mergestring(splitstring(cont, "&"), "");
// popup message text - ask to install 'star' package
// %1 is translated 'Continue' label
boolean inst = Popup::ContinueCancel(sformat(_("Package star is needed to extract
files from the archive.
Press %1 to install this package.
"), cont));
if (inst == true)
{
// install star package
// initialize package manager
Pkg::SourceStartCache(true);
Pkg::TargetInit ("/", false);
boolean ok = true;
// select star package to installation
ok = Pkg::PkgInstall("star");
// solve dependencies
ok = ok && Pkg::PkgSolve(false);
// perform installation (0 means install from all media)
list result = Pkg::PkgCommit(0);
// check result
y2debug ("PkgCommit: %1", result);
inst = ok && result[1]:nil == [] && result[2]:nil == [];
}
// abort restoration if star installation failed
if (inst == false)
{
// error popup message
Report::Error(_("Package star is not installed.
Press OK to exit.
"));
y2error("star package wasn't installed - aborting");
return $[ "aborted" : true, "restored" : restoredfiles, "failed" :
failedfiles, "packages" : restoredpackages, "bootloader" : false ];
}
}
// start subprocess
started = (boolean) SCR::Execute(.background.run_output, tarcommand);
y2milestone("Tar command started: %1", started);
while(SCR::Read(.background.isrunning) == true)
{
sleep(100); // small delay
aborted = eval(abort);
if (aborted == true)
{
y2warning("Restoration aborted!");
SCR::Execute(.background.kill, nil); // kill subprocess
break;
}
}
// read restored files
list<string> stdout = splitstring((string) SCR::Read(.target.string, tempdir + "/tar.stdout"), "\n");
// remove empty lines
if (stdout != nil)
{
stdout = filter(string line, stdout, ``{return size(line) > 0;});
}
// star has more verbose output - get only file names
if (star && stdout != nil)
{
list<string> filteredstdout = [];
foreach(string line, stdout,
``{
string new = regexpsub(line, "x (.*) .* bytes, .* tape blocks", "\\1");
if (new != nil)
{
filteredstdout = add(filteredstdout, new);
}
}
);
stdout = filteredstdout;
}
y2debug("stdout: %1", stdout);
if (stdout != nil)
{
restoredfiles = (list<string>)merge (restoredfiles, stdout);
}
// read failed files
list<string> stderr = splitstring((string) SCR::Read(.target.string, tempdir + "/tar.stderr"), "\n");
// remove empty lines
stderr = filter(string line, stderr, ``{return size(line) > 0;});
integer packagefailedfiles = 0;
if (stderr != [])
{
y2warning("stderr: %1", stderr);
}
// add file names to failedfiles
foreach(string line, stderr, ``{
y2warning("line: %1", line);
if (line != nil && line != "")
{
string file = regexpsub(line, "s{0,1}tar: (.*)", "\\1");
// ignore final star summary
if (file != nil && !regexpmatch(line, "star: .* blocks \\+ .* bytes \\(total of .* bytes = .*k\\)\\.")
&& !regexpmatch(line, "star: WARNING: .*")
)
{
failedfiles = add(failedfiles, file);
packagefailedfiles = packagefailedfiles + 1;
y2warning("Restoration of file %1 failed", file);
}
}
}
);
// package restoration failed
if (restoredfiles != nil && packagefailedfiles == size(restoredfiles))
{
y2warning("failed package: %1", package);
}
else
{
restoredpackages = restoredpackages + 1;
}
if (aborted == true)
{
break;
}
}
}
i = i + 1;
}
// sort list of restored files and remove duplicates (caused by multiple unpacking)
restoredfiles = toset(restoredfiles);
// remove failed files
restoredfiles = filter(string f, restoredfiles, ``{
return !contains(failedfiles, f);
}
);
if (runbootloader == true && progress != nil)
{
// progess bar label
UI::ChangeWidget(`id(progress), `Label, _("Configuring boot loader..."));
UI::ChangeWidget(`id(progress), `Value, i);
}
boolean bootload = ActivateBootloader();
return $[ "aborted" : aborted, "restored" : restoredfiles, "failed" : failedfiles, "packages" : restoredpackages, "bootloader" : bootload ];
}
/**
* Read next volume of multi volume archive
* @param input Archive name in URL-like syntax
* @return map Map $[ "success" : boolean (true on success), "lastvolume" : boolean (true if archive is last volume) ]
*/
global define map ReadNextVolume(string input) ``{
// umount mounted file system
Umount();
boolean ret = false;
boolean last = true;
// mount source
map mount = MountInput(input);
if (mount["success"]:false == true)
{
mountpoint = mount["mpoint"]:"";
// add mounted file
map addvol = AddVolume(mount["file"]:"dummy");
ret = addvol["success"]:false;
last = addvol["lastvolume"]:true;
if (ret == true)
{
inputvolumes = add(inputvolumes, input);
}
}
return $[ "success" : ret, "lastvolume" : last ];
}
/**
* Test all volumes together
* @return boolean True: all volumes are OK, false: an error occured
*/
global define boolean TestAllVolumes() ``{
boolean ret = false;
if (size(volumeparts) > 0)
{
string param = "";
foreach(string f, volumeparts, ``{
param = param + "-f " + f + " ";
}
);
// echo 'q' to tar stdin - if error occurs tar asks for next volume, q means quit
integer exit = (integer) SCR::Execute(.target.bash, "echo q | /bin/tar -t -M " + param);
y2milestone("Test result: %1", exit);
ret = (exit == 0);
}
return ret;
}
/**
* Clear all archive settings
*/
global define void ResetArchiveSelection() ``{
// clear selected archive
filename = "";
inputname = "";
// clear content of archive
archivefiles = [];
// clear installed packages at backup time
installedpkgs = $[];
// clear archive selection
archive_info = $[];
// clear archive info
date = "";
hostname = "";
comment = "";
// clear list of files
volumeparts = [];
Umount();
}
/**
* Clear all settings (archive and list of installed packages)
*/
global define void ResetAll() ``{
ResetArchiveSelection();
// clear list of installed packages
actualinstalledpackages = $[];
}
/**
* Remove shortcut mark from string
* @param scut string with shortcut mark (&)
* @return string result
*/
define string RemoveShortCut(string scut) ``{
return mergestring(splitstring(scut, "&"), "");
}
/**
* Convert boolean value to translated "yes" or "no" string
* @param b input value
* @return string translated Yes/No string
*/
define string yesno(boolean b) ``{
string ret = "?";
if (b == true)
{
ret = _("Yes");
}
if (b == false)
{
ret = _("No");
}
return ret;
}
/**
* Create restore configuration summary. Used in autoinstallation restore module configuration.
* @return string rich text summary
*/
global define string Summary() ``{
if (inputname == "")
{
// not configured yet
return Summary::NotConfigured();
}
else
{
// Summary text header
string archives_info = "<P><B>" + _("Backup Archive") + "<B></P>";
archives_info = archives_info + "<P>" + inputname;
foreach(string vol, inputvolumes, ``{
archives_info = archives_info + vol + "<BR>";
}
);
archives_info = archives_info + "</P>";
// Summary text header
string options_info = "<P><B>" + _("Restore Options") + "<B></P>";
options_info = options_info + "<P>" + RemoveShortCut(_("Activate &boot loader configuration after restoration")) + ": " + yesno(runbootloader) + "<BR>"
+ RemoveShortCut(_("Run &SuSEconfig after restoration")) + ": " + yesno(runSuSEconfig) + "<BR>"
+ RemoveShortCut(_("Restore RPM &database (if present in archive)")) + ": " + yesno(restoreRPMdb) + "<BR></P>";
// summary text heading
string selection_info = "<P><B>" + _("Packages to Restore") + "</B></P><P>";
if (completerestoration)
{
// part of the summary text
selection_info = selection_info + _("<I>Restore all files from the archive</I>");
}
else if (archive_info != nil)
{
foreach(string p, map info, archive_info, ``{
if (p == "")
{
p = _("--No package--");
}
if (info["sel_type"]:" " != " ")
{
selection_info = selection_info + p + "<BR>";
}
}
);
}
selection_info = selection_info + "</P>";
return archives_info + options_info + selection_info;
}
}
global define map<string, any> ProposeRPMdbRestoration() ``{
map<string, map> extra = GetExtraPackages();
map<string, map> missing = GetMissingPackages();
map<string, map<string, string> > mismatched = GetMismatchedPackages();
map<string, map> selected = GetSelectedPackages();
// proposed DB restoration
boolean propose = nil;
y2debug("extra: %1, missig: %2", extra, missing);
list selected_list = [];
foreach(string p, map in, selected, ``{
// store package name and vesion to the list
selected_list = add(selected_list, p + "-" + in["vers"]:"");
}
);
y2debug("selected_list: %1", selected_list);
// propose restoration when:
// - missing packages contain only fully backed packages
// - all fully backed packages will be restored
// - there is no extra package
if (size(extra) == 0)
{
boolean all = true;
y2debug("size(extra): %1", size(extra));
foreach(string fullpkg, complete_backup, ``{
// integer pos = findlastof(fullpkg, "-");
// string basename = substring(fullpkg, pos);
// string version = substring(fullpkg, 0, pos);
if (!contains(selected_list, fullpkg))
{
all = false;
}
}
);
y2debug("all completely packages selected: %1", all);
if (all == true)
{
// check missing packages - it should contain only completely backed up packages
foreach(string pkg, map inf, missing, ``{
string fpkg = pkg + "-" + inf["vers"]:"";
if (!contains(complete_backup, fpkg))
{
all = false;
}
}
);
if (all == true)
{
y2debug("only fully backed up packages are missing");
// RPM DB restoration is recommended
return $["proposed" : true];
}
}
}
boolean ok = true;
// propose no restoration if only installed packages will be restored
foreach(string pk, map inf, selected, ``{
string version = inf["vers"]:"";
// selected packages are not missing
if (ok && (missing[pk]:nil != nil || mismatched[pk]:nil != nil))
{
ok = false;
}
}
);
if (ok == true)
{
return $["proposed" : false];
}
// there will be inconsistency between RPM DB and system,
// some packages will be in DB but no files will be present
// or there will be files without RPM entry or both
// it is also possible that package versions do not match
map<string, string> different_versions = $[]; // package in system => package in RPM in the archive
list not_in_system = []; // package is in DB in the archive, but not in the system and it won't be restored
list not_in_archiveDB = []; // not in the archive, but in the system
foreach(string p, map<string,string> inf, mismatched, ``{
if (selected[p]:nil != nil)
{
// different_versions[p + "-" + selected["vers"]:""] = p + "-" + inf["inst"]:"";
different_versions[p] = p + "-" + inf["inst"]:"";
}
}
);
foreach(string p, map inf, missing, ``{
not_in_system = add(not_in_system, p + "-" + inf["vers"]:"");
}
);
foreach(string p, map inf, extra, ``{
not_in_archiveDB = add(not_in_archiveDB, p + "-" + inf["vers"]:"");
}
);
// both possible operations (restoration or preserving RPM DB)
// will cause to inconsistency in system
return $["proposed" : nil, "mismatched" : different_versions, "missing" : missing, "extra" : extra];
}
global define boolean RPMrestorable() ``{
list nopkgfiles = archive_info["", "files"]:[];
return contains(nopkgfiles, "/var/lib/rpm/Packages");
}
}
ACC SHELL 2018