xref: /onnv-gate/usr/src/cmd/perl/contrib/Sun/Solaris/Project/Project.pm (revision 12388:1bc8d55b0dfd)
10Sstevel@tonic-gate#
2*12388SJohn.Sonnenschein@Sun.COM# Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
37298SMark.J.Nelson@Sun.COM#
47298SMark.J.Nelson@Sun.COM
50Sstevel@tonic-gate#
60Sstevel@tonic-gate# Project.pm provides the bootstrap for the Sun::Solaris::Project module, and
70Sstevel@tonic-gate# also functions for reading, validating and writing out project(4) format
80Sstevel@tonic-gate# files.
90Sstevel@tonic-gate#
100Sstevel@tonic-gate################################################################################
118287SJohn.Sonnenschein@Sun.COMrequire 5.8.4;
120Sstevel@tonic-gate
130Sstevel@tonic-gateuse strict;
140Sstevel@tonic-gateuse warnings;
150Sstevel@tonic-gateuse locale;
160Sstevel@tonic-gateuse Errno;
170Sstevel@tonic-gateuse Fcntl;
180Sstevel@tonic-gateuse File::Basename;
190Sstevel@tonic-gateuse POSIX qw(locale_h limits_h);
200Sstevel@tonic-gate
210Sstevel@tonic-gatepackage Sun::Solaris::Project;
220Sstevel@tonic-gate
237298SMark.J.Nelson@Sun.COMour $VERSION = '1.9';
240Sstevel@tonic-gate
250Sstevel@tonic-gateuse XSLoader;
260Sstevel@tonic-gateXSLoader::load(__PACKAGE__, $VERSION);
270Sstevel@tonic-gate
280Sstevel@tonic-gateour (@EXPORT_OK, %EXPORT_TAGS);
290Sstevel@tonic-gatemy @constants = qw(MAXPROJID PROJNAME_MAX PROJF_PATH PROJECT_BUFSZ
300Sstevel@tonic-gate    SETPROJ_ERR_TASK SETPROJ_ERR_POOL);
310Sstevel@tonic-gatemy @syscalls = qw(getprojid);
320Sstevel@tonic-gatemy @libcalls = qw(setproject activeprojects getprojent setprojent endprojent
330Sstevel@tonic-gate    getprojbyname getprojbyid getdefaultproj fgetprojent inproj
340Sstevel@tonic-gate    getprojidbyname);
350Sstevel@tonic-gatemy @private = qw(projf_read projf_write projf_validate projent_parse
360Sstevel@tonic-gate		 projent_parse_name projent_validate_unique_name
370Sstevel@tonic-gate		 projent_parse_projid projent_validate_unique_id
380Sstevel@tonic-gate		 projent_parse_comment
390Sstevel@tonic-gate		 projent_parse_users
400Sstevel@tonic-gate		 projent_parse_groups
410Sstevel@tonic-gate		 projent_parse_attributes
420Sstevel@tonic-gate		 projent_validate projent_validate_projid
430Sstevel@tonic-gate		 projent_values_equal projent_values2string);
440Sstevel@tonic-gate
450Sstevel@tonic-gate@EXPORT_OK = (@constants, @syscalls, @libcalls, @private);
460Sstevel@tonic-gate%EXPORT_TAGS = (CONSTANTS => \@constants, SYSCALLS => \@syscalls,
470Sstevel@tonic-gate    LIBCALLS => \@libcalls, PRIVATE => \@private, ALL => \@EXPORT_OK);
480Sstevel@tonic-gate
490Sstevel@tonic-gateuse base qw(Exporter);
500Sstevel@tonic-gateuse Sun::Solaris::Utils qw(gettext);
510Sstevel@tonic-gate
520Sstevel@tonic-gate#
530Sstevel@tonic-gate# Set up default rules for validating rctls.
540Sstevel@tonic-gate# These rules are not global-flag specific, but instead
550Sstevel@tonic-gate# are the total set of allowable values on all rctls.
560Sstevel@tonic-gate#
570Sstevel@tonic-gateuse Config;
580Sstevel@tonic-gateour $MaxNum = &RCTL_MAX_VALUE;
590Sstevel@tonic-gateour %RctlRules;
600Sstevel@tonic-gate
610Sstevel@tonic-gatemy %rules;
620Sstevel@tonic-gateour %SigNo;
630Sstevel@tonic-gatemy $j;
640Sstevel@tonic-gatemy $name;
650Sstevel@tonic-gateforeach $name (split(' ', $Config{sig_name})) {
660Sstevel@tonic-gate	$SigNo{$name} = $j;
670Sstevel@tonic-gate	$j++;
680Sstevel@tonic-gate}
690Sstevel@tonic-gate%rules = (
700Sstevel@tonic-gate    'privs' 	=> [ qw(basic privileged priv) ],
710Sstevel@tonic-gate    'actions'	=> [ qw(none deny sig) ],
720Sstevel@tonic-gate    'signals'	=> [ qw(ABRT XRES HUP STOP TERM KILL XFSZ XCPU),
730Sstevel@tonic-gate		     $SigNo{'ABRT'},
740Sstevel@tonic-gate		     $SigNo{'XRES'},
750Sstevel@tonic-gate		     $SigNo{'HUP'},
760Sstevel@tonic-gate		     $SigNo{'STOP'},
770Sstevel@tonic-gate		     $SigNo{'TERM'},
780Sstevel@tonic-gate		     $SigNo{'KILL'},
790Sstevel@tonic-gate		     $SigNo{'XFSZ'},
800Sstevel@tonic-gate		     $SigNo{'XCPU'} ],
810Sstevel@tonic-gate    'max'	=> $MaxNum
820Sstevel@tonic-gate);
830Sstevel@tonic-gate
840Sstevel@tonic-gate$RctlRules{'__DEFAULT__'} = \%rules;
850Sstevel@tonic-gate
860Sstevel@tonic-gate#
870Sstevel@tonic-gate# projf_combine_errors(errorA, errorlistB)
880Sstevel@tonic-gate#
890Sstevel@tonic-gate# Concatenates a single error with a list of errors.  Each error in the new
900Sstevel@tonic-gate# list will have a status matching the status of errorA.
910Sstevel@tonic-gate#
920Sstevel@tonic-gate# Example:
930Sstevel@tonic-gate#
940Sstevel@tonic-gate#	projf_combine_errors(
950Sstevel@tonic-gate#	    [ 5, "Error on line %d, 10 ],
960Sstevel@tonic-gate#	    [ [ 3, "Invalid Value %s", "foo" ],
970Sstevel@tonic-gate#	      [ 6, "Duplicate Value %s", "bar" ]
980Sstevel@tonic-gate#	    ]);
990Sstevel@tonic-gate#
1000Sstevel@tonic-gate# would return the list ref:
1010Sstevel@tonic-gate#
1020Sstevel@tonic-gate#	[ [ 5, "Error on line %d: Invalid Value %s", 10, "foo" ],
1030Sstevel@tonic-gate#	  [ 5, "Error on line %d: Duplicate Value %s", 10, "bar" ]
1040Sstevel@tonic-gate#	]
1050Sstevel@tonic-gate#
1060Sstevel@tonic-gate# This function is used when a fuction wants to add more information to
1070Sstevel@tonic-gate# a list of errors returned by another function.
1080Sstevel@tonic-gate#
1090Sstevel@tonic-gatesub projf_combine_errors
1100Sstevel@tonic-gate{
1110Sstevel@tonic-gate
1120Sstevel@tonic-gate	my ($error1, $errorlist)  = @_;
1130Sstevel@tonic-gate	my $error2;
1140Sstevel@tonic-gate
1150Sstevel@tonic-gate	my $newerror;
1160Sstevel@tonic-gate	my @newerrorlist;
1170Sstevel@tonic-gate
1180Sstevel@tonic-gate	my ($err1, $fmt1, @args1);
1190Sstevel@tonic-gate	my ($err2, $fmt2, @args2);
1200Sstevel@tonic-gate
1210Sstevel@tonic-gate	($err1, $fmt1, @args1) = @$error1;
1220Sstevel@tonic-gate	foreach $error2 (@$errorlist) {
1230Sstevel@tonic-gate
1240Sstevel@tonic-gate		($err2, $fmt2, @args2) = @$error2;
1250Sstevel@tonic-gate		$newerror = [ $err1, $fmt1 . ', ' . $fmt2, @args1, @args2];
1260Sstevel@tonic-gate		push(@newerrorlist, $newerror);
1270Sstevel@tonic-gate	}
1280Sstevel@tonic-gate	return (\@newerrorlist);
1290Sstevel@tonic-gate}
1300Sstevel@tonic-gate
1310Sstevel@tonic-gate#
1320Sstevel@tonic-gate# projf_read(filename, flags)
1330Sstevel@tonic-gate#
1340Sstevel@tonic-gate# Reads and parses a project(4) file, and returns a list of projent hashes.
1350Sstevel@tonic-gate#
1360Sstevel@tonic-gate# Inputs:
1370Sstevel@tonic-gate#	filename - file to read
1380Sstevel@tonic-gate#	flags	 - hash ref of flags
1390Sstevel@tonic-gate#
1400Sstevel@tonic-gate# If flags contains key "validate", the project file entries will also be
1410Sstevel@tonic-gate# validated for run-time correctness  If so, the flags ref is forwarded to
1420Sstevel@tonic-gate# projf_validate().
1430Sstevel@tonic-gate#
1440Sstevel@tonic-gate# Return Value:
1450Sstevel@tonic-gate#
1460Sstevel@tonic-gate# Returns a ref to a list of projent hashes.  See projent_parse() for a
1470Sstevel@tonic-gate# description of a projent hash.
1480Sstevel@tonic-gate#
1490Sstevel@tonic-gatesub projf_read
1500Sstevel@tonic-gate{
1510Sstevel@tonic-gate
1520Sstevel@tonic-gate	my ($fh, $flags) = @_;
1530Sstevel@tonic-gate	my @projents;
1540Sstevel@tonic-gate	my $projent;
1550Sstevel@tonic-gate	my $linenum = 0;
1560Sstevel@tonic-gate	my ($projname, $projid, $comment, $users, $groups, $attributes);
1570Sstevel@tonic-gate	my ($ret, $ref);
1580Sstevel@tonic-gate	my @errs;
1590Sstevel@tonic-gate
1600Sstevel@tonic-gate	my ($line, $origline, $next, @projf);
1610Sstevel@tonic-gate	while (defined($line = <$fh>)) {
1620Sstevel@tonic-gate
1630Sstevel@tonic-gate		$linenum++;
1640Sstevel@tonic-gate		$origline = $line;
1650Sstevel@tonic-gate
1660Sstevel@tonic-gate		# Remove any line continuations and trailing newline.
1670Sstevel@tonic-gate		$line =~ s/\\\n//g;
1680Sstevel@tonic-gate		chomp($line);
1690Sstevel@tonic-gate
1700Sstevel@tonic-gate
1710Sstevel@tonic-gate		if (length($line) > (&PROJECT_BUFSZ - 2)) {
1720Sstevel@tonic-gate			push(@errs,
1730Sstevel@tonic-gate			    [5,
1740Sstevel@tonic-gate			      gettext('Parse error on line %d, line too long'),
1750Sstevel@tonic-gate			    $linenum]);
1760Sstevel@tonic-gate
1770Sstevel@tonic-gate		}
1780Sstevel@tonic-gate
1790Sstevel@tonic-gate		($ret, $ref) = projent_parse($line, {});
1800Sstevel@tonic-gate		if ($ret != 0) {
1810Sstevel@tonic-gate			$ref = projf_combine_errors(
1820Sstevel@tonic-gate			    [5, gettext('Parse error on line %d'), $linenum],
1830Sstevel@tonic-gate			    $ref);
1840Sstevel@tonic-gate			push(@errs, @$ref);
1850Sstevel@tonic-gate			next;
1860Sstevel@tonic-gate		}
1870Sstevel@tonic-gate
1880Sstevel@tonic-gate		$projent = $ref;
1890Sstevel@tonic-gate
1900Sstevel@tonic-gate		#
1910Sstevel@tonic-gate		# Cache original line to save original format if it is
1920Sstevel@tonic-gate		# not changed.
1930Sstevel@tonic-gate		#
1940Sstevel@tonic-gate		$projent->{'line'} = $origline;
1950Sstevel@tonic-gate		$projent->{'modified'} = 'false';
1960Sstevel@tonic-gate		$projent->{'linenum'} = $linenum;
1970Sstevel@tonic-gate
1980Sstevel@tonic-gate		push(@projents, $projent);
1990Sstevel@tonic-gate	}
2000Sstevel@tonic-gate
2010Sstevel@tonic-gate	if (defined($flags->{'validate'}) && ($flags->{'validate'} eq 'true')) {
2020Sstevel@tonic-gate		($ret, $ref) = projf_validate(\@projents, $flags);
2030Sstevel@tonic-gate		if ($ret != 0) {
2040Sstevel@tonic-gate			push(@errs, @$ref);
2050Sstevel@tonic-gate		}
2060Sstevel@tonic-gate	}
2070Sstevel@tonic-gate
2080Sstevel@tonic-gate	if (@errs) {
2090Sstevel@tonic-gate		return (1, \@errs);
2100Sstevel@tonic-gate
2110Sstevel@tonic-gate	} else {
2120Sstevel@tonic-gate		return (0, \@projents);
2130Sstevel@tonic-gate	}
2140Sstevel@tonic-gate}
2150Sstevel@tonic-gate
2160Sstevel@tonic-gate#
2170Sstevel@tonic-gate# projf_write(filehandle, projent list)
2180Sstevel@tonic-gate#
2190Sstevel@tonic-gate# Write a list of projent hashes to a file handle.
2200Sstevel@tonic-gate# projent's with key "modified" => false will be
2210Sstevel@tonic-gate# written using the "line" key.  projent's with
2220Sstevel@tonic-gate# key "modified" => "true" will be written by
2230Sstevel@tonic-gate# constructing a new line based on their "name"
2240Sstevel@tonic-gate# "projid", "comment", "userlist", "grouplist"
2250Sstevel@tonic-gate# and "attributelist" keys.
2260Sstevel@tonic-gate#
2270Sstevel@tonic-gatesub projf_write
2280Sstevel@tonic-gate{
2290Sstevel@tonic-gate	my ($fh, $projents) = @_;
2300Sstevel@tonic-gate	my $projent;
2310Sstevel@tonic-gate	my $string;
2320Sstevel@tonic-gate
2330Sstevel@tonic-gate	foreach $projent (@$projents) {
2340Sstevel@tonic-gate
2350Sstevel@tonic-gate		if ($projent->{'modified'} eq 'false') {
2360Sstevel@tonic-gate			$string = $projent->{'line'};
2370Sstevel@tonic-gate		} else {
2380Sstevel@tonic-gate			$string = projent_2string($projent) . "\n";
2390Sstevel@tonic-gate		}
2400Sstevel@tonic-gate		print $fh "$string";
2410Sstevel@tonic-gate	}
2420Sstevel@tonic-gate}
2430Sstevel@tonic-gate
2440Sstevel@tonic-gate#
2450Sstevel@tonic-gate# projent_parse(line)
2460Sstevel@tonic-gate#
2470Sstevel@tonic-gate# Functions for parsing the project file lines into projent hashes.
2480Sstevel@tonic-gate#
2490Sstevel@tonic-gate# Returns a number and a ref, one of:
2500Sstevel@tonic-gate#
2510Sstevel@tonic-gate# 	(0, ref to projent hash)
2520Sstevel@tonic-gate#	(non-zero, ref to list of errors)
2530Sstevel@tonic-gate#
2540Sstevel@tonic-gate#	Flag can be:
2550Sstevel@tonic-gate#		allowspaces: allow spaces between user and group names.
2560Sstevel@tonic-gate#		allowunits : allow units (K, M, etc), on rctl values.
2570Sstevel@tonic-gate#
2580Sstevel@tonic-gate# A projent hash contains the keys:
2590Sstevel@tonic-gate#
2600Sstevel@tonic-gate#	"name"		- string name of project
2610Sstevel@tonic-gate#	"projid"	- numeric id of project
2620Sstevel@tonic-gate#	"comment"	- comment string
2630Sstevel@tonic-gate#	"users"		- , seperated user list string
2640Sstevel@tonic-gate#	"userlist"	- list ref to list of user name strings
2650Sstevel@tonic-gate#	"groups"	- , seperated group list string
2660Sstevel@tonic-gate#	"grouplist" 	- list ref to liset of group name strings
2670Sstevel@tonic-gate#	"attributes"	- ; seperated attribute list string
2680Sstevel@tonic-gate#	"attributelist" - list ref to list of attribute refs
2690Sstevel@tonic-gate#		          (see projent_parse_attributes() for attribute ref)
2700Sstevel@tonic-gate#
2710Sstevel@tonic-gatesub projent_parse
2720Sstevel@tonic-gate{
2730Sstevel@tonic-gate
2740Sstevel@tonic-gate	my ($line, $flags) = @_;
2750Sstevel@tonic-gate	my $projent = {};
2760Sstevel@tonic-gate	my ($ret, $ref);
2770Sstevel@tonic-gate	my @errs;
2780Sstevel@tonic-gate	my ($projname, $projid, $comment, $users, $groups, $attributes);
2790Sstevel@tonic-gate
2800Sstevel@tonic-gate	#
2810Sstevel@tonic-gate	# Split fields of project line.  split() is not used because
2820Sstevel@tonic-gate	# we must enforce that there are 6 fields.
2830Sstevel@tonic-gate	#
2840Sstevel@tonic-gate	($projname, $projid, $comment, $users, $groups, $attributes) =
2850Sstevel@tonic-gate	    $line =~
2860Sstevel@tonic-gate	    /^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*)$/;
2870Sstevel@tonic-gate
2880Sstevel@tonic-gate	# If there is not a complete match, nothing will be defined;
2890Sstevel@tonic-gate	if (!defined($projname)) {
2900Sstevel@tonic-gate		push(@errs, [5, gettext(
2910Sstevel@tonic-gate		    'Incorrect number of fields.  Should have 5 ":"\'s.')]);
2920Sstevel@tonic-gate
2930Sstevel@tonic-gate		# Get as many fields as we can.
2940Sstevel@tonic-gate		($projname, $projid, $comment, $users, $groups, $attributes) =
2950Sstevel@tonic-gate		    split(/:/, $line);
2960Sstevel@tonic-gate	}
2970Sstevel@tonic-gate
2980Sstevel@tonic-gate	if (defined($projname)) {
2990Sstevel@tonic-gate		$projent->{'name'} = $projname;
3000Sstevel@tonic-gate		($ret, $ref) = projent_parse_name($projname);
3010Sstevel@tonic-gate		if ($ret != 0) {
3020Sstevel@tonic-gate			push(@errs, @$ref);
3030Sstevel@tonic-gate		}
3040Sstevel@tonic-gate	}
3050Sstevel@tonic-gate	if (defined($projid)) {
3060Sstevel@tonic-gate		$projent->{'projid'} = $projid;
3070Sstevel@tonic-gate		($ret, $ref) = projent_parse_projid($projid);
3080Sstevel@tonic-gate		if ($ret != 0) {
3090Sstevel@tonic-gate			push(@errs, @$ref);
3100Sstevel@tonic-gate		}
3110Sstevel@tonic-gate	}
3120Sstevel@tonic-gate	if (defined($comment)) {
3130Sstevel@tonic-gate		$projent->{'comment'} = $comment;
3140Sstevel@tonic-gate		($ret, $ref) = projent_parse_comment($comment);
3150Sstevel@tonic-gate		if ($ret != 0) {
3160Sstevel@tonic-gate			push(@errs, @$ref);
3170Sstevel@tonic-gate		}
3180Sstevel@tonic-gate	}
3190Sstevel@tonic-gate	if (defined($users)) {
3200Sstevel@tonic-gate		$projent->{'users'} = $users;
3210Sstevel@tonic-gate		($ret, $ref) = projent_parse_users($users, $flags);
3220Sstevel@tonic-gate		if ($ret != 0) {
3230Sstevel@tonic-gate			push(@errs, @$ref);
3240Sstevel@tonic-gate		} else {
3250Sstevel@tonic-gate			$projent->{'userlist'} = $ref;
3260Sstevel@tonic-gate		}
3270Sstevel@tonic-gate	}
3280Sstevel@tonic-gate	if (defined($groups)) {
3290Sstevel@tonic-gate		$projent->{'groups'} = $groups;
3300Sstevel@tonic-gate		($ret, $ref) = projent_parse_groups($groups, $flags);
3310Sstevel@tonic-gate		if ($ret != 0) {
3320Sstevel@tonic-gate			push(@errs, @$ref);
3330Sstevel@tonic-gate		} else {
3340Sstevel@tonic-gate			$projent->{'grouplist'} = $ref;
3350Sstevel@tonic-gate		}
3360Sstevel@tonic-gate	}
3370Sstevel@tonic-gate	if (defined($attributes)) {
3380Sstevel@tonic-gate		$projent->{'attributes'} = $attributes;
3390Sstevel@tonic-gate		($ret, $ref) = projent_parse_attributes($attributes, $flags);
3400Sstevel@tonic-gate		if ($ret != 0) {
3410Sstevel@tonic-gate			push(@errs, @$ref);
3420Sstevel@tonic-gate		} else {
3430Sstevel@tonic-gate			$projent->{'attributelist'} = $ref;
3440Sstevel@tonic-gate		}
3450Sstevel@tonic-gate	}
3460Sstevel@tonic-gate
3470Sstevel@tonic-gate	if (@errs) {
3480Sstevel@tonic-gate		return (1, \@errs);
3490Sstevel@tonic-gate
3500Sstevel@tonic-gate	} else {
3510Sstevel@tonic-gate		return (0, $projent);
3520Sstevel@tonic-gate	}
3530Sstevel@tonic-gate}
3540Sstevel@tonic-gate
3550Sstevel@tonic-gate#
3560Sstevel@tonic-gate# Project name syntax checking.
3570Sstevel@tonic-gate#
3580Sstevel@tonic-gatesub projent_parse_name
3590Sstevel@tonic-gate{
3600Sstevel@tonic-gate	my @err;
3610Sstevel@tonic-gate	my ($projname) = @_;
3620Sstevel@tonic-gate
3630Sstevel@tonic-gate	if (!($projname =~ /^[[:alpha:]][[:alnum:]_.-]*$/)) {
3640Sstevel@tonic-gate		push(@err, ([3, gettext(
3650Sstevel@tonic-gate		    'Invalid project name "%s", contains invalid characters'),
3660Sstevel@tonic-gate		    $projname]));
3670Sstevel@tonic-gate		return (1, \@err);
3680Sstevel@tonic-gate	}
3690Sstevel@tonic-gate	if (length($projname) > &PROJNAME_MAX) {
3700Sstevel@tonic-gate		push(@err, ([3, gettext(
3710Sstevel@tonic-gate		    'Invalid project name "%s", name too long'),
3720Sstevel@tonic-gate		    $projname]));
3730Sstevel@tonic-gate		return (1, \@err);
3740Sstevel@tonic-gate	}
3750Sstevel@tonic-gate	return (0, $projname);
3760Sstevel@tonic-gate}
3770Sstevel@tonic-gate
3780Sstevel@tonic-gate#
3790Sstevel@tonic-gate# Projid syntax checking.
3800Sstevel@tonic-gate#
3810Sstevel@tonic-gatesub projent_parse_projid
3820Sstevel@tonic-gate{
3830Sstevel@tonic-gate	my @err;
3840Sstevel@tonic-gate	my ($projid) = @_;
3850Sstevel@tonic-gate
3860Sstevel@tonic-gate	# verify projid is a positive number, and less than UID_MAX
3870Sstevel@tonic-gate	if (!($projid =~ /^\d+$/)) {
3880Sstevel@tonic-gate		push(@err, [3, gettext('Invalid projid "%s"'),
3890Sstevel@tonic-gate		    $projid]);
3900Sstevel@tonic-gate		return (1, \@err);
3910Sstevel@tonic-gate
3920Sstevel@tonic-gate	} elsif ($projid > POSIX::INT_MAX) {
3930Sstevel@tonic-gate		push(@err, [3, gettext('Invalid projid "%s": must be <= '.
3940Sstevel@tonic-gate		    POSIX::INT_MAX),
3950Sstevel@tonic-gate		    $projid]);
3960Sstevel@tonic-gate		return (1, \@err);
3970Sstevel@tonic-gate
3980Sstevel@tonic-gate	} else {
3990Sstevel@tonic-gate		return (0, $projid);
4000Sstevel@tonic-gate	}
4010Sstevel@tonic-gate}
4020Sstevel@tonic-gate
4030Sstevel@tonic-gate#
4040Sstevel@tonic-gate# Project comment syntax checking.
4050Sstevel@tonic-gate#
4060Sstevel@tonic-gatesub projent_parse_comment
4070Sstevel@tonic-gate{
4080Sstevel@tonic-gate	my ($comment) = @_;
4090Sstevel@tonic-gate
4100Sstevel@tonic-gate	# no restrictions on comments
4110Sstevel@tonic-gate	return (0, $comment);
4120Sstevel@tonic-gate}
4130Sstevel@tonic-gate
4140Sstevel@tonic-gate#
4150Sstevel@tonic-gate# projent_parse_users(string, flags)
4160Sstevel@tonic-gate#
4170Sstevel@tonic-gate# Parses "," seperated list of users, and returns list ref to a list of
4180Sstevel@tonic-gate# user names.  If flags contains key "allowspaces", then spaces are
4190Sstevel@tonic-gate# allowed between user names and ","'s.
4200Sstevel@tonic-gate#
4210Sstevel@tonic-gatesub projent_parse_users
4220Sstevel@tonic-gate{
4230Sstevel@tonic-gate	my ($users, $flags) = @_;
4240Sstevel@tonic-gate	my @err;
4250Sstevel@tonic-gate	my $user;
4260Sstevel@tonic-gate	my $pattern;
4270Sstevel@tonic-gate	my @userlist;
4280Sstevel@tonic-gate
4290Sstevel@tonic-gate	if (exists($flags->{'allowspaces'})) {
4300Sstevel@tonic-gate		$pattern = '\s*,\s*';
4310Sstevel@tonic-gate	} else {
4320Sstevel@tonic-gate		$pattern = ',';
4330Sstevel@tonic-gate	}
4340Sstevel@tonic-gate	@userlist = split(/$pattern/, $users);
4350Sstevel@tonic-gate
4360Sstevel@tonic-gate	# Return empty list if there are no users.
4370Sstevel@tonic-gate	if (!(@userlist)) {
4380Sstevel@tonic-gate		return (0, \@userlist);
4390Sstevel@tonic-gate	}
4400Sstevel@tonic-gate
4410Sstevel@tonic-gate	# Verify each user name is the correct format for a valid user name.
4420Sstevel@tonic-gate	foreach $user (@userlist) {
4430Sstevel@tonic-gate
4440Sstevel@tonic-gate		# Allow for wildcards.
4450Sstevel@tonic-gate		if ($user eq '*' || $user eq '!*') {
4460Sstevel@tonic-gate			next;
4470Sstevel@tonic-gate		}
4480Sstevel@tonic-gate
4490Sstevel@tonic-gate		# Allow for ! operator, usernames must begin with alpha-num,
4500Sstevel@tonic-gate		# and contain alpha-num, '_', digits, '.', or '-'.
4510Sstevel@tonic-gate		if (!($user =~ /^!?[[:alpha:]][[:alnum:]_.-]*$/)) {
4520Sstevel@tonic-gate			push(@err, [3, gettext('Invalid user name "%s"'),
4530Sstevel@tonic-gate			    $user]);
4540Sstevel@tonic-gate			next;
4550Sstevel@tonic-gate		}
4560Sstevel@tonic-gate	}
4570Sstevel@tonic-gate	if (@err) {
4580Sstevel@tonic-gate		return (1,\ @err);
4590Sstevel@tonic-gate	} else {
4600Sstevel@tonic-gate		return (0, \@userlist);
4610Sstevel@tonic-gate	}
4620Sstevel@tonic-gate}
4630Sstevel@tonic-gate
4640Sstevel@tonic-gate#
4650Sstevel@tonic-gate# projent_parse_groups(string, flags)
4660Sstevel@tonic-gate#
4670Sstevel@tonic-gate# Parses "," seperated list of groups, and returns list ref to a list of
4680Sstevel@tonic-gate# groups names.  If flags contains key "allowspaces", then spaces are
4690Sstevel@tonic-gate# allowed between group names and ","'s.
4700Sstevel@tonic-gate#
4710Sstevel@tonic-gatesub projent_parse_groups
4720Sstevel@tonic-gate{
4730Sstevel@tonic-gate	my ($groups, $flags) = @_;
4740Sstevel@tonic-gate	my @err;
4750Sstevel@tonic-gate	my $group;
4760Sstevel@tonic-gate	my $pattern;
4770Sstevel@tonic-gate
4780Sstevel@tonic-gate	my @grouplist;
4790Sstevel@tonic-gate
4800Sstevel@tonic-gate	if (exists($flags->{'allowspaces'})) {
4810Sstevel@tonic-gate		$pattern = '\s*,\s*';
4820Sstevel@tonic-gate	} else {
4830Sstevel@tonic-gate		$pattern = ',';
4840Sstevel@tonic-gate	}
4850Sstevel@tonic-gate	@grouplist = split(/$pattern/, $groups);
4860Sstevel@tonic-gate
4870Sstevel@tonic-gate	# Return empty list if there are no groups.
4880Sstevel@tonic-gate	if (!(@grouplist)) {
4890Sstevel@tonic-gate		return (0, \@grouplist);
4900Sstevel@tonic-gate	}
4910Sstevel@tonic-gate
4920Sstevel@tonic-gate	# Verify each group is the correct format for a valid group name.
4930Sstevel@tonic-gate	foreach $group (@grouplist) {
4940Sstevel@tonic-gate
4950Sstevel@tonic-gate		# Allow for wildcards.
4960Sstevel@tonic-gate		if ($group eq '*' || $group eq '!*') {
4970Sstevel@tonic-gate			next;
4980Sstevel@tonic-gate		}
4990Sstevel@tonic-gate
5000Sstevel@tonic-gate		# Allow for ! operator, groupnames can contain only alpha
5010Sstevel@tonic-gate		# characters and digits.
5020Sstevel@tonic-gate		if (!($group =~ /^!?[[:alnum:]]+$/)) {
5030Sstevel@tonic-gate			push(@err, [3, gettext('Invalid group name "%s"'),
5040Sstevel@tonic-gate			    $group]);
5050Sstevel@tonic-gate			next;
5060Sstevel@tonic-gate		}
5070Sstevel@tonic-gate	}
5080Sstevel@tonic-gate
5090Sstevel@tonic-gate	if (@err) {
5100Sstevel@tonic-gate		return (1,\ @err);
5110Sstevel@tonic-gate	} else {
5120Sstevel@tonic-gate		return (0, \@grouplist);
5130Sstevel@tonic-gate	}
5140Sstevel@tonic-gate}
5150Sstevel@tonic-gate
5160Sstevel@tonic-gate#
5170Sstevel@tonic-gate# projent_tokenize_attribute_values(values)
5180Sstevel@tonic-gate#
5190Sstevel@tonic-gate# Values is the right hand side of a name=values attribute/values pair.
5200Sstevel@tonic-gate# This function splits the values string into a list of tokens.  Tokens are
5210Sstevel@tonic-gate# valid string values and the characters ( ) ,
5220Sstevel@tonic-gate#
5230Sstevel@tonic-gatesub projent_tokenize_attribute_values
5240Sstevel@tonic-gate{
5250Sstevel@tonic-gate	#
5260Sstevel@tonic-gate	# This seperates the attribute string into higher level tokens
5270Sstevel@tonic-gate	# for parsing.
5280Sstevel@tonic-gate	#
5290Sstevel@tonic-gate	my $prev;
5300Sstevel@tonic-gate	my $cur;
5310Sstevel@tonic-gate	my $next;
5320Sstevel@tonic-gate	my $token;
5330Sstevel@tonic-gate	my @tokens;
5340Sstevel@tonic-gate	my @newtokens;
5350Sstevel@tonic-gate	my @err;
5360Sstevel@tonic-gate
5370Sstevel@tonic-gate	# Seperate tokens delimited by "(", ")", and ",".
5380Sstevel@tonic-gate	@tokens = split(/([,()])/, $_[0], -1);
5390Sstevel@tonic-gate
5400Sstevel@tonic-gate	# Get rid of blanks
5410Sstevel@tonic-gate	@newtokens = grep($_ ne '', @tokens);
5420Sstevel@tonic-gate
5430Sstevel@tonic-gate	foreach $token (@newtokens) {
5440Sstevel@tonic-gate		if (!($token =~ /^[(),]$/ ||
5450Sstevel@tonic-gate		      $token =~ /^[[:alnum:]_.\/=+-]*$/)) {
5460Sstevel@tonic-gate			push(@err, [3, gettext(
5470Sstevel@tonic-gate			    'Invalid Character at or near "%s"'), $token]);
5480Sstevel@tonic-gate		}
5490Sstevel@tonic-gate	}
5500Sstevel@tonic-gate	if (@err) {
5510Sstevel@tonic-gate		return (1, \@err);
5520Sstevel@tonic-gate	} else {
5530Sstevel@tonic-gate		return (0, \@newtokens);
5540Sstevel@tonic-gate	}
5550Sstevel@tonic-gate}
5560Sstevel@tonic-gate
5570Sstevel@tonic-gate#
5580Sstevel@tonic-gate# projent_parse_attribute_values(values)
5590Sstevel@tonic-gate#
5600Sstevel@tonic-gate# Values is the right hand side of a name=values attribute/values pair.
5610Sstevel@tonic-gate# This function parses the values string into a list of values.  Each value
5620Sstevel@tonic-gate# can be either a scalar value, or a ref to another list of values.
5630Sstevel@tonic-gate# A ref to the list of values is returned.
5640Sstevel@tonic-gate#
5650Sstevel@tonic-gatesub projent_parse_attribute_values
5660Sstevel@tonic-gate{
5670Sstevel@tonic-gate	#
5680Sstevel@tonic-gate	# For some reason attribute values can be lists of values and
5690Sstevel@tonic-gate	# sublists, which are scoped using ()'s.  All values and sublists
5700Sstevel@tonic-gate	# are delimited by ","'s.  Empty values are lists are permitted.
5710Sstevel@tonic-gate
5720Sstevel@tonic-gate	# This function returns a reference to a list of values, each of
5730Sstevel@tonic-gate	# which can be a scalar value, or a reference to a sublist.  Sublists
5740Sstevel@tonic-gate	# can contain both scalar values and references to furthur sublists.
5750Sstevel@tonic-gate	#
5760Sstevel@tonic-gate	my ($values) = @_;
5770Sstevel@tonic-gate	my $tokens;
5780Sstevel@tonic-gate	my @usedtokens;
5790Sstevel@tonic-gate	my $token;
5800Sstevel@tonic-gate	my $prev = '';
5810Sstevel@tonic-gate	my $parendepth = 0;
5820Sstevel@tonic-gate	my @valuestack;
5830Sstevel@tonic-gate	my @err;
5840Sstevel@tonic-gate	my ($ret, $ref);
5850Sstevel@tonic-gate	my $line;
5860Sstevel@tonic-gate
5870Sstevel@tonic-gate	push (@valuestack, []);
5880Sstevel@tonic-gate
5890Sstevel@tonic-gate	($ret, $ref) = projent_tokenize_attribute_values($values);
5900Sstevel@tonic-gate	if ($ret != 0) {
5910Sstevel@tonic-gate		return ($ret, $ref);
5920Sstevel@tonic-gate	}
5930Sstevel@tonic-gate	$tokens = $ref;
5940Sstevel@tonic-gate
5950Sstevel@tonic-gate	foreach $token (@$tokens) {
5960Sstevel@tonic-gate
5970Sstevel@tonic-gate		push(@usedtokens, $token);
5980Sstevel@tonic-gate
5990Sstevel@tonic-gate		if ($token eq ',') {
6000Sstevel@tonic-gate
6010Sstevel@tonic-gate			if ($prev eq ',' || $prev eq '(' ||
6020Sstevel@tonic-gate			    $prev eq '') {
6030Sstevel@tonic-gate				push(@{$valuestack[$#valuestack]}, '');
6040Sstevel@tonic-gate			}
6050Sstevel@tonic-gate			$prev = ',';
6060Sstevel@tonic-gate			next;
6070Sstevel@tonic-gate		}
6080Sstevel@tonic-gate		if ($token eq '(') {
6090Sstevel@tonic-gate
6100Sstevel@tonic-gate			if (!($prev eq '(' || $prev eq ',' ||
6110Sstevel@tonic-gate			      $prev eq '')) {
6120Sstevel@tonic-gate
6130Sstevel@tonic-gate				$line = join('', @usedtokens);
6140Sstevel@tonic-gate				push(@err, [3, gettext(
6150Sstevel@tonic-gate				    '"%s" <- "(" unexpected'),
6160Sstevel@tonic-gate				    $line]);
6170Sstevel@tonic-gate
6180Sstevel@tonic-gate				return (1, \@err);
6190Sstevel@tonic-gate			}
6200Sstevel@tonic-gate
6210Sstevel@tonic-gate			$parendepth++;
6220Sstevel@tonic-gate			my $arrayref = [];
6230Sstevel@tonic-gate			push(@{$valuestack[$#valuestack]}, $arrayref);
6240Sstevel@tonic-gate			push(@valuestack, $arrayref);
6250Sstevel@tonic-gate
6260Sstevel@tonic-gate			$prev = '(';
6270Sstevel@tonic-gate			next;
6280Sstevel@tonic-gate		}
6290Sstevel@tonic-gate		if ($token eq ')') {
6300Sstevel@tonic-gate
6310Sstevel@tonic-gate			if ($parendepth <= 0) {
6320Sstevel@tonic-gate
6330Sstevel@tonic-gate				$line = join('', @usedtokens);
6340Sstevel@tonic-gate				push(@err, [3, gettext(
6350Sstevel@tonic-gate				    '"%s" <- ")" unexpected'),
6360Sstevel@tonic-gate				    $line]);
6370Sstevel@tonic-gate
6380Sstevel@tonic-gate				return (1, \@err);
6390Sstevel@tonic-gate			}
6400Sstevel@tonic-gate
6410Sstevel@tonic-gate			if ($prev eq ',' || $prev eq '(') {
6420Sstevel@tonic-gate				push(@{$valuestack[$#valuestack]}, '');
6430Sstevel@tonic-gate			}
6440Sstevel@tonic-gate			$parendepth--;
6450Sstevel@tonic-gate			pop @valuestack;
6460Sstevel@tonic-gate
6470Sstevel@tonic-gate			$prev = ')';
6480Sstevel@tonic-gate			next;
6490Sstevel@tonic-gate		}
6500Sstevel@tonic-gate
6510Sstevel@tonic-gate		if (!($prev eq ',' || $prev eq '(' || $prev eq '')) {
6520Sstevel@tonic-gate			$line = join('', @usedtokens);
6530Sstevel@tonic-gate			push(@err, [3, gettext(
6540Sstevel@tonic-gate			    '"%s" <- "%s" unexpected'),
6550Sstevel@tonic-gate			    $line, $token]);
6560Sstevel@tonic-gate
6570Sstevel@tonic-gate			return (1, \@err);
6580Sstevel@tonic-gate		}
6590Sstevel@tonic-gate
6600Sstevel@tonic-gate		push(@{$valuestack[$#valuestack]}, $token);
6610Sstevel@tonic-gate		$prev = $token;
6620Sstevel@tonic-gate		next;
6630Sstevel@tonic-gate	}
6640Sstevel@tonic-gate
6650Sstevel@tonic-gate	if ($parendepth != 0) {
6660Sstevel@tonic-gate		push(@err, [3, gettext(
6670Sstevel@tonic-gate		    '"%s" <- ")" missing'),
6680Sstevel@tonic-gate		    $values]);
6690Sstevel@tonic-gate		return (1, \@err);
6700Sstevel@tonic-gate	}
6710Sstevel@tonic-gate
6720Sstevel@tonic-gate	if ($prev eq ',' || $prev eq '') {
6730Sstevel@tonic-gate		push(@{$valuestack[$#valuestack]}, '');
6740Sstevel@tonic-gate	}
6750Sstevel@tonic-gate
6760Sstevel@tonic-gate	return (0, $valuestack[0]);
6770Sstevel@tonic-gate}
6780Sstevel@tonic-gate
6790Sstevel@tonic-gate#
6800Sstevel@tonic-gate# projent_parse_attribute("name=values", $flags)
6810Sstevel@tonic-gate#
6820Sstevel@tonic-gate# $flags is a hash ref.
6830Sstevel@tonic-gate# Valid flags keys:
6840Sstevel@tonic-gate#	'allowunits' - allows numeric values to be scaled on certain attributes
6850Sstevel@tonic-gate#
6860Sstevel@tonic-gate# Returns a hash ref with keys:
6870Sstevel@tonic-gate#
6880Sstevel@tonic-gate#	"name" 		- name of attribute
6890Sstevel@tonic-gate#	"values"	- ref to list of values.
6900Sstevel@tonic-gate#			  Each value can be a scalar value, or a ref to
6910Sstevel@tonic-gate#			  a sub-list of values.
6920Sstevel@tonic-gate#
6930Sstevel@tonic-gatesub projent_parse_attribute
6940Sstevel@tonic-gate{
6950Sstevel@tonic-gate	my ($string, $flags) = @_;
6960Sstevel@tonic-gate	my $attribute = {};
6970Sstevel@tonic-gate	my ($name, $stock, $values);
6980Sstevel@tonic-gate	my ($ret, $ref);
6990Sstevel@tonic-gate	my @err;
7000Sstevel@tonic-gate	my $scale;
7010Sstevel@tonic-gate	my $num;
7020Sstevel@tonic-gate	my $modifier;
7030Sstevel@tonic-gate	my $unit;
7040Sstevel@tonic-gate	my $tuple;
7050Sstevel@tonic-gate	my $rules;
7060Sstevel@tonic-gate	my $rctlmax;
7070Sstevel@tonic-gate	my $rctlflags;
7080Sstevel@tonic-gate
7090Sstevel@tonic-gate	# pattern for matching stock symbols.
7100Sstevel@tonic-gate	my $stockp = '[[:upper:]]{1,5}(?:.[[:upper:]]{1,5})?,';
7110Sstevel@tonic-gate	# Match attribute with no value.
7120Sstevel@tonic-gate	($name, $stock) = $string =~
7130Sstevel@tonic-gate	    /^(($stockp)?[[:alpha:]][[:alnum:]_.-]*)$/;
7140Sstevel@tonic-gate	if ($name) {
7150Sstevel@tonic-gate		$attribute->{'name'} = $name;
7160Sstevel@tonic-gate		return (0, $attribute);
7170Sstevel@tonic-gate	}
7180Sstevel@tonic-gate
7190Sstevel@tonic-gate	# Match attribute with value list.
7200Sstevel@tonic-gate	($name, $stock, $values) = $string =~
7210Sstevel@tonic-gate	    /^(($stockp)?[[:alpha:]][[:alnum:]_.-]*)=(.*)$/;
7220Sstevel@tonic-gate	if ($name) {
7230Sstevel@tonic-gate		$attribute->{'name'} = $name;
7240Sstevel@tonic-gate
7250Sstevel@tonic-gate		if (!defined($values)) {
7260Sstevel@tonic-gate			$values = '';
7270Sstevel@tonic-gate		}
7280Sstevel@tonic-gate
7290Sstevel@tonic-gate		($ret, $ref) = projent_parse_attribute_values($values);
7300Sstevel@tonic-gate		if ($ret != 0) {
7310Sstevel@tonic-gate			$ref = projf_combine_errors(
7320Sstevel@tonic-gate			    [3,
7330Sstevel@tonic-gate			    gettext('Invalid value on attribute "%s"'),
7340Sstevel@tonic-gate			    $name], $ref);
7350Sstevel@tonic-gate			push(@err, @$ref);
7360Sstevel@tonic-gate			return ($ret, \@err)
7370Sstevel@tonic-gate		}
7380Sstevel@tonic-gate
7390Sstevel@tonic-gate		# Scale attributes than can be scaled.
7400Sstevel@tonic-gate		if (exists($flags->{"allowunits"})) {
7410Sstevel@tonic-gate
7420Sstevel@tonic-gate			if ($name eq 'rcap.max-rss' &&
7430Sstevel@tonic-gate			    defined($ref->[0]) && !ref($ref->[0])) {
7440Sstevel@tonic-gate				$scale = 'bytes';
7450Sstevel@tonic-gate
7460Sstevel@tonic-gate				($num, $modifier, $unit) =
7470Sstevel@tonic-gate				    projent_val2num($ref->[0], $scale);
7480Sstevel@tonic-gate
7490Sstevel@tonic-gate				if (!defined($num)) {
7500Sstevel@tonic-gate
7510Sstevel@tonic-gate					if (defined($unit)) {
7520Sstevel@tonic-gate						push(@err, [3, gettext(
7530Sstevel@tonic-gate						    'rcap.max-rss has invalid '.
7540Sstevel@tonic-gate						    'unit "%s"'), $unit]);
7550Sstevel@tonic-gate					} else {
7560Sstevel@tonic-gate						push(@err, [3, gettext(
7570Sstevel@tonic-gate						    'rcap.max-rss has invalid '.
7580Sstevel@tonic-gate						    'value "%s"'), $ref->[0]]);
7590Sstevel@tonic-gate					}
7600Sstevel@tonic-gate				} elsif ($num eq "OVERFLOW") {
7610Sstevel@tonic-gate					push(@err, [3, gettext( 'rcap.max-rss value '.
7620Sstevel@tonic-gate				            '"%s" exceeds maximum value "%s"'),
7630Sstevel@tonic-gate					    $ref->[0], $MaxNum]);
7640Sstevel@tonic-gate				} else {
7650Sstevel@tonic-gate					$ref->[0] = $num;
7660Sstevel@tonic-gate				}
7670Sstevel@tonic-gate			}
7680Sstevel@tonic-gate			# Check hashed cache of rctl rules.
7690Sstevel@tonic-gate			$rules = $RctlRules{$name};
7700Sstevel@tonic-gate			if (!defined($rules)) {
7710Sstevel@tonic-gate				#
7720Sstevel@tonic-gate				# See if this is an resource control name, if so
7730Sstevel@tonic-gate				# cache rules.
7740Sstevel@tonic-gate				#
7750Sstevel@tonic-gate				($rctlmax, $rctlflags) = rctl_get_info($name);
7760Sstevel@tonic-gate				if (defined($rctlmax)) {
7770Sstevel@tonic-gate					$rules = proj_getrctlrules(
7780Sstevel@tonic-gate					    $rctlmax, $rctlflags);
7790Sstevel@tonic-gate					if (defined($rules)) {
7800Sstevel@tonic-gate						$RctlRules{$name} = $rules;
7810Sstevel@tonic-gate					} else {
7820Sstevel@tonic-gate						$RctlRules{$name} =
7830Sstevel@tonic-gate						    "NOT AN RCTL";
7840Sstevel@tonic-gate					}
7850Sstevel@tonic-gate				}
7860Sstevel@tonic-gate			}
7870Sstevel@tonic-gate
7880Sstevel@tonic-gate			# Scale values if this is an rctl.
7890Sstevel@tonic-gate			if (defined ($rules) && ref($rules)) {
7900Sstevel@tonic-gate				$flags->{'type'} = $rules->{'type'};
7910Sstevel@tonic-gate				foreach $tuple (@$ref) {
7920Sstevel@tonic-gate
7930Sstevel@tonic-gate					# Skip if tuple this is not a list.
7940Sstevel@tonic-gate					if (!ref($tuple)) {
7950Sstevel@tonic-gate						next;
7960Sstevel@tonic-gate					}
7970Sstevel@tonic-gate					# Skip if second element is not scalar.
7980Sstevel@tonic-gate					if (!defined($tuple->[1]) ||
7990Sstevel@tonic-gate					     ref($tuple->[1])) {
8000Sstevel@tonic-gate						next;
8010Sstevel@tonic-gate					}
8020Sstevel@tonic-gate					($num, $modifier, $unit) =
8030Sstevel@tonic-gate					    projent_val2num($tuple->[1],
8040Sstevel@tonic-gate					        $flags->{'type'});
8050Sstevel@tonic-gate
8060Sstevel@tonic-gate					if (!defined($num)) {
8070Sstevel@tonic-gate
8080Sstevel@tonic-gate						if (defined($unit)) {
8090Sstevel@tonic-gate							push(@err, [3, gettext(
8100Sstevel@tonic-gate							    'rctl %s has '.
8110Sstevel@tonic-gate							    'invalid unit '.
8120Sstevel@tonic-gate							    '"%s"'),$name,
8130Sstevel@tonic-gate							    $unit]);
8140Sstevel@tonic-gate						} else {
8150Sstevel@tonic-gate							push(@err, [3, gettext(
8160Sstevel@tonic-gate							    'rctl %s has '.
8170Sstevel@tonic-gate							    'invalid value '.
8180Sstevel@tonic-gate						            '"%s"'), $name,
8190Sstevel@tonic-gate							    $tuple->[1]]);
8200Sstevel@tonic-gate						}
8210Sstevel@tonic-gate					} elsif ($num eq "OVERFLOW") {
8220Sstevel@tonic-gate						push(@err, [3, gettext(
8230Sstevel@tonic-gate					            'rctl %s value "%s" '.
8240Sstevel@tonic-gate						    'exceeds maximum value "%s"'),
8250Sstevel@tonic-gate					             $name, $tuple->[1], $MaxNum]);
8260Sstevel@tonic-gate					} else {
8270Sstevel@tonic-gate						$tuple->[1] = $num;
8280Sstevel@tonic-gate					}
8290Sstevel@tonic-gate				}
8300Sstevel@tonic-gate			}
8310Sstevel@tonic-gate		}
8320Sstevel@tonic-gate		$attribute->{'values'} = $ref;
8330Sstevel@tonic-gate		if (@err) {
8340Sstevel@tonic-gate			return (1, \@err);
8350Sstevel@tonic-gate		} else {
8360Sstevel@tonic-gate			return (0, $attribute);
8370Sstevel@tonic-gate		}
8380Sstevel@tonic-gate
8390Sstevel@tonic-gate	} else {
8400Sstevel@tonic-gate		# Attribute did not match name[=value,value...]
8410Sstevel@tonic-gate		push(@err, [3, gettext('Invalid attribute "%s"'), $string]);
8420Sstevel@tonic-gate		return (1, \@err);
8430Sstevel@tonic-gate	}
8440Sstevel@tonic-gate}
8450Sstevel@tonic-gate
8460Sstevel@tonic-gate#
8470Sstevel@tonic-gate# projent_parse_attributes("; seperated list of name=values pairs");
8480Sstevel@tonic-gate#
8490Sstevel@tonic-gate# Returns a list of attribute references, as returned by
8500Sstevel@tonic-gate# projent_parse_attribute().
8510Sstevel@tonic-gate#
8520Sstevel@tonic-gatesub projent_parse_attributes
8530Sstevel@tonic-gate{
8540Sstevel@tonic-gate	my ($attributes, $flags) = @_;
8550Sstevel@tonic-gate	my @attributelist;
8560Sstevel@tonic-gate	my @attributestrings;
8570Sstevel@tonic-gate	my $attributestring;
8580Sstevel@tonic-gate	my $attribute;
8590Sstevel@tonic-gate	my ($ret, $ref);
8600Sstevel@tonic-gate	my @errs;
8610Sstevel@tonic-gate
8620Sstevel@tonic-gate	# Split up attributes by ";"'s.
8630Sstevel@tonic-gate	@attributestrings = split(/;/, $attributes);
8640Sstevel@tonic-gate
8650Sstevel@tonic-gate	# If no attributes, return empty list.
8660Sstevel@tonic-gate	if (!@attributestrings) {
8670Sstevel@tonic-gate		return (0, \@attributelist);
8680Sstevel@tonic-gate	}
8690Sstevel@tonic-gate
8700Sstevel@tonic-gate	foreach $attributestring (@attributestrings) {
8710Sstevel@tonic-gate
8720Sstevel@tonic-gate		($ret, $ref) = projent_parse_attribute($attributestring,
8730Sstevel@tonic-gate		    $flags);
8740Sstevel@tonic-gate		if ($ret != 0) {
8750Sstevel@tonic-gate			push(@errs, @$ref);
8760Sstevel@tonic-gate		} else {
8770Sstevel@tonic-gate			push(@attributelist, $ref);
8780Sstevel@tonic-gate		}
8790Sstevel@tonic-gate	}
8800Sstevel@tonic-gate
8810Sstevel@tonic-gate	if (@errs) {
8820Sstevel@tonic-gate		return (1, \@errs);
8830Sstevel@tonic-gate	} else {
8840Sstevel@tonic-gate		return (0, \@attributelist);
8850Sstevel@tonic-gate	}
8860Sstevel@tonic-gate
8870Sstevel@tonic-gate}
8880Sstevel@tonic-gate
8890Sstevel@tonic-gate#
8900Sstevel@tonic-gate# projent_values_equal(list A, list B)
8910Sstevel@tonic-gate#
8920Sstevel@tonic-gate# Given two references to lists of attribute values (as returned by
8930Sstevel@tonic-gate# projent_parse_attribute_values()), returns 1 if they are identical
8940Sstevel@tonic-gate# lists or 0 if they are not.
8950Sstevel@tonic-gate#
8960Sstevel@tonic-gate# XXX sub projent_values_equal;
8970Sstevel@tonic-gatesub projent_values_equal
8980Sstevel@tonic-gate{
8990Sstevel@tonic-gate	my ($x, $y) = @_;
9000Sstevel@tonic-gate
9010Sstevel@tonic-gate	my $itema;
9020Sstevel@tonic-gate	my $itemb;
9030Sstevel@tonic-gate	my $index = 0;
9040Sstevel@tonic-gate
9050Sstevel@tonic-gate	if (ref($x) && ref($y)) {
9060Sstevel@tonic-gate
9070Sstevel@tonic-gate		if (scalar(@$x) != scalar(@$y)) {
9080Sstevel@tonic-gate			return (0);
9090Sstevel@tonic-gate		} else {
9100Sstevel@tonic-gate			foreach $itema (@$x) {
9110Sstevel@tonic-gate
9120Sstevel@tonic-gate				$itemb = $y->[$index++];
9130Sstevel@tonic-gate
9140Sstevel@tonic-gate				if (!projent_values_equal($itema, $itemb)) {
9150Sstevel@tonic-gate					return (0);
9160Sstevel@tonic-gate				}
9170Sstevel@tonic-gate			}
9180Sstevel@tonic-gate			return (1);
9190Sstevel@tonic-gate		}
9200Sstevel@tonic-gate	} elsif ((!ref($x) && (!ref($y)))) {
9210Sstevel@tonic-gate		return ($x eq $y);
9220Sstevel@tonic-gate	} else {
9230Sstevel@tonic-gate		return (0);
9240Sstevel@tonic-gate	}
9250Sstevel@tonic-gate}
9260Sstevel@tonic-gate
9270Sstevel@tonic-gate#
9280Sstevel@tonic-gate# Converts a list of values to a , seperated string, enclosing sublists
9290Sstevel@tonic-gate# in ()'s.
9300Sstevel@tonic-gate#
9310Sstevel@tonic-gatesub projent_values2string
9320Sstevel@tonic-gate{
9330Sstevel@tonic-gate	my ($values) = @_;
9340Sstevel@tonic-gate	my $string;
9350Sstevel@tonic-gate	my $value;
9360Sstevel@tonic-gate	my @valuelist;
9370Sstevel@tonic-gate
9380Sstevel@tonic-gate	if (!defined($values)) {
9390Sstevel@tonic-gate		return ('');
9400Sstevel@tonic-gate	}
9410Sstevel@tonic-gate	if (!ref($values)) {
9420Sstevel@tonic-gate		return ($values);
9430Sstevel@tonic-gate	}
9440Sstevel@tonic-gate	foreach $value (@$values) {
9450Sstevel@tonic-gate
9460Sstevel@tonic-gate                if (ref($value)) {
9470Sstevel@tonic-gate			push(@valuelist,
9480Sstevel@tonic-gate                            '(' . projent_values2string($value) . ')');
9490Sstevel@tonic-gate                } else {
9500Sstevel@tonic-gate			push(@valuelist, $value);
9510Sstevel@tonic-gate		}
9520Sstevel@tonic-gate        }
9530Sstevel@tonic-gate
9540Sstevel@tonic-gate	$string = join(',', @valuelist)	;
9550Sstevel@tonic-gate	if (!defined($string)) {
9560Sstevel@tonic-gate		$string = '';
9570Sstevel@tonic-gate	}
9580Sstevel@tonic-gate        return ($string);
9590Sstevel@tonic-gate}
9600Sstevel@tonic-gate
9610Sstevel@tonic-gate#
9620Sstevel@tonic-gate# Converts a ref to an attribute hash with keys "name", and "values" to
9630Sstevel@tonic-gate# a string in the form "name=value,value...".
9640Sstevel@tonic-gate#
9650Sstevel@tonic-gatesub projent_attribute2string
9660Sstevel@tonic-gate{
9670Sstevel@tonic-gate	my ($attribute) = @_;
9680Sstevel@tonic-gate	my $string;
9690Sstevel@tonic-gate
9700Sstevel@tonic-gate	$string = $attribute->{'name'};
9710Sstevel@tonic-gate
9720Sstevel@tonic-gate	if (ref($attribute->{'values'}) && @{$attribute->{'values'}}) {
9730Sstevel@tonic-gate		$string = $string . '=' .
9740Sstevel@tonic-gate		    projent_values2string(($attribute->{'values'}));
9750Sstevel@tonic-gate	}
9760Sstevel@tonic-gate	return ($string);
9770Sstevel@tonic-gate}
9780Sstevel@tonic-gate
9790Sstevel@tonic-gate#
9800Sstevel@tonic-gate# Converts a ref to a projent hash (as returned by projent_parse()) to
9810Sstevel@tonic-gate# a project(4) database entry line.
9820Sstevel@tonic-gate#
9830Sstevel@tonic-gatesub projent_2string
9840Sstevel@tonic-gate{
9850Sstevel@tonic-gate	my ($projent) = @_;
9860Sstevel@tonic-gate	my @attributestrings;
9870Sstevel@tonic-gate	my $attribute;
9880Sstevel@tonic-gate
9890Sstevel@tonic-gate	foreach $attribute (@{$projent->{'attributelist'}}) {
9900Sstevel@tonic-gate		push(@attributestrings, projent_attribute2string($attribute));
9910Sstevel@tonic-gate	}
9920Sstevel@tonic-gate	return (join(':', ($projent->{'name'},
9930Sstevel@tonic-gate			   $projent->{'projid'},
9940Sstevel@tonic-gate			   $projent->{'comment'},
9950Sstevel@tonic-gate			   join(',', @{$projent->{'userlist'}}),
9960Sstevel@tonic-gate			   join(',', @{$projent->{'grouplist'}}),
9970Sstevel@tonic-gate			   join(';', @attributestrings))));
9980Sstevel@tonic-gate}
9990Sstevel@tonic-gate
10000Sstevel@tonic-gate#
10010Sstevel@tonic-gate# projf_validate(ref to list of projents hashes, flags)
10020Sstevel@tonic-gate#
10030Sstevel@tonic-gate# For each projent hash ref in the list, checks that users, groups, and pools
10040Sstevel@tonic-gate# exists, and that known attributes are valid.  Attributes matching rctl names
10050Sstevel@tonic-gate# are verified to have valid values given that rctl's global flags and max
10060Sstevel@tonic-gate# value.
10070Sstevel@tonic-gate#
10080Sstevel@tonic-gate# Valid flag keys:
10090Sstevel@tonic-gate#
10100Sstevel@tonic-gate#	"res" 	- allow reserved project ids 0-99
10110Sstevel@tonic-gate#	"dup"   - allow duplicate project ids
10120Sstevel@tonic-gate#
10130Sstevel@tonic-gatesub projf_validate
10140Sstevel@tonic-gate{
10150Sstevel@tonic-gate	my ($projents, $flags) = @_;
10160Sstevel@tonic-gate	my $projent;
10170Sstevel@tonic-gate	my $ret;
10180Sstevel@tonic-gate	my $ref;
10190Sstevel@tonic-gate	my @err;
10200Sstevel@tonic-gate	my %idhash;
10210Sstevel@tonic-gate	my %namehash;
10220Sstevel@tonic-gate	my %seenids;
10230Sstevel@tonic-gate	my %seennames;
10240Sstevel@tonic-gate
10250Sstevel@tonic-gate	# check for unique project names
10260Sstevel@tonic-gate	foreach $projent (@$projents) {
10270Sstevel@tonic-gate
10280Sstevel@tonic-gate		my @lineerr;
10290Sstevel@tonic-gate
10300Sstevel@tonic-gate		$seennames{$projent->{'name'}}++;
10310Sstevel@tonic-gate		$seenids{$projent->{'projid'}}++;
10320Sstevel@tonic-gate
10330Sstevel@tonic-gate		if ($seennames{$projent->{'name'}} > 1) {
10340Sstevel@tonic-gate			push(@lineerr, [4, gettext(
10350Sstevel@tonic-gate			    'Duplicate project name "%s"'),
10360Sstevel@tonic-gate			    $projent->{'name'}]);
10370Sstevel@tonic-gate		}
10380Sstevel@tonic-gate
10390Sstevel@tonic-gate		if (!defined($flags->{'dup'})) {
10400Sstevel@tonic-gate			if ($seenids{$projent->{'projid'}} > 1) {
10410Sstevel@tonic-gate				push(@lineerr, [4, gettext(
10420Sstevel@tonic-gate				    'Duplicate projid "%s"'),
10430Sstevel@tonic-gate				    $projent->{'projid'}]);
10440Sstevel@tonic-gate			}
10450Sstevel@tonic-gate		}
10460Sstevel@tonic-gate		($ret, $ref) = projent_validate($projent, $flags);
10470Sstevel@tonic-gate		if ($ret != 0) {
10480Sstevel@tonic-gate			push(@lineerr, @$ref);
10490Sstevel@tonic-gate		}
10500Sstevel@tonic-gate
10510Sstevel@tonic-gate		if (@lineerr) {
10520Sstevel@tonic-gate
10530Sstevel@tonic-gate			$ref = projf_combine_errors([5, gettext(
10540Sstevel@tonic-gate			    'Validation error on line %d'),
10550Sstevel@tonic-gate			    $projent->{'linenum'}], \@lineerr);
10560Sstevel@tonic-gate			push(@err, @$ref);
10570Sstevel@tonic-gate		}
10580Sstevel@tonic-gate	}
10590Sstevel@tonic-gate	if (@err) {
10600Sstevel@tonic-gate		return (1, \@err);
10610Sstevel@tonic-gate	} else {
10620Sstevel@tonic-gate		return (0, $projents);
10630Sstevel@tonic-gate	}
10640Sstevel@tonic-gate}
10650Sstevel@tonic-gate
10660Sstevel@tonic-gate#
10670Sstevel@tonic-gate# projent_validate_unique_id(
10680Sstevel@tonic-gate#     ref to projent hash, ref to list of projent hashes)
10690Sstevel@tonic-gate#
10700Sstevel@tonic-gate# Verifies that projid of the projent hash only exists once in the list of
10710Sstevel@tonic-gate# projent hashes.
10720Sstevel@tonic-gate#
10730Sstevel@tonic-gatesub projent_validate_unique_id
10740Sstevel@tonic-gate{
10750Sstevel@tonic-gate	my ($projent, $projf, $idhash) = @_;
10760Sstevel@tonic-gate	my @err;
10770Sstevel@tonic-gate	my $ret = 0;
10780Sstevel@tonic-gate	my $projid = $projent->{'projid'};
10790Sstevel@tonic-gate
10800Sstevel@tonic-gate	if (scalar(grep($_->{'projid'} eq $projid, @$projf)) > 1) {
10810Sstevel@tonic-gate		$ret = 1;
10820Sstevel@tonic-gate		push(@err, [4, gettext('Duplicate projid "%s"'),
10830Sstevel@tonic-gate		    $projid]);
10840Sstevel@tonic-gate	}
10850Sstevel@tonic-gate
10860Sstevel@tonic-gate	return ($ret, \@err);
10870Sstevel@tonic-gate}
10880Sstevel@tonic-gate
10890Sstevel@tonic-gate#
10900Sstevel@tonic-gate# projent_validate_unique_id(
10910Sstevel@tonic-gate#     ref to projent hash, ref to list of projent hashes)
10920Sstevel@tonic-gate#
10930Sstevel@tonic-gate# Verifies that project name of the projent hash only exists once in the list
10940Sstevel@tonic-gate# of projent hashes.
10950Sstevel@tonic-gate#
10960Sstevel@tonic-gate# If the seconds argument is a hash ref, it is treated
10970Sstevel@tonic-gate#
10980Sstevel@tonic-gatesub projent_validate_unique_name
10990Sstevel@tonic-gate{
11000Sstevel@tonic-gate	my ($projent, $projf, $namehash) = @_;
11010Sstevel@tonic-gate	my $ret = 0;
11020Sstevel@tonic-gate	my @err;
11030Sstevel@tonic-gate	my $pname = $projent->{'name'};
11040Sstevel@tonic-gate
11050Sstevel@tonic-gate	if (scalar(grep($_->{'name'} eq $pname, @$projf)) > 1) {
11060Sstevel@tonic-gate		$ret = 1;
11070Sstevel@tonic-gate		push(@err,
11080Sstevel@tonic-gate		     [9, gettext('Duplicate project name "%s"'), $pname]);
11090Sstevel@tonic-gate	}
11100Sstevel@tonic-gate
11110Sstevel@tonic-gate	return ($ret, \@err);
11120Sstevel@tonic-gate}
11130Sstevel@tonic-gate
11140Sstevel@tonic-gate#
11150Sstevel@tonic-gate# projent_validate(ref to projents hash, flags)
11160Sstevel@tonic-gate#
11170Sstevel@tonic-gate# Checks that users, groups, and pools exists, and that known attributes
11180Sstevel@tonic-gate# are valid.  Attributes matching rctl names are verified to have valid
11190Sstevel@tonic-gate# values given that rctl's global flags and max value.
11200Sstevel@tonic-gate#
11210Sstevel@tonic-gate# Valid flag keys:
11220Sstevel@tonic-gate#
11230Sstevel@tonic-gate#	"allowspaces" 	- user and group list are allowed to contain whitespace
11240Sstevel@tonic-gate#	"res" 		- allow reserved project ids 0-99
11250Sstevel@tonic-gate#
11260Sstevel@tonic-gatesub projent_validate
11270Sstevel@tonic-gate{
11280Sstevel@tonic-gate	my ($projent, $flags) = @_;
11290Sstevel@tonic-gate	my $ret = 0;
11300Sstevel@tonic-gate	my $ref;
11310Sstevel@tonic-gate	my @err;
11320Sstevel@tonic-gate
11330Sstevel@tonic-gate	($ret, $ref) =
11340Sstevel@tonic-gate	    projent_validate_name($projent->{'name'}, $flags);
11350Sstevel@tonic-gate	if ($ret != 0) {
11360Sstevel@tonic-gate		push(@err, @$ref);
11370Sstevel@tonic-gate	}
11380Sstevel@tonic-gate	($ret, $ref) =
11390Sstevel@tonic-gate	    projent_validate_projid($projent->{'projid'}, $flags);
11400Sstevel@tonic-gate	if ($ret != 0) {
11410Sstevel@tonic-gate		push(@err, @$ref);
11420Sstevel@tonic-gate	}
11430Sstevel@tonic-gate	($ret, $ref) =
11440Sstevel@tonic-gate	    projent_validate_comment($projent->{'comment'}, $flags);
11450Sstevel@tonic-gate	if ($ret != 0) {
11460Sstevel@tonic-gate		push(@err, @$ref);
11470Sstevel@tonic-gate	}
11480Sstevel@tonic-gate	($ret, $ref) =
11490Sstevel@tonic-gate	    projent_validate_users($projent->{'userlist'}, $flags);
11500Sstevel@tonic-gate	if ($ret != 0) {
11510Sstevel@tonic-gate		push(@err, @$ref);
11520Sstevel@tonic-gate	}
11530Sstevel@tonic-gate	($ret, $ref) =
11540Sstevel@tonic-gate	    projent_validate_groups($projent->{'grouplist'}, $flags);
11550Sstevel@tonic-gate	if ($ret != 0) {
11560Sstevel@tonic-gate		push(@err, @$ref);
11570Sstevel@tonic-gate	}
11580Sstevel@tonic-gate	($ret, $ref) = projent_validate_attributes(
11590Sstevel@tonic-gate	    $projent->{'attributelist'}, $flags);
11600Sstevel@tonic-gate	if ($ret != 0) {
11610Sstevel@tonic-gate		push(@err, @$ref);
11620Sstevel@tonic-gate	}
11630Sstevel@tonic-gate
11640Sstevel@tonic-gate	my $string = projent_2string($projent);
11650Sstevel@tonic-gate	if (length($string) > (&PROJECT_BUFSZ - 2)) {
11660Sstevel@tonic-gate		push(@err, [3, gettext('projent line too long')]);
11670Sstevel@tonic-gate	}
11680Sstevel@tonic-gate
11690Sstevel@tonic-gate	if (@err) {
11700Sstevel@tonic-gate		return (1, \@err);
11710Sstevel@tonic-gate	} else {
11720Sstevel@tonic-gate		return (0, $projent);
11730Sstevel@tonic-gate	}
11740Sstevel@tonic-gate}
11750Sstevel@tonic-gate
11760Sstevel@tonic-gate#
11770Sstevel@tonic-gate# projent_validate_name(name, flags)
11780Sstevel@tonic-gate#
11790Sstevel@tonic-gate# does nothing, as any parse-able project name is valid
11800Sstevel@tonic-gate#
11810Sstevel@tonic-gatesub projent_validate_name
11820Sstevel@tonic-gate{
11830Sstevel@tonic-gate	my ($name, $flags) = @_;
11840Sstevel@tonic-gate	my @err;
11850Sstevel@tonic-gate
11860Sstevel@tonic-gate	return (0, \@err);
11870Sstevel@tonic-gate
11880Sstevel@tonic-gate}
11890Sstevel@tonic-gate
11900Sstevel@tonic-gate#
11910Sstevel@tonic-gate# projent_validate_projid(projid, flags)
11920Sstevel@tonic-gate#
11930Sstevel@tonic-gate# Validates that projid is within the valid range of numbers.
11940Sstevel@tonic-gate# Valid flag keys:
11950Sstevel@tonic-gate#	"res"	- allow reserved projid's 0-99
11960Sstevel@tonic-gate#
11970Sstevel@tonic-gatesub projent_validate_projid
11980Sstevel@tonic-gate{
11990Sstevel@tonic-gate	my ($projid, $flags) = @_;
12000Sstevel@tonic-gate	my @err;
12010Sstevel@tonic-gate	my $ret = 0;
12020Sstevel@tonic-gate	my $minprojid;
12030Sstevel@tonic-gate
12040Sstevel@tonic-gate	if (defined($flags->{'res'})) {
12050Sstevel@tonic-gate		$minprojid = 0;
12060Sstevel@tonic-gate	} else {
12070Sstevel@tonic-gate		$minprojid = 100;
12080Sstevel@tonic-gate	}
12090Sstevel@tonic-gate
12100Sstevel@tonic-gate	if ($projid < $minprojid) {
12110Sstevel@tonic-gate
12120Sstevel@tonic-gate		$ret = 1;
12130Sstevel@tonic-gate		push(@err, [3, gettext('Invalid projid "%s": '.
12140Sstevel@tonic-gate		    'must be >= 100'),
12150Sstevel@tonic-gate		    $projid]);
12160Sstevel@tonic-gate
12170Sstevel@tonic-gate	}
12180Sstevel@tonic-gate
12190Sstevel@tonic-gate	return ($ret, \@err);
12200Sstevel@tonic-gate}
12210Sstevel@tonic-gate
12220Sstevel@tonic-gate#
12230Sstevel@tonic-gate# projent_validate_comment(name, flags)
12240Sstevel@tonic-gate#
12250Sstevel@tonic-gate# Does nothing, as any parse-able comment is valid.
12260Sstevel@tonic-gate#
12270Sstevel@tonic-gatesub projent_validate_comment
12280Sstevel@tonic-gate{
12290Sstevel@tonic-gate	my ($comment, $flags) = @_;
12300Sstevel@tonic-gate	my @err;
12310Sstevel@tonic-gate
12320Sstevel@tonic-gate	return (0, \@err);
12330Sstevel@tonic-gate}
12340Sstevel@tonic-gate
12350Sstevel@tonic-gate#
12360Sstevel@tonic-gate# projent_validate_users(ref to list of user names, flags)
12370Sstevel@tonic-gate#
12380Sstevel@tonic-gate# Verifies that each username is either a valid glob, such
12390Sstevel@tonic-gate# as * or !*, or is an existing user.  flags is unused.
12400Sstevel@tonic-gate# Also validates that there are no duplicates.
12410Sstevel@tonic-gate#
12420Sstevel@tonic-gatesub projent_validate_users
12430Sstevel@tonic-gate{
12440Sstevel@tonic-gate	my ($users, $flags) = @_;
12450Sstevel@tonic-gate	my @err;
12460Sstevel@tonic-gate	my $ret = 0;
12470Sstevel@tonic-gate	my $user;
12480Sstevel@tonic-gate	my $username;
12490Sstevel@tonic-gate
12500Sstevel@tonic-gate	foreach $user (@$users) {
12510Sstevel@tonic-gate
12520Sstevel@tonic-gate		if ($user eq '*' || $user eq '!*') {
12530Sstevel@tonic-gate			next;
12540Sstevel@tonic-gate		}
12550Sstevel@tonic-gate		$username = $user;
12560Sstevel@tonic-gate		$username =~ s/^!//;
12570Sstevel@tonic-gate
12580Sstevel@tonic-gate		if (!defined(getpwnam($username))) {
12590Sstevel@tonic-gate			$ret = 1;
12600Sstevel@tonic-gate			push(@err, [6,
12610Sstevel@tonic-gate			    gettext('User "%s" does not exist'),
12620Sstevel@tonic-gate			    $username]);
12630Sstevel@tonic-gate		}
12640Sstevel@tonic-gate	}
12650Sstevel@tonic-gate
12660Sstevel@tonic-gate	my %seen;
12670Sstevel@tonic-gate        my @dups = grep($seen{$_}++ == 1, @$users);
12680Sstevel@tonic-gate	if (@dups) {
12690Sstevel@tonic-gate		$ret = 1;
12700Sstevel@tonic-gate		push(@err, [3, gettext('Duplicate user names "%s"'),
12710Sstevel@tonic-gate		    join(',', @dups)]);
12720Sstevel@tonic-gate	}
12730Sstevel@tonic-gate	return ($ret, \@err)
12740Sstevel@tonic-gate}
12750Sstevel@tonic-gate
12760Sstevel@tonic-gate#
12770Sstevel@tonic-gate# projent_validate_groups(ref to list of group names, flags)
12780Sstevel@tonic-gate#
12790Sstevel@tonic-gate# Verifies that each groupname is either a valid glob, such
12800Sstevel@tonic-gate# as * or !*, or is an existing group.  flags is unused.
12810Sstevel@tonic-gate# Also validates that there are no duplicates.
12820Sstevel@tonic-gate#
12830Sstevel@tonic-gatesub projent_validate_groups
12840Sstevel@tonic-gate{
12850Sstevel@tonic-gate	my ($groups, $flags) = @_;
12860Sstevel@tonic-gate	my @err;
12870Sstevel@tonic-gate	my $ret = 0;
12880Sstevel@tonic-gate	my $group;
12890Sstevel@tonic-gate	my $groupname;
12900Sstevel@tonic-gate
12910Sstevel@tonic-gate	foreach $group (@$groups) {
12920Sstevel@tonic-gate
12930Sstevel@tonic-gate		if ($group eq '*' || $group eq '!*') {
12940Sstevel@tonic-gate			next;
12950Sstevel@tonic-gate		}
12960Sstevel@tonic-gate
12970Sstevel@tonic-gate		$groupname = $group;
12980Sstevel@tonic-gate		$groupname =~ s/^!//;
12990Sstevel@tonic-gate
13000Sstevel@tonic-gate		if (!defined(getgrnam($groupname))) {
13010Sstevel@tonic-gate			$ret = 1;
13020Sstevel@tonic-gate			push(@err, [6,
13030Sstevel@tonic-gate			    gettext('Group "%s" does not exist'),
13040Sstevel@tonic-gate			    $groupname]);
13050Sstevel@tonic-gate		}
13060Sstevel@tonic-gate	}
13070Sstevel@tonic-gate
13080Sstevel@tonic-gate	my %seen;
13090Sstevel@tonic-gate        my @dups = grep($seen{$_}++ == 1, @$groups);
13100Sstevel@tonic-gate	if (@dups) {
13110Sstevel@tonic-gate		$ret = 1;
13120Sstevel@tonic-gate		push(@err, [3, gettext('Duplicate group names "%s"'),
13130Sstevel@tonic-gate		    join(',', @dups)]);
13140Sstevel@tonic-gate	}
13150Sstevel@tonic-gate
13160Sstevel@tonic-gate	return ($ret, \@err)
13170Sstevel@tonic-gate}
13180Sstevel@tonic-gate
13190Sstevel@tonic-gate#
13200Sstevel@tonic-gate# projent_validate_attribute(attribute hash ref, flags)
13210Sstevel@tonic-gate#
13220Sstevel@tonic-gate# Verifies that if the attribute's name is a known attribute or
13230Sstevel@tonic-gate# resource control, that it contains a valid value.
13240Sstevel@tonic-gate# flags is unused.
13250Sstevel@tonic-gate#
13260Sstevel@tonic-gatesub projent_validate_attribute
13270Sstevel@tonic-gate{
13280Sstevel@tonic-gate	my ($attribute, $flags) = @_;
13290Sstevel@tonic-gate	my $name = $attribute->{'name'};
13300Sstevel@tonic-gate	my $values = $attribute->{'values'};
13310Sstevel@tonic-gate	my $value;
13320Sstevel@tonic-gate	my @errs;
13330Sstevel@tonic-gate	my $ret = 0;
13340Sstevel@tonic-gate	my $result;
13350Sstevel@tonic-gate	my $ref;
13360Sstevel@tonic-gate
13370Sstevel@tonic-gate	if (defined($values)) {
13380Sstevel@tonic-gate		$value = $values->[0];
13390Sstevel@tonic-gate	}
13400Sstevel@tonic-gate	if ($name eq 'task.final') {
13410Sstevel@tonic-gate
13420Sstevel@tonic-gate		if (defined($values)) {
13430Sstevel@tonic-gate			$ret = 1;
13440Sstevel@tonic-gate			push(@errs, [3, gettext(
13450Sstevel@tonic-gate			    'task.final should not have value')]);
13460Sstevel@tonic-gate		}
13470Sstevel@tonic-gate
13480Sstevel@tonic-gate	# Need to rcap.max-rss needs to be a number
13490Sstevel@tonic-gate        } elsif ($name eq 'rcap.max-rss') {
13500Sstevel@tonic-gate
13510Sstevel@tonic-gate		if (!defined($values)) {
13520Sstevel@tonic-gate			$ret = 1;
13530Sstevel@tonic-gate			push(@errs, [3, gettext(
13540Sstevel@tonic-gate			    'rcap.max-rss missing value')]);
13550Sstevel@tonic-gate		} elsif (scalar(@$values) != 1) {
13560Sstevel@tonic-gate			$ret = 1;
13570Sstevel@tonic-gate			push(@errs, [3, gettext(
13580Sstevel@tonic-gate			    'rcap.max-rss should have single value')]);
13590Sstevel@tonic-gate		}
13600Sstevel@tonic-gate		if (!defined($value) || ref($value)) {
13610Sstevel@tonic-gate			$ret = 1;
13620Sstevel@tonic-gate			push(@errs, [3, gettext(
13630Sstevel@tonic-gate			    'rcap.max-rss has invalid value "%s"'),
13640Sstevel@tonic-gate			    projent_values2string($values)]);;
13650Sstevel@tonic-gate		} elsif ($value !~ /^\d+$/) {
13660Sstevel@tonic-gate			$ret = 1;
13670Sstevel@tonic-gate			push(@errs, [3, gettext(
13680Sstevel@tonic-gate			    'rcap.max-rss is not an integer value: "%s"'),
13690Sstevel@tonic-gate			    projent_values2string($values)]);;
13700Sstevel@tonic-gate                } elsif ($value > $MaxNum) {
13710Sstevel@tonic-gate			$ret = 1;
13720Sstevel@tonic-gate			push(@errs, [3, gettext(
13730Sstevel@tonic-gate			    'rcap.max-rss too large')]);
13740Sstevel@tonic-gate                }
13750Sstevel@tonic-gate
13760Sstevel@tonic-gate	} elsif ($name eq 'project.pool') {
13770Sstevel@tonic-gate		if (!defined($values)) {
13780Sstevel@tonic-gate			$ret = 1;
13790Sstevel@tonic-gate			push(@errs, [3, gettext(
13800Sstevel@tonic-gate			    'project.pool missing value')]);
13810Sstevel@tonic-gate		} elsif (scalar(@$values) != 1) {
13820Sstevel@tonic-gate			$ret = 1;
13830Sstevel@tonic-gate			push(@errs, [3, gettext(
13840Sstevel@tonic-gate			    'project.pool should have single value')]);
13850Sstevel@tonic-gate		} elsif (!defined($value) || ref($value)) {
13860Sstevel@tonic-gate			$ret = 1;
13870Sstevel@tonic-gate			push(@errs, [3, gettext(
13880Sstevel@tonic-gate			    'project.pool has invalid value "%s'),
13890Sstevel@tonic-gate			    projent_values2string($values)]);;
13900Sstevel@tonic-gate		} elsif (!($value =~ /^[[:alpha:]][[:alnum:]_.-]*$/)) {
13910Sstevel@tonic-gate			$ret = 1;
13920Sstevel@tonic-gate			push(@errs, [3, gettext(
13930Sstevel@tonic-gate			    'project.pool: invalid pool name "%s"'),
13940Sstevel@tonic-gate			    $value]);
13950Sstevel@tonic-gate		# Pool must exist.
13960Sstevel@tonic-gate		} elsif (pool_exists($value) != 0) {
13970Sstevel@tonic-gate			$ret = 1;
13980Sstevel@tonic-gate			push(@errs, [6, gettext(
13990Sstevel@tonic-gate			    'project.pool: pools not enabled or pool does '.
14000Sstevel@tonic-gate			    'not exist: "%s"'),
14010Sstevel@tonic-gate			    $value]);
14020Sstevel@tonic-gate		}
14030Sstevel@tonic-gate	} else {
14040Sstevel@tonic-gate		my $rctlmax;
14050Sstevel@tonic-gate		my $rctlflags;
14060Sstevel@tonic-gate		my $rules;
14070Sstevel@tonic-gate
14080Sstevel@tonic-gate		#
14090Sstevel@tonic-gate		# See if rctl rules exist for this attribute.  If so, it
14100Sstevel@tonic-gate		# is an rctl and is checked for valid values.
14110Sstevel@tonic-gate		#
14120Sstevel@tonic-gate
14130Sstevel@tonic-gate		# check hashed cache of rctl rules.
14140Sstevel@tonic-gate		$rules = $RctlRules{$name};
14150Sstevel@tonic-gate		if (!defined($rules)) {
14160Sstevel@tonic-gate
14170Sstevel@tonic-gate			#
14180Sstevel@tonic-gate			# See if this is an resource control name, if so
14190Sstevel@tonic-gate			# cache rules.
14200Sstevel@tonic-gate			#
14210Sstevel@tonic-gate			($rctlmax, $rctlflags) = rctl_get_info($name);
14220Sstevel@tonic-gate			if (defined($rctlmax)) {
14230Sstevel@tonic-gate				$rules = proj_getrctlrules(
14240Sstevel@tonic-gate				    $rctlmax, $rctlflags);
14250Sstevel@tonic-gate				if (defined($rules)) {
14260Sstevel@tonic-gate					$RctlRules{$name} = $rules;
14270Sstevel@tonic-gate				} else {
14280Sstevel@tonic-gate					$RctlRules{$name} = "NOT AN RCTL";
14290Sstevel@tonic-gate				}
14300Sstevel@tonic-gate			}
14310Sstevel@tonic-gate		}
14320Sstevel@tonic-gate
14330Sstevel@tonic-gate		# If rules are defined, this is a resource control.
14340Sstevel@tonic-gate		if (defined($rules) && ref($rules)) {
14350Sstevel@tonic-gate
14360Sstevel@tonic-gate			($result, $ref) =
14370Sstevel@tonic-gate			    projent_validate_rctl($attribute, $flags);
14380Sstevel@tonic-gate			if ($result != 0) {
14390Sstevel@tonic-gate				$ret = 1;
14400Sstevel@tonic-gate				push(@errs, @$ref);
14410Sstevel@tonic-gate			}
14420Sstevel@tonic-gate		}
14430Sstevel@tonic-gate	}
14440Sstevel@tonic-gate	return ($ret, \@errs);
14450Sstevel@tonic-gate}
14460Sstevel@tonic-gate
14470Sstevel@tonic-gate#
14480Sstevel@tonic-gate# projent_validate_attributes(ref to attribute list, flags)
14490Sstevel@tonic-gate#
14500Sstevel@tonic-gate# Validates all attributes in list of attribute references using
14510Sstevel@tonic-gate# projent_validate_attribute.  flags is unused.
14520Sstevel@tonic-gate# flags is unused.
14530Sstevel@tonic-gate#
14540Sstevel@tonic-gatesub projent_validate_attributes
14550Sstevel@tonic-gate{
14560Sstevel@tonic-gate	my ($attributes, $flags) = @_;
14570Sstevel@tonic-gate	my @err;
14580Sstevel@tonic-gate	my $ret = 0;
14590Sstevel@tonic-gate	my $result = 0;
14600Sstevel@tonic-gate	my $ref;
14610Sstevel@tonic-gate	my $attribute;
14620Sstevel@tonic-gate
14630Sstevel@tonic-gate	foreach $attribute (@$attributes) {
14640Sstevel@tonic-gate
14650Sstevel@tonic-gate		($ret, $ref) = projent_validate_attribute($attribute, $flags);
14660Sstevel@tonic-gate		if ($ret != 0) {
14670Sstevel@tonic-gate			$result = $ret;
14680Sstevel@tonic-gate			push(@err, @$ref);
14690Sstevel@tonic-gate		}
14700Sstevel@tonic-gate	}
14710Sstevel@tonic-gate
14720Sstevel@tonic-gate	my %seen;
14730Sstevel@tonic-gate        my @dups = grep($seen{$_}++ == 1, map { $_->{'name'} } @$attributes);
14740Sstevel@tonic-gate	if (@dups) {
14750Sstevel@tonic-gate		$result = 1;
14760Sstevel@tonic-gate		push(@err, [3, gettext('Duplicate attributes "%s"'),
14770Sstevel@tonic-gate		    join(',', @dups)]);
14780Sstevel@tonic-gate	}
14790Sstevel@tonic-gate
14800Sstevel@tonic-gate	return ($result, \@err);
14810Sstevel@tonic-gate}
14820Sstevel@tonic-gate
14830Sstevel@tonic-gate#
14840Sstevel@tonic-gate# projent_getrctlrules(max value, global flags)
14850Sstevel@tonic-gate#
14860Sstevel@tonic-gate# given an rctls max value and global flags, returns a ref to a hash
14870Sstevel@tonic-gate# of rctl rules that is used by projent_validate_rctl to validate an
14880Sstevel@tonic-gate# rctl's values.
14890Sstevel@tonic-gate#
14900Sstevel@tonic-gatesub proj_getrctlrules
14910Sstevel@tonic-gate{
14920Sstevel@tonic-gate	my ($max, $flags) = @_;
14930Sstevel@tonic-gate	my $signals;
14940Sstevel@tonic-gate	my $rctl;
14950Sstevel@tonic-gate
14960Sstevel@tonic-gate	$rctl = {};
14970Sstevel@tonic-gate	$signals =
14980Sstevel@tonic-gate	    [ qw(ABRT XRES HUP STOP TERM KILL),
14990Sstevel@tonic-gate	      $SigNo{'ABRT'},
15000Sstevel@tonic-gate	      $SigNo{'XRES'},
15010Sstevel@tonic-gate	      $SigNo{'HUP'},
15020Sstevel@tonic-gate	      $SigNo{'STOP'},
15030Sstevel@tonic-gate	      $SigNo{'TERM'},
15040Sstevel@tonic-gate	      $SigNo{'KILL'} ];
15050Sstevel@tonic-gate
15060Sstevel@tonic-gate	$rctl->{'max'} = $max;
15070Sstevel@tonic-gate
15080Sstevel@tonic-gate	if ($flags & &RCTL_GLOBAL_BYTES) {
15090Sstevel@tonic-gate		$rctl->{'type'} = 'bytes';
15100Sstevel@tonic-gate	} elsif ($flags & &RCTL_GLOBAL_SECONDS) {
15110Sstevel@tonic-gate		$rctl->{'type'} = 'seconds';
15120Sstevel@tonic-gate	} elsif ($flags & &RCTL_GLOBAL_COUNT)  {
15130Sstevel@tonic-gate		$rctl->{'type'} = 'count';
15140Sstevel@tonic-gate	} else {
15150Sstevel@tonic-gate		$rctl->{'type'} = 'unknown';
15160Sstevel@tonic-gate	}
15170Sstevel@tonic-gate	if ($flags & &RCTL_GLOBAL_NOBASIC) {
15180Sstevel@tonic-gate		$rctl->{'privs'} = ['privileged', 'priv'];
15190Sstevel@tonic-gate	} else {
15200Sstevel@tonic-gate		$rctl->{'privs'} = ['basic', 'privileged', 'priv'];
15210Sstevel@tonic-gate	}
15220Sstevel@tonic-gate
15230Sstevel@tonic-gate	if ($flags & &RCTL_GLOBAL_DENY_ALWAYS) {
15240Sstevel@tonic-gate		$rctl->{'actions'} = ['deny'];
15250Sstevel@tonic-gate
15260Sstevel@tonic-gate	} elsif ($flags & &RCTL_GLOBAL_DENY_NEVER) {
15270Sstevel@tonic-gate		$rctl->{'actions'} = ['none'];
15280Sstevel@tonic-gate	} else {
15290Sstevel@tonic-gate		$rctl->{'actions'} = ['none', 'deny'];
15300Sstevel@tonic-gate	}
15310Sstevel@tonic-gate
15320Sstevel@tonic-gate	if ($flags & &RCTL_GLOBAL_SIGNAL_NEVER) {
15330Sstevel@tonic-gate		$rctl->{'signals'} = [];
15340Sstevel@tonic-gate
15350Sstevel@tonic-gate	} else {
15360Sstevel@tonic-gate
15370Sstevel@tonic-gate		push(@{$rctl->{'actions'}}, 'sig');
15380Sstevel@tonic-gate
15390Sstevel@tonic-gate		if ($flags & &RCTL_GLOBAL_CPU_TIME) {
15400Sstevel@tonic-gate			push(@$signals, 'XCPU', '30');
15410Sstevel@tonic-gate		}
15420Sstevel@tonic-gate		if ($flags & &RCTL_GLOBAL_FILE_SIZE) {
15430Sstevel@tonic-gate			push(@$signals, 'XFSZ', '31');
15440Sstevel@tonic-gate		}
15450Sstevel@tonic-gate		$rctl->{'signals'} = $signals;
15460Sstevel@tonic-gate	}
15470Sstevel@tonic-gate	return ($rctl);
15480Sstevel@tonic-gate}
15490Sstevel@tonic-gate
15500Sstevel@tonic-gate#
15510Sstevel@tonic-gate# projent_val2num(scaled value, "seconds" | "count" | "bytes")
15520Sstevel@tonic-gate#
15530Sstevel@tonic-gate# converts an integer or scaled value to an integer value.
15540Sstevel@tonic-gate# returns (integer value, modifier character, unit character.
15550Sstevel@tonic-gate#
15560Sstevel@tonic-gate# On failure, integer value is undefined.  If the original
15570Sstevel@tonic-gate# scaled value is a plain integer, modifier character and
15580Sstevel@tonic-gate# unit character will be undefined.
15590Sstevel@tonic-gate#
15600Sstevel@tonic-gatesub projent_val2num
15610Sstevel@tonic-gate{
15620Sstevel@tonic-gate	my ($val, $type) = @_;
15630Sstevel@tonic-gate	my %scaleM = ( k => 1000,
15640Sstevel@tonic-gate		       m => 1000000,
15650Sstevel@tonic-gate		       g => 1000000000,
15660Sstevel@tonic-gate		       t => 1000000000000,
15670Sstevel@tonic-gate		       p => 1000000000000000,
15680Sstevel@tonic-gate		       e => 1000000000000000000);
15690Sstevel@tonic-gate	my %scaleB = ( k => 1024,
15700Sstevel@tonic-gate		       m => 1048576,
15710Sstevel@tonic-gate		       g => 1073741824,
15720Sstevel@tonic-gate		       t => 1099511627776,
15730Sstevel@tonic-gate		       p => 1125899906842624,
15740Sstevel@tonic-gate		       e => 1152921504606846976);
15750Sstevel@tonic-gate
15760Sstevel@tonic-gate	my $scale;
15770Sstevel@tonic-gate	my $base;
15780Sstevel@tonic-gate	my ($num, $modifier, $unit);
15790Sstevel@tonic-gate	my $mul;
15800Sstevel@tonic-gate	my $string;
15810Sstevel@tonic-gate	my $i;
15820Sstevel@tonic-gate	my $undefined;
15830Sstevel@tonic-gate	my $exp_unit;
15840Sstevel@tonic-gate
15850Sstevel@tonic-gate	($num, $modifier, $unit) = $val =~
15860Sstevel@tonic-gate	    /^(\d+(?:\.\d+)?)(?i:([kmgtpe])?([bs])?)$/;
15870Sstevel@tonic-gate
15880Sstevel@tonic-gate	# No numeric match.
15890Sstevel@tonic-gate	if (!defined($num)) {
15900Sstevel@tonic-gate		return ($undefined, $undefined, $undefined);
15910Sstevel@tonic-gate	}
15920Sstevel@tonic-gate
15930Sstevel@tonic-gate	# Decimal number with no scaling modifier.
15940Sstevel@tonic-gate	if (!defined($modifier) && $num =~ /^\d+\.\d+/) {
15950Sstevel@tonic-gate		return ($undefined, $undefined, $undefined);
15960Sstevel@tonic-gate	}
15970Sstevel@tonic-gate
15980Sstevel@tonic-gate	if ($type eq 'bytes') {
15990Sstevel@tonic-gate		$exp_unit = 'b';
16000Sstevel@tonic-gate		$scale = \%scaleB;
16010Sstevel@tonic-gate	} elsif ($type eq 'seconds') {
16020Sstevel@tonic-gate		$exp_unit = 's';
16030Sstevel@tonic-gate		$scale = \%scaleM;
16040Sstevel@tonic-gate	} else {
16050Sstevel@tonic-gate		$scale = \%scaleM;
16060Sstevel@tonic-gate	}
16070Sstevel@tonic-gate
16080Sstevel@tonic-gate	if (defined($unit)) {
16090Sstevel@tonic-gate		$unit = lc($unit);
16100Sstevel@tonic-gate	}
16110Sstevel@tonic-gate
16120Sstevel@tonic-gate	# So not succeed if unit is incorrect.
16130Sstevel@tonic-gate	if (!defined($exp_unit) && defined($unit)) {
16140Sstevel@tonic-gate		return ($undefined, $modifier, $unit);
16150Sstevel@tonic-gate	}
16160Sstevel@tonic-gate	if (defined($unit) && $unit ne $exp_unit) {
16170Sstevel@tonic-gate		return ($undefined, $modifier, $unit);
16180Sstevel@tonic-gate	}
16190Sstevel@tonic-gate
16200Sstevel@tonic-gate	if (defined($modifier)) {
16210Sstevel@tonic-gate
16220Sstevel@tonic-gate		$modifier = lc($modifier);
16230Sstevel@tonic-gate		$mul = $scale->{$modifier};
16240Sstevel@tonic-gate		$num = $num * $mul;
16250Sstevel@tonic-gate	}
16260Sstevel@tonic-gate
16270Sstevel@tonic-gate	# check for integer overflow.
16280Sstevel@tonic-gate	if ($num > $MaxNum) {
16290Sstevel@tonic-gate		return ("OVERFLOW", $modifier, $unit);
16300Sstevel@tonic-gate	}
16310Sstevel@tonic-gate	#
16320Sstevel@tonic-gate	# Trim numbers that are decimal equivalent to the maximum value
16330Sstevel@tonic-gate	# to the maximum integer value.
16340Sstevel@tonic-gate	#
16350Sstevel@tonic-gate	if ($num == $MaxNum) {
16360Sstevel@tonic-gate		$num = $MaxNum;;
16370Sstevel@tonic-gate
16380Sstevel@tonic-gate	} elsif ($num < $MaxNum) {
16390Sstevel@tonic-gate		# convert any decimal numbers to an integer
16400Sstevel@tonic-gate		$num = int($num);
16410Sstevel@tonic-gate	}
16420Sstevel@tonic-gate
16430Sstevel@tonic-gate	return ($num, $modifier, $unit);
16440Sstevel@tonic-gate}
16450Sstevel@tonic-gate#
16460Sstevel@tonic-gate# projent_validate_rctl(ref to rctl attribute hash, flags)
16470Sstevel@tonic-gate#
16480Sstevel@tonic-gate# verifies that the given rctl hash with keys "name" and
16490Sstevel@tonic-gate# "values" contains valid values for the given name.
16500Sstevel@tonic-gate# flags is unused.
16510Sstevel@tonic-gate#
16520Sstevel@tonic-gatesub projent_validate_rctl
16530Sstevel@tonic-gate{
16540Sstevel@tonic-gate	my ($rctl, $flags) = @_;
16550Sstevel@tonic-gate	my $allrules;
16560Sstevel@tonic-gate	my $rules;
16570Sstevel@tonic-gate	my $name;
16580Sstevel@tonic-gate	my $values;
16590Sstevel@tonic-gate	my $value;
16600Sstevel@tonic-gate	my $valuestring;
16610Sstevel@tonic-gate	my $ret = 0;
16620Sstevel@tonic-gate	my @err;
16630Sstevel@tonic-gate	my $priv;
16640Sstevel@tonic-gate	my $val;
16650Sstevel@tonic-gate	my @actions;
16660Sstevel@tonic-gate	my $action;
16670Sstevel@tonic-gate	my $signal;
16680Sstevel@tonic-gate	my $sigstring;	# Full signal string on right hand of signal=SIGXXX.
16690Sstevel@tonic-gate	my $signame;	# Signal number or XXX part of SIGXXX.
16700Sstevel@tonic-gate	my $siglist;
16710Sstevel@tonic-gate	my $nonecount;
16720Sstevel@tonic-gate	my $denycount;
16730Sstevel@tonic-gate	my $sigcount;
16740Sstevel@tonic-gate
16750Sstevel@tonic-gate	$name = $rctl->{'name'};
16760Sstevel@tonic-gate	$values = $rctl->{'values'};
16770Sstevel@tonic-gate
16780Sstevel@tonic-gate	#
16790Sstevel@tonic-gate	# Get the default rules for all rctls, and the specific rules for
16800Sstevel@tonic-gate	# this rctl.
16810Sstevel@tonic-gate	#
16820Sstevel@tonic-gate	$allrules = $RctlRules{'__DEFAULT__'};
16830Sstevel@tonic-gate	$rules = $RctlRules{$name};
16840Sstevel@tonic-gate
16850Sstevel@tonic-gate	if (!defined($rules) || !ref($rules)) {
16860Sstevel@tonic-gate		$rules = $allrules;
16870Sstevel@tonic-gate	}
16880Sstevel@tonic-gate
16890Sstevel@tonic-gate	# Allow for no rctl values on rctl.
16900Sstevel@tonic-gate	if (!defined($values)) {
16910Sstevel@tonic-gate		return (0, \@err);
16920Sstevel@tonic-gate	}
16930Sstevel@tonic-gate
16940Sstevel@tonic-gate	# If values exist, make sure it is a list.
16950Sstevel@tonic-gate	if (!ref($values)) {
16960Sstevel@tonic-gate
16970Sstevel@tonic-gate		push(@err, [3, gettext(
16980Sstevel@tonic-gate		    'rctl "%s" missing value'), $name]);
16990Sstevel@tonic-gate		return (1, \@err);
17000Sstevel@tonic-gate	}
17010Sstevel@tonic-gate
17020Sstevel@tonic-gate	foreach $value (@$values) {
17030Sstevel@tonic-gate
17040Sstevel@tonic-gate		# Each value should be a list.
17050Sstevel@tonic-gate
17060Sstevel@tonic-gate		if (!ref($value)) {
17070Sstevel@tonic-gate			$ret = 1;
17080Sstevel@tonic-gate			push(@err, [3, gettext(
17090Sstevel@tonic-gate			    'rctl "%s" value "%s" should be in ()\'s'),
17100Sstevel@tonic-gate				     $name, $value]);
17110Sstevel@tonic-gate
17120Sstevel@tonic-gate			next;
17130Sstevel@tonic-gate		}
17140Sstevel@tonic-gate
17150Sstevel@tonic-gate		($priv, $val, @actions) = @$value;
17160Sstevel@tonic-gate		if (!@actions) {
17170Sstevel@tonic-gate			$ret = 1;
17180Sstevel@tonic-gate			$valuestring = projent_values2string([$value]);
17190Sstevel@tonic-gate			push(@err, [3, gettext(
17200Sstevel@tonic-gate			    'rctl "%s" value missing action "%s"'),
17210Sstevel@tonic-gate			    $name, $valuestring]);
17220Sstevel@tonic-gate		}
17230Sstevel@tonic-gate
17240Sstevel@tonic-gate		if (!defined($priv)) {
17250Sstevel@tonic-gate			$ret = 1;
17260Sstevel@tonic-gate			push(@err, [3, gettext(
17270Sstevel@tonic-gate			    'rctl "%s" value missing privilege "%s"'),
17280Sstevel@tonic-gate			    $name, $valuestring]);
17290Sstevel@tonic-gate
17300Sstevel@tonic-gate		} elsif (ref($priv)) {
17310Sstevel@tonic-gate			$ret = 1;
17320Sstevel@tonic-gate			$valuestring = projent_values2string([$priv]);
17330Sstevel@tonic-gate			push(@err, [3, gettext(
17340Sstevel@tonic-gate			    'rctl "%s" invalid privilege "%s"'),
17350Sstevel@tonic-gate				     $name, $valuestring]);
17360Sstevel@tonic-gate
17370Sstevel@tonic-gate		} else {
17380Sstevel@tonic-gate			if (!(grep /^$priv$/, @{$allrules->{'privs'}})) {
17390Sstevel@tonic-gate
17400Sstevel@tonic-gate				$ret = 1;
17410Sstevel@tonic-gate				push(@err, [3, gettext(
17420Sstevel@tonic-gate			            'rctl "%s" unknown privilege "%s"'),
17430Sstevel@tonic-gate				    $name, $priv]);
17440Sstevel@tonic-gate
17450Sstevel@tonic-gate			} elsif (!(grep /^$priv$/, @{$rules->{'privs'}})) {
17460Sstevel@tonic-gate
17470Sstevel@tonic-gate				$ret = 1;
17480Sstevel@tonic-gate				push(@err, [3, gettext(
17490Sstevel@tonic-gate				    'rctl "%s" privilege not allowed '.
17500Sstevel@tonic-gate				    '"%s"'), $name, $priv]);
17510Sstevel@tonic-gate			}
17520Sstevel@tonic-gate		}
17530Sstevel@tonic-gate		if (!defined($val)) {
17540Sstevel@tonic-gate			$ret = 1;
17550Sstevel@tonic-gate			push(@err, [3, gettext(
17560Sstevel@tonic-gate			    'rctl "%s" missing value'), $name]);
17570Sstevel@tonic-gate
17580Sstevel@tonic-gate		} elsif (ref($val)) {
17590Sstevel@tonic-gate			$ret = 1;
17600Sstevel@tonic-gate			$valuestring = projent_values2string([$val]);
17610Sstevel@tonic-gate			push(@err, [3, gettext(
17620Sstevel@tonic-gate			    'rctl "%s" invalid value "%s"'),
17630Sstevel@tonic-gate				     $name, $valuestring]);
17640Sstevel@tonic-gate
17650Sstevel@tonic-gate		} else {
17660Sstevel@tonic-gate			if ($val !~ /^\d+$/) {
17670Sstevel@tonic-gate				$ret = 1;
17680Sstevel@tonic-gate				push(@err, [3, gettext(
17690Sstevel@tonic-gate				    'rctl "%s" value "%s" is not '.
17700Sstevel@tonic-gate				    'an integer'), $name, $val]);
17710Sstevel@tonic-gate
17720Sstevel@tonic-gate			} elsif ($val > $rules->{'max'}) {
17730Sstevel@tonic-gate				$ret = 1;
17740Sstevel@tonic-gate				push(@err, [3, gettext(
17750Sstevel@tonic-gate				    'rctl "%s" value "%s" exceeds '.
17760Sstevel@tonic-gate				    'system limit'), $name, $val]);
17770Sstevel@tonic-gate			}
17780Sstevel@tonic-gate		}
17790Sstevel@tonic-gate		$nonecount = 0;
17800Sstevel@tonic-gate		$denycount = 0;
17810Sstevel@tonic-gate		$sigcount = 0;
17820Sstevel@tonic-gate
17830Sstevel@tonic-gate		foreach $action (@actions) {
17840Sstevel@tonic-gate
17850Sstevel@tonic-gate			if (ref($action)) {
17860Sstevel@tonic-gate				$ret = 1;
17870Sstevel@tonic-gate				$valuestring =
17880Sstevel@tonic-gate				    projent_values2string([$action]);
17890Sstevel@tonic-gate				push(@err, [3, gettext(
17900Sstevel@tonic-gate				    'rctl "%s" invalid action "%s"'),
17910Sstevel@tonic-gate				     $name, $valuestring]);
17920Sstevel@tonic-gate
17930Sstevel@tonic-gate				next;
17940Sstevel@tonic-gate			}
17950Sstevel@tonic-gate
17960Sstevel@tonic-gate			if ($action =~ /^sig(nal)?(=.*)?$/) {
17970Sstevel@tonic-gate				$signal = $action;
17980Sstevel@tonic-gate				$action = 'sig';
17990Sstevel@tonic-gate			}
18000Sstevel@tonic-gate			if (!(grep /^$action$/, @{$allrules->{'actions'}})) {
18010Sstevel@tonic-gate
18020Sstevel@tonic-gate				$ret = 1;
18030Sstevel@tonic-gate				push(@err, [3, gettext(
18040Sstevel@tonic-gate				    'rctl "%s" unknown action "%s"'),
18050Sstevel@tonic-gate				    $name, $action]);
18060Sstevel@tonic-gate				next;
18070Sstevel@tonic-gate
18080Sstevel@tonic-gate			} elsif (!(grep /^$action$/, @{$rules->{'actions'}})) {
18090Sstevel@tonic-gate
18100Sstevel@tonic-gate				$ret = 1;
18110Sstevel@tonic-gate				push(@err, [3, gettext(
18120Sstevel@tonic-gate				    'rctl "%s" action not allowed "%s"'),
18130Sstevel@tonic-gate				    $name, $action]);
18140Sstevel@tonic-gate				next;
18150Sstevel@tonic-gate			}
18160Sstevel@tonic-gate
18170Sstevel@tonic-gate			if ($action eq 'none') {
18180Sstevel@tonic-gate				if ($nonecount >= 1) {
18190Sstevel@tonic-gate
18200Sstevel@tonic-gate					$ret = 1;
18210Sstevel@tonic-gate					push(@err, [3, gettext(
18220Sstevel@tonic-gate				    	    'rctl "%s" duplicate action '.
18230Sstevel@tonic-gate					    'none'), $name]);
18240Sstevel@tonic-gate				}
18250Sstevel@tonic-gate				$nonecount++;
18260Sstevel@tonic-gate				next;
18270Sstevel@tonic-gate			}
18280Sstevel@tonic-gate			if ($action eq 'deny') {
18290Sstevel@tonic-gate				if ($denycount >= 1) {
18300Sstevel@tonic-gate
18310Sstevel@tonic-gate					$ret = 1;
18320Sstevel@tonic-gate					push(@err, [3, gettext(
18330Sstevel@tonic-gate				    	    'rctl "%s" duplicate action '.
18340Sstevel@tonic-gate					    'deny'), $name]);
18350Sstevel@tonic-gate				}
18360Sstevel@tonic-gate				$denycount++;
18370Sstevel@tonic-gate				next;
18380Sstevel@tonic-gate			}
18390Sstevel@tonic-gate
18400Sstevel@tonic-gate			# action must be signal
18410Sstevel@tonic-gate			if ($sigcount >= 1) {
18420Sstevel@tonic-gate
18430Sstevel@tonic-gate				$ret = 1;
18440Sstevel@tonic-gate				push(@err, [3, gettext(
18450Sstevel@tonic-gate			    	    'rctl "%s" duplicate action sig'),
18460Sstevel@tonic-gate			    	    $name]);
18470Sstevel@tonic-gate			}
18480Sstevel@tonic-gate			$sigcount++;
18490Sstevel@tonic-gate
18500Sstevel@tonic-gate			#
18510Sstevel@tonic-gate			# Make sure signal is correct format, one of:
18520Sstevel@tonic-gate			# sig=##
18530Sstevel@tonic-gate			# signal=##
18540Sstevel@tonic-gate			# sig=SIGXXX
18550Sstevel@tonic-gate			# signal=SIGXXX
18560Sstevel@tonic-gate			# sig=XXX
18570Sstevel@tonic-gate			# signal=SIGXXX
18580Sstevel@tonic-gate			#
18590Sstevel@tonic-gate			($sigstring) = $signal =~
18600Sstevel@tonic-gate			    /^
18610Sstevel@tonic-gate				 (?:signal|sig)=
18620Sstevel@tonic-gate				     (\d+|
18630Sstevel@tonic-gate				     (?:SIG)?[[:upper:]]+(?:[+-][123])?
18640Sstevel@tonic-gate				 )
18650Sstevel@tonic-gate			     $/x;
18660Sstevel@tonic-gate
18670Sstevel@tonic-gate			if (!defined($sigstring)) {
18680Sstevel@tonic-gate				$ret = 1;
18690Sstevel@tonic-gate				push(@err, [3, gettext(
18700Sstevel@tonic-gate				    'rctl "%s" invalid signal "%s"'),
18710Sstevel@tonic-gate				    $name, $signal]);
18720Sstevel@tonic-gate				next;
18730Sstevel@tonic-gate			}
18740Sstevel@tonic-gate
18750Sstevel@tonic-gate			$signame = $sigstring;
18760Sstevel@tonic-gate			$signame =~ s/SIG//;
18770Sstevel@tonic-gate
18780Sstevel@tonic-gate			# Make sure specific signal is allowed.
18790Sstevel@tonic-gate			$siglist = $allrules->{'signals'};
18800Sstevel@tonic-gate			if (!(grep /^$signame$/, @$siglist)) {
18810Sstevel@tonic-gate				$ret = 1;
18820Sstevel@tonic-gate				push(@err, [3, gettext(
18830Sstevel@tonic-gate				    'rctl "%s" invalid signal "%s"'),
18840Sstevel@tonic-gate				    $name, $signal]);
18850Sstevel@tonic-gate				next;
18860Sstevel@tonic-gate			}
18870Sstevel@tonic-gate			$siglist = $rules->{'signals'};
18880Sstevel@tonic-gate
18890Sstevel@tonic-gate			if (!(grep /^$signame$/, @$siglist)) {
18900Sstevel@tonic-gate				$ret = 1;
18910Sstevel@tonic-gate				push(@err, [3, gettext(
18920Sstevel@tonic-gate				    'rctl "%s" signal not allowed "%s"'),
18930Sstevel@tonic-gate				    $name, $signal]);
18940Sstevel@tonic-gate				next;
18950Sstevel@tonic-gate			}
18960Sstevel@tonic-gate		}
18970Sstevel@tonic-gate
18980Sstevel@tonic-gate		if ($nonecount && ($denycount || $sigcount)) {
18990Sstevel@tonic-gate			$ret = 1;
19000Sstevel@tonic-gate			push(@err, [3, gettext(
19010Sstevel@tonic-gate			    'rctl "%s" action "none" specified with '.
19020Sstevel@tonic-gate			    'other actions'), $name]);
19030Sstevel@tonic-gate		}
19040Sstevel@tonic-gate	}
19050Sstevel@tonic-gate
19060Sstevel@tonic-gate	if (@err) {
19070Sstevel@tonic-gate		return ($ret, \@err);
19080Sstevel@tonic-gate	} else {
19090Sstevel@tonic-gate	    return ($ret, \@err);
19100Sstevel@tonic-gate	}
19110Sstevel@tonic-gate}
19120Sstevel@tonic-gate
19130Sstevel@tonic-gate1;
1914