simple specification in the YaST module
automatic help
automatic checking of arguments (types, format)
interactive session without UI
The aim of the module is to provide as automatic interface as possible for controlling the module. To support interactive sessions, the YaST module needs to provide command-handling loop similar to concept of event-handling in GUIs.
If the module does not need to do any special handling of the actions, it can use the "commandline" include (a wrapper for CommandLine). The include defines a single function "CommandLineRun()", which implements a standard event-loop and returns true on success. The module just needs to specify handlers for actions, user-interface, initialization and finishing.
Example 1.2. Simple CommandLine definition
{ define void deleteHandler( map options ) ``{ string dev = options["device"]:""; CommandLine::Print("Deleting: "+dev); if(Lan::Delete(dev) && Lan::Commit()) CommandLine::Print("Success"); else CommandLine::Print("Error"); } ... map cmdline = $[ "help" : "Configuration of network cards", "id" : "lan", "guihandler": ``(LanSequence()), "initialize": ``(Lan::Read()), "finish" : ``(Lan::Finish()), "actions" : $[ "list" : $[ "help" : "display configuration summary", "example" : "lan list configured", "handler" : ``(listHandler()) ], "add" : $[ "help" : "add a network card", "handler" : ``(addHandler()) ], "delete" : $[ "help" : "delete a network card", "handler" : ``(deleteHandler()) ] ], ... ]; import "Lan"; include "commandline/commandline.ycp"; CommandLineRun(cmdline); /* EOF */ }
The UI handler is specified in the "guihandler" key of the command line description map. It must take no arguments and return boolean, true on success.
The initialize resp. finish handler is specified by the "initialize" resp. "finish" key of the description map. They do not take any arguments and must return boolean, true on success. Notice that "initialize" and "finish" handlers are not used if a user asked for GUI (guihandler is used instead and therefore it must do initializing and finishing on its own). The handler for an action is specified in the "handler" key of the action description map. Each handler must take a single argument containing the options entered by the user and return a boolean, true on success. If the handler returns "false", the command line will abort for non-interactive handling. This is useful for handling error states. However, a handler must use CommandLine::Abort() to stop event-handling in the interactive mode.
The CommandLine module is stateful, i.e., it contains a current state of command line parsing/interactive console control of a YaST module. Therefore, the CommandLineRun() handles the commands as follows:
standard UI start of a module - CommandLine::StartGUI will return true in this case
command given as an argument - the inner while loop will be done only once
interactive controling of a module - the while loop will be done as long as the user does not enter "exit" or "quit"
shows the help text for the command
starts interactive session without UI
shows the command-specific help
option to supress the progress messages
These are available in interactive mode only:
![]() | Note |
---|---|
If the map does not follow the following rules, an error will be emitted into log. |
The map describing the command line interface of a module must contain "id" entry containing the name of the module. The map can contain global help text, a list of actions, a list of options and a list for mapping of options to actions (which optionscan be used for which actions). Each action and option must have its help text.
Actions is a map with the action name as a key. For each action, it isnecessary to provide the help text. Optionally, action can have defined an example of usage.
A list of flags can be specified to change the default behavior of the parameter checker. It is a list with key "options". Currently known flags:
for unknown parameters, do not check their validity can be used for passing unknown options into the handler
Example 1.3. Actions map definition
"actions" : $[ "list" : $[ "help": "display configuration summary", "example": "lan list configured", "options": [ "non_strict" ] ], "add" : $[ "help": "add a network card" ], ... ],
Options is a map with the option name as a key. For each option, it is necessary to provide a description text in "help" key, and a "type" key (for options with arguments only). Optionally, an option can contain an example.
There are two kinds of options: flags and options, which require an argument.For flags ommit type key, or specify type as "". A type is a string describing the type.basic types supported are string, boolean, integer.
Special types:
In this case, you need to specify "typespec" key containing the regular expression the argument should be matched against.
In this case, the typespec key must contain a list of possible values as strings
Example 1.4. Options map definition
"options" : $[ "propose" : $[ "help": "propose a configuration", "example": "lan add propose", "type": "" ], "device": $[ "help": "device ID", "type": "string", "example": "lan add device=eth0" ], "blem" : $[ "help": "other argument (without spaces)", "type": "regex", "typespec": "^[^ ]+$" ], "atboot": $[ "help": "should be brought up at boot?", "type": "enum", "typespec": ["yes","no"] }
The actions and options are grouped together using mappings. Currently, you can mapan action to a set of options. You can't specify required/optional options, all ofthem are optional.
Example 1.5. Mappings between actions and their options
"mappings" $[ "list" : [ "configured", "unconfigured" ], "add" : [ "device", "ip", "netmask", "blem" ], "delete": [ "device" ] ]
If you need to write your own event loop, this is a part of the CommandLine API useful for this:
returns true, if the user asked to start up the module GUI
reads a new command line in interactive mode, splits the arguments into a list
parse (and scan if needed) next command and return its map
lower-level function to parse the command line, check the validity
returns true, if the last command was already returned
returns true, if the user asked to cancel the changes
prints the string and then a message how to obtain the help
prints the string
same as CommandLine::Print, but the string is printed only in verbose mode
Example 1.6. Example of an event-loop
import "CommandLine"; if( ! CommandLine::Init( description_of_commands, Args() ) ) return; if( CommandLine::StartGUI() ) { <do standard GUI module> return; } <initialize call> while( ! CommandLine::Done() ) { map command = CommandLine::Command(); <handle commands here> } <finish call>
The CommandLine::Init() returns boolean whether the module has something to do. If the user only requested help or the arguments passed were not correct, the module should stop processing.
The CommandLine::Command() will do the user interaction if necessary. Also, it will handle all supported"system" commands, like "help", "exit" and "quit".
![]() | Note |
---|---|
In interactive mode, the communication uses /dev/tty. In non-interactive commandline mode it prints everything to standard error output. |
For an example without using the event-loop provided by the
CommandLine module, see lan-simple.ycp
.
/** * File: clients/lan.ycp * Package: Network configuration * Summary: Network cards main file * Authors: Michal Svec <msvec@suse.cz> * * $Id: lan-simple.ycp 10158 2003-06-23 12:48:40Z visnov $ * * Main file for network card configuration. * Uses all other files. */ { /*** * <h3>Network configuration</h3> */ import "CommandLine"; include "network/lan/wizards.ycp"; /** * Command line definition */ map cmdline = $[ "help" : "Configuration of network cards", "id" : "lan", "actions" : $[ "list" : $[ "help" : "display configuration summary", "example" : "lan list configured" ], "add" : $[ "help" : "add a network card" ], "delete" : $[ "help" : "delete a network card" ] ], "options" : $[ "propose" : $[ "help" : "propose a configuration", "example" : "lan add propose", "type" : "" ], "configured" : $[ "help" : "list only configured cards" ], "unconfigured" : $[ "help" : "list only not configured cards" ], "device" : $[ "help" : "device ID", "type" : "string", "example" : "lan add device=eth0" ], "ip" : $[ "help": "device address", "type": "ip" ], "netmask" : $[ "help": "network mask", "type": "netmask" ], ], "mappings" : $[ "list" : [ "configured", "unconfigured" ], "add" : [ "device", "ip", "netmask" ], "delete": [ "device" ], ] ]; /* The main () */ y2milestone("----------------------------------------"); y2milestone("Lan module started"); /* Initialize the arguments */ if(!CommandLine::Init(cmdline, Args())) { y2error("Commandline init failed"); return false; } /* Start GUI */ if(CommandLine::StartGUI()) { any ret = nil; ret = LanSequence(); y2debug("ret=%1", ret); y2milestone("Lan module finished"); y2milestone("----------------------------------------"); return ret; } /* Init */ CommandLine::Print("Initializing"); import "Lan"; import "Progress"; CommandLine::Print("Reading"); Progress::off(); Lan::Read(); /* Init variables */ string command = ""; list flags = []; map options = $[]; string exit = ""; list l = []; while(!CommandLine::Done()) { map m = CommandLine::Command(); command = m["command"]:"exit"; options = m["options"]:$[]; /* list */ if(command == "list") { CommandLine::Print("\nSummary\n"); string summary = sformat("%1\n", Lan::Summary(false)); CommandLine::Print(summary); } /* del */ else if(command == "delete") { string dev = options["device"]:""; CommandLine::Print("Deleting: "+dev); if(Lan::Delete(dev) && Lan::Commit()) CommandLine::Print("Success"); else CommandLine::Print("Error"); } /* add */ else if(command == "add") { string dev = options["device"]:""; } else { /* maybe we got "exit" or "quit" */ if( !CommandLine::Done() ) { CommandLine::Print("Unknown command (should not happen)"); continue; } } } if(!CommandLine::Aborted()) { CommandLine::Print("Writing"); Lan::Write(); CommandLine::Print("Finish"); } else { CommandLine::Print("Quit (without saving)"); } /* Finish */ y2milestone("Lan module finished"); y2milestone("----------------------------------------"); /* EOF */ }
For an example with the standard event-loop provided by the
commandline.ycp include, see lan-simpler.ycp
.
/** * File: clients/lan.ycp * Package: Network configuration * Summary: Network cards main file * Authors: Michal Svec <msvec@suse.cz> * * $Id: lan-simpler.ycp 24831 2005-08-11 15:51:55Z visnov $ * * Main file for network card configuration. * Uses all other files. */ { /*** * <h3>Network configuration</h3> */ import "CommandLine"; include "network/lan/wizards.ycp"; /** * Command line definition */ map cmdline = $[ "help" : "Configuration of network cards", "id" : "lan", "guihandler": ``(LanSequence()), "initialize": ``(Lan::Read()), "finish" : ``(Lan::Write()), "actions" : $[ "list" : $[ "help" : "display configuration summary", "example" : "lan list configured", "handler" : ``(listHandler()) ], "add" : $[ "help" : "add a network card", "handler" : ``(addHandler()) ], "delete" : $[ "help" : "delete a network card", "handler" : ``(deleteHandler()) ] ], "options" : $[ "propose" : $[ "help" : "propose a configuration", "example" : "lan add propose", "type" : "" ], "configured" : $[ "help" : "list only configured cards" ], "unconfigured" : $[ "help" : "list only not configured cards" ], "device" : $[ "help" : "device ID", "type" : "string", "example" : "lan add device=eth0" ], "ip" : $[ "help": "device address", "type": "ip" ], "netmask" : $[ "help": "network mask", "type": "netmask" ], ], "mappings" : $[ "list" : [ "configured", "unconfigured" ], "add" : [ "device", "ip", "netmask" ], "delete": [ "device" ], ] ]; /** handler for action "list" */ define void listHandler( map options ) ``{ CommandLine::Print("\nSummary\n"); string summary = CommandLine::Rich2Plain( sformat("%1\n", mergestring( Lan::Summary(false), "") ) ); CommandLine::Print(summary); } /** handler for action "add" */ define void addHandler( map options ) ``{ string dev = options["device"]:""; } /** handler for action "delete" */ define void deleteHandler( map options ) ``{ string dev = options["device"]:""; CommandLine::Print("Deleting: "+dev); if(Lan::Delete(dev) && Lan::Commit()) CommandLine::Print("Success"); else CommandLine::Print("Error"); } import "Lan"; CommandLineRun( cmdline ); /* Finish */ y2milestone("Lan module finished"); y2milestone("----------------------------------------"); /* EOF */ }