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