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