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