2.9. Connecting the Configuration Data with the UI

This part will show you how to connect the Sshd module data with the UI.

2.9.1. Getting and Setting the Configuration Data

Although our Sshd module can read and write the configuration and the UI is already able to make it read or written we don't have any functionality to get the configuration from the module or to set the new options or values. Let's add the last functions to the src/Sshd.ycp. Add these functions just above the Abort() function definition:

/**
 * Returns the SSHD Option as a list of strings.
 *
 * @param string option_key of the SSHD configuration
 * @return list <string> with option_values
 */
global list <string> GetSSHDOption (string option_key) {
    return SETTINGS[option_key]:DEFAULT_CONFIG[option_key]:[];
}

/**
 * Sets values for an option.
 *
 * @param string option_key with the SSHD configuration key
 * @param list <string> option_values with the SSHD configuration values
 */
global void SetSSHDOption (string option_key, list <string> option_vals) {
    SETTINGS[option_key] = option_vals;
}

Function GetSSHDOption() reads the SETTINGS map with the parameter option_key as the key of the map. If it is not defined, it tries to find the key in the DEFAULT_CONFIG map, otherwise it selects the [] which means the empty list. The first value found is returned by the function.

Function SetSSHDOption() sets the list of strings option_vals as the value of the SETTINGS map identified by the option_key key.

Do not forget to run the sudo make install command in the src/ to compile and install the updated Sshd module

This is the final content of Sshd.ycp:

/**
 * File:	modules/Sshd.ycp
 * Package:	Configuration of SSHD
 * Summary:	SSHD settings, input and output functions
 * Authors:	John The Fish <john@thesmallfish.net>
 *
 * Representation of the configuration of SSHD.
 * Input and output routines.
 */

{

module "Sshd";
textdomain "sshd";

import "Progress";
import "Report";
import "Message";
import "Service";
import "Popup";
import "SCR";

/**
 * Data was modified?
 */
boolean modified = false;

/**
 * Sleep time between Read or Write steps
 */
integer sl = 1000;

/**
 * Returns whether the configuration has been modified.
 */
global boolean GetModified() {
    return modified;
}

/**
 * Sets that the configuration has been modified.
 */
global void SetModified() {
    modified = true;
}

/**
 * map of SSHD settings
 */
map <string, list<string> > SETTINGS = $[];

map <string, list<string> > DEFAULT_CONFIG = $[
    "Port"                      : ["22"],
    "AllowTcpForwarding"        : ["yes"],
    "X11Forwarding"             : ["no"],
    "Compression"               : ["yes"],
    "PrintMotd"                 : ["yes"],
    "PermitRootLogin"           : ["yes"],
    "IgnoreUserKnownHosts"      : ["no"],
    "MaxAuthTries"              : ["6"],
    "PasswordAuthentication"    : ["yes"],
    "RSAAuthentication"         : ["no"],
    "PubkeyAuthentication"      : ["yes"],
];

/**
 * Describes whether the daemon is running
 */
boolean sshd_is_running = false;

/**
 * Returns the SSHD Option as a list of strings.
 *
 * @param string option_key of the SSHD configuration
 * @return list <string> with option_values
 */
global list <string> GetSSHDOption (string option_key) {
    return SETTINGS[option_key]:DEFAULT_CONFIG[option_key]:[];
}

/**
 * Sets values for an option.
 *
 * @param string option_key with the SSHD configuration key
 * @param list <string> option_values with the SSHD configuration values
 */
global void SetSSHDOption (string option_key, list <string> option_vals) {
    SETTINGS[option_key] = option_vals;
}

/**
 * Returns a confirmation popup dialog whether user wants to really abort.
 */
global boolean Abort() {
    return Popup::ReallyAbort(GetModified());
}

/**
 * Checks whether an Abort button has been pressed.
 * If so, calls function to confirm the abort call.
 *
 * @return boolean true if abort confirmed
 */
global boolean PollAbort() {
    if (UI::PollInput() == `abort)
	return Abort();

    return false;
}

/**
 * Reads current sshd configuration
 */
boolean ReadSSHDSettings () {
    foreach (string key, (list <string>) SCR::Dir(.sshd), {
        list <string> val = (list <string>) SCR::Read(add(.sshd, key));
        if (val != nil) SETTINGS[key] = val;
    });
    
    y2milestone("SSHD configuration has been read: %1", SETTINGS);
    return true;
}

/**
 * Writes current sshd configuration
 */
boolean WriteSSHDSettings () {
    y2milestone("Writing SSHD configuration: %1", SETTINGS);

    foreach (string option_key, list <string> option_val, SETTINGS, {
        SCR::Write(add(.sshd, option_key), option_val);
    });
    SCR::Write(.sshd, nil);

    return true;
}

/**
 * Reads current sshd status
 */
boolean ReadSSHDService () {
    if (Service::Status("sshd") == 0) {
        sshd_is_running = true;
    } else {
        sshd_is_running = false;
    }
    y2milestone((sshd_is_running ? "SSH is running":"SSH is not running"));
    
    return true;
}

/**
 * Restarts the sshd when the daemon was running when starting the configuration
 */
boolean WriteSSHDService () {
    boolean all_ok = true;

    if (sshd_is_running) {
        y2milestone("Restarting sshd daemon");
        all_ok = Service::Restart("sshd");
    } else {
        y2milestone("Sshd is not running - leaving...");
    }
    
    return all_ok;
}

/**
 * Read all SSHD settings
 * @return true on success
 */
global boolean Read() {

    /* SSHD read dialog caption */
    string caption = _("Initializing SSHD Configuration");

    integer steps = 2;

    Progress::New( caption, " ", steps, [
	    /* Progress stage 1/2 */
	    _("Read current SSHD configuration"),
	    /* Progress stage 2/2 */
	    _("Read current SSHD state")
	], [
	    /* Progress step 1/2 */
	    _("Reading current SSHD configuration..."),
	    /* Progress step 2/2 */
	    _("Reading current SSHD state..."),
	    /* Progress finished */
	    Message::Finished()
	],
	""
    );

    sleep(sl);

    if(PollAbort()) return false;
    Progress::NextStage();
    /* Error message */
    if(!ReadSSHDSettings()) Report::Error(Message::CannotReadCurrentSettings());
    sleep(sl);

    if(PollAbort()) return false;
    Progress::NextStep();
    /* Error message */
    if(!ReadSSHDService()) Report::Error(_("Cannot read current SSHD state."));
    sleep(sl);

    if(PollAbort()) return false;
    Progress::NextStage ();
    sleep(sl);

    modified = false;
    return true;
}

/**
 * Write all SSHD settings
 * @return true on success
 */
global boolean Write() {

    /* SSHD read dialog caption */
    string caption = _("Saving SSHD Configuration");

    integer steps = 2;
    
    Progress::New(caption, " ", steps, [
	    /* Progress stage 1/2 */
	    _("Write the SSHD settings"),
	    /* Progress stage 2/2 */
	    _("Adjust the SSHD service")
	], [
	    /* Progress step 1/2 */
	    _("Writing the SSHD settings..."),
	    /* Progress step 2/2 */
	    _("Adjusting the SSHD service..."),
	    Message::Finished()
	],
	""
    );

    sleep(sl);

    if(PollAbort()) return false;
    Progress::NextStage();
    /* Error message */
    if(!WriteSSHDSettings()) Report::Error (_("Cannot write SSHD settings."));
    sleep(sl);

    if(PollAbort()) return false;
    Progress::NextStage ();
    /* Error message */
    if(!WriteSSHDService()) Report::Error (Message::CannotAdjustService("sshd"));
    sleep(sl);

    Progress::NextStage ();
    sleep(sl);

    return true;
}

}

2.9.2. Configuration Handling

Almost all of this functionality is done in the scr/complex.ycp. Here you can see the final content of the file, it will be explained below:

/**
 * File:	include/sshd/complex.ycp
 * Package:	Configuration of sshd
 * Summary:	Dialogs handling and definitions
 * Authors:	John The Fish <john@thesmallfish.net>
 *
 * $Id: complex.ycp 13891 2004-02-05 15:16:57Z jtf $
 */

{

textdomain "sshd";

import "Label";
import "Popup";
import "Wizard";
import "Wizard_hw";
import "Sshd";
import "Confirm";
import "Report";

include "sshd/helps.ycp";

/**
 * Read settings dialog
 * @return `abort if aborted and `next otherwise
 */
symbol ReadDialog() {
    Wizard::RestoreHelp(HELPS["read"]:"");
    boolean ret = Sshd::Read();
    return ret ? `next : `abort;
}

/**
 * Write settings dialog
 * @return `abort if aborted and `next otherwise
 */
symbol WriteDialog() {
    Wizard::RestoreHelp(HELPS["write"]:"");
    boolean ret = Sshd::Write();
    return ret ? `next : `abort;
}

/**
 * Initializes the table of ports
 */
void InitPortsTable () {
    list <string> ports = Sshd::GetSSHDOption("Port");
    if (ports != nil && ports != []) {
	list <term> items = [];
	foreach (string port, ports, {
	    items = add (items, `item(`id(port), port));
	});
	// Redraw table of ports and enable modification buttons
	UI::ChangeWidget(`id("Port"), `Items, items);
	UI::ChangeWidget(`id("edit_port"), `Enabled, true);
	UI::ChangeWidget(`id("delete_port"), `Enabled, true);
    } else {
	// Redraw table of ports and disable modification buttons
	UI::ChangeWidget(`id("Port"), `Items, []);
	UI::ChangeWidget(`id("edit_port"), `Enabled, false);
	UI::ChangeWidget(`id("delete_port"), `Enabled, false);
    }
}

/**
 * Initializes the Server Configuration Dialog
 */
void InitServerConfigurationDialog() {
    InitPortsTable();

    foreach (string key, ["AllowTcpForwarding", "X11Forwarding", "Compression"], {
	UI::ChangeWidget(`id(key), `Value, (Sshd::GetSSHDOption(key) == ["yes"]));
    });
}

/**
 * Initializes the Login Settings Dialog
 */
void InitLoginSettingsDialog() {
    UI::ChangeWidget( `id("MaxAuthTries"), `ValidChars, "0123456789");
    list <string> MaxAuthTries = Sshd::GetSSHDOption("MaxAuthTries");
    UI::ChangeWidget(`id("MaxAuthTries"), `Value, MaxAuthTries[0]:"0");

    foreach (string key, ["PrintMotd", "PermitRootLogin",
	"PasswordAuthentication", "RSAAuthentication", "PubkeyAuthentication"], {
	UI::ChangeWidget(`id(key), `Value, (Sshd::GetSSHDOption(key) == ["yes"]));
    });
}

/**
 * Removes the port from list of current ports
 *
 * @param string port_number
 */
void DeletePort (string port) {
    Sshd::SetSSHDOption("Port", filter (
	string single_port, Sshd::GetSSHDOption("Port"), ``(single_port != port)
    ));
}

/**
 * Function handles the adding or editing port number.
 * When the current_port is not 'nil', the dialog will
 * allow to edit it.
 *
 * @param string current_port a port number to be edited or 'nil' when adding a new one
 */
void AddEditPortDialog (string current_port) {
    UI::OpenDialog(`opt(`decorated), `VBox(
        `MinWidth (30,
	    `HBox(
		`HSpacing(1),
        	`Frame(
            	    (current_port == nil ?
			/* A popup dialog caption */
			_("Add New Port")
			:
			/* A popup dialog caption */
			_("Edit Current Port")),
		    /* A text entry */
            	    `TextEntry(`id("port_number"), _("&Port"), (current_port == nil ? "":current_port))
        	),
		`HSpacing(1)
	    )
	),
	`VSpacing(1),
	`HBox(
	    `PushButton(`id(`ok), Label::OKButton()),
	    `HSpacing(1),
	    `PushButton(`id(`cancel), Label::CancelButton())
        )
    ));

    UI::ChangeWidget( `id("port_number"), `ValidChars, "0123456789");

    any ret = nil;
    while (true) {
        ret = UI::UserInput();
        if (ret == `ok) {
            string new_port = (string) UI::QueryWidget(`id("port_number"), `Value);
	    
	    if (new_port == "") {
		UI::SetFocus(`id("port_number"));
		Report::Error(_("Port number must not be empty."));
		continue;
	    }

    	    Sshd::SetSSHDOption("Port", add (Sshd::GetSSHDOption("Port"), new_port));
	    if (current_port != nil) DeletePort(current_port);
        }
	break;
    }

    UI::CloseDialog();
}

/**
 * Function handles Add, Edit and Delete buttons
 *
 * @param any action from "add_port", "edit_port" or "delete_port"
 */
void HandleServerConfigurationDialog(any action) {
    string selected_port = (string) UI::QueryWidget(`id("Port"), `CurrentItem);

    // Adding a new port
    if (action == "add_port") {
	AddEditPortDialog(nil);
    // Editing current port
    } else if (action == "edit_port") {
	AddEditPortDialog(selected_port);
    // Deleting current port
    } else if (action == "delete_port") {
	if (Confirm::DeleteSelected()) DeletePort(selected_port);
    } else {
	y2error("Unknown action %1", action);
    }
    
    InitPortsTable();
}

/**
 * Stores the current configuration from Server Configuration Dialog
 */
void StoreServerConfigurationDialog() {
    Sshd::SetModified();

    // Stores all boolean values and turns them to the "yes"/"no" notation
    foreach (string key, ["AllowTcpForwarding", "X11Forwarding", "Compression"], {
	Sshd::SetSSHDOption(
	    key,
	    [ (((boolean) UI::QueryWidget(`id(key), `Value) == true) ? "yes":"no") ]
	);
    });
}

/**
 * Stores the current configuration from Login Settings  Dialog
 */
void StoreLoginSettingsDialog() {
    Sshd::SetModified();

    // Stores an integer value as a string
    Sshd::SetSSHDOption(
	"MaxAuthTries",
	[ (string) UI::QueryWidget(`id("MaxAuthTries"), `Value) ]
    );

    // Stores all boolean values and turns them to the "yes"/"no" notation
    foreach (string key, ["PrintMotd", "PermitRootLogin",
	"PasswordAuthentication", "RSAAuthentication", "PubkeyAuthentication"], {
	Sshd::SetSSHDOption(
	    key,
	    [ (((boolean) UI::QueryWidget(`id(key), `Value) == true) ? "yes":"no") ]
	);
    });
}

}

Functionality contained in this file is explained in parts.

2.9.2.1. Standard File Header

/**
 * File:	include/sshd/complex.ycp
 * Package:	Configuration of sshd
 * Summary:	Dialogs handling and definitions
 * Authors:	John The Fish <john@thesmallfish.net>
 *
 * $Id: complex.ycp 13891 2004-02-05 15:16:57Z jtf $
 */

{

textdomain "sshd";

This is here only for the completeness of the file. Nevertheles you can see at least the default style of the file header.

2.9.2.2. Imported Modules and Included Files

import "Label";
import "Popup";
import "Wizard";
import "Wizard_hw";
import "Sshd";
import "Confirm";
import "Report";

include "sshd/helps.ycp";

All YCP or Perl modules have their documentation here. Take note of Sshd module being imported too.

2.9.2.3. Read and Write Dialogs

/**
 * Read settings dialog
 * @return `abort if aborted and `next otherwise
 */
symbol ReadDialog() {
    Wizard::RestoreHelp(HELPS["read"]:"");
    boolean ret = Sshd::Read();
    return ret ? `next : `abort;
}

/**
 * Write settings dialog
 * @return `abort if aborted and `next otherwise
 */
symbol WriteDialog() {
    Wizard::RestoreHelp(HELPS["write"]:"");
    boolean ret = Sshd::Write();
    return ret ? `next : `abort;
}

Standard ReadDialog() and WriteDialog() functions which are called from the src/wizards.ycp file. They call the appropriate functions of the Sshd module and return a symbol to the Sequencer.

2.9.2.4. UI Data Initialization

/**
 * Initializes the table of ports
 */
void InitPortsTable () {
    list <string> ports = Sshd::GetSSHDOption("Port");

    if (ports != nil && ports != []) {
	list <term> items = [];
	foreach (string port, ports, {
	    items = add (items, `item(`id(port), port));
	});

	// Redraw table of ports and enable modification buttons
	UI::ChangeWidget(`id("Port"), `Items, items);
	UI::ChangeWidget(`id("edit_port"), `Enabled, true);
	UI::ChangeWidget(`id("delete_port"), `Enabled, true);
    } else {
	// Redraw table of ports and disable modification buttons
	UI::ChangeWidget(`id("Port"), `Items, []);
	UI::ChangeWidget(`id("edit_port"), `Enabled, false);
	UI::ChangeWidget(`id("delete_port"), `Enabled, false);
    }
}

/**
 * Initializes the Server Configuration Dialog
 */
void InitServerConfigurationDialog() {
    InitPortsTable();

    foreach (string key, ["AllowTcpForwarding", "X11Forwarding", "Compression"], {
	UI::ChangeWidget(`id(key), `Value, (Sshd::GetSSHDOption(key) == ["yes"]));
    });
}

/**
 * Initializes the Login Settings Dialog
 */
void InitLoginSettingsDialog() {
    UI::ChangeWidget( `id("MaxAuthTries"), `ValidChars, "0123456789");
    list <string> MaxAuthTries = Sshd::GetSSHDOption("MaxAuthTries");
    UI::ChangeWidget(`id("MaxAuthTries"), `Value, MaxAuthTries[0]:"0");

    foreach (string key, ["PrintMotd", "PermitRootLogin",
	"PasswordAuthentication", "RSAAuthentication", "PubkeyAuthentication"], {
	    UI::ChangeWidget(`id(key), `Value, (Sshd::GetSSHDOption(key) == ["yes"]));
    });
}

Functions InitServerConfigurationDialog() and InitLoginSettingsDialog() initialize the appropriate dialogs and fill them up with the current data.

Function InitLoginSettingsDialog() also sets the valid characters for the MaxAuthTries text entry to numbers only.

Function InitPortsTable() is called from the InitServerConfigurationDialog(). It sets the list of currently configured used ports into the table and enables Edit and Delete buttons when some ports are configured. In the case of no ports configured disables those two buttons.

2.9.2.5. Handling Add, Edit and Delete Buttons

/**
 * Removes the port from list of current ports
 *
 * @param string port_number
 */
void DeletePort (string port) {
    Sshd::SetSSHDOption("Port", filter (
	string single_port, Sshd::GetSSHDOption("Port"), ``(single_port != port)
    ));
}

/**
 * Function handles the adding or editing port number.
 * When the current_port is not 'nil', the dialog will
 * allow to edit it.
 *
 * @param string current_port a port number to be edited or 'nil' when adding a new one
 */
void AddEditPortDialog (string current_port) {
    UI::OpenDialog(`opt(`decorated), `VBox(
	`MinWidth (30,
	    `HBox(
		`HSpacing(1),
		`Frame(
		    (current_port == nil ?
		    /* A popup dialog caption */
		    _("Add New Port")
		    :
		    /* A popup dialog caption */
		    _("Edit Current Port")),
		    /* A text entry */
		    `TextEntry(`id("port_number"), _("&Port"), (current_port == nil ? "":current_port))
		),
		`HSpacing(1)
	    )
	),
	`VSpacing(1),
	`HBox(
	    `PushButton(`id(`ok), Label::OKButton()),
	    `HSpacing(1),
	    `PushButton(`id(`cancel), Label::CancelButton())
	)
    ));

    UI::ChangeWidget( `id("port_number"), `ValidChars, "0123456789");

    any ret = nil;
    while (true) {
	ret = UI::UserInput();
	if (ret == `ok) {
	    string new_port = (string) UI::QueryWidget(`id("port_number"), `Value);

	    if (new_port == "") {
		UI::SetFocus(`id("port_number"));
		Report::Error(_("Port number must not be empty."));
		continue;
	    }

	    Sshd::SetSSHDOption("Port", add (Sshd::GetSSHDOption("Port"), new_port));

	    if (current_port != nil) DeletePort(current_port);
	}

	break;
    }

    UI::CloseDialog();
}

/**
 * Function handles Add, Edit and Delete buttons
 *
 * @param any action from "add_port", "edit_port" or "delete_port"
 */
void HandleServerConfigurationDialog(any action) {
    string selected_port = (string) UI::QueryWidget(`id("Port"), `CurrentItem);

    // Adding a new port
    if (action == "add_port") {
	AddEditPortDialog(nil);
    // Editing current port
    } else if (action == "edit_port") {
	AddEditPortDialog(selected_port);
    // Deleting current port
    } else if (action == "delete_port") {
	if (Confirm::DeleteSelected()) DeletePort(selected_port);
    } else {
	y2error("Unknown action %1", action);
    }
      
    InitPortsTable();
}

The HandleServerConfigurationDialog() function handles the dialog events when any of Add, Edit or Delete buttons are pressed.

  • Add button - Calls the AddEditPortDialog() function with nil as the parameter which means that no port is going to be edited, just added. This AddEditPortDialog() function opens up a small pop-up window containing the Add New Port text entry and OK and Cancel buttons. When the OK button is pressed, the new port is added into the list of current ports and the dialog is closed.

  • Edit button - Behaves almost the same but it calls the same function with the current port as the parameter. The text entry name in the pop-up dialog is Edit Current Port then and when the OK button is pressed, it also removes the old port.

  • Delete button - Calls the standardized Confirm::DeleteSelected() function which should be called every time user tries to remove such entry from a table. Then, if user confirms the deleting, it calls DeletePort() function which deletes the selected port.

2.9.2.6. Capturing the Current Configuration from UI

/**
 * Stores the current configuration from Server Configuration Dialog
 */
void StoreServerConfigurationDialog() {
    Sshd::SetModified();

    // Stores all boolean values and turns them to the "yes"/"no" notation
    foreach (string key, ["AllowTcpForwarding", "X11Forwarding", "Compression"], {
    Sshd::SetSSHDOption(
    key,
    [ (((boolean) UI::QueryWidget(`id(key), `Value) == true) ? "yes":"no") ]
    );
    });
    }

/**
 * Stores the current configuration from Login Settings  Dialog
 */
void StoreLoginSettingsDialog() {
    Sshd::SetModified();

    // Stores an integer value as a string
    Sshd::SetSSHDOption(
	"MaxAuthTries",
	[ (string) UI::QueryWidget(`id("MaxAuthTries"), `Value) ]
    );

    // Stores all boolean values and turns them to the "yes"/"no" notation
    foreach (string key, ["PrintMotd", "PermitRootLogin",
	"PasswordAuthentication", "RSAAuthentication", "PubkeyAuthentication"], {
	    Sshd::SetSSHDOption(
		key,
		[ (((boolean) UI::QueryWidget(`id(key), `Value) == true) ? "yes":"no") ]
	    );
    });
}

These functions get the UI widgets statuses and store them using the Sshd::SetSSHDOption() function. You can see how the boolean value of every check box is read and transformed to the yes/no notation which is used in the configuration file. All options have to be lists of strings.

For reading the widget's status is used the UI::QueryWidget(`id(widget_id), `Value) function.

Widget with id MaxAuthTries has not a boolean value so has its own call of UI::QueryWidget() and Sshd::SetSSHDOption().

2.9.2.7. Standard End of File

}

This is the standard End of File left here just for the file completeness.

2.9.3. Adding the Complex Connection into the Dialogs

The module is nearly finished. The only thing we have to do is to add calling those Init*, Store* and Handle* functions into the dialogs definitions in the src/dialogs.ycp file.

This is the final content, added functions are explained below:

/**
 * File:	include/sshd/dialogs.ycp
 * Package:	Configuration of sshd
 * Summary:	Dialogs definitions
 * Authors:	John The Fish <john@thesmallfish.net>
 *
 * $Id: dialogs.ycp 13879 2004-02-05 11:29:30Z jtf $
 */

{

textdomain "sshd";

import "Label";
import "Wizard";
import "Sshd";

include "sshd/helps.ycp";

/**
 * Server Configuration Dialog
 *
 * @return any dialog result
 */
any ServerConfigurationDialog() {

    /* a dialog caption */
    string caption = _("SSHD Server Configuration");

    term contents = `VBox (
	`Left(`Label(_("SSHD TCP Ports"))),
	`Left(
	    `VBox (
		`MinSize( 40, 5,
		    /* A table header */
		    `Table(`id("Port"), `header("Port"), [])
		),
		`HBox (
		    /* a push button */
		    `PushButton(`id("add_port"), _("&Add...")),
		    /* a push button */
		    `PushButton(`id("edit_port"), _("&Edit...")),
		    /* a push button */
		    `PushButton(`id("delete_port"), _("&Delete"))
		),
		`VSpacing(1),
		`Frame (
		    /* a dialog frame caption */
		    _("Server Features"),
		    `VBox (
			/* a check box */
			`Left(`CheckBox(`id("AllowTcpForwarding"), _("Allow &TCP Forwarding"))),
			/* a check box */
			`Left(`CheckBox(`id("X11Forwarding"),      _("Allow &X11 Forwarding"))),
			/* a check box */
			`Left(`CheckBox(`id("Compression"),        _("Allow &Compression")))
		    )
		),
		`VStretch()
	    )
	)
    );

    Wizard::SetContentsButtons(caption, contents, HELPS["server_configuration"]:"",
	    Label::BackButton(), Label::NextButton());
    Wizard::DisableBackButton();

    InitServerConfigurationDialog();

    any ret = nil;
    while(true) {

	ret = UI::UserInput();

	/* abort? */
	if(ret == `abort) {
	    if(Sshd::Abort()) break;
	    else continue;
	/* next */
	} else if(ret == `next) {
	    StoreServerConfigurationDialog();
            break;
	/* add, edit or delete */
	} else if (ret == "add_port" || ret == "edit_port" || ret == "delete_port") {
	    HandleServerConfigurationDialog(ret);
	/* unknown */
        } else {
            y2error("unexpected retcode: %1", ret);
            continue;
        }
    }

    Wizard::RestoreBackButton();

    return ret;
}

/**
 * Login Settings Dialog
 *
 * @return any dialog result
 */
any LoginSettingsDialog() {

    /* a dialog caption */
    string caption = _("SSHD Server Login Settings");

    term contents = `VBox(
	`Frame (
	    _("General Login Settings"),
	    `VBox (
		/* A check box */
		`Left(`CheckBox(`id("PrintMotd"),            _("Print &Message of the Day After Login"))),
		/* A check box */
		`Left(`CheckBox(`id("PermitRootLogin"),      _("Permi&t Root Login")))
	    )
	),
	`VSpacing(1),
	`Frame (
	    _("Authentication Settings"),
	    `VBox (
		/* A text entry */
		`Left(`TextEntry(`id("MaxAuthTries"),           _("Ma&ximum Authentication Tries"))),
		/* A check box */
		`Left(`CheckBox (`id("PasswordAuthentication"), _("Pa&sswordAuthentication"))),
		/* A check box */
		`Left(`CheckBox (`id("RSAAuthentication"),      _("RSA Authenti&cation"))),
		/* A check box */
		`Left(`CheckBox (`id("PubkeyAuthentication"),   _("Public &Key Authentication")))
	    )
	),
	`VStretch()
    );

    Wizard::SetContentsButtons(caption, contents, HELPS["login_settings"]:"",
	    Label::BackButton(), Label::NextButton());
    Wizard::SetNextButton(`next, Label::AcceptButton());

    InitLoginSettingsDialog();

    any ret = nil;
    while(true) {

	ret = UI::UserInput();

	/* abort? */
	if(ret == `abort) {
	    if(Sshd::Abort()) break;
	    else continue;
	/* next */
	} else if(ret == `next) {
	    StoreLoginSettingsDialog();
            break;
	} else if(ret == `back) {
	    break;
	/* unknown */
        } else {
            y2error("unexpected retcode: %1", ret);
            continue;
        }
    }

    Wizard::RestoreNextButton();

    return ret;
}

}

2.9.3.1. Server Configuration Dialog

Into the ServerConfigurationDialog() function between the Wizard::DisableBackButton(); call and the while-loop add calling the dialog initialization function:

InitServerConfigurationDialog();

Into the ServerConfigurationDialog() function into the while-loop add the function call for storing configuration and for handling the Add, Edit and Delete buttons. The mentioned part of the while-loop should look like this one:

    while(true) {

    ret = UI::UserInput();
      
    /* abort? */
    if(ret == `abort) {
	if(Sshd::Abort()) break;
	else continue;
    /* next */
    } else if(ret == `next) {
	StoreServerConfigurationDialog();
	break;
    /* add, edit or delete */
    } else if (ret == "add_port" || ret == "edit_port" || ret == "delete_port") {
	HandleServerConfigurationDialog(ret);
    /* unknown */
    } else {
	y2error("unexpected retcode: %1", ret);
	continue;
    }
}

2.9.3.2. Login Settings Dialog

Into the LoginSettingsDialog() function between the Wizard::SetNextButton(`next, Label::AcceptButton()); call and the while-loop add calling the dialog initialization function:

InitLoginSettingsDialog();

Into the LoginSettingsDialog() function into the while-loop add the function call for storing configuration. It should look like this one:

    while(true) {

    ret = UI::UserInput();

    /* abort? */
    if(ret == `abort) {
	if(Sshd::Abort()) break;
	else continue;
    /* next */
    } else if(ret == `next) {
	StoreLoginSettingsDialog();
	break;
    } else if(ret == `back) {
	break;
    /* unknown */
    } else {
	y2error("unexpected retcode: %1", ret);
	continue;
    }
}

2.9.4. Checking the Progress

And that's it!

[Important]Important

Do not forget to run command sudo make install after you finish editing the files in the src/ directory to get the new versions installed.

Now you can run your new sshd module with command: /sbin/yast2 sshd.