home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / boot / i386 / root / usr / lib / YaST2 / servers_non_y2 / ag_netd < prev    next >
Text File  |  2006-11-29  |  17KB  |  807 lines

  1. #!/usr/bin/perl -w
  2. #
  3. # $Id: ag_netd 33164 2006-09-27 08:42:24Z jsrain $
  4. # Author: Martin Vidner <mvidner@suse.cz>
  5. #
  6.  
  7. use ycp;
  8. use strict;
  9. use File::Basename;
  10.  
  11. my $debug = defined ($ARGV[0]) && $ARGV[0] =~ "^-.*d";
  12.  
  13. sub DEBUG
  14. {
  15.     print STDERR @_ if $debug;
  16. }
  17. DEBUG "DEBUG\n";
  18.  
  19. # Testsuite hack:
  20. # Automake invokes dejagnu with an absolute srcdir :(
  21. # This will replace the current directory in file names by "./"
  22. # Turn it off by -n
  23. my $strip = ! (defined ($ARGV[0]) && $ARGV[0] =~ "^-.*n");
  24. my $pwd;
  25. if ($strip)
  26. {
  27.     DEBUG "STRIP\n";
  28.     # must use symlinks like make does
  29.     # pwd alone calls /bin/pwd, we want the bash builtin
  30.     $pwd = `bash -c pwd -L`;
  31.     chomp $pwd;
  32.     DEBUG "PWD: '$pwd'\n";
  33. }
  34. sub strip_pwd ($)
  35. {
  36.     DEBUG "STRIP0: $_[0]\n";
  37.     $_[0] =~ s{^$pwd/}{./}o if $strip;
  38.     DEBUG "STRIP1: $_[0]\n";
  39.     return $_[0];
  40. }
  41.  
  42. my @services;
  43. # where new services are added
  44. my $dir_with_includes = "";
  45. my $base_file = "";
  46. # used for a warning about unsupported options
  47. my $message = "";
  48.  
  49. package ycp;
  50. sub to_bool ($)
  51. {
  52.     my $b = shift;
  53.     return $b? \ "true" : \ "false";
  54. }
  55.  
  56.  
  57. package linefile;
  58. # a handle, a name and line number
  59. use IO::File;
  60.  
  61. sub new ($$)
  62. {
  63.     my $class = shift;
  64.     my ($name, $mode) = @_;
  65.     my $f = { "name" => $name,
  66.           "lineno" => 0,
  67.           "fh" => new IO::File ($name, $mode) };
  68.     return bless ($f, $class);
  69. }
  70.  
  71.  
  72. package netd;
  73. # base of inetd and xinetd
  74. use ycp;
  75.  
  76. # makes a hash (keyed by file names) of hashes (keyed by line numbers)
  77. # of changed services
  78. # exception: line -1 is a _list_ of new services
  79. sub get_changed (@)
  80. {
  81.     my %ch = ();
  82.     foreach my $s (@_)
  83.     {
  84.     next if (!$s->{changed});
  85.     if (defined ($s->{iid}) && $s->{iid} =~ m/(\d+):(.*)/)
  86.     {
  87.         my ($line, $file) = ($1, $2);
  88.         $ch{$file}->{$line} = $s;
  89.     }
  90.     else
  91.     {
  92.         y2debug "new $s->{service}";
  93.         # Bug 26999: if we have encountered an "includedir",
  94.         # create the new services there. The file name is
  95.         # the "script" or "service" field.
  96.         my $fname = filename_for_new_service ($s);
  97.         push (@{$ch{$fname}->{-1}}, $s);
  98.     }
  99.     }
  100.     return %ch;
  101. }
  102.  
  103. sub filename_for_new_service ($)
  104. {
  105.     my $s = shift;
  106.     if ($dir_with_includes)
  107.     {
  108.     my $script = $s->{script} || $s->{service};
  109.     return $dir_with_includes ."/".$script;
  110.     }
  111.     else
  112.     {
  113.     # no includes, use the main file
  114.     return $base_file;
  115.     }
  116. }
  117.  
  118. sub parse_file ($)
  119. {
  120.     my $class = shift;
  121.     ::DEBUG ("$class\n");
  122.     my $filename = ::strip_pwd (shift);
  123.  
  124.     use Errno qw(ENOENT);
  125.  
  126.     my $file = new linefile ($filename, "r");
  127.     if (! $file->{fh})
  128.     {
  129. #    return 1 if ($! == ENOENT); # ok if it is not there
  130.     y2error ("$file->{name}: $!");
  131.     return 0;
  132.     }
  133.  
  134.     while (1)
  135.     {
  136.     my $service = $class->parse_service ($file);
  137.     last if !defined ($service);
  138.     if ($service->{is_defaults})
  139.     {
  140.         if ($service->{enabled} || $service->{disabled})
  141.         {
  142.         # TODO rework, must be translated!
  143.         $message = "Enabling and disabling services\n"
  144.             . "in the \"defaults\" section is ignored.";
  145.         }
  146.     }
  147.     else
  148.     {
  149.         push (@services, $service);
  150.     }
  151. #    ::DEBUG ("tick\n");
  152.     }
  153.  
  154.     $file->{fh}->close;
  155.     return 1;
  156. }
  157.  
  158. sub include_dir ($)
  159. {
  160.     my $class = shift;
  161.     ::DEBUG ("$class\n");
  162.     my $dirname = shift;
  163.     $dir_with_includes = $dirname; # for creating new services
  164.     use IO::Dir;
  165.     my $dir = new IO::Dir ($dirname);
  166.     my @files;
  167.     if (defined $dir)
  168.     {
  169.     while (defined ($_ = $dir->read ()))
  170.     {
  171.         push (@files, $_) unless m/\./ || m/~$/;
  172.     }
  173.     $dir->close ();
  174.     }
  175.     else
  176.     {
  177.     y2error "Cannot include dir '$dirname'";
  178.     }
  179.  
  180.     foreach (sort @files)
  181.     {
  182.     $class->parse_file ("$dirname/$_");
  183.     }
  184. }
  185.  
  186. sub write_file ()
  187. {
  188.     use IO::File;
  189.     my $class = shift;
  190.  
  191.     my $ret = 1;
  192.     my %changed_services = get_changed (@services);
  193.     while (my ($filename, $lines) = each %changed_services)
  194.     {
  195.     # TODO guard against changed files
  196.     my $ofilename = "$filename.tmp";
  197.     # if we are creating a new file, $if->{fh} will be undef,
  198.     # which is taken care of below
  199.     my $if = new linefile ($filename, "r");
  200.     my $OF = new IO::File (">$ofilename");
  201.     if (! $OF)
  202.     {
  203.         y2error ("Opening '$ofilename' for writing: $!");
  204.         $ret = 0;
  205.         next;        # continue with the next file
  206.     }
  207.  
  208.     my @new_services = defined ($lines->{-1}) ? @{$lines->{-1}} : ();
  209.     delete $lines->{-1};
  210.  
  211.     # copy the file, replacing services at specified lines by
  212.     # changed ones
  213.     my @linenos = sort keys %{$lines};
  214.     y2debug "@linenos";
  215.  
  216.     # Be careful here not to read the line that starts the changed service
  217.     # because then we would have to "unget" it in both implementations
  218.     # of parse_service.
  219.     # Therefore we don't use the traditional "while(<>) {...}" loop.
  220.     # Loop rewritten to fix missing consecutive modified services (#24379).
  221.     $if->{lineno} = 0;
  222.     while (1)
  223.     {
  224.         # now $lineno is the line number of the contents of $_
  225.         # and $_ or its replacement has been output
  226.  
  227.         if (defined ($linenos[0]) && $if->{lineno}+1 == $linenos[0])
  228.         {
  229.         # got there
  230.         my $service = $lines->{shift @linenos};
  231.         # this writes the new data,
  232.         # "replace" copies the unknown data
  233.         $class->write_service ($OF, $service)
  234.             unless $service->{deleted};
  235.         $class->parse_service ($if); #discard the old data
  236.         }
  237.         else
  238.         {
  239.         # read a line, allowing for a non-existent file
  240.         if ($if->{fh})
  241.         {
  242.             $_ = $if->{fh}->getline;
  243.         }
  244.         else
  245.         {
  246.             $_ = undef;
  247.         }
  248.  
  249.         if (!defined $_)
  250.         {
  251.             # On eof, the branch with parse_service will not be taken
  252.             # because that would mean we think there are services
  253.             # past eof. Even in that case, parse_service will simply
  254.             # return and @linenos will be exhausted and we get here.
  255.             last;
  256.         }
  257.         ++$if->{lineno};
  258.         y2debug ($if->{name}, $if->{lineno}, "copy");
  259.  
  260.         print $OF $_;
  261.         }
  262.     }
  263.  
  264.     # can we add new services?
  265.     foreach my $service (@new_services)
  266.     {
  267.         y2debug "NEW $service->{service}";
  268.         $class->write_service ($OF, $service)
  269.         unless $service->{deleted};
  270.     }
  271.  
  272.     rename ($ofilename, $filename);
  273.     }
  274.     return $ret;
  275. }
  276.  
  277. sub services_by_package ()
  278. {
  279.     my $class = shift;
  280.  
  281.     open (OUT, ">/dev/fd/3");
  282.     foreach my $s (@services)
  283.     {
  284.     # Get the program name - used to see whether a service is installed.
  285.     # Assumedly multiple packages providing the same service will differ
  286.     # in the program name.
  287.     my $program = $s->{server};
  288.     if ($program =~ m:/tcpd$:)
  289.     {
  290.         my @args = split (/\s+/, $s->{server_args});
  291.         $program = shift (@args);
  292.     }
  293.     $program =~ s:^.*/::;
  294.  
  295.     # originally used TABs as delimiters,
  296.     # but awk then misses an empty protocol
  297.     print OUT "$s->{service},$s->{protocol},$program\n";
  298.     }
  299.     close (OUT);
  300. }
  301.  
  302.  
  303. package inetd;
  304. @inetd::ISA = qw(netd);
  305. use ycp;
  306.  
  307. # parses the next service, returns it or undef on EOF
  308. # does not attempt to read the next service!
  309. sub parse_service ($)
  310. {
  311.     my $class = shift;
  312.     ::DEBUG ("$class\n");
  313.     y2debug ("parse_service");
  314.  
  315.     my $file = shift;
  316.  
  317.     my $service = undef;
  318.     my $comment = "";
  319.     my $reclineno = 0; # line at which a record (including comment) starts
  320.     while (defined ($_ = $file->{fh}->getline))
  321.     {
  322.     ++$file->{lineno};
  323.     y2debug ($file->{name}, $file->{lineno}, "parse");
  324.  
  325.     chomp;
  326.     next if /^$/;
  327.     $reclineno ||= $file->{lineno};
  328.  
  329.     # Try parsing also commented out lines
  330.     my $enabled = 1;
  331.     # TODO
  332.     # spaces before #
  333.  
  334.     if (s/^#//)
  335.     {
  336.         $enabled = 0;
  337.     }
  338.     # leave $c intact for comment
  339.     my $c = $_;
  340.     s/^\s+//;
  341.  
  342.     # TODO test a truncated line
  343.     my @fields = split (/\s+/);
  344.     my ($service_ver, $socket_type, $protocol, $wait_max, $user_group,
  345.         $server, @server_args) = @fields;
  346.     if (!defined ($server) ||
  347.         $socket_type !~ /dgram|stream|raw|rdm|seqpacket/)
  348.     {
  349.         if (!$enabled)
  350.         {
  351.         # tried to parse a disabled service, did not work out
  352.         $comment .= "$c\n";
  353.         }
  354.         else
  355.         {
  356.         # TODO error? it is a damaged entry
  357.         }
  358.         next;
  359.     }
  360.     # the second variable of these pairs can be undef
  361.     my ($service_name, $rpc_version) = split (/\//, $service_ver);
  362.     $rpc_version ||= "";
  363.     my ($wait, $nmax) = split (/\./, $wait_max);
  364.     # "" is wrong, but the ycp code works around it
  365.     # convert to numbers
  366.     my $max = $nmax? \$nmax : "";
  367.     my ($user, $group) = split (/[.:]/, $user_group);
  368.     $group ||= "";
  369.  
  370.     # convert to booleans
  371.     $enabled = ycp::to_bool ($enabled);
  372.     $wait = ycp::to_bool ($wait eq "wait");
  373.  
  374.     $service =
  375.     {
  376.      "iid" => "$reclineno:$file->{name}",
  377.      "script" => File::Basename::basename ($file->{name}),
  378.  
  379.      "enabled" => $enabled,
  380.      "comment" => $comment,
  381.      "service" => $service_name,
  382.      "rpc_version" => $rpc_version,
  383.      "socket_type" => $socket_type,
  384.      "protocol" => $protocol,
  385.      "wait" => $wait,
  386.      "max" => $max,
  387.      "user" => $user,
  388.      "group" => $group,
  389.      "server" => $server,
  390.      "server_args" => join (" ", @server_args),
  391.     };
  392.     last;
  393.     }
  394.     return $service;
  395. }
  396.  
  397. sub write_service ($$)
  398. {
  399.     my $class = shift;
  400.     ::DEBUG ("$class\n");
  401.     y2debug ("write_service");
  402.  
  403.     my $f = shift;
  404.     my $s = shift;
  405.  
  406.     foreach (split (/\n/, $s->{comment}))
  407.     {
  408.     print $f "#$_\n";
  409.     }
  410.  
  411.     if (! $s->{enabled})
  412.     {
  413.     print $f "# ";
  414.     }
  415.  
  416.     print $f $s->{service};
  417.     print $f "/". $s->{rpc_version} if $s->{rpc_version};
  418.     print $f " ";
  419.  
  420.     print $f $s->{socket_type}. " ";
  421.     print $f $s->{protocol}. " ";
  422.  
  423.     print $f ($s->{wait}? "wait":"nowait");
  424.     print $f ".". $s->{max} if $s->{max};
  425.     print $f " ";
  426.  
  427.     print $f $s->{user};
  428.     print $f ".". $s->{group} if $s->{group};
  429.     print $f " ";
  430.  
  431.     print $f $s->{server};
  432.     print $f " ". $s->{server_args} if $s->{server_args};
  433.     print $f "\n";
  434. }
  435.  
  436.  
  437. package xinetd;
  438. @xinetd::ISA = qw(netd);
  439. use ycp;
  440.  
  441. sub parse_service ($)
  442. {
  443.     my $class = shift;
  444.     ::DEBUG ("$class\n");
  445.     y2debug ("parse_service");
  446.  
  447.     my $file = shift;
  448.     my $service = undef;
  449.     my $comment = "";
  450.     my $comment_inside = ""; # usually commented out attributes
  451.     my $reclineno = 0; # line at which a record (including comment) starts
  452.     my $state = "init"; # "brace", "inside"
  453.     my $in_defaults = 0;
  454.     my $unparsed = "";
  455.     while ($file && $file->{fh} && defined ($_ = $file->{fh}->getline))
  456.     {
  457.     ++$file->{lineno};
  458.     y2debug ($file->{name}, $file->{lineno}, "parse");
  459.  
  460.     chomp;
  461.     next if /^\s*$/;
  462.     $reclineno ||= $file->{lineno};
  463.  
  464.     if (s/^\s*#//)
  465.     {
  466.         if ($state eq "inside")
  467.         {
  468.         $comment_inside .= "$_\n";
  469.         }
  470.         else
  471.         {
  472.         $comment .= "$_\n";
  473.         }
  474.     }
  475.     else
  476.     {
  477.         if ($state eq "init")
  478.         {
  479.         if (/^\s*include\s+(\S+)/)
  480.         {
  481.             $class->parse_file ($1);
  482.             $reclineno = 0;
  483.         }
  484.         elsif (/^\s*includedir\s+(\S+)/)
  485.         {
  486.             $class->include_dir ($1);
  487.             # Bug 24270:
  488.             # the include directive must not belong
  489.             # to the following service
  490.             $reclineno = 0;
  491.         }
  492.         elsif (/^\s*service\s+(\S+)/)
  493.         {
  494.             $service =
  495.             {
  496.              "service" => $1,
  497.              "iid" => "$reclineno:$file->{name}",
  498.              "script" => File::Basename::basename ($file->{name}),
  499.              # default values:
  500.              "enabled" => ycp::to_bool (1),
  501.              "rpc_version" => "",
  502.              "socket_type" => "MISSING", # mandatory
  503.              "protocol" => "",
  504.              "wait" => ycp::to_bool (0), # mandatory
  505.              "user" => "",
  506.              "group" => "",
  507.              "server" => "",
  508.              "server_args" => "",
  509.             };
  510.             $state = "brace";
  511.             $in_defaults = 0;
  512.         }
  513.         elsif (/^\s*defaults\s*$/)
  514.         {
  515.             $service =
  516.             {
  517.              "is_defaults" => 1,
  518.             };
  519.             $state = "brace";
  520.             $in_defaults = 1;
  521.         }
  522.         else
  523.         {
  524.             y2error ("Expecting \"service\" at ".
  525.                  "$file->{name}:$file->{lineno}");
  526.         }
  527.         }
  528.         elsif ($state eq "brace")
  529.         {
  530.         if (/^\s*\{\s*$/)
  531.         {
  532.             $state = "inside";
  533.         }
  534.         else
  535.         {
  536.             y2error ("An opening brace ({) should follow a ".
  537.                  "\"service $service->{service}\" line. Seen $_.");
  538.             # continue.
  539.         }
  540.         }
  541.         elsif ($state eq "inside")
  542.         {
  543.         if (/^\s*\}\s*$/)
  544.         {
  545.             # wrap up
  546.             $service->{comment} = $comment;
  547.             $service->{comment_inside} = $comment_inside;
  548.             $service->{unparsed} = $unparsed;
  549.             last;
  550.         }
  551.         elsif (m{^\s*(\S+)\s*=\s*(.*?)\s*$})
  552.         {
  553.             my ($attribute, $value) = ($1, $2);
  554.             if ($in_defaults)
  555.             {
  556.             if ($attribute =~ m{enabled|disabled})
  557.             {
  558.                 $service->{$attribute} .= "$value ";
  559.             }
  560.             else
  561.             {
  562.                 $unparsed .= "$_\n";
  563.             }
  564.             }
  565.             else
  566.             {
  567.             # "disable" != "disabled"
  568.             if ($attribute eq "disable")
  569.             {
  570.                 $service->{enabled} = ycp::to_bool($value eq "no");
  571.             }
  572.             elsif ($attribute =~ m{rpc_version|
  573.                     socket_type|
  574.                     protocol|
  575.                     wait| # bool
  576.                     user|
  577.                     group|
  578.                     server|
  579.                     server_args
  580.                        }x)
  581.             {
  582.                 if ($attribute eq "wait")
  583.                 {
  584.                 $value = ycp::to_bool ($value eq "yes");
  585.                 }
  586.                 $service->{$attribute} = $value;
  587.             }
  588.             else
  589.             {
  590.                 $unparsed .= "$_\n";
  591.             }
  592.             }
  593.         }
  594.         else
  595.         {
  596.             $unparsed .= "$_\n";
  597.         }
  598.         }
  599.         else
  600.         {
  601.         y2internal ("Unknown state $state.");
  602.         $state = "inside"; # recover from the error
  603.         }
  604.     }
  605.     }
  606.     return $service;
  607. }
  608.  
  609. sub write_item ($$$;$)
  610. {
  611.     my ($f, $s, $attribute, $value) = @_;
  612.     $value ||= $s->{$attribute};
  613.     # there's #define FIELD_WIDTH 15 in itox.c
  614.     printf $f "\t%-15s = %s\n", $attribute, $value;
  615. }
  616.  
  617. sub write_opt_item ($$$)
  618. {
  619.     my ($f, $s, $attribute) = @_;
  620.     write_item ($f, $s, $attribute) if $s->{$attribute};
  621. }
  622.  
  623. sub write_service ($$)
  624. {
  625.     my $class = shift;
  626.     ::DEBUG ("$class\n");
  627.     y2debug ("write_service");
  628.  
  629.     my $f = shift;
  630.     my $s = shift;
  631.  
  632.     foreach (split (/\n/, $s->{comment} || ""))
  633.     {
  634.     print $f "#$_\n";
  635.     }
  636.  
  637.     print $f "service $s->{service}\n";
  638.     print $f "{\n";
  639.  
  640.     foreach (split (/\n/, $s->{comment_inside} || ""))
  641.     {
  642.     print $f "#$_\n";
  643.     }
  644.  
  645.     if (! $s->{enabled})
  646.     {
  647.     write_item $f, $s, "disable", "yes";
  648.     }
  649.     if ($s->{rpc_version})
  650.     {
  651.     $s->{type} ||= ""; # prevent undef
  652.     $s->{type} .= " RPC" if ($s->{type} !~ /RPC/);
  653.     write_item $f, $s, "rpc_version";
  654.     }
  655.     write_item     $f, $s, "socket_type";
  656.     write_opt_item $f, $s, "protocol";
  657.     write_item     $f, $s, "wait", ($s->{wait}? "yes":"no");
  658.  
  659.     write_opt_item $f, $s, "user";
  660.     write_opt_item $f, $s, "group";
  661.     write_opt_item $f, $s, "server";
  662.     write_opt_item $f, $s, "server_args";
  663.  
  664.     write_opt_item $f, $s, "type";
  665.     print $f $s->{unparsed} || "";
  666.     print $f "}\n";
  667. }
  668.  
  669.  
  670. package main;
  671. #
  672. # MAIN cycle
  673. #
  674.  
  675. my $netd;
  676. # if reading fails, defaulting to no services
  677. @services = ();
  678.  
  679. while ( <STDIN> )
  680. {
  681.     chomp;
  682.     y2debug ("Got: ", $_);
  683.     if (/^nil$/)
  684.     {
  685.     print "nil\n";
  686.     next;
  687.     }
  688.  
  689.     my ($command, @arguments) = ycp::ParseTerm ($_);
  690.     if ($command =~ "Netd|Inetd|Xinetd")
  691.     {
  692.     # reply to the client (this actually gets eaten by the ScriptingAgent)
  693.     ycp::Return (undef);
  694.     print "\n";
  695.     $netd = ($command eq "Xinetd")? "xinetd": "inetd";
  696.     my $fn = shift @arguments || "/etc/$netd.conf";
  697.  
  698.     $base_file = strip_pwd ($fn);
  699.  
  700.     # parsing is done before Read (.services)
  701.     # so that we can re-read the config if a new package is installed
  702.     next;
  703.     }
  704.     # else it should be a regular command
  705.     my $path = "";
  706.     my $pathref = shift @arguments;
  707.     if (defined $pathref)
  708.     {
  709.     if (ref($pathref) eq "SCALAR" && $$pathref =~ /^\./)
  710.     {
  711.         $path = $$pathref;
  712.     }
  713.     # 'result (nil)' is a standard command
  714.     elsif ($command ne "result")
  715.     {
  716.         y2error ("The first argument is not a path. ('$pathref')");
  717.     }
  718.     }
  719.     my $argument = shift @arguments;
  720.     y2warning ("Superfluous command arguments ignored") if (@arguments > 0);
  721.  
  722.  
  723.     if ($command eq "Dir")
  724.     {
  725.     if ($path eq ".")
  726.     {
  727.         ycp::Return (["message", "services"]);
  728.     }
  729.     elsif ($path eq ".services")
  730.     {
  731.         my @snames = map { $_->{service} } @services;
  732.         ycp::Return ([ sort @snames ], 1);
  733.     }
  734.     else
  735.     {
  736.         ycp::Return ([]);
  737.     }
  738.     }
  739.  
  740.     elsif ($command eq "Write")
  741.     {
  742.     my $result = 1;
  743.     if ($path eq ".services" && ref ($argument) eq "ARRAY")
  744.     {
  745.         @services = @{$argument};
  746.         $result = $netd->write_file ();
  747.     }
  748.     else
  749.     {
  750.         y2error ("Wrong path $path or argument: ", ref ($argument));
  751.         $result = 0;
  752.     }
  753.     ycp::Return (ycp::to_bool ($result));
  754.     }
  755.  
  756.     elsif ($command eq "Read")
  757.     {
  758.     if ($path eq ".services")
  759.     {
  760.         # must clear before re-reading
  761.         # but not in parse_file, that would break includedir
  762.         @services = ();
  763.         $netd->parse_file ($base_file);
  764.         ycp::Return (\@services, 1);
  765.     }
  766.     elsif ($path eq ".message")
  767.     {
  768.         ycp::Return ($message, 1);
  769.         $message = "";
  770.     }
  771.     else
  772.     {
  773.         y2error ("Unrecognized path! '$path'");
  774.         ycp::Return (undef);
  775.     }
  776.     }
  777.  
  778.     # A special hack - write known services/protocols to fd3
  779.     # Used for gathering a list of all available services by package
  780.     elsif ($command eq "Execute")
  781.     {
  782.     if ($path eq ".sbp")
  783.     {
  784.         $netd->services_by_package ();
  785.         ycp::Return ("true");
  786.     }
  787.     else
  788.     {
  789.         y2error ("Unrecognized path! '$path'");
  790.         ycp::Return (undef);
  791.     }
  792.     }
  793.  
  794.     elsif ($command eq "result")
  795.     {
  796.     exit;
  797.     }
  798.  
  799.     # Unknown command
  800.     else
  801.     {
  802.     y2error ("Unknown instruction $command or argument: ", ref ($argument));
  803.     ycp::Return (undef);
  804.     }
  805.     print "\n";
  806. }
  807.