xref: /illumos-gate/usr/src/cmd/projadd/projadd.pl (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate#!/usr/perl5/bin/perl -w
2*7c478bd9Sstevel@tonic-gate#
3*7c478bd9Sstevel@tonic-gate# CDDL HEADER START
4*7c478bd9Sstevel@tonic-gate#
5*7c478bd9Sstevel@tonic-gate# The contents of this file are subject to the terms of the
6*7c478bd9Sstevel@tonic-gate# Common Development and Distribution License, Version 1.0 only
7*7c478bd9Sstevel@tonic-gate# (the "License").  You may not use this file except in compliance
8*7c478bd9Sstevel@tonic-gate# with the License.
9*7c478bd9Sstevel@tonic-gate#
10*7c478bd9Sstevel@tonic-gate# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11*7c478bd9Sstevel@tonic-gate# or http://www.opensolaris.org/os/licensing.
12*7c478bd9Sstevel@tonic-gate# See the License for the specific language governing permissions
13*7c478bd9Sstevel@tonic-gate# and limitations under the License.
14*7c478bd9Sstevel@tonic-gate#
15*7c478bd9Sstevel@tonic-gate# When distributing Covered Code, include this CDDL HEADER in each
16*7c478bd9Sstevel@tonic-gate# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17*7c478bd9Sstevel@tonic-gate# If applicable, add the following below this CDDL HEADER, with the
18*7c478bd9Sstevel@tonic-gate# fields enclosed by brackets "[]" replaced with your own identifying
19*7c478bd9Sstevel@tonic-gate# information: Portions Copyright [yyyy] [name of copyright owner]
20*7c478bd9Sstevel@tonic-gate#
21*7c478bd9Sstevel@tonic-gate# CDDL HEADER END
22*7c478bd9Sstevel@tonic-gate#
23*7c478bd9Sstevel@tonic-gate#
24*7c478bd9Sstevel@tonic-gate# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
25*7c478bd9Sstevel@tonic-gate# Use is subject to license terms.
26*7c478bd9Sstevel@tonic-gate#
27*7c478bd9Sstevel@tonic-gate#ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate#
29*7c478bd9Sstevel@tonic-gate
30*7c478bd9Sstevel@tonic-gaterequire 5.005;
31*7c478bd9Sstevel@tonic-gateuse strict;
32*7c478bd9Sstevel@tonic-gateuse locale;
33*7c478bd9Sstevel@tonic-gateuse Errno;
34*7c478bd9Sstevel@tonic-gateuse Fcntl;
35*7c478bd9Sstevel@tonic-gateuse File::Basename;
36*7c478bd9Sstevel@tonic-gateuse Getopt::Std;
37*7c478bd9Sstevel@tonic-gateuse Getopt::Long qw(:config no_ignore_case bundling);
38*7c478bd9Sstevel@tonic-gateuse POSIX qw(locale_h getuid getgid);
39*7c478bd9Sstevel@tonic-gateuse Sun::Solaris::Utils qw(textdomain gettext);
40*7c478bd9Sstevel@tonic-gateuse Sun::Solaris::Project qw(:ALL :PRIVATE);
41*7c478bd9Sstevel@tonic-gate
42*7c478bd9Sstevel@tonic-gate#
43*7c478bd9Sstevel@tonic-gate# Print a usage message and exit.
44*7c478bd9Sstevel@tonic-gate#
45*7c478bd9Sstevel@tonic-gatesub usage
46*7c478bd9Sstevel@tonic-gate{
47*7c478bd9Sstevel@tonic-gate	my (@msg) = @_;
48*7c478bd9Sstevel@tonic-gate	my $prog = basename($0);
49*7c478bd9Sstevel@tonic-gate	my $space = ' ' x length($prog);
50*7c478bd9Sstevel@tonic-gate	print(STDERR "$prog: @msg\n") if (@msg);
51*7c478bd9Sstevel@tonic-gate	printf(STDERR gettext(
52*7c478bd9Sstevel@tonic-gate	    "       %s [-n] [-f filename] [-p projid [-o]] [-c comment]\n".
53*7c478bd9Sstevel@tonic-gate            "       %s [-U user[,user...]] [-G group[,group...]]\n".
54*7c478bd9Sstevel@tonic-gate            "       %s [-K name[=value[,value...]]] project\n"),
55*7c478bd9Sstevel@tonic-gate	       $prog, $space, $space);
56*7c478bd9Sstevel@tonic-gate	exit(2);
57*7c478bd9Sstevel@tonic-gate}
58*7c478bd9Sstevel@tonic-gate
59*7c478bd9Sstevel@tonic-gate#
60*7c478bd9Sstevel@tonic-gate# Print a list of error messages and exit.
61*7c478bd9Sstevel@tonic-gate#
62*7c478bd9Sstevel@tonic-gatesub error
63*7c478bd9Sstevel@tonic-gate{
64*7c478bd9Sstevel@tonic-gate	my $exit = $_[0][0];
65*7c478bd9Sstevel@tonic-gate	my $prog = basename($0) . ': ';
66*7c478bd9Sstevel@tonic-gate	foreach my $err (@_) {
67*7c478bd9Sstevel@tonic-gate		my ($e, $fmt, @args) = @$err;
68*7c478bd9Sstevel@tonic-gate		printf(STDERR $prog . $fmt . "\n", @args);
69*7c478bd9Sstevel@tonic-gate	}
70*7c478bd9Sstevel@tonic-gate	exit($exit);
71*7c478bd9Sstevel@tonic-gate}
72*7c478bd9Sstevel@tonic-gate
73*7c478bd9Sstevel@tonic-gate#
74*7c478bd9Sstevel@tonic-gate# Main routine of script.
75*7c478bd9Sstevel@tonic-gate#
76*7c478bd9Sstevel@tonic-gate# Set the message locale.
77*7c478bd9Sstevel@tonic-gate#
78*7c478bd9Sstevel@tonic-gatesetlocale(LC_ALL, '');
79*7c478bd9Sstevel@tonic-gatetextdomain(TEXT_DOMAIN);
80*7c478bd9Sstevel@tonic-gate
81*7c478bd9Sstevel@tonic-gate
82*7c478bd9Sstevel@tonic-gate# Process command options and do some initial command-line validity checking.
83*7c478bd9Sstevel@tonic-gatemy ($pname, $flags);
84*7c478bd9Sstevel@tonic-gate
85*7c478bd9Sstevel@tonic-gatemy $projfile = &PROJF_PATH;
86*7c478bd9Sstevel@tonic-gatemy $opt_n;
87*7c478bd9Sstevel@tonic-gatemy $opt_c;
88*7c478bd9Sstevel@tonic-gatemy $opt_o;
89*7c478bd9Sstevel@tonic-gatemy $opt_p;
90*7c478bd9Sstevel@tonic-gatemy $opt_U;
91*7c478bd9Sstevel@tonic-gatemy $opt_G;
92*7c478bd9Sstevel@tonic-gatemy @opt_K;
93*7c478bd9Sstevel@tonic-gate
94*7c478bd9Sstevel@tonic-gateGetOptions("f=s" => \$projfile,
95*7c478bd9Sstevel@tonic-gate	   "n"   => \$opt_n,
96*7c478bd9Sstevel@tonic-gate	   "c=s" => \$opt_c,
97*7c478bd9Sstevel@tonic-gate	   "o"	 => \$opt_o,
98*7c478bd9Sstevel@tonic-gate	   "p=s" => \$opt_p,
99*7c478bd9Sstevel@tonic-gate	   "U=s" => \$opt_U,
100*7c478bd9Sstevel@tonic-gate	   "G=s" => \$opt_G,
101*7c478bd9Sstevel@tonic-gate	   "K=s" => \@opt_K) || usage();
102*7c478bd9Sstevel@tonic-gate
103*7c478bd9Sstevel@tonic-gateusage(gettext('Invalid command-line arguments')) if (@ARGV != 1);
104*7c478bd9Sstevel@tonic-gateusage(gettext('No project name specified')) if (! defined($ARGV[0]));
105*7c478bd9Sstevel@tonic-gateusage(gettext('-o requires -p projid to be specified'))
106*7c478bd9Sstevel@tonic-gate    if (defined($opt_o) && ! defined($opt_p));
107*7c478bd9Sstevel@tonic-gate
108*7c478bd9Sstevel@tonic-gate$pname = $ARGV[0];
109*7c478bd9Sstevel@tonic-gatemy $maxpjid = 99;
110*7c478bd9Sstevel@tonic-gatemy $tmpprojf;
111*7c478bd9Sstevel@tonic-gate
112*7c478bd9Sstevel@tonic-gate
113*7c478bd9Sstevel@tonic-gate# Fabricate an unique temporary filename.
114*7c478bd9Sstevel@tonic-gate$tmpprojf = $projfile . ".tmp.$$";
115*7c478bd9Sstevel@tonic-gate
116*7c478bd9Sstevel@tonic-gatemy $pfh;
117*7c478bd9Sstevel@tonic-gate
118*7c478bd9Sstevel@tonic-gateif (defined($opt_n)) {
119*7c478bd9Sstevel@tonic-gate	$flags->{'validate'} = 'false';
120*7c478bd9Sstevel@tonic-gate} else {
121*7c478bd9Sstevel@tonic-gate	$flags->{'validate'} = 'true';
122*7c478bd9Sstevel@tonic-gate}
123*7c478bd9Sstevel@tonic-gate
124*7c478bd9Sstevel@tonic-gate$flags->{'res'} = 'true';
125*7c478bd9Sstevel@tonic-gate$flags->{'dup'} = 'true';
126*7c478bd9Sstevel@tonic-gate
127*7c478bd9Sstevel@tonic-gatemy $pf;
128*7c478bd9Sstevel@tonic-gatemy ($mode, $uid, $gid);
129*7c478bd9Sstevel@tonic-gatemy $tmperr;
130*7c478bd9Sstevel@tonic-gatemy $ret;
131*7c478bd9Sstevel@tonic-gatemy $err;
132*7c478bd9Sstevel@tonic-gate
133*7c478bd9Sstevel@tonic-gate# Read the project file.  sysopen() is used so we can control the file mode.
134*7c478bd9Sstevel@tonic-gateif (! sysopen($pfh, $projfile, O_RDONLY)) {
135*7c478bd9Sstevel@tonic-gate	if ($! == Errno::ENOENT) {
136*7c478bd9Sstevel@tonic-gate		$pf = [];
137*7c478bd9Sstevel@tonic-gate		$mode = 0644;
138*7c478bd9Sstevel@tonic-gate		$uid = getuid();
139*7c478bd9Sstevel@tonic-gate		$gid = getgid();
140*7c478bd9Sstevel@tonic-gate	} else {
141*7c478bd9Sstevel@tonic-gate		error([10, gettext('Cannot open %s: %s'), $projfile, $!]);
142*7c478bd9Sstevel@tonic-gate	}
143*7c478bd9Sstevel@tonic-gate} else {
144*7c478bd9Sstevel@tonic-gate	($mode, $uid, $gid) = (stat($pfh))[2,4,5];
145*7c478bd9Sstevel@tonic-gate
146*7c478bd9Sstevel@tonic-gate	($ret, $pf) = projf_read($pfh, $flags);
147*7c478bd9Sstevel@tonic-gate	if ($ret != 0) {
148*7c478bd9Sstevel@tonic-gate		error(@$pf);
149*7c478bd9Sstevel@tonic-gate	}
150*7c478bd9Sstevel@tonic-gate	close($pfh);
151*7c478bd9Sstevel@tonic-gate	foreach (@$pf) {
152*7c478bd9Sstevel@tonic-gate		$maxpjid = $_->{'projid'} if ($_->{'projid'} > $maxpjid);
153*7c478bd9Sstevel@tonic-gate	}
154*7c478bd9Sstevel@tonic-gate}
155*7c478bd9Sstevel@tonic-gate
156*7c478bd9Sstevel@tonic-gate
157*7c478bd9Sstevel@tonic-gatemy $proj = {};
158*7c478bd9Sstevel@tonic-gatemy ($value, $list);
159*7c478bd9Sstevel@tonic-gate
160*7c478bd9Sstevel@tonic-gate$proj->{'name'} = '';
161*7c478bd9Sstevel@tonic-gate$proj->{'projid'} = $maxpjid + 1;;
162*7c478bd9Sstevel@tonic-gate$proj->{'comment'} = '';
163*7c478bd9Sstevel@tonic-gate$proj->{'userlist'} = [];
164*7c478bd9Sstevel@tonic-gate$proj->{'grouplist'} = [];
165*7c478bd9Sstevel@tonic-gate$proj->{'attributelist'} = [];
166*7c478bd9Sstevel@tonic-gate$proj->{'modified'} = 'true';
167*7c478bd9Sstevel@tonic-gatepush(@$pf, $proj);
168*7c478bd9Sstevel@tonic-gate
169*7c478bd9Sstevel@tonic-gate# Update the record as appropriate.
170*7c478bd9Sstevel@tonic-gate$err = [];
171*7c478bd9Sstevel@tonic-gate
172*7c478bd9Sstevel@tonic-gate($ret, $value) = projent_parse_name($pname);
173*7c478bd9Sstevel@tonic-gateif ($ret != 0) {
174*7c478bd9Sstevel@tonic-gate	push(@$err, @$value);
175*7c478bd9Sstevel@tonic-gate} else {
176*7c478bd9Sstevel@tonic-gate	$proj->{'name'} = $value;
177*7c478bd9Sstevel@tonic-gate	if (!defined($opt_n)) {
178*7c478bd9Sstevel@tonic-gate		($ret, $tmperr) =
179*7c478bd9Sstevel@tonic-gate		    projent_validate_unique_name($proj, $pf);
180*7c478bd9Sstevel@tonic-gate		if ($ret != 0) {
181*7c478bd9Sstevel@tonic-gate			push(@$err, @$tmperr);
182*7c478bd9Sstevel@tonic-gate		}
183*7c478bd9Sstevel@tonic-gate	}
184*7c478bd9Sstevel@tonic-gate}
185*7c478bd9Sstevel@tonic-gate
186*7c478bd9Sstevel@tonic-gate# Apply any changes due to options.
187*7c478bd9Sstevel@tonic-gateif (defined($opt_p)) {
188*7c478bd9Sstevel@tonic-gate
189*7c478bd9Sstevel@tonic-gate	my ($ret, $value) = projent_parse_projid($opt_p);
190*7c478bd9Sstevel@tonic-gate	if ($ret != 0) {
191*7c478bd9Sstevel@tonic-gate		push(@$err, @$value);
192*7c478bd9Sstevel@tonic-gate	} else {
193*7c478bd9Sstevel@tonic-gate		$proj->{'projid'} = $value;
194*7c478bd9Sstevel@tonic-gate		if (!defined($opt_n)) {
195*7c478bd9Sstevel@tonic-gate			($ret, $tmperr) =
196*7c478bd9Sstevel@tonic-gate			    projent_validate_projid($value, {});
197*7c478bd9Sstevel@tonic-gate			if ($ret != 0) {
198*7c478bd9Sstevel@tonic-gate				push(@$err, @$tmperr);
199*7c478bd9Sstevel@tonic-gate			}
200*7c478bd9Sstevel@tonic-gate		}
201*7c478bd9Sstevel@tonic-gate		if ((!defined($opt_n)) && (!defined($opt_o))) {
202*7c478bd9Sstevel@tonic-gate			($ret, $tmperr) =
203*7c478bd9Sstevel@tonic-gate			    projent_validate_unique_id($proj, $pf);
204*7c478bd9Sstevel@tonic-gate			if ($ret != 0) {
205*7c478bd9Sstevel@tonic-gate				push(@$err, @$tmperr);
206*7c478bd9Sstevel@tonic-gate			}
207*7c478bd9Sstevel@tonic-gate		}
208*7c478bd9Sstevel@tonic-gate	}
209*7c478bd9Sstevel@tonic-gate}
210*7c478bd9Sstevel@tonic-gateif (defined($opt_c)) {
211*7c478bd9Sstevel@tonic-gate
212*7c478bd9Sstevel@tonic-gate	my ($ret, $value) = projent_parse_comment($opt_c);
213*7c478bd9Sstevel@tonic-gate	if ($ret != 0) {
214*7c478bd9Sstevel@tonic-gate		push(@$err, @$value);
215*7c478bd9Sstevel@tonic-gate	} else {
216*7c478bd9Sstevel@tonic-gate		$proj->{'comment'} = $value;
217*7c478bd9Sstevel@tonic-gate	}
218*7c478bd9Sstevel@tonic-gate}
219*7c478bd9Sstevel@tonic-gateif (defined($opt_U)) {
220*7c478bd9Sstevel@tonic-gate
221*7c478bd9Sstevel@tonic-gate	my @sortlist;
222*7c478bd9Sstevel@tonic-gate	my ($ret, $list) = projent_parse_users($opt_U,
223*7c478bd9Sstevel@tonic-gate	    { 'allowspaces' => 1 });
224*7c478bd9Sstevel@tonic-gate	if ($ret != 0) {
225*7c478bd9Sstevel@tonic-gate		push(@$err, @$list);
226*7c478bd9Sstevel@tonic-gate	} else {
227*7c478bd9Sstevel@tonic-gate		@sortlist = sort(@$list);
228*7c478bd9Sstevel@tonic-gate		$proj->{'userlist'} = \@sortlist;
229*7c478bd9Sstevel@tonic-gate	}
230*7c478bd9Sstevel@tonic-gate}
231*7c478bd9Sstevel@tonic-gateif (defined($opt_G)) {
232*7c478bd9Sstevel@tonic-gate
233*7c478bd9Sstevel@tonic-gate	my @sortlist;
234*7c478bd9Sstevel@tonic-gate	my ($ret, $list) = projent_parse_groups($opt_G,
235*7c478bd9Sstevel@tonic-gate	    { 'allowspaces' => 1 });
236*7c478bd9Sstevel@tonic-gate	if ($ret != 0) {
237*7c478bd9Sstevel@tonic-gate		push(@$err, @$list);
238*7c478bd9Sstevel@tonic-gate	} else {
239*7c478bd9Sstevel@tonic-gate		@sortlist = sort(@$list);
240*7c478bd9Sstevel@tonic-gate		$proj->{'grouplist'} = \@sortlist;
241*7c478bd9Sstevel@tonic-gate	}
242*7c478bd9Sstevel@tonic-gate}
243*7c478bd9Sstevel@tonic-gate
244*7c478bd9Sstevel@tonic-gatemy $attrib;
245*7c478bd9Sstevel@tonic-gatemy @attriblist;
246*7c478bd9Sstevel@tonic-gatemy @sortlist;
247*7c478bd9Sstevel@tonic-gate
248*7c478bd9Sstevel@tonic-gate# Support multiple instances of -K.
249*7c478bd9Sstevel@tonic-gateforeach $attrib (@opt_K) {
250*7c478bd9Sstevel@tonic-gate
251*7c478bd9Sstevel@tonic-gate	my ($ret, $list) = projent_parse_attributes($attrib,
252*7c478bd9Sstevel@tonic-gate	    {'allowunits' => 1});
253*7c478bd9Sstevel@tonic-gate	if ($ret != 0) {
254*7c478bd9Sstevel@tonic-gate		push(@$err, @$list);
255*7c478bd9Sstevel@tonic-gate	} else {
256*7c478bd9Sstevel@tonic-gate		push(@attriblist, @$list);
257*7c478bd9Sstevel@tonic-gate	}
258*7c478bd9Sstevel@tonic-gate}
259*7c478bd9Sstevel@tonic-gate
260*7c478bd9Sstevel@tonic-gateif (@attriblist) {
261*7c478bd9Sstevel@tonic-gate	@sortlist = sort { $a->{'name'} cmp $b->{'name'} } @attriblist;
262*7c478bd9Sstevel@tonic-gate	$proj->{'attributelist'} = \@sortlist;
263*7c478bd9Sstevel@tonic-gate}
264*7c478bd9Sstevel@tonic-gate
265*7c478bd9Sstevel@tonic-gate# Validate project entry changes.
266*7c478bd9Sstevel@tonic-gateif (!defined($opt_n)) {
267*7c478bd9Sstevel@tonic-gate	($ret, $tmperr) = projent_validate($proj, $flags);
268*7c478bd9Sstevel@tonic-gate	if ($ret != 0) {
269*7c478bd9Sstevel@tonic-gate		push(@$err, @$tmperr);
270*7c478bd9Sstevel@tonic-gate	}
271*7c478bd9Sstevel@tonic-gate}
272*7c478bd9Sstevel@tonic-gateif (@$err) {
273*7c478bd9Sstevel@tonic-gate	error(@$err);
274*7c478bd9Sstevel@tonic-gate}
275*7c478bd9Sstevel@tonic-gate
276*7c478bd9Sstevel@tonic-gate# Write out the project file.
277*7c478bd9Sstevel@tonic-gateumask(0000);
278*7c478bd9Sstevel@tonic-gatesysopen($pfh, $tmpprojf, O_WRONLY | O_CREAT | O_EXCL, $mode) ||
279*7c478bd9Sstevel@tonic-gate    error([10, gettext('Cannot create %s: %s'), $tmpprojf, $!]);
280*7c478bd9Sstevel@tonic-gateprojf_write($pfh, $pf);
281*7c478bd9Sstevel@tonic-gateclose($pfh);
282*7c478bd9Sstevel@tonic-gateif (!chown($uid, $gid, $tmpprojf)) {
283*7c478bd9Sstevel@tonic-gate	unlink($tmpprojf);
284*7c478bd9Sstevel@tonic-gate	error([10, gettext('Cannot set ownership of %s: %s'),
285*7c478bd9Sstevel@tonic-gate	    $tmpprojf, $!]);
286*7c478bd9Sstevel@tonic-gate}
287*7c478bd9Sstevel@tonic-gateif (! rename($tmpprojf, $projfile)) {
288*7c478bd9Sstevel@tonic-gate	unlink($tmpprojf);
289*7c478bd9Sstevel@tonic-gate	error([10, gettext('cannot rename %s to %s: %s'),
290*7c478bd9Sstevel@tonic-gate	    $tmpprojf, $projfile, $!]);
291*7c478bd9Sstevel@tonic-gate}
292*7c478bd9Sstevel@tonic-gate
293*7c478bd9Sstevel@tonic-gateexit(0);
294