ACC SHELL

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

/**
 * File:
 *   Nfs.ycp
 *
 * Module:
 *   Configuration of nfs
 *
 * Summary:
 *   NFS client configuration data, I/O functions.
 *
 * Authors:
 *   Jan Holesovsky <kendy@suse.cz>
 *   Dan Vesely <dan@suse.cz>
 *   Martin Vidner <mvidner@suse.cz>
 *
 * $Id: Nfs.ycp 60683 2010-02-03 18:19:18Z kmachalkova $
 */

{
    module "Nfs";

    textdomain "nfs";

    import "Mode";;
    import "Report";
    import "Service";
    import "Summary";
    import "SuSEFirewall";
    import "Progress";
    import "PackageSystem";
    import "Wizard";

    include "nfs/routines.ycp";


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

    /* Should fstab reading be skipped ? (yes if we're
    embedded in partitioner) */
    global boolean skip_fstab = false;

    /**
     * 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;
    }

    /**
     * Required packages
     */
    global list <string> required_packages = [ "nfs-client" ];

    /**
     * eg.: [ $["spec": "moon:/cheese", file: "/mooncheese", "mntopts": "defaults"], ...]
     */
    global list<map<string,any> > nfs_entries = [];

    /**
     * Read only, intended for checking mount-point uniqueness.
     */
    global list<map> non_nfs_entries = [];

    global boolean nfs4_enabled = false;

    global string idmapd_domain = "";

    string portmapper = "";

    // list of created directories
    list<string> created_dirs = [];

    boolean ReadNfs4 () {
	return (SCR::Read(.sysconfig.nfs.NFS4_SUPPORT)=="yes"); 
    }

    string ReadIdmapd() {
	return (string) SCR::Read(.etc.idmapd_conf, "Domain");
    }

    /**
     * Set module data
     * @param settings module settings
     * @return void
     */
    global define void Set(map <string, any> settings) ``{
	if ( haskey(settings, "enable_nfs4") ) {
	    nfs4_enabled = settings["enable_nfs4"]:false;
	}
	else
	    nfs4_enabled = ReadNfs4();

	if ( haskey(settings, "idmapd_domain") ) {
	    idmapd_domain = settings["idmapd_domain"]:"localdomain";
	}
	else
	    idmapd_domain = ReadIdmapd();

	nfs_entries = maplist(map entry, settings["nfs_entries"]:[], ``{
	    return($[
		     "spec":entry["server_path"]:"",
		     "file":entry["mount_point"]:"",
		     "vfstype":entry["vfstype"]:"",
		     "mntops":entry["nfs_options"]:""
		   ]);
	});
	return;
    }

    /**
     * Get all NFS configuration from a map.
     * When called by nfs_auto (preparing autoinstallation data)
     * the map may be empty.
     * @param settings	a map with a single key: nfs_entries
     * @return	success
     */
    global define boolean Import (map <string, any> settings) ``{

	boolean missing = false;
	settings["nfs_entries"] = maplist(map s,settings["nfs_entries"]:[],``{
	    foreach (string k, ["server_path", "mount_point", "nfs_options"], ``{
		if (! haskey (s, k))
		{
		    y2error ("Missing at Import: '%1'.", k);
		    missing = true;
		}
	    });

	    //Backwards compatibility: with FaTE#302031, we support nfsv4 mounts
	    //thus we need to keep info on nfs version (v3 vs. v4)
	    //But older AY profiles might not contain this element
	    //so let's assume nfsv3 in that case (#395850)
	    if ( !haskey(s, "vfstype") )
	    {
	        s["vfstype"] = "nfs";
	    }
	    return s;

	});
	if (missing)
	{
	    return false;
	}

	Set(settings);
	return true;
    }

    /**
     * Dump the NFS settings to a map, for autoinstallation use.
     * @return a list of nfs entries.
     */
    global define map Export () ``{
	map <string, any> settings = $[];

	settings["enable_nfs4"] = nfs4_enabled;
	settings["idmapd_domain"] = idmapd_domain;

	list entries = maplist(map entry, nfs_entries, ``{
		return($[
			"server_path":entry["spec"]:"",
			"mount_point":entry["file"]:"",
			"vfstype"    :entry["vfstype"]:"",
			"nfs_options":entry["mntops"]:""
			]);
	});
	settings["nfs_entries"]= entries;
	return  settings;
    }

    /* ------------------------------------------------------------ */
    // Space escaping.
    // This should be done by the agent, but any-agent sucks.

    /**
     * Escape spaces " " -> "\\040".
     * @param s a string or nil
     * @return escaped string or nil
     */
    define string EscapeSpaces1 (string s) ``{
	return s == nil ? nil : mergestring (splitstring (s, " "), "\\040");
    }

    /**
     * Escape spaces " " -> "\\040" in all values of all entries
     * @param entries a list of maps, such as nfs_entries
     * @return escaped entries
     */
    define list<map<string,any> > EscapeSpaces (list<map<string,any> > entries) ``{
	return maplist (map<string,any> entry, entries,
			``(mapmap (string key, any value, entry, ``(
					$[ key :
					  (is (value, string)?
					  EscapeSpaces1 ((string) value): value)]
					))
			    ));
    }

    /**
     * (like awk gsub, but return the result, not number of replacements)
     * replaces from back!
     * @param regex regular expression to replace
     * @param replacement the replacement string
     * @param s where to replace
     * @return the changed string
     */
    define string gsub (string regex, string replacement, string s) ``{
	string temp = nil;
	while (true)
	{
	    // argh, regexpsub logs an error if it cannot sub
	    if (! regexpmatch (s, ".*" + regex + ".*"))
	    {
		break;
	    }
	    temp = regexpsub (s, "(.*)"+regex+"(.*)", "\\1"+replacement+"\\2");
	    if (temp == nil)
	    {
		break;
	    }
	    s = temp;
	}
	return s;
    }

    /**
     * Un-escape spaces "\\040" -> " "
     * @param s string or nil
     * @return escaped string or nil
     */
    define string UnescapeSpaces1 (string s) ``{
	// escaped space, \040, is /\\040/
	// which is "\\\\040"
	return s == nil ? nil : gsub ("\\\\040", " ", s);
    }

    /**
     * Un-escape spaces "\\040" -> " " in all values of all entries
     * @param entries a list of maps, such as nfs_entries
     * @return escaped entries
     */
    define list<map<string,any> > UnescapeSpaces (list<map<string,any> > entries) ``{
	return maplist (map<string,any> entry, entries,
			``( mapmap (string key, any value, entry, ``(
					$[ key:
					  (is (value, string)?
					  UnescapeSpaces1 ((string) value): value)]
					))
			    ));
    }

    global string FindPortmapper() {
	//testsuite is dumb - it can't distinguish between the existence 
	//of two services - either both exists or both do not
	if (Mode::testsuite())
	    return "portmap";
	return Service::Find( ["rpcbind", "portmap" ] ); 
    }

    /* ------------------------------------------------------------ */

    /**
     * Reads NFS settings from the SCR (.etc.fstab)
     * @return true on success
     */
    global define boolean Read () ``{

	//Read /etc/fstab if we're running standalone (otherwise, libstorage does the job)
	if( !skip_fstab )
	{
	    list<map<string,any> > fstab = (list<map<string,any> >) SCR::Read (.etc.fstab);
	    fstab = UnescapeSpaces (fstab);
	    y2milestone ("fstab: %1", fstab);

	    // For simplicity, this leaves also the unused fileds in the maps.
	    nfs_entries = filter (map<string,any> entry, fstab, ``{
	        return (entry["vfstype"]:"" == "nfs" || entry["vfstype"]:"" == "nfs4");
	    });
	    non_nfs_entries = filter (map entry, fstab, ``{
	        return (entry["vfstype"]:"" != "nfs" && entry["vfstype"]:"" != "nfs4");
	    });
	}

	nfs4_enabled = (SCR::Read(.sysconfig.nfs.NFS4_SUPPORT)=="yes");
	idmapd_domain = (string) SCR::Read(.etc.idmapd_conf, "Domain");

	boolean progress_orig = Progress::set(false);
	SuSEFirewall::Read ();
	Progress::set(progress_orig);

	portmapper = FindPortmapper();
	//There is neither rpcbind  nor portmap  
	if ( portmapper == "" )
	{
	    //so let's install rpcbind (default since #423026) 
	    required_packages = add( required_packages, "rpcbind" );
	    portmapper = "rpcbind";
	}
	if (nfs4_enabled)
	    required_packages = add( required_packages, "nfsidmap");

	if ( !PackageSystem::CheckAndInstallPackagesInteractive( required_packages ) )
	    return false;

	return true;
    }

    /**
     * Writes the NFS client configuration without
     * starting/stopping the service.
     * Autoinstallation uses this and then calls SuSEconfig only once
     * and starts the services together.
     * (No parameters because it is too short to abort)
     * @return true on success
     */
    global define boolean WriteOnly () ``{
	// Merge with non-nfs entries from fstab:
	list<map<string,any> > fstab = (list<map<string,any> >) SCR::Read (.etc.fstab);
	// unescape deferred for optimization
	fstab = filter (map<string,any> entry, fstab, ``{
	    return ( entry["vfstype"]:"" != "nfs" && entry["vfstype"]:"" != "nfs4");
	});
	fstab = UnescapeSpaces (fstab);

	foreach (map<string,any> entry, nfs_entries, ``{
	    fstab = add (fstab, (map<string,any>)union (entry, $[/*"vfstype": "nfs",*/ "freq": 0, "passno": 0]));

	    // create mount points
	    string file = entry["file"]:"";
	    if (!(boolean) SCR::Execute (.target.mkdir, file))
	    {
		// error popup message
		Report::Warning (sformat (_("Unable to create directory '%1'."), file));
	    }
	});

	y2milestone ("fstab: %1", fstab);

	SCR::Execute (.target.bash, "/bin/cp $ORIG $BACKUP", $["ORIG" : "/etc/fstab", "BACKUP" : "/etc/fstab.YaST2.save"]);

	fstab = EscapeSpaces (fstab);
	if (!SCR::Write(.etc.fstab, fstab))
	{
	    // error popup message
	    Report::Error (_("Unable to write to /etc/fstab.
No changes will be made to the
the NFS client configuration.\n"));
	    return false;
	}

/*	if (size (nfs_entries) == 0)
	{
	    // Service::Adjust ("nfs", "disable"); // what if autofs needs it?
	}*/

	if (size (nfs_entries) != 0)
	{
	    Service::Enable( portmapper );
	    Service::Enable("nfs");
	    // #36737: it just runs sm_notify at boot time
	    // replaces rpc.statd
	    Service::Enable("nfsboot");
	}

        if(nfs4_enabled)
        {
                SCR::Write(.sysconfig.nfs.NFS4_SUPPORT,"yes");
		SCR::Write(.etc.idmapd_conf, ["Domain",  idmapd_domain] );
        }
        else
        {
                SCR::Write(.sysconfig.nfs.NFS4_SUPPORT,"no");
        }

	boolean progress_orig = Progress::set (false);
	SuSEFirewall::WriteOnly ();
	Progress::set (progress_orig);

	return true;
    }

    /**
     * Writes the NFS client configuration and starts/stops the service.
     * (No parameters because it is too short to abort)
     * @return true on success
     */
    global define boolean Write () ``{
	if (WriteOnly ())
	{
	    // dialog label
	    Progress::New (_("Writing NFS Configuration"), " ", 2, [
	    // progress stage label
	    _("Stop services"),
	    // progress stage label
	    _("Start services"),
	   ], [
	    // progress step label
	    _("Stopping services..."),
	    // progress step label
	    _("Starting services..."),
	    // final progress step label
	    _("Finished") ],
	    "" );

	    // help text
	    Wizard::RestoreHelp(_("Writing NFS client settings. Please wait..."));

	    Progress::NextStage ();

	    Service::Stop("nfs");

	    Progress::NextStage ();
	    if (size (nfs_entries) > 0)
	    {
		if (Service::Status ( portmapper ) != 0)
		{
		    // portmap must not be started if it is running already (see bug # 9999)
		    Service::Start( portmapper );
		}

		Service::Start("nfs");
		// #74597: if all mounts are noauto, $? is 6 (unconfigured)
		integer status = Service::Status("nfs");
		if (status != 0 && status != 6)
		{
		    // error popup message
		    Report::Error (_("Unable to mount the NFS entries from /etc/fstab."));
		    return false;
		}
	    }

	    boolean progress_orig = Progress::set (false);
	    SuSEFirewall::ActivateConfiguration ();
	    Progress::set (progress_orig);

	    Progress::NextStage ();
	    return true;
	}
	else
	{
	    return false;
	}
    }

    /**
     * Summary()
     * @return Html formatted configuration summary
     */
    global define string Summary () ``{
	string summary = "";
	string nc = Summary::NotConfigured ();
	// summary header
	summary = Summary::AddHeader(summary, _("NFS Entries"));
	integer entries = size(nfs_entries);
	y2milestone("Entries: %1", nfs_entries);
	// summary item, %1 is a number
	string configured = sformat(_("%1 entries configured"), entries);
	summary = Summary::AddLine(summary, (entries>0) ? configured :  nc);
	return summary;
    }


    /**
     * Mount NFS directory
     * @param server remote server name
     * @param share name of the exported directory
     * @param mpoint mount point (can be empty or nil, in this case it will be mounted in temporary directory)
     * @param options mount options - e.g. "ro,hard,intr", see man nfs
     * @param type nfs type (nfs vs. nfsv4) - if empty, 'nfs' is used
     * @return string directory where volume was mounted or nil if mount failed
     */

    global define string Mount(string server, string share, string mpoint, string options, string type) ``{

	if (size(server) == 0 || size(share) == 0)
	{
	    return nil;
	}

	// check if options are valid
	if (size(options) > 0)
	{
	    if (check_options(options) != "")
	    {
		y2warning("invalid mount options: %1", options);
		return nil;
	    }
	}

	// mount to temporary directory if mpoint is nil
	if (mpoint == nil)
	{
	    string tmpdir = (string) SCR::Read(.target.tmpdir);

	    if (tmpdir == nil || tmpdir == "")
	    {
		y2security("Warning: using /tmp directory!");
		tmpdir = "/tmp";
	    }

	    mpoint = tmpdir + "/nfs" + sformat("%1", size(created_dirs));	// use num to allow parallel mounts
	}

	// check mount point
	if (CheckPath(mpoint) == false)
	{
	    // mount point is not valid
	    y2warning("invalid mount point: %1", mpoint);
	    return nil;
	}

	string portmapper = FindPortmapper();
	// check whether portmapper is installed
	if (IsPortmapperInstalled( portmapper ) == false)
	{
	    y2warning("Neither rpcbind nor portmap is installed");
	    return nil;
	}


	// start portmapper if it isn't running
	if (Service::Status( portmapper ) != 0)
	{
	    if (Service::Start( portmapper ) == false)
	    {
		y2warning( sformat("%1 cannot be started", portmapper) );
		return nil;
	    }
	}

	// create mount point if it doesn't exist
	if (SCR::Read(.target.dir, mpoint) == nil)
	{
	    if (!(boolean) SCR::Execute(.target.mkdir, mpoint))
	    {
		y2warning("cannot create mount point %1", mpoint);
		return nil;
	    }

	    // remember name of created directory
	    created_dirs = add(created_dirs, mpoint);
	}

	// build mount command
	string command = sformat("/bin/mount %1 %2 %3:%4 %5", (size(options) > 0) ? "-o " + options : "",
				 "-t " + (size(type) > 0  ? type : "nfs"),
				 server, share, mpoint);

	// execute mount command
	return (SCR::Execute(.target.bash, command) == 0) ? mpoint : nil;
    }

    /**
     * Unmount NFS directory from the system
     * @param mpoint NFS mount point to unmount
     * @return boolean true on success
     */
    global define boolean Unmount(string mpoint) ``{
	if (size(mpoint) == 0)
	{
	    return false;
	}

	// unmount directory if it's NFS mountpoint
	list<map<string,any> > mounts = (list<map<string,any> >) SCR::Read(.proc.mounts);
	boolean found = false;

	foreach(map<string, any> m, mounts,
	    ``{
		string type = (string) (m["vfstype"]:nil);
		string file = (string) (m["file"]:nil);

		if ( (type == "nfs" || type == "nfs4") && file == mpoint)
		{
		    found = true;
		}
	    }
	);

	if (found)
	{
	    string command = sformat("/bin/umount %1", mpoint);

	    if (SCR::Execute(.target.bash, command) != 0)
	    {
		return false;
	    }
	}
	else
	{
	    y2warning("%1 is not NFS mount point", mpoint);
	    return false;
	}

	// if the directory was created by Mount call and it is empty then remove it
	if (contains(created_dirs, mpoint) && SCR::Read(.target.dir, mpoint) == [])
	{
	    string command = sformat("/bin/rmdir %1", mpoint);

	    if (SCR::Execute(.target.bash, command) != 0)
	    {
		return false;
	    }

	    // remove directory from list
	    created_dirs = filter(string d, created_dirs, ``(d != mpoint));
	}

	return true;
    }

	/**
	* Return required packages for auto-installation
    * @return map of packages to be installed and to be removed
    */
    global define map AutoPackages() ``{
	     return ($["install": required_packages, "remove": []]);

    }




}

ACC SHELL 2018