ACC SHELL

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

/**
 * File:	modules/AutoinstSoftware.ycp
 * Package:	Autoyast
 * Summary:	Software
 * Authors:	Anas Nashif <nashif@suse.de>
 *
 * $Id: AutoinstSoftware.ycp 61894 2010-04-28 09:36:35Z ug $
 *
 */
{
    module "AutoinstSoftware";
    textdomain "autoinst";

    import "Profile";
    import "Summary";
    import "Stage";
    import "SpaceCalculation";
    import "Packages";
    import "Popup";
    import "Report";
    import "Kernel";
    import "AutoinstConfig";
    import "ProductControl";
    import "Storage";
    import "Mode";
    import "Misc";
    import "Directory";

    include "autoinstall/io.ycp";

    // All shared data are in yast2.rpm to break cyclic dependencies
    import "AutoinstData";

    import "PackageAI";

    global map Software = $[];

    global map<string, any> image = $[];
    global string image_arch = "";

    // patterns
    global list<string> patterns = [];

    // Kernel, force type of kernel to be installed
    global string kernel = "";

    // Packages that should be installed in continue mode
    // AutoinstData::post_packages = [];

    global string ft_module = "";

    /* Enable Imaging */
    global boolean imaging = false;

    /* default value of settings modified */
    global boolean modified = false;

    global list<string> inst = [];
    global list<map<string,any> > all_xpatterns = [];

    global string instsource = "";
    global string isolinuxcfg = "";


    /**
     * Function sets internal variable, which indicates, that any
     * settings were modified, to "true"
     */
    global define void SetModified ()
    {
        modified = true;
    }

    /**
     * Functions which returns if the settings were modified
     * @return boolean  settings were modified
     */
    global define boolean GetModified ()
    {
        return modified;
    }


    /**
     * Import data
     * @param settings settings to be imported
     * @return true on success
     */
    global define boolean Import(map settings)
    {
        Software = settings;
        patterns = settings["patterns"]:[];
        instsource = settings["instsource"]:"";

        string notFound = "";

        /* what is this good for? disturbs the main-repo selection */
        /*
        Packages::Init(true);
        Packages::InitializeAddOnProducts();
        */

        list<string> packagesAvailable = Pkg::GetPackages (`available, true);
        list<string> patternsAvailable = [];
        list<map<string,any> > allPatterns = Pkg::ResolvableDependencies ("", `pattern, "");
        allPatterns = filter( map<string,any> m, allPatterns, ``{
            if( m["user_visible"]:false )
                patternsAvailable = add( patternsAvailable, m["name"]:"" );
            return m["user_visible"]:false;
        });

        list<string> regexFound = [];
        settings["packages"] = (list<string>)filter( string want_pack, (list<string>)settings["packages"]:[], ``{
            if(  !issubstring( want_pack, "/" ) )
                return true;
            want_pack = deletechars( want_pack, "/" );
            foreach( string pack, packagesAvailable, ``{
                y2milestone("matching %1 against %2", pack, want_pack);
                if( regexpmatch (pack, want_pack) ) {
                    regexFound = add(regexFound, pack);
                    y2milestone("match");
                }
            });
            return false;
        });
        settings["packages"] = (list<string>)union( settings["packages"]:[], regexFound );

        regexFound = [];
        patterns = (list<string>)filter( string want_patt, patterns, ``{
            if(  !issubstring( want_patt, "/" ) )
                return true;
            want_patt = deletechars( want_patt, "/" );
            foreach( string patt, patternsAvailable, ``{
                y2milestone("matching %1 against %2", patt, want_patt);
                if( regexpmatch (patt, want_patt) ) {
                    regexFound = add(regexFound, patt);
                    y2milestone("match");
                }
            });
            return false;
        });
        patterns = (list<string>)union( patterns, regexFound );

        foreach( string pack, settings["packages"]:[], ``{
            if( ! Pkg::IsAvailable(pack) && Stage::initial() ) {
                notFound = notFound + pack + "\n";
            }
        });
        if( size(notFound) > 0 ) {
            y2error("packages not found: %1",notFound);
            // warning text during the installation. %1 is a list of package names
            Report::Error( sformat(_("These packages could not be found in the software repositories:\n%1"), notFound));
        }

        PackageAI::toinstall  = settings["packages"]:[];
        kernel = settings["kernel"]:"";
        AutoinstData::post_packages = settings["post-packages"]:[];
        AutoinstData::post_patterns = settings["post-patterns"]:[];
        PackageAI::toremove  =  settings["remove-packages"]:[];

        /* Imaging */
        /*
        map<string, any> image = settings["system_images"]:$[];
        imaging = image["enable_multicast_images"]:false;
        ft_module = image["module_name"]:"";
        if (settings == $[])
            modified = false;
        else
            modified = true;
        */
        image = settings["image"]:$[];
        if( image["image_location"]:"" != "" && image["image_name"]:"" != "" )
            imaging=true;

        return true;
    }

    /**
     * Constructer
     */
    global define void AutoinstSoftware()
    {
        if ( Stage::cont () && Mode::autoinst ())
        {
	    Pkg::TargetInit ("/", false);
            Import(Profile::current["software"]:$[]);
        }
        return;
    }

    define string GetArchOfELF (string filename)
    {
        map bash_out = (map) SCR::Execute (.target.bash_output, Directory::ybindir +
                       "/elf-arch " + filename);
        if (bash_out["exit"]:1 != 0)
            return "unknown";
        return deletechars (bash_out["stdout"]:"unknown", "\n");
    }


    global define void createImage( string targetdir ) {
        string rootdir = (string)SCR::Read(.target.tmpdir);
        string zypperCall = "";
        string outputRedirect = " 2>&1 >> /tmp/ay_image.log";
        boolean finalPopup = ( size(targetdir) == 0 );
        image["script_location"] = image["script_location"]:"file:///usr/lib/YaST2/bin//fetch_image.sh";
        image["script_params"]   = image["script_params"]:[ image["image_location"]:""+"/"+image["image_name"]:"image"+".tar.gz" ];

        SCR::Execute (.target.bash, "rm -f /tmp/ay_image.log" );

        // bind-mount devices
        SCR::Execute (.target.mkdir, rootdir+"/dev" );
        integer returnCode = (integer)SCR::Execute (.target.bash, sformat("touch /%1/dev/null %1/dev/zero", rootdir));
        returnCode = (integer)SCR::Execute (.target.bash, sformat("mount -o bind /dev/zero /%1/dev/zero", rootdir));
        returnCode = (integer)SCR::Execute (.target.bash, sformat("mount -o bind /dev/null /%1/dev/null", rootdir));

        // Add Source:
        // zypper --root /space/tmp/tmproot/ ar ftp://10.10.0.100/install/SLP/openSUSE-11.2/i386/DVD1/ main
        zypperCall = sformat("ZYPP_READONLY_HACK=1 zypper --root %1 --gpg-auto-import-keys --non-interactive ar %2 main-source %3", rootdir, instsource, outputRedirect );
        y2milestone("running %1", zypperCall );
        returnCode = (integer)SCR::Execute (.target.bash, zypperCall);
        if( returnCode != 0 && returnCode != 4 ) {
             // 4 means "already exists"
            Popup::Error( sformat( _("Adding repo %1 failed"), instsource) );
        }

        // Add add-ons
        map addOnExport = (map)WFM::CallFunction("add-on_auto", ["Export"]);
        list<map> addOns = addOnExport["add_on_products"]:[];
        foreach( map addOn, addOns, ``{
            zypperCall = sformat("ZYPP_READONLY_HACK=1 zypper --root %1 --gpg-auto-import-keys --non-interactive ar %2 %3 %4",
                                  rootdir, addOn["media_url"]:"", addOn["product"]:"", outputRedirect );
            returnCode = (integer)SCR::Execute (.target.bash, zypperCall);
            if( returnCode != 0 && returnCode != 4 ) {
                Popup::Error( sformat( _("Adding repo %1 failed"), addOn["product"]:"") );
            }
        });

        // Install
        zypperCall = sformat("ZYPP_READONLY_HACK=1 zypper --root %1 --gpg-auto-import-keys --non-interactive install --auto-agree-with-licenses ", rootdir);

        string pattern = mergestring(patterns, " ");
        Popup::ShowFeedback( "Creating Image - installing patterns", "");
        y2milestone("installing %1", pattern);
        returnCode = (integer)SCR::Execute (.target.bash, zypperCall+"-t pattern " + pattern + outputRedirect);
        Popup::ClearFeedback();
        if( returnCode != 0 ) {
            Popup::Error( _("Image creation failed while pattern installation. Please check /tmp/ay_image.log") );
        }

        if( size(PackageAI::toinstall) > 0 ) {
            string package = mergestring( PackageAI::toinstall, " ");
            Popup::ShowFeedback( _("Creating Image - installing packages"), "");
            returnCode = (integer)SCR::Execute (.target.bash, zypperCall+" "+package + outputRedirect);
            Popup::ClearFeedback();
            if( returnCode != 0 ) {
                Popup::Error( _("Image creation failed while package installation. Please check /tmp/ay_image.log") );
            }
        }

        image_arch = GetArchOfELF( sformat("%1/bin/bash",rootdir) );
        y2milestone("Image architecture = %1", image_arch);
        if( targetdir == "" ) {
//            Popup::Message( _("in the next file dialog you have to choose the target directory to save the image") );
            targetdir = UI::AskForExistingDirectory  ( "/", _("Store image to ...") );
        }

        // umount devices
        returnCode = (integer)SCR::Execute (.target.bash, sformat("umount %1/dev/null %1/dev/zero %1/proc", rootdir));
        returnCode = (integer)SCR::Execute (.target.bash, sformat("rm -rf %1/dev", rootdir));

        // Compress image:
        // tar cfz /srv/www/htdocs/image.tar.gz --exclude="proc*"  .
        string tarCommand = sformat("tar cfvz %4/%3.tar.gz --exclude=\"./proc*\" --exclude=\"/%3.tar.gz\" -C %1 . %2",
                                    rootdir, outputRedirect, image["image_name"]:"", targetdir);
        y2milestone("running %1", tarCommand);
        Popup::Message( sformat( _("You can do changes to the image now in %1/\nIf you press the ok-button, the image will be compressed and can't be changed anymore."), rootdir ) );
        Popup::ShowFeedback( "Compressing Image ...", "" );
        returnCode = (integer)SCR::Execute (.target.bash, tarCommand);
        Popup::ClearFeedback();
        if( returnCode != 0 ) {
            Popup::Error( sformat( _("Image compressing failed in '%1'. Please check /tmp/ay_image.log"), rootdir ) );
        }
        if( finalPopup )
            Popup::Message( _("Image created successfully") );

    }

    global define boolean copyFiles4ISO( string target ) {
        boolean ret = true;
        string copy = Misc::SysconfigRead( .sysconfig.autoinstall.COPY_FOR_ISO,
                                           "/,/boot/,/boot/__ARCH__/,/boot/__ARCH__/loader/,/media.1/,/suse/setup/descr/");
        list<string> copyList = splitstring(copy,",");

        foreach( string source, copyList, ``{
            if( issubstring( source, "__ARCH__" ) )
                source = regexpsub( source, "(.*)__ARCH__(.*)", sformat("\\1%1\\2", image_arch) );
            if( substring( source, size(source)-1 ) == "/" ) {
                // copy a directory (ends with / in directory.yast)
                SCR::Execute (.target.mkdir, target+"/"+source );
                if( !GetURL( instsource+"/"+source+"directory.yast", "/tmp/directory.yast" ) ) {
                    Popup::Error( sformat(_("can not get the directory.yast file at `%1`.\nYou can create that file with 'ls -F > directory.yast' if it's missing."), instsource+"/"+source));
                    ret = false;
                    break;
                }
                // directory.yast is our filelist
                string files = (string)SCR::Read(.target.string, "/tmp/directory.yast");
                list<string> filesInDir = splitstring( files, "\n" );
                foreach( string file, filesInDir, ``{
                    // don't copy subdirs. They have to be mentioned explicit. Copy only files from that dir.
                    y2milestone("will get %1 from %2 to %3", file, instsource+"/"+source, target );
                    if( size(file) > 0 && substring( file, size(file)-1 ) != "/" )
                        while( ret && !GetURL( instsource+"/"+source+file, target+"/"+source+"/"+file ) ) {
                            if( !Popup::YesNo( sformat(_("can not read '%1'. Try again?"), instsource+"/"+source+file ) ) )
	                            ret = false;
                        }
                    if( !ret )
                        break;
                });
            } else {
                // copy a file
                if( !GetURL( instsource+"/"+source, target+"/"+source ) ) {
                    Popup::Error( sformat(_("can not read '%1'. ISO creation failed"), instsource+"/"+source) );
                    ret = false;
                    break;
                }
            }
        });
        // lets always copy an optional(!) driverupdate file. It's very unlikely that it's in directory.yast
        GetURL( instsource+"/driverupdate", target+"/driverupdate" );
        SCR::Execute (.target.bash, sformat("cp /usr/lib/YaST2/bin/fetch_image.sh %1/",target) );
        return ret;
    }

    global define void createISO() {
        /* we will have the image.tar.gz and autoinst.xml on the root of the DVD/CD */
        string isodir = "/tmp/ay_iso/";
        SCR::Execute (.target.bash, sformat("rm -rf %1", isodir) );
        SCR::Execute (.target.mkdir, isodir );
        string outputRedirect = " 2>&1 >> /tmp/ay_image.log";
        integer returnCode = 0;
        createImage( isodir );

        Popup::ShowFeedback( _("Preparing ISO Filestructure ..."), "" );
        copyFiles4ISO( isodir );
        Popup::ClearFeedback();

        // prepare and save autoinst.xml
        image["image_location"] = "file:///";
        image["script_params"] = [ image["image_location"]:"" + "/" + image["image_name"]:"" + ".tar.gz" ];
        image["script_location"] = "file:///fetch_image.sh";
        Profile::Save( sformat("%1/autoinst.xml", isodir) );

        // prepare and save isolinux.cfg boot menu of the media
        isolinuxcfg = (string)SCR::Read(.target.string,  sformat("%1/boot/%2/loader/isolinux.cfg", isodir, image_arch));
        list<string> lines = splitstring( isolinuxcfg, "\n" );
        lines = maplist( string l, lines, { 
            if( issubstring( l, " append " ) ) 
                l = l + " autoyast=file:///autoinst.xml"; 
            return l; 
        });
        isolinuxcfg = mergestring( lines, "\n" );

        UI::OpenDialog(
                       `opt( `decorated ),
                       `VBox(
                             `HBox( `VSpacing(14),
                                    `MultiLineEdit(`id(`isolinuxcfg), _("boot config for the DVD"), isolinuxcfg )
                             ),
                             `PushButton( `id( `create_image ), `opt( `default, `hstretch ), _("Ok") ),
                             `Label( sformat( _("You can do changes to the ISO now in %1, like adding a complete different AutoYaST XML file.\nIf you press the ok-button, the iso will be created."), isodir) )
                        )
                       );
        UI::UserInput();
        isolinuxcfg = (string)UI::QueryWidget(`isolinuxcfg, `Value);
        UI::CloseDialog();
        SCR::Write(.target.string,  sformat("%1/boot/%2/loader/isolinux.cfg", isodir, image_arch), isolinuxcfg);

        // create the actual ISO file
//        Popup::Message( _("Please choose a place where you want to save the ISO file in the next dialog") );
        string targetdir = UI::AskForExistingDirectory  ( "/", _("Store ISO image to ...") );
        Popup::ShowFeedback( _("Creating ISO File ..."), "" );
        string cmd = sformat("mkisofs -o %1/%2.iso -R -b boot/%3/loader/isolinux.bin -c boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table %4",
                              targetdir, image["image_name"]:"", image_arch, isodir);
        y2milestone("executing %1", cmd + outputRedirect);
        returnCode = (integer)SCR::Execute (.target.bash, cmd);
        Popup::ClearFeedback();
        if( returnCode != 0 ) {
            Popup::Error( sformat("ISO creation failed in '%1'. Please check /tmp/ay_image.log", isodir ) );
        } else {
            Popup::Message( sformat(_("ISO successfully created at %1"), "/tmp/"+image["image_name"]:""+".iso" ) );
        }
    }

    /**
     * Export data
     * @return dumped settings (later acceptable by Import())
     */
    global define map Export()
    {
        map s = $[];
        if (kernel != "")
            s["kernel"] = kernel ;

        if( patterns != [])
            s["patterns"] = patterns;

        if (PackageAI::toinstall != [])
            s["packages"] = PackageAI::toinstall;

        if (AutoinstData::post_packages != [])
            s["post-packages"] = AutoinstData::post_packages ;

        if (PackageAI::toremove != [])
            s["remove-packages"] = PackageAI::toremove;

        s["instsource"] = instsource;

        s["image"] = image;

        return (s);
    }


    /**
     * Add packages needed by modules, i.e. NIS, NFS etc.
     * @param list of strings packages to add
     * @return void
     */
    global define void AddModulePackages(list<string> module_packages)
    {
        PackageAI::toinstall = toset((list<string>)
                union(PackageAI::toinstall,  module_packages));
        //
        // Update profile
        //
        Profile::current["software"] = Export();
        return;
    }

    /**
     * Remove packages not needed by modules, i.e. NIS, NFS etc.
     * @param list of packages to remove
     * @return void
     */
    global define void RemoveModulePackages (list<string> module_packages)
    {
        PackageAI::toinstall = filter(string p, PackageAI::toinstall, ``(!contains(module_packages,p)));
        Profile::current["software"] = Export();
        return;
    }



    /**
     * Summary
     * @return Html formatted configuration summary
     */
    global define string Summary()
    {
        string summary = "";
        
        summary = Summary::AddHeader(summary, _("Selected Patterns"));
        if (size( patterns ) > 0 )
        {
            summary = Summary::OpenList(summary);
            foreach(string a, patterns, ``{
                summary = Summary::AddListItem(summary, a);
            });
            summary = Summary::CloseList(summary);
        }
        else
        {
            summary = Summary::AddLine(summary, Summary::NotConfigured());
        }
        summary = Summary::AddHeader(summary, _("Individually Selected Packages"));
        summary = Summary::AddLine(summary, sformat("%1",
                    size(PackageAI::toinstall)));

        summary = Summary::AddHeader(summary, _("Packages to Remove"));
        summary = Summary::AddLine(summary, sformat("%1",
                    size(PackageAI::toremove)));

        if (kernel != "")
        {
            summary = Summary::AddHeader(summary, _("Force Kernel Package"));
            summary = Summary::AddLine(summary, sformat("%1", kernel));
        }
        return summary;
    }
    /**
     * Compute list of packages selected by user and other packages needed for important
     * configuration modules.
     * @return list of strings list of packages needed for autoinstallation
     */
    global define list<string> autoinstPackages()
    {

        list<string> allpackages = [];

        // the primary list of packages
        allpackages = (list<string>) union (allpackages,  PackageAI::toinstall);

        // In autoinst mode, a kernel should not be  available
        // in <packages>
        if ( size(kernel) == 0)
        {
            list <string> kernel_pkgs = Kernel::ComputePackages ();
            allpackages = (list <string>) union (allpackages, kernel_pkgs);
        }
        else
        {
            if (Pkg::IsAvailable (kernel))
            {
                allpackages = add (allpackages, kernel);
                string kernel_nongpl = kernel + "-nongpl";

                if (Pkg::IsAvailable (kernel_nongpl))
                    allpackages = add (allpackages, kernel_nongpl);
            }
            else
            {
                y2warning ("%1 not available, using kernel-default", kernel);
                list <string> kernel_pkgs = Kernel::ComputePackages ();
                allpackages = (list <string>) union (allpackages, kernel_pkgs);
            }
        }

        return allpackages;
    }



    /**
     * Configure software settings
     * @param void
     * @return boolean
     */
    global define boolean Write()
    {

        if (imaging)
        {
            if( ! image["run_kickoff"]:false )
                ProductControl::DisableModule ("kickoff");
            ProductControl::DisableModule ("rpmcopy");
            return true;
        }

        boolean ok = true;

        Packages::Init(true);
        list<string> failed = [];

        /* switch for recommended patterns installation (workaround for our very weird pattern design) */
        if( Profile::current["software","install_recommended"]:false == false ) {
            /* set SoftLock to avoid the installation of recommended patterns (#159466) */
            foreach( map<string,any> p, Pkg::ResolvableProperties ("", `pattern, ""), ``{
                Pkg::ResolvableSetSoftLock( p["name"]:"", `pattern );
            });
        }

        foreach(string p, toset(patterns),
                ``{
            if (! Pkg::ResolvableInstall( p, `pattern ) )
            {
                failed = add(failed, p);
            }
        });

        if (size(failed) > 0 )
        {
            y2error("Error while setting pattern: %1",  mergestring(failed, ","));
            Report::Warning(sformat(_("Could not set patterns: %1."), mergestring(failed, ",")));
        }

        list<string> autoinstPacks = autoinstPackages ();
// FIXME: optimization for package list evaluation turned off because it optimized it
//        into an unbootable state (no kernel) bnc#427731
//
//        list<string> autoinstPacks = PackageAI::toinstall;
        y2milestone("Packages selected in autoinstall mode: %1", autoinstPacks);

        if (size(autoinstPacks) > 0 )
        {
            y2milestone("Installing individual packages: %1", Pkg::DoProvide(autoinstPacks) );
        }


        list<string> computed_packages = Packages::ComputeSystemPackageList();
        y2debug("Computed list of packages: %1", computed_packages);

        foreach( string pack, computed_packages, ``{
            if( size(kernel) > 0 && pack != kernel && search(pack, "kernel-") == 0 ) {
                y2milestone("taboo for kernel %1",pack);
                PackageAI::toremove = add( PackageAI::toremove, pack );
            }
        });


        //
        // Now remove all packages listed in remove-packages
        //
        y2milestone("Packages to be removed: %1", PackageAI::toremove);
        if (size(PackageAI::toremove) > 0 )
        {
            foreach (string rp, PackageAI::toremove, ``{
                //Pkg::ResolvableSetSoftLock( rp, `package ); // FIXME: maybe better Pkg::PkgTaboo(rp) ?
                Pkg::PkgTaboo(rp);
            });

            Pkg::DoRemove(PackageAI::toremove);
        }
        list<string> pack = Storage::AddPackageList();
        if( size(pack)>0 )
        {
            y2milestone("Installing storage packages: %1", Pkg::DoProvide( pack ));
        }
        //
        // Solve dependencies
        //
        if( !Pkg::PkgSolve(false) ) {
            Report::Error( _("The package resolver run failed. Please check your software section in the autoyast profile.") );
        }

        SpaceCalculation::ShowPartitionWarning();

        return (ok);
    }


    /**
     * Initialize temporary target
     */
    global define void  pmInit()
    {

//        string tmproot = AutoinstConfig::tmpDir;

//        SCR::Execute(.target.mkdir, tmproot + "/root");
//        Pkg::TargetInit( tmproot + "/root", true);
//        Pkg::TargetInit( "/", true);
        Pkg::TargetInit( (string)SCR::Read(.target.tmpdir), true );
        y2milestone("SourceStartCache: %1", Pkg::SourceStartCache(false));
        return;
    }


    /**
     * Add post packages
     * @param list calculated post packages
     * @return void
     */
    global define void addPostPackages(list<string> calcpost)
    {
        AutoinstData::post_packages = (list<string>)toset(union(calcpost,AutoinstData::post_packages));
        return;
    }

    /**
     * Return list of software packages of calling client
     * @return map map of installed software package
     *		"patterns" -> list<string> addon selections
     *		"packages" -> list<string> user selected packages
     *      "remove-packages" -> list<string> packages to remove
     */
    global define map<string, any > ReadHelper()
    {
        boolean ret = Pkg::TargetInit("/", false);
//        Pkg::TargetInitialize ("/");
        Pkg::TargetLoad();
        Pkg::SourceStartManager (true);
        Pkg::PkgSolve(false);

        inst = Pkg::GetPackages(`installed, true);
        list<map<string,any> > all_patterns = Pkg::ResolvableProperties ("", `pattern, "");
        all_xpatterns = Pkg::ResolvableDependencies ("", `pattern, "");
        list<string> patterns = [];
        list<string> visible_patterns = [];

        list<map> patternsFullData = filter( map p, all_patterns, ``{
                boolean ret = false;
		if( p["status"]:`none == `installed && !contains( patterns, p["name"]:"no name" ) ) {
                        patterns = add( patterns, p["name"]:"no name" );
                        ret = true;
	                if( p["user_visible"]:true == true )
        	                visible_patterns = add( visible_patterns, p["name"]:"no name" );
		}
                return ret;
        });
        Pkg::TargetFinish ();

        string tmproot = AutoinstConfig::tmpDir;
        SCR::Execute(.target.mkdir, tmproot + "/rootclone");
        Pkg::TargetInit( tmproot + "/rootclone", true);
        y2debug("SourceStartCache: %1", Pkg::SourceStartCache(false));

        Pkg::SourceStartManager(true);

        list<string> userpackages = Pkg::FilterPackages(false, false, true, true);
        Pkg::TargetFinish ();
        // Remove kernel packages
        list<string> removepackages = [];

        list<string> patternPackages = [];
        list<string> new_p = [];
        foreach( string tmp_pattern, patterns, ``{
            list<map<string,any> > xpattern = filter( map<string,any> p, all_xpatterns, ``( p["name"]:"" == tmp_pattern ) );
            map<string,any> found = xpattern[0]:$[];
            boolean req = false;
            // kick out hollow patterns (always fullfilled patterns)
            foreach( map<string,any> d, found["dependencies"]:[], ``{
                if( d["res_kind"]:"" == "package" && (d["dep_kind"]:"" == "requires" || d["dep_kind"]:"" == "recommends") ) {
                    patternPackages = add(patternPackages, d["name"]:"");
                    req = true;
                }
            });
            // workaround for our pattern design
            // a pattern with no requires at all is always fullfilled of course
            // you can fullfill the games pattern with no games installed at all
            if( req == true )
                new_p = add( new_p, tmp_pattern );
        });
        patterns = new_p;

        list<string> patternPackagesTemp = patternPackages;
        boolean run = false;
        integer emergency_break = 0;
        do {
            run = false;
            emergency_break = emergency_break + 1;
            /* remove all packages that are pulled in by the resolver anyway */
            list<string> tmp = [];
            patternPackagesTemp = toset( patternPackagesTemp );
            foreach( string ppackage, patternPackagesTemp, ``{
                foreach( map<string,any> d, Pkg::ResolvableDependencies (ppackage, `package, ""), ``{
                    foreach( map<string,any> dd, d["dependencies"]:[], ``{
                        if( dd["res_kind"]:"" == "package" && (dd["dep_kind"]:"" == "requires" || dd["dep_kind"]:"" == "recommends") && !contains(patternPackages , dd["name"]:"") ) {
                            patternPackages = add(patternPackages, dd["name"]:"");
                            tmp = add(tmp, dd["name"]:"");
                            run = true;
                        }
                    });
                });
            });
            patternPackagesTemp = tmp;	
            y2milestone("temp package list = %1", tmp);	
        } while( run == true && emergency_break < 20 );

        map<string, any > software = $[];
        if( size(patterns) > 0 ) {
            foreach(string p, inst, ``{
                if (!contains(patternPackages, p))
                    userpackages = add( userpackages, p );
            });
            foreach(string p, patternPackages, ``{
                if (!contains(inst,p))
                    removepackages = add( removepackages, p );
            });
        }

        software["packages"] = sort( filter(string pkg, userpackages, ``(! regexpmatch(pkg, "kernel-.*") || pkg == "kernel-uml")) );
        software["patterns"] = sort( visible_patterns );
        software["remove-packages"] = toset( removepackages );
        return software;
    }

    global define boolean Read(){
      return Import( ReadHelper() );
    }

    //EOF
}

ACC SHELL 2018