ACC SHELL

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

/**
 * File:	modules/Profile.ycp
 * Module:	Auto-Installation
 * Summary:	Profile handling
 * Authors:	Anas Nashif <nashif@suse.de>
 *
 * $Id: Profile.ycp 61506 2010-03-26 08:57:51Z ug $
 */
{
    module "Profile";
    textdomain "autoinst";

    import "Stage";
    import "Mode";
    import "AutoinstConfig";
    import "XML";
    import "Label";
    import "Popup";
    import "ProductControl";
    import "Directory";
    import "FileUtils"; 
    import "PackageSystem";

    include "autoinstall/xml.ycp";


    // The Complete current Profile
    global map<string,any> current = $[];

    // defined in Y2ModuleConfig
    global map<string, map> ModuleMap = $[];


    global string Version = "";

    string Description = "";

    global boolean changed = false;

    global boolean prepare = true;

    /**
     * Constructor
     * @return void
     */
    global define void Profile ()
    {
        //
        // setup profile XML parameters for writing
        //
        profileSetup();
        if (Stage::initial ())
        {
            SCR::Execute(.target.mkdir, AutoinstConfig::profile_dir);
        }
        return;
    }


    /**
     * Detect Version
     * @return string
     */
    define string DetectVersion() {
        return "";
    }

    /**
     * compatibility to new storage lib in 10.0
     */
    define void storageLibCompat() {
        list<map> newPart = [];
        foreach( map d, current["partitioning"]:[], {
            if( haskey(d, "is_lvm_vg") && d["is_lvm_vg"]:false == true ) {
                d = remove( d, "is_lvm_vg" );
                d["type"] = `CT_LVM;
            } else if( haskey(d, "device") && d["device"]:"" == "/dev/md" ) {
                d["type"] = `CT_MD;
            } else if( ! haskey(d,"type") ) {
                d["type"] = `CT_DISK;
            }

            // actually, this is not a compatibility hook for the new
            // storage lib. It's a hook to be compatibel with the autoyast
            // documentation for reusing partitions
            //
            d["partitions"] = maplist( map p, d["partitions"]:[], ``{
                if( haskey(p, "create") && p["create"]:true == false &&
                    haskey(p, "partition_nr") ) {
                    p["usepart"] = p["partition_nr"]:0; // useless default
                }
                if( haskey(p, "partition_id") ) {
                    // that's a bit strange. There is a naming mixup between
                    // autoyast and the storage part of yast. Actually filesystem_id
                    // does not make sense at all but in autoyast it is the
                    // partition id (maybe that's because yast calls
                    // the partition id "fsid" internally).
                    // partition_id in the profile does not work at all, so we copy
                    // that value to filesystem_id
                    p["filesystem_id"] = p["partition_id"]:0;
                }
                return p;
            });
            newPart = add( newPart, d );
        });
        y2milestone("partitioning is now %1",newPart);
        current["partitioning"] = newPart;
    }

    define void softwareCompat() {
        current["software"] = current["software"]:$[];
        if( !contains(current["software","packages"]:[], "autoyast2-installation") )
            current["software","packages"] = add( current["software","packages"]:[], "autoyast2-installation" );

        /* without autoyast2, <files ...> does not work */
        if( haskey( current, "files" ) && !contains(current["software","packages"]:[], "autoyast2") )
            current["software","packages"] = add( current["software","packages"]:[], "autoyast2" );

        /* workaround for missing "REQUIRES" in content file to stay backward compatible */
        /* FIXME: needs a more sophisticated or compatibility breaking solution after SLES11 */
        if( size( current["software","patterns"]:[] ) == 0 )
            current["software","patterns"] = [ "base" ];
     }

    /**
     * compatibility to new language,keyboard and timezone client in 10.1
     */
    global define void generalCompat() {
        if( haskey( current, "general" ) ) {
            if( haskey( current["general"]:$[], "keyboard" ) ) {
                current["keyboard"] = current["general","keyboard"]:$[];
                current["general"] = remove( current["general"]:$[], "keyboard" );
            }
            if( haskey( current["general"]:$[], "language" ) ) {
                current["language"] = $[ "language":current["general","language"]:"" ];
                current["general"] = remove( current["general"]:$[], "language" );
            }
            if( haskey( current["general"]:$[], "clock" ) ) {
                current["timezone"] = current["general","clock"]:$[];
                current["general"] = remove( current["general"]:$[], "clock" );
            }
            if( current["general","mode","final_halt"]:false ) {
                map script = $[ "filename":"zzz_halt", "source":"chkconfig autoyast off\nshutdown -h now" ];
                if( ! haskey( current, "scripts" ) )
                    current["scripts"] = $[];
                if( ! haskey( current["scripts"]:$[], "init-scripts" ) )
                    current["scripts","init-scripts"] = [];
                current["scripts","init-scripts"] = add( current["scripts","init-scripts"]:[], script );
            }
            if( current["general","mode","final_reboot"]:false ) {
                map script = $[ "filename":"zzz_reboot", "source":"chkconfig autoyast off\nshutdown -r now" ];
                if( ! haskey( current, "scripts" ) )
                    current["scripts"] = $[];
                if( ! haskey( current["scripts"]:$[], "init-scripts" ) )
                    current["scripts","init-scripts"] = [];
                current["scripts","init-scripts"] = add( current["scripts","init-scripts"]:[], script );
            }
            if( haskey( current["software"]:$[], "additional_locales" ) ) {
                if( ! haskey(current, "language") ) {
                    current["language"] = $[];
                }
                current["language","languages"] = mergestring( current["software","additional_locales"]:[], "," );
                current["software"] = remove( current["software"]:$[], "additional_locales" );
            }
        }
    }

    /**
     * Read Profile properties and Version
     * @param map Profile Properties
     * @return void
     */
    global define void ReadProperties (map properties) {
        Version = properties["version"]:"";
        Description = properties["description"]:"";

        if (Version != "3.0" || Version == "")
        {
            Version = DetectVersion();
            if (Version == "")
            {
                y2milestone("Can't detect Profile Version");
                return;
            }
        } else {
            y2milestone("AutoYaST Profile Version  %1 Detected.", Version);
        }
        return;
    }



    /**
     * Import Profile
     * @param map profile
     * @return void
     */
    global define void Import(map<string, any> profile)
    {
        y2milestone("importing profile");
        current  = profile;

        ReadProperties(current["properties"]:$[]);

        // old style
        if (haskey(profile, "configure") || haskey(profile, "install")) {
            map _configure = profile["configure"]:$[];
            map _install = profile["install"]:$[];
            if( haskey(profile, "configure") )
                current = remove( current, "configure" );
            if( haskey(profile, "install") )
                current = remove( current, "install" );
            map tmp = (map<string, any>)union( _configure, _install );
            current = (map<string, any>)union( tmp, current );
        }

        // should not be needed anymore with new libsax
        //if (!current["x11", "configure_x11"]:false)
        //    ProductControl::DisableModule ("x11");

        // raise the network immediately after configuring it
        if( haskey(current, "networking") && ! haskey(current["networking"]:$[], "start_immediately") ) {
            current["networking","start_immediately"] = true;
            y2milestone("start_immediately set to true");
        }
        storageLibCompat(); // compatibility to new storage library (SL 10.0)
        generalCompat();    // compatibility to new language,keyboard and timezone (SL10.1)
        softwareCompat();

        y2debug("Current Profile=%1", current );
        return;
    }


    /**
     * Prepare Profile for saving and remove empty data structs
     * @return void
     */
    global define void Prepare()
    {
        if (!prepare)
            return;

        Popup::ShowFeedback(_("Collecting configuration data..."), 
                _("This may take a while"));

        list<string> e = [];

        foreach(string p, map d, ModuleMap, {
            //
            // Set resource name, if not using default value
            //
            string resource = d["X-SuSE-YaST-AutoInstResource"]:"";
            if (resource == "")
                resource = p;

            string tomerge = d["X-SuSE-YaST-AutoInstMerge"]:"";
            string module_auto =d["X-SuSE-YaST-AutoInstClient"]:"none";
            if ( (boolean) WFM::CallFunction(module_auto, ["GetModified"]))
            {
                any resource_data = WFM::CallFunction(module_auto, ["Export"]);

                integer s = 0;
                if ( tomerge == "") {
                    if ( d["X-SuSE-YaST-AutoInstDataType"]:"map" == "map" )
                    {
                        s = size((map)resource_data);
                    }
                    else
                    {
                        s = size((list)resource_data);
                    }
                }

                if ( s != 0 || tomerge != "")
                {

                    if (size(tomerge) > 0 )
                    {
                        integer i = 0;
                        string tomergetypes = d["X-SuSE-YaST-AutoInstMergeTypes"]:"";
                        list MergeTypes = splitstring(tomergetypes, ",");

                        foreach( string res, (list<string>)splitstring(tomerge, ",") , ``{
                            if ( MergeTypes[i]:"map" == "map")
                            {
                                map<string,any> rd = (map<string,any>)resource_data;
                                current[res] = rd[res]:$[];
                            } else {
                                map<string,any> rd =(map<string,any>)resource_data;
                                current[res] = rd[res]:[];
                            }
                            i = i + 1;
                        });
                    } else {
                            current[resource] = resource_data;
                    }
                }
                else if (s == 0 )
                {
                    e = add ( e, resource);
                }
            }
        });


        foreach(string k, any v, current, ``{
                if (!haskey(current, k) && !contains(e, k ))
                    current[k] = v;
        });

        Popup::ClearFeedback();
        prepare = false;
        return;

    }

    /**
     * Reset profile to initial status
     * @return void
     */
    global define void Reset ()
    {
        y2milestone("Resetting profile contents");
        current = $[];
        return;
    }

    /**
     * Save YCP data into XML
     * @param  path to file
     * @return	boolean true on success
     */
    global define boolean Save (string file)
    {
        Prepare();
        boolean ret = false;
        y2debug("Saving data (%1) to XML file %2", current, file);
        if( AutoinstConfig::ProfileEncrypted ) {
            string xml = XML::YCPToXMLString(`profile, current);
            if( size(xml) > 0 ) {
                if( AutoinstConfig::ProfilePassword == "" ) {
                    string p = "";
                    string q = "";
                    do {
                        UI::OpenDialog(
                                   `VBox(
                                         `Label(_("Encrypted AutoYaST profile. Please enter the password twice")),
                                         `Password(`id(`password), ""),
                                         `Password(`id(`password2), ""),
                                         `PushButton(`id(`ok), Label::OKButton())
                                         )
                                    );
                        any button = nil;
                        repeat {
                            button = UI::UserInput();
                            p = (string) UI::QueryWidget(`id(`password), `Value);
                            q = (string) UI::QueryWidget(`id(`password2), `Value);
                        } until ( button == `ok );
                        UI::CloseDialog();
                    } while( p != q );
                    AutoinstConfig::ProfilePassword = AutoinstConfig::ShellEscape((string)p);
                }
                string dir = (string)SCR::Read(.target.tmpdir);
                string command = sformat("gpg2 -c --armor --batch --passphrase \"%1\" --output %2/encrypted_autoyast.xml",
                                            AutoinstConfig::ProfilePassword,dir);
                SCR::Execute(.target.bash_input, command, xml);
                if( SCR::Read(.target.size, dir+"/encrypted_autoyast.xml") > 0 ) {
                    command = sformat( "mv %1/encrypted_autoyast.xml %2", dir, file );
                    SCR::Execute(.target.bash, command, $[]);
                    ret = true;
                }
            }
        } else {
            ret = XML::YCPToXMLFile(`profile, current,  file);
        }
        return ret;
    }

    /**
     * Save sections of current profile to separate files
     *
     * @param dir - directory to store section xml files in
     * @return	  - list of filenames
     */
    global define map<string,string> SaveSingleSections( string dir ){
       Prepare();
       y2milestone("Saving data (%1) to XML single files", current);
       map<string,string> sectionFiles = $[];
       foreach( string sectionName, any section, current, {
	  string sectionFileName = dir + "/" + sectionName + ".xml";
	  map<string,any> tmpProfile = $[ sectionName : section ];
	  if(XML::YCPToXMLFile(`profile, tmpProfile, sectionFileName)){
	    y2milestone( "Wrote section %1 to file %2", sectionName, sectionFileName );
	    sectionFiles = add(sectionFiles, sectionName, sectionFileName);
	  }
	  else{
	    y2error(sformat(_("Couldn't write section %1 to file %2"), sectionName, sectionFileName));
	  }
       });
       return sectionFiles;
    }

    /**
     * Save the current data into a file to be read after a reboot.
     * @param	-
     * @return  true on success
     * @see	Restore()
     */
    global define boolean SaveProfileStructure ( string parsedControlFile)
    {
        y2milestone("Saving control file in YCP format");
        return SCR::Write( .target.ycp, parsedControlFile, current );
    }

    /**
     * Read YCP data as the control file
     * @param ycp file
     * @return boolean
     */
    global define boolean ReadProfileStructure ( string parsedControlFile )
    {
        current = (map<string,any>) SCR::Read( .target.ycp, [ parsedControlFile, $[]] );
        if (current == $[])
            return false;
        else
            Import (current);

        return true;
    }


    /**
     * General compatibility issues
     * @param current profile
     * @return map converted profile
     */
    define map<string, any> Compat(map<string, any> _current)
    {
        // scripts
        if ( haskey(_current, "pre-scripts") ||
            haskey(_current, "post-scripts") ||
            haskey(_current, "chroot-scripts")	    )
        {
            list pre = _current["pre-scripts"]:[];
            list post = _current["post-scripts"]:[];
            list chroot = _current["chroot-scripts"]:[];
            map scripts = $[
                            "pre-scripts":pre,
                            "post-scripts":post,
                            "chroot-scripts":chroot
            ];
            _current = remove(_current, "pre-scripts");
            _current = remove(_current, "post-scripts");
            _current = remove(_current, "chroot-scripts");

            _current["scripts"] = scripts;
        }

        // general
        boolean old = false;

        map<string, any> general_options
                                = _current["general"]:$[];
        map security		= _current["security"]:$[];
        map report		= _current["report"]:$[];

        foreach(string k, any v, general_options , ``{
            if (k == "keyboard" && is(v, string))
                old = true;
            else if (k == "mouse" && is(v, string))
                old = true;
            else if (k == "encryption_method")
                old = true;
            else if (k == "timezone" &&  is(v, string))
                old = true;
        });

        map new_general = $[];

        if (old) {
            y2milestone("Old format, converting.....");
            new_general["language"]	= general_options["language"]:"";
            map keyboard = $[];
            keyboard["keymap"]		= general_options["keyboard"]:"";
            new_general["keyboard"]	= keyboard;

            map clock = $[];
            clock["timezone"]		= general_options["timezone"]:"";
            if ( general_options["hwclock"]:"" == "localtime")
            {
                clock["hwclock"] = "localtime";
            }
            else if  ( general_options["hwclock"]:""  == "GMT")
            {
                clock["hwclock"] = "GMT";
            }
            new_general["clock"]	= clock;

            map mode = $[];
            if (haskey(general_options, "reboot")) {
                mode["reboot"] =  general_options["reboot"]:false;
            }
            if (haskey(report, "confirm")) {
                mode["confirm"]	= report["confirm"]:false;
                report = remove(report, "confirm");
            }
            new_general["mode"]	= mode;


            if (haskey(general_options, "encryption_method"))
            {
                security["encryption"] = general_options["encryption_method"]:"";
            }

            map net = _current["networking"]:$[];
            list<map<string, string> > ifaces = net["interfaces"]:[];

            list newifaces = maplist(map<string, string> iface , ifaces, ``{
                map newiface = mapmap(string k, string v, iface, ``{
                    return ($[tolower(k): v]);
                });
                return newiface;
            });

            net["interfaces"] =  newifaces;

            _current["general"] = new_general;
            _current["report"] = report;
            _current["security"] = security;
            _current["networking"] = net;
        }

        return _current;
    }


    /**
     * Read XML into  YCP data
     * @param  path to file
     * @return	boolean
     */
    global define boolean ReadXML (string file) {
        // does not work here
        //if ( !FileUtils::Exists( file ) )
        //    return false;

        string tmp = (string)SCR::Read (.target.string, file);
        list<string> l = splitstring (tmp, "\n");
        if( tmp != nil && l[0]:"" == "-----BEGIN PGP MESSAGE-----" ) {
            map<string,any> out = $[];
            while( out["stdout"]:"" == "" ) {
                UI::OpenDialog(
                           `VBox(
                                 `Label(_("Encrypted AutoYaST profile. Please enter the correct password.")),
                                 `Password(`id(`password), ""),
                                 `PushButton(`id(`ok), Label::OKButton())
                                 )
                           );
                string p = "";
                any button = nil;
                repeat {
                    button = UI::UserInput();
                    p = (string) UI::QueryWidget(`id(`password), `Value);
                } until ( button == `ok );
                UI::CloseDialog();
                string command = sformat("gpg2 -d --batch --passphrase \"%1\" %2",p, file);
                out = (map<string,any>)SCR::Execute(.target.bash_output, command, $[]);
            }
            current = XML::XMLToYCPString( out["stdout"]:"" );
            AutoinstConfig::ProfileEncrypted = true;

            // FIXME: rethink and check for sanity of that!
            // save decrypted profile for modifying pre-scripts
            if( Stage::initial() )
                //SCR::Write(.target.string, AutoinstConfig::profile_dir+"/autoinst.xml", out["stdout"]:"");
                SCR::Write(.target.string, file, out["stdout"]:"");
        } else {
            y2debug("Reading %1", file);
            current = XML::XMLToYCPFile(file);
        }

        if ( current != $[]  && size(current) == 0 )
        {
        // autoyast has read the autoyast configuration file but something went wrong
        string message = _("The XML parser reported an error while parsing the autoyast profile. The error message is:\n");
        message = message + XML::XMLError();
        Popup::Error ( message );
            return (false);
        }
        Import (current);
        return (true);
    }


    define map<string,any> setMValue( list<any> l, any v, map<string,any> m );
    define list<any> setLValue( list<any> l, any v, list<any> m );


    /*
        setMValue together with setLValue are helper functions for
        setElementyList
    */
    map<string,any> setMValue( list<any> l, any v, map<string,any> m ) {
        string i = (string)l[0]:"";
        list<any> tmp = remove(l,0);
        if( size(tmp) > 0 ) {
            if( is( tmp[0]:nil, string ) ) {
                m[i] = setMValue( tmp, v, m[i]:$[] );
            } else {
                m[i] = setLValue( tmp, v, m[i]:[] );
            }
        } else {
            y2debug("setting %1 to %2",i,v);
            m[i] = v;
        }
        return m;
    }

    list<any> setLValue( list<any> l, any v, list<any> m ) {
        integer i = (integer)l[0]:0;
        list<any> tmp = remove(l,0);
        if( size(tmp) > 0 ) {
            if( is( tmp[0]:nil, string ) ) {
                m[i] = setMValue( tmp, v, m[i]:$[] );
            } else {
                m[i] = setLValue( tmp, v, m[i]:[] );
            }
        } else {
            y2debug("setting %1 to %2",i,v);
            m[i] = v;
        }
        return m;
    }

    /**
     *  this function is a replacement for this code:
     *      list<any> l = [ "key1",0,"key3" ]; 
     *      m[ l ] = v;
     *  @return map
     */
    global define map<string,any> setElementByList( list<any> l, any v, map<string,any> m ) {
        m = setMValue( l, v, m );
        return m;
    }

    global void checkProfile() {
        string file = AutoinstConfig::tmpDir + "/" + "valid.xml";
        Save(file);
        string summary = "Some schema check failed!\nPlease attach your logfile to bug id 211014\n\n";
        boolean valid = true;

        list<list> validators = [
            [
            _("Checking XML with RNG validation..."),
            "/usr/bin/xmllint --noout --relaxng "
            + Directory::schemadir + "/autoyast/rng/profile.rng", ""
            ]
        ];
        if ( ! Stage::cont() && PackageSystem::Installed("jing") ) {
            validators = add( validators, [
            _("Checking XML with RNC validation..."),
            "/usr/bin/jing >&2 -c "
            + Directory::schemadir + "/autoyast/rnc/profile.rnc", "jing_sucks"
            ] );
        }

        foreach (list i, validators, {
            string header = i[0]:"";
            string cmd = i[1]:"" + " " + file;

            summary = summary + header + "\n";

            map o = (map)SCR::Execute (.target.bash_output, cmd);
            y2debug("validation output: %1", o);

            summary = summary + cmd + "\n";
            summary = summary + o["stderr"]:"" + "\n";
            summary = summary + "\n";
            if( o["exit"]:1 != 0 || (i[2]:"" == "jing_sucks" && size(o["stderr"]:"") > 0 ) )
                    valid = false;
        });
        if( ! valid ) {
            Popup::Error(summary);
            y2milestone("Profile check failed please attach the log to bug id 211014: %1",summary);
        }
    }

}

ACC SHELL 2018