7759 Perl modules update
[unleashed-userland.git] / components / perl / sun_solaris / src / Sun / Solaris / Project / Project.pm
blobdee5ebe476aa6ed9efef938dc15aeb24a63fa3db
2 # Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
6 # Project.pm provides the bootstrap for the Sun::Solaris::Project module, and
7 # also functions for reading, validating and writing out project(4) format
8 # files.
10 ################################################################################
11 require 5.8.4;
13 use strict;
14 use warnings;
15 use locale;
16 use Errno;
17 use Fcntl;
18 use File::Basename;
19 use POSIX qw(locale_h limits_h);
21 package Sun::Solaris::Project;
23 our $VERSION = '1.9';
25 use XSLoader;
26 XSLoader::load(__PACKAGE__, $VERSION);
28 our (@EXPORT_OK, %EXPORT_TAGS);
29 my @constants = qw(MAXPROJID PROJNAME_MAX PROJF_PATH PROJECT_BUFSZ
30 SETPROJ_ERR_TASK SETPROJ_ERR_POOL);
31 my @syscalls = qw(getprojid);
32 my @libcalls = qw(setproject activeprojects getprojent setprojent endprojent
33 getprojbyname getprojbyid getdefaultproj fgetprojent inproj
34 getprojidbyname);
35 my @private = qw(projf_read projf_write projf_validate projent_parse
36 projent_parse_name projent_validate_unique_name
37 projent_parse_projid projent_validate_unique_id
38 projent_parse_comment
39 projent_parse_users
40 projent_parse_groups
41 projent_parse_attributes
42 projent_validate projent_validate_projid
43 projent_values_equal projent_values2string);
45 @EXPORT_OK = (@constants, @syscalls, @libcalls, @private);
46 %EXPORT_TAGS = (CONSTANTS => \@constants, SYSCALLS => \@syscalls,
47 LIBCALLS => \@libcalls, PRIVATE => \@private, ALL => \@EXPORT_OK);
49 use base qw(Exporter);
50 use Sun::Solaris::Utils qw(gettext);
53 # Set up default rules for validating rctls.
54 # These rules are not global-flag specific, but instead
55 # are the total set of allowable values on all rctls.
57 use Config;
58 our $MaxNum = &RCTL_MAX_VALUE;
59 our %RctlRules;
61 my %rules;
62 our %SigNo;
63 my $j;
64 my $name;
65 foreach $name (split(' ', $Config{sig_name})) {
66 $SigNo{$name} = $j;
67 $j++;
69 %rules = (
70 'privs' => [ qw(basic privileged priv) ],
71 'actions' => [ qw(none deny sig) ],
72 'signals' => [ qw(ABRT XRES HUP STOP TERM KILL XFSZ XCPU),
73 $SigNo{'ABRT'},
74 $SigNo{'XRES'},
75 $SigNo{'HUP'},
76 $SigNo{'STOP'},
77 $SigNo{'TERM'},
78 $SigNo{'KILL'},
79 $SigNo{'XFSZ'},
80 $SigNo{'XCPU'} ],
81 'max' => $MaxNum
84 $RctlRules{'__DEFAULT__'} = \%rules;
87 # projf_combine_errors(errorA, errorlistB)
89 # Concatenates a single error with a list of errors. Each error in the new
90 # list will have a status matching the status of errorA.
92 # Example:
94 # projf_combine_errors(
95 # [ 5, "Error on line %d, 10 ],
96 # [ [ 3, "Invalid Value %s", "foo" ],
97 # [ 6, "Duplicate Value %s", "bar" ]
98 # ]);
100 # would return the list ref:
102 # [ [ 5, "Error on line %d: Invalid Value %s", 10, "foo" ],
103 # [ 5, "Error on line %d: Duplicate Value %s", 10, "bar" ]
106 # This function is used when a fuction wants to add more information to
107 # a list of errors returned by another function.
109 sub projf_combine_errors
112 my ($error1, $errorlist) = @_;
113 my $error2;
115 my $newerror;
116 my @newerrorlist;
118 my ($err1, $fmt1, @args1);
119 my ($err2, $fmt2, @args2);
121 ($err1, $fmt1, @args1) = @$error1;
122 foreach $error2 (@$errorlist) {
124 ($err2, $fmt2, @args2) = @$error2;
125 $newerror = [ $err1, $fmt1 . ', ' . $fmt2, @args1, @args2];
126 push(@newerrorlist, $newerror);
128 return (\@newerrorlist);
132 # projf_read(filename, flags)
134 # Reads and parses a project(4) file, and returns a list of projent hashes.
136 # Inputs:
137 # filename - file to read
138 # flags - hash ref of flags
140 # If flags contains key "validate", the project file entries will also be
141 # validated for run-time correctness If so, the flags ref is forwarded to
142 # projf_validate().
144 # Return Value:
146 # Returns a ref to a list of projent hashes. See projent_parse() for a
147 # description of a projent hash.
149 sub projf_read
152 my ($fh, $flags) = @_;
153 my @projents;
154 my $projent;
155 my $linenum = 0;
156 my ($projname, $projid, $comment, $users, $groups, $attributes);
157 my ($ret, $ref);
158 my @errs;
160 my ($line, $origline, $next, @projf);
161 while (defined($line = <$fh>)) {
163 $linenum++;
164 $origline = $line;
166 # Remove any line continuations and trailing newline.
167 $line =~ s/\\\n//g;
168 chomp($line);
171 if (length($line) > (&PROJECT_BUFSZ - 2)) {
172 push(@errs,
174 gettext('Parse error on line %d, line too long'),
175 $linenum]);
179 ($ret, $ref) = projent_parse($line, {});
180 if ($ret != 0) {
181 $ref = projf_combine_errors(
182 [5, gettext('Parse error on line %d'), $linenum],
183 $ref);
184 push(@errs, @$ref);
185 next;
188 $projent = $ref;
191 # Cache original line to save original format if it is
192 # not changed.
194 $projent->{'line'} = $origline;
195 $projent->{'modified'} = 'false';
196 $projent->{'linenum'} = $linenum;
198 push(@projents, $projent);
201 if (defined($flags->{'validate'}) && ($flags->{'validate'} eq 'true')) {
202 ($ret, $ref) = projf_validate(\@projents, $flags);
203 if ($ret != 0) {
204 push(@errs, @$ref);
208 if (@errs) {
209 return (1, \@errs);
211 } else {
212 return (0, \@projents);
217 # projf_write(filehandle, projent list)
219 # Write a list of projent hashes to a file handle.
220 # projent's with key "modified" => false will be
221 # written using the "line" key. projent's with
222 # key "modified" => "true" will be written by
223 # constructing a new line based on their "name"
224 # "projid", "comment", "userlist", "grouplist"
225 # and "attributelist" keys.
227 sub projf_write
229 my ($fh, $projents) = @_;
230 my $projent;
231 my $string;
233 foreach $projent (@$projents) {
235 if ($projent->{'modified'} eq 'false') {
236 $string = $projent->{'line'};
237 } else {
238 $string = projent_2string($projent) . "\n";
240 print $fh "$string";
245 # projent_parse(line)
247 # Functions for parsing the project file lines into projent hashes.
249 # Returns a number and a ref, one of:
251 # (0, ref to projent hash)
252 # (non-zero, ref to list of errors)
254 # Flag can be:
255 # allowspaces: allow spaces between user and group names.
256 # allowunits : allow units (K, M, etc), on rctl values.
258 # A projent hash contains the keys:
260 # "name" - string name of project
261 # "projid" - numeric id of project
262 # "comment" - comment string
263 # "users" - , seperated user list string
264 # "userlist" - list ref to list of user name strings
265 # "groups" - , seperated group list string
266 # "grouplist" - list ref to liset of group name strings
267 # "attributes" - ; seperated attribute list string
268 # "attributelist" - list ref to list of attribute refs
269 # (see projent_parse_attributes() for attribute ref)
271 sub projent_parse
274 my ($line, $flags) = @_;
275 my $projent = {};
276 my ($ret, $ref);
277 my @errs;
278 my ($projname, $projid, $comment, $users, $groups, $attributes);
281 # Split fields of project line. split() is not used because
282 # we must enforce that there are 6 fields.
284 ($projname, $projid, $comment, $users, $groups, $attributes) =
285 $line =~
286 /^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*)$/;
288 # If there is not a complete match, nothing will be defined;
289 if (!defined($projname)) {
290 push(@errs, [5, gettext(
291 'Incorrect number of fields. Should have 5 ":"\'s.')]);
293 # Get as many fields as we can.
294 ($projname, $projid, $comment, $users, $groups, $attributes) =
295 split(/:/, $line);
298 if (defined($projname)) {
299 $projent->{'name'} = $projname;
300 ($ret, $ref) = projent_parse_name($projname);
301 if ($ret != 0) {
302 push(@errs, @$ref);
305 if (defined($projid)) {
306 $projent->{'projid'} = $projid;
307 ($ret, $ref) = projent_parse_projid($projid);
308 if ($ret != 0) {
309 push(@errs, @$ref);
312 if (defined($comment)) {
313 $projent->{'comment'} = $comment;
314 ($ret, $ref) = projent_parse_comment($comment);
315 if ($ret != 0) {
316 push(@errs, @$ref);
319 if (defined($users)) {
320 $projent->{'users'} = $users;
321 ($ret, $ref) = projent_parse_users($users, $flags);
322 if ($ret != 0) {
323 push(@errs, @$ref);
324 } else {
325 $projent->{'userlist'} = $ref;
328 if (defined($groups)) {
329 $projent->{'groups'} = $groups;
330 ($ret, $ref) = projent_parse_groups($groups, $flags);
331 if ($ret != 0) {
332 push(@errs, @$ref);
333 } else {
334 $projent->{'grouplist'} = $ref;
337 if (defined($attributes)) {
338 $projent->{'attributes'} = $attributes;
339 ($ret, $ref) = projent_parse_attributes($attributes, $flags);
340 if ($ret != 0) {
341 push(@errs, @$ref);
342 } else {
343 $projent->{'attributelist'} = $ref;
347 if (@errs) {
348 return (1, \@errs);
350 } else {
351 return (0, $projent);
356 # Project name syntax checking.
358 sub projent_parse_name
360 my @err;
361 my ($projname) = @_;
363 if (!($projname =~ /^[[:alpha:]][[:alnum:]_.-]*$/)) {
364 push(@err, ([3, gettext(
365 'Invalid project name "%s", contains invalid characters'),
366 $projname]));
367 return (1, \@err);
369 if (length($projname) > &PROJNAME_MAX) {
370 push(@err, ([3, gettext(
371 'Invalid project name "%s", name too long'),
372 $projname]));
373 return (1, \@err);
375 return (0, $projname);
379 # Projid syntax checking.
381 sub projent_parse_projid
383 my @err;
384 my ($projid) = @_;
386 # verify projid is a positive number, and less than UID_MAX
387 if (!($projid =~ /^\d+$/)) {
388 push(@err, [3, gettext('Invalid projid "%s"'),
389 $projid]);
390 return (1, \@err);
392 } elsif ($projid > POSIX::INT_MAX) {
393 push(@err, [3, gettext('Invalid projid "%s": must be <= '.
394 POSIX::INT_MAX),
395 $projid]);
396 return (1, \@err);
398 } else {
399 return (0, $projid);
404 # Project comment syntax checking.
406 sub projent_parse_comment
408 my ($comment) = @_;
410 # no restrictions on comments
411 return (0, $comment);
415 # projent_parse_users(string, flags)
417 # Parses "," seperated list of users, and returns list ref to a list of
418 # user names. If flags contains key "allowspaces", then spaces are
419 # allowed between user names and ","'s.
421 sub projent_parse_users
423 my ($users, $flags) = @_;
424 my @err;
425 my $user;
426 my $pattern;
427 my @userlist;
429 if (exists($flags->{'allowspaces'})) {
430 $pattern = '\s*,\s*';
431 } else {
432 $pattern = ',';
434 @userlist = split(/$pattern/, $users);
436 # Return empty list if there are no users.
437 if (!(@userlist)) {
438 return (0, \@userlist);
441 # Verify each user name is the correct format for a valid user name.
442 foreach $user (@userlist) {
444 # Allow for wildcards.
445 if ($user eq '*' || $user eq '!*') {
446 next;
449 # Allow for ! operator, usernames must begin with alpha-num,
450 # and contain alpha-num, '_', digits, '.', or '-'.
451 if (!($user =~ /^!?[[:alpha:]][[:alnum:]_.-]*$/)) {
452 push(@err, [3, gettext('Invalid user name "%s"'),
453 $user]);
454 next;
457 if (@err) {
458 return (1,\ @err);
459 } else {
460 return (0, \@userlist);
465 # projent_parse_groups(string, flags)
467 # Parses "," seperated list of groups, and returns list ref to a list of
468 # groups names. If flags contains key "allowspaces", then spaces are
469 # allowed between group names and ","'s.
471 sub projent_parse_groups
473 my ($groups, $flags) = @_;
474 my @err;
475 my $group;
476 my $pattern;
478 my @grouplist;
480 if (exists($flags->{'allowspaces'})) {
481 $pattern = '\s*,\s*';
482 } else {
483 $pattern = ',';
485 @grouplist = split(/$pattern/, $groups);
487 # Return empty list if there are no groups.
488 if (!(@grouplist)) {
489 return (0, \@grouplist);
492 # Verify each group is the correct format for a valid group name.
493 foreach $group (@grouplist) {
495 # Allow for wildcards.
496 if ($group eq '*' || $group eq '!*') {
497 next;
500 # Allow for ! operator, groupnames can contain only alpha
501 # characters and digits.
502 if (!($group =~ /^!?[[:alnum:]]+$/)) {
503 push(@err, [3, gettext('Invalid group name "%s"'),
504 $group]);
505 next;
509 if (@err) {
510 return (1,\ @err);
511 } else {
512 return (0, \@grouplist);
517 # projent_tokenize_attribute_values(values)
519 # Values is the right hand side of a name=values attribute/values pair.
520 # This function splits the values string into a list of tokens. Tokens are
521 # valid string values and the characters ( ) ,
523 sub projent_tokenize_attribute_values
526 # This seperates the attribute string into higher level tokens
527 # for parsing.
529 my $prev;
530 my $cur;
531 my $next;
532 my $token;
533 my @tokens;
534 my @newtokens;
535 my @err;
537 # Seperate tokens delimited by "(", ")", and ",".
538 @tokens = split(/([,()])/, $_[0], -1);
540 # Get rid of blanks
541 @newtokens = grep($_ ne '', @tokens);
543 foreach $token (@newtokens) {
544 if (!($token =~ /^[(),]$/ ||
545 $token =~ /^[[:alnum:]_.\/=+-]*$/)) {
546 push(@err, [3, gettext(
547 'Invalid Character at or near "%s"'), $token]);
550 if (@err) {
551 return (1, \@err);
552 } else {
553 return (0, \@newtokens);
558 # projent_parse_attribute_values(values)
560 # Values is the right hand side of a name=values attribute/values pair.
561 # This function parses the values string into a list of values. Each value
562 # can be either a scalar value, or a ref to another list of values.
563 # A ref to the list of values is returned.
565 sub projent_parse_attribute_values
568 # For some reason attribute values can be lists of values and
569 # sublists, which are scoped using ()'s. All values and sublists
570 # are delimited by ","'s. Empty values are lists are permitted.
572 # This function returns a reference to a list of values, each of
573 # which can be a scalar value, or a reference to a sublist. Sublists
574 # can contain both scalar values and references to furthur sublists.
576 my ($values) = @_;
577 my $tokens;
578 my @usedtokens;
579 my $token;
580 my $prev = '';
581 my $parendepth = 0;
582 my @valuestack;
583 my @err;
584 my ($ret, $ref);
585 my $line;
587 push (@valuestack, []);
589 ($ret, $ref) = projent_tokenize_attribute_values($values);
590 if ($ret != 0) {
591 return ($ret, $ref);
593 $tokens = $ref;
595 foreach $token (@$tokens) {
597 push(@usedtokens, $token);
599 if ($token eq ',') {
601 if ($prev eq ',' || $prev eq '(' ||
602 $prev eq '') {
603 push(@{$valuestack[$#valuestack]}, '');
605 $prev = ',';
606 next;
608 if ($token eq '(') {
610 if (!($prev eq '(' || $prev eq ',' ||
611 $prev eq '')) {
613 $line = join('', @usedtokens);
614 push(@err, [3, gettext(
615 '"%s" <- "(" unexpected'),
616 $line]);
618 return (1, \@err);
621 $parendepth++;
622 my $arrayref = [];
623 push(@{$valuestack[$#valuestack]}, $arrayref);
624 push(@valuestack, $arrayref);
626 $prev = '(';
627 next;
629 if ($token eq ')') {
631 if ($parendepth <= 0) {
633 $line = join('', @usedtokens);
634 push(@err, [3, gettext(
635 '"%s" <- ")" unexpected'),
636 $line]);
638 return (1, \@err);
641 if ($prev eq ',' || $prev eq '(') {
642 push(@{$valuestack[$#valuestack]}, '');
644 $parendepth--;
645 pop @valuestack;
647 $prev = ')';
648 next;
651 if (!($prev eq ',' || $prev eq '(' || $prev eq '')) {
652 $line = join('', @usedtokens);
653 push(@err, [3, gettext(
654 '"%s" <- "%s" unexpected'),
655 $line, $token]);
657 return (1, \@err);
660 push(@{$valuestack[$#valuestack]}, $token);
661 $prev = $token;
662 next;
665 if ($parendepth != 0) {
666 push(@err, [3, gettext(
667 '"%s" <- ")" missing'),
668 $values]);
669 return (1, \@err);
672 if ($prev eq ',' || $prev eq '') {
673 push(@{$valuestack[$#valuestack]}, '');
676 return (0, $valuestack[0]);
680 # projent_parse_attribute("name=values", $flags)
682 # $flags is a hash ref.
683 # Valid flags keys:
684 # 'allowunits' - allows numeric values to be scaled on certain attributes
686 # Returns a hash ref with keys:
688 # "name" - name of attribute
689 # "values" - ref to list of values.
690 # Each value can be a scalar value, or a ref to
691 # a sub-list of values.
693 sub projent_parse_attribute
695 my ($string, $flags) = @_;
696 my $attribute = {};
697 my ($name, $stock, $values);
698 my ($ret, $ref);
699 my @err;
700 my $scale;
701 my $num;
702 my $modifier;
703 my $unit;
704 my $tuple;
705 my $rules;
706 my $rctlmax;
707 my $rctlflags;
709 # pattern for matching stock symbols.
710 my $stockp = '[[:upper:]]{1,5}(?:.[[:upper:]]{1,5})?,';
711 # Match attribute with no value.
712 ($name, $stock) = $string =~
713 /^(($stockp)?[[:alpha:]][[:alnum:]_.-]*)$/;
714 if ($name) {
715 $attribute->{'name'} = $name;
716 return (0, $attribute);
719 # Match attribute with value list.
720 ($name, $stock, $values) = $string =~
721 /^(($stockp)?[[:alpha:]][[:alnum:]_.-]*)=(.*)$/;
722 if ($name) {
723 $attribute->{'name'} = $name;
725 if (!defined($values)) {
726 $values = '';
729 ($ret, $ref) = projent_parse_attribute_values($values);
730 if ($ret != 0) {
731 $ref = projf_combine_errors(
733 gettext('Invalid value on attribute "%s"'),
734 $name], $ref);
735 push(@err, @$ref);
736 return ($ret, \@err)
739 # Scale attributes than can be scaled.
740 if (exists($flags->{"allowunits"})) {
742 if ($name eq 'rcap.max-rss' &&
743 defined($ref->[0]) && !ref($ref->[0])) {
744 $scale = 'bytes';
746 ($num, $modifier, $unit) =
747 projent_val2num($ref->[0], $scale);
749 if (!defined($num)) {
751 if (defined($unit)) {
752 push(@err, [3, gettext(
753 'rcap.max-rss has invalid '.
754 'unit "%s"'), $unit]);
755 } else {
756 push(@err, [3, gettext(
757 'rcap.max-rss has invalid '.
758 'value "%s"'), $ref->[0]]);
760 } elsif ($num eq "OVERFLOW") {
761 push(@err, [3, gettext( 'rcap.max-rss value '.
762 '"%s" exceeds maximum value "%s"'),
763 $ref->[0], $MaxNum]);
764 } else {
765 $ref->[0] = $num;
768 # Check hashed cache of rctl rules.
769 $rules = $RctlRules{$name};
770 if (!defined($rules)) {
772 # See if this is an resource control name, if so
773 # cache rules.
775 ($rctlmax, $rctlflags) = rctl_get_info($name);
776 if (defined($rctlmax)) {
777 $rules = proj_getrctlrules(
778 $rctlmax, $rctlflags);
779 if (defined($rules)) {
780 $RctlRules{$name} = $rules;
781 } else {
782 $RctlRules{$name} =
783 "NOT AN RCTL";
788 # Scale values if this is an rctl.
789 if (defined ($rules) && ref($rules)) {
790 $flags->{'type'} = $rules->{'type'};
791 foreach $tuple (@$ref) {
793 # Skip if tuple this is not a list.
794 if (!ref($tuple)) {
795 next;
797 # Skip if second element is not scalar.
798 if (!defined($tuple->[1]) ||
799 ref($tuple->[1])) {
800 next;
802 ($num, $modifier, $unit) =
803 projent_val2num($tuple->[1],
804 $flags->{'type'});
806 if (!defined($num)) {
808 if (defined($unit)) {
809 push(@err, [3, gettext(
810 'rctl %s has '.
811 'invalid unit '.
812 '"%s"'),$name,
813 $unit]);
814 } else {
815 push(@err, [3, gettext(
816 'rctl %s has '.
817 'invalid value '.
818 '"%s"'), $name,
819 $tuple->[1]]);
821 } elsif ($num eq "OVERFLOW") {
822 push(@err, [3, gettext(
823 'rctl %s value "%s" '.
824 'exceeds maximum value "%s"'),
825 $name, $tuple->[1], $MaxNum]);
826 } else {
827 $tuple->[1] = $num;
832 $attribute->{'values'} = $ref;
833 if (@err) {
834 return (1, \@err);
835 } else {
836 return (0, $attribute);
839 } else {
840 # Attribute did not match name[=value,value...]
841 push(@err, [3, gettext('Invalid attribute "%s"'), $string]);
842 return (1, \@err);
847 # projent_parse_attributes("; seperated list of name=values pairs");
849 # Returns a list of attribute references, as returned by
850 # projent_parse_attribute().
852 sub projent_parse_attributes
854 my ($attributes, $flags) = @_;
855 my @attributelist;
856 my @attributestrings;
857 my $attributestring;
858 my $attribute;
859 my ($ret, $ref);
860 my @errs;
862 # Split up attributes by ";"'s.
863 @attributestrings = split(/;/, $attributes);
865 # If no attributes, return empty list.
866 if (!@attributestrings) {
867 return (0, \@attributelist);
870 foreach $attributestring (@attributestrings) {
872 ($ret, $ref) = projent_parse_attribute($attributestring,
873 $flags);
874 if ($ret != 0) {
875 push(@errs, @$ref);
876 } else {
877 push(@attributelist, $ref);
881 if (@errs) {
882 return (1, \@errs);
883 } else {
884 return (0, \@attributelist);
890 # projent_values_equal(list A, list B)
892 # Given two references to lists of attribute values (as returned by
893 # projent_parse_attribute_values()), returns 1 if they are identical
894 # lists or 0 if they are not.
896 # XXX sub projent_values_equal;
897 sub projent_values_equal
899 my ($x, $y) = @_;
901 my $itema;
902 my $itemb;
903 my $index = 0;
905 if (ref($x) && ref($y)) {
907 if (scalar(@$x) != scalar(@$y)) {
908 return (0);
909 } else {
910 foreach $itema (@$x) {
912 $itemb = $y->[$index++];
914 if (!projent_values_equal($itema, $itemb)) {
915 return (0);
918 return (1);
920 } elsif ((!ref($x) && (!ref($y)))) {
921 return ($x eq $y);
922 } else {
923 return (0);
928 # Converts a list of values to a , seperated string, enclosing sublists
929 # in ()'s.
931 sub projent_values2string
933 my ($values) = @_;
934 my $string;
935 my $value;
936 my @valuelist;
938 if (!defined($values)) {
939 return ('');
941 if (!ref($values)) {
942 return ($values);
944 foreach $value (@$values) {
946 if (ref($value)) {
947 push(@valuelist,
948 '(' . projent_values2string($value) . ')');
949 } else {
950 push(@valuelist, $value);
954 $string = join(',', @valuelist) ;
955 if (!defined($string)) {
956 $string = '';
958 return ($string);
962 # Converts a ref to an attribute hash with keys "name", and "values" to
963 # a string in the form "name=value,value...".
965 sub projent_attribute2string
967 my ($attribute) = @_;
968 my $string;
970 $string = $attribute->{'name'};
972 if (ref($attribute->{'values'}) && @{$attribute->{'values'}}) {
973 $string = $string . '=' .
974 projent_values2string(($attribute->{'values'}));
976 return ($string);
980 # Converts a ref to a projent hash (as returned by projent_parse()) to
981 # a project(4) database entry line.
983 sub projent_2string
985 my ($projent) = @_;
986 my @attributestrings;
987 my $attribute;
989 foreach $attribute (@{$projent->{'attributelist'}}) {
990 push(@attributestrings, projent_attribute2string($attribute));
992 return (join(':', ($projent->{'name'},
993 $projent->{'projid'},
994 $projent->{'comment'},
995 join(',', @{$projent->{'userlist'}}),
996 join(',', @{$projent->{'grouplist'}}),
997 join(';', @attributestrings))));
1001 # projf_validate(ref to list of projents hashes, flags)
1003 # For each projent hash ref in the list, checks that users, groups, and pools
1004 # exists, and that known attributes are valid. Attributes matching rctl names
1005 # are verified to have valid values given that rctl's global flags and max
1006 # value.
1008 # Valid flag keys:
1010 # "res" - allow reserved project ids 0-99
1011 # "dup" - allow duplicate project ids
1013 sub projf_validate
1015 my ($projents, $flags) = @_;
1016 my $projent;
1017 my $ret;
1018 my $ref;
1019 my @err;
1020 my %idhash;
1021 my %namehash;
1022 my %seenids;
1023 my %seennames;
1025 # check for unique project names
1026 foreach $projent (@$projents) {
1028 my @lineerr;
1030 $seennames{$projent->{'name'}}++;
1031 $seenids{$projent->{'projid'}}++;
1033 if ($seennames{$projent->{'name'}} > 1) {
1034 push(@lineerr, [4, gettext(
1035 'Duplicate project name "%s"'),
1036 $projent->{'name'}]);
1039 if (!defined($flags->{'dup'})) {
1040 if ($seenids{$projent->{'projid'}} > 1) {
1041 push(@lineerr, [4, gettext(
1042 'Duplicate projid "%s"'),
1043 $projent->{'projid'}]);
1046 ($ret, $ref) = projent_validate($projent, $flags);
1047 if ($ret != 0) {
1048 push(@lineerr, @$ref);
1051 if (@lineerr) {
1053 $ref = projf_combine_errors([5, gettext(
1054 'Validation error on line %d'),
1055 $projent->{'linenum'}], \@lineerr);
1056 push(@err, @$ref);
1059 if (@err) {
1060 return (1, \@err);
1061 } else {
1062 return (0, $projents);
1067 # projent_validate_unique_id(
1068 # ref to projent hash, ref to list of projent hashes)
1070 # Verifies that projid of the projent hash only exists once in the list of
1071 # projent hashes.
1073 sub projent_validate_unique_id
1075 my ($projent, $projf, $idhash) = @_;
1076 my @err;
1077 my $ret = 0;
1078 my $projid = $projent->{'projid'};
1080 if (scalar(grep($_->{'projid'} eq $projid, @$projf)) > 1) {
1081 $ret = 1;
1082 push(@err, [4, gettext('Duplicate projid "%s"'),
1083 $projid]);
1086 return ($ret, \@err);
1090 # projent_validate_unique_id(
1091 # ref to projent hash, ref to list of projent hashes)
1093 # Verifies that project name of the projent hash only exists once in the list
1094 # of projent hashes.
1096 # If the seconds argument is a hash ref, it is treated
1098 sub projent_validate_unique_name
1100 my ($projent, $projf, $namehash) = @_;
1101 my $ret = 0;
1102 my @err;
1103 my $pname = $projent->{'name'};
1105 if (scalar(grep($_->{'name'} eq $pname, @$projf)) > 1) {
1106 $ret = 1;
1107 push(@err,
1108 [9, gettext('Duplicate project name "%s"'), $pname]);
1111 return ($ret, \@err);
1115 # projent_validate(ref to projents hash, flags)
1117 # Checks that users, groups, and pools exists, and that known attributes
1118 # are valid. Attributes matching rctl names are verified to have valid
1119 # values given that rctl's global flags and max value.
1121 # Valid flag keys:
1123 # "allowspaces" - user and group list are allowed to contain whitespace
1124 # "res" - allow reserved project ids 0-99
1126 sub projent_validate
1128 my ($projent, $flags) = @_;
1129 my $ret = 0;
1130 my $ref;
1131 my @err;
1133 ($ret, $ref) =
1134 projent_validate_name($projent->{'name'}, $flags);
1135 if ($ret != 0) {
1136 push(@err, @$ref);
1138 ($ret, $ref) =
1139 projent_validate_projid($projent->{'projid'}, $flags);
1140 if ($ret != 0) {
1141 push(@err, @$ref);
1143 ($ret, $ref) =
1144 projent_validate_comment($projent->{'comment'}, $flags);
1145 if ($ret != 0) {
1146 push(@err, @$ref);
1148 ($ret, $ref) =
1149 projent_validate_users($projent->{'userlist'}, $flags);
1150 if ($ret != 0) {
1151 push(@err, @$ref);
1153 ($ret, $ref) =
1154 projent_validate_groups($projent->{'grouplist'}, $flags);
1155 if ($ret != 0) {
1156 push(@err, @$ref);
1158 ($ret, $ref) = projent_validate_attributes(
1159 $projent->{'attributelist'}, $flags);
1160 if ($ret != 0) {
1161 push(@err, @$ref);
1164 my $string = projent_2string($projent);
1165 if (length($string) > (&PROJECT_BUFSZ - 2)) {
1166 push(@err, [3, gettext('projent line too long')]);
1169 if (@err) {
1170 return (1, \@err);
1171 } else {
1172 return (0, $projent);
1177 # projent_validate_name(name, flags)
1179 # does nothing, as any parse-able project name is valid
1181 sub projent_validate_name
1183 my ($name, $flags) = @_;
1184 my @err;
1186 return (0, \@err);
1191 # projent_validate_projid(projid, flags)
1193 # Validates that projid is within the valid range of numbers.
1194 # Valid flag keys:
1195 # "res" - allow reserved projid's 0-99
1197 sub projent_validate_projid
1199 my ($projid, $flags) = @_;
1200 my @err;
1201 my $ret = 0;
1202 my $minprojid;
1204 if (defined($flags->{'res'})) {
1205 $minprojid = 0;
1206 } else {
1207 $minprojid = 100;
1210 if ($projid < $minprojid) {
1212 $ret = 1;
1213 push(@err, [3, gettext('Invalid projid "%s": '.
1214 'must be >= 100'),
1215 $projid]);
1219 return ($ret, \@err);
1223 # projent_validate_comment(name, flags)
1225 # Does nothing, as any parse-able comment is valid.
1227 sub projent_validate_comment
1229 my ($comment, $flags) = @_;
1230 my @err;
1232 return (0, \@err);
1236 # projent_validate_users(ref to list of user names, flags)
1238 # Verifies that each username is either a valid glob, such
1239 # as * or !*, or is an existing user. flags is unused.
1240 # Also validates that there are no duplicates.
1242 sub projent_validate_users
1244 my ($users, $flags) = @_;
1245 my @err;
1246 my $ret = 0;
1247 my $user;
1248 my $username;
1250 foreach $user (@$users) {
1252 if ($user eq '*' || $user eq '!*') {
1253 next;
1255 $username = $user;
1256 $username =~ s/^!//;
1258 if (!defined(getpwnam($username))) {
1259 $ret = 1;
1260 push(@err, [6,
1261 gettext('User "%s" does not exist'),
1262 $username]);
1266 my %seen;
1267 my @dups = grep($seen{$_}++ == 1, @$users);
1268 if (@dups) {
1269 $ret = 1;
1270 push(@err, [3, gettext('Duplicate user names "%s"'),
1271 join(',', @dups)]);
1273 return ($ret, \@err)
1277 # projent_validate_groups(ref to list of group names, flags)
1279 # Verifies that each groupname is either a valid glob, such
1280 # as * or !*, or is an existing group. flags is unused.
1281 # Also validates that there are no duplicates.
1283 sub projent_validate_groups
1285 my ($groups, $flags) = @_;
1286 my @err;
1287 my $ret = 0;
1288 my $group;
1289 my $groupname;
1291 foreach $group (@$groups) {
1293 if ($group eq '*' || $group eq '!*') {
1294 next;
1297 $groupname = $group;
1298 $groupname =~ s/^!//;
1300 if (!defined(getgrnam($groupname))) {
1301 $ret = 1;
1302 push(@err, [6,
1303 gettext('Group "%s" does not exist'),
1304 $groupname]);
1308 my %seen;
1309 my @dups = grep($seen{$_}++ == 1, @$groups);
1310 if (@dups) {
1311 $ret = 1;
1312 push(@err, [3, gettext('Duplicate group names "%s"'),
1313 join(',', @dups)]);
1316 return ($ret, \@err)
1320 # projent_validate_attribute(attribute hash ref, flags)
1322 # Verifies that if the attribute's name is a known attribute or
1323 # resource control, that it contains a valid value.
1324 # flags is unused.
1326 sub projent_validate_attribute
1328 my ($attribute, $flags) = @_;
1329 my $name = $attribute->{'name'};
1330 my $values = $attribute->{'values'};
1331 my $value;
1332 my @errs;
1333 my $ret = 0;
1334 my $result;
1335 my $ref;
1337 if (defined($values)) {
1338 $value = $values->[0];
1340 if ($name eq 'task.final') {
1342 if (defined($values)) {
1343 $ret = 1;
1344 push(@errs, [3, gettext(
1345 'task.final should not have value')]);
1348 # Need to rcap.max-rss needs to be a number
1349 } elsif ($name eq 'rcap.max-rss') {
1351 if (!defined($values)) {
1352 $ret = 1;
1353 push(@errs, [3, gettext(
1354 'rcap.max-rss missing value')]);
1355 } elsif (scalar(@$values) != 1) {
1356 $ret = 1;
1357 push(@errs, [3, gettext(
1358 'rcap.max-rss should have single value')]);
1360 if (!defined($value) || ref($value)) {
1361 $ret = 1;
1362 push(@errs, [3, gettext(
1363 'rcap.max-rss has invalid value "%s"'),
1364 projent_values2string($values)]);;
1365 } elsif ($value !~ /^\d+$/) {
1366 $ret = 1;
1367 push(@errs, [3, gettext(
1368 'rcap.max-rss is not an integer value: "%s"'),
1369 projent_values2string($values)]);;
1370 } elsif ($value > $MaxNum) {
1371 $ret = 1;
1372 push(@errs, [3, gettext(
1373 'rcap.max-rss too large')]);
1376 } elsif ($name eq 'project.pool') {
1377 if (!defined($values)) {
1378 $ret = 1;
1379 push(@errs, [3, gettext(
1380 'project.pool missing value')]);
1381 } elsif (scalar(@$values) != 1) {
1382 $ret = 1;
1383 push(@errs, [3, gettext(
1384 'project.pool should have single value')]);
1385 } elsif (!defined($value) || ref($value)) {
1386 $ret = 1;
1387 push(@errs, [3, gettext(
1388 'project.pool has invalid value "%s'),
1389 projent_values2string($values)]);;
1390 } elsif (!($value =~ /^[[:alpha:]][[:alnum:]_.-]*$/)) {
1391 $ret = 1;
1392 push(@errs, [3, gettext(
1393 'project.pool: invalid pool name "%s"'),
1394 $value]);
1395 # Pool must exist.
1396 } elsif (pool_exists($value) != 0) {
1397 $ret = 1;
1398 push(@errs, [6, gettext(
1399 'project.pool: pools not enabled or pool does '.
1400 'not exist: "%s"'),
1401 $value]);
1403 } else {
1404 my $rctlmax;
1405 my $rctlflags;
1406 my $rules;
1409 # See if rctl rules exist for this attribute. If so, it
1410 # is an rctl and is checked for valid values.
1413 # check hashed cache of rctl rules.
1414 $rules = $RctlRules{$name};
1415 if (!defined($rules)) {
1418 # See if this is an resource control name, if so
1419 # cache rules.
1421 ($rctlmax, $rctlflags) = rctl_get_info($name);
1422 if (defined($rctlmax)) {
1423 $rules = proj_getrctlrules(
1424 $rctlmax, $rctlflags);
1425 if (defined($rules)) {
1426 $RctlRules{$name} = $rules;
1427 } else {
1428 $RctlRules{$name} = "NOT AN RCTL";
1433 # If rules are defined, this is a resource control.
1434 if (defined($rules) && ref($rules)) {
1436 ($result, $ref) =
1437 projent_validate_rctl($attribute, $flags);
1438 if ($result != 0) {
1439 $ret = 1;
1440 push(@errs, @$ref);
1444 return ($ret, \@errs);
1448 # projent_validate_attributes(ref to attribute list, flags)
1450 # Validates all attributes in list of attribute references using
1451 # projent_validate_attribute. flags is unused.
1452 # flags is unused.
1454 sub projent_validate_attributes
1456 my ($attributes, $flags) = @_;
1457 my @err;
1458 my $ret = 0;
1459 my $result = 0;
1460 my $ref;
1461 my $attribute;
1463 foreach $attribute (@$attributes) {
1465 ($ret, $ref) = projent_validate_attribute($attribute, $flags);
1466 if ($ret != 0) {
1467 $result = $ret;
1468 push(@err, @$ref);
1472 my %seen;
1473 my @dups = grep($seen{$_}++ == 1, map { $_->{'name'} } @$attributes);
1474 if (@dups) {
1475 $result = 1;
1476 push(@err, [3, gettext('Duplicate attributes "%s"'),
1477 join(',', @dups)]);
1480 return ($result, \@err);
1484 # projent_getrctlrules(max value, global flags)
1486 # given an rctls max value and global flags, returns a ref to a hash
1487 # of rctl rules that is used by projent_validate_rctl to validate an
1488 # rctl's values.
1490 sub proj_getrctlrules
1492 my ($max, $flags) = @_;
1493 my $signals;
1494 my $rctl;
1496 $rctl = {};
1497 $signals =
1498 [ qw(ABRT XRES HUP STOP TERM KILL),
1499 $SigNo{'ABRT'},
1500 $SigNo{'XRES'},
1501 $SigNo{'HUP'},
1502 $SigNo{'STOP'},
1503 $SigNo{'TERM'},
1504 $SigNo{'KILL'} ];
1506 $rctl->{'max'} = $max;
1508 if ($flags & &RCTL_GLOBAL_BYTES) {
1509 $rctl->{'type'} = 'bytes';
1510 } elsif ($flags & &RCTL_GLOBAL_SECONDS) {
1511 $rctl->{'type'} = 'seconds';
1512 } elsif ($flags & &RCTL_GLOBAL_COUNT) {
1513 $rctl->{'type'} = 'count';
1514 } else {
1515 $rctl->{'type'} = 'unknown';
1517 if ($flags & &RCTL_GLOBAL_NOBASIC) {
1518 $rctl->{'privs'} = ['privileged', 'priv'];
1519 } else {
1520 $rctl->{'privs'} = ['basic', 'privileged', 'priv'];
1523 if ($flags & &RCTL_GLOBAL_DENY_ALWAYS) {
1524 $rctl->{'actions'} = ['deny'];
1526 } elsif ($flags & &RCTL_GLOBAL_DENY_NEVER) {
1527 $rctl->{'actions'} = ['none'];
1528 } else {
1529 $rctl->{'actions'} = ['none', 'deny'];
1532 if ($flags & &RCTL_GLOBAL_SIGNAL_NEVER) {
1533 $rctl->{'signals'} = [];
1535 } else {
1537 push(@{$rctl->{'actions'}}, 'sig');
1539 if ($flags & &RCTL_GLOBAL_CPU_TIME) {
1540 push(@$signals, 'XCPU', '30');
1542 if ($flags & &RCTL_GLOBAL_FILE_SIZE) {
1543 push(@$signals, 'XFSZ', '31');
1545 $rctl->{'signals'} = $signals;
1547 return ($rctl);
1551 # projent_val2num(scaled value, "seconds" | "count" | "bytes")
1553 # converts an integer or scaled value to an integer value.
1554 # returns (integer value, modifier character, unit character.
1556 # On failure, integer value is undefined. If the original
1557 # scaled value is a plain integer, modifier character and
1558 # unit character will be undefined.
1560 sub projent_val2num
1562 my ($val, $type) = @_;
1563 my %scaleM = ( k => 1000,
1564 m => 1000000,
1565 g => 1000000000,
1566 t => 1000000000000,
1567 p => 1000000000000000,
1568 e => 1000000000000000000);
1569 my %scaleB = ( k => 1024,
1570 m => 1048576,
1571 g => 1073741824,
1572 t => 1099511627776,
1573 p => 1125899906842624,
1574 e => 1152921504606846976);
1576 my $scale;
1577 my $base;
1578 my ($num, $modifier, $unit);
1579 my $mul;
1580 my $string;
1581 my $i;
1582 my $undefined;
1583 my $exp_unit;
1585 ($num, $modifier, $unit) = $val =~
1586 /^(\d+(?:\.\d+)?)(?i:([kmgtpe])?([bs])?)$/;
1588 # No numeric match.
1589 if (!defined($num)) {
1590 return ($undefined, $undefined, $undefined);
1593 # Decimal number with no scaling modifier.
1594 if (!defined($modifier) && $num =~ /^\d+\.\d+/) {
1595 return ($undefined, $undefined, $undefined);
1598 if ($type eq 'bytes') {
1599 $exp_unit = 'b';
1600 $scale = \%scaleB;
1601 } elsif ($type eq 'seconds') {
1602 $exp_unit = 's';
1603 $scale = \%scaleM;
1604 } else {
1605 $scale = \%scaleM;
1608 if (defined($unit)) {
1609 $unit = lc($unit);
1612 # So not succeed if unit is incorrect.
1613 if (!defined($exp_unit) && defined($unit)) {
1614 return ($undefined, $modifier, $unit);
1616 if (defined($unit) && $unit ne $exp_unit) {
1617 return ($undefined, $modifier, $unit);
1620 if (defined($modifier)) {
1622 $modifier = lc($modifier);
1623 $mul = $scale->{$modifier};
1624 $num = $num * $mul;
1627 # check for integer overflow.
1628 if ($num > $MaxNum) {
1629 return ("OVERFLOW", $modifier, $unit);
1632 # Trim numbers that are decimal equivalent to the maximum value
1633 # to the maximum integer value.
1635 if ($num == $MaxNum) {
1636 $num = $MaxNum;;
1638 } elsif ($num < $MaxNum) {
1639 # convert any decimal numbers to an integer
1640 $num = int($num);
1643 return ($num, $modifier, $unit);
1646 # projent_validate_rctl(ref to rctl attribute hash, flags)
1648 # verifies that the given rctl hash with keys "name" and
1649 # "values" contains valid values for the given name.
1650 # flags is unused.
1652 sub projent_validate_rctl
1654 my ($rctl, $flags) = @_;
1655 my $allrules;
1656 my $rules;
1657 my $name;
1658 my $values;
1659 my $value;
1660 my $valuestring;
1661 my $ret = 0;
1662 my @err;
1663 my $priv;
1664 my $val;
1665 my @actions;
1666 my $action;
1667 my $signal;
1668 my $sigstring; # Full signal string on right hand of signal=SIGXXX.
1669 my $signame; # Signal number or XXX part of SIGXXX.
1670 my $siglist;
1671 my $nonecount;
1672 my $denycount;
1673 my $sigcount;
1675 $name = $rctl->{'name'};
1676 $values = $rctl->{'values'};
1679 # Get the default rules for all rctls, and the specific rules for
1680 # this rctl.
1682 $allrules = $RctlRules{'__DEFAULT__'};
1683 $rules = $RctlRules{$name};
1685 if (!defined($rules) || !ref($rules)) {
1686 $rules = $allrules;
1689 # Allow for no rctl values on rctl.
1690 if (!defined($values)) {
1691 return (0, \@err);
1694 # If values exist, make sure it is a list.
1695 if (!ref($values)) {
1697 push(@err, [3, gettext(
1698 'rctl "%s" missing value'), $name]);
1699 return (1, \@err);
1702 foreach $value (@$values) {
1704 # Each value should be a list.
1706 if (!ref($value)) {
1707 $ret = 1;
1708 push(@err, [3, gettext(
1709 'rctl "%s" value "%s" should be in ()\'s'),
1710 $name, $value]);
1712 next;
1715 ($priv, $val, @actions) = @$value;
1716 if (!@actions) {
1717 $ret = 1;
1718 $valuestring = projent_values2string([$value]);
1719 push(@err, [3, gettext(
1720 'rctl "%s" value missing action "%s"'),
1721 $name, $valuestring]);
1724 if (!defined($priv)) {
1725 $ret = 1;
1726 push(@err, [3, gettext(
1727 'rctl "%s" value missing privilege "%s"'),
1728 $name, $valuestring]);
1730 } elsif (ref($priv)) {
1731 $ret = 1;
1732 $valuestring = projent_values2string([$priv]);
1733 push(@err, [3, gettext(
1734 'rctl "%s" invalid privilege "%s"'),
1735 $name, $valuestring]);
1737 } else {
1738 if (!(grep /^$priv$/, @{$allrules->{'privs'}})) {
1740 $ret = 1;
1741 push(@err, [3, gettext(
1742 'rctl "%s" unknown privilege "%s"'),
1743 $name, $priv]);
1745 } elsif (!(grep /^$priv$/, @{$rules->{'privs'}})) {
1747 $ret = 1;
1748 push(@err, [3, gettext(
1749 'rctl "%s" privilege not allowed '.
1750 '"%s"'), $name, $priv]);
1753 if (!defined($val)) {
1754 $ret = 1;
1755 push(@err, [3, gettext(
1756 'rctl "%s" missing value'), $name]);
1758 } elsif (ref($val)) {
1759 $ret = 1;
1760 $valuestring = projent_values2string([$val]);
1761 push(@err, [3, gettext(
1762 'rctl "%s" invalid value "%s"'),
1763 $name, $valuestring]);
1765 } else {
1766 if ($val !~ /^\d+$/) {
1767 $ret = 1;
1768 push(@err, [3, gettext(
1769 'rctl "%s" value "%s" is not '.
1770 'an integer'), $name, $val]);
1772 } elsif ($val > $rules->{'max'}) {
1773 $ret = 1;
1774 push(@err, [3, gettext(
1775 'rctl "%s" value "%s" exceeds '.
1776 'system limit'), $name, $val]);
1779 $nonecount = 0;
1780 $denycount = 0;
1781 $sigcount = 0;
1783 foreach $action (@actions) {
1785 if (ref($action)) {
1786 $ret = 1;
1787 $valuestring =
1788 projent_values2string([$action]);
1789 push(@err, [3, gettext(
1790 'rctl "%s" invalid action "%s"'),
1791 $name, $valuestring]);
1793 next;
1796 if ($action =~ /^sig(nal)?(=.*)?$/) {
1797 $signal = $action;
1798 $action = 'sig';
1800 if (!(grep /^$action$/, @{$allrules->{'actions'}})) {
1802 $ret = 1;
1803 push(@err, [3, gettext(
1804 'rctl "%s" unknown action "%s"'),
1805 $name, $action]);
1806 next;
1808 } elsif (!(grep /^$action$/, @{$rules->{'actions'}})) {
1810 $ret = 1;
1811 push(@err, [3, gettext(
1812 'rctl "%s" action not allowed "%s"'),
1813 $name, $action]);
1814 next;
1817 if ($action eq 'none') {
1818 if ($nonecount >= 1) {
1820 $ret = 1;
1821 push(@err, [3, gettext(
1822 'rctl "%s" duplicate action '.
1823 'none'), $name]);
1825 $nonecount++;
1826 next;
1828 if ($action eq 'deny') {
1829 if ($denycount >= 1) {
1831 $ret = 1;
1832 push(@err, [3, gettext(
1833 'rctl "%s" duplicate action '.
1834 'deny'), $name]);
1836 $denycount++;
1837 next;
1840 # action must be signal
1841 if ($sigcount >= 1) {
1843 $ret = 1;
1844 push(@err, [3, gettext(
1845 'rctl "%s" duplicate action sig'),
1846 $name]);
1848 $sigcount++;
1851 # Make sure signal is correct format, one of:
1852 # sig=##
1853 # signal=##
1854 # sig=SIGXXX
1855 # signal=SIGXXX
1856 # sig=XXX
1857 # signal=SIGXXX
1859 ($sigstring) = $signal =~
1861 (?:signal|sig)=
1862 (\d+|
1863 (?:SIG)?[[:upper:]]+(?:[+-][123])?
1865 $/x;
1867 if (!defined($sigstring)) {
1868 $ret = 1;
1869 push(@err, [3, gettext(
1870 'rctl "%s" invalid signal "%s"'),
1871 $name, $signal]);
1872 next;
1875 $signame = $sigstring;
1876 $signame =~ s/SIG//;
1878 # Make sure specific signal is allowed.
1879 $siglist = $allrules->{'signals'};
1880 if (!(grep /^$signame$/, @$siglist)) {
1881 $ret = 1;
1882 push(@err, [3, gettext(
1883 'rctl "%s" invalid signal "%s"'),
1884 $name, $signal]);
1885 next;
1887 $siglist = $rules->{'signals'};
1889 if (!(grep /^$signame$/, @$siglist)) {
1890 $ret = 1;
1891 push(@err, [3, gettext(
1892 'rctl "%s" signal not allowed "%s"'),
1893 $name, $signal]);
1894 next;
1898 if ($nonecount && ($denycount || $sigcount)) {
1899 $ret = 1;
1900 push(@err, [3, gettext(
1901 'rctl "%s" action "none" specified with '.
1902 'other actions'), $name]);
1906 if (@err) {
1907 return ($ret, \@err);
1908 } else {
1909 return ($ret, \@err);