3900 illumos will not build against gcc compiled perl
[illumos-gate.git] / usr / src / cmd / perl / contrib / Sun / Solaris / Project / Project.pm
blobaaeff1a450737577c7fbbbaa4ba1a2ff465acf6d
2 # Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
3 # Copyright (c) 2014 Racktop Systems.
7 # Project.pm provides the bootstrap for the Sun::Solaris::Project module, and
8 # also functions for reading, validating and writing out project(4) format
9 # files.
11 ################################################################################
12 require 5.0010;
14 use strict;
15 use warnings;
16 use locale;
17 use Errno;
18 use Fcntl;
19 use File::Basename;
20 use POSIX qw(locale_h limits_h);
22 package Sun::Solaris::Project;
24 our $VERSION = '1.9';
26 use XSLoader;
27 XSLoader::load(__PACKAGE__, $VERSION);
29 our (@EXPORT_OK, %EXPORT_TAGS);
30 my @constants = qw(MAXPROJID PROJNAME_MAX PROJF_PATH PROJECT_BUFSZ
31 SETPROJ_ERR_TASK SETPROJ_ERR_POOL);
32 my @syscalls = qw(getprojid);
33 my @libcalls = qw(setproject activeprojects getprojent setprojent endprojent
34 getprojbyname getprojbyid getdefaultproj fgetprojent inproj
35 getprojidbyname);
36 my @private = qw(projf_read projf_write projf_validate projent_parse
37 projent_parse_name projent_validate_unique_name
38 projent_parse_projid projent_validate_unique_id
39 projent_parse_comment
40 projent_parse_users
41 projent_parse_groups
42 projent_parse_attributes
43 projent_validate projent_validate_projid
44 projent_values_equal projent_values2string);
46 @EXPORT_OK = (@constants, @syscalls, @libcalls, @private);
47 %EXPORT_TAGS = (CONSTANTS => \@constants, SYSCALLS => \@syscalls,
48 LIBCALLS => \@libcalls, PRIVATE => \@private, ALL => \@EXPORT_OK);
50 use base qw(Exporter);
51 use Sun::Solaris::Utils qw(gettext);
54 # Set up default rules for validating rctls.
55 # These rules are not global-flag specific, but instead
56 # are the total set of allowable values on all rctls.
58 use Config;
59 our $MaxNum = &RCTL_MAX_VALUE;
60 our %RctlRules;
62 my %rules;
63 our %SigNo;
64 my $j;
65 my $name;
66 foreach $name (split(' ', $Config{sig_name})) {
67 $SigNo{$name} = $j;
68 $j++;
70 %rules = (
71 'privs' => [ qw(basic privileged priv) ],
72 'actions' => [ qw(none deny sig) ],
73 'signals' => [ qw(ABRT XRES HUP STOP TERM KILL XFSZ XCPU),
74 $SigNo{'ABRT'},
75 $SigNo{'XRES'},
76 $SigNo{'HUP'},
77 $SigNo{'STOP'},
78 $SigNo{'TERM'},
79 $SigNo{'KILL'},
80 $SigNo{'XFSZ'},
81 $SigNo{'XCPU'} ],
82 'max' => $MaxNum
85 $RctlRules{'__DEFAULT__'} = \%rules;
88 # projf_combine_errors(errorA, errorlistB)
90 # Concatenates a single error with a list of errors. Each error in the new
91 # list will have a status matching the status of errorA.
93 # Example:
95 # projf_combine_errors(
96 # [ 5, "Error on line %d, 10 ],
97 # [ [ 3, "Invalid Value %s", "foo" ],
98 # [ 6, "Duplicate Value %s", "bar" ]
99 # ]);
101 # would return the list ref:
103 # [ [ 5, "Error on line %d: Invalid Value %s", 10, "foo" ],
104 # [ 5, "Error on line %d: Duplicate Value %s", 10, "bar" ]
107 # This function is used when a fuction wants to add more information to
108 # a list of errors returned by another function.
110 sub projf_combine_errors
113 my ($error1, $errorlist) = @_;
114 my $error2;
116 my $newerror;
117 my @newerrorlist;
119 my ($err1, $fmt1, @args1);
120 my ($err2, $fmt2, @args2);
122 ($err1, $fmt1, @args1) = @$error1;
123 foreach $error2 (@$errorlist) {
125 ($err2, $fmt2, @args2) = @$error2;
126 $newerror = [ $err1, $fmt1 . ', ' . $fmt2, @args1, @args2];
127 push(@newerrorlist, $newerror);
129 return (\@newerrorlist);
133 # projf_read(filename, flags)
135 # Reads and parses a project(4) file, and returns a list of projent hashes.
137 # Inputs:
138 # filename - file to read
139 # flags - hash ref of flags
141 # If flags contains key "validate", the project file entries will also be
142 # validated for run-time correctness If so, the flags ref is forwarded to
143 # projf_validate().
145 # Return Value:
147 # Returns a ref to a list of projent hashes. See projent_parse() for a
148 # description of a projent hash.
150 sub projf_read
153 my ($fh, $flags) = @_;
154 my @projents;
155 my $projent;
156 my $linenum = 0;
157 my ($projname, $projid, $comment, $users, $groups, $attributes);
158 my ($ret, $ref);
159 my @errs;
161 my ($line, $origline, $next, @projf);
162 while (defined($line = <$fh>)) {
164 $linenum++;
165 $origline = $line;
167 # Remove any line continuations and trailing newline.
168 $line =~ s/\\\n//g;
169 chomp($line);
172 if (length($line) > (&PROJECT_BUFSZ - 2)) {
173 push(@errs,
175 gettext('Parse error on line %d, line too long'),
176 $linenum]);
180 ($ret, $ref) = projent_parse($line, {});
181 if ($ret != 0) {
182 $ref = projf_combine_errors(
183 [5, gettext('Parse error on line %d'), $linenum],
184 $ref);
185 push(@errs, @$ref);
186 next;
189 $projent = $ref;
192 # Cache original line to save original format if it is
193 # not changed.
195 $projent->{'line'} = $origline;
196 $projent->{'modified'} = 'false';
197 $projent->{'linenum'} = $linenum;
199 push(@projents, $projent);
202 if (defined($flags->{'validate'}) && ($flags->{'validate'} eq 'true')) {
203 ($ret, $ref) = projf_validate(\@projents, $flags);
204 if ($ret != 0) {
205 push(@errs, @$ref);
209 if (@errs) {
210 return (1, \@errs);
212 } else {
213 return (0, \@projents);
218 # projf_write(filehandle, projent list)
220 # Write a list of projent hashes to a file handle.
221 # projent's with key "modified" => false will be
222 # written using the "line" key. projent's with
223 # key "modified" => "true" will be written by
224 # constructing a new line based on their "name"
225 # "projid", "comment", "userlist", "grouplist"
226 # and "attributelist" keys.
228 sub projf_write
230 my ($fh, $projents) = @_;
231 my $projent;
232 my $string;
234 foreach $projent (@$projents) {
236 if ($projent->{'modified'} eq 'false') {
237 $string = $projent->{'line'};
238 } else {
239 $string = projent_2string($projent) . "\n";
241 print $fh "$string";
246 # projent_parse(line)
248 # Functions for parsing the project file lines into projent hashes.
250 # Returns a number and a ref, one of:
252 # (0, ref to projent hash)
253 # (non-zero, ref to list of errors)
255 # Flag can be:
256 # allowspaces: allow spaces between user and group names.
257 # allowunits : allow units (K, M, etc), on rctl values.
259 # A projent hash contains the keys:
261 # "name" - string name of project
262 # "projid" - numeric id of project
263 # "comment" - comment string
264 # "users" - , seperated user list string
265 # "userlist" - list ref to list of user name strings
266 # "groups" - , seperated group list string
267 # "grouplist" - list ref to liset of group name strings
268 # "attributes" - ; seperated attribute list string
269 # "attributelist" - list ref to list of attribute refs
270 # (see projent_parse_attributes() for attribute ref)
272 sub projent_parse
275 my ($line, $flags) = @_;
276 my $projent = {};
277 my ($ret, $ref);
278 my @errs;
279 my ($projname, $projid, $comment, $users, $groups, $attributes);
282 # Split fields of project line. split() is not used because
283 # we must enforce that there are 6 fields.
285 ($projname, $projid, $comment, $users, $groups, $attributes) =
286 $line =~
287 /^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*)$/;
289 # If there is not a complete match, nothing will be defined;
290 if (!defined($projname)) {
291 push(@errs, [5, gettext(
292 'Incorrect number of fields. Should have 5 ":"\'s.')]);
294 # Get as many fields as we can.
295 ($projname, $projid, $comment, $users, $groups, $attributes) =
296 split(/:/, $line);
299 if (defined($projname)) {
300 $projent->{'name'} = $projname;
301 ($ret, $ref) = projent_parse_name($projname);
302 if ($ret != 0) {
303 push(@errs, @$ref);
306 if (defined($projid)) {
307 $projent->{'projid'} = $projid;
308 ($ret, $ref) = projent_parse_projid($projid);
309 if ($ret != 0) {
310 push(@errs, @$ref);
313 if (defined($comment)) {
314 $projent->{'comment'} = $comment;
315 ($ret, $ref) = projent_parse_comment($comment);
316 if ($ret != 0) {
317 push(@errs, @$ref);
320 if (defined($users)) {
321 $projent->{'users'} = $users;
322 ($ret, $ref) = projent_parse_users($users, $flags);
323 if ($ret != 0) {
324 push(@errs, @$ref);
325 } else {
326 $projent->{'userlist'} = $ref;
329 if (defined($groups)) {
330 $projent->{'groups'} = $groups;
331 ($ret, $ref) = projent_parse_groups($groups, $flags);
332 if ($ret != 0) {
333 push(@errs, @$ref);
334 } else {
335 $projent->{'grouplist'} = $ref;
338 if (defined($attributes)) {
339 $projent->{'attributes'} = $attributes;
340 ($ret, $ref) = projent_parse_attributes($attributes, $flags);
341 if ($ret != 0) {
342 push(@errs, @$ref);
343 } else {
344 $projent->{'attributelist'} = $ref;
348 if (@errs) {
349 return (1, \@errs);
351 } else {
352 return (0, $projent);
357 # Project name syntax checking.
359 sub projent_parse_name
361 my @err;
362 my ($projname) = @_;
364 if (!($projname =~ /^[[:alpha:]][[:alnum:]_.-]*$/)) {
365 push(@err, ([3, gettext(
366 'Invalid project name "%s", contains invalid characters'),
367 $projname]));
368 return (1, \@err);
370 if (length($projname) > &PROJNAME_MAX) {
371 push(@err, ([3, gettext(
372 'Invalid project name "%s", name too long'),
373 $projname]));
374 return (1, \@err);
376 return (0, $projname);
380 # Projid syntax checking.
382 sub projent_parse_projid
384 my @err;
385 my ($projid) = @_;
387 # verify projid is a positive number, and less than UID_MAX
388 if (!($projid =~ /^\d+$/)) {
389 push(@err, [3, gettext('Invalid projid "%s"'),
390 $projid]);
391 return (1, \@err);
393 } elsif ($projid > POSIX::INT_MAX) {
394 push(@err, [3, gettext('Invalid projid "%s": must be <= '.
395 POSIX::INT_MAX),
396 $projid]);
397 return (1, \@err);
399 } else {
400 return (0, $projid);
405 # Project comment syntax checking.
407 sub projent_parse_comment
409 my ($comment) = @_;
411 # no restrictions on comments
412 return (0, $comment);
416 # projent_parse_users(string, flags)
418 # Parses "," seperated list of users, and returns list ref to a list of
419 # user names. If flags contains key "allowspaces", then spaces are
420 # allowed between user names and ","'s.
422 sub projent_parse_users
424 my ($users, $flags) = @_;
425 my @err;
426 my $user;
427 my $pattern;
428 my @userlist;
430 if (exists($flags->{'allowspaces'})) {
431 $pattern = '\s*,\s*';
432 } else {
433 $pattern = ',';
435 @userlist = split(/$pattern/, $users);
437 # Return empty list if there are no users.
438 if (!(@userlist)) {
439 return (0, \@userlist);
442 # Verify each user name is the correct format for a valid user name.
443 foreach $user (@userlist) {
445 # Allow for wildcards.
446 if ($user eq '*' || $user eq '!*') {
447 next;
450 # Allow for ! operator, usernames must begin with alpha-num,
451 # and contain alpha-num, '_', digits, '.', or '-'.
452 if (!($user =~ /^!?[[:alpha:]][[:alnum:]_.-]*$/)) {
453 push(@err, [3, gettext('Invalid user name "%s"'),
454 $user]);
455 next;
458 if (@err) {
459 return (1,\ @err);
460 } else {
461 return (0, \@userlist);
466 # projent_parse_groups(string, flags)
468 # Parses "," seperated list of groups, and returns list ref to a list of
469 # groups names. If flags contains key "allowspaces", then spaces are
470 # allowed between group names and ","'s.
472 sub projent_parse_groups
474 my ($groups, $flags) = @_;
475 my @err;
476 my $group;
477 my $pattern;
479 my @grouplist;
481 if (exists($flags->{'allowspaces'})) {
482 $pattern = '\s*,\s*';
483 } else {
484 $pattern = ',';
486 @grouplist = split(/$pattern/, $groups);
488 # Return empty list if there are no groups.
489 if (!(@grouplist)) {
490 return (0, \@grouplist);
493 # Verify each group is the correct format for a valid group name.
494 foreach $group (@grouplist) {
496 # Allow for wildcards.
497 if ($group eq '*' || $group eq '!*') {
498 next;
501 # Allow for ! operator, groupnames can contain only alpha
502 # characters and digits.
503 if (!($group =~ /^!?[[:alnum:]]+$/)) {
504 push(@err, [3, gettext('Invalid group name "%s"'),
505 $group]);
506 next;
510 if (@err) {
511 return (1,\ @err);
512 } else {
513 return (0, \@grouplist);
518 # projent_tokenize_attribute_values(values)
520 # Values is the right hand side of a name=values attribute/values pair.
521 # This function splits the values string into a list of tokens. Tokens are
522 # valid string values and the characters ( ) ,
524 sub projent_tokenize_attribute_values
527 # This seperates the attribute string into higher level tokens
528 # for parsing.
530 my $prev;
531 my $cur;
532 my $next;
533 my $token;
534 my @tokens;
535 my @newtokens;
536 my @err;
538 # Seperate tokens delimited by "(", ")", and ",".
539 @tokens = split(/([,()])/, $_[0], -1);
541 # Get rid of blanks
542 @newtokens = grep($_ ne '', @tokens);
544 foreach $token (@newtokens) {
545 if (!($token =~ /^[(),]$/ ||
546 $token =~ /^[[:alnum:]_.\/=+-]*$/)) {
547 push(@err, [3, gettext(
548 'Invalid Character at or near "%s"'), $token]);
551 if (@err) {
552 return (1, \@err);
553 } else {
554 return (0, \@newtokens);
559 # projent_parse_attribute_values(values)
561 # Values is the right hand side of a name=values attribute/values pair.
562 # This function parses the values string into a list of values. Each value
563 # can be either a scalar value, or a ref to another list of values.
564 # A ref to the list of values is returned.
566 sub projent_parse_attribute_values
569 # For some reason attribute values can be lists of values and
570 # sublists, which are scoped using ()'s. All values and sublists
571 # are delimited by ","'s. Empty values are lists are permitted.
573 # This function returns a reference to a list of values, each of
574 # which can be a scalar value, or a reference to a sublist. Sublists
575 # can contain both scalar values and references to furthur sublists.
577 my ($values) = @_;
578 my $tokens;
579 my @usedtokens;
580 my $token;
581 my $prev = '';
582 my $parendepth = 0;
583 my @valuestack;
584 my @err;
585 my ($ret, $ref);
586 my $line;
588 push (@valuestack, []);
590 ($ret, $ref) = projent_tokenize_attribute_values($values);
591 if ($ret != 0) {
592 return ($ret, $ref);
594 $tokens = $ref;
596 foreach $token (@$tokens) {
598 push(@usedtokens, $token);
600 if ($token eq ',') {
602 if ($prev eq ',' || $prev eq '(' ||
603 $prev eq '') {
604 push(@{$valuestack[$#valuestack]}, '');
606 $prev = ',';
607 next;
609 if ($token eq '(') {
611 if (!($prev eq '(' || $prev eq ',' ||
612 $prev eq '')) {
614 $line = join('', @usedtokens);
615 push(@err, [3, gettext(
616 '"%s" <- "(" unexpected'),
617 $line]);
619 return (1, \@err);
622 $parendepth++;
623 my $arrayref = [];
624 push(@{$valuestack[$#valuestack]}, $arrayref);
625 push(@valuestack, $arrayref);
627 $prev = '(';
628 next;
630 if ($token eq ')') {
632 if ($parendepth <= 0) {
634 $line = join('', @usedtokens);
635 push(@err, [3, gettext(
636 '"%s" <- ")" unexpected'),
637 $line]);
639 return (1, \@err);
642 if ($prev eq ',' || $prev eq '(') {
643 push(@{$valuestack[$#valuestack]}, '');
645 $parendepth--;
646 pop @valuestack;
648 $prev = ')';
649 next;
652 if (!($prev eq ',' || $prev eq '(' || $prev eq '')) {
653 $line = join('', @usedtokens);
654 push(@err, [3, gettext(
655 '"%s" <- "%s" unexpected'),
656 $line, $token]);
658 return (1, \@err);
661 push(@{$valuestack[$#valuestack]}, $token);
662 $prev = $token;
663 next;
666 if ($parendepth != 0) {
667 push(@err, [3, gettext(
668 '"%s" <- ")" missing'),
669 $values]);
670 return (1, \@err);
673 if ($prev eq ',' || $prev eq '') {
674 push(@{$valuestack[$#valuestack]}, '');
677 return (0, $valuestack[0]);
681 # projent_parse_attribute("name=values", $flags)
683 # $flags is a hash ref.
684 # Valid flags keys:
685 # 'allowunits' - allows numeric values to be scaled on certain attributes
687 # Returns a hash ref with keys:
689 # "name" - name of attribute
690 # "values" - ref to list of values.
691 # Each value can be a scalar value, or a ref to
692 # a sub-list of values.
694 sub projent_parse_attribute
696 my ($string, $flags) = @_;
697 my $attribute = {};
698 my ($name, $stock, $values);
699 my ($ret, $ref);
700 my @err;
701 my $scale;
702 my $num;
703 my $modifier;
704 my $unit;
705 my $tuple;
706 my $rules;
707 my $rctlmax;
708 my $rctlflags;
710 # pattern for matching stock symbols.
711 my $stockp = '[[:upper:]]{1,5}(?:.[[:upper:]]{1,5})?,';
712 # Match attribute with no value.
713 ($name, $stock) = $string =~
714 /^(($stockp)?[[:alpha:]][[:alnum:]_.-]*)$/;
715 if ($name) {
716 $attribute->{'name'} = $name;
717 return (0, $attribute);
720 # Match attribute with value list.
721 ($name, $stock, $values) = $string =~
722 /^(($stockp)?[[:alpha:]][[:alnum:]_.-]*)=(.*)$/;
723 if ($name) {
724 $attribute->{'name'} = $name;
726 if (!defined($values)) {
727 $values = '';
730 ($ret, $ref) = projent_parse_attribute_values($values);
731 if ($ret != 0) {
732 $ref = projf_combine_errors(
734 gettext('Invalid value on attribute "%s"'),
735 $name], $ref);
736 push(@err, @$ref);
737 return ($ret, \@err)
740 # Scale attributes than can be scaled.
741 if (exists($flags->{"allowunits"})) {
743 if ($name eq 'rcap.max-rss' &&
744 defined($ref->[0]) && !ref($ref->[0])) {
745 $scale = 'bytes';
747 ($num, $modifier, $unit) =
748 projent_val2num($ref->[0], $scale);
750 if (!defined($num)) {
752 if (defined($unit)) {
753 push(@err, [3, gettext(
754 'rcap.max-rss has invalid '.
755 'unit "%s"'), $unit]);
756 } else {
757 push(@err, [3, gettext(
758 'rcap.max-rss has invalid '.
759 'value "%s"'), $ref->[0]]);
761 } elsif ($num eq "OVERFLOW") {
762 push(@err, [3, gettext( 'rcap.max-rss value '.
763 '"%s" exceeds maximum value "%s"'),
764 $ref->[0], $MaxNum]);
765 } else {
766 $ref->[0] = $num;
769 # Check hashed cache of rctl rules.
770 $rules = $RctlRules{$name};
771 if (!defined($rules)) {
773 # See if this is an resource control name, if so
774 # cache rules.
776 ($rctlmax, $rctlflags) = rctl_get_info($name);
777 if (defined($rctlmax)) {
778 $rules = proj_getrctlrules(
779 $rctlmax, $rctlflags);
780 if (defined($rules)) {
781 $RctlRules{$name} = $rules;
782 } else {
783 $RctlRules{$name} =
784 "NOT AN RCTL";
789 # Scale values if this is an rctl.
790 if (defined ($rules) && ref($rules)) {
791 $flags->{'type'} = $rules->{'type'};
792 foreach $tuple (@$ref) {
794 # Skip if tuple this is not a list.
795 if (!ref($tuple)) {
796 next;
798 # Skip if second element is not scalar.
799 if (!defined($tuple->[1]) ||
800 ref($tuple->[1])) {
801 next;
803 ($num, $modifier, $unit) =
804 projent_val2num($tuple->[1],
805 $flags->{'type'});
807 if (!defined($num)) {
809 if (defined($unit)) {
810 push(@err, [3, gettext(
811 'rctl %s has '.
812 'invalid unit '.
813 '"%s"'),$name,
814 $unit]);
815 } else {
816 push(@err, [3, gettext(
817 'rctl %s has '.
818 'invalid value '.
819 '"%s"'), $name,
820 $tuple->[1]]);
822 } elsif ($num eq "OVERFLOW") {
823 push(@err, [3, gettext(
824 'rctl %s value "%s" '.
825 'exceeds maximum value "%s"'),
826 $name, $tuple->[1], $MaxNum]);
827 } else {
828 $tuple->[1] = $num;
833 $attribute->{'values'} = $ref;
834 if (@err) {
835 return (1, \@err);
836 } else {
837 return (0, $attribute);
840 } else {
841 # Attribute did not match name[=value,value...]
842 push(@err, [3, gettext('Invalid attribute "%s"'), $string]);
843 return (1, \@err);
848 # projent_parse_attributes("; seperated list of name=values pairs");
850 # Returns a list of attribute references, as returned by
851 # projent_parse_attribute().
853 sub projent_parse_attributes
855 my ($attributes, $flags) = @_;
856 my @attributelist;
857 my @attributestrings;
858 my $attributestring;
859 my $attribute;
860 my ($ret, $ref);
861 my @errs;
863 # Split up attributes by ";"'s.
864 @attributestrings = split(/;/, $attributes);
866 # If no attributes, return empty list.
867 if (!@attributestrings) {
868 return (0, \@attributelist);
871 foreach $attributestring (@attributestrings) {
873 ($ret, $ref) = projent_parse_attribute($attributestring,
874 $flags);
875 if ($ret != 0) {
876 push(@errs, @$ref);
877 } else {
878 push(@attributelist, $ref);
882 if (@errs) {
883 return (1, \@errs);
884 } else {
885 return (0, \@attributelist);
891 # projent_values_equal(list A, list B)
893 # Given two references to lists of attribute values (as returned by
894 # projent_parse_attribute_values()), returns 1 if they are identical
895 # lists or 0 if they are not.
897 # XXX sub projent_values_equal;
898 sub projent_values_equal
900 my ($x, $y) = @_;
902 my $itema;
903 my $itemb;
904 my $index = 0;
906 if (ref($x) && ref($y)) {
908 if (scalar(@$x) != scalar(@$y)) {
909 return (0);
910 } else {
911 foreach $itema (@$x) {
913 $itemb = $y->[$index++];
915 if (!projent_values_equal($itema, $itemb)) {
916 return (0);
919 return (1);
921 } elsif ((!ref($x) && (!ref($y)))) {
922 return ($x eq $y);
923 } else {
924 return (0);
929 # Converts a list of values to a , seperated string, enclosing sublists
930 # in ()'s.
932 sub projent_values2string
934 my ($values) = @_;
935 my $string;
936 my $value;
937 my @valuelist;
939 if (!defined($values)) {
940 return ('');
942 if (!ref($values)) {
943 return ($values);
945 foreach $value (@$values) {
947 if (ref($value)) {
948 push(@valuelist,
949 '(' . projent_values2string($value) . ')');
950 } else {
951 push(@valuelist, $value);
955 $string = join(',', @valuelist) ;
956 if (!defined($string)) {
957 $string = '';
959 return ($string);
963 # Converts a ref to an attribute hash with keys "name", and "values" to
964 # a string in the form "name=value,value...".
966 sub projent_attribute2string
968 my ($attribute) = @_;
969 my $string;
971 $string = $attribute->{'name'};
973 if (ref($attribute->{'values'}) && @{$attribute->{'values'}}) {
974 $string = $string . '=' .
975 projent_values2string(($attribute->{'values'}));
977 return ($string);
981 # Converts a ref to a projent hash (as returned by projent_parse()) to
982 # a project(4) database entry line.
984 sub projent_2string
986 my ($projent) = @_;
987 my @attributestrings;
988 my $attribute;
990 foreach $attribute (@{$projent->{'attributelist'}}) {
991 push(@attributestrings, projent_attribute2string($attribute));
993 return (join(':', ($projent->{'name'},
994 $projent->{'projid'},
995 $projent->{'comment'},
996 join(',', @{$projent->{'userlist'}}),
997 join(',', @{$projent->{'grouplist'}}),
998 join(';', @attributestrings))));
1002 # projf_validate(ref to list of projents hashes, flags)
1004 # For each projent hash ref in the list, checks that users, groups, and pools
1005 # exists, and that known attributes are valid. Attributes matching rctl names
1006 # are verified to have valid values given that rctl's global flags and max
1007 # value.
1009 # Valid flag keys:
1011 # "res" - allow reserved project ids 0-99
1012 # "dup" - allow duplicate project ids
1014 sub projf_validate
1016 my ($projents, $flags) = @_;
1017 my $projent;
1018 my $ret;
1019 my $ref;
1020 my @err;
1021 my %idhash;
1022 my %namehash;
1023 my %seenids;
1024 my %seennames;
1026 # check for unique project names
1027 foreach $projent (@$projents) {
1029 my @lineerr;
1031 $seennames{$projent->{'name'}}++;
1032 $seenids{$projent->{'projid'}}++;
1034 if ($seennames{$projent->{'name'}} > 1) {
1035 push(@lineerr, [4, gettext(
1036 'Duplicate project name "%s"'),
1037 $projent->{'name'}]);
1040 if (!defined($flags->{'dup'})) {
1041 if ($seenids{$projent->{'projid'}} > 1) {
1042 push(@lineerr, [4, gettext(
1043 'Duplicate projid "%s"'),
1044 $projent->{'projid'}]);
1047 ($ret, $ref) = projent_validate($projent, $flags);
1048 if ($ret != 0) {
1049 push(@lineerr, @$ref);
1052 if (@lineerr) {
1054 $ref = projf_combine_errors([5, gettext(
1055 'Validation error on line %d'),
1056 $projent->{'linenum'}], \@lineerr);
1057 push(@err, @$ref);
1060 if (@err) {
1061 return (1, \@err);
1062 } else {
1063 return (0, $projents);
1068 # projent_validate_unique_id(
1069 # ref to projent hash, ref to list of projent hashes)
1071 # Verifies that projid of the projent hash only exists once in the list of
1072 # projent hashes.
1074 sub projent_validate_unique_id
1076 my ($projent, $projf, $idhash) = @_;
1077 my @err;
1078 my $ret = 0;
1079 my $projid = $projent->{'projid'};
1081 if (scalar(grep($_->{'projid'} eq $projid, @$projf)) > 1) {
1082 $ret = 1;
1083 push(@err, [4, gettext('Duplicate projid "%s"'),
1084 $projid]);
1087 return ($ret, \@err);
1091 # projent_validate_unique_id(
1092 # ref to projent hash, ref to list of projent hashes)
1094 # Verifies that project name of the projent hash only exists once in the list
1095 # of projent hashes.
1097 # If the seconds argument is a hash ref, it is treated
1099 sub projent_validate_unique_name
1101 my ($projent, $projf, $namehash) = @_;
1102 my $ret = 0;
1103 my @err;
1104 my $pname = $projent->{'name'};
1106 if (scalar(grep($_->{'name'} eq $pname, @$projf)) > 1) {
1107 $ret = 1;
1108 push(@err,
1109 [9, gettext('Duplicate project name "%s"'), $pname]);
1112 return ($ret, \@err);
1116 # projent_validate(ref to projents hash, flags)
1118 # Checks that users, groups, and pools exists, and that known attributes
1119 # are valid. Attributes matching rctl names are verified to have valid
1120 # values given that rctl's global flags and max value.
1122 # Valid flag keys:
1124 # "allowspaces" - user and group list are allowed to contain whitespace
1125 # "res" - allow reserved project ids 0-99
1127 sub projent_validate
1129 my ($projent, $flags) = @_;
1130 my $ret = 0;
1131 my $ref;
1132 my @err;
1134 ($ret, $ref) =
1135 projent_validate_name($projent->{'name'}, $flags);
1136 if ($ret != 0) {
1137 push(@err, @$ref);
1139 ($ret, $ref) =
1140 projent_validate_projid($projent->{'projid'}, $flags);
1141 if ($ret != 0) {
1142 push(@err, @$ref);
1144 ($ret, $ref) =
1145 projent_validate_comment($projent->{'comment'}, $flags);
1146 if ($ret != 0) {
1147 push(@err, @$ref);
1149 ($ret, $ref) =
1150 projent_validate_users($projent->{'userlist'}, $flags);
1151 if ($ret != 0) {
1152 push(@err, @$ref);
1154 ($ret, $ref) =
1155 projent_validate_groups($projent->{'grouplist'}, $flags);
1156 if ($ret != 0) {
1157 push(@err, @$ref);
1159 ($ret, $ref) = projent_validate_attributes(
1160 $projent->{'attributelist'}, $flags);
1161 if ($ret != 0) {
1162 push(@err, @$ref);
1165 my $string = projent_2string($projent);
1166 if (length($string) > (&PROJECT_BUFSZ - 2)) {
1167 push(@err, [3, gettext('projent line too long')]);
1170 if (@err) {
1171 return (1, \@err);
1172 } else {
1173 return (0, $projent);
1178 # projent_validate_name(name, flags)
1180 # does nothing, as any parse-able project name is valid
1182 sub projent_validate_name
1184 my ($name, $flags) = @_;
1185 my @err;
1187 return (0, \@err);
1192 # projent_validate_projid(projid, flags)
1194 # Validates that projid is within the valid range of numbers.
1195 # Valid flag keys:
1196 # "res" - allow reserved projid's 0-99
1198 sub projent_validate_projid
1200 my ($projid, $flags) = @_;
1201 my @err;
1202 my $ret = 0;
1203 my $minprojid;
1205 if (defined($flags->{'res'})) {
1206 $minprojid = 0;
1207 } else {
1208 $minprojid = 100;
1211 if ($projid < $minprojid) {
1213 $ret = 1;
1214 push(@err, [3, gettext('Invalid projid "%s": '.
1215 'must be >= 100'),
1216 $projid]);
1220 return ($ret, \@err);
1224 # projent_validate_comment(name, flags)
1226 # Does nothing, as any parse-able comment is valid.
1228 sub projent_validate_comment
1230 my ($comment, $flags) = @_;
1231 my @err;
1233 return (0, \@err);
1237 # projent_validate_users(ref to list of user names, flags)
1239 # Verifies that each username is either a valid glob, such
1240 # as * or !*, or is an existing user. flags is unused.
1241 # Also validates that there are no duplicates.
1243 sub projent_validate_users
1245 my ($users, $flags) = @_;
1246 my @err;
1247 my $ret = 0;
1248 my $user;
1249 my $username;
1251 foreach $user (@$users) {
1253 if ($user eq '*' || $user eq '!*') {
1254 next;
1256 $username = $user;
1257 $username =~ s/^!//;
1259 if (!defined(getpwnam($username))) {
1260 $ret = 1;
1261 push(@err, [6,
1262 gettext('User "%s" does not exist'),
1263 $username]);
1267 my %seen;
1268 my @dups = grep($seen{$_}++ == 1, @$users);
1269 if (@dups) {
1270 $ret = 1;
1271 push(@err, [3, gettext('Duplicate user names "%s"'),
1272 join(',', @dups)]);
1274 return ($ret, \@err)
1278 # projent_validate_groups(ref to list of group names, flags)
1280 # Verifies that each groupname is either a valid glob, such
1281 # as * or !*, or is an existing group. flags is unused.
1282 # Also validates that there are no duplicates.
1284 sub projent_validate_groups
1286 my ($groups, $flags) = @_;
1287 my @err;
1288 my $ret = 0;
1289 my $group;
1290 my $groupname;
1292 foreach $group (@$groups) {
1294 if ($group eq '*' || $group eq '!*') {
1295 next;
1298 $groupname = $group;
1299 $groupname =~ s/^!//;
1301 if (!defined(getgrnam($groupname))) {
1302 $ret = 1;
1303 push(@err, [6,
1304 gettext('Group "%s" does not exist'),
1305 $groupname]);
1309 my %seen;
1310 my @dups = grep($seen{$_}++ == 1, @$groups);
1311 if (@dups) {
1312 $ret = 1;
1313 push(@err, [3, gettext('Duplicate group names "%s"'),
1314 join(',', @dups)]);
1317 return ($ret, \@err)
1321 # projent_validate_attribute(attribute hash ref, flags)
1323 # Verifies that if the attribute's name is a known attribute or
1324 # resource control, that it contains a valid value.
1325 # flags is unused.
1327 sub projent_validate_attribute
1329 my ($attribute, $flags) = @_;
1330 my $name = $attribute->{'name'};
1331 my $values = $attribute->{'values'};
1332 my $value;
1333 my @errs;
1334 my $ret = 0;
1335 my $result;
1336 my $ref;
1338 if (defined($values)) {
1339 $value = $values->[0];
1341 if ($name eq 'task.final') {
1343 if (defined($values)) {
1344 $ret = 1;
1345 push(@errs, [3, gettext(
1346 'task.final should not have value')]);
1349 # Need to rcap.max-rss needs to be a number
1350 } elsif ($name eq 'rcap.max-rss') {
1352 if (!defined($values)) {
1353 $ret = 1;
1354 push(@errs, [3, gettext(
1355 'rcap.max-rss missing value')]);
1356 } elsif (scalar(@$values) != 1) {
1357 $ret = 1;
1358 push(@errs, [3, gettext(
1359 'rcap.max-rss should have single value')]);
1361 if (!defined($value) || ref($value)) {
1362 $ret = 1;
1363 push(@errs, [3, gettext(
1364 'rcap.max-rss has invalid value "%s"'),
1365 projent_values2string($values)]);;
1366 } elsif ($value !~ /^\d+$/) {
1367 $ret = 1;
1368 push(@errs, [3, gettext(
1369 'rcap.max-rss is not an integer value: "%s"'),
1370 projent_values2string($values)]);;
1371 } elsif ($value > $MaxNum) {
1372 $ret = 1;
1373 push(@errs, [3, gettext(
1374 'rcap.max-rss too large')]);
1377 } elsif ($name eq 'project.pool') {
1378 if (!defined($values)) {
1379 $ret = 1;
1380 push(@errs, [3, gettext(
1381 'project.pool missing value')]);
1382 } elsif (scalar(@$values) != 1) {
1383 $ret = 1;
1384 push(@errs, [3, gettext(
1385 'project.pool should have single value')]);
1386 } elsif (!defined($value) || ref($value)) {
1387 $ret = 1;
1388 push(@errs, [3, gettext(
1389 'project.pool has invalid value "%s'),
1390 projent_values2string($values)]);;
1391 } elsif (!($value =~ /^[[:alpha:]][[:alnum:]_.-]*$/)) {
1392 $ret = 1;
1393 push(@errs, [3, gettext(
1394 'project.pool: invalid pool name "%s"'),
1395 $value]);
1396 # Pool must exist.
1397 } elsif (pool_exists($value) != 0) {
1398 $ret = 1;
1399 push(@errs, [6, gettext(
1400 'project.pool: pools not enabled or pool does '.
1401 'not exist: "%s"'),
1402 $value]);
1404 } else {
1405 my $rctlmax;
1406 my $rctlflags;
1407 my $rules;
1410 # See if rctl rules exist for this attribute. If so, it
1411 # is an rctl and is checked for valid values.
1414 # check hashed cache of rctl rules.
1415 $rules = $RctlRules{$name};
1416 if (!defined($rules)) {
1419 # See if this is an resource control name, if so
1420 # cache rules.
1422 ($rctlmax, $rctlflags) = rctl_get_info($name);
1423 if (defined($rctlmax)) {
1424 $rules = proj_getrctlrules(
1425 $rctlmax, $rctlflags);
1426 if (defined($rules)) {
1427 $RctlRules{$name} = $rules;
1428 } else {
1429 $RctlRules{$name} = "NOT AN RCTL";
1434 # If rules are defined, this is a resource control.
1435 if (defined($rules) && ref($rules)) {
1437 ($result, $ref) =
1438 projent_validate_rctl($attribute, $flags);
1439 if ($result != 0) {
1440 $ret = 1;
1441 push(@errs, @$ref);
1445 return ($ret, \@errs);
1449 # projent_validate_attributes(ref to attribute list, flags)
1451 # Validates all attributes in list of attribute references using
1452 # projent_validate_attribute. flags is unused.
1453 # flags is unused.
1455 sub projent_validate_attributes
1457 my ($attributes, $flags) = @_;
1458 my @err;
1459 my $ret = 0;
1460 my $result = 0;
1461 my $ref;
1462 my $attribute;
1464 foreach $attribute (@$attributes) {
1466 ($ret, $ref) = projent_validate_attribute($attribute, $flags);
1467 if ($ret != 0) {
1468 $result = $ret;
1469 push(@err, @$ref);
1473 my %seen;
1474 my @dups = grep($seen{$_}++ == 1, map { $_->{'name'} } @$attributes);
1475 if (@dups) {
1476 $result = 1;
1477 push(@err, [3, gettext('Duplicate attributes "%s"'),
1478 join(',', @dups)]);
1481 return ($result, \@err);
1485 # projent_getrctlrules(max value, global flags)
1487 # given an rctls max value and global flags, returns a ref to a hash
1488 # of rctl rules that is used by projent_validate_rctl to validate an
1489 # rctl's values.
1491 sub proj_getrctlrules
1493 my ($max, $flags) = @_;
1494 my $signals;
1495 my $rctl;
1497 $rctl = {};
1498 $signals =
1499 [ qw(ABRT XRES HUP STOP TERM KILL),
1500 $SigNo{'ABRT'},
1501 $SigNo{'XRES'},
1502 $SigNo{'HUP'},
1503 $SigNo{'STOP'},
1504 $SigNo{'TERM'},
1505 $SigNo{'KILL'} ];
1507 $rctl->{'max'} = $max;
1509 if ($flags & &RCTL_GLOBAL_BYTES) {
1510 $rctl->{'type'} = 'bytes';
1511 } elsif ($flags & &RCTL_GLOBAL_SECONDS) {
1512 $rctl->{'type'} = 'seconds';
1513 } elsif ($flags & &RCTL_GLOBAL_COUNT) {
1514 $rctl->{'type'} = 'count';
1515 } else {
1516 $rctl->{'type'} = 'unknown';
1518 if ($flags & &RCTL_GLOBAL_NOBASIC) {
1519 $rctl->{'privs'} = ['privileged', 'priv'];
1520 } else {
1521 $rctl->{'privs'} = ['basic', 'privileged', 'priv'];
1524 if ($flags & &RCTL_GLOBAL_DENY_ALWAYS) {
1525 $rctl->{'actions'} = ['deny'];
1527 } elsif ($flags & &RCTL_GLOBAL_DENY_NEVER) {
1528 $rctl->{'actions'} = ['none'];
1529 } else {
1530 $rctl->{'actions'} = ['none', 'deny'];
1533 if ($flags & &RCTL_GLOBAL_SIGNAL_NEVER) {
1534 $rctl->{'signals'} = [];
1536 } else {
1538 push(@{$rctl->{'actions'}}, 'sig');
1540 if ($flags & &RCTL_GLOBAL_CPU_TIME) {
1541 push(@$signals, 'XCPU', '30');
1543 if ($flags & &RCTL_GLOBAL_FILE_SIZE) {
1544 push(@$signals, 'XFSZ', '31');
1546 $rctl->{'signals'} = $signals;
1548 return ($rctl);
1552 # projent_val2num(scaled value, "seconds" | "count" | "bytes")
1554 # converts an integer or scaled value to an integer value.
1555 # returns (integer value, modifier character, unit character.
1557 # On failure, integer value is undefined. If the original
1558 # scaled value is a plain integer, modifier character and
1559 # unit character will be undefined.
1561 sub projent_val2num
1563 my ($val, $type) = @_;
1564 my %scaleM = ( k => 1000,
1565 m => 1000000,
1566 g => 1000000000,
1567 t => 1000000000000,
1568 p => 1000000000000000,
1569 e => 1000000000000000000);
1570 my %scaleB = ( k => 1024,
1571 m => 1048576,
1572 g => 1073741824,
1573 t => 1099511627776,
1574 p => 1125899906842624,
1575 e => 1152921504606846976);
1577 my $scale;
1578 my $base;
1579 my ($num, $modifier, $unit);
1580 my $mul;
1581 my $string;
1582 my $i;
1583 my $undefined;
1584 my $exp_unit;
1586 ($num, $modifier, $unit) = $val =~
1587 /^(\d+(?:\.\d+)?)(?i:([kmgtpe])?([bs])?)$/;
1589 # No numeric match.
1590 if (!defined($num)) {
1591 return ($undefined, $undefined, $undefined);
1594 # Decimal number with no scaling modifier.
1595 if (!defined($modifier) && $num =~ /^\d+\.\d+/) {
1596 return ($undefined, $undefined, $undefined);
1599 if ($type eq 'bytes') {
1600 $exp_unit = 'b';
1601 $scale = \%scaleB;
1602 } elsif ($type eq 'seconds') {
1603 $exp_unit = 's';
1604 $scale = \%scaleM;
1605 } else {
1606 $scale = \%scaleM;
1609 if (defined($unit)) {
1610 $unit = lc($unit);
1613 # So not succeed if unit is incorrect.
1614 if (!defined($exp_unit) && defined($unit)) {
1615 return ($undefined, $modifier, $unit);
1617 if (defined($unit) && $unit ne $exp_unit) {
1618 return ($undefined, $modifier, $unit);
1621 if (defined($modifier)) {
1623 $modifier = lc($modifier);
1624 $mul = $scale->{$modifier};
1625 $num = $num * $mul;
1628 # check for integer overflow.
1629 if ($num > $MaxNum) {
1630 return ("OVERFLOW", $modifier, $unit);
1633 # Trim numbers that are decimal equivalent to the maximum value
1634 # to the maximum integer value.
1636 if ($num == $MaxNum) {
1637 $num = $MaxNum;;
1639 } elsif ($num < $MaxNum) {
1640 # convert any decimal numbers to an integer
1641 $num = int($num);
1644 return ($num, $modifier, $unit);
1647 # projent_validate_rctl(ref to rctl attribute hash, flags)
1649 # verifies that the given rctl hash with keys "name" and
1650 # "values" contains valid values for the given name.
1651 # flags is unused.
1653 sub projent_validate_rctl
1655 my ($rctl, $flags) = @_;
1656 my $allrules;
1657 my $rules;
1658 my $name;
1659 my $values;
1660 my $value;
1661 my $valuestring;
1662 my $ret = 0;
1663 my @err;
1664 my $priv;
1665 my $val;
1666 my @actions;
1667 my $action;
1668 my $signal;
1669 my $sigstring; # Full signal string on right hand of signal=SIGXXX.
1670 my $signame; # Signal number or XXX part of SIGXXX.
1671 my $siglist;
1672 my $nonecount;
1673 my $denycount;
1674 my $sigcount;
1676 $name = $rctl->{'name'};
1677 $values = $rctl->{'values'};
1680 # Get the default rules for all rctls, and the specific rules for
1681 # this rctl.
1683 $allrules = $RctlRules{'__DEFAULT__'};
1684 $rules = $RctlRules{$name};
1686 if (!defined($rules) || !ref($rules)) {
1687 $rules = $allrules;
1690 # Allow for no rctl values on rctl.
1691 if (!defined($values)) {
1692 return (0, \@err);
1695 # If values exist, make sure it is a list.
1696 if (!ref($values)) {
1698 push(@err, [3, gettext(
1699 'rctl "%s" missing value'), $name]);
1700 return (1, \@err);
1703 foreach $value (@$values) {
1705 # Each value should be a list.
1707 if (!ref($value)) {
1708 $ret = 1;
1709 push(@err, [3, gettext(
1710 'rctl "%s" value "%s" should be in ()\'s'),
1711 $name, $value]);
1713 next;
1716 ($priv, $val, @actions) = @$value;
1717 if (!@actions) {
1718 $ret = 1;
1719 $valuestring = projent_values2string([$value]);
1720 push(@err, [3, gettext(
1721 'rctl "%s" value missing action "%s"'),
1722 $name, $valuestring]);
1725 if (!defined($priv)) {
1726 $ret = 1;
1727 push(@err, [3, gettext(
1728 'rctl "%s" value missing privilege "%s"'),
1729 $name, $valuestring]);
1731 } elsif (ref($priv)) {
1732 $ret = 1;
1733 $valuestring = projent_values2string([$priv]);
1734 push(@err, [3, gettext(
1735 'rctl "%s" invalid privilege "%s"'),
1736 $name, $valuestring]);
1738 } else {
1739 if (!(grep /^$priv$/, @{$allrules->{'privs'}})) {
1741 $ret = 1;
1742 push(@err, [3, gettext(
1743 'rctl "%s" unknown privilege "%s"'),
1744 $name, $priv]);
1746 } elsif (!(grep /^$priv$/, @{$rules->{'privs'}})) {
1748 $ret = 1;
1749 push(@err, [3, gettext(
1750 'rctl "%s" privilege not allowed '.
1751 '"%s"'), $name, $priv]);
1754 if (!defined($val)) {
1755 $ret = 1;
1756 push(@err, [3, gettext(
1757 'rctl "%s" missing value'), $name]);
1759 } elsif (ref($val)) {
1760 $ret = 1;
1761 $valuestring = projent_values2string([$val]);
1762 push(@err, [3, gettext(
1763 'rctl "%s" invalid value "%s"'),
1764 $name, $valuestring]);
1766 } else {
1767 if ($val !~ /^\d+$/) {
1768 $ret = 1;
1769 push(@err, [3, gettext(
1770 'rctl "%s" value "%s" is not '.
1771 'an integer'), $name, $val]);
1773 } elsif ($val > $rules->{'max'}) {
1774 $ret = 1;
1775 push(@err, [3, gettext(
1776 'rctl "%s" value "%s" exceeds '.
1777 'system limit'), $name, $val]);
1780 $nonecount = 0;
1781 $denycount = 0;
1782 $sigcount = 0;
1784 foreach $action (@actions) {
1786 if (ref($action)) {
1787 $ret = 1;
1788 $valuestring =
1789 projent_values2string([$action]);
1790 push(@err, [3, gettext(
1791 'rctl "%s" invalid action "%s"'),
1792 $name, $valuestring]);
1794 next;
1797 if ($action =~ /^sig(nal)?(=.*)?$/) {
1798 $signal = $action;
1799 $action = 'sig';
1801 if (!(grep /^$action$/, @{$allrules->{'actions'}})) {
1803 $ret = 1;
1804 push(@err, [3, gettext(
1805 'rctl "%s" unknown action "%s"'),
1806 $name, $action]);
1807 next;
1809 } elsif (!(grep /^$action$/, @{$rules->{'actions'}})) {
1811 $ret = 1;
1812 push(@err, [3, gettext(
1813 'rctl "%s" action not allowed "%s"'),
1814 $name, $action]);
1815 next;
1818 if ($action eq 'none') {
1819 if ($nonecount >= 1) {
1821 $ret = 1;
1822 push(@err, [3, gettext(
1823 'rctl "%s" duplicate action '.
1824 'none'), $name]);
1826 $nonecount++;
1827 next;
1829 if ($action eq 'deny') {
1830 if ($denycount >= 1) {
1832 $ret = 1;
1833 push(@err, [3, gettext(
1834 'rctl "%s" duplicate action '.
1835 'deny'), $name]);
1837 $denycount++;
1838 next;
1841 # action must be signal
1842 if ($sigcount >= 1) {
1844 $ret = 1;
1845 push(@err, [3, gettext(
1846 'rctl "%s" duplicate action sig'),
1847 $name]);
1849 $sigcount++;
1852 # Make sure signal is correct format, one of:
1853 # sig=##
1854 # signal=##
1855 # sig=SIGXXX
1856 # signal=SIGXXX
1857 # sig=XXX
1858 # signal=SIGXXX
1860 ($sigstring) = $signal =~
1862 (?:signal|sig)=
1863 (\d+|
1864 (?:SIG)?[[:upper:]]+(?:[+-][123])?
1866 $/x;
1868 if (!defined($sigstring)) {
1869 $ret = 1;
1870 push(@err, [3, gettext(
1871 'rctl "%s" invalid signal "%s"'),
1872 $name, $signal]);
1873 next;
1876 $signame = $sigstring;
1877 $signame =~ s/SIG//;
1879 # Make sure specific signal is allowed.
1880 $siglist = $allrules->{'signals'};
1881 if (!(grep /^$signame$/, @$siglist)) {
1882 $ret = 1;
1883 push(@err, [3, gettext(
1884 'rctl "%s" invalid signal "%s"'),
1885 $name, $signal]);
1886 next;
1888 $siglist = $rules->{'signals'};
1890 if (!(grep /^$signame$/, @$siglist)) {
1891 $ret = 1;
1892 push(@err, [3, gettext(
1893 'rctl "%s" signal not allowed "%s"'),
1894 $name, $signal]);
1895 next;
1899 if ($nonecount && ($denycount || $sigcount)) {
1900 $ret = 1;
1901 push(@err, [3, gettext(
1902 'rctl "%s" action "none" specified with '.
1903 'other actions'), $name]);
1907 if (@err) {
1908 return ($ret, \@err);
1909 } else {
1910 return ($ret, \@err);