ACC SHELL

Path : /usr/lib/YaST2/servers_non_y2/
File Upload :
Current File : //usr/lib/YaST2/servers_non_y2/ag_passwd

#!/usr/bin/perl -w

use lib "/usr/lib/YaST2/agents_non_y2";
use ycp;
use strict;

my $max_system_uid	= 499;
my $max_system_gid	= 499;

my %last_uid		= (
    "local"	=> $max_system_uid + 1,
    "system"	=> 0
);

my %users		= ();
my %groups		= ();
my %shadow_tmp		= ();
my %shadow		= ();

# used for check if values like uid,username are unique:
my %uids		= ();
my %usernames		= ();
my %homes		= (); 
my %gids		= ();
my %groupnames		= ();

# for each user there is a list of groups, where the user is contained
my %users_groups	= ();

my %users_by_uidnumber	= ();
my %groups_by_gidnumber	= ();

my $plus_line_group 	= "";
my $plus_line_passwd	= "";
my $plus_line_shadow	= "";

my @plus_lines_group 	= ();
my @plus_lines_passwd	= ();
my @plus_lines_shadow	= ();

my @comments_group 	= ();
my @comments_passwd	= ();
my @comments_shadow	= ();

# error number
my $errno		= 0;

#more information about the error
my $error_info		= "";

# directory where user/group/shadow data should be found
my $base_directory	 = "/etc";

# compares 2 arrays; return 1 if they are equal
# (from perlfaq)
sub same_arrays {

    my ($first, $second) = @_;
    return 0 unless @$first == @$second;
    for (my $i = 0; $i < @$first; $i++) {
	return 0 if $first->[$i] ne $second->[$i];
    }
    return 1;
}


#---------------------------------------------
# read /etc/shadow and prepare global 'shadow_tmp' structure

sub read_shadow {

    %shadow_tmp	= ();
    @plus_lines_shadow	= ();
    @comments_shadow	= ();

    my $o = open SHADOW, "< $base_directory/shadow";
    if (!defined $o) {
	y2warning ("$base_directory/shadow cannot be opened for reading!");
	return 0;
    }

    foreach my $shadow_entry (<SHADOW>)
    {
	chomp $shadow_entry;

	if ($shadow_entry eq "") {
	    y2warning ("empty line in shadow file...");
	    next;
	}

	my ($uname,$pass,$last_change,$min, $max, $warn, $inact, $expire, $flag)
	    = split(/:/,$shadow_entry);  
        my $first = substr ($uname, 0, 1);

	if ($first eq "#") {
	    y2warning ("Found comment line in shadow file: '$shadow_entry'");
	    y2warning ("It will be moved to the end of file");
	    push @comments_shadow, $shadow_entry;
	}
	elsif ($first ne "+" && $first ne "-")
	{
	    if (!defined $uname || $uname eq "") {
		y2error ("strange line in shadow file: '$shadow_entry'");
		$errno 		= 9;
		return 9;
	    }

	    if (defined $shadow_tmp{$uname})
	    {
		y2error ("duplicated username in /etc/shadow! Exiting...");
		$errno		= 3;
		$error_info 	= $uname;
		return 3;
	    }
	    $shadow_tmp{$uname} = {
		"shadowlastchange"	=> $last_change,
		"shadowwarning"		=> $warn,
		"shadowinactive"	=> $inact,
		"shadowexpire"		=> $expire,
		"shadowmin"		=> $min,
		"shadowmax"		=> $max,
		"shadowflag"		=> $flag,
		"userpassword"		=> $pass
	    };
	}
	else # plus line in /etc/shadow
	{
	    $plus_line_shadow = $shadow_entry;
	    push @plus_lines_shadow, $shadow_entry;
	}
    }
    close SHADOW;
    return 0;
}

#---------------------------------------------
# read /etc/group and prepar global 'users_groups' structure

sub read_group {

    %groups	= ();
    %gids	= ();
    %groupnames	= ();
    @plus_lines_group	= ();
    @comments_group	= ();

    my $o = open GROUP, "< $base_directory/group";
    if (!defined $o) {
	y2warning ("$base_directory/group cannot be opened for reading!");
	return 0;
    }

    foreach my $group (<GROUP>) {
	
	chomp $group;

	if ($group eq "") {
	    y2warning ("empty line in group file ...");
	    next;
	}
        my ($groupname, $pass, $gid, $users) = split (/:/,$group);
	my $first = substr ($groupname, 0, 1);

	if ($first eq "#") {
	    y2warning ("Found comment line in group file: '$group'");
	    y2warning ("It will be moved to the end of file");
	    push @comments_group, $group;
	}
        elsif ( $first ne "+" && $first ne "-" ) {
	    
	    if (!defined $pass || !defined $gid || !defined $users ||
		$gid eq "") {
		y2error ("strange line in group file: '$group'");
		$errno 		= 8;
		$error_info 	= "$group";
		return 8;
	    }
		
	    my $group_type = "local";
	    if (($gid <= $max_system_gid || $groupname eq "nobody" ||
		 $groupname eq "nogroup") &&
		($groupname ne "users"))
	    {
		$group_type = "system";
	    }

            # check for duplicates...
	    if (defined $gids{$group_type}{$gid})
	    {
		y2warning ("duplicated gid ($gid) in /etc/group");
	        $gids{$group_type}{$gid} = $gids{$group_type}{$gid} + 1;
	    }
	    else
            {
	        $gids{$group_type}{$gid} = 1;
	    }

            if (defined $groupnames{"local"}{$groupname} ||
		defined $groupnames{"system"}{$groupname})
            {
	        y2error ("duplicated groupname in /etc/group! Exiting...");
		$errno		= 6;
		$error_info 	= $groupname;
	        return 6;
            }
	    else
	    {
		$groupnames{$group_type}{$groupname} = 1;
            }
            # for each user generate list of groups, where the user is contained
	    my @userlist	= split(/,/,$users);
	    my %userlist	= ();
	    foreach my $u (@userlist) {
		$userlist{$u}			= 1;
		$users_groups{$u}{$groupname}	= 1;
	    }
	    $groups{$group_type}{$groupname} = {
		"cn"		=> $groupname,
		"gidnumber" 	=> $gid,
		"userlist"	=> \%userlist,
		"type"		=> $group_type,
		"userpassword"	=> $pass,
		"more_users"	=> {}
	    };

	    if (!defined $groups_by_gidnumber{$group_type}{$gid}) {
		$groups_by_gidnumber{$group_type}{$gid} = {};
	    }
	    $groups_by_gidnumber{$group_type}{$gid}{$groupname}	= 1;
	}
	else # save the possible "+"/"-" entries
        {
	    $plus_line_group = $group;
	    push @plus_lines_group, $group;
        }
    }
    close GROUP;
    return 0;
}

# --------------------------------------------------------------------
# read /etc/passwd

sub read_passwd {

    my $o = open PASSWD, "< $base_directory/passwd";
    if (!defined $o) {
	y2warning ("$base_directory/passwd cannot be opened for reading!");
	return 0;
    }

    %users 	= ();
    %shadow	= ();
    %uids	= ();
    %usernames	= ();
    %homes	= ();
    @plus_lines_passwd	= ();
    @comments_passwd	= ();

    foreach my $user (<PASSWD>) {

	chomp $user;

	if ($user eq "") {
	    y2warning ("empty line in passwd file...");
	    next;
	}
	my ($username, $password, $uid, $gid, $full, $home, $shell)
	    = split(/:/,$user);
        my $first = substr ($username, 0, 1);

	if ($first eq "#") {
	    y2warning ("Found comment line in passwd file: '$user'");
	    y2warning ("It will be moved to the end of file");
	    push @comments_passwd, $user;
	}
	elsif ($first ne "+" && $first ne "-") {

	    if (!defined $password || !defined $uid || !defined $gid ||
		!defined $full || !defined $home || !defined $shell ||
		$username eq "" || $uid eq "" || $gid eq "") {
		y2error ("strange line in passwd file: '$user'");
		$errno 		= 7;
		$error_info 	= "$user";
		return 7;
	    }
		
            my $user_type	= "local";
	    my $group_type	= "";
	    my $groupname	= "";
	    my %grouplist	= ();

	    if (defined $groups_by_gidnumber{"system"}{$gid})
	    {
		$group_type = "system";
	    }
	    if (defined $groups_by_gidnumber{"local"}{$gid})
	    {
		$group_type = "local";
	    }
	    if ($group_type ne "")
	    {
		$groupname = (keys %{$groups_by_gidnumber{$group_type}{$gid}})[0];
		# modify default group's more_users entry
		$groups{$group_type}{$groupname}{"more_users"}{$username}	= 1;
	    }

	    # add the grouplist
	    if (defined $users_groups{$username}) {
		%grouplist = %{$users_groups{$username}};
	    }

	    if (($uid <= $max_system_uid) || ($username eq "nobody")) {
		$user_type = "system";
		if ($last_uid{"system"} < $uid  && $username ne "nobody") {
		    $last_uid{"system"} = $uid;
		}
	    }
	    else {
		if ($last_uid{"local"} < $uid) {
		    $last_uid{"local"} = $uid;
		}
	    }
	    my $encoding = "";
	    if ($encoding ne "") {
		from_to ($full, $encoding, "utf-8");
	    }
    
	    my $colon = index ($full, ",");
	    my $additional = "";
	    if ( $colon > -1)
	    {
		$additional = $full;
		$full = substr ($additional, 0, $colon);
		$additional = substr ($additional, $colon + 1,
		    length ($additional));
	    }

            # check for duplicates in /etc/passwd:
	    if (defined $uids{$user_type}{$uid})
	    {
		y2warning ("duplicated UID in /etc/passwd: $uid");
	        $uids{$user_type}{$uid} = $uids{$user_type}{$uid} + 1;
	    }
            else
	    {
	        $uids{$user_type}{$uid} = 1;
            }
	    
	    if (defined $usernames{"local"}{$username} ||
		defined $usernames{"system"}{$username})
	    {
		y2error ("duplicated username in /etc/passwd! Exiting...");
		$errno = 2;
		$error_info 	= $username;
		return 2;
	    }
	    else
	    {
		$usernames{$user_type}{$username} = 1;
	    }
	    if ($home ne "")
	    {
		$homes{$user_type}{$home} = 1;
	    }
    
	    my @grouplist	= keys (%grouplist);

	    # such map we would like to export from the read script...
	    $users{$user_type}{$username} = {
		"addit_data"	=> $additional,
		"cn"		=> $full,
		"homedirectory"	=> $home,
		"uid"		=> $username,
		"uidnumber"	=> $uid,
		"gidnumber"	=> $gid,
		"loginshell"	=> $shell,
		"groupname"	=> $groupname,
		"grouplist"	=> \%grouplist,
		"userpassword"	=> undef,
		"type"		=> $user_type
	    };

	    if (! defined $shadow_tmp{$username}) {
		y2debug ("There is no shadow entry for user $username.");
	    }
	    else {
		# divide shadow map accoring to user type
		$shadow{$user_type}{$username} = $shadow_tmp{$username};
	    }

	    if (!defined $users_by_uidnumber{$user_type}{$uid}) {
		$users_by_uidnumber{$user_type}{$uid} = {};
	    }
	    $users_by_uidnumber{$user_type}{$uid}{$username}	= 1;
	}
	else # the "+" entry in passwd
	{
	    $plus_line_passwd = $user;
	    push @plus_lines_passwd, $user;
	}
    }
    close PASSWD;
    return 0;
}

#---------------------------------------------
# write map of users to the file

sub write_passwd {

    my %users_w		= %{$_[0]};

    # do not allow user to remove whole passwd
    if (!%users_w) {
	%users_w	= %users;
    }

    my $o = open PASSWD, "> $base_directory/passwd";
    if (!defined $o) {
	y2error ("$base_directory/passwd cannot be opened for writing!");
	$errno		= 100;
	return 0;
    }

    foreach my $type (sort {$b cmp $a} keys %users_w) {

	if ($type ne "local" && $type ne "system") {
	    next;
	}

	foreach my $username (sort keys %{$users_w{$type}}) {

	    my %user	= %{$users_w{$type}{$username}};
	    my $pass	= "x";
	    my $cn	= $user{"cn"} || "";
	    if (defined $user{"addit_data"} && $user{"addit_data"} ne "") {
		$cn	.= ",".$user{"addit_data"};
	    }
	    my $userline	= join (":", (
		$user{"uid"} || "",
		$pass,
		$user{"uidnumber"} || 0,
		$user{"gidnumber"} || 0,
		$cn,
		$user{"homedirectory"} || "",
		$user{"loginshell"} || "",
	    ));
	    if (defined $userline) {
		print PASSWD "$userline\n";
	    }
	}
    }
    if (@comments_passwd > 0) {
	foreach my $comment (@comments_passwd) {
	    print PASSWD "$comment\n";
	}
    }
    if (@plus_lines_passwd > 0) {
	foreach my $plusline (@plus_lines_passwd) {
	    print PASSWD "$plusline\n";
	}
    }
    close PASSWD;
    return 1;
}

#---------------------------------------------
# write map of shadow info to the file

sub write_shadow {

    my %shadow_w	= %{$_[0]};

    # do not allow user to remove whole shadow
    if (!%shadow_w) {
	%shadow_w	= %shadow;
    }

    my $o = open SHADOW, "> $base_directory/shadow";
    if (!defined $o) {
	y2error ("$base_directory/shadow cannot be opened for writing!");
	$errno		= 101;
	return 0;
    }

    foreach my $type (sort {$b cmp $a} keys %shadow_w ) {

	if ($type ne "local" && $type ne "system") {
	    next;
	}

        foreach my $uname (sort keys %{$shadow_w{$type}}) {

	    my %shadow_entry	= %{$shadow_w{$type}{$uname}};
	    foreach my $key ("shadowwarning", "shadowinactive", "shadowexpire", "shadowflag", "userpassword") {
		if (!defined $shadow_entry{$key}) {
		    $shadow_entry{$key}	= "";
		}
	    }
	    my $shadowline	= join (":", (
		$uname,
		$shadow_entry{"userpassword"},
		$shadow_entry{"shadowlastchange"},
		$shadow_entry{"shadowmin"},
		$shadow_entry{"shadowmax"},
		$shadow_entry{"shadowwarning"},
		$shadow_entry{"shadowinactive"},
		$shadow_entry{"shadowexpire"},
		$shadow_entry{"shadowflag"}
	    ));
	    if (defined $shadowline) {
		print SHADOW "$shadowline\n";
	    }
	}
    }
    if (@comments_shadow > 0) {
	foreach my $comment (@comments_shadow) {
	    print SHADOW "$comment\n";
	}
    }
    if (@plus_lines_shadow > 0) {
	foreach my $plusline (@plus_lines_shadow) {
	    print SHADOW "$plusline\n";
	}
    }
    close SHADOW;
}


#---------------------------------------------
# write map of groups to the file

sub write_group {

    my %groups_w = %{$_[0]};

    # do not allow user to remove whole group
    if (!%groups_w) {
	%groups_w	= %groups;
    }

    my $o = open GROUP, "> $base_directory/group";
    if (!defined $o) {
	y2error ("$base_directory/group cannot be opened for writing!");
	$errno		= 102;
	return 0;
    }

    # sort order: system items go before local ones
    foreach my $type (sort {$b cmp $a} keys %groups_w ) {

	if ($type ne "local" && $type ne "system") {
	    next;
	}

	# sort order: id
        foreach my $groupname (sort keys %{$groups_w{$type}}) {

	    my %group	= %{$groups_w{$type}{$groupname}};
	    my $pass	= "x";
	    if (defined $group{"userpassword"}) {
		$pass 	= $group{"userpassword"};
	    }
	    my @group_entry	= (
		$group{"cn"},
		$pass,
		$group{"gidnumber"} || 0,
		join (",", sort keys %{$group{"userlist"}})
	    );
	    my $groupline	= join (":", @group_entry);

	    if (defined $groupline) {
		print GROUP "$groupline\n";
	    }
	}
    }
    if (@comments_group > 0) {
	foreach my $comment (@comments_group) {
	    print GROUP "$comment\n";
	}
    }
    if (@plus_lines_group > 0) {
	foreach my $plusline (@plus_lines_group) {
	    print GROUP "$plusline\n";
	}
    }
    close GROUP;
}



sub read_all () {

    my $error = 0;

    $error = read_shadow ();
    
    if ($error == 0) {
	$error = read_group ();
    }
    if ($error == 0) {
	$error = read_passwd ();
    }
    return $error;
}


# --------------------------------------- main -----------------------------
while ( <STDIN> )
{

    my ($command, $path, $argument) = ycp::ParseCommand ($_);

    y2debug ("command: $command, path: $path");
    
    if ( $command eq "Execute" ) {
    
	if ( $path eq '.init' ) {
	    if (ref ($argument) eq "HASH") {
		if (defined ($argument->{"max_system_uid"})) {
		    $max_system_uid	= $argument->{"max_system_uid"};
		}
		if (defined ($argument->{"max_system_gid"})) {
		    $max_system_gid	= $argument->{"max_system_gid"};
		}
		if (defined ($argument->{"base_directory"})) {
		    $base_directory	= $argument->{"base_directory"};
		}
	    }
	    my $error = read_all ();
	    if ($error > 0) {
		ycp::Return ("false");
	    }
	    else {
		ycp::Return("true");
	    }
	}
	else {
	    y2error ("wrong path ($path) or argument type:", ref ($argument));
	    ycp::Return("false");
	}
    }
    elsif ( $command eq "Read" ) {

	# check if initialization was done
	if (!%users && $path ne '.error' && $path ne '.error.info') {
	    my $error = read_all ();
	    if ($error > 0) {
		ycp::Return ({});
	    }
	}
	if ($path eq '.error') {
	    ycp::Return ($errno);
	}
	elsif ($path eq '.error.info') {
	    ycp::Return ($error_info);
	}
	elsif    ( $path eq '.local.users' ) {
	    ycp::Return (\%{$users{"local"}}, 1);
	}
	elsif ( $path eq '.system.users' ) {
	    ycp::Return (\%{$users{"system"}}, 1);
	}
	elsif ( $path eq '.local.shadow' ) {
	    ycp::Return (\%{$shadow{"local"}}, 1);
	}
	elsif ( $path eq '.system.shadow' ) {
	    ycp::Return (\%{$shadow{"system"}}, 1);
	}
	elsif ( $path eq '.local.groups' ) {
	    ycp::Return (\%{$groups{"local"}}, 1);
	}
	elsif ( $path eq '.system.groups' ) {
	    ycp::Return (\%{$groups{"system"}}, 1);
	}
	elsif ( $path eq '.local.users.by_name' ) {
	    y2warning ("$command $path is deprecated");
	    ycp::Return ({});
	}
	elsif ( $path eq '.system.users.by_name' ) {
	    y2warning ("$command $path is deprecated");
	    ycp::Return ({});
	}
	elsif ( $path eq '.local.users.by_uidnumber' ) {
	    ycp::Return (\%{$users_by_uidnumber{"local"}}, 1);
	}
	elsif ( $path eq '.system.users.by_uidnumber' ) {
	    ycp::Return (\%{$users_by_uidnumber{"system"}}, 1);
	}
	elsif ( $path eq '.local.users.uids' ) {
	    ycp::Return (\%{$uids{"local"}}, 1);
	}
	elsif ( $path eq '.system.users.uids' ) {
	    ycp::Return (\%{$uids{"system"}}, 1);
	}
	elsif ( $path eq '.local.users.usernames' ) {
	    ycp::Return (\%{$usernames{"local"}}, 1);
	}
	elsif ( $path eq '.system.users.usernames' ) {
	    ycp::Return (\%{$usernames{"system"}}, 1);
	}
	elsif ( $path eq '.local.users.homes' ) {
	    ycp::Return (\%{$homes{"local"}}, 1);
	}
	elsif ( $path eq '.system.users.homes' ) {
	    ycp::Return (\%{$homes{"system"}}, 1);
	}
	elsif ( $path eq '.local.users.last_uid' ) {
	    ycp::Return ($last_uid{"local"}, 1);
	}
	elsif ( $path eq '.system.users.last_uid' ) {
	    ycp::Return ($last_uid{"system"}, 1);
	}
	elsif ( $path eq '.local.groups.by_name' ) {
	    y2warning ("$command $path is deprecated");
	    ycp::Return ({});
	}
	elsif ( $path eq '.system.groups.by_name' ) {
	    y2warning ("$command $path is deprecated");
	    ycp::Return ({});
	}
	elsif ( $path eq '.local.groups.by_gidnumber' ) {
	    ycp::Return (\%{$groups_by_gidnumber{"local"}}, 1);
	}
	elsif ( $path eq '.system.groups.by_gidnumber' ) {
	    ycp::Return (\%{$groups_by_gidnumber{"system"}}, 1);
	}
	elsif ( $path eq '.local.groups.gids' ) {
	    ycp::Return (\%{$gids{"local"}}, 1);
	}
	elsif ( $path eq '.system.groups.gids' ) {
	    ycp::Return (\%{$gids{"system"}}, 1);
	}
	elsif ( $path eq '.local.groups.groupnames' ) {
	    ycp::Return (\%{$groupnames{"local"}}, 1);
	}
	elsif ( $path eq '.system.groups.groupnames' ) {
	    ycp::Return (\%{$groupnames{"system"}}, 1);
	}
	elsif ( $path eq '.passwd.plusline' ) {
	    y2warning ("$command $path is deprecated, use '.passwd.pluslines' instead!");
	    ycp::Return ($plus_line_passwd);
	}
	elsif ( $path eq '.shadow.plusline' ) {
	    y2warning ("$command $path is deprecated, use '.shadow.pluslines' instead!");
	    ycp::Return ($plus_line_shadow);
	}
	elsif ( $path eq '.group.plusline' ) {
	    y2warning ("$command $path is deprecated, use '.group.pluslines' instead!");
	    ycp::Return ($plus_line_group);
	}
	elsif ( $path eq '.passwd.pluslines' ) {
	    ycp::Return (\@plus_lines_passwd, 1);
	}
	elsif ( $path eq '.shadow.pluslines' ) {
	    ycp::Return (\@plus_lines_shadow, 1);
	}
	elsif ( $path eq '.group.pluslines' ) {
	    ycp::Return (\@plus_lines_group, 1);
	}
	elsif ( $path eq '.passwd.comments' ) {
	    ycp::Return (\@comments_passwd, 1);
	}
	elsif ( $path eq '.group.comments' ) {
	    ycp::Return (\@comments_group, 1);
	}
	elsif ( $path eq '.shadow.comments' ) {
	    ycp::Return (\@comments_shadow, 1);
	}
	else {
	    y2error ("wrong path ($path) or argument: ", ref ($argument));
	    ycp::Return("false");
	}
    }
    elsif ( $command eq "Write" )
    {
	if ( $path eq '.users' && ref ($argument) eq "HASH" ) {
	    if (write_passwd ($argument)) {
		ycp::Return ("true");
	    }
	    else {
		ycp::Return ("false");
	    }
	}
	elsif ( $path eq '.shadow' && ref ($argument) eq "HASH" ) {
	    if (write_shadow ($argument)) {
		ycp::Return ("true");
	    }
	    else {
		ycp::Return ("false");
	    }
	}
	elsif ( $path eq '.groups' && ref ($argument) eq "HASH" ) {
	    if (write_group ($argument)) {
		ycp::Return ("true");
	    }
	    else {
		ycp::Return ("false");
	    }
	}
	elsif ( $path eq '.passwd.plusline' ) {
	    y2warning ("$command $path deprecated; use '.passwd.pluslines' instead!");
	    if ($argument eq $plus_line_passwd) {
		ycp::Return ("false");
	    }
	    else {
		@plus_lines_passwd	= ($argument);
		ycp::Return ("true");
	    }
	}
	elsif ( $path eq '.group.plusline' ) {
	    y2warning ("$command $path deprecated; use '.group.pluslines' instead!");
	    if ($argument eq $plus_line_group) {
		ycp::Return ("false");
	    }
	    else {
		@plus_lines_group	= ($argument);
		ycp::Return ("true");
	    }
	}
	elsif ( $path eq '.shadow.plusline' ) {
	    y2warning ("$command $path deprecated; use '.shadow.pluslines' instead!");
	    if ($argument eq $plus_line_shadow) {
		ycp::Return ("false");
	    }
	    else {
		@plus_lines_shadow	= ($argument);
		ycp::Return ("true");
	    }
	}
	elsif ($path eq '.passwd.pluslines' && ref ($argument) eq "ARRAY") {
	    if (same_arrays (\@plus_lines_passwd, $argument)) {
		ycp::Return ("false");
	    }
	    else {
		@plus_lines_passwd = @$argument;
		y2milestone ("new plus lines in passwd: ", @plus_lines_passwd);
		ycp::Return ("true");
	    }
	}
	elsif ($path eq '.shadow.pluslines' && ref ($argument) eq "ARRAY") {
	    if (same_arrays (\@plus_lines_shadow, $argument)) {
		ycp::Return ("false");
	    }
	    else {
		@plus_lines_shadow = @$argument;
		y2milestone ("new plus lines in shadow: ", @plus_lines_shadow);
		ycp::Return ("true");
	    }
	}
	elsif ($path eq '.group.pluslines' && ref ($argument) eq "ARRAY") {
	    if (same_arrays (\@plus_lines_group, $argument)) {
		ycp::Return ("false");
	    }
	    else {
		@plus_lines_group = @$argument;
		y2milestone ("new plus lines in group: ", @plus_lines_group);
		ycp::Return ("true");
	    }
	}
	else {
	    y2error ("wrong path ($path) or argument type: ",  ref ($argument));
	    ycp::Return("false");
	}
    }
    elsif ( $command eq "result")
    {
	exit;
    }
    else
    {
	y2error ("wrong command: $command");
	ycp::Return("wrong command ($command)");
    }
}

# end

ACC SHELL 2018