xref: /titanic_51/usr/src/cmd/newtask/newtask.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
30*7c478bd9Sstevel@tonic-gate #include <sys/task.h>
31*7c478bd9Sstevel@tonic-gate 
32*7c478bd9Sstevel@tonic-gate #include <alloca.h>
33*7c478bd9Sstevel@tonic-gate #include <libproc.h>
34*7c478bd9Sstevel@tonic-gate #include <libintl.h>
35*7c478bd9Sstevel@tonic-gate #include <libgen.h>
36*7c478bd9Sstevel@tonic-gate #include <limits.h>
37*7c478bd9Sstevel@tonic-gate #include <project.h>
38*7c478bd9Sstevel@tonic-gate #include <pwd.h>
39*7c478bd9Sstevel@tonic-gate #include <secdb.h>
40*7c478bd9Sstevel@tonic-gate #include <stdio.h>
41*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
42*7c478bd9Sstevel@tonic-gate #include <string.h>
43*7c478bd9Sstevel@tonic-gate #include <sys/varargs.h>
44*7c478bd9Sstevel@tonic-gate #include <unistd.h>
45*7c478bd9Sstevel@tonic-gate #include <errno.h>
46*7c478bd9Sstevel@tonic-gate #include <signal.h>
47*7c478bd9Sstevel@tonic-gate #include <priv_utils.h>
48*7c478bd9Sstevel@tonic-gate 
49*7c478bd9Sstevel@tonic-gate #include "utils.h"
50*7c478bd9Sstevel@tonic-gate 
51*7c478bd9Sstevel@tonic-gate #define	OPTIONS_STRING	"Fc:lp:v"
52*7c478bd9Sstevel@tonic-gate #define	NENV		8
53*7c478bd9Sstevel@tonic-gate #define	ENVSIZE		255
54*7c478bd9Sstevel@tonic-gate #define	PATH		"PATH=/usr/bin"
55*7c478bd9Sstevel@tonic-gate #define	SUPATH		"PATH=/usr/sbin:/usr/bin"
56*7c478bd9Sstevel@tonic-gate #define	SHELL		"/usr/bin/sh"
57*7c478bd9Sstevel@tonic-gate #define	SHELL2		"/sbin/sh"
58*7c478bd9Sstevel@tonic-gate #define	TIMEZONEFILE	"/etc/default/init"
59*7c478bd9Sstevel@tonic-gate #define	LOGINFILE	"/etc/default/login"
60*7c478bd9Sstevel@tonic-gate #define	GLOBAL_ERR_SZ	1024
61*7c478bd9Sstevel@tonic-gate #define	GRAB_RETRY_MAX	100
62*7c478bd9Sstevel@tonic-gate 
63*7c478bd9Sstevel@tonic-gate static const char *pname;
64*7c478bd9Sstevel@tonic-gate extern char **environ;
65*7c478bd9Sstevel@tonic-gate static char *supath = SUPATH;
66*7c478bd9Sstevel@tonic-gate static char *path = PATH;
67*7c478bd9Sstevel@tonic-gate static char global_error[GLOBAL_ERR_SZ];
68*7c478bd9Sstevel@tonic-gate static int verbose = 0;
69*7c478bd9Sstevel@tonic-gate 
70*7c478bd9Sstevel@tonic-gate static priv_set_t *nset;
71*7c478bd9Sstevel@tonic-gate 
72*7c478bd9Sstevel@tonic-gate /* Private definitions for libproject */
73*7c478bd9Sstevel@tonic-gate extern projid_t setproject_proc(const char *, const char *, int, pid_t,
74*7c478bd9Sstevel@tonic-gate     struct ps_prochandle *, struct project *);
75*7c478bd9Sstevel@tonic-gate extern priv_set_t *setproject_initpriv(void);
76*7c478bd9Sstevel@tonic-gate 
77*7c478bd9Sstevel@tonic-gate static void usage(void);
78*7c478bd9Sstevel@tonic-gate 
79*7c478bd9Sstevel@tonic-gate static void preserve_error(const char *format, ...);
80*7c478bd9Sstevel@tonic-gate 
81*7c478bd9Sstevel@tonic-gate static int update_running_proc(int, char *, char *);
82*7c478bd9Sstevel@tonic-gate static int set_ids(struct ps_prochandle *, struct project *,
83*7c478bd9Sstevel@tonic-gate     struct passwd *);
84*7c478bd9Sstevel@tonic-gate static struct passwd *match_user(uid_t, char *, int);
85*7c478bd9Sstevel@tonic-gate static void setproject_err(char *, char *, int, struct project *);
86*7c478bd9Sstevel@tonic-gate 
87*7c478bd9Sstevel@tonic-gate static void
88*7c478bd9Sstevel@tonic-gate usage(void)
89*7c478bd9Sstevel@tonic-gate {
90*7c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("usage: \n\t%s [-v] [-p project] "
91*7c478bd9Sstevel@tonic-gate 	    "[-c pid | [-Fl] [command [args ...]]]\n"), pname);
92*7c478bd9Sstevel@tonic-gate 	exit(2);
93*7c478bd9Sstevel@tonic-gate }
94*7c478bd9Sstevel@tonic-gate 
95*7c478bd9Sstevel@tonic-gate int
96*7c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
97*7c478bd9Sstevel@tonic-gate {
98*7c478bd9Sstevel@tonic-gate 	int c;
99*7c478bd9Sstevel@tonic-gate 	struct passwd *pw;
100*7c478bd9Sstevel@tonic-gate 	char *projname = NULL;
101*7c478bd9Sstevel@tonic-gate 	uid_t uid;
102*7c478bd9Sstevel@tonic-gate 	int login_flag = 0;
103*7c478bd9Sstevel@tonic-gate 	int finalize_flag = TASK_NORMAL;
104*7c478bd9Sstevel@tonic-gate 	int newproj_flag = 0;
105*7c478bd9Sstevel@tonic-gate 	taskid_t taskid;
106*7c478bd9Sstevel@tonic-gate 	char *shell;
107*7c478bd9Sstevel@tonic-gate 	char *env[NENV];
108*7c478bd9Sstevel@tonic-gate 	char **targs;
109*7c478bd9Sstevel@tonic-gate 	char *filename, *procname = NULL;
110*7c478bd9Sstevel@tonic-gate 	int error;
111*7c478bd9Sstevel@tonic-gate 
112*7c478bd9Sstevel@tonic-gate 	nset = setproject_initpriv();
113*7c478bd9Sstevel@tonic-gate 	if (nset == NULL)
114*7c478bd9Sstevel@tonic-gate 		die(gettext("privilege initialization failed\n"));
115*7c478bd9Sstevel@tonic-gate 
116*7c478bd9Sstevel@tonic-gate 	pname = getpname(argv[0]);
117*7c478bd9Sstevel@tonic-gate 
118*7c478bd9Sstevel@tonic-gate 	while ((c = getopt(argc, argv, OPTIONS_STRING)) != EOF) {
119*7c478bd9Sstevel@tonic-gate 		switch (c) {
120*7c478bd9Sstevel@tonic-gate 		case 'v':
121*7c478bd9Sstevel@tonic-gate 			verbose = 1;
122*7c478bd9Sstevel@tonic-gate 			break;
123*7c478bd9Sstevel@tonic-gate 		case 'p':
124*7c478bd9Sstevel@tonic-gate 			newproj_flag = 1;
125*7c478bd9Sstevel@tonic-gate 			projname = optarg;
126*7c478bd9Sstevel@tonic-gate 			break;
127*7c478bd9Sstevel@tonic-gate 		case 'F':
128*7c478bd9Sstevel@tonic-gate 			finalize_flag = TASK_FINAL;
129*7c478bd9Sstevel@tonic-gate 			break;
130*7c478bd9Sstevel@tonic-gate 		case 'l':
131*7c478bd9Sstevel@tonic-gate 			login_flag++;
132*7c478bd9Sstevel@tonic-gate 			break;
133*7c478bd9Sstevel@tonic-gate 		case 'c':
134*7c478bd9Sstevel@tonic-gate 			procname = optarg;
135*7c478bd9Sstevel@tonic-gate 			break;
136*7c478bd9Sstevel@tonic-gate 		case '?':
137*7c478bd9Sstevel@tonic-gate 		default:
138*7c478bd9Sstevel@tonic-gate 			usage();
139*7c478bd9Sstevel@tonic-gate 			/*NOTREACHED*/
140*7c478bd9Sstevel@tonic-gate 		}
141*7c478bd9Sstevel@tonic-gate 	}
142*7c478bd9Sstevel@tonic-gate 
143*7c478bd9Sstevel@tonic-gate 	/* -c option is invalid with -F, -l, or a specified command */
144*7c478bd9Sstevel@tonic-gate 	if ((procname != NULL) &&
145*7c478bd9Sstevel@tonic-gate 	    (finalize_flag == TASK_FINAL || login_flag || optind < argc))
146*7c478bd9Sstevel@tonic-gate 		usage();
147*7c478bd9Sstevel@tonic-gate 
148*7c478bd9Sstevel@tonic-gate 	if (procname != NULL) {
149*7c478bd9Sstevel@tonic-gate 		/* Change project/task of an existing process */
150*7c478bd9Sstevel@tonic-gate 		return (update_running_proc(newproj_flag, procname, projname));
151*7c478bd9Sstevel@tonic-gate 	}
152*7c478bd9Sstevel@tonic-gate 
153*7c478bd9Sstevel@tonic-gate 	/*
154*7c478bd9Sstevel@tonic-gate 	 * Get user data, so that we can confirm project membership as
155*7c478bd9Sstevel@tonic-gate 	 * well as construct an appropriate login environment.
156*7c478bd9Sstevel@tonic-gate 	 */
157*7c478bd9Sstevel@tonic-gate 	uid = getuid();
158*7c478bd9Sstevel@tonic-gate 	if ((pw = match_user(uid, projname, 1)) == NULL) {
159*7c478bd9Sstevel@tonic-gate 		die("%s\n", global_error);
160*7c478bd9Sstevel@tonic-gate 	}
161*7c478bd9Sstevel@tonic-gate 
162*7c478bd9Sstevel@tonic-gate 	/*
163*7c478bd9Sstevel@tonic-gate 	 * If no projname was specified, we're just creating a new task
164*7c478bd9Sstevel@tonic-gate 	 * under the current project, so we can just set the new taskid.
165*7c478bd9Sstevel@tonic-gate 	 * If our project is changing, we need to update any attendant
166*7c478bd9Sstevel@tonic-gate 	 * pool/rctl bindings, so let setproject() do the dirty work.
167*7c478bd9Sstevel@tonic-gate 	 */
168*7c478bd9Sstevel@tonic-gate 	(void) __priv_bracket(PRIV_ON);
169*7c478bd9Sstevel@tonic-gate 	if (projname == NULL) {
170*7c478bd9Sstevel@tonic-gate 		if (settaskid(getprojid(), finalize_flag) == -1)
171*7c478bd9Sstevel@tonic-gate 			if (errno == EAGAIN)
172*7c478bd9Sstevel@tonic-gate 				die(gettext("resource control limit has been "
173*7c478bd9Sstevel@tonic-gate 				    "reached"));
174*7c478bd9Sstevel@tonic-gate 			else
175*7c478bd9Sstevel@tonic-gate 				die(gettext("settaskid failed"));
176*7c478bd9Sstevel@tonic-gate 	} else {
177*7c478bd9Sstevel@tonic-gate 		if ((error = setproject(projname,
178*7c478bd9Sstevel@tonic-gate 		    pw->pw_name, finalize_flag)) != 0) {
179*7c478bd9Sstevel@tonic-gate 			setproject_err(pw->pw_name, projname, error, NULL);
180*7c478bd9Sstevel@tonic-gate 			if (error < 0)
181*7c478bd9Sstevel@tonic-gate 				die("%s\n", global_error);
182*7c478bd9Sstevel@tonic-gate 			else
183*7c478bd9Sstevel@tonic-gate 				warn("%s\n", global_error);
184*7c478bd9Sstevel@tonic-gate 		}
185*7c478bd9Sstevel@tonic-gate 	}
186*7c478bd9Sstevel@tonic-gate 	__priv_relinquish();
187*7c478bd9Sstevel@tonic-gate 
188*7c478bd9Sstevel@tonic-gate 	taskid = gettaskid();
189*7c478bd9Sstevel@tonic-gate 
190*7c478bd9Sstevel@tonic-gate 	if (verbose)
191*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%d\n", (int)taskid);
192*7c478bd9Sstevel@tonic-gate 
193*7c478bd9Sstevel@tonic-gate 	/*
194*7c478bd9Sstevel@tonic-gate 	 * Validate user's shell from passwd database.
195*7c478bd9Sstevel@tonic-gate 	 */
196*7c478bd9Sstevel@tonic-gate 	if (strcmp(pw->pw_shell, "") == 0) {
197*7c478bd9Sstevel@tonic-gate 		if (access(SHELL, X_OK) == 0)
198*7c478bd9Sstevel@tonic-gate 			pw->pw_shell = SHELL;
199*7c478bd9Sstevel@tonic-gate 		else
200*7c478bd9Sstevel@tonic-gate 			pw->pw_shell = SHELL2;
201*7c478bd9Sstevel@tonic-gate 	}
202*7c478bd9Sstevel@tonic-gate 
203*7c478bd9Sstevel@tonic-gate 	if (login_flag) {
204*7c478bd9Sstevel@tonic-gate 		/*
205*7c478bd9Sstevel@tonic-gate 		 * Since we've been invoked as a "simulated login", set up the
206*7c478bd9Sstevel@tonic-gate 		 * environment.
207*7c478bd9Sstevel@tonic-gate 		 */
208*7c478bd9Sstevel@tonic-gate 		char *cur_tz = getenv("TZ");
209*7c478bd9Sstevel@tonic-gate 		char *cur_term = getenv("TERM");
210*7c478bd9Sstevel@tonic-gate 
211*7c478bd9Sstevel@tonic-gate 		char **envnext;
212*7c478bd9Sstevel@tonic-gate 
213*7c478bd9Sstevel@tonic-gate 		size_t len_home = strlen(pw->pw_dir) + strlen("HOME=") + 1;
214*7c478bd9Sstevel@tonic-gate 		size_t len_logname = strlen(pw->pw_name) + strlen("LOGNAME=") +
215*7c478bd9Sstevel@tonic-gate 		    1;
216*7c478bd9Sstevel@tonic-gate 		size_t len_shell = strlen(pw->pw_shell) + strlen("SHELL=") + 1;
217*7c478bd9Sstevel@tonic-gate 		size_t len_mail = strlen(pw->pw_name) +
218*7c478bd9Sstevel@tonic-gate 		    strlen("MAIL=/var/mail/") + 1;
219*7c478bd9Sstevel@tonic-gate 		size_t len_tz;
220*7c478bd9Sstevel@tonic-gate 		size_t len_term;
221*7c478bd9Sstevel@tonic-gate 
222*7c478bd9Sstevel@tonic-gate 		char *env_home = safe_malloc(len_home);
223*7c478bd9Sstevel@tonic-gate 		char *env_logname = safe_malloc(len_logname);
224*7c478bd9Sstevel@tonic-gate 		char *env_shell = safe_malloc(len_shell);
225*7c478bd9Sstevel@tonic-gate 		char *env_mail = safe_malloc(len_mail);
226*7c478bd9Sstevel@tonic-gate 		char *env_tz;
227*7c478bd9Sstevel@tonic-gate 		char *env_term;
228*7c478bd9Sstevel@tonic-gate 
229*7c478bd9Sstevel@tonic-gate 		(void) snprintf(env_home, len_home, "HOME=%s", pw->pw_dir);
230*7c478bd9Sstevel@tonic-gate 		(void) snprintf(env_logname, len_logname, "LOGNAME=%s",
231*7c478bd9Sstevel@tonic-gate 		    pw->pw_name);
232*7c478bd9Sstevel@tonic-gate 		(void) snprintf(env_shell, len_shell, "SHELL=%s", pw->pw_shell);
233*7c478bd9Sstevel@tonic-gate 		(void) snprintf(env_mail, len_mail, "MAIL=/var/mail/%s",
234*7c478bd9Sstevel@tonic-gate 		    pw->pw_name);
235*7c478bd9Sstevel@tonic-gate 
236*7c478bd9Sstevel@tonic-gate 		env[0] = env_home;
237*7c478bd9Sstevel@tonic-gate 		env[1] = env_logname;
238*7c478bd9Sstevel@tonic-gate 		env[2] = (pw->pw_uid == 0 ? supath : path);
239*7c478bd9Sstevel@tonic-gate 		env[3] = env_shell;
240*7c478bd9Sstevel@tonic-gate 		env[4] = env_mail;
241*7c478bd9Sstevel@tonic-gate 		env[5] = NULL;
242*7c478bd9Sstevel@tonic-gate 		env[6] = NULL;
243*7c478bd9Sstevel@tonic-gate 		env[7] = NULL;
244*7c478bd9Sstevel@tonic-gate 
245*7c478bd9Sstevel@tonic-gate 		envnext = (char **)&env[5];
246*7c478bd9Sstevel@tonic-gate 
247*7c478bd9Sstevel@tonic-gate 		/*
248*7c478bd9Sstevel@tonic-gate 		 * It's possible that TERM wasn't defined in the outer
249*7c478bd9Sstevel@tonic-gate 		 * environment.
250*7c478bd9Sstevel@tonic-gate 		 */
251*7c478bd9Sstevel@tonic-gate 		if (cur_term != NULL) {
252*7c478bd9Sstevel@tonic-gate 			len_term = strlen(cur_term) + strlen("TERM=") + 1;
253*7c478bd9Sstevel@tonic-gate 			env_term = safe_malloc(len_term);
254*7c478bd9Sstevel@tonic-gate 
255*7c478bd9Sstevel@tonic-gate 			(void) snprintf(env_term, len_term, "TERM=%s",
256*7c478bd9Sstevel@tonic-gate 			    cur_term);
257*7c478bd9Sstevel@tonic-gate 			*envnext = env_term;
258*7c478bd9Sstevel@tonic-gate 			envnext++;
259*7c478bd9Sstevel@tonic-gate 		}
260*7c478bd9Sstevel@tonic-gate 
261*7c478bd9Sstevel@tonic-gate 		/*
262*7c478bd9Sstevel@tonic-gate 		 * It is also possible that TZ wasn't defined in the outer
263*7c478bd9Sstevel@tonic-gate 		 * environment.  In that case, we must attempt to open the file
264*7c478bd9Sstevel@tonic-gate 		 * defining the default timezone and select the appropriate
265*7c478bd9Sstevel@tonic-gate 		 * entry. If there is no default timezone there, try
266*7c478bd9Sstevel@tonic-gate 		 * TIMEZONE in /etc/default/login, duplicating the algorithm
267*7c478bd9Sstevel@tonic-gate 		 * that login uses.
268*7c478bd9Sstevel@tonic-gate 		 */
269*7c478bd9Sstevel@tonic-gate 		if (cur_tz != NULL) {
270*7c478bd9Sstevel@tonic-gate 			len_tz = strlen(cur_tz) + strlen("TZ=") + 1;
271*7c478bd9Sstevel@tonic-gate 			env_tz = safe_malloc(len_tz);
272*7c478bd9Sstevel@tonic-gate 
273*7c478bd9Sstevel@tonic-gate 			(void) snprintf(env_tz, len_tz, "TZ=%s", cur_tz);
274*7c478bd9Sstevel@tonic-gate 			*envnext = env_tz;
275*7c478bd9Sstevel@tonic-gate 		} else {
276*7c478bd9Sstevel@tonic-gate 			if ((env_tz = getdefault(TIMEZONEFILE, "TZ=",
277*7c478bd9Sstevel@tonic-gate 			    "TZ=")) != NULL)
278*7c478bd9Sstevel@tonic-gate 				*envnext = env_tz;
279*7c478bd9Sstevel@tonic-gate 			else {
280*7c478bd9Sstevel@tonic-gate 				env_tz = getdefault(LOGINFILE, "TIMEZONE=",
281*7c478bd9Sstevel@tonic-gate 				    "TZ=");
282*7c478bd9Sstevel@tonic-gate 				*envnext = env_tz;
283*7c478bd9Sstevel@tonic-gate 			}
284*7c478bd9Sstevel@tonic-gate 		}
285*7c478bd9Sstevel@tonic-gate 
286*7c478bd9Sstevel@tonic-gate 		environ = (char **)&env[0];
287*7c478bd9Sstevel@tonic-gate 
288*7c478bd9Sstevel@tonic-gate 		/*
289*7c478bd9Sstevel@tonic-gate 		 * Prefix the shell string with a hyphen, indicating a login
290*7c478bd9Sstevel@tonic-gate 		 * shell.
291*7c478bd9Sstevel@tonic-gate 		 */
292*7c478bd9Sstevel@tonic-gate 		shell = safe_malloc(PATH_MAX);
293*7c478bd9Sstevel@tonic-gate 		(void) snprintf(shell, PATH_MAX, "-%s", basename(pw->pw_shell));
294*7c478bd9Sstevel@tonic-gate 	} else {
295*7c478bd9Sstevel@tonic-gate 		shell = basename(pw->pw_shell);
296*7c478bd9Sstevel@tonic-gate 	}
297*7c478bd9Sstevel@tonic-gate 
298*7c478bd9Sstevel@tonic-gate 	/*
299*7c478bd9Sstevel@tonic-gate 	 * If there are no arguments, we launch the user's shell; otherwise, the
300*7c478bd9Sstevel@tonic-gate 	 * remaining commands are assumed to form a valid command invocation
301*7c478bd9Sstevel@tonic-gate 	 * that we can exec.
302*7c478bd9Sstevel@tonic-gate 	 */
303*7c478bd9Sstevel@tonic-gate 	if (optind >= argc) {
304*7c478bd9Sstevel@tonic-gate 		targs = alloca(2 * sizeof (char *));
305*7c478bd9Sstevel@tonic-gate 		filename = pw->pw_shell;
306*7c478bd9Sstevel@tonic-gate 		targs[0] = shell;
307*7c478bd9Sstevel@tonic-gate 		targs[1] = NULL;
308*7c478bd9Sstevel@tonic-gate 	} else {
309*7c478bd9Sstevel@tonic-gate 		targs = &argv[optind];
310*7c478bd9Sstevel@tonic-gate 		filename = targs[0];
311*7c478bd9Sstevel@tonic-gate 	}
312*7c478bd9Sstevel@tonic-gate 
313*7c478bd9Sstevel@tonic-gate 	if (execvp(filename, targs) == -1)
314*7c478bd9Sstevel@tonic-gate 		die(gettext("exec of %s failed"), targs[0]);
315*7c478bd9Sstevel@tonic-gate 
316*7c478bd9Sstevel@tonic-gate 	/*
317*7c478bd9Sstevel@tonic-gate 	 * We should never get here.
318*7c478bd9Sstevel@tonic-gate 	 */
319*7c478bd9Sstevel@tonic-gate 	return (1);
320*7c478bd9Sstevel@tonic-gate }
321*7c478bd9Sstevel@tonic-gate 
322*7c478bd9Sstevel@tonic-gate static int
323*7c478bd9Sstevel@tonic-gate update_running_proc(int newproj_flag, char *procname, char *projname)
324*7c478bd9Sstevel@tonic-gate {
325*7c478bd9Sstevel@tonic-gate 	struct ps_prochandle *p;
326*7c478bd9Sstevel@tonic-gate 	prcred_t original_prcred, current_prcred;
327*7c478bd9Sstevel@tonic-gate 	projid_t prprojid;
328*7c478bd9Sstevel@tonic-gate 	taskid_t taskid;
329*7c478bd9Sstevel@tonic-gate 	int error = 0, gret;
330*7c478bd9Sstevel@tonic-gate 	struct project project;
331*7c478bd9Sstevel@tonic-gate 	char prbuf[PROJECT_BUFSZ];
332*7c478bd9Sstevel@tonic-gate 	struct passwd *passwd_entry;
333*7c478bd9Sstevel@tonic-gate 	int grab_retry_count = 0;
334*7c478bd9Sstevel@tonic-gate 
335*7c478bd9Sstevel@tonic-gate 	/*
336*7c478bd9Sstevel@tonic-gate 	 * Catch signals from terminal. There isn't much sense in
337*7c478bd9Sstevel@tonic-gate 	 * doing anything but ignoring them since we don't do anything
338*7c478bd9Sstevel@tonic-gate 	 * after the point we'd be capable of handling them again.
339*7c478bd9Sstevel@tonic-gate 	 */
340*7c478bd9Sstevel@tonic-gate 	(void) sigignore(SIGHUP);
341*7c478bd9Sstevel@tonic-gate 	(void) sigignore(SIGINT);
342*7c478bd9Sstevel@tonic-gate 	(void) sigignore(SIGQUIT);
343*7c478bd9Sstevel@tonic-gate 	(void) sigignore(SIGTERM);
344*7c478bd9Sstevel@tonic-gate 
345*7c478bd9Sstevel@tonic-gate 	/* flush stdout before grabbing the proc to avoid deadlock */
346*7c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);
347*7c478bd9Sstevel@tonic-gate 
348*7c478bd9Sstevel@tonic-gate 	/*
349*7c478bd9Sstevel@tonic-gate 	 * We need to grab the process, which will force it to stop execution
350*7c478bd9Sstevel@tonic-gate 	 * until the grab is released, in order to aquire some information about
351*7c478bd9Sstevel@tonic-gate 	 * it, such as its current project (which is achieved via an injected
352*7c478bd9Sstevel@tonic-gate 	 * system call and therefore needs an agent) and its credentials. We
353*7c478bd9Sstevel@tonic-gate 	 * will then need to release it again because it may be a process that
354*7c478bd9Sstevel@tonic-gate 	 * we rely on for later calls, for example nscd.
355*7c478bd9Sstevel@tonic-gate 	 */
356*7c478bd9Sstevel@tonic-gate 	if ((p = proc_arg_grab(procname, PR_ARG_PIDS, 0, &gret)) == NULL) {
357*7c478bd9Sstevel@tonic-gate 		warn(gettext("failed to grab for process %s: %s\n"),
358*7c478bd9Sstevel@tonic-gate 		    procname, Pgrab_error(gret));
359*7c478bd9Sstevel@tonic-gate 		return (1);
360*7c478bd9Sstevel@tonic-gate 	}
361*7c478bd9Sstevel@tonic-gate 	if (Pcreate_agent(p) != 0) {
362*7c478bd9Sstevel@tonic-gate 		Prelease(p, 0);
363*7c478bd9Sstevel@tonic-gate 		warn(gettext("cannot control process %s\n"), procname);
364*7c478bd9Sstevel@tonic-gate 		return (1);
365*7c478bd9Sstevel@tonic-gate 	}
366*7c478bd9Sstevel@tonic-gate 
367*7c478bd9Sstevel@tonic-gate 	/*
368*7c478bd9Sstevel@tonic-gate 	 * The victim process is now held. Do not call any functions
369*7c478bd9Sstevel@tonic-gate 	 * which generate stdout/stderr until the process has been
370*7c478bd9Sstevel@tonic-gate 	 * released.
371*7c478bd9Sstevel@tonic-gate 	 */
372*7c478bd9Sstevel@tonic-gate 
373*7c478bd9Sstevel@tonic-gate /*
374*7c478bd9Sstevel@tonic-gate  * The target process will soon be restarted (in case it is in newtask's
375*7c478bd9Sstevel@tonic-gate  * execution path) and then stopped again. We need to ensure that our cached
376*7c478bd9Sstevel@tonic-gate  * data doesn't change while the process runs so return here if the target
377*7c478bd9Sstevel@tonic-gate  * process changes its user id in between our stop operations, so that we can
378*7c478bd9Sstevel@tonic-gate  * try again.
379*7c478bd9Sstevel@tonic-gate  */
380*7c478bd9Sstevel@tonic-gate pgrab_retry:
381*7c478bd9Sstevel@tonic-gate 
382*7c478bd9Sstevel@tonic-gate 	/* Cache required information about the process. */
383*7c478bd9Sstevel@tonic-gate 	if (Pcred(p, &original_prcred, 0) != 0) {
384*7c478bd9Sstevel@tonic-gate 		preserve_error(gettext("cannot get process credentials %s\n"),
385*7c478bd9Sstevel@tonic-gate 		    procname);
386*7c478bd9Sstevel@tonic-gate 		error = 1;
387*7c478bd9Sstevel@tonic-gate 	}
388*7c478bd9Sstevel@tonic-gate 	if ((prprojid = pr_getprojid(p)) == -1) {
389*7c478bd9Sstevel@tonic-gate 		preserve_error(gettext("cannot get process project id %s\n"),
390*7c478bd9Sstevel@tonic-gate 		    procname);
391*7c478bd9Sstevel@tonic-gate 		error = 1;
392*7c478bd9Sstevel@tonic-gate 	}
393*7c478bd9Sstevel@tonic-gate 
394*7c478bd9Sstevel@tonic-gate 	/*
395*7c478bd9Sstevel@tonic-gate 	 * We now have all the required information, so release the target
396*7c478bd9Sstevel@tonic-gate 	 * process and perform our sanity checks. The process needs to be
397*7c478bd9Sstevel@tonic-gate 	 * running at this point because it may be in the execution path of the
398*7c478bd9Sstevel@tonic-gate 	 * calls made below.
399*7c478bd9Sstevel@tonic-gate 	 */
400*7c478bd9Sstevel@tonic-gate 	Pdestroy_agent(p);
401*7c478bd9Sstevel@tonic-gate 	Prelease(p, 0);
402*7c478bd9Sstevel@tonic-gate 
403*7c478bd9Sstevel@tonic-gate 	/* if our data acquisition failed, then we can't continue. */
404*7c478bd9Sstevel@tonic-gate 	if (error) {
405*7c478bd9Sstevel@tonic-gate 		warn("%s\n", global_error);
406*7c478bd9Sstevel@tonic-gate 		return (1);
407*7c478bd9Sstevel@tonic-gate 	}
408*7c478bd9Sstevel@tonic-gate 
409*7c478bd9Sstevel@tonic-gate 	if (newproj_flag == 0) {
410*7c478bd9Sstevel@tonic-gate 		/*
411*7c478bd9Sstevel@tonic-gate 		 * Just changing the task, so set projname to the current
412*7c478bd9Sstevel@tonic-gate 		 * project of the running process.
413*7c478bd9Sstevel@tonic-gate 		 */
414*7c478bd9Sstevel@tonic-gate 		if (getprojbyid(prprojid, &project, &prbuf,
415*7c478bd9Sstevel@tonic-gate 		    PROJECT_BUFSZ) == NULL) {
416*7c478bd9Sstevel@tonic-gate 			warn(gettext("unable to get project name "
417*7c478bd9Sstevel@tonic-gate 			    "for projid %d"), prprojid);
418*7c478bd9Sstevel@tonic-gate 			return (1);
419*7c478bd9Sstevel@tonic-gate 		}
420*7c478bd9Sstevel@tonic-gate 		projname = project.pj_name;
421*7c478bd9Sstevel@tonic-gate 	} else {
422*7c478bd9Sstevel@tonic-gate 		/*
423*7c478bd9Sstevel@tonic-gate 		 * cache info for the project which user passed in via the
424*7c478bd9Sstevel@tonic-gate 		 * command line
425*7c478bd9Sstevel@tonic-gate 		 */
426*7c478bd9Sstevel@tonic-gate 		if (getprojbyname(projname, &project, &prbuf,
427*7c478bd9Sstevel@tonic-gate 		    PROJECT_BUFSZ) == NULL) {
428*7c478bd9Sstevel@tonic-gate 			warn(gettext("unknown project \"%s\"\n"), projname);
429*7c478bd9Sstevel@tonic-gate 			return (1);
430*7c478bd9Sstevel@tonic-gate 		}
431*7c478bd9Sstevel@tonic-gate 	}
432*7c478bd9Sstevel@tonic-gate 
433*7c478bd9Sstevel@tonic-gate 	/*
434*7c478bd9Sstevel@tonic-gate 	 * Use our cached information to verify that the owner of the running
435*7c478bd9Sstevel@tonic-gate 	 * process is a member of proj
436*7c478bd9Sstevel@tonic-gate 	 */
437*7c478bd9Sstevel@tonic-gate 	if ((passwd_entry = match_user(original_prcred.pr_ruid,
438*7c478bd9Sstevel@tonic-gate 	    projname, 0)) == NULL) {
439*7c478bd9Sstevel@tonic-gate 		warn("%s\n", global_error);
440*7c478bd9Sstevel@tonic-gate 		return (1);
441*7c478bd9Sstevel@tonic-gate 	}
442*7c478bd9Sstevel@tonic-gate 
443*7c478bd9Sstevel@tonic-gate 	/*
444*7c478bd9Sstevel@tonic-gate 	 * We can now safely stop the process again in order to change the
445*7c478bd9Sstevel@tonic-gate 	 * project and taskid as required.
446*7c478bd9Sstevel@tonic-gate 	 */
447*7c478bd9Sstevel@tonic-gate 	if ((p = proc_arg_grab(procname, PR_ARG_PIDS, 0, &gret)) == NULL) {
448*7c478bd9Sstevel@tonic-gate 		warn(gettext("failed to grab for process %s: %s\n"),
449*7c478bd9Sstevel@tonic-gate 		    procname, Pgrab_error(gret));
450*7c478bd9Sstevel@tonic-gate 		return (1);
451*7c478bd9Sstevel@tonic-gate 	}
452*7c478bd9Sstevel@tonic-gate 	if (Pcreate_agent(p) != 0) {
453*7c478bd9Sstevel@tonic-gate 		Prelease(p, 0);
454*7c478bd9Sstevel@tonic-gate 		warn(gettext("cannot control process %s\n"), procname);
455*7c478bd9Sstevel@tonic-gate 		return (1);
456*7c478bd9Sstevel@tonic-gate 	}
457*7c478bd9Sstevel@tonic-gate 
458*7c478bd9Sstevel@tonic-gate 	/*
459*7c478bd9Sstevel@tonic-gate 	 * Now that the target process is stopped, check the validity of our
460*7c478bd9Sstevel@tonic-gate 	 * cached info. If we aren't superuser then match_user() will have
461*7c478bd9Sstevel@tonic-gate 	 * checked to make sure that the owner of the process is in the relevant
462*7c478bd9Sstevel@tonic-gate 	 * project. If our ruid has changed, then match_user()'s conclusion may
463*7c478bd9Sstevel@tonic-gate 	 * be invalid.
464*7c478bd9Sstevel@tonic-gate 	 */
465*7c478bd9Sstevel@tonic-gate 	if (getuid() != 0) {
466*7c478bd9Sstevel@tonic-gate 		if (Pcred(p, &current_prcred, 0) != 0) {
467*7c478bd9Sstevel@tonic-gate 			Pdestroy_agent(p);
468*7c478bd9Sstevel@tonic-gate 			Prelease(p, 0);
469*7c478bd9Sstevel@tonic-gate 			warn(gettext("can't get process credentials %s\n"),
470*7c478bd9Sstevel@tonic-gate 			    procname);
471*7c478bd9Sstevel@tonic-gate 			return (1);
472*7c478bd9Sstevel@tonic-gate 		}
473*7c478bd9Sstevel@tonic-gate 
474*7c478bd9Sstevel@tonic-gate 		if (original_prcred.pr_ruid != current_prcred.pr_ruid) {
475*7c478bd9Sstevel@tonic-gate 			if (grab_retry_count++ < GRAB_RETRY_MAX)
476*7c478bd9Sstevel@tonic-gate 				goto pgrab_retry;
477*7c478bd9Sstevel@tonic-gate 
478*7c478bd9Sstevel@tonic-gate 			warn(gettext("process consistently changed its "
479*7c478bd9Sstevel@tonic-gate 			    "user id %s\n"), procname);
480*7c478bd9Sstevel@tonic-gate 			return (1);
481*7c478bd9Sstevel@tonic-gate 		}
482*7c478bd9Sstevel@tonic-gate 	}
483*7c478bd9Sstevel@tonic-gate 
484*7c478bd9Sstevel@tonic-gate 	error = set_ids(p, &project, passwd_entry);
485*7c478bd9Sstevel@tonic-gate 
486*7c478bd9Sstevel@tonic-gate 	if (verbose)
487*7c478bd9Sstevel@tonic-gate 		taskid = pr_gettaskid(p);
488*7c478bd9Sstevel@tonic-gate 
489*7c478bd9Sstevel@tonic-gate 	Pdestroy_agent(p);
490*7c478bd9Sstevel@tonic-gate 	Prelease(p, 0);
491*7c478bd9Sstevel@tonic-gate 
492*7c478bd9Sstevel@tonic-gate 	if (error) {
493*7c478bd9Sstevel@tonic-gate 		/*
494*7c478bd9Sstevel@tonic-gate 		 * error is serious enough to stop, only if negative.
495*7c478bd9Sstevel@tonic-gate 		 * Otherwise, it simply indicates one of the resource
496*7c478bd9Sstevel@tonic-gate 		 * control assignments failed, which is worth warning
497*7c478bd9Sstevel@tonic-gate 		 * about.
498*7c478bd9Sstevel@tonic-gate 		 */
499*7c478bd9Sstevel@tonic-gate 		warn("%s\n", global_error);
500*7c478bd9Sstevel@tonic-gate 		if (error < 0)
501*7c478bd9Sstevel@tonic-gate 			return (1);
502*7c478bd9Sstevel@tonic-gate 	}
503*7c478bd9Sstevel@tonic-gate 
504*7c478bd9Sstevel@tonic-gate 	if (verbose)
505*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%d\n", (int)taskid);
506*7c478bd9Sstevel@tonic-gate 
507*7c478bd9Sstevel@tonic-gate 	return (0);
508*7c478bd9Sstevel@tonic-gate }
509*7c478bd9Sstevel@tonic-gate 
510*7c478bd9Sstevel@tonic-gate static int
511*7c478bd9Sstevel@tonic-gate set_ids(struct ps_prochandle *p, struct project *project,
512*7c478bd9Sstevel@tonic-gate     struct passwd *passwd_entry)
513*7c478bd9Sstevel@tonic-gate {
514*7c478bd9Sstevel@tonic-gate 	int be_su = 0;
515*7c478bd9Sstevel@tonic-gate 	prcred_t old_prcred;
516*7c478bd9Sstevel@tonic-gate 	int error;
517*7c478bd9Sstevel@tonic-gate 	prpriv_t *old_prpriv, *new_prpriv;
518*7c478bd9Sstevel@tonic-gate 	size_t prsz = sizeof (prpriv_t);
519*7c478bd9Sstevel@tonic-gate 	priv_set_t *eset, *pset;
520*7c478bd9Sstevel@tonic-gate 	int ind;
521*7c478bd9Sstevel@tonic-gate 
522*7c478bd9Sstevel@tonic-gate 	if (Pcred(p, &old_prcred, 0) != 0) {
523*7c478bd9Sstevel@tonic-gate 		preserve_error(gettext("can't get process credentials"));
524*7c478bd9Sstevel@tonic-gate 		return (1);
525*7c478bd9Sstevel@tonic-gate 	}
526*7c478bd9Sstevel@tonic-gate 
527*7c478bd9Sstevel@tonic-gate 	old_prpriv = proc_get_priv(Pstatus(p)->pr_pid);
528*7c478bd9Sstevel@tonic-gate 	if (old_prpriv == NULL) {
529*7c478bd9Sstevel@tonic-gate 		preserve_error(gettext("can't get process privileges"));
530*7c478bd9Sstevel@tonic-gate 		return (1);
531*7c478bd9Sstevel@tonic-gate 	}
532*7c478bd9Sstevel@tonic-gate 
533*7c478bd9Sstevel@tonic-gate 	prsz = PRIV_PRPRIV_SIZE(old_prpriv);
534*7c478bd9Sstevel@tonic-gate 
535*7c478bd9Sstevel@tonic-gate 	new_prpriv = malloc(prsz);
536*7c478bd9Sstevel@tonic-gate 	if (new_prpriv == NULL) {
537*7c478bd9Sstevel@tonic-gate 		preserve_error(gettext("can't allocate memory"));
538*7c478bd9Sstevel@tonic-gate 		free(old_prpriv);
539*7c478bd9Sstevel@tonic-gate 		return (1);
540*7c478bd9Sstevel@tonic-gate 	}
541*7c478bd9Sstevel@tonic-gate 
542*7c478bd9Sstevel@tonic-gate 	(void) memcpy(new_prpriv, old_prpriv, prsz);
543*7c478bd9Sstevel@tonic-gate 
544*7c478bd9Sstevel@tonic-gate 	/*
545*7c478bd9Sstevel@tonic-gate 	 * If the process already has the proc_taskid privilege,
546*7c478bd9Sstevel@tonic-gate 	 * we don't need to elevate its privileges; if it doesn't,
547*7c478bd9Sstevel@tonic-gate 	 * we try to do it here.
548*7c478bd9Sstevel@tonic-gate 	 * As we do not wish to leave a window in which the process runs
549*7c478bd9Sstevel@tonic-gate 	 * with elevated privileges, we make sure that the process dies
550*7c478bd9Sstevel@tonic-gate 	 * when we go away unexpectedly.
551*7c478bd9Sstevel@tonic-gate 	 */
552*7c478bd9Sstevel@tonic-gate 
553*7c478bd9Sstevel@tonic-gate 	ind = priv_getsetbyname(PRIV_EFFECTIVE);
554*7c478bd9Sstevel@tonic-gate 	eset = (priv_set_t *)&new_prpriv->pr_sets[new_prpriv->pr_setsize * ind];
555*7c478bd9Sstevel@tonic-gate 	ind = priv_getsetbyname(PRIV_PERMITTED);
556*7c478bd9Sstevel@tonic-gate 	pset = (priv_set_t *)&new_prpriv->pr_sets[new_prpriv->pr_setsize * ind];
557*7c478bd9Sstevel@tonic-gate 
558*7c478bd9Sstevel@tonic-gate 	if (!priv_issubset(nset, eset)) {
559*7c478bd9Sstevel@tonic-gate 		be_su = 1;
560*7c478bd9Sstevel@tonic-gate 		priv_union(nset, eset);
561*7c478bd9Sstevel@tonic-gate 		priv_union(nset, pset);
562*7c478bd9Sstevel@tonic-gate 		if (Psetflags(p, PR_KLC) != 0) {
563*7c478bd9Sstevel@tonic-gate 			preserve_error(gettext("cannot set process "
564*7c478bd9Sstevel@tonic-gate 			    "privileges"));
565*7c478bd9Sstevel@tonic-gate 			(void) Punsetflags(p, PR_KLC);
566*7c478bd9Sstevel@tonic-gate 			free(new_prpriv);
567*7c478bd9Sstevel@tonic-gate 			free(old_prpriv);
568*7c478bd9Sstevel@tonic-gate 			return (1);
569*7c478bd9Sstevel@tonic-gate 		}
570*7c478bd9Sstevel@tonic-gate 		(void) __priv_bracket(PRIV_ON);
571*7c478bd9Sstevel@tonic-gate 		if (Psetpriv(p, new_prpriv) != 0) {
572*7c478bd9Sstevel@tonic-gate 			(void) __priv_bracket(PRIV_OFF);
573*7c478bd9Sstevel@tonic-gate 			preserve_error(gettext("cannot set process "
574*7c478bd9Sstevel@tonic-gate 			    "privileges"));
575*7c478bd9Sstevel@tonic-gate 			(void) Punsetflags(p, PR_KLC);
576*7c478bd9Sstevel@tonic-gate 			free(new_prpriv);
577*7c478bd9Sstevel@tonic-gate 			free(old_prpriv);
578*7c478bd9Sstevel@tonic-gate 			return (1);
579*7c478bd9Sstevel@tonic-gate 		}
580*7c478bd9Sstevel@tonic-gate 		(void) __priv_bracket(PRIV_OFF);
581*7c478bd9Sstevel@tonic-gate 	}
582*7c478bd9Sstevel@tonic-gate 
583*7c478bd9Sstevel@tonic-gate 	(void) __priv_bracket(PRIV_ON);
584*7c478bd9Sstevel@tonic-gate 	if ((error = setproject_proc(project->pj_name,
585*7c478bd9Sstevel@tonic-gate 	    passwd_entry->pw_name, 0, Pstatus(p)->pr_pid, p, project)) != 0) {
586*7c478bd9Sstevel@tonic-gate 		/* global_error is set by setproject_err */
587*7c478bd9Sstevel@tonic-gate 		setproject_err(passwd_entry->pw_name, project->pj_name,
588*7c478bd9Sstevel@tonic-gate 		    error, project);
589*7c478bd9Sstevel@tonic-gate 	}
590*7c478bd9Sstevel@tonic-gate 	(void) __priv_bracket(PRIV_OFF);
591*7c478bd9Sstevel@tonic-gate 
592*7c478bd9Sstevel@tonic-gate 	/* relinquish added privileges */
593*7c478bd9Sstevel@tonic-gate 	if (be_su) {
594*7c478bd9Sstevel@tonic-gate 		(void) __priv_bracket(PRIV_ON);
595*7c478bd9Sstevel@tonic-gate 		if (Psetpriv(p, old_prpriv) != 0) {
596*7c478bd9Sstevel@tonic-gate 			/*
597*7c478bd9Sstevel@tonic-gate 			 * We shouldn't ever be in a state where we can't
598*7c478bd9Sstevel@tonic-gate 			 * set the process back to its old creds, but we
599*7c478bd9Sstevel@tonic-gate 			 * don't want to take the chance of leaving a
600*7c478bd9Sstevel@tonic-gate 			 * non-privileged process with enhanced creds. So,
601*7c478bd9Sstevel@tonic-gate 			 * release the process from libproc control, knowing
602*7c478bd9Sstevel@tonic-gate 			 * that it will be killed.
603*7c478bd9Sstevel@tonic-gate 			 */
604*7c478bd9Sstevel@tonic-gate 			(void) __priv_bracket(PRIV_OFF);
605*7c478bd9Sstevel@tonic-gate 			Pdestroy_agent(p);
606*7c478bd9Sstevel@tonic-gate 			die(gettext("cannot relinquish superuser credentials "
607*7c478bd9Sstevel@tonic-gate 			    "for pid %d. The process was killed."),
608*7c478bd9Sstevel@tonic-gate 			    Pstatus(p)->pr_pid);
609*7c478bd9Sstevel@tonic-gate 		}
610*7c478bd9Sstevel@tonic-gate 		(void) __priv_bracket(PRIV_OFF);
611*7c478bd9Sstevel@tonic-gate 		if (Punsetflags(p, PR_KLC) != 0)
612*7c478bd9Sstevel@tonic-gate 			preserve_error(gettext("error relinquishing "
613*7c478bd9Sstevel@tonic-gate 			    "credentials. Process %d will be killed."),
614*7c478bd9Sstevel@tonic-gate 			    Pstatus(p)->pr_pid);
615*7c478bd9Sstevel@tonic-gate 	}
616*7c478bd9Sstevel@tonic-gate 	free(new_prpriv);
617*7c478bd9Sstevel@tonic-gate 	free(old_prpriv);
618*7c478bd9Sstevel@tonic-gate 
619*7c478bd9Sstevel@tonic-gate 	return (error);
620*7c478bd9Sstevel@tonic-gate }
621*7c478bd9Sstevel@tonic-gate 
622*7c478bd9Sstevel@tonic-gate /*
623*7c478bd9Sstevel@tonic-gate  * preserve_error() should be called rather than warn() by any
624*7c478bd9Sstevel@tonic-gate  * function that is called while the victim process is being
625*7c478bd9Sstevel@tonic-gate  * held by Pgrab.
626*7c478bd9Sstevel@tonic-gate  *
627*7c478bd9Sstevel@tonic-gate  * It saves a single error message to be printed until after
628*7c478bd9Sstevel@tonic-gate  * the process has been released. Since multiple errors are not
629*7c478bd9Sstevel@tonic-gate  * stored, any error should be considered critical.
630*7c478bd9Sstevel@tonic-gate  */
631*7c478bd9Sstevel@tonic-gate void
632*7c478bd9Sstevel@tonic-gate preserve_error(const char *format, ...)
633*7c478bd9Sstevel@tonic-gate {
634*7c478bd9Sstevel@tonic-gate 	va_list alist;
635*7c478bd9Sstevel@tonic-gate 
636*7c478bd9Sstevel@tonic-gate 	va_start(alist, format);
637*7c478bd9Sstevel@tonic-gate 
638*7c478bd9Sstevel@tonic-gate 	/*
639*7c478bd9Sstevel@tonic-gate 	 * GLOBAL_ERR_SZ is pretty big. If the error is longer
640*7c478bd9Sstevel@tonic-gate 	 * than that, just truncate it, rather than chance missing
641*7c478bd9Sstevel@tonic-gate 	 * the error altogether.
642*7c478bd9Sstevel@tonic-gate 	 */
643*7c478bd9Sstevel@tonic-gate 	(void) vsnprintf(global_error, GLOBAL_ERR_SZ-1, format, alist);
644*7c478bd9Sstevel@tonic-gate 
645*7c478bd9Sstevel@tonic-gate 	va_end(alist);
646*7c478bd9Sstevel@tonic-gate 
647*7c478bd9Sstevel@tonic-gate }
648*7c478bd9Sstevel@tonic-gate 
649*7c478bd9Sstevel@tonic-gate /*
650*7c478bd9Sstevel@tonic-gate  * Given the input arguments, return the passwd structure that matches best.
651*7c478bd9Sstevel@tonic-gate  * Also, since we use getpwnam() and friends, subsequent calls to this
652*7c478bd9Sstevel@tonic-gate  * function will re-use the memory previously returned.
653*7c478bd9Sstevel@tonic-gate  */
654*7c478bd9Sstevel@tonic-gate static struct passwd *
655*7c478bd9Sstevel@tonic-gate match_user(uid_t uid, char *projname, int is_my_uid)
656*7c478bd9Sstevel@tonic-gate {
657*7c478bd9Sstevel@tonic-gate 	char prbuf[PROJECT_BUFSZ], username[LOGNAME_MAX+1];
658*7c478bd9Sstevel@tonic-gate 	struct project prj;
659*7c478bd9Sstevel@tonic-gate 	char *tmp_name;
660*7c478bd9Sstevel@tonic-gate 	struct passwd *pw = NULL;
661*7c478bd9Sstevel@tonic-gate 
662*7c478bd9Sstevel@tonic-gate 	/*
663*7c478bd9Sstevel@tonic-gate 	 * In order to allow users with the same UID but distinguishable
664*7c478bd9Sstevel@tonic-gate 	 * user names to be in different projects we play a guessing
665*7c478bd9Sstevel@tonic-gate 	 * game of which username is most appropriate. If we're checking
666*7c478bd9Sstevel@tonic-gate 	 * for the uid of the calling process, the login name is a
667*7c478bd9Sstevel@tonic-gate 	 * good starting point.
668*7c478bd9Sstevel@tonic-gate 	 */
669*7c478bd9Sstevel@tonic-gate 	if (is_my_uid) {
670*7c478bd9Sstevel@tonic-gate 		if ((tmp_name = getlogin()) == NULL ||
671*7c478bd9Sstevel@tonic-gate 		    (pw = getpwnam(tmp_name)) == NULL || (pw->pw_uid != uid) ||
672*7c478bd9Sstevel@tonic-gate 		    (pw->pw_name == NULL))
673*7c478bd9Sstevel@tonic-gate 			pw = NULL;
674*7c478bd9Sstevel@tonic-gate 	}
675*7c478bd9Sstevel@tonic-gate 
676*7c478bd9Sstevel@tonic-gate 	/*
677*7c478bd9Sstevel@tonic-gate 	 * If the login name doesn't work,  we try the first match for
678*7c478bd9Sstevel@tonic-gate 	 * the current uid in the password file.
679*7c478bd9Sstevel@tonic-gate 	 */
680*7c478bd9Sstevel@tonic-gate 	if (pw == NULL) {
681*7c478bd9Sstevel@tonic-gate 		if (((pw = getpwuid(uid)) == NULL) || pw->pw_name == NULL) {
682*7c478bd9Sstevel@tonic-gate 			preserve_error(gettext("cannot find username "
683*7c478bd9Sstevel@tonic-gate 			    "for uid %d"), uid);
684*7c478bd9Sstevel@tonic-gate 			return (NULL);
685*7c478bd9Sstevel@tonic-gate 		}
686*7c478bd9Sstevel@tonic-gate 	}
687*7c478bd9Sstevel@tonic-gate 
688*7c478bd9Sstevel@tonic-gate 	/*
689*7c478bd9Sstevel@tonic-gate 	 * If projname wasn't supplied, we've done our best, so just return
690*7c478bd9Sstevel@tonic-gate 	 * what we've got now. Alternatively, if newtask's invoker has
691*7c478bd9Sstevel@tonic-gate 	 * superuser privileges, return the pw structure we've got now, with
692*7c478bd9Sstevel@tonic-gate 	 * no further checking from inproj(). Superuser should be able to
693*7c478bd9Sstevel@tonic-gate 	 * join any project, and the subsequent call to setproject() will
694*7c478bd9Sstevel@tonic-gate 	 * allow this.
695*7c478bd9Sstevel@tonic-gate 	 */
696*7c478bd9Sstevel@tonic-gate 	if (projname == NULL || getuid() == (uid_t)0)
697*7c478bd9Sstevel@tonic-gate 		return (pw);
698*7c478bd9Sstevel@tonic-gate 
699*7c478bd9Sstevel@tonic-gate 	(void) strcpy(username, pw->pw_name);
700*7c478bd9Sstevel@tonic-gate 
701*7c478bd9Sstevel@tonic-gate 	if (inproj(username, projname, prbuf, PROJECT_BUFSZ) == 0) {
702*7c478bd9Sstevel@tonic-gate 		char **u;
703*7c478bd9Sstevel@tonic-gate 		tmp_name = NULL;
704*7c478bd9Sstevel@tonic-gate 
705*7c478bd9Sstevel@tonic-gate 		/*
706*7c478bd9Sstevel@tonic-gate 		 * If the previous guesses didn't work, walk through all
707*7c478bd9Sstevel@tonic-gate 		 * project members and test for UID-equivalence.
708*7c478bd9Sstevel@tonic-gate 		 */
709*7c478bd9Sstevel@tonic-gate 
710*7c478bd9Sstevel@tonic-gate 		if (getprojbyname(projname, &prj, prbuf,
711*7c478bd9Sstevel@tonic-gate 		    PROJECT_BUFSZ) == NULL) {
712*7c478bd9Sstevel@tonic-gate 			preserve_error(gettext("unknown project \"%s\""),
713*7c478bd9Sstevel@tonic-gate 			    projname);
714*7c478bd9Sstevel@tonic-gate 			return (NULL);
715*7c478bd9Sstevel@tonic-gate 		}
716*7c478bd9Sstevel@tonic-gate 
717*7c478bd9Sstevel@tonic-gate 		for (u = prj.pj_users; *u; u++) {
718*7c478bd9Sstevel@tonic-gate 			if ((pw = getpwnam(*u)) == NULL)
719*7c478bd9Sstevel@tonic-gate 				continue;
720*7c478bd9Sstevel@tonic-gate 
721*7c478bd9Sstevel@tonic-gate 			if (pw->pw_uid == uid) {
722*7c478bd9Sstevel@tonic-gate 				tmp_name = pw->pw_name;
723*7c478bd9Sstevel@tonic-gate 				break;
724*7c478bd9Sstevel@tonic-gate 			}
725*7c478bd9Sstevel@tonic-gate 		}
726*7c478bd9Sstevel@tonic-gate 
727*7c478bd9Sstevel@tonic-gate 		if (tmp_name == NULL) {
728*7c478bd9Sstevel@tonic-gate 			preserve_error(gettext("user \"%s\" is not a member of "
729*7c478bd9Sstevel@tonic-gate 			    "project \"%s\""), username, projname);
730*7c478bd9Sstevel@tonic-gate 			return (NULL);
731*7c478bd9Sstevel@tonic-gate 		}
732*7c478bd9Sstevel@tonic-gate 	}
733*7c478bd9Sstevel@tonic-gate 
734*7c478bd9Sstevel@tonic-gate 	return (pw);
735*7c478bd9Sstevel@tonic-gate }
736*7c478bd9Sstevel@tonic-gate 
737*7c478bd9Sstevel@tonic-gate void
738*7c478bd9Sstevel@tonic-gate setproject_err(char *username, char *projname, int error, struct project *proj)
739*7c478bd9Sstevel@tonic-gate {
740*7c478bd9Sstevel@tonic-gate 	kva_t *kv_array = NULL;
741*7c478bd9Sstevel@tonic-gate 	char prbuf[PROJECT_BUFSZ];
742*7c478bd9Sstevel@tonic-gate 	struct project local_proj;
743*7c478bd9Sstevel@tonic-gate 
744*7c478bd9Sstevel@tonic-gate 	switch (error) {
745*7c478bd9Sstevel@tonic-gate 	case SETPROJ_ERR_TASK:
746*7c478bd9Sstevel@tonic-gate 		if (errno == EAGAIN)
747*7c478bd9Sstevel@tonic-gate 			preserve_error(gettext("resource control limit has "
748*7c478bd9Sstevel@tonic-gate 			    "been reached"));
749*7c478bd9Sstevel@tonic-gate 		else if (errno == ESRCH)
750*7c478bd9Sstevel@tonic-gate 			preserve_error(gettext("user \"%s\" is not a member of "
751*7c478bd9Sstevel@tonic-gate 			    "project \"%s\""), username, projname);
752*7c478bd9Sstevel@tonic-gate 		else if (errno == EACCES)
753*7c478bd9Sstevel@tonic-gate 			preserve_error(gettext("the invoking task is final"));
754*7c478bd9Sstevel@tonic-gate 		else
755*7c478bd9Sstevel@tonic-gate 			preserve_error(
756*7c478bd9Sstevel@tonic-gate 			    gettext("could not join project \"%s\""),
757*7c478bd9Sstevel@tonic-gate 			    projname);
758*7c478bd9Sstevel@tonic-gate 		break;
759*7c478bd9Sstevel@tonic-gate 	case SETPROJ_ERR_POOL:
760*7c478bd9Sstevel@tonic-gate 		if (errno == EACCES)
761*7c478bd9Sstevel@tonic-gate 			preserve_error(gettext("no resource pool accepting "
762*7c478bd9Sstevel@tonic-gate 			    "default bindings exists for project \"%s\""),
763*7c478bd9Sstevel@tonic-gate 			    projname);
764*7c478bd9Sstevel@tonic-gate 		else if (errno == ESRCH)
765*7c478bd9Sstevel@tonic-gate 			preserve_error(gettext("specified resource pool does "
766*7c478bd9Sstevel@tonic-gate 			    "not exist for project \"%s\""), projname);
767*7c478bd9Sstevel@tonic-gate 		else
768*7c478bd9Sstevel@tonic-gate 			preserve_error(gettext("could not bind to default "
769*7c478bd9Sstevel@tonic-gate 			    "resource pool for project \"%s\""), projname);
770*7c478bd9Sstevel@tonic-gate 		break;
771*7c478bd9Sstevel@tonic-gate 	default:
772*7c478bd9Sstevel@tonic-gate 		if (error <= 0) {
773*7c478bd9Sstevel@tonic-gate 			preserve_error(gettext("setproject failed for "
774*7c478bd9Sstevel@tonic-gate 			    "project \"%s\""), projname);
775*7c478bd9Sstevel@tonic-gate 			return;
776*7c478bd9Sstevel@tonic-gate 		}
777*7c478bd9Sstevel@tonic-gate 		/*
778*7c478bd9Sstevel@tonic-gate 		 * If we have a stopped target process it may be in
779*7c478bd9Sstevel@tonic-gate 		 * getprojbyname()'s execution path which would make it unsafe
780*7c478bd9Sstevel@tonic-gate 		 * to access the project table, so only do that if the caller
781*7c478bd9Sstevel@tonic-gate 		 * hasn't provided a cached version of the project structure.
782*7c478bd9Sstevel@tonic-gate 		 */
783*7c478bd9Sstevel@tonic-gate 		if (proj == NULL)
784*7c478bd9Sstevel@tonic-gate 			proj = getprojbyname(projname, &local_proj, prbuf,
785*7c478bd9Sstevel@tonic-gate 			    PROJECT_BUFSZ);
786*7c478bd9Sstevel@tonic-gate 
787*7c478bd9Sstevel@tonic-gate 		if (proj == NULL || (kv_array = _str2kva(proj->pj_attr,
788*7c478bd9Sstevel@tonic-gate 		    KV_ASSIGN, KV_DELIMITER)) == NULL ||
789*7c478bd9Sstevel@tonic-gate 		    kv_array->length < error) {
790*7c478bd9Sstevel@tonic-gate 			preserve_error(gettext("warning, resource control "
791*7c478bd9Sstevel@tonic-gate 			    "assignment failed for project \"%s\" "
792*7c478bd9Sstevel@tonic-gate 			    "attribute %d"),
793*7c478bd9Sstevel@tonic-gate 			    projname, error);
794*7c478bd9Sstevel@tonic-gate 			if (kv_array)
795*7c478bd9Sstevel@tonic-gate 				_kva_free(kv_array);
796*7c478bd9Sstevel@tonic-gate 			return;
797*7c478bd9Sstevel@tonic-gate 		}
798*7c478bd9Sstevel@tonic-gate 		preserve_error(gettext("warning, %s resource control "
799*7c478bd9Sstevel@tonic-gate 		    "assignment failed for project \"%s\""),
800*7c478bd9Sstevel@tonic-gate 		    kv_array->data[error - 1].key, projname);
801*7c478bd9Sstevel@tonic-gate 		_kva_free(kv_array);
802*7c478bd9Sstevel@tonic-gate 	}
803*7c478bd9Sstevel@tonic-gate }
804