xref: /illumos-gate/usr/src/cmd/perl/contrib/Sun/Solaris/Project/t/Project.t (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1#
2# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3# Use is subject to license terms.
4#
5# CDDL HEADER START
6#
7# The contents of this file are subject to the terms of the
8# Common Development and Distribution License, Version 1.0 only
9# (the "License").  You may not use this file except in compliance
10# with the License.
11#
12# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
13# or http://www.opensolaris.org/os/licensing.
14# See the License for the specific language governing permissions
15# and limitations under the License.
16#
17# When distributing Covered Code, include this CDDL HEADER in each
18# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
19# If applicable, add the following below this CDDL HEADER, with the
20# fields enclosed by brackets "[]" replaced with your own identifying
21# information: Portions Copyright [yyyy] [name of copyright owner]
22#
23# CDDL HEADER END
24#
25#ident	"%Z%%M%	%I%	%E% SMI"
26#
27# test script for Sun::Solaris::Project
28#
29
30use warnings;
31use strict;
32use Data::Dumper;
33$Data::Dumper::Terse = 1;
34$Data::Dumper::Indent = 0;
35
36sub cleanup {
37	unlink("/tmp/project.$$.1");
38	unlink("/tmp/project.$$.2");
39	unlink("/tmp/project.$$.3");
40	unlink("/tmp/project.$$.4");
41	unlink("/tmp/project.$$.5");
42	unlink("/tmp/project.$$.1.w");
43	unlink("/tmp/project.$$.2.w");
44	unlink("/tmp/project.$$.3.w");
45	unlink("/tmp/project.$$.4.w");
46	unlink("/tmp/project.$$.5.w");
47	unlink("/tmp/projent.$$");
48}
49
50# 'use Sun::Solaris::Project;' counts as test 1
51our $test = 1;
52our $intest = 1;
53our $loaded = 0;
54
55#
56# Status reporting utils
57#
58# Expected calling sequence is:
59#	start()
60#	pass() or fail()
61#	start()
62#	pass() or fail()
63#	...
64#	...
65#
66#	Calling start() twice in a row will fail test.
67#	Calling start() and then exiting will fail test.
68#
69sub start
70{
71	if ($intest != 0) {
72		fatal("Started new test before finishing previous.");
73	}
74	$test++;
75	$intest = 1;
76	print "# Starting Test $test: @_\n" if (@_);
77}
78
79sub pass
80{
81	if ($intest == 0) {
82		fatal("pass() without start()");
83	}
84	print("ok $test @_\n");
85	$intest = 0;
86}
87
88sub fail
89{
90	if ($intest == 0) {
91		fatal("fail() without start()");
92	}
93	print("not ok $test @_\n");
94	$intest = 0;
95}
96
97sub fatal
98{
99	print(STDERR "FATAL!\n");
100	print("not ok $test @_\n");
101	exit(1);
102}
103
104sub comment
105{
106	print("# @_\n");
107}
108
109#
110# Read in a project file and build into the same data structure that we will
111# get if we do the same with the getXXX functions
112#
113
114sub read_pfile
115{
116	my ($fh) = @_;
117	my ($line, @a1, @a2);
118	while (defined($line = <$fh>)) {
119		chomp($line);
120		@a2 = split(/:/, $line, 6);
121		$a2[2] = '' if (! defined($a2[2]));
122		$a2[3] = defined($a2[3]) ? [ split(/,/, $a2[3]) ] : [];
123		$a2[4] = defined($a2[4]) ? [ split(/,/, $a2[4]) ] : [];
124		$a2[5] = '' if (! defined($a2[5]));
125		push(@a1, [ @a2 ]);
126	}
127	return(\@a1);
128}
129
130#
131# Compare two arrays of project structures & check for equivalence.
132# Converts each into a string using Data::Dumper and then does a string
133# comparison.  Dirty but effective :-)
134#
135
136sub cmp_recs
137{
138	my ($a1, $a2) = @_;
139	my $s1 = Dumper($a1);
140	my $s2 = Dumper($a2);
141
142	# Make sure numbers and quoted numbers compare the same
143	$s1 =~ s/'([+-]?[\d.]+)'/$1/g;
144	$s2 =~ s/'([+-]?[\d.]+)'/$1/g;
145
146	return($s1 eq $s2);
147}
148
149sub hash2string
150{
151	my ($key, $value);
152	my @strings;
153	my $string;
154	my $hash = $_[0];
155	foreach $key (keys(%$hash)) {
156		push(@strings, "$key => $hash->{$key}");
157	}
158	$string = "{ " . join(", ", @strings) . " }";
159	return ($string);
160}
161
162#
163# Main body of tests starts here.
164#
165
166# Check the module loads.
167BEGIN {
168	$| = 1;
169	print "1..548\n";
170}
171
172END {
173	fail("not ok 1") unless ($loaded);
174	fail("Exited during test!") if ($intest == 1);
175	cleanup();
176}
177
178use Sun::Solaris::Project qw(:ALL :PRIVATE);
179$loaded = 1;
180pass();
181
182start("Check the constants.");
183my ($fh, $line, $n1, $n2, $n3, $s);
184open($fh, "</usr/include/project.h") || fatal($!);
185while (defined($line = <$fh>)) {
186	$n1 = $1 if ($line =~ /#define\s+PROJNAME_MAX\s+(\d+)/);
187	$n2 = $1 if ($line =~ /#define\s+PROJECT_BUFSZ\s+(\d+)/);
188	$s = $1 if ($line =~ /#define\s+PROJF_PATH\s+"([^"]+)"/);
189}
190close($fh);
191open($fh, "</usr/include/sys/param.h") || fatal($!);
192while (defined($line = <$fh>)) {
193	$n3 = $1 if ($line =~ /#define\s+MAXUID\s+(\d+)/);
194}
195close($fh);
196if (! defined($s) || ! defined($n1) || ! defined($n2)) {
197	fail();
198} else {
199	if ($n1 == &PROJNAME_MAX && $n2 == &PROJECT_BUFSZ &&
200	    $n3 == &MAXPROJID && $s eq &PROJF_PATH) {
201		pass();
202	} else {
203		fail();
204	}
205}
206
207#
208# projf_read on various files with various flags.
209#
210# This table represents when projf_read should fail given a file
211# and flags.
212#
213# file/flags  # {}	validate	validate,res	validate,dup
214# ###################################################################
215# parse error #	no	no		no		no
216# dup names   #	yes	no		no		no
217# dup ids     #	yes	no		no		yes
218# system ids  #	yes	no		yes		no
219# all user    #	yes	yes		yes		yes
220#
221
222my $flags1 = {};
223my $flags2 = { "validate" => "true" };
224my $flags3 = { "validate" => "true", "res" => 1 };
225my $flags4 = { "validate" => "true", "dup" => 1 };
226
227# Make a temporary project files.
228my ($ret, $file1, $file2, $file3, $file4, $file5, $pass);
229
230# file1, parse error (extra ":") on group.staff project.
231open($file1, "+>/tmp/project.$$.1") || fatal($!);
232print $file1 <<EOF;
233test1:123:project one:root,bin:adm:attr1=a;attr2=b
234user.test2:456:project two:adm,uucp:staff:attr1=p;attr2=q
235group.test3:678:project three::root,nobody:root,lp:attr1=y;attr2=z
236test4:678:project four:root:root:
237test5:679:project five::sys:
238test6:690::::
239EOF
240
241# file2, duplicate project names.
242open($file2, "+>/tmp/project.$$.2") || fatal($!);
243print $file2 <<EOF;
244test1:123:project one:root,bin:adm:attr1=a;attr2=b
245user.test2:456:project two:adm,uucp:staff:attr1=p;attr2=q
246group.test3:677:project three:root,nobody:root,lp:attr1=y;attr2=z
247test1:678:project four:root:root:
248test5:679:project five::sys:
249test6:690::::
250EOF
251
252# file3, duplicate project ids.
253open($file3, "+>/tmp/project.$$.3") || fatal($!);
254print $file3 <<EOF;
255test1:123:project one:root,bin:adm:attr1=a;attr2=b
256user.test2:456:project two:adm,uucp:staff:attr1=p;attr2=q
257group.test3:677:project three:root,nobody:root,lp:attr1=y;attr2=z
258test4:678:project four:root:root:
259test5:678:project five::sys:
260test6:690::::
261EOF
262
263# file4, system project ids.
264open($file4, "+>/tmp/project.$$.4") || fatal($!);
265print $file4 <<EOF;
266system:0::::
267user.root:1::::
268noproject:2::::
269default:3::::
270group.staff:10::::
271test1:123:project one:root,bin:adm:attr1=a;attr2=b
272user.test2:456:project two:adm,uucp:staff:attr1=p;attr2=q
273group.test3:677:project three:root,nobody:root,lp:attr1=y;attr2=z
274test4:678:project four:root:root:
275test5:679:project five::sys:
276test6:690::::
277EOF
278
279# file5, all unique user projects.
280open($file5, "+>/tmp/project.$$.5") || fatal($!);
281print $file5 <<EOF;
282test1:123:project one:root,bin:adm:attr1=a;attr2=b
283user.test2:456:project two:adm,uucp:staff:attr1=p;attr2=q
284group.test3:677:project three:root,nobody:root,lp:attr1=y;attr2=z
285test4:678:project four:root:root:
286test5:679:project five::sys:
287test6:690::::
288EOF
289
290#
291# Each test is the file description, input file, filename, flags, and the expected
292# return value.
293#
294my @read_tests = (
295	[ "parse error", $file1, "/tmp/project.$$.1", $flags1, 1 ],
296	[ "parse error", $file1, "/tmp/project.$$.1", $flags2, 1 ],
297	[ "parse error", $file1, "/tmp/project.$$.1", $flags3, 1 ],
298	[ "parse error", $file1, "/tmp/project.$$.1", $flags4, 1 ],
299	[ "dup names", $file2, "/tmp/project.$$.2", $flags1, 0 ],
300	[ "dup names", $file2, "/tmp/project.$$.2", $flags2, 1 ],
301	[ "dup names", $file2, "/tmp/project.$$.2", $flags3, 1 ],
302	[ "dup names", $file2, "/tmp/project.$$.2", $flags4, 1 ],
303	[ "dup ids", $file3, "/tmp/project.$$.3", $flags1, 0 ],
304	[ "dup ids", $file3, "/tmp/project.$$.3", $flags2, 1 ],
305	[ "dup ids", $file3, "/tmp/project.$$.3", $flags3, 1 ],
306	[ "dup ids", $file3, "/tmp/project.$$.3", $flags4, 0 ],
307	[ "sys ids", $file4, "/tmp/project.$$.4", $flags1, 0 ],
308	[ "sys ids", $file4, "/tmp/project.$$.4", $flags2, 1 ],
309	[ "sys ids", $file4, "/tmp/project.$$.4", $flags3, 0 ],
310	[ "sys ids", $file4, "/tmp/project.$$.4", $flags4, 1 ],
311	[ "unique users", $file5, "/tmp/project.$$.5", $flags1, 0 ],
312	[ "unique users", $file5, "/tmp/project.$$.5", $flags2, 0 ],
313	[ "unique users", $file5, "/tmp/project.$$.5", $flags3, 0 ],
314	[ "unique users", $file5, "/tmp/project.$$.5", $flags4, 0 ]
315);
316
317my $projents;
318my @goodprojents;
319my $read_test;
320my $desc;
321my $file;
322my $filename;
323my $flags;
324my $flagstring;
325my $exp;
326my $error;
327
328# Do projf_read tests.
329foreach $read_test (@read_tests) {
330
331	($desc, $file, $filename, $flags, $exp) = @$read_test;
332	$flagstring = hash2string($flags);
333	start("projf_read(): $desc, flags: $flagstring, file: $filename");
334
335	seek($file, 0, 0);
336
337	($ret, $projents) = projf_read($file, $flags);
338	# check return is expected result
339	if ($ret != $exp) {
340		fail("Expected $exp, Returned $ret");
341		if ($ret) {
342			foreach $error (@$projents) {
343				comment("# " . join(", ", @$error));;
344			}
345		}
346		next;
347	}
348	# verify either projents or error messages were returned
349	if (!(@$projents)) {
350		fail("Missing projents or error messages");
351		next;
352	}
353	pass();
354
355	# Save projents from successful reads for testing projf_write.
356	if ($ret == 0) {
357		push(@goodprojents, [$desc, $flags, $projents, $filename]);
358	}
359}
360
361close($file1);
362close($file2);
363close($file3);
364close($file4);
365close($file5);
366
367# Test projf_write, write each successfully read file.
368
369my @write_tests;
370my $write_test;
371
372foreach $write_test (@goodprojents) {
373
374	($desc, $flags, $projents, $filename) = @$write_test;
375	$flagstring = hash2string($flags);
376	start("projf_write(): $desc, flags: $flagstring, file: $filename");
377
378	open($fh, ">$filename.w") || fatal($!);
379
380	projf_write($fh, $projents);
381	close($fh);
382	system("cmp -s $filename $filename.w") == 0 ? pass() :
383	    fail("Written file $filename.w does not match file $filename");
384}
385
386# Tests for projent_parse and projent_validate.
387
388my @projent_tests;
389my $projent_test;
390
391#
392# Tests, in format:
393#
394#  [ parse_result_expected, validate_result_expected, flags, project-line ]
395#
396@projent_tests = (
397
398# positive
399
400	[ 0, 0, { "res" => 1 }, "system:0::::" ],
401	[ 0, 0, { "res" => 1 }, "user.root:1::::" ],
402	[ 0, 0, { "res" => 1 }, "noproject:2::::" ],
403	[ 0, 0, { "res" => 1 }, "default:3::::" ],
404	[ 0, 0, { "res" => 1 }, "group.staff:10::::" ],
405	[ 0, 0, {}, "long:100::::" . "a" x 2048 ],
406	[ 0, 0, {}, "Validname:101::::" ],
407	[ 0, 0, {}, "Validname2:102::::" ],
408	[ 0, 0, {}, "valid3name:103::::" ],
409	[ 0, 0, {}, "VALIDNAME:104::::" ],
410	[ 0, 0, {}, "VALIDNAME5:105::::" ],
411	[ 0, 0, {}, "vAlid5name:106::::" ],
412	[ 0, 0, {}, "valid.name:107::::" ],
413	[ 0, 0, {}, "valid8.NAME:108::::" ],
414	[ 0, 0, {}, "Valid_name9:109::::" ],
415	[ 0, 0, {}, "V_alid.name10:110::::" ],
416	[ 0, 0, {}, "valid12345678901234567890123456789012345678901234567890123456789:111::::" ],
417	[ 0, 0, {}, "projid:2147483647::::" ],
418	[ 0, 0, {}, "comment:111: this is ! & my crazy	!@#$%^&*()_+|~`\=-][ 0, 0, {},}{';\"/.,?>< comment:::" ],
419	[ 0, 0, {}, "user1:112::*::" ],
420	[ 0, 0, {}, "user2:113::!*::" ],
421	[ 0, 0, {}, "user3:114::root::" ],
422	[ 0, 0, {}, "user4:115::!root::" ],
423	[ 0, 0, {}, "user5:116::*,!sys::" ],
424	[ 0, 0, {}, "user6:117::!*,daemon::" ],
425	[ 0, 0, {}, "user7:118::root,sys,daemon,bin::" ],
426	[ 0, 0, {}, "user8:119::root,!sys,daemon,!bin::" ],
427	[ 0, 0, { "allowspaces" => 1 }, "user9:116::*, !sys::" ],
428	[ 0, 0, { "allowspaces" => 1 }, "user10:117::!* ,daemon::" ],
429	[ 0, 0, { "allowspaces" => 1 }, "user11:118::root ,sys ,daemon, bin::" ],
430	[ 0, 0, { "allowspaces" => 1 }, "user12:119::root, !sys, daemon ,!bin::" ],
431	[ 0, 0, {}, "group1:120:::*:" ],
432	[ 0, 0, {}, "group2:121:::!*:" ],
433	[ 0, 0, {}, "group3:122:::root:" ],
434	[ 0, 0, {}, "group4:123:::!root:" ],
435	[ 0, 0, {}, "group5:124:::*,!sys:" ],
436	[ 0, 0, {}, "group6:125:::!*,daemon:" ],
437	[ 0, 0, {}, "group7:126:::root,sys,daemon,bin:" ],
438	[ 0, 0, {}, "group8:127:::root,!sys,daemon,!bin:" ],
439	[ 0, 0, { "allowspaces" => 1 }, "group9:124:::*, !sys:" ],
440	[ 0, 0, { "allowspaces" => 1 }, "group10:125:::!* ,daemon:" ],
441	[ 0, 0, { "allowspaces" => 1 }, "group11:126:::root, sys ,daemon, bin:" ],
442	[ 0, 0, { "allowspaces" => 1 }, "group12:127:::root ,!sys, daemon ,!bin:" ],
443	[ 0, 0, {}, "group9:128:::sys:" ],
444	[ 0, 0, {}, "attrib1:129::::one" ],
445	[ 0, 0, {}, "attrib2:130::::One" ],
446	[ 0, 0, {}, "attrib3:131::::ONE" ],
447	[ 0, 0, {}, "attrib4:132::::attrib10" ],
448	[ 0, 0, {}, "attrib5:133::::attrib.attrib=" ],
449	[ 0, 0, {}, "attrib6:134::::attib_" ],
450	[ 0, 0, {}, "attrib7:135::::a10-._attib" ],
451	[ 0, 0, {}, "attrib8:136::::SUNW,attrib" ],
452	[ 0, 0, {}, "attrib9:137::::A,A10=" ],
453	[ 0, 0, {}, "attrib10:138::::FIVEE,name" ],
454	[ 0, 0, {}, "attrib11:139::::one;two" ],
455	[ 0, 0, {}, "attrib12:140::::one=1;two=four" ],
456	[ 0, 0, {}, "attrib13:141::::one;two=;three=four" ],
457	[ 0, 0, {}, "value1:142::::one=foo,bar" ],
458	[ 0, 0, {}, "value2:143::::one=,bar," ],
459	[ 0, 0, {}, "value3:144::::one=(foo,bar)" ],
460	[ 0, 0, {}, "value4:145::::one=(foo,bar,baz),boo" ],
461	[ 0, 0, {}, "value5:146::::one;two=bar,(baz),foo,((baz)),(,)" ],
462	[ 0, 0, {}, "value6:147::::one=100/200" ],
463	[ 0, 0, {}, "value7:148::::two=.-_/=" ],
464	[ 0, 0, {}, "value8:149::::name=one=two" ],
465	[ 0, 0, { "allowunits" => 1 }, "value9:150::::task.max-lwps=(priv,1000M,deny,signal=SIGHUP),(priv,1000k,deny,signal=SIGKILL)" ],
466	[ 0, 0, {}, "comma1:151::,::" ],
467	[ 0, 0, {}, "comma2:152::,,::" ],
468	[ 0, 0, {}, "comma3:153::root,::" ],
469	[ 0, 0, {}, "comma4:154::bin,root,,::" ],
470	[ 0, 0, {}, "comma5:155:::,:" ],
471	[ 0, 0, {}, "comma6:156:::,,:" ],
472	[ 0, 0, {}, "comma7:157:::bin,root,:" ],
473	[ 0, 0, {}, "comma8:158:::root,,:" ],
474	[ 0, 0, {}, "semi1:159::::;" ],
475	[ 0, 0, {}, "semi2:160::::;;" ],
476	[ 0, 0, {}, "semi3:161::::foo=(one,two);" ],
477	[ 0, 0, {}, "semi4:162::::foo;;" ],
478	[ 0, 0, { "allowunits" => 1 }, "rctl1:163::::task.max-lwps=(priv,1000,deny,signal=HUP),(priv,1000k,deny,signal=15)" ],
479	[ 0, 0, {}, "rctl1:163::::task.max-lwps=(priv,1000,deny,signal=HUP),(priv,10001,deny,signal=15)" ],
480	[ 0, 0, {}, "rctl2:164::::process.max-port-events=(basic,1000,deny)" ],
481	[ 0, 0, { "allowunits" => 1 }, "rctl3:165::::project.max-crypto-memory=(priv,2.2gb,deny)" ],
482	[ 0, 0, {}, "rctl3:165::::project.max-crypto-memory=(priv,10,deny)" ],
483	[ 0, 0, { "allowunits" => 1 }, "rctl4:166::::project.max-crypto-memory=(privileged,100m,deny)" ],
484	[ 0, 0, {}, "rctl4:166::::project.max-crypto-memory=(privileged,100,deny)" ],
485	[ 0, 0, { "allowunits" => 1 }, "rctl5:167::::project.max-crypto-memory=(priv,1000m,deny)" ],
486	[ 0, 0, {}, "rctl5:167::::project.max-crypto-memory=(priv,1000,deny)" ],
487	[ 0, 0, { "allowunits" => 1 }, "rctl6:168::::project.max-crypto-memory=(priv,1000k,deny)" ],
488	[ 0, 0, { "allowunits" => 1 }, "rctl6:168::::project.max-crypto-memory=(priv,1000m,deny)" ],
489	[ 0, 0, {}, "rctl7:169::::process.max-msg-messages=(priv,10,deny)" ],
490	[ 0, 0, { "allowunits" => 1 }, "rctl8:170::::process.max-msg-qbytes=(priv,10000kb,deny)" ],
491	[ 0, 0, {}, "rctl8:170::::process.max-msg-qbytes=(priv,10000,deny)" ],
492	[ 0, 0, {}, "rctl9:171::::process.max-sem-ops=(priv,10000000,deny)" ],
493	[ 0, 0, {}, "rctl10:172::::process.max-sem-nsems=(basic,1,deny)" ],
494	[ 0, 0, { "allowunits" => 1 }, "rctl11:173::::process.max-address-space=(priv,2GB,deny)" ],
495	[ 0, 0, { "allowunits" => 1 }, "rctl12:174::::process.max-file-descriptor=(basic,1K,deny),(basic,2K,deny)" ],
496	[ 0, 0, { "allowunits" => 1 }, "rctl13:175::::process.max-core-size=(priv,10Mb,deny),(priv,2GB,deny)" ],
497	[ 0, 0, { "allowunits" => 1 }, "rctl14:176::::process.max-stack-size=(priv,1.8Gb,deny),(priv,100MB,deny)" ],
498	[ 0, 0, {}, "rctl15:177::::process.max-data-size=(priv,1010100101,deny)" ],
499	[ 0, 0, { "allowunits" => 1 }, "rctl16:178::::process.max-file-size=(priv,100mb,deny,signal=SIGXFSZ),(priv,1000mb,deny,signal=31)" ],
500	[ 0, 0, { "allowunits" => 1 }, "rctl17:179::::process.max-cpu-time=(priv,1t,signal=XCPU),(priv,100ms,sig=30)" ],
501	[ 0, 0, { "allowunits" => 1 }, "rctl18:180::::task.max-cpu-time=(priv,1M,sig=SIGKILL)" ],
502	[ 0, 0, { "allowunits" => 1 }, "rctl19:181::::task.max-lwps=(basic,10,signal=1),(priv,100,deny,signal=KILL)" ],
503	[ 0, 0, {}, "rctl20:182::::project.max-device-locked-memory=(priv,1000,deny,sig=TERM)" ],
504	[ 0, 0, {}, "rctl21:183::::project.max-port-ids=(priv,100,deny)" ],
505	[ 0, 0, { "allowunits" => 1 }, "rctl22:184::::project.max-shm-memory=(priv,1000mb,deny)" ],
506	[ 0, 0, { "allowunits" => 1 }, "rctl23:185::::project.max-shm-ids=(priv,1k,deny,signal=SIGSTOP)" ],
507	[ 0, 0, { "allowunits" => 1 }, "rctl24:186::::project.max-msg-ids=(priv,1m,deny,signal=XRES)" ],
508	[ 0, 0, {}, "rctl25:187::::project.max-sem-ids=(priv,10,deny,signal=ABRT)" ],
509	[ 0, 0, { "allowunits" => 1 }, "rctl26:188::::project.cpu-shares=(priv,63k,none)" ],
510	[ 0, 0, { "allowunits" => 1 }, "rctl27:189::::zone.cpu-shares=(priv,20k,none)" ],
511	[ 0, 0, {}, "rctl28:190::::zone.cpu-shares=(priv,100,none)" ],
512	[ 0, 0, { "allowunits" => 1 }, "rctl29:191::::project.max-shm-memory=(priv,200G,deny)" ],
513	[ 0, 0, { "allowunits" => 1 }, "rctl30:192::::project.max-shm-memory=(priv,200Gb,deny)" ],
514	[ 0, 0, { "allowunits" => 1 }, "rctl31:193::::project.max-shm-memory=(priv,2000B,deny)" ],
515	[ 0, 0, {}, "rctl32:194::::project.max-shm-memory=(priv,2000,deny)" ],
516	[ 0, 0, {}, "rctl33:195::::task.max-cpu-time=(priv,2000,none)" ],
517	[ 0, 0, { "allowunits" => 1 }, "rctl34:196::::task.max-cpu-time=(priv,2000s,none)" ],
518	[ 0, 0, { "allowunits" => 1 }, "rctl35:197::::task.max-cpu-time=(priv,20.1ps,none)" ],
519	[ 0, 0, { "allowunits" => 1 }, "rctl36:198::::task.max-cpu-time=(priv,20T,none)" ],
520
521# negative
522
523	[ 0, 1, {}, "system:0::::" ],
524	[ 0, 1, {}, "user.root:1::::" ],
525	[ 0, 1, {}, "noproject:2::::" ],
526	[ 0, 1, {}, "default:3::::" ],
527	[ 0, 1, {}, "group.staff:10::::" ],
528	[ 0, 1, {}, "long:100::::" . "a" x 4096 ],
529	[ 1, 0, {}, "extrafields:101:::::" ],
530	[ 1, 0, {}, "missingfields:102:::" ],
531	[ 1, 0, {}, "_invalidname:103::::" ],
532	[ 1, 0, {}, "10invlidname:104::::" ],
533	[ 1, 0, {}, "invalid%name:105::::" ],
534	[ 1, 0, {}, "invalid/name:106::::" ],
535	[ 1, 0, {}, ".invalidname:107::::" ],
536	[ 1, 0, {}, "=invalidName:108::::" ],
537	[ 1, 0, {}, "invalid=name:109::::" ],
538	[ 1, 0, {}, "invalid/name:110::::" ],
539	[ 1, 0, {}, "/invalidname:111::::" ],
540	[ 1, 0, {}, "/invalidname:112::::" ],
541	[ 1, 0, {}, "invalidname*:113::::" ],
542	[ 1, 0, {}, "invalid?name:114::::" ],
543	[ 1, 0, {}, ":115:invalid name comment:::" ],
544	[ 1, 0, {}, "invalid!name:116::::" ],
545	[ 1, 0, {}, "invalidname!:117::::" ],
546	[ 1, 0, {}, "invalid12345678901234567890123456789012345678901234567890123456789:118::::" ],
547	[ 1, 0, {}, "projid:-1::::" ],
548	[ 1, 0, {}, "projid:abc::::" ],
549	[ 1, 0, {}, "projid:2147483648::::" ],
550	[ 1, 0, {}, "projid:::::" ],
551	[ 1, 0, {}, "user1:118::*!::" ],
552	[ 1, 0, {}, "user2:119::10user::" ],
553	[ 0, 1, {}, "user3:120::NOLOWER::" ],
554	[ 0, 1, {}, "user4:121::toooolong::" ],
555	[ 1, 0, {}, "user5:122::root!::" ],
556	[ 1, 0, {}, "user6:123::root;sys::" ],
557	[ 0, 1, {}, "user7:124::sys,NOLOWER::" ],
558	[ 1, 0, {}, "user8:125::sys/bin,root::" ],
559	[ 1, 0, {}, "user9:116::*, !sys::" ],
560	[ 1, 0, {}, "user10:117::!* ,daemon::" ],
561	[ 1, 0, {}, "user11:118::root ,sys ,daemon, bin::" ],
562	[ 1, 0, {}, "user12:119::root, !sys, daemon ,!bin::" ],
563	[ 1, 0, {}, "group1:126:::*!:" ],
564	[ 0, 1, {}, "group2:127:::oneUpper:" ],
565	[ 0, 1, {}, "group3:128:::NOLOWER:" ],
566	[ 0, 1, {}, "group4:129:::toooolong:" ],
567	[ 1, 0, {}, "group5:130:::root!:" ],
568	[ 1, 0, {}, "group6:131:::root;sys:" ],
569	[ 0, 1, {}, "group7:132:::sys,NOLOWER:" ],
570	[ 1, 0, {}, "group8:133:::sys-bin,root:" ],
571	[ 1, 0, {}, "group9:124:::*, !sys:" ],
572	[ 1, 0, {}, "group10:125:::!* ,daemon:" ],
573	[ 1, 0, {}, "group11:126:::root, sys ,daemon, bin:" ],
574	[ 1, 0, {}, "group12:127:::root ,!sys, daemon ,!bin:" ],
575	[ 1, 0, {}, "attrib1:134::::10" ],
576	[ 1, 0, {}, "attrib2:135::::_foo=" ],
577	[ 1, 0, {}, "attrib3:136::::,foo" ],
578	[ 1, 0, {}, "attrib4:137::::sun,foo" ],
579	[ 1, 0, {}, "attrib6:139::::!attrib" ],
580	[ 1, 0, {}, "attrib7:140::::_attrib" ],
581	[ 1, 0, {}, "attrib8:141::::attib,attrib" ],
582	[ 1, 0, {}, "attrib9:142::::attrib/attrib" ],
583	[ 1, 0, {}, "attrib10:143::::one;two,three" ],
584	[ 1, 0, {}, "attrib11:144::::one=two;three/" ],
585	[ 1, 0, {}, "value1:145::::one=foo%" ],
586	[ 1, 0, {}, "value2:146::::one= two" ],
587	[ 1, 0, {}, "value3:147::::var=foo?" ],
588	[ 1, 0, {}, "value4:148::::name=value;name=value2)" ],
589	[ 1, 0, {}, "value5:149::::(foo)" ],
590	[ 1, 0, {}, "value6:150::::name=(foo,bar" ],
591	[ 1, 0, {}, "value7:151::::name=(value)(value)" ],
592	[ 1, 0, {}, "value8:152::::name=)" ],
593	[ 1, 0, {}, "value9:153::::name=value,(value value)" ],
594	[ 1, 0, {}, "value10:154::::name=(value(value))" ],
595	[ 1, 0, {}, "value11:155::::name=(value)value" ],
596	[ 1, 0, {}, "value11:156::::name=va?lue" ],
597	[ 1, 0, {}, "value12:157::::name=(value,value))" ],
598	[ 1, 0, {}, "value13:158::::name=(value),value)" ],
599	[ 1, 0, {}, "space1 :159::::" ],
600	[ 1, 0, {}, " space2:160::::" ],
601	[ 1, 0, {}, "space3: 161::::" ],
602	[ 1, 0, {}, "space4:162 ::::" ],
603	[ 1, 0, {}, "space 5:163::::" ],
604	[ 1, 0, {}, "space6:1 64::::" ],
605	[ 1, 0, {}, "space7:165:: root::" ],
606	[ 1, 0, {}, "space8:166::root ::" ],
607	[ 1, 0, {}, "space9:167::daemon, root::" ],
608	[ 1, 0, {}, "space10:168::bin root::" ],
609	[ 1, 0, {}, "space11:169::daemon ,root::" ],
610	[ 1, 0, {}, "space12 :170::::" ],
611	[ 1, 0, {}, " space13:171::::" ],
612	[ 1, 0, {}, "space14: 172::::" ],
613	[ 1, 0, {}, "space15:173 ::::" ],
614	[ 1, 0, {}, "space 16:174::::" ],
615	[ 1, 0, {}, "space17:1 75::::" ],
616	[ 1, 0, {}, "space18:176::: root:" ],
617	[ 1, 0, {}, "space19:177:::root :" ],
618	[ 1, 0, {}, "space20:178:::daemon, root:" ],
619	[ 1, 0, {}, "space21:179:::bin root:" ],
620	[ 1, 0, {}, "space22:180:::daemon ,root:" ],
621	[ 1, 0, {}, "space23:181:::: foo" ],
622	[ 1, 0, {}, "space34:182::::foo =one" ],
623	[ 1, 0, {}, "space35:183::::foo= (one)" ],
624	[ 1, 0, {}, "space36:184::::foo=(one, two)" ],
625	[ 1, 0, {}, "space37:185::::foo=(one ,two)" ],
626	[ 1, 0, {}, "space38:186::::foo=( one)" ],
627	[ 1, 0, {}, "space39:187::::foo=(one )" ],
628	[ 1, 0, {}, "space40:188::::foo=(one) ,two" ],
629	[ 1, 0, {}, "space41:189::::foo=one, (two)" ],
630	[ 1, 0, {}, "comma1:190::,root,bin::" ],
631	[ 1, 0, {}, "comma2:191::root,,bin::" ],
632	[ 1, 0, {}, "comma3:192::,,root,bin::" ],
633	[ 1, 0, {}, "comma4:193:::,root,bin:" ],
634	[ 1, 0, {}, "comma5:194:::root,,bin:" ],
635	[ 1, 0, {}, "comma6:195:::,,root,bin:" ],
636	[ 1, 0, {}, "semi1:196::::;foo" ],
637	[ 1, 0, {}, "semi2:197::::foo;;bar=1" ],
638	[ 1, 0, {}, "semi3:198::::;;bar=(10)" ],
639	[ 0, 1, {}, "rctl1:199::::task.max-lwps=," ],
640	[ 0, 1, {}, "rctl2:200::::task.max-lwps=" ],
641	[ 0, 1, {}, "rctl3:201::::task.max-lwps=priv" ],
642	[ 0, 1, {}, "rctl4:202::::task.max-lwps=priv,1000" ],
643	[ 0, 1, {}, "rctl5:203::::task.max-lwps=priv,1000,deny" ],
644	[ 0, 1, {}, "rctl6:204::::task.max-lwps=(priv)" ],
645	[ 0, 1, {}, "rctl7:205::::task.max-lwps=(priv,1000)" ],
646	[ 0, 1, {}, "rctl8:206::::task.max-lwps=(foo,100,deny)" ],
647	[ 0, 1, {}, "rctl9:207::::task.max-lwps=(priv,foo,none)" ],
648	[ 1, 0, { "allowunits" => 1 }, "rctl9:207::::task.max-lwps=(priv,foo,none)" ],
649	[ 1, 0, { "allowunits" => 1 }, "rctl10:208::::task.max-lwps=(priv,100foo,none)" ],
650	[ 0, 1, {}, "rctl11:209::::task.max-lwps=(priv,1000,foo)" ],
651	[ 0, 1, { "allowunits" => 1 }, "rctl12:210::::task.max-lwps=(priv,1000k,deny,signal)" ],
652	[ 0, 1, {}, "rctl13:211::::task.max-lwps=(priv,1000,deny,signal=)" ],
653	[ 0, 1, {}, "rctl14:212::::task.max-lwps=(priv,1000,deny,signal=foo)" ],
654	[ 0, 1, {}, "rctl15:213::::task.max-lwps=(priv,1000,deny,signal=1fo)" ],
655	[ 0, 1, {}, "rctl16:214::::task.max-lwps=(priv,1000,deny,signal=100)" ],
656	[ 0, 1, {}, "rctl17:215::::task.max-lwps=(priv,1000,deny,signal=SIG)" ],
657	[ 0, 1, {}, "rctl18:216::::task.max-lwps=(priv,1000,deny,signal=SIG1)" ],
658	[ 0, 1, {}, "rctl19:217::::task.max-lwps=(priv,1000,deny,signal=SIGhup)" ],
659	[ 0, 1, {}, "rctl20:218::::task.max-lwps=(priv,1000,deny,signal=SIGHU)" ],
660	[ 0, 1, {}, "rctl21:219::::task.max-lwps=(priv,1000,deny,signal=SIGHUPP)" ],
661	[ 0, 1, {}, "rctl22:220::::task.max-lwps=(priv,1000,deny,signal=SIGURG)" ],
662	[ 0, 1, {}, "rctl23:221::::task.max-lwps=(priv,1000,deny,signal=SIGXCPU)" ],
663	[ 0, 1, {}, "rctl24:222::::task.max-lwps=(priv,1000,deny,signal=SIGKILL,10)" ],
664	[ 0, 1, {}, "rctl25:223::::task.max-lwps=(priv,1000,deny,signal=SIGKILL,foo)" ],
665	[ 0, 1, {}, "rctl26:224::::process.max-port-events=(priv,1000,none)" ],
666	[ 0, 1, { "allowunits" => 1 }, "rctl27:225::::process.max-address-space=(basic,1024mb,deny,signal=TERM)" ],
667	[ 0, 1, {}, "rctl28:226::::process.max-cpu-time=(basic,3600,deny)" ],
668	[ 0, 1, {}, "rctl29:227::::task.max-lwps=()" ],
669	[ 0, 1, {}, "rctl30:228::::task.max-lwps=((priv),deny)" ],
670	[ 0, 1, {}, "rctl31:229::::task.max-lwps=((priv,1000,deny))" ],
671	[ 0, 1, {}, "rctl32:230::::task.max-lwps=(priv,((1000,2000,1000)),deny)" ],
672	[ 0, 1, {}, "rctl33:231::::task.max-lwps=(,,,)" ],
673	[ 0, 1, {}, "rctl34:232::::task.max-lwps=(priv,1000,(deny))" ],
674	[ 0, 1, {}, "rctl35:233::::task.max-lwps=(priv,1000,deny),foo" ],
675	[ 0, 1, {}, "rctl36:234::::task.max-lwps=(priv,1000,deny),(priv,1000)" ],
676	[ 1, 0, { "allowunits" => 1 }, "rctl37:235::::project.max-msg-ids=(priv,15EB,deny)" ],
677	[ 1, 0, { "allowunits" => 1 }, "rctl38:236::::process.max-address-space=(priv,16.1EB,deny)" ],
678	[ 1, 0, { "allowunits" => 1 }, "rctl39:237::::process.max-address-space=(priv,18000000000gb,deny)" ],
679	[ 1, 0, { "allowunits" => 1 }, "rctl40:238::::zone.cpu-shares=(priv,10kb,none)" ],
680	[ 1, 0, { "allowunits" => 1 }, "rctl41:239::::zone.cpu-shares=(priv,10Ks,none)" ],
681	[ 1, 0, { "allowunits" => 1 }, "rctl42:240::::zone.cpu-shares=(priv,10s,none)" ],
682	[ 1, 0, { "allowunits" => 1 }, "rctl43:241::::zone.cpu-shares=(priv,100000b,none)" ],
683	[ 1, 0, { "allowunits" => 1 }, "rctl44:242::::project.max-shm-memory=(priv,200Ts,deny)" ],
684	[ 1, 0, { "allowunits" => 1 }, "rctl45:243::::project.max-shm-memory=(priv,200s,deny)" ],
685	[ 1, 0, { "allowunits" => 1 }, "rctl46:244::::task.max-cpu-time=(priv,20B,none)" ],
686	[ 1, 0, { "allowunits" => 1 }, "rctl47:245::::task.max-cpu-time=(priv,20Kb,none)" ],
687	[ 0, 1, { "allowunits" => 1 }, "rctl48:246::::project.cpu-shares=(priv,100k,none)" ],
688	[ 0, 1, {}, "rctl147:150::::task.max-lwps=(priv,1000M,deny,signal=SIGHUP),(priv,1000k,deny,signal=SIGKILL)" ],
689	[ 0, 1, {}, "rctl148:163::::task.max-lwps=(priv,1000,deny,signal=HUP),(priv,1000k,deny,signal=15)" ],
690	[ 0, 1, {}, "rctl3:165::::project.max-crypto-memory=(priv,10eb,deny)" ],
691	[ 0, 1, {}, "rctl4:166::::project.max-crypto-memory=(privileged,100p,deny)" ],
692	[ 0, 1, {}, "rctl5:167::::project.max-crypto-memory=(priv,1000t,deny)" ],
693	[ 0, 1, {}, "rctl6:168::::project.max-crypto-memory=(priv,1000g,deny)" ],
694	[ 0, 1, {}, "rctl7:169::::process.max-msg-messages=(priv,10m,deny)" ],
695	[ 0, 1, {}, "rctl8:170::::process.max-msg-qbytes=(priv,10000kb,deny)" ],
696	[ 0, 1, {}, "rctl11:173::::process.max-address-space=(priv,10EB,deny)" ],
697	[ 0, 1, {}, "rctl12:174::::process.max-file-descriptor=(basic,1K,deny),(basic,2K,deny)" ],
698	[ 0, 1, {}, "rctl13:175::::process.max-core-size=(priv,1Eb,deny),(priv,10PB,deny)" ],
699	[ 0, 1, {}, "rctl14:176::::process.max-stack-size=(priv,10Tb,deny),(priv,10TB,deny)" ],
700	[ 0, 1, {}, "rctl16:178::::process.max-file-size=(priv,100mb,deny,signal=SIGXFSZ),(priv,1000mb,deny,signal=31)" ],
701	[ 0, 1, {}, "rctl17:179::::process.max-cpu-time=(priv,1t,signal=XCPU),(priv,100ms,sig=30)" ],
702	[ 0, 1, {}, "rctl18:180::::task.max-cpu-time=(priv,1M,sig=SIGKILL)" ],
703	[ 0, 1, {}, "rctl22:184::::project.max-shm-memory=(priv,1000mb,deny)" ],
704	[ 0, 1, {}, "rctl23:185::::project.max-shm-ids=(priv,1k,deny,signal=SIGSTOP)" ],
705	[ 0, 1, {}, "rctl24:186::::project.max-msg-ids=(priv,1m,deny,signal=XRES)" ],
706	[ 0, 1, {}, "rctl26:188::::project.cpu-shares=(priv,63k,none)" ],
707	[ 0, 1, {}, "rctl27:189::::zone.cpu-shares=(priv,20k,none)" ],
708	[ 0, 1, {}, "rctl29:191::::project.max-shm-memory=(priv,200G,deny)" ],
709	[ 0, 1, {}, "rctl30:192::::project.max-shm-memory=(priv,200Gb,deny)" ],
710	[ 0, 1, {}, "rctl31:193::::project.max-shm-memory=(priv,2000B,deny)" ],
711	[ 0, 1, {}, "rctl34:196::::task.max-cpu-time=(priv,2000s,none)" ],
712	[ 0, 1, {}, "rctl35:197::::task.max-cpu-time=(priv,20.1ps,none)" ],
713	[ 0, 1, {}, "rctl36:198::::task.max-cpu-time=(priv,20T,none)" ],
714);
715
716my $parse_exp;
717my $parse_ret;
718my $validate_exp;
719my $validate_ret;
720my $project;
721my $projent;
722my $errors;
723
724foreach $projent_test ( @projent_tests) {
725
726	($parse_exp, $validate_exp, $flags, $project) = @$projent_test;
727	$flagstring = hash2string($flags);
728	start("projent_parse(): flags: $flagstring, project: $project");
729	($ret, $projent) = projent_parse($project, $flags);
730	if ($ret != $parse_exp) {
731		fail("Expected $parse_exp, Returned $ret");
732		if ($ret) {
733			foreach $error (@$projent) {
734				comment("# " . join(", ", @$error));
735			}
736		}
737		next;
738	}
739	pass();
740
741	# projent_validate() can only be successfully parsed projents
742	if ($ret) {
743		next;
744	}
745
746	start("projent_validate():  flags: $flagstring, project: $project");
747	($ret, $errors) = projent_validate($projent, $flags);
748	if ($ret != $validate_exp) {
749		fail("Expected $validate_exp, Returned $ret");
750		if ($ret) {
751			foreach $error (@$errors) {
752				comment("# " . join(", ", @$error));
753			}
754		}
755		next;
756	}
757	pass();
758}
759
760my $pf1;
761my $pf2;
762my $fh1;
763my $fh2;
764my @lines;
765
766# get projects and make local copy
767open($fh1, "/usr/bin/getent project |") || fatal($!);
768open($fh2, ">/tmp/projent.$$") || fatal($!);
769@lines = <$fh1>;
770print $fh2 @lines;
771close($fh1);
772close($fh2);
773
774open($fh1, "</tmp/projent.$$") || fatal($!);
775$pf1 = read_pfile($fh1);
776close($fh1);
777
778
779start("Test getprojid");
780($s) = `/usr/xpg4/bin/id -p` =~ /projid=(\d+)/;
781defined($s) && $s == getprojid() ? pass() : fail();
782
783start("Test fgetprojent");
784$pf2 = [];
785open($fh, "</tmp/projent.$$") || fatal($!);
786while (my @proj = fgetprojent($fh)) {
787	push(@$pf2, [ @proj ]);
788}
789close($fh);
790cmp_recs($pf1, $pf2) ? pass() : fail();
791
792my %pf_byname = map({ $_->[0] => $_} @$pf1);
793my %pf_byid = map({ $_->[1] => $_} @$pf1);
794my (%h, @a1, @a2, $k, $v);
795
796start("Test getprojent.  Don't assume anything about the order it returns stuff in");
797%h = %pf_byname;
798$pass = 1;
799@a2 = ();
800while (@a1 = getprojent()) {
801	@a2 = @a1 if (! scalar(@a2));
802	if (exists($h{$a1[0]})) {
803		$pass = 0 if (! cmp_recs(\@a1, $h{$a1[0]}));
804		delete($h{$a1[0]});
805	} else {
806		$pass = 0;
807	}
808}
809$pass && ! %h ? pass() : fail();
810
811start("Test getprojent when at end");
812@a1 = getprojent();
813cmp_recs(\@a1, []) ? pass() : fail();
814
815
816start("Test endprojent/getprojent");
817endprojent();
818@a1 = getprojent();
819cmp_recs(\@a1, \@a2) ? pass() : fail();
820
821start("Test setprojent/getprojent");
822setprojent();
823@a1 = getprojent();
824cmp_recs(\@a1, \@a2) ? pass() : fail();
825setprojent();
826
827start("Test getprojbyname");
828$pass = 1;
829while (($k, $v) = each(%pf_byname)) {
830	@a1 = getprojbyname($k);
831	$pass = 0 if (! cmp_recs(\@a1, $v));
832}
833$pass ? pass() : fail();
834
835start("Test getprojbyid");
836$pass = 1;
837while (($k, $v) = each(%pf_byid)) {
838	@a1 = getprojbyid($k);
839	$pass = 0 if (! cmp_recs(\@a1, $v));
840}
841$pass ? pass() : fail();
842
843start("Test getprojidbyname");
844$pass = 1;
845while (($k, $v) = each(%pf_byname)) {
846	$pass = 0 if (getprojidbyname($k) != $v->[1]);
847}
848$pass ? pass() : fail();
849
850start("Test getdefaultproj");
851my $username = getpwuid($>);
852my $projid;
853$s = `/usr/bin/id -p` ;
854($projid) = $s =~ /projid=\d+\(([^)]+)\)/;
855defined($projid) && $projid eq getdefaultproj($username) ? pass() : fail();
856
857start("test inproj");
858$s = `/usr/bin/projects`;
859($s) = split(/\s+/, $s);
860inproj($username, $s) ? pass() : fail();
861
862exit(0);
863