This document describes new features of the Perl module for writing YaST2 SCR agents.
Contents:
Introduction
Basic usage
Agent Configuration
Logging
Mike's original documentation describes in detail a simple "ping" agent, with instructions how to test it and run it.
The original module could only handle simple data. Lists and maps could not be nested. Klaas later added a function to return a complex structure.
The new features are:
A reference document is avaiable. It also suggests better equivalents to obsoleted functions.
In this example we look at an actual agent for accessing mailer tables.
A mailer table is conceptually just a map of keys and values. The agent also wants to preserve the textual structure of a map file so it also stores comments and preserves the order of entries. So the actual representation of the table is @table, a list of maps with the keys "comment", "key" and "value". A trailing comment is held separately in $trailing_comment.
Commands are parsed by ycp::ParseCommand, which replaces ycpInit. It returns a list containing a command, a path and an optional argument. Instead of ycpCommandIs..., use a simple string comparison.
Values are returned by ycp::Return, which replaces ycpReturn...As... and ycpReturn.
while ( <STDIN> ) { my ($command, $path, $argument) = ycp::ParseCommand ($_); if ($command eq "Dir") { if ($path eq ".") { ycp::Return (["table", "trailing_comment"]); } else { ycp::Return ([]); } }
The argument is a PerlYCPValue: simple types such as integers, strings and booleans are represented as scalars; maps and lists are represented as references to hashes and arrays. Nil is represented as undef. Paths are represented as references to strings. Instead of ycpArgIs..., ycpGetArgType and ycpGetArg..., use ref($argument) eq "ARRAY" and the like.
Note that ycp::Return will guess what type a scalar is and return an appropriate YCP type. This may not be always what you want (cf. bug 21804) and you can prevent it by passing it a second parameter of 1.
# Write command elsif ($command eq "Write") { my $result = "true"; if ($path eq ".trailing_comment" && ! ref ($argument)) { $trailing_comment = $argument; } elsif ($path eq ".table" && ref ($argument) eq "ARRAY") { @table = @{$argument}; } elsif ($path eq "." && !defined ($argument)) { $result = write_file (); } else { y2error ("Wrong path $path or argument: ", ref ($argument)); $result = "false"; } ycp::Return ($result); } # Read command elsif ($command eq "Read") { if ($path eq ".table") { ycp::Return (\@table, 1); } elsif ($path eq ".trailing_comment") { ycp::Return ($trailing_comment, 1); } else { y2error ("Unrecognized path! '$path'"); ycp::Return (""); } } # result: we should exit elsif ($command eq "result") { exit; } # Unknown command else { y2error ("Unknown instruction $command or argument: ", ref ($argument)); ycp::Return ("false"); } }
In a SCR configuration file, you can specify a configuration command that will be the first one passed to the agent:
.mail `ag_mailtable ( Mailtable ($[ "filename": "/home/mvidner/mta/virtusertable1", "continue_escaped_newline": true, "continue_leading_blanks": true, "colon": false, ]) )
In C++, this is handled by SCRAgent::otherCommand. In Perl, use ycp::ParseTerm. In the example agent above, the main loop is actually preceded by this code:
# read the agent arguments $_ = <STDIN>; # reply to the client (this actually gets eaten by the ScriptingAgent) ycp::Return (undef); my ($symbol, $config, @rest) = ycp::ParseTerm ($_); if ($symbol ne "Mailtable") { y2error ("The first command must be the configuration.(Seen '$_')"); exit; } else { $filename = $config->{"filename"}; $continue_escaped_newline = $config->{"continue_escaped_newline"}; $continue_leading_blanks = $config->{"continue_leading_blanks"}; $separator = $config->{"colon"} ? qr/:\s+/ : qr/\s+/; $oseparator = $config->{"colon"} ? ":\t" : "\t"; }
A standard set of logging functions is now available: y2debug, y2milestone, y2warning, y2error, y2security and y2internal.
Normally, all these functions except y2debug produce output. Setting the environment variable Y2DEBUG to any value enables y2debug too.