shellmod Introduction 11.. WWhhaatt iiss sshheellllmmoodd Shellmod is a Linuxconf module as well as a stand-alone utility. Its goal is to allow easy writing of other Linuxconf modules and stand- alone utilities using sh (/bin/sh, the shell interpretor). The benefit of writing Linuxconf modules (or simply administration utilities) this way are: +o Nice interface: Your script will work both in text and graphical mode, with a much better appearance than an usual shell script. When used as a module in Linuxconf, it will even work in HTML, completely transparently. +o Simplicity: It is possible to write useful stuff in a 10-15 lines shell script. It is possible to turn an non interactive existing 10-15 lines shell script into a 15-20 lines cool interactive script, running both in text, GUI mode, and even HTML mode. +o Used as a module, your script will be able to hook itself in various Linuxconf menus, making it fully integrated in the administration scheme. +o Not only the script can enhance Linuxconf menus, it can even participate as a co-manager in some dialogs. This means that a simple script can add new fields in the user account dialog for example. Normal C++ Linuxconf module can do more things obviously, but scripts offers a nice and efficient solution for custom installation and on site support. A new Linuxconf module may be develop with the user/friend/customer looking over your shoulder. 22.. PPrriinncciipplleess In this section we will see the basic layout of a shellmod script, how to use it and how it communicate with Linuxconf. 22..11.. CCoommppoonneennttss ooff tthhee sshheellllmmoodd ppaacckkaaggee The shellmod package provides the following parts: +o The shellmod utility used to run shellmod script without Linuxconf. This utility is located in /usr/bin. +o The shellmod module which allows shellmod script to interact with Linuxconf. Note that a shellmod script can be used both ways, either stand-alone or embedded in Linuxconf. +o The shellmod-lib.sh function library. This is located in /usr/lib/linuxconf/lib. This contains utility functions simplifying shellmod scripts. This file must be sourced in all scripts. +o Some sample script. +o At some point, the package will contain some contrib scripts and a browser will be designed to easily find and try those contrib scripts. If you have written something general and useful, send it on the Linuxconf mailing list, for inclusion in this package. 22..22.. HHooww ttoo uussee sshheellllmmoodd ssccrriippttss A script may be used either stand-alone or within Linuxconf. It can be used both ways. 22..22..11.. AAss aa ssttaanndd--aalloonnee uuttiilliittyy To run a shellmod script, you either do shellmod path_of_the_script [ arguments ] Or if the script start with #!/usr/bin/shellmod, you simply run it like any utility. Note that in all cases, the script must be executable (chmod +x script). 22..22..22.. AAss aa LLiinnuuxxccoonnff mmoodduullee To be used this way, it must be registered in Linuxconf. This is done either interactively or using a special command line. The shellmod module register its configuration menu in the Control files and systems menu. You will find there a dialog to register your script. Just enter the path of your script and that's it. The script will be visible in Linuxconf at the next run. The script may be registered using the following command line. This is especially useful if you include your script in a package and would like to register the script in Linuxconf at package installation (RPM packagers might consider the trigger facility to do this). linuxconf --modulemain shellmod --setmod script_path Note that the linuxconf --modulemain construct is the normal way to yield control to a module own command line: shellmod is not different. You may unregister a script from Linuxconf by doing: linuxconf --modulemain shellmod --unsetmod script_path Note that the above syntax replicate the way normal (C++) Linuxconf modules may be registered and unregistered, by using the linuxconf --setmod and linuxconf --unsetmod command lines. 22..22..33.. AAss aann iinnddeeppeennddaanntt ssccrriipptt,, iinn lliinnuuxxccoonnff ccoonntteexxtt This is almost equivalent to the standalone mode, except that linuxconf and all its modules are listening. This mode is necessary if your script uses the virtual registry: Linuxconf and its modules have to be alived to react properly. linuxconf --modulemain shellmod --exec script_path \ [--debug] [--perl] script arguments 22..33.. LLaayyoouutt ooff aa sshheellllmmoodd ssccrriipptt A shellmod script will always look like this: #!/usr/bin/shellmod . /usr/lib/linuxconf/lib/shellmod-lib.sh register(){ # Put regmenu and comanager calls here } # Put here the various functions referenced above main(){ # We define a sub-menu or a dialog } # The script always end with the dispatch function call. Always dispatch 22..33..11.. TThhee sshheellllmmoodd--lliibb..sshh ffuunnccttiioonn lliibbrraarryy 22..33..22.. TThhee rreeggiisstteerr ffuunnccttiioonn The register function is required if the script is used as a Linuxconf module. Linuxconf will call the function expecting the script to pass back the list of menu and dialog in which it want to register. Here is an example of a register function installing two sub-menu and one co- manager. # Register in the "miscellaneous service" menu echo regmenu main MENU_MISCSERV \"main menu of the module\" # Register at the end of the dnsconf menu echo regmenu function2 dnsconf \"Some title\" # Register as a co-manager in the user account dialog echo comanager function-prefix user 22..33..33.. TThhee rreeggiisstteerr ffuunnccttiioonn ffoorr ssttaanndd--aalloonnee oonnllyy mmoodduullee The register function is not needed for stand-alone module. It is a good idea to provide a minimal one in case a user try to register this module in Linuxconf. The following one will make it clear the script is not intended to be use this way: register(){ echo error \"This shellmod script can't be use as a module\" } 22..33..44.. TThhee mmaaiinn ffuunnccttiioonn If the script is used stand-alone, it requires a function called main. The shellmod utility will blindly call this function. It is a good idea to always provide one. In the register function we often register in a Linuxconf menu the main function. Done this way, the script provides a consistent interface used either stand-alone or as a Linuxconf module. The main function will generally define a menu or a dialog. Note that the main function is the one which will receive the script arguments. The arguments will often be used as default values for the dialog fields. Here is a small example. main(){ echo DIALOG echo newf_str path \"Document path\" $1 echo newf_str mail \"Email this document to\" $2 echo newf_str fax \"Fax this document to\" $3 echo edit \"Document manager\" \"Fill this form to send the document\" dispatch echo end if [ "$CODE" = "accept" ] ; then if [ "$mail" != "" ] ; then cat $path | mail $mail fi if [ "$fax" != "" ] ; then spoolfax $fax $path fi fi } 22..33..55.. TThhee ddiissppaattcchh ffuunnccttiioonn A module always ends with a call to the dispatch function. A shellmod script may be seen as a set of function waiting to be called (A library). Based on context, Linuxconf (or shellmod for stand-alone scripts) will call the appropriate one. Note that the script can perform some initialization before calling the dispatch function. As explain later, a script may be seen as a small server which keeps its state between calls to its various functions. 22..44.. HHooww ddooeess iitt wwoorrkkss A shellmod script interact with shellmod or Linuxconf by echoing commands on it standard output. It receives directives by simply reading its standard input. The dispatch function takes care of reading and processing (dispatching) the request it receives. We can think of the script as being in sandwich between two shellmod process like this shellmod | script | shellmod Everything echoed by the script is grabbed by shellmod and interpreted as protocol commands. It is important to redirect the output of sub- command you are using either to the error channel (command >&2) or to /dev/null. Failing to do so will trigger many error messages from shellmod. 33.. PPrroottooccooll we will now present the various commands issued by the script. Those commands will be split by task 33..11.. CCoommmmaanndd llaayyoouutt aanndd qquuoottiinngg All command sent to shellmod are done using the echo shell command. They always looks like: echo command arg1 arg2 arg3 Arguments are either a single or multiple words (separated with spaces). To group multiple words as a single argument, quotes must be used. While this is standard command line practice, there is a catch: Quoting will only affect the argument as seen by the echo command. Once process, the whole line will be received as a single stream of words separated by space and the quote will be gone. The quotes must be part of the line received by shellmod. Here is an example echo newf_str name \"Enter your name\" \"Tux the penguin\" The shellmod-lib.sh define the qecho helper function you may used to get a more standard shell quoting behavior. Here the same example rewritten with qecho. qecho newf_str name "Enter your name" 'Tux the penguin' As you see, with qecho you can mix the different shell quoting syntax. The qecho function insures that proper double quotes surround all arguments. 33..22.. SSeennddiinngg mmuullttii--lliinnee ppaarraammeetteerrss Shell scripts are not that good at handling and passing multi-line text. The "defval" command was created to fix that. Here is a typical usage: echo var1 "This is the first line" echo var1 "This is the second line" echo error =var1 This creates a popup error message with two lines. "defval" is used repeatedly to define the multi-line text and is referenced as an argument using the = sign. The argument must not be quoted. So you can't use the qecho helper function using a defvar parameter, because qecho insures quoting around all parameters. 33..33.. BBuuiillddiinngg aa ddiiaalloogg 33..33..11.. eecchhoo DDIIAALLOOGG A dialog always starts with this command. It requires no further argument. Then you stuff it with field definitions, add optional buttons and then you call the edit function. Here is a sample: echo DIALOG echo newf_str var \"field title\" value edho edit \"Dialog title\" \"Dialog introduction\" dispatch echo end 33..33..22.. eecchhoo sseettttyyppee DDIIAATTYYPPEE__PPOOPPUUPP This requires the window manager to present this dialog as an independant window in the middle of the screen. This is usually used for transient dialog requiring immediate attention from the user. 33..33..33.. eecchhoo eeddiitt \\""ttiittllee\\"" \\""IInnttrroodduuccttiioonn\\"" [[ \\""bbuuttttoonn,,bbuuttttoonn,,......\\"" ]] This pops up the dialog. The window title control using the first argument and the longer dialog description is controlled by the introduction field. The last argument is option and lets you control the buttons displayed at the bottom of the dialog. The value "0" means to display no button (useful to display some gauge for example). When the last argument is omitted, the button Accept and Cancel are displayed. 33..33..44.. eecchhoo sshhooww \\""ttiittllee\\"" \\""IInnttrroodduuccttiioonn\\"" This works like edit. It pops up the dialog with the same arguments. But it return immediatly. This is generally used with the ``newf_gauge'' command. 33..33..55.. ddiissppaattcchh This yield control to Linuxconf/shellmod and wait for results. Field variable are updated and the CODE variable is set to the button value selected by the user: This is either "accept" or "cancel". 33..33..66.. eecchhoo eenndd This deletes the dialog. It is removed from the screen and forgotten. We often issue the end command right after the dispatch command. But more complex dialog may use the end command after a validation loop. 33..33..77.. AA ccoommpplleettee eexxaammppllee #!/usr/bin/shellmod . /usr/lib/linuxconf/lib/shellmod-lib.sh main(){ echo DIALOG echo newf_str name \"User name\" while true do echo edit \"User query\" \"Enter the user name\" dispatch if [ "$CODE" = "cancel" ] ; then break elif [ "$name" = "" ] ; then echo error \"Please provide a name\" else echo notice \"Doing something with the user account\" break fi done echo end } dispatch 33..44.. BBuuiillddiinngg aa mmeennuu This is handled by the DIALOG_MENU and new_menuitem commands. 33..44..11.. eecchhoo DDIIAALLOOGG__MMEENNUU This does not require any argument. 33..44..22.. eecchhoo nneeww__mmeennuuiitteemm ffuunnccttiioonn pprroommpptt ttiittllee This records one menu entry. Each menu entry is associated with one script function (that must be defined). The prompt is generally a keyword. The title is the rest of the menu entry. 33..44..33.. eecchhoo eeddiittmmeennuu \\""MMeennuu ttiittllee\\"" \\""IInnttrroodduuccttiioonn\\"" This pops the menu. It is followed by a call to dispatch. 33..44..44.. ddiissppaattcchh This is where everything is done for a menu. The dispatch function will run until the user select the "quit" button. The corrresponding function is called whenever a menu entry is selected. This is handled transparently by the dispatch function. Note that the dispatch function also exits if the user select an optional buttons (added with the button command). So it is possible to handle a in a loop like the example dialog above. 33..44..55.. eecchhoo eenndd This deletes the menu. 33..44..66.. AA ccoommpplleettee eexxaammppllee #!/usr/bin/shellmod . /usr/lib/linuxconf/lib/shellmod-lib.sh menufunc1(){ echo notice \"menufunc1 selected\" } menufunc2(){ echo notice \"menufunc2 selected\" } main(){ echo DIALOG_MENU echo new_menuitem menufunc1 Select \"First option\" echo new_menuitem menufunc2 \"\" \"Second option\" echo editmenu \"Main menu\" \"Pick an option\" dispatch echo end } dispatch 33..55.. MMaannaaggiinngg aa lliisstt ooff rreeccoorrddss 33..55..11.. eecchhoo DDIIAALLOOGG__LLIISSTT A DIALOG_LIST is just a variation of a menu. The main difference is that the number of entry might be very long. In that case a filter dialog will popup so restrict the amount of diaplayed record. You can deal with very large list of item. DIALOG_LIST does not require any argument. 33..55..22.. eecchhoo nneewwff__hheeaadd \\""CCoolluummnn11 ttiittllee\\"" \\""CCoolluummnn22 ttiittllee\\"" ...... You can define the heading of the list. This is optional, but looks much more nicer. 33..55..33.. eecchhoo nneeww__mmeennuuiitteemm \\""ffuunnccttiioonn aarrggss\\"" \\""CCoolluummnn11 vvaalluuee\\"" \\""CCooll-- uummnn22 vvaalluuee\\"" ...... It works like DIALOG_MENU, except that we often supply more columns. There is another variation as the function is defined with arguments. Note that this may be used with DIALOG_MENU as well. We generally define one function per menu entry and a single function to process each record of the list, using the argument to differentiate the processing. Note also that you can use a mixed solution within the same DIALOG_MENU or DIALOG_LIST: You can use a single function to provide a common processing for some record and different function to manage exception. 33..55..44.. eecchhoo eeddiittmmeennuu \\""LLiisstt ttiittllee\\"" \\""IInnttrroodduuccttiioonn\\"" Same as with DIALOG_MENU. 33..55..55.. ddiissppaattcchh This behave like the DIALOG_MENU. 33..55..66.. eecchhoo eenndd This delete the list. 33..55..77.. AA ccoommpplleettee eexxaammppllee Here is a realistic example where we display a directory and let the user pick one file and do something with it. We see in this example how easy it is to parse the output of the "ls -l" command and present the file name, the size and revision date in three formatted column. #!/usr/bin/shellmod . /usr/lib/linuxconf/lib/shellmod-lib.sh fileshow(){ echo notice \"Processing file $1\" } main(){ echo DIALOG_LIST echo newf_head \"File name\" Sise \"Revision date\" ls -l $1 | \ ( read total while read perm count user group size month day hour name do echo new_menuitem \"fileshow $name\" $name $size \"$month $day $hour\" done ) echo editmenu \"Current directory\" \"This show all file in the current directory\" dispatch echo end } dispatch 33..66.. DDeeffiinniinngg ffiieellddss All commands defining a dialog field start with the prefix newf_. We have used the same name and same parameter order (when possible) as the C++ module API. The first argument is always the name of the shell variable which will receive the value entered by the user. You will often use the following construct to edit (correct the current value) of a given variable. qecho newf_str var "title" "$var" . qecho edit ... dispatch if [ "$CODE" = "accept" ] ; then if [ "$var" = "..." ] ;then . . fi fi 33..77.. CCoommmmaanndd lliisstt Here is the list of all field definition commands: +o ``newf_chk'' +o ``newf_chkm'' +o ``newf_combo'' +o ``newf_dbl'' +o ``newf_enum'' +o ``newf_gauge'' +o ``newf_hexnum'' +o ``newf_info'' +o ``newf_list'' +o ``newf_num'' +o ``newf_pass'' +o ``newf_radio'' +o ``newf_slider'' +o ``newf_str'' +o ``newf_title'' 33..77..11.. eecchhoo nneewwff__cchhkk vvaarr \\""ffiieelldd ttiittllee\\"" \\""IInniittiiaall 00 oorr 11 vvaalluuee\\"" \\""SSuuffffiixx ttiittllee\\"" Setup a check box field (on/off or yes/no). The variable var will be setup to either 0 or 1. This field has two title. One on the left of the check box and one on the right. This is often used like this in Linuxconf: The current feature [ ] is selected 33..77..22.. VVaarriiaattiioonn ooff tthhee nneewwff__cchhkk ssyynnttaaxx When using newf_chk, you must pass a 0 or 1 as the value. The edit variable will receive the result as 0 or 1. This is not so useful as often shell script are dealing with different type of boolean value. For example, a script dealing with an SQL server may find itself dealing with Y and N values. To avoid translating from one system to the other, the syntax of the initial value has been expanded. +o To make things easy, shellmod will accept as a selected value anything in 1,Y and Yes (case insensitive). Unless told differently, these values will be translated to 1. Anything else will be translated to 0. +o By using a special triplet format, one can specify the actual value and the dictionary used to interpret it. You specify the value like this: value:on_value:off_value So if you application is dealing with ON/OFF values, you can specify the initial value like this: $var:ON:OFF Not only ON and OFF will be accepted to interpret the value, but the end result will be sent to the script using one of those word. 33..77..33.. eecchhoo nneewwff__cchhkkmm vvaarr \\""ffiieelldd ttiittllee\\"" \\""IInniittiiaall nnuummeerriicc vvaalluuee\\"" \\""vvaalluuee11\\"" \\""vvaalluuee22\\"" ...... Setup of multiple selection field using check boxes. The boxes are presented horizontally. Here is an example followed with the field it produced in text mode: echo newf_chkm sel \"Which position\" 1 left center right This produces: Which position ( ) left (o) center ( ) right The variable var will take the numerical index of the selected item, starting at 0. 33..77..44.. eecchhoo nneewwff__ccoommbboo vvaarr \\""ffiieelldd ttiittllee\\"" \\""IInniittiiaall vvaalluuee\\"" Setup a single line + selector field. The user will be able to pick a value out of a pick list or enter another by hand. The variable var will contain the textual value either entered or selected. newf_combo is used with the comboitem command. You setup first the combo field and then you pass one by one the possible values using comboitem. Note that the values are followed by a descriptive text (optional). Here is a code sample: echo newf_combo port \"Which port\" ttyS1 echo comboitem ttyS0 \"COM1 in DOS\" echo comboitem ttyS1 \"COM2 in DOS\" echo comboitem ttyS2 \"COM3 in DOS\" The variable $port will take the value ttyS0, ttyS1, ttyS2 or anything the user dares to enter. See ``newf_enum'' and ``newf_list'' for a variation of this input field. 33..77..55.. eecchhoo nneewwff__ddbbll vvaarr \\""ffiieelldd ttiittllee\\"" \\""IInniittiiaall nnuummeerriicc vvaalluuee\\"" nnuummbbeerr--ooff-- ddeecciimmaall Setup a numerical input field with decimal notation. Works like ``newf_num'' except that a decimal point is allowed. The number of decimal parameter control the number of digits allowed after the decimal point as well as the formatting of the field. 33..77..66.. eecchhoo nneewwff__eennuumm vvaarr \\""ffiieelldd ttiittllee\\"" \\""IInniittiiaall nnuummeerriicc vvaalluuee\\"" Setup a selector field. The user will be able to pick a value out of a pick list. The variable var will contain the index of the selected item. The user is limited to picking one item of the list. newf_enum is used with the enumitem command. You setup first the enum field and then you pass one by one the possible values using enumitem. Here is a code sample: echo newf_enum no \"Which port\" 1 echo enumitem ttyS0 \"COM1 in DOS\" echo enumitem ttyS1 \"COM2 in DOS\" echo enumitem ttyS2 \"COM3 in DOS\" The variable $no will take the value 0 1 or 2. See ``newf_combo'' and ``newf_list'' for a variation of this input field. 33..77..77.. eecchhoo nneewwff__ggaauuggee IIDD \\""ffiieelldd ttiittllee\\"" \\""IInniittiiaall ddeecciimmaall vvaalluuee\\"" \\""MMaaxxiimmuumm vvaalluuee\\"" Setup a visual gauge, generally used to display the completion status of a process (loading, installing). The state of the gauge is changed by calling this command again. The first call defines the field, the next ones update the gauge. This widget is generally used with the "show" command so the script never stop in the "edit" state. Here is a small example echo DIALOG qecho newf_gauge ID "Status" 0 10 qecho show "Status" "" i=0 while [ "$i" != "10" ] ; do i=`expr $i + 1` sleep 1 qecho newf_gauge ID "Status" $i 10 qecho show "Status" "" done 33..77..88.. eecchhoo nneewwff__hheexxnnuumm vvaarr \\""ffiieelldd ttiittllee\\"" \\""IInniittiiaall hheexxaa--ddeecciimmaall vvaalluuee\\"" Setup a numerical input field (integer). Works like ``newf_num'' but accept digits and hexa-decimal digits. 33..77..99.. eecchhoo nneewwff__iinnffoo \\""ffiieelldd ttiittllee\\"" \\""FFiieelldd vvaalluuee\\"" Setup an information field. It is presented like an input field with a prompt on the left, but the field value is simply drawn and can't be edited. 33..77..1100.. eecchhoo nneewwff__lliisstt vvaarr \\""ffiieelldd ttiittllee\\"" \\""IInniittiiaall vvaalluuee\\"" Setup a selector field. The user will be able to pick a value out of a pick list only The variable var will contain the textual value selected. newf_list is used with the listitem command. You setup first the list field and then you pass one by one the possible values using listitem. Note that the values are followed by a descriptive text (optional). Here is a code sample: echo newf_list port \"Which port\" ttyS1 echo listitem ttyS0 \"COM1 in DOS\" echo listitem ttyS1 \"COM2 in DOS\" echo listitem ttyS2 \"COM3 in DOS\" The variable $port will take the value ttyS0, ttyS1 or ttyS2. See ``newf_enum'' and ``newf_combo'' for a variation of this input field. 33..77..1111.. eecchhoo nneewwff__nnuumm vvaarr \\""ffiieelldd ttiittllee\\"" \\""IInniittiiaall nnuummeerriicc vvaalluuee\\"" Setup a numerical input field (integer). Works like newf_str but accept only digit. 33..77..1122.. eecchhoo nneewwff__ppaassss vvaarr \\""ffiieelldd ttiittllee\\"" \\""IInniittiiaall vvaalluuee\\"" Setup a password field. Works like a newf_str field except the input is not echoed (invisible typing). 33..77..1133.. eecchhoo nneewwff__rraaddiioo vvaarr \\""ffiieelldd ttiittllee\\"" nnuummeerriicc--vvaalluuee iinnssttaannccee--vvaalluuee \\""ssuuffffiixx ttiittllee\\"" Setup a radio button field. Several radio button fields must be defined for each possible selection. All radio buttons sharing the same input variable (var above) operate together: Selecting one De- select the other. The var variable will get the numeric-value of the selected radio button field. Related radio buttons may be placed anywhere in a dialog. They do not have to be sequential or even on the same page of the notebook dialog 33..77..1144.. eecchhoo nneewwff__sslliiddeerr vvaarr \\""ffiieelldd ttiittllee\\"" \\""IInniittiiaall ddeecciimmaall vvaalluuee\\"" \\""mmiinnii-- mmuumm vvaalluuee\\"" \\""MMaaxxiimmuumm vvaalluuee\\"" This works like the newf_num, to edit a decimal value. But it is shown as a visual slider. The minimum and maximum value represent the left and right edge of the slider. qecho newf_slider var "Meeting hour" 15 9 16 33..77..1155.. eecchhoo nneewwff__ssttrr vvaarr \\""ffiieelldd ttiittllee\\"" \\""IInniittiiaall vvaalluuee\\"" Setup a one line text input. 33..77..1166.. eecchhoo nneewwff__ttiittllee ""PPaadd ttiittllee"" lleevveell ""LLeefftt ttiittllee"" ""TTeexxtt mmooddee ttiittllee"" This is not an input field but a way to organize a large dialog in section. The end result is far different in graphic mode than in text or HTML mode. In graphic mode, this will create a notebook dialog and each newf_title defines one page (one pad) of the notebook. The first argument, pad title, is only used in graphic mode. The second argument is a number and represent the notebook level. This allows very complex dialogs with notebook within notebook. In text and HTML mode, this argument has no effect. +o A value of 0 add an horizontal splitter between two fields. The "Text mode title" is centered in that splitter. +o A value of 1 create the first level of notebook. A value of 2 create a sub-notebook with the current one. The third argument, left title, is used only in text and HTML mode. It places a small title on the left of the input fields. The last argument, text mode title, appears centered between two input fields. 33..88.. AAddddiinngg bbuuttttoonnss You can add optional buttons simply by using the button command. It requires to arguments. The first is the button ID which will be passed to the script using the CODE variable. The second is the label (title) of the button. Here is an example: echo DIALOG echo newf_str name \"User name\" $name echo button Add \"Add a new user\" echo edit "title" \"User management\" dispatch echo end if [ "$CODE" = "Add" ] ; then # Adding a new user elif [ "$CODE" = "accept" ] ; then # Inspecting a user record fi 33..99.. SSeett tthhee ccuurrrreenntt iinnppuutt ffiieelldd The "setcurfield" opcode sets the keyboard focus on a specific field. You must pass the field ID as a single argument. Here is an example: qecho DIALOG qecho newf_str uid "User id" qecho newf_str name "Name" qecho newf_str phone "Name" qecho setcurfield name qecho edit "sample" "The focus is now on name" dispatch echo end 33..1100.. MMiisscceellllaanneeoouuss 33..1100..11.. eecchhoo rreemmoovvee__aallll Remove all fields from a dialog, allowing you to redefine its content. 44.. BBuuiillddiinngg ccoo--mmaannaaggeerrss Co-managers are exciting. They allows one to add functionalities to Linuxconf right where it belongs, instead of adding new sub-menus. 44..11.. PPrriinncciipplleess A co-manager is a component that participate in a dialog. It may add fields and validation. The most common case is the user account dialog, where several co-managers participate. The most visible one is the mailconf module, which insert a complete section to handle email aliases. Another is the pppdialin module which add a section to deal with PPP parameters. A co-manager must provide 4 functions: +o Adding fields to the dialog. Here the co-manager will probably add a new section and few fields. It is not forced to add anything. +o Add validation. The co-manager may do all kind of validation not only on its own fields. The admin is not allowed to commit any changes to the user account until all co-managers agree. +o Save the inputs. The co-manager must save the value of the fields it manages. User account co-managers have to preserver information on a per user basis. +o Delete information associated to the user account/information record when it is deleted. Note that the co-managers have been created with user accounts in mind, but the concept is more general than that. Change "user account" above for "record" and you get a more general picture. 44..22.. RReeggiisstteerriinngg tthhee ccoo--mmaannaaggeerr Linuxconf must be notified that a co-manager exists for a given dialog. Dialogs are identified by a small key. For example, the key for the user account dialog is "user". A shellmod co-manager must provide four functions, using the same prefix. When registering the co-manager, you provide the function prefix and the dialog key. This is done with in the register function Here is an example: # Register as a co-manager in the user account dialog echo comanager ufct user The function prefix is ufct. One must create four functions according to this prefix: +o ufct-setupdia +o ufct-validate +o ufct-save +o ufct-deluser 44..33.. TThhee vvaarriioouuss ffuunnccttiioonnss 44..33..11.. sseettuuppddiiaa This function is called when building the dialog. You can use any opcode normally used to define a dialog. You do not have to define the dialog yourself (echo DIALOG) as it is implicitly defined. While you can simply add fields in the dialog, one will generally define a new dialog section. Unless you do that, your new field will be appended in the current section, which can be any section. You define a section with the newf_title opcode qecho newf_title "Mailing lists" 1 - "Mailing lists" 44..33..22.. vvaalliiddaattee This function receives all field value (entered by the user). Each field correspond to a shell variable, so you can test their value directly. You must use the "retcode" opcode to tell if the validation was successful. Here is an example echo retcode 0 Any value different from 0 is taken as a validation failure. Any error must be reported with the "error" opcode. You may also use the "setcurfield" to jump to a specific field. if [ "$a1" = "valid" ] ; then echo retcode 0 else echo setcurfield a1 echo retcode -1 echo error \"sample.sh: Invalid value, enter the word valid\" fi 44..33..33.. ssaavvee All field's value are returned to your script as shell variable. The save function must do various things with the information. It can either save it in a file, or perform some actions (send an email to an administrator about the required changes for example). Other shell variable also contain information related to this dialog (see dialog context below). 44..33..44.. ddeelluusseerr This function name is a bit miss-leading. It means that the record must be deleted (in general, a user account). You must use the information from the dialog context (see below) to perform the required deletion. 44..44.. DDiiaalloogg ccoonntteexxtt A co-manager is handling one part of a larger dialog. To properly manage its part, the co-manager must know other information about the current dialog. For example, to enhance the user account dialog, one must know the user account (user id) being edited as well as the domain (is it a virtual email domain user account ?). This extra infomation is passed as shell variable, so readily usable. The available information is dialog dependent. For user account co- managers, the following variables are provided: ddoommaaiinn The virtual email domain is passed. For the main domain, this variable is set to /. iiss__nneeww This is a flag telling you if this is a new account (name is empty) or not. nnaammee This is the user id. One way to learn the information available to your script is by running it in debug mode under Linuxconf. You enable this mode from Linuxconf user interface, in control file and system/shell module management/shellmod configuration. 55.. UUssiinngg tthhee vviirrttuuaall rreeggiissttrryy The virtual registry is a general interface to get and set value from various modules in Linuxconf. You can learn more about it by reading the "manual" page for the vregistry module. We describe here how to use it from a shellmod script. 55..11.. vvrreegg__sseett rreeggiissttrryy__vvaarriiaabbllee__nnaammee vvaalluuee You can set any variable by issuing the vreg_set command. The variable name is one registry variable. You can learn the about the available variable by running /sbin/vregistry --list. You generally issue several vreg_set statements and then you call the vreg_do function to commit the request. The various modules are updated at this time only. 55..22.. vvrreegg__ggeett rreeggiissttrryy__vvaarriiaabbllee__nnaammee sshheellll--vvaarriiaabbllee This will collect the value of the registry variable into a local variable of the shell named shell-variable. You generally issue several vreg_get statements and then you call the vreg_do function to commit the request. After having called vreg_do, your shell variables are properly assigned. qecho vreg_get samba.workgroup workgroup qecho vreg_get samba.winsserver wins # At this point, neither workgroup or wins are assigned vreg_do qecho notice "Your workgroup is $workgroup, wins server, $wins" 55..33.. vvrreegg__ddoo vreg_do is a shell function. so you use it directly unlike most other shellmod command which are "echoed". vreg_do acts like the dispatch command. It requests action and grab the results. 66.. SSccrriipptt pprroocceessss lliiffee Shellmod and Linuxconf will start the script whenever it is needed. Used as a Linuxconf module, the script is not started at Linuxconf startup but only when one of its menu or co-manager entries are selected. The script is start and proceed to its dispatch function, where it waits for request. Some request triggers the execution of one of the script's function. When the function done, the original dispatch function resume and continue to wait. Note that dispatch may be used recursively. Once started, the script sits there waiting for request until Linuxconf end. As such it can be seen as a small server sharing information collected while performing various request. When used as a stand-alone utility, the script will end as soon as its main function exits. 77.. MMoodduullee bbuuiillddeerr A shell module builder is available to get you started faster. You access this builder from shellmod main menu or with the shellmod utility: shellmod --build A single dialog appears. You fill it and this create the framework for a new shell module. Here are the various fields 77..11.. PPaatthh ooff tthhee mmoodduullee You simply enter the full or relative path of the new shell script you want to create. The builder is not able to edit an existing module. 77..22.. IInnsseerrtt iinn mmeennuu You must supply the menu id of the menu in which you want to insert the module (in Linuxconf). A module may insert in several Linuxconf menus, but the builder only supports one. Modifying the code later is easy. This field has a help list to pick the proper ID. 77..33.. MMeennuu ttiittllee Just enter the text of the menu entry. 77..44.. TThhiiss iiss aa uusseerr aaccccoouunntt ccoo--mmaannaaggeerr By selecting this check-box, you instruct the builder to generate the proper co-manager function templates (setupdia,save,deluser,validate). Note that a module may be a co-manager and insert in menus as well. 77..55.. MMaaiinn mmeennuu eennttrriieess You simply enter the various menu entries. For each one, the builder will generate a shell function template menufunc1, menufunc2. It will also generate the menu code in the main function. You will have to provide the code for the various menufunc functions.