xref: /illumos-gate/usr/src/cmd/projadd/projmod.pl (revision 532877c4)
17c478bd9Sstevel@tonic-gate#!/usr/perl5/bin/perl -w
27c478bd9Sstevel@tonic-gate#
37c478bd9Sstevel@tonic-gate# CDDL HEADER START
47c478bd9Sstevel@tonic-gate#
57c478bd9Sstevel@tonic-gate# The contents of this file are subject to the terms of the
6*532877c4Srd# Common Development and Distribution License (the "License").
7*532877c4Srd# You may not use this file except in compliance with the License.
87c478bd9Sstevel@tonic-gate#
97c478bd9Sstevel@tonic-gate# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate# or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate# See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate# and limitations under the License.
137c478bd9Sstevel@tonic-gate#
147c478bd9Sstevel@tonic-gate# When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate# If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate# fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate# information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate#
207c478bd9Sstevel@tonic-gate# CDDL HEADER END
217c478bd9Sstevel@tonic-gate#
227c478bd9Sstevel@tonic-gate#
23*532877c4Srd# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate# Use is subject to license terms.
257c478bd9Sstevel@tonic-gate#
267c478bd9Sstevel@tonic-gate#ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate#
287c478bd9Sstevel@tonic-gate
297c478bd9Sstevel@tonic-gaterequire 5.005;
307c478bd9Sstevel@tonic-gateuse strict;
317c478bd9Sstevel@tonic-gateuse locale;
327c478bd9Sstevel@tonic-gateuse Errno;
337c478bd9Sstevel@tonic-gateuse Fcntl;
347c478bd9Sstevel@tonic-gateuse File::Basename;
357c478bd9Sstevel@tonic-gateuse Getopt::Std;
367c478bd9Sstevel@tonic-gateuse Getopt::Long qw(:config no_ignore_case bundling);
377c478bd9Sstevel@tonic-gateuse POSIX qw(locale_h);
387c478bd9Sstevel@tonic-gateuse Sun::Solaris::Utils qw(textdomain gettext);
397c478bd9Sstevel@tonic-gateuse Sun::Solaris::Project qw(:ALL :PRIVATE);
40*532877c4Srduse Sun::Solaris::Task qw(:ALL);
417c478bd9Sstevel@tonic-gate
427c478bd9Sstevel@tonic-gate#
437c478bd9Sstevel@tonic-gate# Print a usage message and exit.
447c478bd9Sstevel@tonic-gate#
457c478bd9Sstevel@tonic-gatesub usage
467c478bd9Sstevel@tonic-gate{
477c478bd9Sstevel@tonic-gate	my (@msg) = @_;
487c478bd9Sstevel@tonic-gate	my $prog = basename($0);
497c478bd9Sstevel@tonic-gate	my $space = ' ' x length($prog);
507c478bd9Sstevel@tonic-gate	print(STDERR "$prog: @msg\n") if (@msg);
517c478bd9Sstevel@tonic-gate	printf(STDERR gettext(
527c478bd9Sstevel@tonic-gate	    "Usage: %s [-n] [-f filename]\n"), $prog);
537c478bd9Sstevel@tonic-gate	printf(STDERR gettext(
54*532877c4Srd	    "       %s [-n] [-A|-f filename] [-p projid [-o]] [-c comment]\n".
557c478bd9Sstevel@tonic-gate            "       %s [-a|-s|-r] [-U user[,user...]] [-G group[,group...]]\n".
567c478bd9Sstevel@tonic-gate            "       %s [-K name[=value[,value...]]] [-l new_projectname] ".
577c478bd9Sstevel@tonic-gate	    "project\n"), $prog, $space, $space, $space);
587c478bd9Sstevel@tonic-gate	exit(2);
597c478bd9Sstevel@tonic-gate}
607c478bd9Sstevel@tonic-gate
617c478bd9Sstevel@tonic-gate#
627c478bd9Sstevel@tonic-gate# Print a list of error messages and exit.
637c478bd9Sstevel@tonic-gate#
647c478bd9Sstevel@tonic-gatesub error
657c478bd9Sstevel@tonic-gate{
667c478bd9Sstevel@tonic-gate	my $exit = $_[0][0];
677c478bd9Sstevel@tonic-gate	my $prog = basename($0) . ': ';
687c478bd9Sstevel@tonic-gate	foreach my $err (@_) {
697c478bd9Sstevel@tonic-gate		my ($e, $fmt, @args) = @$err;
707c478bd9Sstevel@tonic-gate		printf(STDERR $prog . $fmt . "\n", @args);
717c478bd9Sstevel@tonic-gate	}
727c478bd9Sstevel@tonic-gate	exit($exit);
737c478bd9Sstevel@tonic-gate}
747c478bd9Sstevel@tonic-gate
757c478bd9Sstevel@tonic-gate#
767c478bd9Sstevel@tonic-gate# Merge an array of users/groups with an existing array.  The array to merge
777c478bd9Sstevel@tonic-gate# is the first argument, an array ref is the second argument.  The third
787c478bd9Sstevel@tonic-gate# argument is the mode which can be one of:
797c478bd9Sstevel@tonic-gate#     add	add all entries in the first arg to the second
807c478bd9Sstevel@tonic-gate#     remove	remove all entries in the first arg from the second
817c478bd9Sstevel@tonic-gate#     replace	replace the second arg by the first
827c478bd9Sstevel@tonic-gate# The resulting array is returned as a reference.
837c478bd9Sstevel@tonic-gate#
847c478bd9Sstevel@tonic-gatesub merge_lists
857c478bd9Sstevel@tonic-gate{
867c478bd9Sstevel@tonic-gate	my ($new, $old, $mode) = @_;
877c478bd9Sstevel@tonic-gate	my @err;
887c478bd9Sstevel@tonic-gate
897c478bd9Sstevel@tonic-gate	if ($mode eq 'add') {
907c478bd9Sstevel@tonic-gate		my @merged = @$old;
917c478bd9Sstevel@tonic-gate		my %look = map { $_ => 1 } @$old;
927c478bd9Sstevel@tonic-gate		my @leftover;
937c478bd9Sstevel@tonic-gate		foreach my $e (@$new) {
947c478bd9Sstevel@tonic-gate			if (! exists($look{$e})) {
957c478bd9Sstevel@tonic-gate				push(@merged, $e);
967c478bd9Sstevel@tonic-gate			} else {
977c478bd9Sstevel@tonic-gate				push(@leftover, $e);
987c478bd9Sstevel@tonic-gate			}
997c478bd9Sstevel@tonic-gate		}
1007c478bd9Sstevel@tonic-gate		if (@leftover) {
1017c478bd9Sstevel@tonic-gate			push(@err,
1027c478bd9Sstevel@tonic-gate			    [6, gettext('Project already contains "%s"'),
1037c478bd9Sstevel@tonic-gate			    join(',', @leftover)]);
1047c478bd9Sstevel@tonic-gate			return (1, \@err);
1057c478bd9Sstevel@tonic-gate		}
1067c478bd9Sstevel@tonic-gate
1077c478bd9Sstevel@tonic-gate		return(0, \@merged);
1087c478bd9Sstevel@tonic-gate
1097c478bd9Sstevel@tonic-gate	} elsif ($mode eq 'remove') {
1107c478bd9Sstevel@tonic-gate
1117c478bd9Sstevel@tonic-gate		my %seen;
1127c478bd9Sstevel@tonic-gate		my @dups = grep($seen{$_}++ == 1, @$new);
1137c478bd9Sstevel@tonic-gate		if (@dups) {
1147c478bd9Sstevel@tonic-gate			push(@err, [6, gettext('Duplicate names "%s"'),
1157c478bd9Sstevel@tonic-gate			    join(',', @dups)]);
1167c478bd9Sstevel@tonic-gate			return (1, \@err);
1177c478bd9Sstevel@tonic-gate		}
1187c478bd9Sstevel@tonic-gate		my @merged;
1197c478bd9Sstevel@tonic-gate		my %look = map { $_ => 0 } @$new;
1207c478bd9Sstevel@tonic-gate		foreach my $e (@$old) {
1217c478bd9Sstevel@tonic-gate			if (exists($look{$e})) {
1227c478bd9Sstevel@tonic-gate				$look{$e}++;
1237c478bd9Sstevel@tonic-gate			} else {
1247c478bd9Sstevel@tonic-gate				push(@merged, $e);
1257c478bd9Sstevel@tonic-gate			}
1267c478bd9Sstevel@tonic-gate		}
1277c478bd9Sstevel@tonic-gate		my @leftover = grep(! $look{$_}, keys(%look));
1287c478bd9Sstevel@tonic-gate		if (@leftover) {
1297c478bd9Sstevel@tonic-gate			push(@err, [6,
1307c478bd9Sstevel@tonic-gate		            gettext('Project does not contain "%s"'),
1317c478bd9Sstevel@tonic-gate			    join(',', @leftover)]);
1327c478bd9Sstevel@tonic-gate			return (1, \@err);
1337c478bd9Sstevel@tonic-gate		}
1347c478bd9Sstevel@tonic-gate		return (0, \@merged);
1357c478bd9Sstevel@tonic-gate
1367c478bd9Sstevel@tonic-gate	} elsif ($mode eq 'replace' || $mode eq 'substitute') {
1377c478bd9Sstevel@tonic-gate		return (0, $new);
1387c478bd9Sstevel@tonic-gate	}
1397c478bd9Sstevel@tonic-gate}
1407c478bd9Sstevel@tonic-gate
1417c478bd9Sstevel@tonic-gate#
1427c478bd9Sstevel@tonic-gate# merge_values(ref to listA, ref to listB, mode
1437c478bd9Sstevel@tonic-gate#
1447c478bd9Sstevel@tonic-gate# Merges the values in listB with the values in listA.  Dups are not
1457c478bd9Sstevel@tonic-gate# merged away, but instead are maintained.
1467c478bd9Sstevel@tonic-gate#
1477c478bd9Sstevel@tonic-gate# modes:
1487c478bd9Sstevel@tonic-gate#	add   :	add values in listB to listA
1497c478bd9Sstevel@tonic-gate#	remove:	removes first instance of each value in listB from listA
1507c478bd9Sstevel@tonic-gate#
1517c478bd9Sstevel@tonic-gatesub merge_values
1527c478bd9Sstevel@tonic-gate{
1537c478bd9Sstevel@tonic-gate
1547c478bd9Sstevel@tonic-gate	my ($new, $old, $mode) = @_;
1557c478bd9Sstevel@tonic-gate	my $undefined;
1567c478bd9Sstevel@tonic-gate	my @merged;
1577c478bd9Sstevel@tonic-gate	my $lastmerged;
1587c478bd9Sstevel@tonic-gate	my ($oldval, $newval);
1597c478bd9Sstevel@tonic-gate	my $found;
1607c478bd9Sstevel@tonic-gate	my @err;
1617c478bd9Sstevel@tonic-gate
1627c478bd9Sstevel@tonic-gate	if (!defined($old) && !defined($new)) {
1637c478bd9Sstevel@tonic-gate		return (0, $undefined);
1647c478bd9Sstevel@tonic-gate	}
1657c478bd9Sstevel@tonic-gate
1667c478bd9Sstevel@tonic-gate	if ($mode eq 'add') {
1677c478bd9Sstevel@tonic-gate
1687c478bd9Sstevel@tonic-gate		if (defined($old)) {
1697c478bd9Sstevel@tonic-gate			push(@merged, @$old);
1707c478bd9Sstevel@tonic-gate		}
1717c478bd9Sstevel@tonic-gate		if (defined($new)) {
1727c478bd9Sstevel@tonic-gate			push(@merged, @$new);
1737c478bd9Sstevel@tonic-gate		}
1747c478bd9Sstevel@tonic-gate		return (0, \@merged);
1757c478bd9Sstevel@tonic-gate
1767c478bd9Sstevel@tonic-gate	} elsif ($mode eq 'remove') {
1777c478bd9Sstevel@tonic-gate
1787c478bd9Sstevel@tonic-gate		$lastmerged = $old;
1797c478bd9Sstevel@tonic-gate		foreach $newval (@$new) {
1807c478bd9Sstevel@tonic-gate			$found = 0;
1817c478bd9Sstevel@tonic-gate			@merged = ();
1827c478bd9Sstevel@tonic-gate			foreach $oldval (@$lastmerged) {
1837c478bd9Sstevel@tonic-gate				if (!$found &&
1847c478bd9Sstevel@tonic-gate				    projent_values_equal($newval, $oldval)) {
1857c478bd9Sstevel@tonic-gate					$found = 1;
1867c478bd9Sstevel@tonic-gate				} else {
1877c478bd9Sstevel@tonic-gate					push(@merged, $oldval);
1887c478bd9Sstevel@tonic-gate				}
1897c478bd9Sstevel@tonic-gate
1907c478bd9Sstevel@tonic-gate			}
1917c478bd9Sstevel@tonic-gate			if (!$found) {
1927c478bd9Sstevel@tonic-gate				push(@err, [6, gettext(
1937c478bd9Sstevel@tonic-gate				    'Value "%s" not found'),
1947c478bd9Sstevel@tonic-gate				    projent_values2string($newval)]);
1957c478bd9Sstevel@tonic-gate			}
1967c478bd9Sstevel@tonic-gate			@$lastmerged = @merged;
1977c478bd9Sstevel@tonic-gate		}
1987c478bd9Sstevel@tonic-gate
1997c478bd9Sstevel@tonic-gate		if (@err) {
2007c478bd9Sstevel@tonic-gate			return (1, \@err);
2017c478bd9Sstevel@tonic-gate		} else {
2027c478bd9Sstevel@tonic-gate			return (0, \@merged);
2037c478bd9Sstevel@tonic-gate		}
2047c478bd9Sstevel@tonic-gate	}
2057c478bd9Sstevel@tonic-gate}
2067c478bd9Sstevel@tonic-gate
2077c478bd9Sstevel@tonic-gate#
2087c478bd9Sstevel@tonic-gate# merge_attribs(listA ref, listB ref, mode)
2097c478bd9Sstevel@tonic-gate#
2107c478bd9Sstevel@tonic-gate# Merge listB of attribute/values hash refs with listA
2117c478bd9Sstevel@tonic-gate# Each hash ref should have keys "name" and "values"
2127c478bd9Sstevel@tonic-gate#
2137c478bd9Sstevel@tonic-gate# modes:
2147c478bd9Sstevel@tonic-gate#     add	For each attribute in listB, add its values to
2157c478bd9Sstevel@tonic-gate#	        the matching attribute in listA.  If listA does not
2167c478bd9Sstevel@tonic-gate#		contain this attribute, add it.
2177c478bd9Sstevel@tonic-gate#
2187c478bd9Sstevel@tonic-gate#     remove	For each attribute in listB, remove its values from
2197c478bd9Sstevel@tonic-gate#	        the matching attribute in listA.  If all of an
2207c478bd9Sstevel@tonic-gate#		attributes values are removed, the attribute is removed.
2217c478bd9Sstevel@tonic-gate#		If the attribute in listB has no values, then the attribute
2227c478bd9Sstevel@tonic-gate#		and all of it's values are removed from listA
2237c478bd9Sstevel@tonic-gate#
2247c478bd9Sstevel@tonic-gate#     substitute For each attribute in listB, replace the values of
2257c478bd9Sstevel@tonic-gate#	        the matching attribute in listA with its values.  If
2267c478bd9Sstevel@tonic-gate#		listA does not contain this attribute, add it.
2277c478bd9Sstevel@tonic-gate#
2287c478bd9Sstevel@tonic-gate#     replace	Return listB
2297c478bd9Sstevel@tonic-gate#
2307c478bd9Sstevel@tonic-gate# The resulting array is returned as a reference.
2317c478bd9Sstevel@tonic-gate#
2327c478bd9Sstevel@tonic-gatesub merge_attribs
2337c478bd9Sstevel@tonic-gate{
2347c478bd9Sstevel@tonic-gate	my ($new, $old, $mode) = @_;
2357c478bd9Sstevel@tonic-gate	my @merged;
2367c478bd9Sstevel@tonic-gate	my @err;
2377c478bd9Sstevel@tonic-gate	my $ret;
2387c478bd9Sstevel@tonic-gate	my $tmp;
2397c478bd9Sstevel@tonic-gate	my $newattrib;
2407c478bd9Sstevel@tonic-gate	my $oldattrib;
2417c478bd9Sstevel@tonic-gate	my $values;
2427c478bd9Sstevel@tonic-gate
2437c478bd9Sstevel@tonic-gate	if ($mode eq 'add') {
2447c478bd9Sstevel@tonic-gate
2457c478bd9Sstevel@tonic-gate		my %oldhash;
2467c478bd9Sstevel@tonic-gate		push(@merged, @$old);
2477c478bd9Sstevel@tonic-gate		%oldhash = map { $_->{'name'} => $_ } @$old;
2487c478bd9Sstevel@tonic-gate		foreach $newattrib (@$new) {
2497c478bd9Sstevel@tonic-gate
2507c478bd9Sstevel@tonic-gate			$oldattrib = $oldhash{$newattrib->{'name'}};
2517c478bd9Sstevel@tonic-gate			if (defined($oldattrib)) {
2527c478bd9Sstevel@tonic-gate				($ret, $tmp) = merge_values(
2537c478bd9Sstevel@tonic-gate				    $newattrib->{'values'},
2547c478bd9Sstevel@tonic-gate				    $oldattrib->{'values'},
2557c478bd9Sstevel@tonic-gate				    $mode);
2567c478bd9Sstevel@tonic-gate
2577c478bd9Sstevel@tonic-gate				if ($ret != 0) {
2587c478bd9Sstevel@tonic-gate					push(@err, @$tmp);
2597c478bd9Sstevel@tonic-gate				} else {
2607c478bd9Sstevel@tonic-gate					$oldattrib->{'values'} = $tmp;
2617c478bd9Sstevel@tonic-gate				}
2627c478bd9Sstevel@tonic-gate			} else {
2637c478bd9Sstevel@tonic-gate				push(@merged, $newattrib);
2647c478bd9Sstevel@tonic-gate			}
2657c478bd9Sstevel@tonic-gate		}
2667c478bd9Sstevel@tonic-gate		if (@err) {
2677c478bd9Sstevel@tonic-gate			return (1, \@err);
2687c478bd9Sstevel@tonic-gate		} else {
2697c478bd9Sstevel@tonic-gate			return (0, \@merged);
2707c478bd9Sstevel@tonic-gate		}
2717c478bd9Sstevel@tonic-gate
2727c478bd9Sstevel@tonic-gate	} elsif ($mode eq 'remove') {
2737c478bd9Sstevel@tonic-gate
2747c478bd9Sstevel@tonic-gate		my %seen;
2757c478bd9Sstevel@tonic-gate		my @dups = grep($seen{$_}++ == 1, map { $_->{'name'} } @$new);
2767c478bd9Sstevel@tonic-gate		if (@dups) {
2777c478bd9Sstevel@tonic-gate			push(@err, [6, gettext(
2787c478bd9Sstevel@tonic-gate			    'Duplicate Attributes "%s"'),
2797c478bd9Sstevel@tonic-gate			     join(',', @dups)]);
2807c478bd9Sstevel@tonic-gate			return (1, \@err);
2817c478bd9Sstevel@tonic-gate		}
2827c478bd9Sstevel@tonic-gate		my %toremove = map { $_->{'name'} => $_ } @$new;
2837c478bd9Sstevel@tonic-gate
2847c478bd9Sstevel@tonic-gate		foreach $oldattrib (@$old) {
2857c478bd9Sstevel@tonic-gate			$newattrib = $toremove{$oldattrib->{'name'}};
2867c478bd9Sstevel@tonic-gate			if (!defined($newattrib)) {
2877c478bd9Sstevel@tonic-gate
2887c478bd9Sstevel@tonic-gate				push(@merged, $oldattrib);
2897c478bd9Sstevel@tonic-gate
2907c478bd9Sstevel@tonic-gate			} else {
2917c478bd9Sstevel@tonic-gate				if (defined($newattrib->{'values'})) {
2927c478bd9Sstevel@tonic-gate					($ret, $tmp) = merge_values(
2937c478bd9Sstevel@tonic-gate					    $newattrib->{'values'},
2947c478bd9Sstevel@tonic-gate					    $oldattrib->{'values'},
2957c478bd9Sstevel@tonic-gate					    $mode);
2967c478bd9Sstevel@tonic-gate
2977c478bd9Sstevel@tonic-gate					if ($ret != 0) {
2987c478bd9Sstevel@tonic-gate						push(@err, @$tmp);
2997c478bd9Sstevel@tonic-gate					} else {
3007c478bd9Sstevel@tonic-gate						$oldattrib->{'values'} = $tmp;
3017c478bd9Sstevel@tonic-gate					}
3027c478bd9Sstevel@tonic-gate					if (defined($tmp) && @$tmp) {
3037c478bd9Sstevel@tonic-gate						push(@merged, $oldattrib);
3047c478bd9Sstevel@tonic-gate					}
3057c478bd9Sstevel@tonic-gate				}
3067c478bd9Sstevel@tonic-gate				delete $toremove{$oldattrib->{'name'}};
3077c478bd9Sstevel@tonic-gate			}
3087c478bd9Sstevel@tonic-gate		}
3097c478bd9Sstevel@tonic-gate		foreach $tmp (keys(%toremove)) {
3107c478bd9Sstevel@tonic-gate			push(@err, [6,
3117c478bd9Sstevel@tonic-gate		            gettext('Project does not contain "%s"'),
3127c478bd9Sstevel@tonic-gate			    $tmp]);
3137c478bd9Sstevel@tonic-gate		}
3147c478bd9Sstevel@tonic-gate
3157c478bd9Sstevel@tonic-gate		if (@err) {
3167c478bd9Sstevel@tonic-gate			return (1, \@err);
3177c478bd9Sstevel@tonic-gate		} else {
3187c478bd9Sstevel@tonic-gate			return (0, \@merged);
3197c478bd9Sstevel@tonic-gate		}
3207c478bd9Sstevel@tonic-gate
3217c478bd9Sstevel@tonic-gate	} elsif ($mode eq 'substitute') {
3227c478bd9Sstevel@tonic-gate
3237c478bd9Sstevel@tonic-gate		my %oldhash;
3247c478bd9Sstevel@tonic-gate		push(@merged, @$old);
3257c478bd9Sstevel@tonic-gate		%oldhash = map { $_->{'name'} => $_ } @$old;
3267c478bd9Sstevel@tonic-gate		foreach $newattrib (@$new) {
3277c478bd9Sstevel@tonic-gate
3287c478bd9Sstevel@tonic-gate			$oldattrib = $oldhash{$newattrib->{'name'}};
3297c478bd9Sstevel@tonic-gate			if (defined($oldattrib)) {
3307c478bd9Sstevel@tonic-gate
3317c478bd9Sstevel@tonic-gate				$oldattrib->{'values'} =
3327c478bd9Sstevel@tonic-gate				    $newattrib->{'values'};
3337c478bd9Sstevel@tonic-gate
3347c478bd9Sstevel@tonic-gate			} else {
3357c478bd9Sstevel@tonic-gate				push(@merged, $newattrib);
3367c478bd9Sstevel@tonic-gate			}
3377c478bd9Sstevel@tonic-gate		}
3387c478bd9Sstevel@tonic-gate		if (@err) {
3397c478bd9Sstevel@tonic-gate			return (1, \@err);
3407c478bd9Sstevel@tonic-gate		} else {
3417c478bd9Sstevel@tonic-gate			return (0, \@merged);
3427c478bd9Sstevel@tonic-gate		}
3437c478bd9Sstevel@tonic-gate
3447c478bd9Sstevel@tonic-gate	} elsif ($mode eq 'replace') {
3457c478bd9Sstevel@tonic-gate		return (0, $new);
3467c478bd9Sstevel@tonic-gate	}
3477c478bd9Sstevel@tonic-gate}
3487c478bd9Sstevel@tonic-gate
3497c478bd9Sstevel@tonic-gate#
3507c478bd9Sstevel@tonic-gate# Main routine of script.
3517c478bd9Sstevel@tonic-gate#
3527c478bd9Sstevel@tonic-gate# Set the message locale.
3537c478bd9Sstevel@tonic-gate#
3547c478bd9Sstevel@tonic-gatesetlocale(LC_ALL, '');
3557c478bd9Sstevel@tonic-gatetextdomain(TEXT_DOMAIN);
3567c478bd9Sstevel@tonic-gate
3577c478bd9Sstevel@tonic-gate
3587c478bd9Sstevel@tonic-gate# Process command options and do some initial command-line validity checking.
3597c478bd9Sstevel@tonic-gatemy ($pname, $flags);
3607c478bd9Sstevel@tonic-gate$flags = {};
3617c478bd9Sstevel@tonic-gatemy $modify = 0;
3627c478bd9Sstevel@tonic-gate
363*532877c4Srdmy $projfile;
3647c478bd9Sstevel@tonic-gatemy $opt_n;
3657c478bd9Sstevel@tonic-gatemy $opt_c;
3667c478bd9Sstevel@tonic-gatemy $opt_o;
3677c478bd9Sstevel@tonic-gatemy $opt_p;
3687c478bd9Sstevel@tonic-gatemy $opt_l;
3697c478bd9Sstevel@tonic-gatemy $opt_a;
3707c478bd9Sstevel@tonic-gatemy $opt_r;
3717c478bd9Sstevel@tonic-gatemy $opt_s;
3727c478bd9Sstevel@tonic-gatemy $opt_U;
3737c478bd9Sstevel@tonic-gatemy $opt_G;
3747c478bd9Sstevel@tonic-gatemy @opt_K;
375*532877c4Srdmy $opt_A;
3767c478bd9Sstevel@tonic-gate
3777c478bd9Sstevel@tonic-gateGetOptions("f=s" => \$projfile,
3787c478bd9Sstevel@tonic-gate	   "n"   => \$opt_n,
3797c478bd9Sstevel@tonic-gate	   "c=s" => \$opt_c,
3807c478bd9Sstevel@tonic-gate	   "o"	 => \$opt_o,
3817c478bd9Sstevel@tonic-gate	   "p=s" => \$opt_p,
3827c478bd9Sstevel@tonic-gate	   "l=s" => \$opt_l,
3837c478bd9Sstevel@tonic-gate	   "s"	 => \$opt_s,
3847c478bd9Sstevel@tonic-gate	   "r"	 => \$opt_r,
3857c478bd9Sstevel@tonic-gate	   "a"	 => \$opt_a,
3867c478bd9Sstevel@tonic-gate	   "U=s" => \$opt_U,
3877c478bd9Sstevel@tonic-gate	   "G=s" => \$opt_G,
388*532877c4Srd	   "K=s" => \@opt_K,
389*532877c4Srd  	   "A"	 => \$opt_A) || usage();
3907c478bd9Sstevel@tonic-gate
3917c478bd9Sstevel@tonic-gateusage(gettext('Invalid command-line arguments')) if (@ARGV > 1);
3927c478bd9Sstevel@tonic-gate
393*532877c4Srdif ($opt_c || $opt_G || $opt_l || $opt_p || $opt_U || @opt_K || $opt_A) {
3947c478bd9Sstevel@tonic-gate	$modify = 1;
3957c478bd9Sstevel@tonic-gate	if (! defined($ARGV[0])) {
3967c478bd9Sstevel@tonic-gate		usage(gettext('No project name specified'));
3977c478bd9Sstevel@tonic-gate	}
3987c478bd9Sstevel@tonic-gate}
3997c478bd9Sstevel@tonic-gate
4007c478bd9Sstevel@tonic-gateif (!$modify && defined($ARGV[0])) {
4017c478bd9Sstevel@tonic-gate	usage(gettext('missing -c, -G, -l, -p, -U, or -K'));
4027c478bd9Sstevel@tonic-gate}
4037c478bd9Sstevel@tonic-gate
404*532877c4Srdif (defined($opt_A) && defined($projfile)) {
405*532877c4Srd	usage(gettext('-A and -f are mutually exclusive'));
406*532877c4Srd}
407*532877c4Srd
408*532877c4Srdif (! defined($projfile)) {
409*532877c4Srd	$projfile = &PROJF_PATH;
410*532877c4Srd}
411*532877c4Srd
4127c478bd9Sstevel@tonic-gateif ($modify && $projfile eq '-') {
4137c478bd9Sstevel@tonic-gate	usage(gettext('Cannot modify standard input'));
4147c478bd9Sstevel@tonic-gate}
4157c478bd9Sstevel@tonic-gate
4167c478bd9Sstevel@tonic-gate$pname = $ARGV[0];
4177c478bd9Sstevel@tonic-gateusage(gettext('-o requires -p projid to be specified'))
4187c478bd9Sstevel@tonic-gate    if (defined($opt_o) && ! defined($opt_p));
4197c478bd9Sstevel@tonic-gateusage(gettext('-a, -r, and -s are mutually exclusive'))
4207c478bd9Sstevel@tonic-gate    if ((defined($opt_a) && (defined($opt_r) || defined($opt_s))) ||
4217c478bd9Sstevel@tonic-gate	(defined($opt_r) && (defined($opt_a) || defined($opt_s))) ||
4227c478bd9Sstevel@tonic-gate	(defined($opt_s) && (defined($opt_a) || defined($opt_r))));
4237c478bd9Sstevel@tonic-gate
4247c478bd9Sstevel@tonic-gateusage(gettext('-a and -r require -U users or -G groups to be specified'))
4257c478bd9Sstevel@tonic-gate    if ((defined($opt_a) || defined($opt_r) || defined($opt_s)) &&
4267c478bd9Sstevel@tonic-gate    ! (defined($opt_U) || defined($opt_G) || (@opt_K)));
4277c478bd9Sstevel@tonic-gate
4287c478bd9Sstevel@tonic-gate
4297c478bd9Sstevel@tonic-gateif (defined($opt_a)) {
4307c478bd9Sstevel@tonic-gate	$flags->{mode} = 'add';
4317c478bd9Sstevel@tonic-gate} elsif (defined($opt_r)) {
4327c478bd9Sstevel@tonic-gate	$flags->{mode} = 'remove';
4337c478bd9Sstevel@tonic-gate} elsif (defined($opt_s)) {
4347c478bd9Sstevel@tonic-gate	$flags->{mode} = 'substitute';
4357c478bd9Sstevel@tonic-gate} else {
4367c478bd9Sstevel@tonic-gate	$flags->{mode} = 'replace';
4377c478bd9Sstevel@tonic-gate}
4387c478bd9Sstevel@tonic-gate
4397c478bd9Sstevel@tonic-gate# Fabricate an unique temporary filename.
4407c478bd9Sstevel@tonic-gatemy $tmpprojf = $projfile . ".tmp.$$";
4417c478bd9Sstevel@tonic-gate
4427c478bd9Sstevel@tonic-gatemy $pfh;
4437c478bd9Sstevel@tonic-gate
4447c478bd9Sstevel@tonic-gate#
4457c478bd9Sstevel@tonic-gate# Read the project file.  sysopen() is used so we can control the file mode.
4467c478bd9Sstevel@tonic-gate# Handle special case for standard input.
4477c478bd9Sstevel@tonic-gateif ($projfile eq '-') {
4487c478bd9Sstevel@tonic-gate	open($pfh, "<&=STDIN") or error( [10,
4497c478bd9Sstevel@tonic-gate	    gettext('Cannot open standard input')]);
4507c478bd9Sstevel@tonic-gate} elsif (! sysopen($pfh, $projfile, O_RDONLY)) {
4517c478bd9Sstevel@tonic-gate	error([10, gettext('Cannot open %s: %s'), $projfile, $!]);
4527c478bd9Sstevel@tonic-gate}
4537c478bd9Sstevel@tonic-gatemy ($mode, $uid, $gid) = (stat($pfh))[2,4,5];
4547c478bd9Sstevel@tonic-gate
4557c478bd9Sstevel@tonic-gate
4567c478bd9Sstevel@tonic-gateif ($opt_n) {
4577c478bd9Sstevel@tonic-gate	$flags->{'validate'} = 'false';
4587c478bd9Sstevel@tonic-gate} else {
4597c478bd9Sstevel@tonic-gate	$flags->{'validate'} = 'true';
4607c478bd9Sstevel@tonic-gate}
4617c478bd9Sstevel@tonic-gate
4627c478bd9Sstevel@tonic-gate$flags->{'res'} = 'true';
4637c478bd9Sstevel@tonic-gate$flags->{'dup'} = 'true';
4647c478bd9Sstevel@tonic-gate
4657c478bd9Sstevel@tonic-gatemy ($ret, $pf) = projf_read($pfh, $flags);
4667c478bd9Sstevel@tonic-gateif ($ret != 0) {
4677c478bd9Sstevel@tonic-gate	error(@$pf);
4687c478bd9Sstevel@tonic-gate}
4697c478bd9Sstevel@tonic-gateclose($pfh);
4707c478bd9Sstevel@tonic-gatemy $err;
4717c478bd9Sstevel@tonic-gatemy $tmperr;
4727c478bd9Sstevel@tonic-gatemy $value;
4737c478bd9Sstevel@tonic-gate
4747c478bd9Sstevel@tonic-gate# Find existing record.
4757c478bd9Sstevel@tonic-gatemy ($proj, $idx);
4767c478bd9Sstevel@tonic-gate$idx = 0;
4777c478bd9Sstevel@tonic-gate
4787c478bd9Sstevel@tonic-gateif (defined($pname)) {
4797c478bd9Sstevel@tonic-gate	foreach my $r (@$pf) {
4807c478bd9Sstevel@tonic-gate		if ($r->{'name'} eq $pname) {
4817c478bd9Sstevel@tonic-gate			$proj = $r;
4827c478bd9Sstevel@tonic-gate			last;
4837c478bd9Sstevel@tonic-gate		}
4847c478bd9Sstevel@tonic-gate		$idx++;
4857c478bd9Sstevel@tonic-gate	}
4867c478bd9Sstevel@tonic-gate	error([6, gettext('Project "%s" does not exist'), $pname])
4877c478bd9Sstevel@tonic-gate	    if (! $proj);
4887c478bd9Sstevel@tonic-gate}
4897c478bd9Sstevel@tonic-gate#
4907c478bd9Sstevel@tonic-gate# If there are no modification options, simply reading the file, which
4917c478bd9Sstevel@tonic-gate# includes parsing and verifying, is sufficient.
4927c478bd9Sstevel@tonic-gate#
4937c478bd9Sstevel@tonic-gateif (!$modify) {
4947c478bd9Sstevel@tonic-gate	exit(0);
4957c478bd9Sstevel@tonic-gate}
4967c478bd9Sstevel@tonic-gate
4977c478bd9Sstevel@tonic-gateforeach my $r (@$pf) {
4987c478bd9Sstevel@tonic-gate	if ($r->{'name'} eq $pname) {
4997c478bd9Sstevel@tonic-gate		$proj = $r;
5007c478bd9Sstevel@tonic-gate		last;
5017c478bd9Sstevel@tonic-gate	}
5027c478bd9Sstevel@tonic-gate	$idx++;
5037c478bd9Sstevel@tonic-gate}
5047c478bd9Sstevel@tonic-gate
5057c478bd9Sstevel@tonic-gate# Update the record as appropriate.
5067c478bd9Sstevel@tonic-gate$err = [];
5077c478bd9Sstevel@tonic-gate
5087c478bd9Sstevel@tonic-gate# Set new project name.
5097c478bd9Sstevel@tonic-gateif (defined($opt_l)) {
5107c478bd9Sstevel@tonic-gate
5117c478bd9Sstevel@tonic-gate	($ret, $value) = projent_parse_name($opt_l);
5127c478bd9Sstevel@tonic-gate	if ($ret != 0) {
5137c478bd9Sstevel@tonic-gate		push(@$err, @$value);
5147c478bd9Sstevel@tonic-gate	} else {
5157c478bd9Sstevel@tonic-gate		$proj->{'name'} = $value;
5167c478bd9Sstevel@tonic-gate		if (!defined($opt_n)) {
5177c478bd9Sstevel@tonic-gate			($ret, $tmperr) =
5187c478bd9Sstevel@tonic-gate			    projent_validate_unique_name($proj, $pf);
5197c478bd9Sstevel@tonic-gate			if ($ret != 0) {
5207c478bd9Sstevel@tonic-gate				push(@$err, @$tmperr);
5217c478bd9Sstevel@tonic-gate			}
5227c478bd9Sstevel@tonic-gate		}
5237c478bd9Sstevel@tonic-gate	}
5247c478bd9Sstevel@tonic-gate}
5257c478bd9Sstevel@tonic-gate
5267c478bd9Sstevel@tonic-gate# Set new project id.
5277c478bd9Sstevel@tonic-gateif (defined($opt_p)) {
5287c478bd9Sstevel@tonic-gate
5297c478bd9Sstevel@tonic-gate	($ret, $value) = projent_parse_projid($opt_p);
5307c478bd9Sstevel@tonic-gate	if ($ret != 0) {
5317c478bd9Sstevel@tonic-gate		push(@$err, @$value);
5327c478bd9Sstevel@tonic-gate	} else {
5337c478bd9Sstevel@tonic-gate		$proj->{'projid'} = $value;
5347c478bd9Sstevel@tonic-gate
5357c478bd9Sstevel@tonic-gate		# Check for dupicate.
5367c478bd9Sstevel@tonic-gate		if ((!defined($opt_n)) && (!defined($opt_o))) {
5377c478bd9Sstevel@tonic-gate			($ret, $tmperr) =
5387c478bd9Sstevel@tonic-gate			    projent_validate_unique_id($proj, $pf);
5397c478bd9Sstevel@tonic-gate			if ($ret != 0) {
5407c478bd9Sstevel@tonic-gate				push(@$err, @$tmperr);
5417c478bd9Sstevel@tonic-gate			}
5427c478bd9Sstevel@tonic-gate		}
5437c478bd9Sstevel@tonic-gate	}
5447c478bd9Sstevel@tonic-gate}
5457c478bd9Sstevel@tonic-gate
5467c478bd9Sstevel@tonic-gate# Set new comment.
5477c478bd9Sstevel@tonic-gateif (defined($opt_c)) {
5487c478bd9Sstevel@tonic-gate
5497c478bd9Sstevel@tonic-gate	($ret, $value) = projent_parse_comment($opt_c);
5507c478bd9Sstevel@tonic-gate	if ($ret != 0) {
5517c478bd9Sstevel@tonic-gate		push(@$err, @$value);
5527c478bd9Sstevel@tonic-gate	} else {
5537c478bd9Sstevel@tonic-gate		$proj->{'comment'} = $value;
5547c478bd9Sstevel@tonic-gate	}
5557c478bd9Sstevel@tonic-gate}
5567c478bd9Sstevel@tonic-gate
5577c478bd9Sstevel@tonic-gate# Set new users.
5587c478bd9Sstevel@tonic-gateif (defined($opt_U)) {
5597c478bd9Sstevel@tonic-gate
5607c478bd9Sstevel@tonic-gate	my @sortlist;
5617c478bd9Sstevel@tonic-gate	my $list;
5627c478bd9Sstevel@tonic-gate	($ret, $list) = projent_parse_users($opt_U, {'allowspaces' => 1});
5637c478bd9Sstevel@tonic-gate	if ($ret != 0) {
5647c478bd9Sstevel@tonic-gate		push(@$err, @$list);
5657c478bd9Sstevel@tonic-gate	} else {
5667c478bd9Sstevel@tonic-gate		($ret, $list) =
5677c478bd9Sstevel@tonic-gate		    merge_lists($list, $proj->{'userlist'}, $flags->{mode});
5687c478bd9Sstevel@tonic-gate		if ($ret != 0) {
5697c478bd9Sstevel@tonic-gate			push(@$err, @$list);
5707c478bd9Sstevel@tonic-gate		} else {
5717c478bd9Sstevel@tonic-gate			@sortlist = sort(@$list);
5727c478bd9Sstevel@tonic-gate			$proj->{'userlist'} = \@sortlist;
5737c478bd9Sstevel@tonic-gate		}
5747c478bd9Sstevel@tonic-gate	}
5757c478bd9Sstevel@tonic-gate}
5767c478bd9Sstevel@tonic-gate
5777c478bd9Sstevel@tonic-gate# Set new groups.
5787c478bd9Sstevel@tonic-gateif (defined($opt_G)) {
5797c478bd9Sstevel@tonic-gate
5807c478bd9Sstevel@tonic-gate	my @sortlist;
5817c478bd9Sstevel@tonic-gate	my $list;
5827c478bd9Sstevel@tonic-gate	($ret, $list) = projent_parse_groups($opt_G, {'allowspaces' => 1});
5837c478bd9Sstevel@tonic-gate	if ($ret != 0) {
5847c478bd9Sstevel@tonic-gate		push(@$err, @$list);
5857c478bd9Sstevel@tonic-gate	} else {
5867c478bd9Sstevel@tonic-gate		($ret, $list) =
5877c478bd9Sstevel@tonic-gate		    merge_lists($list, $proj->{'grouplist'}, $flags->{mode});
5887c478bd9Sstevel@tonic-gate		if ($ret != 0) {
5897c478bd9Sstevel@tonic-gate			push(@$err, @$list);
5907c478bd9Sstevel@tonic-gate		} else {
5917c478bd9Sstevel@tonic-gate			@sortlist = sort(@$list);
5927c478bd9Sstevel@tonic-gate			$proj->{'grouplist'} = \@sortlist;
5937c478bd9Sstevel@tonic-gate		}
5947c478bd9Sstevel@tonic-gate	}
5957c478bd9Sstevel@tonic-gate}
5967c478bd9Sstevel@tonic-gate
5977c478bd9Sstevel@tonic-gate# Set new attributes.
5987c478bd9Sstevel@tonic-gatemy $attrib;
5997c478bd9Sstevel@tonic-gatemy @attriblist;
6007c478bd9Sstevel@tonic-gate
6017c478bd9Sstevel@tonic-gateforeach $attrib (@opt_K) {
6027c478bd9Sstevel@tonic-gate
6037c478bd9Sstevel@tonic-gate	my $list;
6047c478bd9Sstevel@tonic-gate	($ret, $list) = projent_parse_attributes($attrib, {'allowunits' => 1});
6057c478bd9Sstevel@tonic-gate	if ($ret != 0) {
6067c478bd9Sstevel@tonic-gate		push(@$err, @$list);
6077c478bd9Sstevel@tonic-gate	} else {
6087c478bd9Sstevel@tonic-gate		push(@attriblist, @$list);
6097c478bd9Sstevel@tonic-gate	}
6107c478bd9Sstevel@tonic-gate}
6117c478bd9Sstevel@tonic-gate
6127c478bd9Sstevel@tonic-gateif (@attriblist) {
6137c478bd9Sstevel@tonic-gate	my @sortlist;
6147c478bd9Sstevel@tonic-gate	my $list;
6157c478bd9Sstevel@tonic-gate
6167c478bd9Sstevel@tonic-gate	($ret, $list) =
6177c478bd9Sstevel@tonic-gate	    merge_attribs(\@attriblist, $proj->{'attributelist'},
6187c478bd9Sstevel@tonic-gate	    $flags->{mode});
6197c478bd9Sstevel@tonic-gate	if ($ret != 0) {
6207c478bd9Sstevel@tonic-gate		push(@$err, @$list);
6217c478bd9Sstevel@tonic-gate	} else {
6227c478bd9Sstevel@tonic-gate		@sortlist =
6237c478bd9Sstevel@tonic-gate		    sort { $a->{'name'} cmp $b->{'name'} } @$list;
6247c478bd9Sstevel@tonic-gate		$proj->{'attributelist'} = \@sortlist;
6257c478bd9Sstevel@tonic-gate	}
6267c478bd9Sstevel@tonic-gate}
6277c478bd9Sstevel@tonic-gate
6287c478bd9Sstevel@tonic-gate# Validate all projent fields.
6297c478bd9Sstevel@tonic-gateif (!defined($opt_n)) {
6307c478bd9Sstevel@tonic-gate	($ret, $tmperr) = projent_validate($proj, $flags);
6317c478bd9Sstevel@tonic-gate	if ($ret != 0) {
6327c478bd9Sstevel@tonic-gate		push(@$err, @$tmperr);
6337c478bd9Sstevel@tonic-gate	}
6347c478bd9Sstevel@tonic-gate}
6357c478bd9Sstevel@tonic-gateif (@$err) {
6367c478bd9Sstevel@tonic-gate	error(@$err);
6377c478bd9Sstevel@tonic-gate}
6387c478bd9Sstevel@tonic-gate
6397c478bd9Sstevel@tonic-gate# Write out the project file.
6407c478bd9Sstevel@tonic-gateif ($modify) {
6417c478bd9Sstevel@tonic-gate
6427c478bd9Sstevel@tonic-gate	#
6437c478bd9Sstevel@tonic-gate	# Mark projent to write based on new values instead of
6447c478bd9Sstevel@tonic-gate	# original line.
6457c478bd9Sstevel@tonic-gate	#
6467c478bd9Sstevel@tonic-gate	$proj->{'modified'} = 'true';
6477c478bd9Sstevel@tonic-gate	umask(0000);
6487c478bd9Sstevel@tonic-gate	sysopen($pfh, $tmpprojf, O_WRONLY | O_CREAT | O_EXCL, $mode) ||
6497c478bd9Sstevel@tonic-gate	    error([10, gettext('Cannot create %s: %s'), $tmpprojf, $!]);
6507c478bd9Sstevel@tonic-gate	projf_write($pfh, $pf);
6517c478bd9Sstevel@tonic-gate	close($pfh);
6527c478bd9Sstevel@tonic-gate
6537c478bd9Sstevel@tonic-gate	# Update file attributes.
6547c478bd9Sstevel@tonic-gate	if (!chown($uid, $gid, $tmpprojf)) {
6557c478bd9Sstevel@tonic-gate		unlink($tmpprojf);
6567c478bd9Sstevel@tonic-gate		error([10, gettext('Cannot set ownership of %s: %s'),
6577c478bd9Sstevel@tonic-gate		    $tmpprojf, $!]);
6587c478bd9Sstevel@tonic-gate	}
6597c478bd9Sstevel@tonic-gate	if (! rename($tmpprojf, $projfile)) {
6607c478bd9Sstevel@tonic-gate		unlink($tmpprojf);
6617c478bd9Sstevel@tonic-gate		error([10, gettext('cannot rename %s to %s: %s'),
6627c478bd9Sstevel@tonic-gate	            $tmpprojf, $projfile, $!]);
6637c478bd9Sstevel@tonic-gate	}
6647c478bd9Sstevel@tonic-gate
6657c478bd9Sstevel@tonic-gate}
6667c478bd9Sstevel@tonic-gate
667*532877c4Srdif (defined($opt_A)) {
668*532877c4Srd	my $error;
669*532877c4Srd
670*532877c4Srd	if (($error = setproject($pname, "root", TASK_FINAL|TASK_PROJ_PURGE)) != 0) {
671*532877c4Srd
672*532877c4Srd		if ($error == SETPROJ_ERR_TASK) {
673*532877c4Srd			if ($!{EAGAIN}) {
674*532877c4Srd				error([5, gettext("resource control limit has ".
675*532877c4Srd				     "been reached\n")]);
676*532877c4Srd			} elsif ($!{ESRCH}) {
677*532877c4Srd				error([5, gettext("user \"%s\" is not a member ".
678*532877c4Srd				     "of project \"%s\"\n"), "root", $pname]);
679*532877c4Srd			} elsif ($!{EACCES}) {
680*532877c4Srd				error([5, gettext("the invoking task is final\n"
681*532877c4Srd				     )]);
682*532877c4Srd			} else {
683*532877c4Srd				error([5, gettext("could not join project \"%s".
684*532877c4Srd				     "\"\n"), $pname]);
685*532877c4Srd			}
6867c478bd9Sstevel@tonic-gate
687*532877c4Srd		} elsif ($error == SETPROJ_ERR_POOL) {
688*532877c4Srd			if ($!{EACCES}) {
689*532877c4Srd				error([5, gettext("no resource pool accepting ".
690*532877c4Srd				     "default bindings exists for project \"%s".
691*532877c4Srd				     "\"\n"), $pname]);
692*532877c4Srd	        	} elsif ($!{ESRCH}) {
693*532877c4Srd				error([5, gettext("specified resource pool ".
694*532877c4Srd				     "does not exist for project \"%s\"\n"),
695*532877c4Srd				     $pname]);
696*532877c4Srd			} else {
697*532877c4Srd				error([5, gettext("could not bind to default ".
698*532877c4Srd				     "resource pool for project \"%s\"\n"),
699*532877c4Srd				     $pname]);
700*532877c4Srd			}
7017c478bd9Sstevel@tonic-gate
702*532877c4Srd		} else {
703*532877c4Srd			#
704*532877c4Srd			# $error represents the position - within the semi-colon
705*532877c4Srd			# delimited $attribute - that generated the error
706*532877c4Srd			#
707*532877c4Srd			if ($error <= 0) {
708*532877c4Srd				error([5, gettext("setproject failed for ".
709*532877c4Srd				     "project \"%s\"\n"), $pname]);
710*532877c4Srd			} else {
711*532877c4Srd				my ($name, $projid, $comment, $users_ref,
712*532877c4Srd				     $groups_ref, $attr) = getprojbyname($pname);
713*532877c4Srd				my $attribute = ($attr =~
714*532877c4Srd				     /(\S+?)=\S+?(?:;|\z)/g)[$error - 1];
715*532877c4Srd
716*532877c4Srd				if (!$attribute) {
717*532877c4Srd					error([5, gettext("warning, resource ".
718*532877c4Srd					     "control assignment failed for ".
719*532877c4Srd					     "project \"%s\" attribute %d\n"),
720*532877c4Srd					     $pname, $error]);
721*532877c4Srd				} else {
722*532877c4Srd					error([5, gettext("warning, %s ".
723*532877c4Srd					     "resource control assignment ".
724*532877c4Srd					     "failed for project \"%s\"\n"),
725*532877c4Srd					     $attribute, $pname]);
726*532877c4Srd				}
727*532877c4Srd			}
728*532877c4Srd		}
729*532877c4Srd	}
730*532877c4Srd}
7317c478bd9Sstevel@tonic-gate
732*532877c4Srdexit(0);
733