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