We have shown how really easy is to create a new YaST module, how to install it and run. Now you should learn more about source files inside the project: what they are good for, what they contain and what they handle.
This picture shows the disposal of project files to the particular
layers of YaST. This is how the new YaST modules is supposed to be divided
into files and corresponds with final
version. For instance, the file sshd.scr
doesn't exist in a newly-generated template.
This part is rather complex and not so important - you can skip it if you want.
What is done between calling the /sbin/yast2 sshd command and opening the UI up?
Script /sbin/yast2
defines internal variables,
such as LC_CTYPE. Then it checks whether you have
Qt or ncurses available and
calls the /usr/lib/YaST2/bin/y2base
binary.
Binary /usr/lib/YaST2/bin/y2base
is called
with "sshd qt" or "sshd
ncurses" as two program arguments. The
y2base checks whether there is any
sshd.ycp
client in the
/usr/share/YaST2/clients/
directory and executes
it.
The sshd.ycp
imports some binary modules such
as Progress, Report or
CommandLine, defines the command line options and
includes the wizards.ycp
file.
The wizards.ycp
file includes the
dialogs.ycp
file with defined dialogs and
complex.ycp
file with defined complex dialogs
functions and then dialog sequences are defined.
After closing the UI, the control is returned back to the
sshd.ycp
client which finishes the operation and
exits.
You will find source files under the sshd/src/
path. All those files are copied into the
/usr/share/YaST2/
path during the make
install procedure. Let's clarify what is the purpose of these
files on some simple examples. Files listed here are simplified, most of
the content is replaced with a "...
" mark. Important
parts are emphasized. Files are sorted in a logical order.
Application definitions for KDE, AutoYaST, YaST Control Center...
[Desktop Entry] Type=Application ... X-SuSE-YaST-Group=Misc ... Icon=yast-sshd Exec=/sbin/yast2 sshd ... Name=Sshd GenericName=sshd ...
These settings are used by another applications than the YaST SSHD Configuration itself. They define the module behavior and identification. See the YaST Desktop Files document for more details.
A basic application client also with a command line interface definition. Command line support will be described in another tutorial.
Clients are stored under the
/usr/share/YaST2/clients/
path
/** * File: clients/sshd.ycp ... */ { textdomain "sshd"; /* The main () */ y2milestone ("----------------------------------------"); y2milestone ("Sshd module started"); import "Progress"; import "Report"; import "Summary"; import "CommandLine"; include "sshd/wizards.ycp"; map cmdline_description = $[ ... "guihandler" : SshdSequence, ... ]; /* is this proposal or not? */ boolean propose = false; ... /* main ui function */ any ret = nil; if (propose) ret = SshdAutoSequence(); else ret = CommandLine::Run(cmdline_description); y2debug("ret=%1", ret); /* Finish */ y2milestone("Sshd module finished"); y2milestone("----------------------------------------"); return ret; /* EOF */ }
This client is called by YaST binary, it includes the
wizard.ycp
file with defined sequences and calls
the CommandLine::Run()
wich, in our case, calls the
SshdSequence()
workflow from
wizard.ycp
.
![]() | Note |
---|---|
We have also other clients
|
Contains dialogs sequence definitions that are used by clients.
Wizards are stored under the
/usr/share/YaST2/include/<module-name>/
path.
/** * File: include/sshd/wizards.ycp ... */ { textdomain "sshd"; import "Sequencer"; import "Wizard"; include "sshd/complex.ycp"; include "sshd/dialogs.ycp"; /** * Add a configuration of sshd * @return sequence result */ any AddSequence() { ... } /** * Main workflow of the sshd configuration * @return sequence result */ any MainSequence() { map aliases = $[ "summary" : ``( SummaryDialog() ), "overview" : ``( OverviewDialog() ), "add" : [ ``( AddSequence() ), true ], "edit" : [ ``( AddSequence() ), true ] ]; map sequence = $[ "ws_start" : "summary", "summary" : $[ `abort : `abort, `next : `next, `overview : "overview", ], "overview" : $[ `abort : `abort, `next : `next, `add : "add", `edit : "edit", ], "add" : $[ `abort : `abort, `next : "overview", ], "edit" : $[ `abort : `abort, `next : "overview", ] ]; any ret = Sequencer::Run(aliases, sequence); return ret; } /** * Whole configuration of sshd * @return sequence result */ any SshdSequence() { map aliases = $[ "read" : [ ``( ReadDialog() ), true ], "main" : ``( MainSequence() ), "write" : [ ``( WriteDialog() ), true ] ]; map sequence = $[ "ws_start" : "read", "read" : $[ `abort : `abort, `next : "main" ], "main" : $[ `abort : `abort, `next : "write" ], "write" : $[ `abort : `abort, `next : `next ] ]; Wizard::CreateDialog(); any ret = Sequencer::Run(aliases, sequence); UI::CloseDialog(); return ret; } /** * Whole configuration of sshd but without reading and writing. * For use with autoinstallation. * @return sequence result */ any SshdAutoSequence() { ... } /* EOF */ }
First of all, complex.ycp
and
dialogs.ycp
are included because they define
dialogs that are used in dialog sequences.
After that, aliases to these dialogs are defined in
aliases
map. Then sequences pointing to these
aliases are defined in a sequence
map.
Sequence handlers defines the relationship between
event returned by a dialog function and
action called by sequencer.
At the end, the SshdSequence()
is called
from the sshd.ycp
client.
![]() | Important |
---|---|
Never define the `back event, dialog wizard handles it by itself. |
![]() | Note |
---|---|
Please, take note, that sequences can call another ones:
Sequence called from a client should contain
Screenshot of the Wizard window: ![]() |
Dialogs definitions and their simple handling.
Dialogs are stored under the
/usr/share/YaST2/include/<module-name>/
path
/** * File: include/sshd/dialogs.ycp ... */ { textdomain "sshd"; import "Label"; import "Wizard"; import "Sshd"; include "sshd/helps.ycp"; /** * Configure1 dialog * @return dialog result */ any Configure1Dialog () { /* Sshd configure1 dialog caption */ string caption = _("Sshd Configuration"); /* Sshd configure1 dialog contents */ term contents = `Label (_("First part of configuration of sshd")); Wizard::SetContentsButtons(caption, contents, HELPS["c1"]:"", Label::BackButton(), Label::NextButton()); any ret = nil; while (true) { ret = UI::UserInput(); /* abort? */ if(ret == `abort || ret == `cancel) { if(ReallyAbort()) break; else continue; } else if(ret == `next || ret == `back) { break; } else { y2error("unexpected retcode: %1", ret); continue; } } return ret; } /** * Configure2 dialog * @return dialog result */ any Configure2Dialog () { ... } /* EOF */ }
This file defines two dialog functions
Configure1Dialog()
and
Configure2Dialog()
. These functions set the behavior
by calling the Wizard::SetContentsButtons()
function. We can use this advanced function because we have used the
Wizard::CreateDialog()
in the
wizards.ycp
.
After the dialog is created there is a loop function that handles the user input.
Help texts are included from the helps.ycp
as
the HELPS
map.
![]() | Note |
---|---|
Every string we want to mark for translation uses
the Translations insist on defined
|
YCP include which contains complex functions for dialogs handling.
/** * File: include/sshd/complex.ycp ... */ { textdomain "sshd"; import "Label"; ... import "Sshd"; include "sshd/helps.ycp"; ... /** * Read settings dialog * @return `abort if aborted and `next otherwise */ symbol ReadDialog() { Wizard::RestoreHelp(HELPS["read"]:""); // Sshd::AbortFunction = PollAbort; if (!Confirm::MustBeRoot()) return `abort; boolean ret = Sshd::Read(); return ret ? `next : `abort; } /** * Write settings dialog * @return `abort if aborted and `next otherwise */ symbol WriteDialog() { ... } /** * Summary dialog * @return dialog result */ any SummaryDialog() { .. } /** * Overview dialog * @return dialog result */ any OverviewDialog() { /* Sshd overview dialog caption */ string caption = _("Sshd Overview"); list overview = Sshd::Overview(); /* FIXME table header */ term contents = Wizard_hw::ConfiguredContent( /* Table header */ `header(_("Number"), _("Sshd")), overview, nil, nil, nil, nil ); contents = Wizard_hw::SpacingAround(contents, 1.5, 1.5, 1.0, 1.0); Wizard::SetContentsButtons(caption, contents, HELPS["overview"]:"", Label::BackButton(), Label::FinishButton()); any ret = nil; while (true) { ret = UI::UserInput(); /* abort? */ if(ret == `abort || ret == `cancel) { if(ReallyAbort()) break; else continue; } /* add */ else if(ret == `add_button) { ret = `add; break; } /* edit */ else if(ret == `edit_button) { ret = `edit; break; } /* delete */ else if(ret == `delete_button) { continue; } else if(ret == `next || ret == `back) { break; } else { y2error("unexpected retcode: %1", ret); continue; } } return ret; } /* EOF */ }
This file is very similar to the
dialogs.ycp
one but here should be more complex
dialogs with handling.
The most important is the ReadDialog()
that calls the Sshd::Read()
—a global function
from the Sshd
YCP module.
Dialog functions defined in the file can be seen used in the
wizards.ycp
file in map of aliases.
Help texts for every dialog.
Helps are stored under the
/usr/share/YaST2/include/<module-name>/
path
/** * File: include/sshd/helps.ycp ... */ { textdomain "sshd"; /** * All helps are here */ map HELPS = $[ /* Read dialog help 1/2 */ "read" : _("<p><b><big>Initializing sshd Configuration</big></b><br> Please wait...<br></p> ") + /* Read dialog help 2/2 */ _("<p><b><big>Aborting Initialization:</big></b><br> Safely abort the configuration utility by pressing <b>Abort</b> now.</p> "), ... /* Configure1 dialog help 1/2 */ "c1" : _("<p><b><big>Configuration Part One</big></b><br> Press <b>Next</b> to continue. <br></p>") + /* Configure1 dialog help 2/2 */ _("<p><b><big>Selecting Something</big></b><br> It is not possible. You must code it first. :-) </p>"), ... ]; /* EOF */ }
This file is included by the dialogs.ycp
and complex.ycp
. All texts are defined in the
HELPS
map.
All help texts are written in HTML but they must follow the Text Style Guide.
![]() | Note |
---|---|
Please, take note, that help texts are divided into smaller chunks by logic parts. For translators, it's also easier to translate only a short string after you change something. |
YCP module with global API that can be used from another modules or projects.
All modules are stored under the
/usr/share/YaST2/modules/
path.
/** * File: modules/Sshd.ycp * Package: Configuration of sshd * Summary: Sshd settings, input and output functions * Authors: John The Fish <john@thesmallfish.net> * * $Id: Sshd.ycp 27914 2006-02-13 14:32:08Z jtf $ * * Representation of the configuration of sshd. * Input and output routines. */ { module "Sshd"; textdomain "sshd"; ... /** * Read all sshd settings * @return true on success */ global boolean Read() { /* Sshd read dialog caption */ string caption = _("Initializing sshd Configuration"); ... integer steps = 4; ... Progress::New( caption, " ", steps, [ ... ], [ ... ], "" ); ... Progress::NextStage(); ... if(Abort()) return false; modified = false; return true; } /** * Write all sshd settings * @return true on success */ global boolean Write() { /* Sshd write dialog caption */ string caption = _("Saving sshd Configuration"); ... integer steps = 4; ... Progress::New( caption, " ", steps, [ ... ], [ ... ], "" ); ... Progress::NextStage(); ... if(Abort()) return false; return true; } ... }
The module
keyword sets the module namespace.
You can acces the global variables and function with the
module_name::
prefix (e.g.,
Sshd::
for Sshd
module).
Global Read
and Write
functions also define the Progress bar that is
changed by calling Progress::NextStage()
function.
Read
and Write
functions
should always return a boolean whether they succeeded or
failed.
![]() | Note |
---|---|
Our project also contains the |