ACC SHELL

Path : /usr/share/YaST2/modules/
File Upload :
Current File : //usr/share/YaST2/modules/FileChanges.ycp

/**
 * File:	modules/FileChanges.ycp
 * Module:	yast2
 * Summary:	Detect if a configuratil file was changed
 * Authors:	Jiri Srain <jsrain@suse.cz>
 *
 * Support routines for detecting changes of configuration files being done
 * externally (not by YaST) to prevent the changes from being lost because
 * of YaST not handling the configuration files correctly (eg. removing
 * comments in some cases, changing order of options,...)
 *
 * Warns user if such change is detected.
 *
 * Usage:
 * Before reading the configuration file:
 *   call boolean CheckFiles (list<string>) with all files. If any of them
 *   is detected to be changed, YaST asks a popup for you.
 *   alternatively use boolean FileChanged (string) for each file (does not
 *   ask any question, immediatelly returns status of the file
 *
 * After writing the configuraiton file:
 *   call void StoreFileCheckSum (string) for each file to store recent
 *   checksum. YaST will use this checksum next time checking.
 *
 */

{

module "FileChanges";

textdomain "base";

import "Mode";
import "Popup";
import "Directory";
import "Label";

string data_file = "/var/lib/YaST2/file_checksums.ycp";

map<string,string> file_checksums = $[];

/**
 * Read the data file containing file checksums
 */
void ReadSettings () {
    if ((integer)SCR::Read (.target.size, data_file) <= 0)
    {
	file_checksums = $[];
	return;
    }
    file_checksums = (map<string,string>)SCR::Read (.target.ycp, data_file);
    if (file_checksums == nil)
	file_checksums = $[];
}

/**
 * Write the data file containing checksums
 */
void WriteSettings () {
    SCR::Write (.target.ycp, data_file, file_checksums);
}

/**
 * Compute the checksum of a file
 * @param file string the file to compute checksum of
 * @return string the checksum
 */
string ComputeFileChecksum (string file) {
    // See also FileUtils::MD5sum()
    string cmd = sformat ("/usr/bin/md5sum %1", file);
    map out = (map)SCR::Execute (.target.bash_output, cmd);
    // note: it also contains file name, but since it is only to be compared
    // it does not matter
    string sum = out["stdout"]:"";
    return sum;
}

/**
 * Check if file was modified compared to the one distributed
 * with the RPM package
 * @param file string the file to check
 * @return boolean true of was changed
 */
boolean FileChangedFromPackage (string file) {
    // queryformat: no trailing newline!
    string cmd = sformat ("/bin/rpm -qf %1 --qf %%{NAME}-%%{VERSION}-%%{RELEASE}", file);
    map out = (map)SCR::Execute (.target.bash_output, cmd);
    string package = out["stdout"]:"";
    y2milestone ("Package owning %1: %2", file, package);
    if (package == "")
	return false;
    cmd = sformat ("rpm -V %1 |grep ' %2$'", package, file);
    out = (map)SCR::Execute (.target.bash_output, cmd);
    string changes = out["stdout"]:"";
    y2milestone ("File possibly changed: %1", changes);
    list<string> lines = splitstring (changes, "\n");
    boolean changed = false;
    foreach (string line, lines, {
	if (regexpmatch (line, "^S"))
	    changed = true;
	if (regexpmatch (line, "^..5"))
	    changed = true;
	if (regexpmatch (line, "^.......T"))
	    changed = true;
    });
    return changed;
}

/**
 * Check if a file was modified externally (without YaST)
 * @param file string boolean the file to check
 * @return boolean true if was changed externally
 */
global boolean FileChanged (string file) {
    // when generating AutoYaST configuration, they are not written back
    if (Mode::config ())
	return false;
    ReadSettings ();
    boolean ret = false;
    if (haskey (file_checksums, file))
    {
	y2milestone ("Comparing file %1 to stored checksum", file);
	string sum = ComputeFileChecksum (file);
	ret = ! (sum == file_checksums[file]:"");
    }
    else
    {
	y2milestone ("Comparing file %1 to RPM database", file);
	ret = FileChangedFromPackage (file);
    }
    y2milestone ("File differs: %1", ret);
    return ret;
}

/**
 * Store checksum of a file to the store
 * @param file string filename to compute and store
 */
global void StoreFileCheckSum (string file) {
    ReadSettings ();
    string sum = ComputeFileChecksum (file);
    file_checksums[file] = sum;
    WriteSettings ();
}

/**
 * Check files if any of them were changed
 * Issue a question whether to continue if some were chaned
 * @param files a list of files to check
 * @return boolean true if either none was changed or user agreed
 *  to continue
 */
global boolean CheckFiles (list<string> files) {
    files = filter (string f, files, { return FileChanged (f); });
    if (size (files) > 0)
    {
	// Continue/Cancel question, %1 is a file name
	string msg = _("File %1 has been changed manually.
YaST might lose some of the changes");
	if (size (files) > 1)
	    // Continue/Cancel question, %1 is a coma separated list of file names
	    msg = _("Files %1 have been changed manually.
YaST might lose some of the changes");
	msg = sformat (msg, mergestring (files, ", "));
	string popup_file = "/filechecks_non_verbose";
	if ($[] == SCR::Read (.target.stat, Directory::vardir + popup_file))
	 {
	  term content = `VBox(
		`Label(msg),
		`Left(`CheckBox(`id(`disable), _("Do not show this message anymore"))),
		`ButtonBox (
		    `PushButton(`id(`ok), `opt (`okButton), Label::ContinueButton()),
		    `PushButton(`id(`cancel), `opt (`cancelButton), Label::CancelButton())
		)
	    );
	  UI::OpenDialog(content);
	  UI::SetFocus(`ok);
	  any ret=UI::UserInput();
	  y2milestone("ret = %1", ret);
	  if (ret==`ok && (boolean)UI::QueryWidget(`disable, `Value)){
	   y2milestone("Disabled checksum popups");
	   SCR::Write ( .target.string, Directory::vardir + popup_file, "");
	   }
	  UI::CloseDialog();
	  if (ret==`ok)return true;
		else return false;
	 }
//			return Popup::ContinueCancel (msg);
		else return true;
    }
    return true;
}

}

ACC SHELL 2018