# # Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "@(#)Project.pm 1.5 03/03/13 SMI" # # Project.pm provides the bootstrap for the Sun::Solaris::Project module, and # also functions for reading, validating and writing out project(4) format # files. # require 5.6.1; use strict; use warnings; package Sun::Solaris::Project; our $VERSION = '1.5'; use XSLoader; XSLoader::load(__PACKAGE__, $VERSION); our (@EXPORT_OK, %EXPORT_TAGS); my @constants = qw(MAXPROJID PROJNAME_MAX PROJF_PATH PROJECT_BUFSZ SETPROJ_ERR_TASK SETPROJ_ERR_POOL); my @syscalls = qw(getprojid); my @libcalls = qw(setproject activeprojects getprojent setprojent endprojent getprojbyname getprojbyid getdefaultproj fgetprojent inproj getprojidbyname); my @private = qw(projf_read projf_write proj_validate projf_validate); @EXPORT_OK = (@constants, @syscalls, @libcalls, @private); %EXPORT_TAGS = (CONSTANTS => \@constants, SYSCALLS => \@syscalls, LIBCALLS => \@libcalls, PRIVATE => \@private, ALL => \@EXPORT_OK); use base qw(Exporter); use Sun::Solaris::Utils qw(gettext); # # Read in the passed project filehandle. Returns a reference to an array of # project entries in the same format as returned by getprojent et al. # sub projf_read($) { my ($fh) = @_; my ($line, @projf); while (defined($line = <$fh>)) { my @proj; chomp($line); @proj = split(/:/, $line, 6); $proj[2] = '' if (! defined($proj[2])); $proj[3] = defined($proj[3]) ? [split(/,/, $proj[3])] : []; $proj[4] = defined($proj[4]) ? [split(/,/, $proj[4])] : []; $proj[5] = '' if (! defined($proj[5])); push(@projf, \@proj); } return(\@projf); } # # Write out to the passed project filehandle. Fisrt parameter is a filehandle, # second is a reference to an array of project entries in the same format as # returned by getprojent et al. # sub projf_write($$) { my ($fh, $projf) = @_; foreach my $proj (@$projf) { $proj->[3] = join(',', @{$proj->[3]}); $proj->[4] = join(',', @{$proj->[4]}); print($fh join(':', @$proj), "\n"); } } # # Validate a project entry in the same format as returned by getprojent et al. # The first arg is a reference to a project record as returned by getprojent. # The second argument is a reference to a flags hash, where the currently # understood flags are: # 'dup' - Allow duplicate projid # 'res' - Allow projid in the reserved (0-99) range # If project names are to be checked for uniqueness, a reference to a project # file array as returned by projf_read should be passed as the third argument. # In a scalar context the number of errors found will be returned, in a list # context a list of error messages for the entry will be returned. Each entry # in the list is in turn a list containing an exit code followed by a printf # format string and any required arguments. # sub proj_validate($;$$) { my ($proj_rec, $flag, $projf) = @_; my ($pname, $id, $comment, $user, $group, $attr) = @$proj_rec; $flag ||= {}; my ($low_projid, $linelen, @err); $low_projid = exists($flag->{res}) ? 0 : 100; $linelen = 0; # Validate project name. push(@err, [3, gettext("Invalid project name \"%s\""), $pname]) if ($pname !~ /^[A-Za-z][\w.-]*$/); push(@err, [9, gettext("Duplicate project name \"%s\""), $pname]) if (grep {$_->[0] eq $pname} @$projf); $linelen += length($pname) + 1; # Validate project id. if ($id !~ /^[+-]?\d+$/) { push(@err, [3, gettext("Invalid projid \"%s\": must be numeric"), $id]) } else { push(@err, [3, gettext("Invalid projid \"%d\": must be >= %d"), $id, $low_projid]) if ($id < $low_projid); push(@err, [3, gettext("Invalid projid \"%.f\": must be <= %d"), $id, &MAXPROJID]) if ($id > &MAXPROJID); push(@err, [4, gettext("Duplicate projid \"%d\""), $id]) if (! exists($flag->{dup}) && defined($projf) && grep { $_->[1] == $id } @$projf); } $linelen += length($id) + 1; # Validate comment. push(@err, [3, gettext("Invalid character \"%s\" in comment"), $1]) if ($comment =~ /([\n:])/); $linelen += length($comment) + 1; # Validate users. foreach my $u (@$user) { push(@err, [6, gettext("User \"%s\" does not exist"), $u]) if (! (($u =~ /^\d+$/ && defined(getpwuid($u))) || ($u =~ /^\*$/) || ($u =~ /^\!\*$/) || ($u =~ /^\!(\S+)$/ && defined(getpwnam($1))) || defined(getpwnam($u)))); $linelen += length($u) + 1; } $linelen += 1 if (! @$user); # Validate groups. foreach my $g (@$group) { push(@err, [6, gettext("Group \"%s\" does not exist"), $g]) if (! (($g =~ /^\d+$/ && defined(getgrgid($g))) || ($g =~ /^\*$/) || ($g =~ /^\!\*$/) || ($g =~ /^\!(\S+)$/ && defined(getgrnam($1))) || defined(getgrnam($g)))); $linelen += length($g) + 1; } $linelen += 1 if (! @$group); # Validate attribute string. push(@err, [3, gettext("Invalid attribute string \"%s\""), $attr]) if ($attr !~ /^$|^(?:[A-Za-z][\w.-]*=[^\s;]+)(?:;[A-Za-z][\w.-]*=[^\s;]+)*$/); $linelen += length($attr); # Validate line length. push(@err, [10, gettext("Project entry > %d bytes"), &PROJECT_BUFSZ]) if ($linelen > &PROJECT_BUFSZ); return (wantarray() ? @err : scalar(@err)); } # # Validate an entire project file as returned from projf_read. Applies # proj_validate to each entry, and returns a list of all the errors found # in the same format as proj_validate, with a line number appended to each # error message. # sub projf_validate($;$) { my @projf = @{shift(@_)}; # Make a copy of the array. my $flag = shift(@_); my @err; my $line = 1; my $where = gettext(" at line %d"); while (my $rec = shift(@projf)) { foreach my $e (proj_validate($rec, $flag, \@projf)) { $e->[1] .= $where; push(@$e, $line); push(@err, $e); } $line++; } return(wantarray() ? @err : scalar(@err)); } 1;