xref: /illumos-gate/usr/src/cmd/newtask/newtask.c (revision 43051d27)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
230a1278f2SGary Mills  * Copyright (c) 2013 Gary Mills
24*43051d27SRobert Mustacchi  * Copyright 2015, Joyent, Inc.
250a1278f2SGary Mills  *
267c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
277c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #include <sys/types.h>
317c478bd9Sstevel@tonic-gate #include <sys/task.h>
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #include <alloca.h>
347c478bd9Sstevel@tonic-gate #include <libproc.h>
357c478bd9Sstevel@tonic-gate #include <libintl.h>
367c478bd9Sstevel@tonic-gate #include <libgen.h>
377c478bd9Sstevel@tonic-gate #include <limits.h>
387c478bd9Sstevel@tonic-gate #include <project.h>
397c478bd9Sstevel@tonic-gate #include <pwd.h>
407c478bd9Sstevel@tonic-gate #include <secdb.h>
417c478bd9Sstevel@tonic-gate #include <stdio.h>
427c478bd9Sstevel@tonic-gate #include <stdlib.h>
437c478bd9Sstevel@tonic-gate #include <string.h>
447c478bd9Sstevel@tonic-gate #include <sys/varargs.h>
457c478bd9Sstevel@tonic-gate #include <unistd.h>
467c478bd9Sstevel@tonic-gate #include <errno.h>
477c478bd9Sstevel@tonic-gate #include <signal.h>
487c478bd9Sstevel@tonic-gate #include <priv_utils.h>
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate #include "utils.h"
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate #define	OPTIONS_STRING	"Fc:lp:v"
537c478bd9Sstevel@tonic-gate #define	NENV		8
547c478bd9Sstevel@tonic-gate #define	ENVSIZE		255
557c478bd9Sstevel@tonic-gate #define	PATH		"PATH=/usr/bin"
567c478bd9Sstevel@tonic-gate #define	SUPATH		"PATH=/usr/sbin:/usr/bin"
577c478bd9Sstevel@tonic-gate #define	SHELL		"/usr/bin/sh"
587c478bd9Sstevel@tonic-gate #define	SHELL2		"/sbin/sh"
597c478bd9Sstevel@tonic-gate #define	TIMEZONEFILE	"/etc/default/init"
607c478bd9Sstevel@tonic-gate #define	LOGINFILE	"/etc/default/login"
617c478bd9Sstevel@tonic-gate #define	GLOBAL_ERR_SZ	1024
627c478bd9Sstevel@tonic-gate #define	GRAB_RETRY_MAX	100
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate static const char *pname;
657c478bd9Sstevel@tonic-gate extern char **environ;
667c478bd9Sstevel@tonic-gate static char *supath = SUPATH;
677c478bd9Sstevel@tonic-gate static char *path = PATH;
687c478bd9Sstevel@tonic-gate static char global_error[GLOBAL_ERR_SZ];
697c478bd9Sstevel@tonic-gate static int verbose = 0;
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate static priv_set_t *nset;
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate /* Private definitions for libproject */
747c478bd9Sstevel@tonic-gate extern projid_t setproject_proc(const char *, const char *, int, pid_t,
757c478bd9Sstevel@tonic-gate     struct ps_prochandle *, struct project *);
767c478bd9Sstevel@tonic-gate extern priv_set_t *setproject_initpriv(void);
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate static void usage(void);
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate static void preserve_error(const char *format, ...);
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate static int update_running_proc(int, char *, char *);
837c478bd9Sstevel@tonic-gate static int set_ids(struct ps_prochandle *, struct project *,
847c478bd9Sstevel@tonic-gate     struct passwd *);
857c478bd9Sstevel@tonic-gate static struct passwd *match_user(uid_t, char *, int);
867c478bd9Sstevel@tonic-gate static void setproject_err(char *, char *, int, struct project *);
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate static void
usage(void)897c478bd9Sstevel@tonic-gate usage(void)
907c478bd9Sstevel@tonic-gate {
917c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("usage: \n\t%s [-v] [-p project] "
927c478bd9Sstevel@tonic-gate 	    "[-c pid | [-Fl] [command [args ...]]]\n"), pname);
937c478bd9Sstevel@tonic-gate 	exit(2);
947c478bd9Sstevel@tonic-gate }
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate int
main(int argc,char * argv[])977c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
987c478bd9Sstevel@tonic-gate {
997c478bd9Sstevel@tonic-gate 	int c;
1007c478bd9Sstevel@tonic-gate 	struct passwd *pw;
1017c478bd9Sstevel@tonic-gate 	char *projname = NULL;
1027c478bd9Sstevel@tonic-gate 	uid_t uid;
1037c478bd9Sstevel@tonic-gate 	int login_flag = 0;
1047c478bd9Sstevel@tonic-gate 	int finalize_flag = TASK_NORMAL;
1057c478bd9Sstevel@tonic-gate 	int newproj_flag = 0;
1067c478bd9Sstevel@tonic-gate 	taskid_t taskid;
1077c478bd9Sstevel@tonic-gate 	char *shell;
1087c478bd9Sstevel@tonic-gate 	char *env[NENV];
1097c478bd9Sstevel@tonic-gate 	char **targs;
1107c478bd9Sstevel@tonic-gate 	char *filename, *procname = NULL;
1117c478bd9Sstevel@tonic-gate 	int error;
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate 	nset = setproject_initpriv();
1147c478bd9Sstevel@tonic-gate 	if (nset == NULL)
1157c478bd9Sstevel@tonic-gate 		die(gettext("privilege initialization failed\n"));
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate 	pname = getpname(argv[0]);
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate 	while ((c = getopt(argc, argv, OPTIONS_STRING)) != EOF) {
1207c478bd9Sstevel@tonic-gate 		switch (c) {
1217c478bd9Sstevel@tonic-gate 		case 'v':
1227c478bd9Sstevel@tonic-gate 			verbose = 1;
1237c478bd9Sstevel@tonic-gate 			break;
1247c478bd9Sstevel@tonic-gate 		case 'p':
1257c478bd9Sstevel@tonic-gate 			newproj_flag = 1;
1267c478bd9Sstevel@tonic-gate 			projname = optarg;
1277c478bd9Sstevel@tonic-gate 			break;
1287c478bd9Sstevel@tonic-gate 		case 'F':
1297c478bd9Sstevel@tonic-gate 			finalize_flag = TASK_FINAL;
1307c478bd9Sstevel@tonic-gate 			break;
1317c478bd9Sstevel@tonic-gate 		case 'l':
1327c478bd9Sstevel@tonic-gate 			login_flag++;
1337c478bd9Sstevel@tonic-gate 			break;
1347c478bd9Sstevel@tonic-gate 		case 'c':
1357c478bd9Sstevel@tonic-gate 			procname = optarg;
1367c478bd9Sstevel@tonic-gate 			break;
1377c478bd9Sstevel@tonic-gate 		case '?':
1387c478bd9Sstevel@tonic-gate 		default:
1397c478bd9Sstevel@tonic-gate 			usage();
1407c478bd9Sstevel@tonic-gate 			/*NOTREACHED*/
1417c478bd9Sstevel@tonic-gate 		}
1427c478bd9Sstevel@tonic-gate 	}
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate 	/* -c option is invalid with -F, -l, or a specified command */
1457c478bd9Sstevel@tonic-gate 	if ((procname != NULL) &&
1467c478bd9Sstevel@tonic-gate 	    (finalize_flag == TASK_FINAL || login_flag || optind < argc))
1477c478bd9Sstevel@tonic-gate 		usage();
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate 	if (procname != NULL) {
1507c478bd9Sstevel@tonic-gate 		/* Change project/task of an existing process */
1517c478bd9Sstevel@tonic-gate 		return (update_running_proc(newproj_flag, procname, projname));
1527c478bd9Sstevel@tonic-gate 	}
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate 	/*
1557c478bd9Sstevel@tonic-gate 	 * Get user data, so that we can confirm project membership as
1567c478bd9Sstevel@tonic-gate 	 * well as construct an appropriate login environment.
1577c478bd9Sstevel@tonic-gate 	 */
1587c478bd9Sstevel@tonic-gate 	uid = getuid();
1597c478bd9Sstevel@tonic-gate 	if ((pw = match_user(uid, projname, 1)) == NULL) {
1607c478bd9Sstevel@tonic-gate 		die("%s\n", global_error);
1617c478bd9Sstevel@tonic-gate 	}
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 	/*
1647c478bd9Sstevel@tonic-gate 	 * If no projname was specified, we're just creating a new task
1657c478bd9Sstevel@tonic-gate 	 * under the current project, so we can just set the new taskid.
1667c478bd9Sstevel@tonic-gate 	 * If our project is changing, we need to update any attendant
1677c478bd9Sstevel@tonic-gate 	 * pool/rctl bindings, so let setproject() do the dirty work.
1687c478bd9Sstevel@tonic-gate 	 */
1697c478bd9Sstevel@tonic-gate 	(void) __priv_bracket(PRIV_ON);
1707c478bd9Sstevel@tonic-gate 	if (projname == NULL) {
1717c478bd9Sstevel@tonic-gate 		if (settaskid(getprojid(), finalize_flag) == -1)
1727c478bd9Sstevel@tonic-gate 			if (errno == EAGAIN)
1737c478bd9Sstevel@tonic-gate 				die(gettext("resource control limit has been "
1747c478bd9Sstevel@tonic-gate 				    "reached"));
1757c478bd9Sstevel@tonic-gate 			else
1767c478bd9Sstevel@tonic-gate 				die(gettext("settaskid failed"));
1777c478bd9Sstevel@tonic-gate 	} else {
1787c478bd9Sstevel@tonic-gate 		if ((error = setproject(projname,
1797c478bd9Sstevel@tonic-gate 		    pw->pw_name, finalize_flag)) != 0) {
1807c478bd9Sstevel@tonic-gate 			setproject_err(pw->pw_name, projname, error, NULL);
1817c478bd9Sstevel@tonic-gate 			if (error < 0)
1827c478bd9Sstevel@tonic-gate 				die("%s\n", global_error);
1837c478bd9Sstevel@tonic-gate 			else
1847c478bd9Sstevel@tonic-gate 				warn("%s\n", global_error);
1857c478bd9Sstevel@tonic-gate 		}
1867c478bd9Sstevel@tonic-gate 	}
1877c478bd9Sstevel@tonic-gate 	__priv_relinquish();
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 	taskid = gettaskid();
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 	if (verbose)
1927c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%d\n", (int)taskid);
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate 	/*
1957c478bd9Sstevel@tonic-gate 	 * Validate user's shell from passwd database.
1967c478bd9Sstevel@tonic-gate 	 */
1977c478bd9Sstevel@tonic-gate 	if (strcmp(pw->pw_shell, "") == 0) {
1987c478bd9Sstevel@tonic-gate 		if (access(SHELL, X_OK) == 0)
1997c478bd9Sstevel@tonic-gate 			pw->pw_shell = SHELL;
2007c478bd9Sstevel@tonic-gate 		else
2017c478bd9Sstevel@tonic-gate 			pw->pw_shell = SHELL2;
2027c478bd9Sstevel@tonic-gate 	}
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate 	if (login_flag) {
2057c478bd9Sstevel@tonic-gate 		/*
2067c478bd9Sstevel@tonic-gate 		 * Since we've been invoked as a "simulated login", set up the
2077c478bd9Sstevel@tonic-gate 		 * environment.
2087c478bd9Sstevel@tonic-gate 		 */
2097c478bd9Sstevel@tonic-gate 		char *cur_tz = getenv("TZ");
2107c478bd9Sstevel@tonic-gate 		char *cur_term = getenv("TERM");
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate 		char **envnext;
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 		size_t len_home = strlen(pw->pw_dir) + strlen("HOME=") + 1;
2157c478bd9Sstevel@tonic-gate 		size_t len_logname = strlen(pw->pw_name) + strlen("LOGNAME=") +
2167c478bd9Sstevel@tonic-gate 		    1;
2177c478bd9Sstevel@tonic-gate 		size_t len_shell = strlen(pw->pw_shell) + strlen("SHELL=") + 1;
2187c478bd9Sstevel@tonic-gate 		size_t len_mail = strlen(pw->pw_name) +
2197c478bd9Sstevel@tonic-gate 		    strlen("MAIL=/var/mail/") + 1;
2207c478bd9Sstevel@tonic-gate 		size_t len_tz;
2217c478bd9Sstevel@tonic-gate 		size_t len_term;
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 		char *env_home = safe_malloc(len_home);
2247c478bd9Sstevel@tonic-gate 		char *env_logname = safe_malloc(len_logname);
2257c478bd9Sstevel@tonic-gate 		char *env_shell = safe_malloc(len_shell);
2267c478bd9Sstevel@tonic-gate 		char *env_mail = safe_malloc(len_mail);
2277c478bd9Sstevel@tonic-gate 		char *env_tz;
2287c478bd9Sstevel@tonic-gate 		char *env_term;
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 		(void) snprintf(env_home, len_home, "HOME=%s", pw->pw_dir);
2317c478bd9Sstevel@tonic-gate 		(void) snprintf(env_logname, len_logname, "LOGNAME=%s",
2327c478bd9Sstevel@tonic-gate 		    pw->pw_name);
2337c478bd9Sstevel@tonic-gate 		(void) snprintf(env_shell, len_shell, "SHELL=%s", pw->pw_shell);
2347c478bd9Sstevel@tonic-gate 		(void) snprintf(env_mail, len_mail, "MAIL=/var/mail/%s",
2357c478bd9Sstevel@tonic-gate 		    pw->pw_name);
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 		env[0] = env_home;
2387c478bd9Sstevel@tonic-gate 		env[1] = env_logname;
2397c478bd9Sstevel@tonic-gate 		env[2] = (pw->pw_uid == 0 ? supath : path);
2407c478bd9Sstevel@tonic-gate 		env[3] = env_shell;
2417c478bd9Sstevel@tonic-gate 		env[4] = env_mail;
2427c478bd9Sstevel@tonic-gate 		env[5] = NULL;
2437c478bd9Sstevel@tonic-gate 		env[6] = NULL;
2447c478bd9Sstevel@tonic-gate 		env[7] = NULL;
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 		envnext = (char **)&env[5];
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 		/*
2497c478bd9Sstevel@tonic-gate 		 * It's possible that TERM wasn't defined in the outer
2507c478bd9Sstevel@tonic-gate 		 * environment.
2517c478bd9Sstevel@tonic-gate 		 */
2527c478bd9Sstevel@tonic-gate 		if (cur_term != NULL) {
2537c478bd9Sstevel@tonic-gate 			len_term = strlen(cur_term) + strlen("TERM=") + 1;
2547c478bd9Sstevel@tonic-gate 			env_term = safe_malloc(len_term);
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate 			(void) snprintf(env_term, len_term, "TERM=%s",
2577c478bd9Sstevel@tonic-gate 			    cur_term);
2587c478bd9Sstevel@tonic-gate 			*envnext = env_term;
2597c478bd9Sstevel@tonic-gate 			envnext++;
2607c478bd9Sstevel@tonic-gate 		}
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 		/*
2637c478bd9Sstevel@tonic-gate 		 * It is also possible that TZ wasn't defined in the outer
2647c478bd9Sstevel@tonic-gate 		 * environment.  In that case, we must attempt to open the file
2657c478bd9Sstevel@tonic-gate 		 * defining the default timezone and select the appropriate
2667c478bd9Sstevel@tonic-gate 		 * entry. If there is no default timezone there, try
2677c478bd9Sstevel@tonic-gate 		 * TIMEZONE in /etc/default/login, duplicating the algorithm
2687c478bd9Sstevel@tonic-gate 		 * that login uses.
2697c478bd9Sstevel@tonic-gate 		 */
2707c478bd9Sstevel@tonic-gate 		if (cur_tz != NULL) {
2717c478bd9Sstevel@tonic-gate 			len_tz = strlen(cur_tz) + strlen("TZ=") + 1;
2727c478bd9Sstevel@tonic-gate 			env_tz = safe_malloc(len_tz);
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 			(void) snprintf(env_tz, len_tz, "TZ=%s", cur_tz);
2757c478bd9Sstevel@tonic-gate 			*envnext = env_tz;
2767c478bd9Sstevel@tonic-gate 		} else {
2777c478bd9Sstevel@tonic-gate 			if ((env_tz = getdefault(TIMEZONEFILE, "TZ=",
2787c478bd9Sstevel@tonic-gate 			    "TZ=")) != NULL)
2797c478bd9Sstevel@tonic-gate 				*envnext = env_tz;
2807c478bd9Sstevel@tonic-gate 			else {
2817c478bd9Sstevel@tonic-gate 				env_tz = getdefault(LOGINFILE, "TIMEZONE=",
2827c478bd9Sstevel@tonic-gate 				    "TZ=");
2837c478bd9Sstevel@tonic-gate 				*envnext = env_tz;
2847c478bd9Sstevel@tonic-gate 			}
2857c478bd9Sstevel@tonic-gate 		}
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 		environ = (char **)&env[0];
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 		/*
2907c478bd9Sstevel@tonic-gate 		 * Prefix the shell string with a hyphen, indicating a login
2917c478bd9Sstevel@tonic-gate 		 * shell.
2927c478bd9Sstevel@tonic-gate 		 */
2937c478bd9Sstevel@tonic-gate 		shell = safe_malloc(PATH_MAX);
2947c478bd9Sstevel@tonic-gate 		(void) snprintf(shell, PATH_MAX, "-%s", basename(pw->pw_shell));
2957c478bd9Sstevel@tonic-gate 	} else {
2967c478bd9Sstevel@tonic-gate 		shell = basename(pw->pw_shell);
2977c478bd9Sstevel@tonic-gate 	}
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate 	/*
3007c478bd9Sstevel@tonic-gate 	 * If there are no arguments, we launch the user's shell; otherwise, the
3017c478bd9Sstevel@tonic-gate 	 * remaining commands are assumed to form a valid command invocation
3027c478bd9Sstevel@tonic-gate 	 * that we can exec.
3037c478bd9Sstevel@tonic-gate 	 */
3047c478bd9Sstevel@tonic-gate 	if (optind >= argc) {
3057c478bd9Sstevel@tonic-gate 		targs = alloca(2 * sizeof (char *));
3067c478bd9Sstevel@tonic-gate 		filename = pw->pw_shell;
3077c478bd9Sstevel@tonic-gate 		targs[0] = shell;
3087c478bd9Sstevel@tonic-gate 		targs[1] = NULL;
3097c478bd9Sstevel@tonic-gate 	} else {
3107c478bd9Sstevel@tonic-gate 		targs = &argv[optind];
3117c478bd9Sstevel@tonic-gate 		filename = targs[0];
3127c478bd9Sstevel@tonic-gate 	}
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 	if (execvp(filename, targs) == -1)
3157c478bd9Sstevel@tonic-gate 		die(gettext("exec of %s failed"), targs[0]);
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 	/*
3187c478bd9Sstevel@tonic-gate 	 * We should never get here.
3197c478bd9Sstevel@tonic-gate 	 */
3207c478bd9Sstevel@tonic-gate 	return (1);
3217c478bd9Sstevel@tonic-gate }
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate static int
update_running_proc(int newproj_flag,char * procname,char * projname)3247c478bd9Sstevel@tonic-gate update_running_proc(int newproj_flag, char *procname, char *projname)
3257c478bd9Sstevel@tonic-gate {
3267c478bd9Sstevel@tonic-gate 	struct ps_prochandle *p;
3277c478bd9Sstevel@tonic-gate 	prcred_t original_prcred, current_prcred;
3287c478bd9Sstevel@tonic-gate 	projid_t prprojid;
3297c478bd9Sstevel@tonic-gate 	taskid_t taskid;
3307c478bd9Sstevel@tonic-gate 	int error = 0, gret;
3317c478bd9Sstevel@tonic-gate 	struct project project;
3327c478bd9Sstevel@tonic-gate 	char prbuf[PROJECT_BUFSZ];
3337c478bd9Sstevel@tonic-gate 	struct passwd *passwd_entry;
3347c478bd9Sstevel@tonic-gate 	int grab_retry_count = 0;
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate 	/*
3377c478bd9Sstevel@tonic-gate 	 * Catch signals from terminal. There isn't much sense in
3387c478bd9Sstevel@tonic-gate 	 * doing anything but ignoring them since we don't do anything
3397c478bd9Sstevel@tonic-gate 	 * after the point we'd be capable of handling them again.
3407c478bd9Sstevel@tonic-gate 	 */
3417c478bd9Sstevel@tonic-gate 	(void) sigignore(SIGHUP);
3427c478bd9Sstevel@tonic-gate 	(void) sigignore(SIGINT);
3437c478bd9Sstevel@tonic-gate 	(void) sigignore(SIGQUIT);
3447c478bd9Sstevel@tonic-gate 	(void) sigignore(SIGTERM);
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 	/* flush stdout before grabbing the proc to avoid deadlock */
3477c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);
3487c478bd9Sstevel@tonic-gate 
3497c478bd9Sstevel@tonic-gate 	/*
3507c478bd9Sstevel@tonic-gate 	 * We need to grab the process, which will force it to stop execution
3517c478bd9Sstevel@tonic-gate 	 * until the grab is released, in order to aquire some information about
3527c478bd9Sstevel@tonic-gate 	 * it, such as its current project (which is achieved via an injected
3537c478bd9Sstevel@tonic-gate 	 * system call and therefore needs an agent) and its credentials. We
3547c478bd9Sstevel@tonic-gate 	 * will then need to release it again because it may be a process that
3557c478bd9Sstevel@tonic-gate 	 * we rely on for later calls, for example nscd.
3567c478bd9Sstevel@tonic-gate 	 */
3577c478bd9Sstevel@tonic-gate 	if ((p = proc_arg_grab(procname, PR_ARG_PIDS, 0, &gret)) == NULL) {
3587c478bd9Sstevel@tonic-gate 		warn(gettext("failed to grab for process %s: %s\n"),
3597c478bd9Sstevel@tonic-gate 		    procname, Pgrab_error(gret));
3607c478bd9Sstevel@tonic-gate 		return (1);
3617c478bd9Sstevel@tonic-gate 	}
3627c478bd9Sstevel@tonic-gate 	if (Pcreate_agent(p) != 0) {
3637c478bd9Sstevel@tonic-gate 		Prelease(p, 0);
3647c478bd9Sstevel@tonic-gate 		warn(gettext("cannot control process %s\n"), procname);
3657c478bd9Sstevel@tonic-gate 		return (1);
3667c478bd9Sstevel@tonic-gate 	}
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 	/*
3697c478bd9Sstevel@tonic-gate 	 * The victim process is now held. Do not call any functions
3707c478bd9Sstevel@tonic-gate 	 * which generate stdout/stderr until the process has been
3717c478bd9Sstevel@tonic-gate 	 * released.
3727c478bd9Sstevel@tonic-gate 	 */
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate /*
3757c478bd9Sstevel@tonic-gate  * The target process will soon be restarted (in case it is in newtask's
3767c478bd9Sstevel@tonic-gate  * execution path) and then stopped again. We need to ensure that our cached
3777c478bd9Sstevel@tonic-gate  * data doesn't change while the process runs so return here if the target
3787c478bd9Sstevel@tonic-gate  * process changes its user id in between our stop operations, so that we can
3797c478bd9Sstevel@tonic-gate  * try again.
3807c478bd9Sstevel@tonic-gate  */
3817c478bd9Sstevel@tonic-gate pgrab_retry:
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 	/* Cache required information about the process. */
3847c478bd9Sstevel@tonic-gate 	if (Pcred(p, &original_prcred, 0) != 0) {
3857c478bd9Sstevel@tonic-gate 		preserve_error(gettext("cannot get process credentials %s\n"),
3867c478bd9Sstevel@tonic-gate 		    procname);
3877c478bd9Sstevel@tonic-gate 		error = 1;
3887c478bd9Sstevel@tonic-gate 	}
3897c478bd9Sstevel@tonic-gate 	if ((prprojid = pr_getprojid(p)) == -1) {
3907c478bd9Sstevel@tonic-gate 		preserve_error(gettext("cannot get process project id %s\n"),
3917c478bd9Sstevel@tonic-gate 		    procname);
3927c478bd9Sstevel@tonic-gate 		error = 1;
3937c478bd9Sstevel@tonic-gate 	}
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 	/*
3967c478bd9Sstevel@tonic-gate 	 * We now have all the required information, so release the target
3977c478bd9Sstevel@tonic-gate 	 * process and perform our sanity checks. The process needs to be
3987c478bd9Sstevel@tonic-gate 	 * running at this point because it may be in the execution path of the
3997c478bd9Sstevel@tonic-gate 	 * calls made below.
4007c478bd9Sstevel@tonic-gate 	 */
4017c478bd9Sstevel@tonic-gate 	Pdestroy_agent(p);
4027c478bd9Sstevel@tonic-gate 	Prelease(p, 0);
4037c478bd9Sstevel@tonic-gate 
4047c478bd9Sstevel@tonic-gate 	/* if our data acquisition failed, then we can't continue. */
4057c478bd9Sstevel@tonic-gate 	if (error) {
4067c478bd9Sstevel@tonic-gate 		warn("%s\n", global_error);
4077c478bd9Sstevel@tonic-gate 		return (1);
4087c478bd9Sstevel@tonic-gate 	}
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate 	if (newproj_flag == 0) {
4117c478bd9Sstevel@tonic-gate 		/*
4127c478bd9Sstevel@tonic-gate 		 * Just changing the task, so set projname to the current
4137c478bd9Sstevel@tonic-gate 		 * project of the running process.
4147c478bd9Sstevel@tonic-gate 		 */
4157c478bd9Sstevel@tonic-gate 		if (getprojbyid(prprojid, &project, &prbuf,
4167c478bd9Sstevel@tonic-gate 		    PROJECT_BUFSZ) == NULL) {
4177c478bd9Sstevel@tonic-gate 			warn(gettext("unable to get project name "
4187c478bd9Sstevel@tonic-gate 			    "for projid %d"), prprojid);
4197c478bd9Sstevel@tonic-gate 			return (1);
4207c478bd9Sstevel@tonic-gate 		}
4217c478bd9Sstevel@tonic-gate 		projname = project.pj_name;
4227c478bd9Sstevel@tonic-gate 	} else {
4237c478bd9Sstevel@tonic-gate 		/*
4247c478bd9Sstevel@tonic-gate 		 * cache info for the project which user passed in via the
4257c478bd9Sstevel@tonic-gate 		 * command line
4267c478bd9Sstevel@tonic-gate 		 */
4277c478bd9Sstevel@tonic-gate 		if (getprojbyname(projname, &project, &prbuf,
4287c478bd9Sstevel@tonic-gate 		    PROJECT_BUFSZ) == NULL) {
4297c478bd9Sstevel@tonic-gate 			warn(gettext("unknown project \"%s\"\n"), projname);
4307c478bd9Sstevel@tonic-gate 			return (1);
4317c478bd9Sstevel@tonic-gate 		}
4327c478bd9Sstevel@tonic-gate 	}
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 	/*
4357c478bd9Sstevel@tonic-gate 	 * Use our cached information to verify that the owner of the running
4367c478bd9Sstevel@tonic-gate 	 * process is a member of proj
4377c478bd9Sstevel@tonic-gate 	 */
4387c478bd9Sstevel@tonic-gate 	if ((passwd_entry = match_user(original_prcred.pr_ruid,
4397c478bd9Sstevel@tonic-gate 	    projname, 0)) == NULL) {
4407c478bd9Sstevel@tonic-gate 		warn("%s\n", global_error);
4417c478bd9Sstevel@tonic-gate 		return (1);
4427c478bd9Sstevel@tonic-gate 	}
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 	/*
4457c478bd9Sstevel@tonic-gate 	 * We can now safely stop the process again in order to change the
4467c478bd9Sstevel@tonic-gate 	 * project and taskid as required.
4477c478bd9Sstevel@tonic-gate 	 */
4487c478bd9Sstevel@tonic-gate 	if ((p = proc_arg_grab(procname, PR_ARG_PIDS, 0, &gret)) == NULL) {
4497c478bd9Sstevel@tonic-gate 		warn(gettext("failed to grab for process %s: %s\n"),
4507c478bd9Sstevel@tonic-gate 		    procname, Pgrab_error(gret));
4517c478bd9Sstevel@tonic-gate 		return (1);
4527c478bd9Sstevel@tonic-gate 	}
4537c478bd9Sstevel@tonic-gate 	if (Pcreate_agent(p) != 0) {
4547c478bd9Sstevel@tonic-gate 		Prelease(p, 0);
4557c478bd9Sstevel@tonic-gate 		warn(gettext("cannot control process %s\n"), procname);
4567c478bd9Sstevel@tonic-gate 		return (1);
4577c478bd9Sstevel@tonic-gate 	}
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	/*
4607c478bd9Sstevel@tonic-gate 	 * Now that the target process is stopped, check the validity of our
4617c478bd9Sstevel@tonic-gate 	 * cached info. If we aren't superuser then match_user() will have
4627c478bd9Sstevel@tonic-gate 	 * checked to make sure that the owner of the process is in the relevant
4637c478bd9Sstevel@tonic-gate 	 * project. If our ruid has changed, then match_user()'s conclusion may
4647c478bd9Sstevel@tonic-gate 	 * be invalid.
4657c478bd9Sstevel@tonic-gate 	 */
4667c478bd9Sstevel@tonic-gate 	if (getuid() != 0) {
4677c478bd9Sstevel@tonic-gate 		if (Pcred(p, &current_prcred, 0) != 0) {
4687c478bd9Sstevel@tonic-gate 			Pdestroy_agent(p);
4697c478bd9Sstevel@tonic-gate 			Prelease(p, 0);
4707c478bd9Sstevel@tonic-gate 			warn(gettext("can't get process credentials %s\n"),
4717c478bd9Sstevel@tonic-gate 			    procname);
4727c478bd9Sstevel@tonic-gate 			return (1);
4737c478bd9Sstevel@tonic-gate 		}
4747c478bd9Sstevel@tonic-gate 
4757c478bd9Sstevel@tonic-gate 		if (original_prcred.pr_ruid != current_prcred.pr_ruid) {
4767c478bd9Sstevel@tonic-gate 			if (grab_retry_count++ < GRAB_RETRY_MAX)
4777c478bd9Sstevel@tonic-gate 				goto pgrab_retry;
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate 			warn(gettext("process consistently changed its "
4807c478bd9Sstevel@tonic-gate 			    "user id %s\n"), procname);
4817c478bd9Sstevel@tonic-gate 			return (1);
4827c478bd9Sstevel@tonic-gate 		}
4837c478bd9Sstevel@tonic-gate 	}
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate 	error = set_ids(p, &project, passwd_entry);
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 	if (verbose)
4887c478bd9Sstevel@tonic-gate 		taskid = pr_gettaskid(p);
4897c478bd9Sstevel@tonic-gate 
4907c478bd9Sstevel@tonic-gate 	Pdestroy_agent(p);
4917c478bd9Sstevel@tonic-gate 	Prelease(p, 0);
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate 	if (error) {
4947c478bd9Sstevel@tonic-gate 		/*
4957c478bd9Sstevel@tonic-gate 		 * error is serious enough to stop, only if negative.
4967c478bd9Sstevel@tonic-gate 		 * Otherwise, it simply indicates one of the resource
4977c478bd9Sstevel@tonic-gate 		 * control assignments failed, which is worth warning
4987c478bd9Sstevel@tonic-gate 		 * about.
4997c478bd9Sstevel@tonic-gate 		 */
5007c478bd9Sstevel@tonic-gate 		warn("%s\n", global_error);
5017c478bd9Sstevel@tonic-gate 		if (error < 0)
5027c478bd9Sstevel@tonic-gate 			return (1);
5037c478bd9Sstevel@tonic-gate 	}
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate 	if (verbose)
5067c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%d\n", (int)taskid);
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 	return (0);
5097c478bd9Sstevel@tonic-gate }
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate static int
set_ids(struct ps_prochandle * p,struct project * project,struct passwd * passwd_entry)5127c478bd9Sstevel@tonic-gate set_ids(struct ps_prochandle *p, struct project *project,
5137c478bd9Sstevel@tonic-gate     struct passwd *passwd_entry)
5147c478bd9Sstevel@tonic-gate {
5157c478bd9Sstevel@tonic-gate 	int be_su = 0;
5167c478bd9Sstevel@tonic-gate 	prcred_t old_prcred;
5177c478bd9Sstevel@tonic-gate 	int error;
5187c478bd9Sstevel@tonic-gate 	prpriv_t *old_prpriv, *new_prpriv;
5197c478bd9Sstevel@tonic-gate 	size_t prsz = sizeof (prpriv_t);
5207c478bd9Sstevel@tonic-gate 	priv_set_t *eset, *pset;
5217c478bd9Sstevel@tonic-gate 	int ind;
5227c478bd9Sstevel@tonic-gate 
5237c478bd9Sstevel@tonic-gate 	if (Pcred(p, &old_prcred, 0) != 0) {
5247c478bd9Sstevel@tonic-gate 		preserve_error(gettext("can't get process credentials"));
5257c478bd9Sstevel@tonic-gate 		return (1);
5267c478bd9Sstevel@tonic-gate 	}
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate 	old_prpriv = proc_get_priv(Pstatus(p)->pr_pid);
5297c478bd9Sstevel@tonic-gate 	if (old_prpriv == NULL) {
5307c478bd9Sstevel@tonic-gate 		preserve_error(gettext("can't get process privileges"));
5317c478bd9Sstevel@tonic-gate 		return (1);
5327c478bd9Sstevel@tonic-gate 	}
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate 	prsz = PRIV_PRPRIV_SIZE(old_prpriv);
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 	new_prpriv = malloc(prsz);
5377c478bd9Sstevel@tonic-gate 	if (new_prpriv == NULL) {
5387c478bd9Sstevel@tonic-gate 		preserve_error(gettext("can't allocate memory"));
539*43051d27SRobert Mustacchi 		proc_free_priv(old_prpriv);
5407c478bd9Sstevel@tonic-gate 		return (1);
5417c478bd9Sstevel@tonic-gate 	}
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate 	(void) memcpy(new_prpriv, old_prpriv, prsz);
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate 	/*
5467c478bd9Sstevel@tonic-gate 	 * If the process already has the proc_taskid privilege,
5477c478bd9Sstevel@tonic-gate 	 * we don't need to elevate its privileges; if it doesn't,
5487c478bd9Sstevel@tonic-gate 	 * we try to do it here.
5497c478bd9Sstevel@tonic-gate 	 * As we do not wish to leave a window in which the process runs
5507c478bd9Sstevel@tonic-gate 	 * with elevated privileges, we make sure that the process dies
5517c478bd9Sstevel@tonic-gate 	 * when we go away unexpectedly.
5527c478bd9Sstevel@tonic-gate 	 */
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 	ind = priv_getsetbyname(PRIV_EFFECTIVE);
5557c478bd9Sstevel@tonic-gate 	eset = (priv_set_t *)&new_prpriv->pr_sets[new_prpriv->pr_setsize * ind];
5567c478bd9Sstevel@tonic-gate 	ind = priv_getsetbyname(PRIV_PERMITTED);
5577c478bd9Sstevel@tonic-gate 	pset = (priv_set_t *)&new_prpriv->pr_sets[new_prpriv->pr_setsize * ind];
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate 	if (!priv_issubset(nset, eset)) {
5607c478bd9Sstevel@tonic-gate 		be_su = 1;
5617c478bd9Sstevel@tonic-gate 		priv_union(nset, eset);
5627c478bd9Sstevel@tonic-gate 		priv_union(nset, pset);
5637c478bd9Sstevel@tonic-gate 		if (Psetflags(p, PR_KLC) != 0) {
5647c478bd9Sstevel@tonic-gate 			preserve_error(gettext("cannot set process "
5657c478bd9Sstevel@tonic-gate 			    "privileges"));
5667c478bd9Sstevel@tonic-gate 			(void) Punsetflags(p, PR_KLC);
5677c478bd9Sstevel@tonic-gate 			free(new_prpriv);
568*43051d27SRobert Mustacchi 			proc_free_priv(old_prpriv);
5697c478bd9Sstevel@tonic-gate 			return (1);
5707c478bd9Sstevel@tonic-gate 		}
5717c478bd9Sstevel@tonic-gate 		(void) __priv_bracket(PRIV_ON);
5727c478bd9Sstevel@tonic-gate 		if (Psetpriv(p, new_prpriv) != 0) {
5737c478bd9Sstevel@tonic-gate 			(void) __priv_bracket(PRIV_OFF);
5747c478bd9Sstevel@tonic-gate 			preserve_error(gettext("cannot set process "
5757c478bd9Sstevel@tonic-gate 			    "privileges"));
5767c478bd9Sstevel@tonic-gate 			(void) Punsetflags(p, PR_KLC);
5777c478bd9Sstevel@tonic-gate 			free(new_prpriv);
578*43051d27SRobert Mustacchi 			proc_free_priv(old_prpriv);
5797c478bd9Sstevel@tonic-gate 			return (1);
5807c478bd9Sstevel@tonic-gate 		}
5817c478bd9Sstevel@tonic-gate 		(void) __priv_bracket(PRIV_OFF);
5827c478bd9Sstevel@tonic-gate 	}
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate 	(void) __priv_bracket(PRIV_ON);
5857c478bd9Sstevel@tonic-gate 	if ((error = setproject_proc(project->pj_name,
5867c478bd9Sstevel@tonic-gate 	    passwd_entry->pw_name, 0, Pstatus(p)->pr_pid, p, project)) != 0) {
5877c478bd9Sstevel@tonic-gate 		/* global_error is set by setproject_err */
5887c478bd9Sstevel@tonic-gate 		setproject_err(passwd_entry->pw_name, project->pj_name,
5897c478bd9Sstevel@tonic-gate 		    error, project);
5907c478bd9Sstevel@tonic-gate 	}
5917c478bd9Sstevel@tonic-gate 	(void) __priv_bracket(PRIV_OFF);
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate 	/* relinquish added privileges */
5947c478bd9Sstevel@tonic-gate 	if (be_su) {
5957c478bd9Sstevel@tonic-gate 		(void) __priv_bracket(PRIV_ON);
5967c478bd9Sstevel@tonic-gate 		if (Psetpriv(p, old_prpriv) != 0) {
5977c478bd9Sstevel@tonic-gate 			/*
5987c478bd9Sstevel@tonic-gate 			 * We shouldn't ever be in a state where we can't
5997c478bd9Sstevel@tonic-gate 			 * set the process back to its old creds, but we
6007c478bd9Sstevel@tonic-gate 			 * don't want to take the chance of leaving a
6017c478bd9Sstevel@tonic-gate 			 * non-privileged process with enhanced creds. So,
6027c478bd9Sstevel@tonic-gate 			 * release the process from libproc control, knowing
6037c478bd9Sstevel@tonic-gate 			 * that it will be killed.
6047c478bd9Sstevel@tonic-gate 			 */
6057c478bd9Sstevel@tonic-gate 			(void) __priv_bracket(PRIV_OFF);
6067c478bd9Sstevel@tonic-gate 			Pdestroy_agent(p);
6077c478bd9Sstevel@tonic-gate 			die(gettext("cannot relinquish superuser credentials "
6087c478bd9Sstevel@tonic-gate 			    "for pid %d. The process was killed."),
6097c478bd9Sstevel@tonic-gate 			    Pstatus(p)->pr_pid);
6107c478bd9Sstevel@tonic-gate 		}
6117c478bd9Sstevel@tonic-gate 		(void) __priv_bracket(PRIV_OFF);
6127c478bd9Sstevel@tonic-gate 		if (Punsetflags(p, PR_KLC) != 0)
6137c478bd9Sstevel@tonic-gate 			preserve_error(gettext("error relinquishing "
6147c478bd9Sstevel@tonic-gate 			    "credentials. Process %d will be killed."),
6157c478bd9Sstevel@tonic-gate 			    Pstatus(p)->pr_pid);
6167c478bd9Sstevel@tonic-gate 	}
6177c478bd9Sstevel@tonic-gate 	free(new_prpriv);
618*43051d27SRobert Mustacchi 	proc_free_priv(old_prpriv);
6197c478bd9Sstevel@tonic-gate 
6207c478bd9Sstevel@tonic-gate 	return (error);
6217c478bd9Sstevel@tonic-gate }
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate /*
6247c478bd9Sstevel@tonic-gate  * preserve_error() should be called rather than warn() by any
6257c478bd9Sstevel@tonic-gate  * function that is called while the victim process is being
6267c478bd9Sstevel@tonic-gate  * held by Pgrab.
6277c478bd9Sstevel@tonic-gate  *
6287c478bd9Sstevel@tonic-gate  * It saves a single error message to be printed until after
6297c478bd9Sstevel@tonic-gate  * the process has been released. Since multiple errors are not
6307c478bd9Sstevel@tonic-gate  * stored, any error should be considered critical.
6317c478bd9Sstevel@tonic-gate  */
6327c478bd9Sstevel@tonic-gate void
preserve_error(const char * format,...)6337c478bd9Sstevel@tonic-gate preserve_error(const char *format, ...)
6347c478bd9Sstevel@tonic-gate {
6357c478bd9Sstevel@tonic-gate 	va_list alist;
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate 	va_start(alist, format);
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 	/*
6407c478bd9Sstevel@tonic-gate 	 * GLOBAL_ERR_SZ is pretty big. If the error is longer
6417c478bd9Sstevel@tonic-gate 	 * than that, just truncate it, rather than chance missing
6427c478bd9Sstevel@tonic-gate 	 * the error altogether.
6437c478bd9Sstevel@tonic-gate 	 */
6447c478bd9Sstevel@tonic-gate 	(void) vsnprintf(global_error, GLOBAL_ERR_SZ-1, format, alist);
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 	va_end(alist);
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate }
6497c478bd9Sstevel@tonic-gate 
6507c478bd9Sstevel@tonic-gate /*
6517c478bd9Sstevel@tonic-gate  * Given the input arguments, return the passwd structure that matches best.
6527c478bd9Sstevel@tonic-gate  * Also, since we use getpwnam() and friends, subsequent calls to this
6537c478bd9Sstevel@tonic-gate  * function will re-use the memory previously returned.
6547c478bd9Sstevel@tonic-gate  */
6557c478bd9Sstevel@tonic-gate static struct passwd *
match_user(uid_t uid,char * projname,int is_my_uid)6567c478bd9Sstevel@tonic-gate match_user(uid_t uid, char *projname, int is_my_uid)
6577c478bd9Sstevel@tonic-gate {
6587c478bd9Sstevel@tonic-gate 	char prbuf[PROJECT_BUFSZ], username[LOGNAME_MAX+1];
6597c478bd9Sstevel@tonic-gate 	struct project prj;
6607c478bd9Sstevel@tonic-gate 	char *tmp_name;
6617c478bd9Sstevel@tonic-gate 	struct passwd *pw = NULL;
6627c478bd9Sstevel@tonic-gate 
6637c478bd9Sstevel@tonic-gate 	/*
6647c478bd9Sstevel@tonic-gate 	 * In order to allow users with the same UID but distinguishable
6657c478bd9Sstevel@tonic-gate 	 * user names to be in different projects we play a guessing
6667c478bd9Sstevel@tonic-gate 	 * game of which username is most appropriate. If we're checking
6677c478bd9Sstevel@tonic-gate 	 * for the uid of the calling process, the login name is a
6687c478bd9Sstevel@tonic-gate 	 * good starting point.
6697c478bd9Sstevel@tonic-gate 	 */
6707c478bd9Sstevel@tonic-gate 	if (is_my_uid) {
6717c478bd9Sstevel@tonic-gate 		if ((tmp_name = getlogin()) == NULL ||
6727c478bd9Sstevel@tonic-gate 		    (pw = getpwnam(tmp_name)) == NULL || (pw->pw_uid != uid) ||
6737c478bd9Sstevel@tonic-gate 		    (pw->pw_name == NULL))
6747c478bd9Sstevel@tonic-gate 			pw = NULL;
6757c478bd9Sstevel@tonic-gate 	}
6767c478bd9Sstevel@tonic-gate 
6777c478bd9Sstevel@tonic-gate 	/*
6787c478bd9Sstevel@tonic-gate 	 * If the login name doesn't work,  we try the first match for
6797c478bd9Sstevel@tonic-gate 	 * the current uid in the password file.
6807c478bd9Sstevel@tonic-gate 	 */
6817c478bd9Sstevel@tonic-gate 	if (pw == NULL) {
6827c478bd9Sstevel@tonic-gate 		if (((pw = getpwuid(uid)) == NULL) || pw->pw_name == NULL) {
6837c478bd9Sstevel@tonic-gate 			preserve_error(gettext("cannot find username "
6847c478bd9Sstevel@tonic-gate 			    "for uid %d"), uid);
6857c478bd9Sstevel@tonic-gate 			return (NULL);
6867c478bd9Sstevel@tonic-gate 		}
6877c478bd9Sstevel@tonic-gate 	}
6887c478bd9Sstevel@tonic-gate 
6897c478bd9Sstevel@tonic-gate 	/*
6907c478bd9Sstevel@tonic-gate 	 * If projname wasn't supplied, we've done our best, so just return
6917c478bd9Sstevel@tonic-gate 	 * what we've got now. Alternatively, if newtask's invoker has
6927c478bd9Sstevel@tonic-gate 	 * superuser privileges, return the pw structure we've got now, with
6937c478bd9Sstevel@tonic-gate 	 * no further checking from inproj(). Superuser should be able to
6947c478bd9Sstevel@tonic-gate 	 * join any project, and the subsequent call to setproject() will
6957c478bd9Sstevel@tonic-gate 	 * allow this.
6967c478bd9Sstevel@tonic-gate 	 */
6977c478bd9Sstevel@tonic-gate 	if (projname == NULL || getuid() == (uid_t)0)
6987c478bd9Sstevel@tonic-gate 		return (pw);
6997c478bd9Sstevel@tonic-gate 
7000a1278f2SGary Mills 	(void) strlcpy(username, pw->pw_name, sizeof (username));
7017c478bd9Sstevel@tonic-gate 
7027c478bd9Sstevel@tonic-gate 	if (inproj(username, projname, prbuf, PROJECT_BUFSZ) == 0) {
7037c478bd9Sstevel@tonic-gate 		char **u;
7047c478bd9Sstevel@tonic-gate 		tmp_name = NULL;
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 		/*
7077c478bd9Sstevel@tonic-gate 		 * If the previous guesses didn't work, walk through all
7087c478bd9Sstevel@tonic-gate 		 * project members and test for UID-equivalence.
7097c478bd9Sstevel@tonic-gate 		 */
7107c478bd9Sstevel@tonic-gate 
7117c478bd9Sstevel@tonic-gate 		if (getprojbyname(projname, &prj, prbuf,
7127c478bd9Sstevel@tonic-gate 		    PROJECT_BUFSZ) == NULL) {
7137c478bd9Sstevel@tonic-gate 			preserve_error(gettext("unknown project \"%s\""),
7147c478bd9Sstevel@tonic-gate 			    projname);
7157c478bd9Sstevel@tonic-gate 			return (NULL);
7167c478bd9Sstevel@tonic-gate 		}
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 		for (u = prj.pj_users; *u; u++) {
7197c478bd9Sstevel@tonic-gate 			if ((pw = getpwnam(*u)) == NULL)
7207c478bd9Sstevel@tonic-gate 				continue;
7217c478bd9Sstevel@tonic-gate 
7227c478bd9Sstevel@tonic-gate 			if (pw->pw_uid == uid) {
7237c478bd9Sstevel@tonic-gate 				tmp_name = pw->pw_name;
7247c478bd9Sstevel@tonic-gate 				break;
7257c478bd9Sstevel@tonic-gate 			}
7267c478bd9Sstevel@tonic-gate 		}
7277c478bd9Sstevel@tonic-gate 
7287c478bd9Sstevel@tonic-gate 		if (tmp_name == NULL) {
7297c478bd9Sstevel@tonic-gate 			preserve_error(gettext("user \"%s\" is not a member of "
7307c478bd9Sstevel@tonic-gate 			    "project \"%s\""), username, projname);
7317c478bd9Sstevel@tonic-gate 			return (NULL);
7327c478bd9Sstevel@tonic-gate 		}
7337c478bd9Sstevel@tonic-gate 	}
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate 	return (pw);
7367c478bd9Sstevel@tonic-gate }
7377c478bd9Sstevel@tonic-gate 
7387c478bd9Sstevel@tonic-gate void
setproject_err(char * username,char * projname,int error,struct project * proj)7397c478bd9Sstevel@tonic-gate setproject_err(char *username, char *projname, int error, struct project *proj)
7407c478bd9Sstevel@tonic-gate {
7417c478bd9Sstevel@tonic-gate 	kva_t *kv_array = NULL;
7427c478bd9Sstevel@tonic-gate 	char prbuf[PROJECT_BUFSZ];
7437c478bd9Sstevel@tonic-gate 	struct project local_proj;
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate 	switch (error) {
7467c478bd9Sstevel@tonic-gate 	case SETPROJ_ERR_TASK:
7477c478bd9Sstevel@tonic-gate 		if (errno == EAGAIN)
7487c478bd9Sstevel@tonic-gate 			preserve_error(gettext("resource control limit has "
7497c478bd9Sstevel@tonic-gate 			    "been reached"));
7507c478bd9Sstevel@tonic-gate 		else if (errno == ESRCH)
7517c478bd9Sstevel@tonic-gate 			preserve_error(gettext("user \"%s\" is not a member of "
7527c478bd9Sstevel@tonic-gate 			    "project \"%s\""), username, projname);
7537c478bd9Sstevel@tonic-gate 		else if (errno == EACCES)
7547c478bd9Sstevel@tonic-gate 			preserve_error(gettext("the invoking task is final"));
7557c478bd9Sstevel@tonic-gate 		else
7567c478bd9Sstevel@tonic-gate 			preserve_error(
7577c478bd9Sstevel@tonic-gate 			    gettext("could not join project \"%s\""),
7587c478bd9Sstevel@tonic-gate 			    projname);
7597c478bd9Sstevel@tonic-gate 		break;
7607c478bd9Sstevel@tonic-gate 	case SETPROJ_ERR_POOL:
7617c478bd9Sstevel@tonic-gate 		if (errno == EACCES)
7627c478bd9Sstevel@tonic-gate 			preserve_error(gettext("no resource pool accepting "
7637c478bd9Sstevel@tonic-gate 			    "default bindings exists for project \"%s\""),
7647c478bd9Sstevel@tonic-gate 			    projname);
7657c478bd9Sstevel@tonic-gate 		else if (errno == ESRCH)
7667c478bd9Sstevel@tonic-gate 			preserve_error(gettext("specified resource pool does "
7677c478bd9Sstevel@tonic-gate 			    "not exist for project \"%s\""), projname);
7687c478bd9Sstevel@tonic-gate 		else
7697c478bd9Sstevel@tonic-gate 			preserve_error(gettext("could not bind to default "
7707c478bd9Sstevel@tonic-gate 			    "resource pool for project \"%s\""), projname);
7717c478bd9Sstevel@tonic-gate 		break;
7727c478bd9Sstevel@tonic-gate 	default:
7737c478bd9Sstevel@tonic-gate 		if (error <= 0) {
7747c478bd9Sstevel@tonic-gate 			preserve_error(gettext("setproject failed for "
7757c478bd9Sstevel@tonic-gate 			    "project \"%s\""), projname);
7767c478bd9Sstevel@tonic-gate 			return;
7777c478bd9Sstevel@tonic-gate 		}
7787c478bd9Sstevel@tonic-gate 		/*
7797c478bd9Sstevel@tonic-gate 		 * If we have a stopped target process it may be in
7807c478bd9Sstevel@tonic-gate 		 * getprojbyname()'s execution path which would make it unsafe
7817c478bd9Sstevel@tonic-gate 		 * to access the project table, so only do that if the caller
7827c478bd9Sstevel@tonic-gate 		 * hasn't provided a cached version of the project structure.
7837c478bd9Sstevel@tonic-gate 		 */
7847c478bd9Sstevel@tonic-gate 		if (proj == NULL)
7857c478bd9Sstevel@tonic-gate 			proj = getprojbyname(projname, &local_proj, prbuf,
7867c478bd9Sstevel@tonic-gate 			    PROJECT_BUFSZ);
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate 		if (proj == NULL || (kv_array = _str2kva(proj->pj_attr,
7897c478bd9Sstevel@tonic-gate 		    KV_ASSIGN, KV_DELIMITER)) == NULL ||
7907c478bd9Sstevel@tonic-gate 		    kv_array->length < error) {
7917c478bd9Sstevel@tonic-gate 			preserve_error(gettext("warning, resource control "
7927c478bd9Sstevel@tonic-gate 			    "assignment failed for project \"%s\" "
7937c478bd9Sstevel@tonic-gate 			    "attribute %d"),
7947c478bd9Sstevel@tonic-gate 			    projname, error);
7957c478bd9Sstevel@tonic-gate 			if (kv_array)
7967c478bd9Sstevel@tonic-gate 				_kva_free(kv_array);
7977c478bd9Sstevel@tonic-gate 			return;
7987c478bd9Sstevel@tonic-gate 		}
7997c478bd9Sstevel@tonic-gate 		preserve_error(gettext("warning, %s resource control "
8007c478bd9Sstevel@tonic-gate 		    "assignment failed for project \"%s\""),
8017c478bd9Sstevel@tonic-gate 		    kv_array->data[error - 1].key, projname);
8027c478bd9Sstevel@tonic-gate 		_kva_free(kv_array);
8037c478bd9Sstevel@tonic-gate 	}
8047c478bd9Sstevel@tonic-gate }
805