changeset 3684:a0773f73b68d

PSARC 2006/554 setproject(3PROJECT) defining, and enhancing behaviour 6194864 simultaneous setproject()'s on the same project can fail to set rctl 6449567 setproject(3PROJECT) deletes resource controls set through prctl(1M) 6450539 projmod(1M) does not provide a mechanism to refresh "in-core" enforced resource controls 6491754 project.max-contracts should not allow basic privileges 6491804 task.final project property is not honoured if pools are not enabled
author rd117015
date Tue, 20 Feb 2007 10:39:20 -0800
parents 39e9a07d770e
children 7a7a1dd7ce48
files usr/src/cmd/perl/contrib/Sun/Solaris/Task/Task.pm usr/src/cmd/perl/contrib/Sun/Solaris/Task/Task.xs usr/src/cmd/projadd/projmod.pl usr/src/cmd/truss/codes.c usr/src/cmd/truss/expound.c usr/src/cmd/truss/print.c usr/src/cmd/truss/print.h usr/src/cmd/truss/systable.c usr/src/lib/common/inc/c_synonyms.h usr/src/lib/libc/inc/synonyms.h usr/src/lib/libc/port/llib-lc usr/src/lib/libc/port/mapfile-vers usr/src/lib/libc/port/sys/rctlsys.c usr/src/lib/libproc/common/libproc.h usr/src/lib/libproc/common/llib-lproc usr/src/lib/libproc/common/mapfile-vers usr/src/lib/libproc/common/pr_getrctl.c usr/src/lib/libproject/common/setproject.c usr/src/uts/common/os/project.c usr/src/uts/common/os/rctl.c usr/src/uts/common/sys/rctl.h usr/src/uts/common/sys/rctl_impl.h usr/src/uts/common/sys/task.h usr/src/uts/common/syscall/rctlsys.c
diffstat 24 files changed, 821 insertions(+), 106 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/perl/contrib/Sun/Solaris/Task/Task.pm	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Task/Task.pm	Tue Feb 20 10:39:20 2007 -0800
@@ -1,13 +1,12 @@
 #
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -38,7 +37,7 @@
 XSLoader::load(__PACKAGE__, $VERSION);
 
 our (@EXPORT_OK, %EXPORT_TAGS);
-my @constants = qw(TASK_NORMAL TASK_FINAL);
+my @constants = qw(TASK_NORMAL TASK_FINAL TASK_PROJ_PURGE);
 my @syscalls = qw(settaskid gettaskid);
 @EXPORT_OK = (@constants, @syscalls);
 %EXPORT_TAGS = (CONSTANTS => \@constants, SYSCALLS => \@syscalls,
--- a/usr/src/cmd/perl/contrib/Sun/Solaris/Task/Task.xs	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Task/Task.xs	Tue Feb 20 10:39:20 2007 -0800
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
  * Task.xs contains XS wrappers for the task maniplulation functions.
@@ -58,6 +57,7 @@
 	stash = gv_stashpv("Sun::Solaris::Task", TRUE);
 	newCONSTSUB(stash, "TASK_NORMAL", newSViv(TASK_NORMAL));
 	newCONSTSUB(stash, "TASK_FINAL", newSViv(TASK_FINAL));
+	newCONSTSUB(stash, "TASK_PROJ_PURGE", newSViv(TASK_PROJ_PURGE));
 	}
 
 taskid_t
--- a/usr/src/cmd/projadd/projmod.pl	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/cmd/projadd/projmod.pl	Tue Feb 20 10:39:20 2007 -0800
@@ -3,9 +3,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -21,7 +20,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #ident	"%Z%%M%	%I%	%E% SMI"
@@ -38,6 +37,7 @@
 use POSIX qw(locale_h);
 use Sun::Solaris::Utils qw(textdomain gettext);
 use Sun::Solaris::Project qw(:ALL :PRIVATE);
+use Sun::Solaris::Task qw(:ALL);
 
 #
 # Print a usage message and exit.
@@ -51,7 +51,7 @@
 	printf(STDERR gettext(
 	    "Usage: %s [-n] [-f filename]\n"), $prog);
 	printf(STDERR gettext(
-	    "       %s [-n] [-f filename] [-p projid [-o]] [-c comment]\n".
+	    "       %s [-n] [-A|-f filename] [-p projid [-o]] [-c comment]\n".
             "       %s [-a|-s|-r] [-U user[,user...]] [-G group[,group...]]\n".
             "       %s [-K name[=value[,value...]]] [-l new_projectname] ".
 	    "project\n"), $prog, $space, $space, $space);
@@ -360,7 +360,7 @@
 $flags = {};
 my $modify = 0;
 
-my $projfile = &PROJF_PATH;
+my $projfile;
 my $opt_n;
 my $opt_c;
 my $opt_o;
@@ -372,6 +372,7 @@
 my $opt_U;
 my $opt_G;
 my @opt_K;
+my $opt_A;
 
 GetOptions("f=s" => \$projfile,
 	   "n"   => \$opt_n,
@@ -384,11 +385,12 @@
 	   "a"	 => \$opt_a,
 	   "U=s" => \$opt_U,
 	   "G=s" => \$opt_G,
-	   "K=s" => \@opt_K) || usage();
+	   "K=s" => \@opt_K,
+  	   "A"	 => \$opt_A) || usage();
 
 usage(gettext('Invalid command-line arguments')) if (@ARGV > 1);
 
-if ($opt_c || $opt_G || $opt_l || $opt_p || $opt_U || @opt_K) {
+if ($opt_c || $opt_G || $opt_l || $opt_p || $opt_U || @opt_K || $opt_A) {
 	$modify = 1;
 	if (! defined($ARGV[0])) {
 		usage(gettext('No project name specified'));
@@ -399,6 +401,14 @@
 	usage(gettext('missing -c, -G, -l, -p, -U, or -K'));
 }
 
+if (defined($opt_A) && defined($projfile)) {
+	usage(gettext('-A and -f are mutually exclusive'));
+}
+
+if (! defined($projfile)) {
+	$projfile = &PROJF_PATH;
+}
+
 if ($modify && $projfile eq '-') {
 	usage(gettext('Cannot modify standard input'));
 }
@@ -654,8 +664,69 @@
 
 }
 
-exit(0);
+if (defined($opt_A)) {
+	my $error;
+
+	if (($error = setproject($pname, "root", TASK_FINAL|TASK_PROJ_PURGE)) != 0) {
 
+		if ($error == SETPROJ_ERR_TASK) {
+			if ($!{EAGAIN}) {
+				error([5, gettext("resource control limit has ".
+				     "been reached\n")]);
+			} elsif ($!{ESRCH}) {
+				error([5, gettext("user \"%s\" is not a member ".
+				     "of project \"%s\"\n"), "root", $pname]);
+			} elsif ($!{EACCES}) {
+				error([5, gettext("the invoking task is final\n"
+				     )]);
+			} else {
+				error([5, gettext("could not join project \"%s".
+				     "\"\n"), $pname]);
+			}
 
+		} elsif ($error == SETPROJ_ERR_POOL) {
+			if ($!{EACCES}) {
+				error([5, gettext("no resource pool accepting ".
+				     "default bindings exists for project \"%s".
+				     "\"\n"), $pname]);
+	        	} elsif ($!{ESRCH}) {
+				error([5, gettext("specified resource pool ".
+				     "does not exist for project \"%s\"\n"),
+				     $pname]);
+			} else {
+				error([5, gettext("could not bind to default ".
+				     "resource pool for project \"%s\"\n"),
+				     $pname]);
+			}
 
+		} else {
+			#
+			# $error represents the position - within the semi-colon
+			# delimited $attribute - that generated the error
+			#
+			if ($error <= 0) {
+				error([5, gettext("setproject failed for ".
+				     "project \"%s\"\n"), $pname]);
+			} else {
+				my ($name, $projid, $comment, $users_ref,
+				     $groups_ref, $attr) = getprojbyname($pname);
+				my $attribute = ($attr =~
+				     /(\S+?)=\S+?(?:;|\z)/g)[$error - 1];
 
+				if (!$attribute) {
+					error([5, gettext("warning, resource ".
+					     "control assignment failed for ".
+					     "project \"%s\" attribute %d\n"),
+					     $pname, $error]);
+				} else {
+					error([5, gettext("warning, %s ".
+					     "resource control assignment ".
+					     "failed for project \"%s\"\n"),
+					     $attribute, $pname]);
+				}
+			}
+		}
+	}
+}
+
+exit(0);
--- a/usr/src/cmd/truss/codes.c	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/cmd/truss/codes.c	Tue Feb 20 10:39:20 2007 -0800
@@ -1129,6 +1129,7 @@
 	case 1:		str = "SETRCTL";	break;
 	case 2:		str = "RCTLSYS_LST";	break;
 	case 3:		str = "RCTLSYS_CTL";	break;
+	case 4:		str = "RCTLSYS_SETPROJ";	break;
 	default:	str = "UNKNOWN";	break;
 	}
 	return (str);
--- a/usr/src/cmd/truss/expound.c	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/cmd/truss/expound.c	Tue Feb 20 10:39:20 2007 -0800
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -93,6 +93,7 @@
 #include <tsol/label.h>
 #include <sys/nvpair.h>
 #include <libnvpair.h>
+#include <sys/rctl_impl.h>
 
 #include "ramdata.h"
 #include "systable.h"
@@ -4687,6 +4688,8 @@
 static void
 show_rctls(private_t *pri)
 {
+	int entry;
+
 	switch (pri->sys_args[0]) {
 	case 0:	/* getrctl */
 	case 1: /* setrctl */
@@ -4705,6 +4708,16 @@
 			show_rctlblk(pri, pri->sys_args[3]);
 		}
 		break;
+	case 4: /* setprojrctl */
+		for (entry = 0; entry < pri->sys_args[4]; entry++) {
+			(void) printf("%s\tNew rctlblk[%d]: 0x%lx\n",
+			    pri->pname, entry,
+			    (long)RCTLBLK_INC(pri->sys_args[3], entry));
+			if (RCTLBLK_INC(pri->sys_args[3], entry) != NULL) {
+				show_rctlblk(pri,
+				    (long)RCTLBLK_INC(pri->sys_args[3], entry));
+			}
+		}
 	}
 }
 
--- a/usr/src/cmd/truss/print.c	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/cmd/truss/print.c	Tue Feb 20 10:39:20 2007 -0800
@@ -80,6 +80,7 @@
 #include <sys/rctl.h>
 #include <sys/rctl_impl.h>
 #include <sys/fork.h>
+#include <sys/task.h>
 #include "ramdata.h"
 #include "print.h"
 #include "proto.h"
@@ -2542,6 +2543,20 @@
 }
 
 /*
+ * Print setprojrctl flags
+ */
+void
+prt_spf(private_t *pri, int raw, long val)
+{
+	long action = val & TASK_PROJ_MASK;
+
+	if (!raw && (action == TASK_PROJ_PURGE))
+		outstring(pri, "TASK_PROJ_PURGE");
+	else
+		prt_hex(pri, 0, val);
+}
+
+/*
  * Print forkx() flags
  */
 void
@@ -2664,5 +2679,6 @@
 	prt_rsf,	/* RSF -- print setrctl() flags */
 	prt_rcf,	/* RCF -- print rctlsys_ctl() flags */
 	prt_fxf,	/* FXF -- print forkx() flags */
+	prt_spf,	/* SPF -- print rctlsys_projset() flags */
 	prt_dec,	/* HID -- hidden argument, make this the last one */
 };
--- a/usr/src/cmd/truss/print.h	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/cmd/truss/print.h	Tue Feb 20 10:39:20 2007 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -133,7 +133,8 @@
 #define	RSF	91		/* print rctlsys_set flags */
 #define	RCF	92		/* print rctlsys_ctl flags */
 #define	FXF	93		/* print forkx flags */
-#define	HID	94		/* hidden argument, don't print */
+#define	SPF	94		/* print rctlsys_projset flags */
+#define	HID	95		/* hidden argument, don't print */
 				/* make sure HID is always the last member */
 
 /*
--- a/usr/src/cmd/truss/systable.c	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/cmd/truss/systable.c	Tue Feb 20 10:39:20 2007 -0800
@@ -578,6 +578,7 @@
 {"setrctl",	6, DEC, NOV, HID, STG, HEX, HEX, HID, RSF},	/* 1 */
 {"rctlsys_lst",	6, DEC, NOV, HID, HID, HEX, HID, HEX, HID},	/* 2 */
 {"rctlsys_ctl",	6, DEC, NOV, HID, STG, HEX, HID, HID, RCF},	/* 3 */
+{"setprojrctl",	6, DEC, NOV, HID, STG, HID, HEX, HEX, SPF},	/* 4 */
 };
 #define	NRCTLCODE	(sizeof (rctltable) / sizeof (struct systable))
 
--- a/usr/src/lib/common/inc/c_synonyms.h	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/lib/common/inc/c_synonyms.h	Tue Feb 20 10:39:20 2007 -0800
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -800,6 +800,7 @@
 #define	setppriv			_setppriv
 #define	setpwent			_setpwent
 #define	setrctl				_setrctl
+#define	setprojrctl			_setprojrctl
 #define	setregid			_setregid
 #define	setreuid			_setreuid
 #define	setrlimit			_setrlimit
--- a/usr/src/lib/libc/inc/synonyms.h	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/lib/libc/inc/synonyms.h	Tue Feb 20 10:39:20 2007 -0800
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -917,6 +917,7 @@
 #define	setpgid			_setpgid
 #define	setpgrp			_setpgrp
 #define	setppriv		_setppriv
+#define	setprojrctl		_setprojrctl
 #define	setpwent		_setpwent
 #define	setrctl			_setrctl
 #define	setregid		_setregid
--- a/usr/src/lib/libc/port/llib-lc	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/lib/libc/port/llib-lc	Tue Feb 20 10:39:20 2007 -0800
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -1348,6 +1348,7 @@
 int setrctl(const char *name, rctlblk_t *old_rblk, rctlblk_t *new_rblk,
     int flags);
 /* (private functions) */
+int setprojrctl(const char *name, rctlblk_t *new_rblk, size_t size, int flags);
 int rctlctl(const char *, rctlblk_t *, int);
 size_t rctllist(char *, size_t);
 
--- a/usr/src/lib/libc/port/mapfile-vers	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/lib/libc/port/mapfile-vers	Tue Feb 20 10:39:20 2007 -0800
@@ -1989,6 +1989,8 @@
 	_setgrent;
 	_setlogmask;
 	_setpwent;
+	setprojrctl;
+	_setprojrctl;
 	_setregid;
 	_setreuid;
 	setsigacthandler;
--- a/usr/src/lib/libc/port/sys/rctlsys.c	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/lib/libc/port/sys/rctlsys.c	Tue Feb 20 10:39:20 2007 -0800
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -30,6 +29,7 @@
 #pragma weak getrctl = _getrctl
 #pragma weak rctllist = _rctllist
 #pragma weak rctlctl = _rctlctl
+#pragma	weak setprojrctl = _setprojrctl
 
 #include "synonyms.h"
 #include <sys/types.h>
@@ -71,3 +71,10 @@
 {
 	return (syscall(SYS_rctlsys, 3, name, rblk, NULL, 0, flags));
 }
+
+int
+setprojrctl(const char *name, rctlblk_t *new_rblk, size_t size, int flags)
+{
+	return (syscall(SYS_rctlsys,
+	    4, name, NULL, new_rblk, size, flags));
+}
--- a/usr/src/lib/libproc/common/libproc.h	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/lib/libproc/common/libproc.h	Tue Feb 20 10:39:20 2007 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -325,6 +325,8 @@
 			int, struct rlimit *);
 extern	int	pr_setrlimit(struct ps_prochandle *,
 			int, const struct rlimit *);
+extern	int	pr_setprojrctl(struct ps_prochandle *, const char *,
+			rctlblk_t *, size_t, int);
 #if defined(_LARGEFILE64_SOURCE)
 extern	int	pr_getrlimit64(struct ps_prochandle *,
 			int, struct rlimit64 *);
--- a/usr/src/lib/libproc/common/llib-lproc	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/lib/libproc/common/llib-lproc	Tue Feb 20 10:39:20 2007 -0800
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -23,7 +22,7 @@
 /* PROTOLIB1 */
 
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 #pragma ident	"%Z%%M%	%I%	%E% SMI"
@@ -248,6 +247,8 @@
 		rctlblk_t *old_blk, rctlblk_t *new_blk, int rflag);
 int   pr_setrctl(struct ps_prochandle *Pr, const char *rname,
 		rctlblk_t *old_blk, rctlblk_t *new_blk, int rflag);
+int   pr_setprojrctl(struct ps_prochandle *Pr, const char *rname,
+		rctlblk_t *new_blk, size_t size, int rflag);
 
 /* pr_getrlimit.c */
 int	pr_getrlimit(struct ps_prochandle *Pr,
--- a/usr/src/lib/libproc/common/mapfile-vers	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/lib/libproc/common/mapfile-vers	Tue Feb 20 10:39:20 2007 -0800
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # ident	"%Z%%M%	%I%	%E% SMI"
@@ -208,6 +208,7 @@
 	pr_pset_bind;
 	pr_rename;
 	pr_setitimer;
+	pr_setprojrctl;
 	pr_setrctl;
 	pr_setrlimit;
 	pr_setrlimit64;
--- a/usr/src/lib/libproc/common/pr_getrctl.c	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/lib/libproc/common/pr_getrctl.c	Tue Feb 20 10:39:20 2007 -0800
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,8 +19,8 @@
  * CDDL HEADER END
  */
 /*
- * Copyright (c) 2001 by Sun Microsystems, Inc.
- * All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
  */
 
 #pragma ident	"%Z%%M%	%I%	%E% SMI"
@@ -33,6 +32,7 @@
 #include <errno.h>
 #include <strings.h>
 #include "libproc.h"
+#include <sys/rctl_impl.h>
 
 /*
  * getrctl() system call -- executed by subject process
@@ -197,3 +197,78 @@
 	}
 	return (rval.sys_rval1);
 }
+
+/*
+ * setprojrctl() system call -- executed by subject process
+ */
+int
+pr_setprojrctl(struct ps_prochandle *Pr, const char *rname,
+	rctlblk_t *new_blk, size_t size, int rflag)
+{
+	sysret_t rval;
+	argdes_t argd[6];
+	argdes_t *adp;
+	int error;
+
+	if (Pr == NULL)		/* no subject process */
+		return (setprojrctl(rname, new_blk, size, rflag));
+
+	adp = &argd[0];
+	adp->arg_value = 4;	/* switch for setprojrctls in rctlsys */
+	adp->arg_object = NULL;
+	adp->arg_type = AT_BYVAL;
+	adp->arg_inout = AI_INPUT;
+	adp->arg_size = 0;
+
+	adp++;
+	adp->arg_value = 0;
+	adp->arg_object = (void *)rname;
+	adp->arg_type = AT_BYREF;
+	adp->arg_inout = AI_INPUT;
+	adp->arg_size = strlen(rname) + 1;
+
+	adp++;
+	adp->arg_value = 0;	/* old_blk is not used by setprojrctls() */
+	adp->arg_object = NULL;
+	adp->arg_type = AT_BYVAL;
+	adp->arg_inout = AI_INPUT;
+	adp->arg_size = 0;
+
+
+	adp++;
+	if (new_blk == NULL) {
+		adp->arg_value = 0;
+		adp->arg_object = NULL;
+		adp->arg_type = AT_BYVAL;
+		adp->arg_inout = AI_INPUT;
+		adp->arg_size = 0;
+	} else {
+		adp->arg_value = 0;
+		adp->arg_object = new_blk;
+		adp->arg_type = AT_BYREF;
+		adp->arg_inout = AI_INPUT;
+		adp->arg_size = rctlblk_size() * size;
+	}
+
+	adp++;
+	adp->arg_value = size;		/* obufsz is used by setrctls() */
+	adp->arg_object = NULL;
+	adp->arg_type = AT_BYVAL;
+	adp->arg_inout = AI_INPUT;
+	adp->arg_size = 0;
+
+	adp++;
+	adp->arg_value = rflag;
+	adp->arg_object = NULL;
+	adp->arg_type = AT_BYVAL;
+	adp->arg_inout = AI_INPUT;
+	adp->arg_size = 0;
+
+	error = Psyscall(Pr, &rval, SYS_rctlsys, 6, &argd[0]);
+
+	if (error) {
+		errno = (error > 0) ? error : ENOSYS;
+		return (-1);
+	}
+	return (rval.sys_rval1);
+}
--- a/usr/src/lib/libproject/common/setproject.c	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/lib/libproject/common/setproject.c	Tue Feb 20 10:39:20 2007 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -36,6 +36,7 @@
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
+#include <strings.h>
 #include <nss_dbdefs.h>
 #include <pwd.h>
 #include <pool.h>
@@ -45,6 +46,7 @@
 #include <zone.h>
 #include <sys/pool.h>
 #include <sys/pool_impl.h>
+#include <sys/rctl_impl.h>
 
 static void
 xstrtolower(char *s)
@@ -177,7 +179,7 @@
 #define	BADSPEC		5
 
 static int
-rctl_set(char *ctl_name, char *val, struct ps_prochandle *Pr)
+rctl_set(char *ctl_name, char *val, struct ps_prochandle *Pr, int flags)
 {
 	int error = 0;
 	uint_t component = 0;
@@ -185,18 +187,68 @@
 	uint_t state = 0;
 	char *component_head;
 	rctlblk_t *blk;
+	rctlblk_t *ablk;
+	int project_entity = 0;
+	int count = 0;
+	char *tmp;
+	int local_action;
 
-	remove_spaces(val);
-	if ((blk = malloc(rctlblk_size())) == NULL) {
+	/* We cannot modify a zone resource control */
+	if (strncmp(ctl_name, "zone.", strlen("zone.")) == 0) {
 		return (SETFAILED);
 	}
 
+	remove_spaces(val);
+
 	/*
-	 * Tear down everything with this ctl name.
+	 * As we are operating in a new task, both process and task
+	 * rctls are referenced by this process alone.  Tear down
+	 * matching process and task rctls only.
+	 *
+	 * blk will be the RCPRIV_SYSTEM for this resource control,
+	 * populated by the last pr_setrctl().
 	 */
-	while (pr_getrctl(Pr, ctl_name, NULL, blk, RCTL_FIRST) != -1 &&
-	    rctlblk_get_privilege(blk) != RCPRIV_SYSTEM) {
-		(void) pr_setrctl(Pr, ctl_name, NULL, blk, RCTL_DELETE);
+	if ((strncmp(ctl_name, "process.", strlen("process.")) == 0) ||
+	    (strncmp(ctl_name, "task.", strlen("task.")) == 0)) {
+
+		if ((blk = (rctlblk_t *)malloc(rctlblk_size())) == NULL) {
+			return (SETFAILED);
+		}
+
+
+		while (pr_getrctl(Pr, ctl_name, NULL, blk, RCTL_FIRST) != -1 &&
+		    rctlblk_get_privilege(blk) != RCPRIV_SYSTEM) {
+			(void) pr_setrctl(Pr, ctl_name, NULL, blk, RCTL_DELETE);
+		}
+
+	} else if (strncmp(ctl_name, "project.", strlen("project.")) == 0) {
+		project_entity = 1;
+
+		/* Determine how many attributes we'll be setting */
+		for (tmp = val; *tmp != '\0'; tmp++) {
+			if (*tmp == '(')
+				count++;
+		}
+
+		/* Allocate sufficient memory for rctl blocks */
+		if ((count == 0) || ((ablk =
+		    (rctlblk_t *)malloc(rctlblk_size() * count)) == NULL)) {
+			return (SETFAILED);
+		}
+		blk = ablk;
+
+		/*
+		 * In order to set the new rctl's local_action, we'll need the
+		 * current value of global_flags.  We obtain global_flags by
+		 * performing a pr_getrctl().
+		 *
+		 * The ctl_name has been verified as valid, so we have no reason
+		 * to suspect that pr_getrctl() will return an error.
+		 */
+		(void) pr_getrctl(Pr, ctl_name, NULL, blk, RCTL_FIRST);
+
+	} else {
+		return (SETFAILED);
 	}
 
 	/*
@@ -205,11 +257,13 @@
 	rctlblk_set_privilege(blk, RCPRIV_PRIVILEGED);
 	rctlblk_set_value(blk, 0);
 	rctlblk_set_local_flags(blk, 0);
+
 	if (rctlblk_get_global_flags(blk) & RCTL_GLOBAL_DENY_ALWAYS)
-		rctlblk_set_local_action(blk, RCTL_LOCAL_DENY, 0);
+		local_action = RCTL_LOCAL_DENY;
 	else
-		rctlblk_set_local_action(blk, RCTL_LOCAL_NOACTION, 0);
+		local_action = RCTL_LOCAL_NOACTION;
 
+	rctlblk_set_local_action(blk, local_action, 0);
 
 	for (; ; val++) {
 		switch (*val) {
@@ -238,22 +292,35 @@
 					state &= ~INPAREN;
 					component = 0;
 					valuecount++;
-					if (pr_setrctl(Pr, ctl_name, NULL, blk,
-					    RCTL_INSERT) == -1)
+
+					if (project_entity &&
+					    (rctlblk_get_privilege(blk) ==
+					    RCPRIV_BASIC)) {
 						error = SETFAILED;
+					} else if (project_entity) {
+						if (valuecount > count)
+							return (SETFAILED);
+
+						if (valuecount != count)
+							blk = RCTLBLK_INC(ablk,
+								valuecount);
+					} else {
+						if (pr_setrctl(Pr, ctl_name,
+						    NULL, blk, RCTL_INSERT) ==
+						    -1)
+							error = SETFAILED;
+					}
 
 					/* re-initialize block */
-					rctlblk_set_privilege(blk,
-					    RCPRIV_PRIVILEGED);
-					rctlblk_set_value(blk, 0);
-					rctlblk_set_local_flags(blk, 0);
-					if (rctlblk_get_global_flags(blk) &
-					    RCTL_GLOBAL_DENY_ALWAYS)
+					if (!project_entity ||
+					    (valuecount != count)) {
+						rctlblk_set_privilege(blk,
+						    RCPRIV_PRIVILEGED);
+						rctlblk_set_value(blk, 0);
+						rctlblk_set_local_flags(blk, 0);
 						rctlblk_set_local_action(blk,
-						    RCTL_LOCAL_DENY, 0);
-					else
-						rctlblk_set_local_action(blk,
-						    RCTL_LOCAL_NOACTION, 0);
+						    local_action, 0);
+					}
 				} else {
 					error = CLOSEBEFOREOPEN;
 				}
@@ -288,6 +355,12 @@
 			break;
 	}
 
+	if (project_entity) {
+		blk = ablk;
+		if (pr_setprojrctl(Pr, ctl_name, blk, count, flags) == -1)
+			error = SETFAILED;
+	}
+
 	free(blk);
 
 	if (valuecount == 0)
@@ -465,6 +538,7 @@
 	int ret = 0;
 	kva_t *kv_array;
 	struct project local_proj; /* space to store proj if not provided */
+	const char *pool_name = NULL;
 
 	if (project_name != NULL) {
 		/*
@@ -508,45 +582,37 @@
 		projid = getprojid();
 	}
 
+
+	if ((kv_array = _str2kva(proj->pj_attr, KV_ASSIGN,
+	    KV_DELIMITER)) != NULL) {
+		for (i = 0; i < kv_array->length; i++) {
+			if (strcmp(kv_array->data[i].key,
+			    "project.pool") == 0) {
+				pool_name = kv_array->data[i].value;
+			}
+			if (strcmp(kv_array->data[i].key, "task.final") == 0) {
+				flags |= TASK_FINAL;
+			}
+		}
+	}
+
 	/*
-	 * Only bind to a pool if pools are configured.
+	 * Bind process to a pool only if pools are configured
 	 */
 	if (pools_enabled() == 1) {
-		const char *pool_name = NULL;
 		char *old_pool_name;
-		int taskflags = flags;
 		/*
 		 * Attempt to bind to pool before calling
 		 * settaskid().
 		 */
-		if ((kv_array = _str2kva(proj->pj_attr, KV_ASSIGN,
-		    KV_DELIMITER)) != NULL) {
-			for (i = 0; i < kv_array->length; i++) {
-				if (strcmp(kv_array->data[i].key,
-				    "project.pool") == 0) {
-					pool_name = kv_array->data[i].value;
-					break;
-				}
-				if (strcmp(kv_array->data[i].key,
-				    "task.final") == 0) {
-					taskflags |= TASK_FINAL;
-				}
-			}
-		}
-
 		old_pool_name = pool_get_binding(pid);
-
-		/*
-		 * If parent is not bound to the default pool, then we want
-		 * to preserve same binding as parent.
-		 */
-		if (pool_name != NULL && bind_to_pool(pool_name, pid, 0) != 0) {
+		if (bind_to_pool(pool_name, pid, 0) != 0) {
 			if (old_pool_name)
 				free(old_pool_name);
 			_kva_free(kv_array);
 			return (SETPROJ_ERR_POOL);
 		}
-		if (pr_settaskid(Pr, projid, taskflags) == -1) {
+		if (pr_settaskid(Pr, projid, flags & TASK_MASK) == -1) {
 			int saved_errno = errno;
 
 			/*
@@ -568,9 +634,10 @@
 		/*
 		 * Pools are not configured, so simply create new task.
 		 */
-		if (pr_settaskid(Pr, projid, flags) == -1)
+		if (pr_settaskid(Pr, projid, flags & TASK_MASK) == -1) {
+			_kva_free(kv_array);
 			return (SETPROJ_ERR_TASK);
-		kv_array = _str2kva(proj->pj_attr, KV_ASSIGN, KV_DELIMITER);
+		}
 	}
 
 	if (project_name == NULL) {
@@ -612,7 +679,7 @@
 		}
 
 		ret = rctl_set(kv_array->data[i].key,
-		    kv_array->data[i].value, Pr);
+		    kv_array->data[i].value, Pr, flags & TASK_PROJ_MASK);
 
 		if (ret && unknown == 0) {
 			/*
--- a/usr/src/uts/common/os/project.c	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/uts/common/os/project.c	Tue Feb 20 10:39:20 2007 -0800
@@ -905,8 +905,8 @@
 	 * Per project limit on contracts.
 	 */
 	rc_project_contract = rctl_register("project.max-contracts",
-	    RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_COUNT,
-	    INT_MAX, INT_MAX, &project_contract_ops);
+	    RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
+	    RCTL_GLOBAL_COUNT, INT_MAX, INT_MAX, &project_contract_ops);
 	rctl_add_default_limit("project.max-contracts", 10000,
 	    RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY);
 
--- a/usr/src/uts/common/os/rctl.c	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/uts/common/os/rctl.c	Tue Feb 20 10:39:20 2007 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -150,6 +150,41 @@
  *
  *   The locking subsequence of interest is: p_lock, rctl_dict_lock,
  *   rctl_lists_lock, entity->rcs_lock.
+ *
+ * The projects(4) database and project entity resource controls
+ *   A special case is made for RCENTITY_PROJECT values set through the
+ *   setproject(3PROJECT) interface.  setproject() makes use of a private
+ *   interface, setprojrctl(), which passes through an array of resource control
+ *   blocks that need to be set while holding the entity->rcs_lock.  This
+ *   ensures that the act of modifying a project's resource controls is
+ *   "atomic" within the kernel.
+ *
+ *   Within the rctl sub-system, we provide two interfaces that are only used by
+ *   the setprojrctl() code path - rctl_local_insert_all() and
+ *   rctl_local_replace_all().  rctl_local_insert_all() will ensure that the
+ *   resource values specified in *new_values are applied.
+ *   rctl_local_replace_all() will purge the current rctl->rc_projdb and
+ *   rctl->rc_values entries, and apply the *new_values.
+ *
+ *   These functions modify not only the linked list of active resource controls
+ *   (rctl->rc_values), but also a "cached" linked list (rctl->rc_projdb) of
+ *   values set through these interfaces.  To clarify:
+ *
+ *      rctl->rc_values - a linked list of rctl_val_t.  These are the active
+ *      resource values associated with this rctl, and may have been set by
+ *      setrctl() - via prctl(1M), or by setprojrctl() - via
+ *      setproject(3PROJECT).
+ *
+ *      rctl->rc_projdb - a linked list of rctl_val_t.  These reflect the
+ *      resource values set by the setprojrctl() code path.  rc_projdb is not
+ *      referenced by any other component of the rctl sub-system.
+ *
+ *   As various locks are held when calling these functions, we ensure that all
+ *   the possible memory allocations are performed prior to calling the
+ *   function.  *alloc_values is a linked list of uninitialized rctl_val_t,
+ *   which may be used to duplicate a new resource control value (passed in as
+ *   one of the members of the *new_values linked list), in order to populate
+ *   rctl->rc_values.
  */
 
 id_t max_rctl_hndl = 32768;
@@ -1081,6 +1116,7 @@
 
 		rctl->rc_dict_entry = rde;
 		rctl->rc_id = rde->rcd_id;
+		rctl->rc_projdb = NULL;
 
 		rctl->rc_values = rctl_val_list_dup(rde->rcd_default_value,
 		    ragp, NULL, p);
@@ -1688,6 +1724,259 @@
 	return (rctl_local_op(hndl, NULL, val, rctl_local_insert_cb, p));
 }
 
+/*
+ * rctl_local_insert_all_cb()
+ *
+ * Overview
+ *   Called for RCENTITY_PROJECT rctls only, via rctlsys_projset().
+ *
+ *   Inserts new values from the project database (new_values).  alloc_values
+ *   should be a linked list of pre-allocated rctl_val_t, which are used to
+ *   populate (rc_projdb).
+ *
+ *   Should the *new_values linked list match the contents of the rctl's
+ *   rp_projdb then we do nothing.
+ *
+ * Return Values
+ *   0 is always returned.
+ */
+/*ARGSUSED*/
+static int
+rctl_local_insert_all_cb(rctl_hndl_t hndl, struct proc *p, rctl_entity_p_t *e,
+    rctl_t *rctl, rctl_val_t *new_values, rctl_val_t *alloc_values)
+{
+	rctl_val_t *val;
+	rctl_val_t *tmp_val;
+	rctl_val_t *next;
+	int modified = 0;
+
+	/*
+	 * If this the first time we've set this project rctl, then we delete
+	 * all the privilege values.  These privilege values have been set by
+	 * rctl_add_default_limit().
+	 *
+	 * We save some cycles here by not calling rctl_val_list_delete().
+	 */
+	if (rctl->rc_projdb == NULL) {
+		val = rctl->rc_values;
+
+		while (val != NULL) {
+			if (val->rcv_privilege == RCPRIV_PRIVILEGED) {
+				if (val->rcv_prev != NULL)
+					val->rcv_prev->rcv_next = val->rcv_next;
+				else
+					rctl->rc_values = val->rcv_next;
+
+				if (val->rcv_next != NULL)
+					val->rcv_next->rcv_prev = val->rcv_prev;
+
+				tmp_val = val;
+				val = val->rcv_next;
+				kmem_cache_free(rctl_val_cache, tmp_val);
+			} else {
+				val = val->rcv_next;
+			}
+		}
+		modified = 1;
+	}
+
+	/*
+	 * Delete active values previously set through the project database.
+	 */
+	val = rctl->rc_projdb;
+
+	while (val != NULL) {
+
+		/* Is the old value found in the new values? */
+		if (rctl_val_list_find(&new_values, val) == NULL) {
+
+			/*
+			 * Delete from the active values if it originated from
+			 * the project database.
+			 */
+			if (((tmp_val = rctl_val_list_find(&rctl->rc_values,
+			    val)) != NULL) &&
+			    (tmp_val->rcv_flagaction & RCTL_LOCAL_PROJDB)) {
+				(void) rctl_val_list_delete(&rctl->rc_values,
+				    tmp_val);
+			}
+
+			tmp_val = val->rcv_next;
+			(void) rctl_val_list_delete(&rctl->rc_projdb, val);
+			val = tmp_val;
+			modified = 1;
+
+		} else
+			val = val->rcv_next;
+	}
+
+	/*
+	 * Insert new values from the project database.
+	 */
+	while (new_values != NULL) {
+		next = new_values->rcv_next;
+
+		/*
+		 * Insert this new value into the rc_projdb, and duplicate this
+		 * entry to the active list.
+		 */
+		if (rctl_val_list_insert(&rctl->rc_projdb, new_values) == 0) {
+
+			tmp_val = alloc_values->rcv_next;
+			bcopy(new_values, alloc_values, sizeof (rctl_val_t));
+			alloc_values->rcv_next = tmp_val;
+
+			if (rctl_val_list_insert(&rctl->rc_values,
+				alloc_values) == 0) {
+				/* inserted move alloc_values on */
+				alloc_values = tmp_val;
+				modified = 1;
+			}
+		} else {
+			/*
+			 * Unlike setrctl() we don't want to return an error on
+			 * a duplicate entry; we are concerned solely with
+			 * ensuring that all the values specified are set.
+			 */
+			kmem_cache_free(rctl_val_cache, new_values);
+		}
+		new_values = next;
+	}
+
+	/* Teardown any unused rctl_val_t */
+	while (alloc_values != NULL) {
+		tmp_val = alloc_values;
+		alloc_values = alloc_values->rcv_next;
+		kmem_cache_free(rctl_val_cache, tmp_val);
+	}
+
+	/* Reset the cursor if rctl values have been modified */
+	if (modified) {
+		rctl->rc_cursor = rctl->rc_values;
+		rctl_val_list_reset(rctl->rc_cursor);
+		RCTLOP_SET(rctl, p, e, rctl_model_value(rctl->rc_dict_entry, p,
+		    rctl->rc_cursor->rcv_value));
+	}
+
+	return (0);
+}
+
+int
+rctl_local_insert_all(rctl_hndl_t hndl, rctl_val_t *new_values,
+    rctl_val_t *alloc_values, struct proc *p)
+{
+	return (rctl_local_op(hndl, new_values, alloc_values,
+	    rctl_local_insert_all_cb, p));
+}
+
+/*
+ * rctl_local_replace_all_cb()
+ *
+ * Overview
+ *   Called for RCENTITY_PROJECT rctls only, via rctlsys_projset().
+ *
+ *   Clears the active rctl values (rc_values), and stored values from the
+ *   previous insertions from the project database (rc_projdb).
+ *
+ *   Inserts new values from the project database (new_values).  alloc_values
+ *   should be a linked list of pre-allocated rctl_val_t, which are used to
+ *   populate (rc_projdb).
+ *
+ * Return Values
+ *   0 is always returned.
+ */
+/*ARGSUSED*/
+static int
+rctl_local_replace_all_cb(rctl_hndl_t hndl, struct proc *p, rctl_entity_p_t *e,
+    rctl_t *rctl, rctl_val_t *new_values, rctl_val_t *alloc_values)
+{
+	rctl_val_t *val;
+	rctl_val_t *next;
+	rctl_val_t *tmp_val;
+
+	/* Delete all the privilege vaules */
+	val = rctl->rc_values;
+
+	while (val != NULL) {
+		if (val->rcv_privilege == RCPRIV_PRIVILEGED) {
+			if (val->rcv_prev != NULL)
+				val->rcv_prev->rcv_next = val->rcv_next;
+			else
+				rctl->rc_values = val->rcv_next;
+
+			if (val->rcv_next != NULL)
+				val->rcv_next->rcv_prev = val->rcv_prev;
+
+			tmp_val = val;
+			val = val->rcv_next;
+			kmem_cache_free(rctl_val_cache, tmp_val);
+		} else {
+			val = val->rcv_next;
+		}
+	}
+
+	/* Delete the contents of rc_projdb */
+	val = rctl->rc_projdb;
+	while (val != NULL) {
+
+		tmp_val = val;
+		val = val->rcv_next;
+		kmem_cache_free(rctl_val_cache, tmp_val);
+	}
+	rctl->rc_projdb = NULL;
+
+	/*
+	 * Insert new values from the project database.
+	 */
+	while (new_values != NULL) {
+		next = new_values->rcv_next;
+
+		if (rctl_val_list_insert(&rctl->rc_projdb, new_values) == 0) {
+			tmp_val = alloc_values->rcv_next;
+			bcopy(new_values, alloc_values, sizeof (rctl_val_t));
+			alloc_values->rcv_next = tmp_val;
+
+			if (rctl_val_list_insert(&rctl->rc_values,
+				alloc_values) == 0) {
+				/* inserted, so move alloc_values on */
+				alloc_values = tmp_val;
+			}
+		} else {
+			/*
+			 * Unlike setrctl() we don't want to return an error on
+			 * a duplicate entry; we are concerned solely with
+			 * ensuring that all the values specified are set.
+			 */
+			kmem_cache_free(rctl_val_cache, new_values);
+		}
+
+		new_values = next;
+	}
+
+	/* Teardown any unused rctl_val_t */
+	while (alloc_values != NULL) {
+		tmp_val = alloc_values;
+		alloc_values = alloc_values->rcv_next;
+		kmem_cache_free(rctl_val_cache, tmp_val);
+	}
+
+	/* Always reset the cursor */
+	rctl->rc_cursor = rctl->rc_values;
+	rctl_val_list_reset(rctl->rc_cursor);
+	RCTLOP_SET(rctl, p, e, rctl_model_value(rctl->rc_dict_entry, p,
+	    rctl->rc_cursor->rcv_value));
+
+	return (0);
+}
+
+int
+rctl_local_replace_all(rctl_hndl_t hndl, rctl_val_t *new_values,
+    rctl_val_t *alloc_values, struct proc *p)
+{
+	return (rctl_local_op(hndl, new_values, alloc_values,
+	    rctl_local_replace_all_cb, p));
+}
+
 static int
 rctl_local_replace_cb(rctl_hndl_t hndl, struct proc *p, rctl_entity_p_t *e,
     rctl_t *rctl, rctl_val_t *oval, rctl_val_t *nval)
--- a/usr/src/uts/common/sys/rctl.h	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/uts/common/sys/rctl.h	Tue Feb 20 10:39:20 2007 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -44,9 +44,10 @@
 #define	RCTL_LOCAL_DENY			0x00000002
 
 #define	RCTL_LOCAL_MAXIMAL		0x80000000
+#define	RCTL_LOCAL_PROJDB		0x40000000
 
 #define	RCTL_LOCAL_ACTION_MASK		0xffff0000
-#define	RCTL_LOCAL_MASK			0x80000003
+#define	RCTL_LOCAL_MASK			0xc0000003
 
 /*
  * Available global actions and flags.
@@ -216,6 +217,7 @@
 	rctl_val_t	*rc_cursor;		/* currently enforced value  */
 	struct rctl_dict_entry *rc_dict_entry;	/* global control properties */
 	rctl_hndl_t	rc_id;			/* control handle (hash key) */
+	rctl_val_t	*rc_projdb;		/* project database rctls    */
 } rctl_t;
 
 /*
@@ -309,6 +311,10 @@
 
 int rctl_local_delete(rctl_hndl_t, rctl_val_t *, struct proc *p);
 int rctl_local_insert(rctl_hndl_t, rctl_val_t *, struct proc *p);
+int rctl_local_insert_all(rctl_hndl_t, rctl_val_t *, rctl_val_t *,
+    struct proc *p);
+int rctl_local_replace_all(rctl_hndl_t, rctl_val_t *, rctl_val_t *,
+    struct proc *p);
 int rctl_local_get(rctl_hndl_t, rctl_val_t *, rctl_val_t *, struct proc *p);
 int rctl_local_replace(rctl_hndl_t, rctl_val_t *, rctl_val_t *,
     struct proc *p);
--- a/usr/src/uts/common/sys/rctl_impl.h	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/uts/common/sys/rctl_impl.h	Tue Feb 20 10:39:20 2007 -0800
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -44,6 +43,7 @@
 
 extern int rctlctl(const char *, rctlblk_t *, int);
 extern size_t rctllist(char *, size_t);
+extern int setprojrctl(const char *, rctlblk_t *, size_t, int);
 
 #endif /* _KERNEL */
 
@@ -62,6 +62,10 @@
 extern uint_t rlim_fd_cur;
 extern uint_t rlim_fd_max;
 
+/* Given an array of rctlblk_t calculate the address of the n'th element */
+#define	RCTLBLK_INC(blk, n)	(rctlblk_t *)(((char *)blk) \
+	+ (n * rctlblk_size()))
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/uts/common/sys/task.h	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/uts/common/sys/task.h	Tue Feb 20 10:39:20 2007 -0800
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -39,6 +38,10 @@
 
 #define	TASK_NORMAL	0x0	/* task may create tasks via settaskid() */
 #define	TASK_FINAL	0x1	/* task finalized, settaskid() will fail */
+#define	TASK_MASK	0x1	/* task flags mask */
+
+#define	TASK_PROJ_PURGE	0x100000	/* purge project.* rctl entities */
+#define	TASK_PROJ_MASK	0x100000
 
 #ifdef _KERNEL
 
--- a/usr/src/uts/common/syscall/rctlsys.c	Tue Feb 20 09:46:06 2007 -0800
+++ b/usr/src/uts/common/syscall/rctlsys.c	Tue Feb 20 10:39:20 2007 -0800
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -843,6 +842,155 @@
 	return (0);
 }
 
+/*
+ * The arbitrary maximum number of rctl_opaque_t that we can pass to
+ * rctl_projset().
+ */
+#define	RCTL_PROJSET_MAXSIZE	1024
+
+static long
+rctlsys_projset(char *name, rctl_opaque_t *rblk, size_t size, int flags)
+{
+	rctl_dict_entry_t *krde;
+	rctl_opaque_t *krblk;
+	char *kname;
+	size_t klen;
+	rctl_hndl_t hndl;
+	rctl_val_t *new_values = NULL;
+	rctl_val_t *alloc_values = NULL;
+	rctl_val_t *new_val;
+	rctl_val_t *alloc_val;
+	int error = 0;
+	int count;
+
+	kname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+	if (name == NULL || copyinstr(name, kname, MAXPATHLEN, &klen) != 0) {
+		kmem_free(kname, MAXPATHLEN);
+		return (set_errno(EFAULT));
+	}
+
+	if (secpolicy_rctlsys(CRED(), B_TRUE) != 0) {
+		kmem_free(kname, MAXPATHLEN);
+		return (set_errno(EPERM));
+	}
+
+	if (size > RCTL_PROJSET_MAXSIZE) {
+		kmem_free(kname, MAXPATHLEN);
+		return (set_errno(EINVAL));
+	}
+
+	if ((hndl = rctl_hndl_lookup(kname)) == -1) {
+		kmem_free(kname, MAXPATHLEN);
+		return (set_errno(EINVAL));
+	}
+
+	krde = rctl_dict_lookup_hndl(hndl);
+
+	/* If not a project entity then exit */
+	if ((krde->rcd_entity != RCENTITY_PROJECT) || (size <= 0)) {
+		kmem_free(kname, MAXPATHLEN);
+		return (set_errno(EINVAL));
+	}
+
+	/* Allocate an array large enough for all resource control blocks */
+	krblk = kmem_zalloc(sizeof (rctl_opaque_t) * size, KM_SLEEP);
+
+	if (copyin(rblk, krblk, sizeof (rctl_opaque_t) * size) == 0) {
+
+		for (count = 0; (count < size) && (error == 0); count++) {
+			new_val = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
+			alloc_val = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
+
+			rctlsys_rblk_xfrm(&krblk[count], NULL, new_val,
+			    RBX_FROM_BLK | RBX_VAL);
+
+			/*
+			 * Project entity resource control values should always
+			 * be privileged
+			 */
+			if (new_val->rcv_privilege != RCPRIV_PRIVILEGED) {
+				kmem_cache_free(rctl_val_cache, new_val);
+				kmem_cache_free(rctl_val_cache, alloc_val);
+
+				error = EPERM;
+			} else if (rctl_invalid_value(krde, new_val) == 0) {
+
+				/*
+				 * This is a project entity; we do not set
+				 * rcv_action_recipient or rcv_action_recip_pid
+				 */
+				new_val->rcv_action_recipient = NULL;
+				new_val->rcv_action_recip_pid = -1;
+				new_val->rcv_flagaction |= RCTL_LOCAL_PROJDB;
+				new_val->rcv_firing_time = 0;
+
+				new_val->rcv_prev = NULL;
+				new_val->rcv_next = new_values;
+				new_values = new_val;
+
+				/*
+				 * alloc_val is left largely uninitialized, it
+				 * is a pre-allocated rctl_val_t which is used
+				 * later in rctl_local_replace_all() /
+				 * rctl_local_insert_all().
+				 */
+				alloc_val->rcv_prev = NULL;
+				alloc_val->rcv_next = alloc_values;
+				alloc_values = alloc_val;
+			} else {
+				kmem_cache_free(rctl_val_cache, new_val);
+				kmem_cache_free(rctl_val_cache, alloc_val);
+
+				error = EINVAL;
+			}
+		}
+
+		kmem_free(krblk, sizeof (rctl_opaque_t) * size);
+	} else {
+		error = EFAULT;
+	}
+
+	kmem_free(kname, MAXPATHLEN);
+
+	if (error) {
+		/*
+		 * We will have the same number of items in the alloc_values
+		 * linked list, as we have in new_values.  However, we remain
+		 * cautious, and teardown the linked lists individually.
+		 */
+		while (new_values != NULL) {
+			new_val = new_values;
+			new_values = new_values->rcv_next;
+			kmem_cache_free(rctl_val_cache, new_val);
+		}
+
+		while (alloc_values != NULL) {
+			alloc_val = alloc_values;
+			alloc_values = alloc_values->rcv_next;
+			kmem_cache_free(rctl_val_cache, alloc_val);
+		}
+
+		return (set_errno(error));
+	}
+
+	/*
+	 * We take the p_lock here to maintain consistency with other functions
+	 * - rctlsys_get() and rctlsys_set()
+	 */
+	mutex_enter(&curproc->p_lock);
+	if (flags & TASK_PROJ_PURGE)  {
+		(void) rctl_local_replace_all(hndl, new_values, alloc_values,
+		    curproc);
+	} else {
+		(void) rctl_local_insert_all(hndl, new_values, alloc_values,
+		    curproc);
+	}
+	mutex_exit(&curproc->p_lock);
+
+	return (0);
+}
+
 long
 rctlsys(int code, char *name, void *obuf, void *nbuf, size_t obufsz, int flags)
 {
@@ -864,6 +1012,11 @@
 		 * Private code for rctladm(1M):  "rctlctl".
 		 */
 		return (rctlsys_ctl(name, obuf, flags));
+	case 4:
+		/*
+		 * Private code for setproject(3PROJECT).
+		 */
+		return (rctlsys_projset(name, nbuf, obufsz, flags));
 
 	default:
 		return (set_errno(EINVAL));