This part will show you how to connect the Sshd module data with the UI.
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; } }
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.
/** * 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.
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.
/** * 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.
/** * 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.
/** * 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.
/** * 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().
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; } }
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; } }
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; } }