1 /* 2 * Copyright 2014, Paweł Dziepak, pdziepak@quarnos.org. 3 * Copyright 2008-2016, Ingo Weinhold, ingo_weinhold@gmx.de. 4 * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de. 5 * Distributed under the terms of the MIT License. 6 * 7 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 8 * Distributed under the terms of the NewOS License. 9 */ 10 11 12 /*! Team functions */ 13 14 15 #include <team.h> 16 17 #include <errno.h> 18 #include <stdio.h> 19 #include <stdlib.h> 20 #include <string.h> 21 #include <sys/wait.h> 22 23 #include <OS.h> 24 25 #include <AutoDeleter.h> 26 #include <FindDirectory.h> 27 28 #include <extended_system_info_defs.h> 29 30 #include <commpage.h> 31 #include <boot_device.h> 32 #include <elf.h> 33 #include <file_cache.h> 34 #include <find_directory_private.h> 35 #include <fs/KPath.h> 36 #include <heap.h> 37 #include <int.h> 38 #include <kernel.h> 39 #include <kimage.h> 40 #include <kscheduler.h> 41 #include <ksignal.h> 42 #include <Notifications.h> 43 #include <port.h> 44 #include <posix/realtime_sem.h> 45 #include <posix/xsi_semaphore.h> 46 #include <safemode.h> 47 #include <sem.h> 48 #include <syscall_process_info.h> 49 #include <syscall_load_image.h> 50 #include <syscall_restart.h> 51 #include <syscalls.h> 52 #include <tls.h> 53 #include <tracing.h> 54 #include <user_runtime.h> 55 #include <user_thread.h> 56 #include <usergroup.h> 57 #include <vfs.h> 58 #include <vm/vm.h> 59 #include <vm/VMAddressSpace.h> 60 #include <util/AutoLock.h> 61 62 #include "TeamThreadTables.h" 63 64 65 //#define TRACE_TEAM 66 #ifdef TRACE_TEAM 67 # define TRACE(x) dprintf x 68 #else 69 # define TRACE(x) ; 70 #endif 71 72 73 struct team_key { 74 team_id id; 75 }; 76 77 struct team_arg { 78 char *path; 79 char **flat_args; 80 size_t flat_args_size; 81 uint32 arg_count; 82 uint32 env_count; 83 mode_t umask; 84 uint32 flags; 85 port_id error_port; 86 uint32 error_token; 87 }; 88 89 #define TEAM_ARGS_FLAG_NO_ASLR 0x01 90 91 92 namespace { 93 94 95 class TeamNotificationService : public DefaultNotificationService { 96 public: 97 TeamNotificationService(); 98 99 void Notify(uint32 eventCode, Team* team); 100 }; 101 102 103 // #pragma mark - TeamTable 104 105 106 typedef BKernel::TeamThreadTable<Team> TeamTable; 107 108 109 // #pragma mark - ProcessGroupHashDefinition 110 111 112 struct ProcessGroupHashDefinition { 113 typedef pid_t KeyType; 114 typedef ProcessGroup ValueType; 115 116 size_t HashKey(pid_t key) const 117 { 118 return key; 119 } 120 121 size_t Hash(ProcessGroup* value) const 122 { 123 return HashKey(value->id); 124 } 125 126 bool Compare(pid_t key, ProcessGroup* value) const 127 { 128 return value->id == key; 129 } 130 131 ProcessGroup*& GetLink(ProcessGroup* value) const 132 { 133 return value->next; 134 } 135 }; 136 137 typedef BOpenHashTable<ProcessGroupHashDefinition> ProcessGroupHashTable; 138 139 140 } // unnamed namespace 141 142 143 // #pragma mark - 144 145 146 // the team_id -> Team hash table and the lock protecting it 147 static TeamTable sTeamHash; 148 static rw_spinlock sTeamHashLock = B_RW_SPINLOCK_INITIALIZER; 149 150 // the pid_t -> ProcessGroup hash table and the lock protecting it 151 static ProcessGroupHashTable sGroupHash; 152 static spinlock sGroupHashLock = B_SPINLOCK_INITIALIZER; 153 154 static Team* sKernelTeam = NULL; 155 static bool sDisableUserAddOns = false; 156 157 // A list of process groups of children of dying session leaders that need to 158 // be signalled, if they have become orphaned and contain stopped processes. 159 static ProcessGroupList sOrphanedCheckProcessGroups; 160 static mutex sOrphanedCheckLock 161 = MUTEX_INITIALIZER("orphaned process group check"); 162 163 // some arbitrarily chosen limits -- should probably depend on the available 164 // memory (the limit is not yet enforced) 165 static int32 sMaxTeams = 2048; 166 static int32 sUsedTeams = 1; 167 168 static TeamNotificationService sNotificationService; 169 170 static const size_t kTeamUserDataReservedSize = 128 * B_PAGE_SIZE; 171 static const size_t kTeamUserDataInitialSize = 4 * B_PAGE_SIZE; 172 173 174 // #pragma mark - TeamListIterator 175 176 177 TeamListIterator::TeamListIterator() 178 { 179 // queue the entry 180 InterruptsWriteSpinLocker locker(sTeamHashLock); 181 sTeamHash.InsertIteratorEntry(&fEntry); 182 } 183 184 185 TeamListIterator::~TeamListIterator() 186 { 187 // remove the entry 188 InterruptsWriteSpinLocker locker(sTeamHashLock); 189 sTeamHash.RemoveIteratorEntry(&fEntry); 190 } 191 192 193 Team* 194 TeamListIterator::Next() 195 { 196 // get the next team -- if there is one, get reference for it 197 InterruptsWriteSpinLocker locker(sTeamHashLock); 198 Team* team = sTeamHash.NextElement(&fEntry); 199 if (team != NULL) 200 team->AcquireReference(); 201 202 return team; 203 } 204 205 206 // #pragma mark - Tracing 207 208 209 #if TEAM_TRACING 210 namespace TeamTracing { 211 212 class TeamForked : public AbstractTraceEntry { 213 public: 214 TeamForked(thread_id forkedThread) 215 : 216 fForkedThread(forkedThread) 217 { 218 Initialized(); 219 } 220 221 virtual void AddDump(TraceOutput& out) 222 { 223 out.Print("team forked, new thread %" B_PRId32, fForkedThread); 224 } 225 226 private: 227 thread_id fForkedThread; 228 }; 229 230 231 class ExecTeam : public AbstractTraceEntry { 232 public: 233 ExecTeam(const char* path, int32 argCount, const char* const* args, 234 int32 envCount, const char* const* env) 235 : 236 fArgCount(argCount), 237 fArgs(NULL) 238 { 239 fPath = alloc_tracing_buffer_strcpy(path, B_PATH_NAME_LENGTH, 240 false); 241 242 // determine the buffer size we need for the args 243 size_t argBufferSize = 0; 244 for (int32 i = 0; i < argCount; i++) 245 argBufferSize += strlen(args[i]) + 1; 246 247 // allocate a buffer 248 fArgs = (char*)alloc_tracing_buffer(argBufferSize); 249 if (fArgs) { 250 char* buffer = fArgs; 251 for (int32 i = 0; i < argCount; i++) { 252 size_t argSize = strlen(args[i]) + 1; 253 memcpy(buffer, args[i], argSize); 254 buffer += argSize; 255 } 256 } 257 258 // ignore env for the time being 259 (void)envCount; 260 (void)env; 261 262 Initialized(); 263 } 264 265 virtual void AddDump(TraceOutput& out) 266 { 267 out.Print("team exec, \"%p\", args:", fPath); 268 269 if (fArgs != NULL) { 270 char* args = fArgs; 271 for (int32 i = 0; !out.IsFull() && i < fArgCount; i++) { 272 out.Print(" \"%s\"", args); 273 args += strlen(args) + 1; 274 } 275 } else 276 out.Print(" <too long>"); 277 } 278 279 private: 280 char* fPath; 281 int32 fArgCount; 282 char* fArgs; 283 }; 284 285 286 static const char* 287 job_control_state_name(job_control_state state) 288 { 289 switch (state) { 290 case JOB_CONTROL_STATE_NONE: 291 return "none"; 292 case JOB_CONTROL_STATE_STOPPED: 293 return "stopped"; 294 case JOB_CONTROL_STATE_CONTINUED: 295 return "continued"; 296 case JOB_CONTROL_STATE_DEAD: 297 return "dead"; 298 default: 299 return "invalid"; 300 } 301 } 302 303 304 class SetJobControlState : public AbstractTraceEntry { 305 public: 306 SetJobControlState(team_id team, job_control_state newState, Signal* signal) 307 : 308 fTeam(team), 309 fNewState(newState), 310 fSignal(signal != NULL ? signal->Number() : 0) 311 { 312 Initialized(); 313 } 314 315 virtual void AddDump(TraceOutput& out) 316 { 317 out.Print("team set job control state, team %" B_PRId32 ", " 318 "new state: %s, signal: %d", 319 fTeam, job_control_state_name(fNewState), fSignal); 320 } 321 322 private: 323 team_id fTeam; 324 job_control_state fNewState; 325 int fSignal; 326 }; 327 328 329 class WaitForChild : public AbstractTraceEntry { 330 public: 331 WaitForChild(pid_t child, uint32 flags) 332 : 333 fChild(child), 334 fFlags(flags) 335 { 336 Initialized(); 337 } 338 339 virtual void AddDump(TraceOutput& out) 340 { 341 out.Print("team wait for child, child: %" B_PRId32 ", " 342 "flags: %#" B_PRIx32, fChild, fFlags); 343 } 344 345 private: 346 pid_t fChild; 347 uint32 fFlags; 348 }; 349 350 351 class WaitForChildDone : public AbstractTraceEntry { 352 public: 353 WaitForChildDone(const job_control_entry& entry) 354 : 355 fState(entry.state), 356 fTeam(entry.thread), 357 fStatus(entry.status), 358 fReason(entry.reason), 359 fSignal(entry.signal) 360 { 361 Initialized(); 362 } 363 364 WaitForChildDone(status_t error) 365 : 366 fTeam(error) 367 { 368 Initialized(); 369 } 370 371 virtual void AddDump(TraceOutput& out) 372 { 373 if (fTeam >= 0) { 374 out.Print("team wait for child done, team: %" B_PRId32 ", " 375 "state: %s, status: %#" B_PRIx32 ", reason: %#x, signal: %d\n", 376 fTeam, job_control_state_name(fState), fStatus, fReason, 377 fSignal); 378 } else { 379 out.Print("team wait for child failed, error: " 380 "%#" B_PRIx32 ", ", fTeam); 381 } 382 } 383 384 private: 385 job_control_state fState; 386 team_id fTeam; 387 status_t fStatus; 388 uint16 fReason; 389 uint16 fSignal; 390 }; 391 392 } // namespace TeamTracing 393 394 # define T(x) new(std::nothrow) TeamTracing::x; 395 #else 396 # define T(x) ; 397 #endif 398 399 400 // #pragma mark - TeamNotificationService 401 402 403 TeamNotificationService::TeamNotificationService() 404 : DefaultNotificationService("teams") 405 { 406 } 407 408 409 void 410 TeamNotificationService::Notify(uint32 eventCode, Team* team) 411 { 412 char eventBuffer[128]; 413 KMessage event; 414 event.SetTo(eventBuffer, sizeof(eventBuffer), TEAM_MONITOR); 415 event.AddInt32("event", eventCode); 416 event.AddInt32("team", team->id); 417 event.AddPointer("teamStruct", team); 418 419 DefaultNotificationService::Notify(event, eventCode); 420 } 421 422 423 // #pragma mark - Team 424 425 426 Team::Team(team_id id, bool kernel) 427 { 428 // allocate an ID 429 this->id = id; 430 visible = true; 431 serial_number = -1; 432 433 // init mutex 434 if (kernel) { 435 mutex_init(&fLock, "Team:kernel"); 436 } else { 437 char lockName[16]; 438 snprintf(lockName, sizeof(lockName), "Team:%" B_PRId32, id); 439 mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME); 440 } 441 442 hash_next = siblings_next = children = parent = NULL; 443 fName[0] = '\0'; 444 fArgs[0] = '\0'; 445 num_threads = 0; 446 io_context = NULL; 447 address_space = NULL; 448 realtime_sem_context = NULL; 449 xsi_sem_context = NULL; 450 thread_list = NULL; 451 main_thread = NULL; 452 loading_info = NULL; 453 state = TEAM_STATE_BIRTH; 454 flags = 0; 455 death_entry = NULL; 456 user_data_area = -1; 457 user_data = 0; 458 used_user_data = 0; 459 user_data_size = 0; 460 free_user_threads = NULL; 461 462 commpage_address = NULL; 463 464 supplementary_groups = NULL; 465 supplementary_group_count = 0; 466 467 dead_threads_kernel_time = 0; 468 dead_threads_user_time = 0; 469 cpu_clock_offset = 0; 470 471 // dead threads 472 list_init(&dead_threads); 473 dead_threads_count = 0; 474 475 // dead children 476 dead_children.count = 0; 477 dead_children.kernel_time = 0; 478 dead_children.user_time = 0; 479 480 // job control entry 481 job_control_entry = new(nothrow) ::job_control_entry; 482 if (job_control_entry != NULL) { 483 job_control_entry->state = JOB_CONTROL_STATE_NONE; 484 job_control_entry->thread = id; 485 job_control_entry->team = this; 486 } 487 488 // exit status -- setting initialized to false suffices 489 exit.initialized = false; 490 491 list_init(&sem_list); 492 list_init_etc(&port_list, port_team_link_offset()); 493 list_init(&image_list); 494 list_init(&watcher_list); 495 496 clear_team_debug_info(&debug_info, true); 497 498 // init dead/stopped/continued children condition vars 499 dead_children.condition_variable.Init(&dead_children, "team children"); 500 501 B_INITIALIZE_SPINLOCK(&time_lock); 502 B_INITIALIZE_SPINLOCK(&signal_lock); 503 504 fQueuedSignalsCounter = new(std::nothrow) BKernel::QueuedSignalsCounter( 505 kernel ? -1 : MAX_QUEUED_SIGNALS); 506 memset(fSignalActions, 0, sizeof(fSignalActions)); 507 508 fUserDefinedTimerCount = 0; 509 510 fCoreDumpCondition = NULL; 511 } 512 513 514 Team::~Team() 515 { 516 // get rid of all associated data 517 PrepareForDeletion(); 518 519 if (io_context != NULL) 520 vfs_put_io_context(io_context); 521 delete_owned_ports(this); 522 sem_delete_owned_sems(this); 523 524 DeleteUserTimers(false); 525 526 fPendingSignals.Clear(); 527 528 if (fQueuedSignalsCounter != NULL) 529 fQueuedSignalsCounter->ReleaseReference(); 530 531 while (thread_death_entry* threadDeathEntry 532 = (thread_death_entry*)list_remove_head_item(&dead_threads)) { 533 free(threadDeathEntry); 534 } 535 536 while (::job_control_entry* entry = dead_children.entries.RemoveHead()) 537 delete entry; 538 539 while (free_user_thread* entry = free_user_threads) { 540 free_user_threads = entry->next; 541 free(entry); 542 } 543 544 malloc_referenced_release(supplementary_groups); 545 546 delete job_control_entry; 547 // usually already NULL and transferred to the parent 548 549 mutex_destroy(&fLock); 550 } 551 552 553 /*static*/ Team* 554 Team::Create(team_id id, const char* name, bool kernel) 555 { 556 // create the team object 557 Team* team = new(std::nothrow) Team(id, kernel); 558 if (team == NULL) 559 return NULL; 560 ObjectDeleter<Team> teamDeleter(team); 561 562 if (name != NULL) 563 team->SetName(name); 564 565 // check initialization 566 if (team->job_control_entry == NULL || team->fQueuedSignalsCounter == NULL) 567 return NULL; 568 569 // finish initialization (arch specifics) 570 if (arch_team_init_team_struct(team, kernel) != B_OK) 571 return NULL; 572 573 if (!kernel) { 574 status_t error = user_timer_create_team_timers(team); 575 if (error != B_OK) 576 return NULL; 577 } 578 579 // everything went fine 580 return teamDeleter.Detach(); 581 } 582 583 584 /*! \brief Returns the team with the given ID. 585 Returns a reference to the team. 586 Team and thread spinlock must not be held. 587 */ 588 /*static*/ Team* 589 Team::Get(team_id id) 590 { 591 if (id == B_CURRENT_TEAM) { 592 Team* team = thread_get_current_thread()->team; 593 team->AcquireReference(); 594 return team; 595 } 596 597 InterruptsReadSpinLocker locker(sTeamHashLock); 598 Team* team = sTeamHash.Lookup(id); 599 if (team != NULL) 600 team->AcquireReference(); 601 return team; 602 } 603 604 605 /*! \brief Returns the team with the given ID in a locked state. 606 Returns a reference to the team. 607 Team and thread spinlock must not be held. 608 */ 609 /*static*/ Team* 610 Team::GetAndLock(team_id id) 611 { 612 // get the team 613 Team* team = Get(id); 614 if (team == NULL) 615 return NULL; 616 617 // lock it 618 team->Lock(); 619 620 // only return the team, when it isn't already dying 621 if (team->state >= TEAM_STATE_SHUTDOWN) { 622 team->Unlock(); 623 team->ReleaseReference(); 624 return NULL; 625 } 626 627 return team; 628 } 629 630 631 /*! Locks the team and its parent team (if any). 632 The caller must hold a reference to the team or otherwise make sure that 633 it won't be deleted. 634 If the team doesn't have a parent, only the team itself is locked. If the 635 team's parent is the kernel team and \a dontLockParentIfKernel is \c true, 636 only the team itself is locked. 637 638 \param dontLockParentIfKernel If \c true, the team's parent team is only 639 locked, if it is not the kernel team. 640 */ 641 void 642 Team::LockTeamAndParent(bool dontLockParentIfKernel) 643 { 644 // The locking order is parent -> child. Since the parent can change as long 645 // as we don't lock the team, we need to do a trial and error loop. 646 Lock(); 647 648 while (true) { 649 // If the team doesn't have a parent, we're done. Otherwise try to lock 650 // the parent.This will succeed in most cases, simplifying things. 651 Team* parent = this->parent; 652 if (parent == NULL || (dontLockParentIfKernel && parent == sKernelTeam) 653 || parent->TryLock()) { 654 return; 655 } 656 657 // get a temporary reference to the parent, unlock this team, lock the 658 // parent, and re-lock this team 659 BReference<Team> parentReference(parent); 660 661 Unlock(); 662 parent->Lock(); 663 Lock(); 664 665 // If the parent hasn't changed in the meantime, we're done. 666 if (this->parent == parent) 667 return; 668 669 // The parent has changed -- unlock and retry. 670 parent->Unlock(); 671 } 672 } 673 674 675 /*! Unlocks the team and its parent team (if any). 676 */ 677 void 678 Team::UnlockTeamAndParent() 679 { 680 if (parent != NULL) 681 parent->Unlock(); 682 683 Unlock(); 684 } 685 686 687 /*! Locks the team, its parent team (if any), and the team's process group. 688 The caller must hold a reference to the team or otherwise make sure that 689 it won't be deleted. 690 If the team doesn't have a parent, only the team itself is locked. 691 */ 692 void 693 Team::LockTeamParentAndProcessGroup() 694 { 695 LockTeamAndProcessGroup(); 696 697 // We hold the group's and the team's lock, but not the parent team's lock. 698 // If we have a parent, try to lock it. 699 if (this->parent == NULL || this->parent->TryLock()) 700 return; 701 702 // No success -- unlock the team and let LockTeamAndParent() do the rest of 703 // the job. 704 Unlock(); 705 LockTeamAndParent(false); 706 } 707 708 709 /*! Unlocks the team, its parent team (if any), and the team's process group. 710 */ 711 void 712 Team::UnlockTeamParentAndProcessGroup() 713 { 714 group->Unlock(); 715 716 if (parent != NULL) 717 parent->Unlock(); 718 719 Unlock(); 720 } 721 722 723 void 724 Team::LockTeamAndProcessGroup() 725 { 726 // The locking order is process group -> child. Since the process group can 727 // change as long as we don't lock the team, we need to do a trial and error 728 // loop. 729 Lock(); 730 731 while (true) { 732 // Try to lock the group. This will succeed in most cases, simplifying 733 // things. 734 ProcessGroup* group = this->group; 735 if (group->TryLock()) 736 return; 737 738 // get a temporary reference to the group, unlock this team, lock the 739 // group, and re-lock this team 740 BReference<ProcessGroup> groupReference(group); 741 742 Unlock(); 743 group->Lock(); 744 Lock(); 745 746 // If the group hasn't changed in the meantime, we're done. 747 if (this->group == group) 748 return; 749 750 // The group has changed -- unlock and retry. 751 group->Unlock(); 752 } 753 } 754 755 756 void 757 Team::UnlockTeamAndProcessGroup() 758 { 759 group->Unlock(); 760 Unlock(); 761 } 762 763 764 void 765 Team::SetName(const char* name) 766 { 767 if (const char* lastSlash = strrchr(name, '/')) 768 name = lastSlash + 1; 769 770 strlcpy(fName, name, B_OS_NAME_LENGTH); 771 } 772 773 774 void 775 Team::SetArgs(const char* args) 776 { 777 strlcpy(fArgs, args, sizeof(fArgs)); 778 } 779 780 781 void 782 Team::SetArgs(const char* path, const char* const* otherArgs, int otherArgCount) 783 { 784 fArgs[0] = '\0'; 785 strlcpy(fArgs, path, sizeof(fArgs)); 786 for (int i = 0; i < otherArgCount; i++) { 787 strlcat(fArgs, " ", sizeof(fArgs)); 788 strlcat(fArgs, otherArgs[i], sizeof(fArgs)); 789 } 790 } 791 792 793 void 794 Team::ResetSignalsOnExec() 795 { 796 // We are supposed to keep pending signals. Signal actions shall be reset 797 // partially: SIG_IGN and SIG_DFL dispositions shall be kept as they are 798 // (for SIGCHLD it's implementation-defined). Others shall be reset to 799 // SIG_DFL. SA_ONSTACK shall be cleared. There's no mention of the other 800 // flags, but since there aren't any handlers, they make little sense, so 801 // we clear them. 802 803 for (uint32 i = 1; i <= MAX_SIGNAL_NUMBER; i++) { 804 struct sigaction& action = SignalActionFor(i); 805 if (action.sa_handler != SIG_IGN && action.sa_handler != SIG_DFL) 806 action.sa_handler = SIG_DFL; 807 808 action.sa_mask = 0; 809 action.sa_flags = 0; 810 action.sa_userdata = NULL; 811 } 812 } 813 814 815 void 816 Team::InheritSignalActions(Team* parent) 817 { 818 memcpy(fSignalActions, parent->fSignalActions, sizeof(fSignalActions)); 819 } 820 821 822 /*! Adds the given user timer to the team and, if user-defined, assigns it an 823 ID. 824 825 The caller must hold the team's lock. 826 827 \param timer The timer to be added. If it doesn't have an ID yet, it is 828 considered user-defined and will be assigned an ID. 829 \return \c B_OK, if the timer was added successfully, another error code 830 otherwise. 831 */ 832 status_t 833 Team::AddUserTimer(UserTimer* timer) 834 { 835 // don't allow addition of timers when already shutting the team down 836 if (state >= TEAM_STATE_SHUTDOWN) 837 return B_BAD_TEAM_ID; 838 839 // If the timer is user-defined, check timer limit and increment 840 // user-defined count. 841 if (timer->ID() < 0 && !CheckAddUserDefinedTimer()) 842 return EAGAIN; 843 844 fUserTimers.AddTimer(timer); 845 846 return B_OK; 847 } 848 849 850 /*! Removes the given user timer from the team. 851 852 The caller must hold the team's lock. 853 854 \param timer The timer to be removed. 855 856 */ 857 void 858 Team::RemoveUserTimer(UserTimer* timer) 859 { 860 fUserTimers.RemoveTimer(timer); 861 862 if (timer->ID() >= USER_TIMER_FIRST_USER_DEFINED_ID) 863 UserDefinedTimersRemoved(1); 864 } 865 866 867 /*! Deletes all (or all user-defined) user timers of the team. 868 869 Timer's belonging to the team's threads are not affected. 870 The caller must hold the team's lock. 871 872 \param userDefinedOnly If \c true, only the user-defined timers are deleted, 873 otherwise all timers are deleted. 874 */ 875 void 876 Team::DeleteUserTimers(bool userDefinedOnly) 877 { 878 int32 count = fUserTimers.DeleteTimers(userDefinedOnly); 879 UserDefinedTimersRemoved(count); 880 } 881 882 883 /*! If not at the limit yet, increments the team's user-defined timer count. 884 \return \c true, if the limit wasn't reached yet, \c false otherwise. 885 */ 886 bool 887 Team::CheckAddUserDefinedTimer() 888 { 889 int32 oldCount = atomic_add(&fUserDefinedTimerCount, 1); 890 if (oldCount >= MAX_USER_TIMERS_PER_TEAM) { 891 atomic_add(&fUserDefinedTimerCount, -1); 892 return false; 893 } 894 895 return true; 896 } 897 898 899 /*! Subtracts the given count for the team's user-defined timer count. 900 \param count The count to subtract. 901 */ 902 void 903 Team::UserDefinedTimersRemoved(int32 count) 904 { 905 atomic_add(&fUserDefinedTimerCount, -count); 906 } 907 908 909 void 910 Team::DeactivateCPUTimeUserTimers() 911 { 912 while (TeamTimeUserTimer* timer = fCPUTimeUserTimers.Head()) 913 timer->Deactivate(); 914 915 while (TeamUserTimeUserTimer* timer = fUserTimeUserTimers.Head()) 916 timer->Deactivate(); 917 } 918 919 920 /*! Returns the team's current total CPU time (kernel + user + offset). 921 922 The caller must hold \c time_lock. 923 924 \param ignoreCurrentRun If \c true and the current thread is one team's 925 threads, don't add the time since the last time \c last_time was 926 updated. Should be used in "thread unscheduled" scheduler callbacks, 927 since although the thread is still running at that time, its time has 928 already been stopped. 929 \return The team's current total CPU time. 930 */ 931 bigtime_t 932 Team::CPUTime(bool ignoreCurrentRun, Thread* lockedThread) const 933 { 934 bigtime_t time = cpu_clock_offset + dead_threads_kernel_time 935 + dead_threads_user_time; 936 937 Thread* currentThread = thread_get_current_thread(); 938 bigtime_t now = system_time(); 939 940 for (Thread* thread = thread_list; thread != NULL; 941 thread = thread->team_next) { 942 bool alreadyLocked = thread == lockedThread; 943 SpinLocker threadTimeLocker(thread->time_lock, alreadyLocked); 944 time += thread->kernel_time + thread->user_time; 945 946 if (thread->last_time != 0) { 947 if (!ignoreCurrentRun || thread != currentThread) 948 time += now - thread->last_time; 949 } 950 951 if (alreadyLocked) 952 threadTimeLocker.Detach(); 953 } 954 955 return time; 956 } 957 958 959 /*! Returns the team's current user CPU time. 960 961 The caller must hold \c time_lock. 962 963 \return The team's current user CPU time. 964 */ 965 bigtime_t 966 Team::UserCPUTime() const 967 { 968 bigtime_t time = dead_threads_user_time; 969 970 bigtime_t now = system_time(); 971 972 for (Thread* thread = thread_list; thread != NULL; 973 thread = thread->team_next) { 974 SpinLocker threadTimeLocker(thread->time_lock); 975 time += thread->user_time; 976 977 if (thread->last_time != 0 && !thread->in_kernel) 978 time += now - thread->last_time; 979 } 980 981 return time; 982 } 983 984 985 // #pragma mark - ProcessGroup 986 987 988 ProcessGroup::ProcessGroup(pid_t id) 989 : 990 id(id), 991 teams(NULL), 992 fSession(NULL), 993 fInOrphanedCheckList(false) 994 { 995 char lockName[32]; 996 snprintf(lockName, sizeof(lockName), "Group:%" B_PRId32, id); 997 mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME); 998 } 999 1000 1001 ProcessGroup::~ProcessGroup() 1002 { 1003 TRACE(("ProcessGroup::~ProcessGroup(): id = %" B_PRId32 "\n", id)); 1004 1005 // If the group is in the orphaned check list, remove it. 1006 MutexLocker orphanedCheckLocker(sOrphanedCheckLock); 1007 1008 if (fInOrphanedCheckList) 1009 sOrphanedCheckProcessGroups.Remove(this); 1010 1011 orphanedCheckLocker.Unlock(); 1012 1013 // remove group from the hash table and from the session 1014 if (fSession != NULL) { 1015 InterruptsSpinLocker groupHashLocker(sGroupHashLock); 1016 sGroupHash.RemoveUnchecked(this); 1017 groupHashLocker.Unlock(); 1018 1019 fSession->ReleaseReference(); 1020 } 1021 1022 mutex_destroy(&fLock); 1023 } 1024 1025 1026 /*static*/ ProcessGroup* 1027 ProcessGroup::Get(pid_t id) 1028 { 1029 InterruptsSpinLocker groupHashLocker(sGroupHashLock); 1030 ProcessGroup* group = sGroupHash.Lookup(id); 1031 if (group != NULL) 1032 group->AcquireReference(); 1033 return group; 1034 } 1035 1036 1037 /*! Adds the group the given session and makes it publicly accessible. 1038 The caller must not hold the process group hash lock. 1039 */ 1040 void 1041 ProcessGroup::Publish(ProcessSession* session) 1042 { 1043 InterruptsSpinLocker groupHashLocker(sGroupHashLock); 1044 PublishLocked(session); 1045 } 1046 1047 1048 /*! Adds the group to the given session and makes it publicly accessible. 1049 The caller must hold the process group hash lock. 1050 */ 1051 void 1052 ProcessGroup::PublishLocked(ProcessSession* session) 1053 { 1054 ASSERT(sGroupHash.Lookup(this->id) == NULL); 1055 1056 fSession = session; 1057 fSession->AcquireReference(); 1058 1059 sGroupHash.InsertUnchecked(this); 1060 } 1061 1062 1063 /*! Checks whether the process group is orphaned. 1064 The caller must hold the group's lock. 1065 \return \c true, if the group is orphaned, \c false otherwise. 1066 */ 1067 bool 1068 ProcessGroup::IsOrphaned() const 1069 { 1070 // Orphaned Process Group: "A process group in which the parent of every 1071 // member is either itself a member of the group or is not a member of the 1072 // group's session." (Open Group Base Specs Issue 7) 1073 bool orphaned = true; 1074 1075 Team* team = teams; 1076 while (orphaned && team != NULL) { 1077 team->LockTeamAndParent(false); 1078 1079 Team* parent = team->parent; 1080 if (parent != NULL && parent->group_id != id 1081 && parent->session_id == fSession->id) { 1082 orphaned = false; 1083 } 1084 1085 team->UnlockTeamAndParent(); 1086 1087 team = team->group_next; 1088 } 1089 1090 return orphaned; 1091 } 1092 1093 1094 void 1095 ProcessGroup::ScheduleOrphanedCheck() 1096 { 1097 MutexLocker orphanedCheckLocker(sOrphanedCheckLock); 1098 1099 if (!fInOrphanedCheckList) { 1100 sOrphanedCheckProcessGroups.Add(this); 1101 fInOrphanedCheckList = true; 1102 } 1103 } 1104 1105 1106 void 1107 ProcessGroup::UnsetOrphanedCheck() 1108 { 1109 fInOrphanedCheckList = false; 1110 } 1111 1112 1113 // #pragma mark - ProcessSession 1114 1115 1116 ProcessSession::ProcessSession(pid_t id) 1117 : 1118 id(id), 1119 controlling_tty(-1), 1120 foreground_group(-1) 1121 { 1122 char lockName[32]; 1123 snprintf(lockName, sizeof(lockName), "Session:%" B_PRId32, id); 1124 mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME); 1125 } 1126 1127 1128 ProcessSession::~ProcessSession() 1129 { 1130 mutex_destroy(&fLock); 1131 } 1132 1133 1134 // #pragma mark - KDL functions 1135 1136 1137 static void 1138 _dump_team_info(Team* team) 1139 { 1140 kprintf("TEAM: %p\n", team); 1141 kprintf("id: %" B_PRId32 " (%#" B_PRIx32 ")\n", team->id, 1142 team->id); 1143 kprintf("serial_number: %" B_PRId64 "\n", team->serial_number); 1144 kprintf("name: '%s'\n", team->Name()); 1145 kprintf("args: '%s'\n", team->Args()); 1146 kprintf("hash_next: %p\n", team->hash_next); 1147 kprintf("parent: %p", team->parent); 1148 if (team->parent != NULL) { 1149 kprintf(" (id = %" B_PRId32 ")\n", team->parent->id); 1150 } else 1151 kprintf("\n"); 1152 1153 kprintf("children: %p\n", team->children); 1154 kprintf("num_threads: %d\n", team->num_threads); 1155 kprintf("state: %d\n", team->state); 1156 kprintf("flags: 0x%" B_PRIx32 "\n", team->flags); 1157 kprintf("io_context: %p\n", team->io_context); 1158 if (team->address_space) 1159 kprintf("address_space: %p\n", team->address_space); 1160 kprintf("user data: %p (area %" B_PRId32 ")\n", 1161 (void*)team->user_data, team->user_data_area); 1162 kprintf("free user thread: %p\n", team->free_user_threads); 1163 kprintf("main_thread: %p\n", team->main_thread); 1164 kprintf("thread_list: %p\n", team->thread_list); 1165 kprintf("group_id: %" B_PRId32 "\n", team->group_id); 1166 kprintf("session_id: %" B_PRId32 "\n", team->session_id); 1167 } 1168 1169 1170 static int 1171 dump_team_info(int argc, char** argv) 1172 { 1173 ulong arg; 1174 bool found = false; 1175 1176 if (argc < 2) { 1177 Thread* thread = thread_get_current_thread(); 1178 if (thread != NULL && thread->team != NULL) 1179 _dump_team_info(thread->team); 1180 else 1181 kprintf("No current team!\n"); 1182 return 0; 1183 } 1184 1185 arg = strtoul(argv[1], NULL, 0); 1186 if (IS_KERNEL_ADDRESS(arg)) { 1187 // semi-hack 1188 _dump_team_info((Team*)arg); 1189 return 0; 1190 } 1191 1192 // walk through the thread list, trying to match name or id 1193 for (TeamTable::Iterator it = sTeamHash.GetIterator(); 1194 Team* team = it.Next();) { 1195 if ((team->Name() && strcmp(argv[1], team->Name()) == 0) 1196 || team->id == (team_id)arg) { 1197 _dump_team_info(team); 1198 found = true; 1199 break; 1200 } 1201 } 1202 1203 if (!found) 1204 kprintf("team \"%s\" (%" B_PRId32 ") doesn't exist!\n", argv[1], (team_id)arg); 1205 return 0; 1206 } 1207 1208 1209 static int 1210 dump_teams(int argc, char** argv) 1211 { 1212 kprintf("%-*s id %-*s name\n", B_PRINTF_POINTER_WIDTH, "team", 1213 B_PRINTF_POINTER_WIDTH, "parent"); 1214 1215 for (TeamTable::Iterator it = sTeamHash.GetIterator(); 1216 Team* team = it.Next();) { 1217 kprintf("%p%7" B_PRId32 " %p %s\n", team, team->id, team->parent, team->Name()); 1218 } 1219 1220 return 0; 1221 } 1222 1223 1224 // #pragma mark - Private functions 1225 1226 1227 /*! Inserts team \a team into the child list of team \a parent. 1228 1229 The caller must hold the lock of both \a parent and \a team. 1230 1231 \param parent The parent team. 1232 \param team The team to be inserted into \a parent's child list. 1233 */ 1234 static void 1235 insert_team_into_parent(Team* parent, Team* team) 1236 { 1237 ASSERT(parent != NULL); 1238 1239 team->siblings_next = parent->children; 1240 parent->children = team; 1241 team->parent = parent; 1242 } 1243 1244 1245 /*! Removes team \a team from the child list of team \a parent. 1246 1247 The caller must hold the lock of both \a parent and \a team. 1248 1249 \param parent The parent team. 1250 \param team The team to be removed from \a parent's child list. 1251 */ 1252 static void 1253 remove_team_from_parent(Team* parent, Team* team) 1254 { 1255 Team* child; 1256 Team* last = NULL; 1257 1258 for (child = parent->children; child != NULL; 1259 child = child->siblings_next) { 1260 if (child == team) { 1261 if (last == NULL) 1262 parent->children = child->siblings_next; 1263 else 1264 last->siblings_next = child->siblings_next; 1265 1266 team->parent = NULL; 1267 break; 1268 } 1269 last = child; 1270 } 1271 } 1272 1273 1274 /*! Returns whether the given team is a session leader. 1275 The caller must hold the team's lock or its process group's lock. 1276 */ 1277 static bool 1278 is_session_leader(Team* team) 1279 { 1280 return team->session_id == team->id; 1281 } 1282 1283 1284 /*! Returns whether the given team is a process group leader. 1285 The caller must hold the team's lock or its process group's lock. 1286 */ 1287 static bool 1288 is_process_group_leader(Team* team) 1289 { 1290 return team->group_id == team->id; 1291 } 1292 1293 1294 /*! Inserts the given team into the given process group. 1295 The caller must hold the process group's lock, the team's lock, and the 1296 team's parent's lock. 1297 */ 1298 static void 1299 insert_team_into_group(ProcessGroup* group, Team* team) 1300 { 1301 team->group = group; 1302 team->group_id = group->id; 1303 team->session_id = group->Session()->id; 1304 1305 team->group_next = group->teams; 1306 group->teams = team; 1307 group->AcquireReference(); 1308 } 1309 1310 1311 /*! Removes the given team from its process group. 1312 1313 The caller must hold the process group's lock, the team's lock, and the 1314 team's parent's lock. Interrupts must be enabled. 1315 1316 \param team The team that'll be removed from its process group. 1317 */ 1318 static void 1319 remove_team_from_group(Team* team) 1320 { 1321 ProcessGroup* group = team->group; 1322 Team* current; 1323 Team* last = NULL; 1324 1325 // the team must be in a process group to let this function have any effect 1326 if (group == NULL) 1327 return; 1328 1329 for (current = group->teams; current != NULL; 1330 current = current->group_next) { 1331 if (current == team) { 1332 if (last == NULL) 1333 group->teams = current->group_next; 1334 else 1335 last->group_next = current->group_next; 1336 1337 team->group = NULL; 1338 break; 1339 } 1340 last = current; 1341 } 1342 1343 team->group = NULL; 1344 team->group_next = NULL; 1345 1346 group->ReleaseReference(); 1347 } 1348 1349 1350 static status_t 1351 create_team_user_data(Team* team, void* exactAddress = NULL) 1352 { 1353 void* address; 1354 uint32 addressSpec; 1355 1356 if (exactAddress != NULL) { 1357 address = exactAddress; 1358 addressSpec = B_EXACT_ADDRESS; 1359 } else { 1360 address = (void*)KERNEL_USER_DATA_BASE; 1361 addressSpec = B_RANDOMIZED_BASE_ADDRESS; 1362 } 1363 1364 status_t result = vm_reserve_address_range(team->id, &address, addressSpec, 1365 kTeamUserDataReservedSize, RESERVED_AVOID_BASE); 1366 1367 virtual_address_restrictions virtualRestrictions = {}; 1368 if (result == B_OK || exactAddress != NULL) { 1369 if (exactAddress != NULL) 1370 virtualRestrictions.address = exactAddress; 1371 else 1372 virtualRestrictions.address = address; 1373 virtualRestrictions.address_specification = B_EXACT_ADDRESS; 1374 } else { 1375 virtualRestrictions.address = (void*)KERNEL_USER_DATA_BASE; 1376 virtualRestrictions.address_specification = B_RANDOMIZED_BASE_ADDRESS; 1377 } 1378 1379 physical_address_restrictions physicalRestrictions = {}; 1380 team->user_data_area = create_area_etc(team->id, "user area", 1381 kTeamUserDataInitialSize, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA, 0, 0, 1382 &virtualRestrictions, &physicalRestrictions, &address); 1383 if (team->user_data_area < 0) 1384 return team->user_data_area; 1385 1386 team->user_data = (addr_t)address; 1387 team->used_user_data = 0; 1388 team->user_data_size = kTeamUserDataInitialSize; 1389 team->free_user_threads = NULL; 1390 1391 return B_OK; 1392 } 1393 1394 1395 static void 1396 delete_team_user_data(Team* team) 1397 { 1398 if (team->user_data_area >= 0) { 1399 vm_delete_area(team->id, team->user_data_area, true); 1400 vm_unreserve_address_range(team->id, (void*)team->user_data, 1401 kTeamUserDataReservedSize); 1402 1403 team->user_data = 0; 1404 team->used_user_data = 0; 1405 team->user_data_size = 0; 1406 team->user_data_area = -1; 1407 while (free_user_thread* entry = team->free_user_threads) { 1408 team->free_user_threads = entry->next; 1409 free(entry); 1410 } 1411 } 1412 } 1413 1414 1415 static status_t 1416 copy_user_process_args(const char* const* userFlatArgs, size_t flatArgsSize, 1417 int32 argCount, int32 envCount, char**& _flatArgs) 1418 { 1419 if (argCount < 0 || envCount < 0) 1420 return B_BAD_VALUE; 1421 1422 if (flatArgsSize > MAX_PROCESS_ARGS_SIZE) 1423 return B_TOO_MANY_ARGS; 1424 if ((argCount + envCount + 2) * sizeof(char*) > flatArgsSize) 1425 return B_BAD_VALUE; 1426 1427 if (!IS_USER_ADDRESS(userFlatArgs)) 1428 return B_BAD_ADDRESS; 1429 1430 // allocate kernel memory 1431 char** flatArgs = (char**)malloc(_ALIGN(flatArgsSize)); 1432 if (flatArgs == NULL) 1433 return B_NO_MEMORY; 1434 1435 if (user_memcpy(flatArgs, userFlatArgs, flatArgsSize) != B_OK) { 1436 free(flatArgs); 1437 return B_BAD_ADDRESS; 1438 } 1439 1440 // check and relocate the array 1441 status_t error = B_OK; 1442 const char* stringBase = (char*)flatArgs + argCount + envCount + 2; 1443 const char* stringEnd = (char*)flatArgs + flatArgsSize; 1444 for (int32 i = 0; i < argCount + envCount + 2; i++) { 1445 if (i == argCount || i == argCount + envCount + 1) { 1446 // check array null termination 1447 if (flatArgs[i] != NULL) { 1448 error = B_BAD_VALUE; 1449 break; 1450 } 1451 } else { 1452 // check string 1453 char* arg = (char*)flatArgs + (flatArgs[i] - (char*)userFlatArgs); 1454 size_t maxLen = stringEnd - arg; 1455 if (arg < stringBase || arg >= stringEnd 1456 || strnlen(arg, maxLen) == maxLen) { 1457 error = B_BAD_VALUE; 1458 break; 1459 } 1460 1461 flatArgs[i] = arg; 1462 } 1463 } 1464 1465 if (error == B_OK) 1466 _flatArgs = flatArgs; 1467 else 1468 free(flatArgs); 1469 1470 return error; 1471 } 1472 1473 1474 static void 1475 free_team_arg(struct team_arg* teamArg) 1476 { 1477 if (teamArg != NULL) { 1478 free(teamArg->flat_args); 1479 free(teamArg->path); 1480 free(teamArg); 1481 } 1482 } 1483 1484 1485 static status_t 1486 create_team_arg(struct team_arg** _teamArg, const char* path, char** flatArgs, 1487 size_t flatArgsSize, int32 argCount, int32 envCount, mode_t umask, 1488 port_id port, uint32 token) 1489 { 1490 struct team_arg* teamArg = (struct team_arg*)malloc(sizeof(team_arg)); 1491 if (teamArg == NULL) 1492 return B_NO_MEMORY; 1493 1494 teamArg->path = strdup(path); 1495 if (teamArg->path == NULL) { 1496 free(teamArg); 1497 return B_NO_MEMORY; 1498 } 1499 1500 // copy the args over 1501 teamArg->flat_args = flatArgs; 1502 teamArg->flat_args_size = flatArgsSize; 1503 teamArg->arg_count = argCount; 1504 teamArg->env_count = envCount; 1505 teamArg->flags = 0; 1506 teamArg->umask = umask; 1507 teamArg->error_port = port; 1508 teamArg->error_token = token; 1509 1510 // determine the flags from the environment 1511 const char* const* env = flatArgs + argCount + 1; 1512 for (int32 i = 0; i < envCount; i++) { 1513 if (strcmp(env[i], "DISABLE_ASLR=1") == 0) { 1514 teamArg->flags |= TEAM_ARGS_FLAG_NO_ASLR; 1515 break; 1516 } 1517 } 1518 1519 *_teamArg = teamArg; 1520 return B_OK; 1521 } 1522 1523 1524 static status_t 1525 team_create_thread_start_internal(void* args) 1526 { 1527 status_t err; 1528 Thread* thread; 1529 Team* team; 1530 struct team_arg* teamArgs = (struct team_arg*)args; 1531 const char* path; 1532 addr_t entry; 1533 char** userArgs; 1534 char** userEnv; 1535 struct user_space_program_args* programArgs; 1536 uint32 argCount, envCount; 1537 1538 thread = thread_get_current_thread(); 1539 team = thread->team; 1540 cache_node_launched(teamArgs->arg_count, teamArgs->flat_args); 1541 1542 TRACE(("team_create_thread_start: entry thread %" B_PRId32 "\n", 1543 thread->id)); 1544 1545 // Main stack area layout is currently as follows (starting from 0): 1546 // 1547 // size | usage 1548 // ---------------------------------+-------------------------------- 1549 // USER_MAIN_THREAD_STACK_SIZE | actual stack 1550 // TLS_SIZE | TLS data 1551 // sizeof(user_space_program_args) | argument structure for the runtime 1552 // | loader 1553 // flat arguments size | flat process arguments and environment 1554 1555 // TODO: ENV_SIZE is a) limited, and b) not used after libroot copied it to 1556 // the heap 1557 // TODO: we could reserve the whole USER_STACK_REGION upfront... 1558 1559 argCount = teamArgs->arg_count; 1560 envCount = teamArgs->env_count; 1561 1562 programArgs = (struct user_space_program_args*)(thread->user_stack_base 1563 + thread->user_stack_size + TLS_SIZE); 1564 1565 userArgs = (char**)(programArgs + 1); 1566 userEnv = userArgs + argCount + 1; 1567 path = teamArgs->path; 1568 1569 if (user_strlcpy(programArgs->program_path, path, 1570 sizeof(programArgs->program_path)) < B_OK 1571 || user_memcpy(&programArgs->arg_count, &argCount, sizeof(int32)) < B_OK 1572 || user_memcpy(&programArgs->args, &userArgs, sizeof(char**)) < B_OK 1573 || user_memcpy(&programArgs->env_count, &envCount, sizeof(int32)) < B_OK 1574 || user_memcpy(&programArgs->env, &userEnv, sizeof(char**)) < B_OK 1575 || user_memcpy(&programArgs->error_port, &teamArgs->error_port, 1576 sizeof(port_id)) < B_OK 1577 || user_memcpy(&programArgs->error_token, &teamArgs->error_token, 1578 sizeof(uint32)) < B_OK 1579 || user_memcpy(&programArgs->umask, &teamArgs->umask, sizeof(mode_t)) < B_OK 1580 || user_memcpy(&programArgs->disable_user_addons, 1581 &sDisableUserAddOns, sizeof(bool)) < B_OK 1582 || user_memcpy(userArgs, teamArgs->flat_args, 1583 teamArgs->flat_args_size) < B_OK) { 1584 // the team deletion process will clean this mess 1585 free_team_arg(teamArgs); 1586 return B_BAD_ADDRESS; 1587 } 1588 1589 TRACE(("team_create_thread_start: loading elf binary '%s'\n", path)); 1590 1591 // set team args and update state 1592 team->Lock(); 1593 team->SetArgs(path, teamArgs->flat_args + 1, argCount - 1); 1594 team->state = TEAM_STATE_NORMAL; 1595 team->Unlock(); 1596 1597 free_team_arg(teamArgs); 1598 // the arguments are already on the user stack, we no longer need 1599 // them in this form 1600 1601 // Clone commpage area 1602 area_id commPageArea = clone_commpage_area(team->id, 1603 &team->commpage_address); 1604 if (commPageArea < B_OK) { 1605 TRACE(("team_create_thread_start: clone_commpage_area() failed: %s\n", 1606 strerror(commPageArea))); 1607 return commPageArea; 1608 } 1609 1610 // Register commpage image 1611 image_id commPageImage = get_commpage_image(); 1612 extended_image_info imageInfo; 1613 err = get_image_info(commPageImage, &imageInfo.basic_info); 1614 if (err != B_OK) { 1615 TRACE(("team_create_thread_start: get_image_info() failed: %s\n", 1616 strerror(err))); 1617 return err; 1618 } 1619 imageInfo.basic_info.text = team->commpage_address; 1620 imageInfo.text_delta = (ssize_t)(addr_t)team->commpage_address; 1621 imageInfo.symbol_table = NULL; 1622 imageInfo.symbol_hash = NULL; 1623 imageInfo.string_table = NULL; 1624 image_id image = register_image(team, &imageInfo, sizeof(imageInfo)); 1625 if (image < 0) { 1626 TRACE(("team_create_thread_start: register_image() failed: %s\n", 1627 strerror(image))); 1628 return image; 1629 } 1630 1631 // NOTE: Normally arch_thread_enter_userspace() never returns, that is 1632 // automatic variables with function scope will never be destroyed. 1633 { 1634 // find runtime_loader path 1635 KPath runtimeLoaderPath; 1636 err = __find_directory(B_SYSTEM_DIRECTORY, gBootDevice, false, 1637 runtimeLoaderPath.LockBuffer(), runtimeLoaderPath.BufferSize()); 1638 if (err < B_OK) { 1639 TRACE(("team_create_thread_start: find_directory() failed: %s\n", 1640 strerror(err))); 1641 return err; 1642 } 1643 runtimeLoaderPath.UnlockBuffer(); 1644 err = runtimeLoaderPath.Append("runtime_loader"); 1645 1646 if (err == B_OK) { 1647 err = elf_load_user_image(runtimeLoaderPath.Path(), team, 0, 1648 &entry); 1649 } 1650 } 1651 1652 if (err < B_OK) { 1653 // Luckily, we don't have to clean up the mess we created - that's 1654 // done for us by the normal team deletion process 1655 TRACE(("team_create_thread_start: elf_load_user_image() failed: " 1656 "%s\n", strerror(err))); 1657 return err; 1658 } 1659 1660 TRACE(("team_create_thread_start: loaded elf. entry = %#lx\n", entry)); 1661 1662 // enter userspace -- returns only in case of error 1663 return thread_enter_userspace_new_team(thread, (addr_t)entry, 1664 programArgs, team->commpage_address); 1665 } 1666 1667 1668 static status_t 1669 team_create_thread_start(void* args) 1670 { 1671 team_create_thread_start_internal(args); 1672 team_init_exit_info_on_error(thread_get_current_thread()->team); 1673 thread_exit(); 1674 // does not return 1675 return B_OK; 1676 } 1677 1678 1679 static thread_id 1680 load_image_internal(char**& _flatArgs, size_t flatArgsSize, int32 argCount, 1681 int32 envCount, int32 priority, team_id parentID, uint32 flags, 1682 port_id errorPort, uint32 errorToken) 1683 { 1684 char** flatArgs = _flatArgs; 1685 thread_id thread; 1686 status_t status; 1687 struct team_arg* teamArgs; 1688 struct team_loading_info loadingInfo; 1689 ConditionVariableEntry loadingWaitEntry; 1690 io_context* parentIOContext = NULL; 1691 team_id teamID; 1692 bool teamLimitReached = false; 1693 1694 if (flatArgs == NULL || argCount == 0) 1695 return B_BAD_VALUE; 1696 1697 const char* path = flatArgs[0]; 1698 1699 TRACE(("load_image_internal: name '%s', args = %p, argCount = %" B_PRId32 1700 "\n", path, flatArgs, argCount)); 1701 1702 // cut the path from the main thread name 1703 const char* threadName = strrchr(path, '/'); 1704 if (threadName != NULL) 1705 threadName++; 1706 else 1707 threadName = path; 1708 1709 // create the main thread object 1710 Thread* mainThread; 1711 status = Thread::Create(threadName, mainThread); 1712 if (status != B_OK) 1713 return status; 1714 BReference<Thread> mainThreadReference(mainThread, true); 1715 1716 // create team object 1717 Team* team = Team::Create(mainThread->id, path, false); 1718 if (team == NULL) 1719 return B_NO_MEMORY; 1720 BReference<Team> teamReference(team, true); 1721 1722 if ((flags & B_WAIT_TILL_LOADED) != 0) { 1723 loadingInfo.condition.Init(team, "image load"); 1724 loadingInfo.condition.Add(&loadingWaitEntry); 1725 loadingInfo.result = B_ERROR; 1726 team->loading_info = &loadingInfo; 1727 } 1728 1729 // get the parent team 1730 Team* parent = Team::Get(parentID); 1731 if (parent == NULL) 1732 return B_BAD_TEAM_ID; 1733 BReference<Team> parentReference(parent, true); 1734 1735 parent->LockTeamAndProcessGroup(); 1736 team->Lock(); 1737 1738 // inherit the parent's user/group 1739 inherit_parent_user_and_group(team, parent); 1740 1741 // get a reference to the parent's I/O context -- we need it to create ours 1742 parentIOContext = parent->io_context; 1743 vfs_get_io_context(parentIOContext); 1744 1745 team->Unlock(); 1746 parent->UnlockTeamAndProcessGroup(); 1747 1748 // check the executable's set-user/group-id permission 1749 update_set_id_user_and_group(team, path); 1750 1751 status = create_team_arg(&teamArgs, path, flatArgs, flatArgsSize, argCount, 1752 envCount, (mode_t)-1, errorPort, errorToken); 1753 if (status != B_OK) 1754 goto err1; 1755 1756 _flatArgs = NULL; 1757 // args are owned by the team_arg structure now 1758 1759 // create a new io_context for this team 1760 team->io_context = vfs_new_io_context(parentIOContext, true); 1761 if (!team->io_context) { 1762 status = B_NO_MEMORY; 1763 goto err2; 1764 } 1765 1766 // We don't need the parent's I/O context any longer. 1767 vfs_put_io_context(parentIOContext); 1768 parentIOContext = NULL; 1769 1770 // remove any fds that have the CLOEXEC flag set (emulating BeOS behaviour) 1771 vfs_exec_io_context(team->io_context); 1772 1773 // create an address space for this team 1774 status = VMAddressSpace::Create(team->id, USER_BASE, USER_SIZE, false, 1775 &team->address_space); 1776 if (status != B_OK) 1777 goto err2; 1778 1779 team->address_space->SetRandomizingEnabled( 1780 (teamArgs->flags & TEAM_ARGS_FLAG_NO_ASLR) == 0); 1781 1782 // create the user data area 1783 status = create_team_user_data(team); 1784 if (status != B_OK) 1785 goto err4; 1786 1787 // insert the team into its parent and the teams hash 1788 parent->LockTeamAndProcessGroup(); 1789 team->Lock(); 1790 1791 { 1792 InterruptsWriteSpinLocker teamsLocker(sTeamHashLock); 1793 1794 sTeamHash.Insert(team); 1795 teamLimitReached = sUsedTeams >= sMaxTeams; 1796 if (!teamLimitReached) 1797 sUsedTeams++; 1798 } 1799 1800 insert_team_into_parent(parent, team); 1801 insert_team_into_group(parent->group, team); 1802 1803 team->Unlock(); 1804 parent->UnlockTeamAndProcessGroup(); 1805 1806 // notify team listeners 1807 sNotificationService.Notify(TEAM_ADDED, team); 1808 1809 if (teamLimitReached) { 1810 status = B_NO_MORE_TEAMS; 1811 goto err6; 1812 } 1813 1814 // In case we start the main thread, we shouldn't access the team object 1815 // afterwards, so cache the team's ID. 1816 teamID = team->id; 1817 1818 // Create a kernel thread, but under the context of the new team 1819 // The new thread will take over ownership of teamArgs. 1820 { 1821 ThreadCreationAttributes threadAttributes(team_create_thread_start, 1822 threadName, B_NORMAL_PRIORITY, teamArgs, teamID, mainThread); 1823 threadAttributes.additional_stack_size = sizeof(user_space_program_args) 1824 + teamArgs->flat_args_size; 1825 thread = thread_create_thread(threadAttributes, false); 1826 if (thread < 0) { 1827 status = thread; 1828 goto err6; 1829 } 1830 } 1831 1832 // The team has been created successfully, so we keep the reference. Or 1833 // more precisely: It's owned by the team's main thread, now. 1834 teamReference.Detach(); 1835 1836 // wait for the loader of the new team to finish its work 1837 if ((flags & B_WAIT_TILL_LOADED) != 0) { 1838 if (mainThread != NULL) { 1839 // resume the team's main thread 1840 thread_continue(mainThread); 1841 } 1842 1843 // Now wait until loading is finished. We will be woken either by the 1844 // thread, when it finished or aborted loading, or when the team is 1845 // going to die (e.g. is killed). In either case the one notifying is 1846 // responsible for unsetting `loading_info` in the team structure. 1847 loadingWaitEntry.Wait(); 1848 1849 if (loadingInfo.result < B_OK) 1850 return loadingInfo.result; 1851 } 1852 1853 // notify the debugger 1854 user_debug_team_created(teamID); 1855 1856 return thread; 1857 1858 err6: 1859 // Remove the team structure from the process group, the parent team, and 1860 // the team hash table and delete the team structure. 1861 parent->LockTeamAndProcessGroup(); 1862 team->Lock(); 1863 1864 remove_team_from_group(team); 1865 remove_team_from_parent(team->parent, team); 1866 1867 team->Unlock(); 1868 parent->UnlockTeamAndProcessGroup(); 1869 1870 { 1871 InterruptsWriteSpinLocker teamsLocker(sTeamHashLock); 1872 sTeamHash.Remove(team); 1873 if (!teamLimitReached) 1874 sUsedTeams--; 1875 } 1876 1877 sNotificationService.Notify(TEAM_REMOVED, team); 1878 1879 delete_team_user_data(team); 1880 err4: 1881 team->address_space->Put(); 1882 err2: 1883 free_team_arg(teamArgs); 1884 err1: 1885 if (parentIOContext != NULL) 1886 vfs_put_io_context(parentIOContext); 1887 1888 return status; 1889 } 1890 1891 1892 /*! Almost shuts down the current team and loads a new image into it. 1893 If successful, this function does not return and will takeover ownership of 1894 the arguments provided. 1895 This function may only be called in a userland team (caused by one of the 1896 exec*() syscalls). 1897 */ 1898 static status_t 1899 exec_team(const char* path, char**& _flatArgs, size_t flatArgsSize, 1900 int32 argCount, int32 envCount, mode_t umask) 1901 { 1902 // NOTE: Since this function normally doesn't return, don't use automatic 1903 // variables that need destruction in the function scope. 1904 char** flatArgs = _flatArgs; 1905 Team* team = thread_get_current_thread()->team; 1906 struct team_arg* teamArgs; 1907 const char* threadName; 1908 thread_id nubThreadID = -1; 1909 1910 TRACE(("exec_team(path = \"%s\", argc = %" B_PRId32 ", envCount = %" 1911 B_PRId32 "): team %" B_PRId32 "\n", path, argCount, envCount, 1912 team->id)); 1913 1914 T(ExecTeam(path, argCount, flatArgs, envCount, flatArgs + argCount + 1)); 1915 1916 // switching the kernel at run time is probably not a good idea :) 1917 if (team == team_get_kernel_team()) 1918 return B_NOT_ALLOWED; 1919 1920 // we currently need to be single threaded here 1921 // TODO: maybe we should just kill all other threads and 1922 // make the current thread the team's main thread? 1923 Thread* currentThread = thread_get_current_thread(); 1924 if (currentThread != team->main_thread) 1925 return B_NOT_ALLOWED; 1926 1927 // The debug nub thread, a pure kernel thread, is allowed to survive. 1928 // We iterate through the thread list to make sure that there's no other 1929 // thread. 1930 TeamLocker teamLocker(team); 1931 InterruptsSpinLocker debugInfoLocker(team->debug_info.lock); 1932 1933 if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) 1934 nubThreadID = team->debug_info.nub_thread; 1935 1936 debugInfoLocker.Unlock(); 1937 1938 for (Thread* thread = team->thread_list; thread != NULL; 1939 thread = thread->team_next) { 1940 if (thread != team->main_thread && thread->id != nubThreadID) 1941 return B_NOT_ALLOWED; 1942 } 1943 1944 team->DeleteUserTimers(true); 1945 team->ResetSignalsOnExec(); 1946 1947 teamLocker.Unlock(); 1948 1949 status_t status = create_team_arg(&teamArgs, path, flatArgs, flatArgsSize, 1950 argCount, envCount, umask, -1, 0); 1951 if (status != B_OK) 1952 return status; 1953 1954 _flatArgs = NULL; 1955 // args are owned by the team_arg structure now 1956 1957 // TODO: remove team resources if there are any left 1958 // thread_atkernel_exit() might not be called at all 1959 1960 thread_reset_for_exec(); 1961 1962 user_debug_prepare_for_exec(); 1963 1964 delete_team_user_data(team); 1965 vm_delete_areas(team->address_space, false); 1966 xsi_sem_undo(team); 1967 delete_owned_ports(team); 1968 sem_delete_owned_sems(team); 1969 remove_images(team); 1970 vfs_exec_io_context(team->io_context); 1971 delete_realtime_sem_context(team->realtime_sem_context); 1972 team->realtime_sem_context = NULL; 1973 1974 // update ASLR 1975 team->address_space->SetRandomizingEnabled( 1976 (teamArgs->flags & TEAM_ARGS_FLAG_NO_ASLR) == 0); 1977 1978 status = create_team_user_data(team); 1979 if (status != B_OK) { 1980 // creating the user data failed -- we're toast 1981 free_team_arg(teamArgs); 1982 exit_thread(status); 1983 return status; 1984 } 1985 1986 user_debug_finish_after_exec(); 1987 1988 // rename the team 1989 1990 team->Lock(); 1991 team->SetName(path); 1992 team->Unlock(); 1993 1994 // cut the path from the team name and rename the main thread, too 1995 threadName = strrchr(path, '/'); 1996 if (threadName != NULL) 1997 threadName++; 1998 else 1999 threadName = path; 2000 rename_thread(thread_get_current_thread_id(), threadName); 2001 2002 atomic_or(&team->flags, TEAM_FLAG_EXEC_DONE); 2003 2004 // Update user/group according to the executable's set-user/group-id 2005 // permission. 2006 update_set_id_user_and_group(team, path); 2007 2008 user_debug_team_exec(); 2009 2010 // notify team listeners 2011 sNotificationService.Notify(TEAM_EXEC, team); 2012 2013 // get a user thread for the thread 2014 user_thread* userThread = team_allocate_user_thread(team); 2015 // cannot fail (the allocation for the team would have failed already) 2016 ThreadLocker currentThreadLocker(currentThread); 2017 currentThread->user_thread = userThread; 2018 currentThreadLocker.Unlock(); 2019 2020 // create the user stack for the thread 2021 status = thread_create_user_stack(currentThread->team, currentThread, NULL, 2022 0, sizeof(user_space_program_args) + teamArgs->flat_args_size); 2023 if (status == B_OK) { 2024 // prepare the stack, load the runtime loader, and enter userspace 2025 team_create_thread_start(teamArgs); 2026 // does never return 2027 } else 2028 free_team_arg(teamArgs); 2029 2030 // Sorry, we have to kill ourselves, there is no way out anymore 2031 // (without any areas left and all that). 2032 exit_thread(status); 2033 2034 // We return a status here since the signal that is sent by the 2035 // call above is not immediately handled. 2036 return B_ERROR; 2037 } 2038 2039 2040 static thread_id 2041 fork_team(void) 2042 { 2043 Thread* parentThread = thread_get_current_thread(); 2044 Team* parentTeam = parentThread->team; 2045 Team* team; 2046 arch_fork_arg* forkArgs; 2047 struct area_info info; 2048 thread_id threadID; 2049 status_t status; 2050 ssize_t areaCookie; 2051 bool teamLimitReached = false; 2052 2053 TRACE(("fork_team(): team %" B_PRId32 "\n", parentTeam->id)); 2054 2055 if (parentTeam == team_get_kernel_team()) 2056 return B_NOT_ALLOWED; 2057 2058 // create a new team 2059 // TODO: this is very similar to load_image_internal() - maybe we can do 2060 // something about it :) 2061 2062 // create the main thread object 2063 Thread* thread; 2064 status = Thread::Create(parentThread->name, thread); 2065 if (status != B_OK) 2066 return status; 2067 BReference<Thread> threadReference(thread, true); 2068 2069 // create the team object 2070 team = Team::Create(thread->id, NULL, false); 2071 if (team == NULL) 2072 return B_NO_MEMORY; 2073 2074 parentTeam->LockTeamAndProcessGroup(); 2075 team->Lock(); 2076 2077 team->SetName(parentTeam->Name()); 2078 team->SetArgs(parentTeam->Args()); 2079 2080 team->commpage_address = parentTeam->commpage_address; 2081 2082 // Inherit the parent's user/group. 2083 inherit_parent_user_and_group(team, parentTeam); 2084 2085 // inherit signal handlers 2086 team->InheritSignalActions(parentTeam); 2087 2088 team->Unlock(); 2089 parentTeam->UnlockTeamAndProcessGroup(); 2090 2091 // inherit some team debug flags 2092 team->debug_info.flags |= atomic_get(&parentTeam->debug_info.flags) 2093 & B_TEAM_DEBUG_INHERITED_FLAGS; 2094 2095 forkArgs = (arch_fork_arg*)malloc(sizeof(arch_fork_arg)); 2096 if (forkArgs == NULL) { 2097 status = B_NO_MEMORY; 2098 goto err1; 2099 } 2100 2101 // create a new io_context for this team 2102 team->io_context = vfs_new_io_context(parentTeam->io_context, false); 2103 if (!team->io_context) { 2104 status = B_NO_MEMORY; 2105 goto err2; 2106 } 2107 2108 // duplicate the realtime sem context 2109 if (parentTeam->realtime_sem_context) { 2110 team->realtime_sem_context = clone_realtime_sem_context( 2111 parentTeam->realtime_sem_context); 2112 if (team->realtime_sem_context == NULL) { 2113 status = B_NO_MEMORY; 2114 goto err2; 2115 } 2116 } 2117 2118 // create an address space for this team 2119 status = VMAddressSpace::Create(team->id, USER_BASE, USER_SIZE, false, 2120 &team->address_space); 2121 if (status < B_OK) 2122 goto err3; 2123 2124 // copy all areas of the team 2125 // TODO: should be able to handle stack areas differently (ie. don't have 2126 // them copy-on-write) 2127 2128 areaCookie = 0; 2129 while (get_next_area_info(B_CURRENT_TEAM, &areaCookie, &info) == B_OK) { 2130 if (info.area == parentTeam->user_data_area) { 2131 // don't clone the user area; just create a new one 2132 status = create_team_user_data(team, info.address); 2133 if (status != B_OK) 2134 break; 2135 2136 thread->user_thread = team_allocate_user_thread(team); 2137 } else { 2138 void* address; 2139 area_id area = vm_copy_area(team->address_space->ID(), info.name, 2140 &address, B_CLONE_ADDRESS, info.protection, info.area); 2141 if (area < B_OK) { 2142 status = area; 2143 break; 2144 } 2145 2146 if (info.area == parentThread->user_stack_area) 2147 thread->user_stack_area = area; 2148 } 2149 } 2150 2151 if (status < B_OK) 2152 goto err4; 2153 2154 if (thread->user_thread == NULL) { 2155 #if KDEBUG 2156 panic("user data area not found, parent area is %" B_PRId32, 2157 parentTeam->user_data_area); 2158 #endif 2159 status = B_ERROR; 2160 goto err4; 2161 } 2162 2163 thread->user_stack_base = parentThread->user_stack_base; 2164 thread->user_stack_size = parentThread->user_stack_size; 2165 thread->user_local_storage = parentThread->user_local_storage; 2166 thread->sig_block_mask = parentThread->sig_block_mask; 2167 thread->signal_stack_base = parentThread->signal_stack_base; 2168 thread->signal_stack_size = parentThread->signal_stack_size; 2169 thread->signal_stack_enabled = parentThread->signal_stack_enabled; 2170 2171 arch_store_fork_frame(forkArgs); 2172 2173 // copy image list 2174 if (copy_images(parentTeam->id, team) != B_OK) 2175 goto err5; 2176 2177 // insert the team into its parent and the teams hash 2178 parentTeam->LockTeamAndProcessGroup(); 2179 team->Lock(); 2180 2181 { 2182 InterruptsWriteSpinLocker teamsLocker(sTeamHashLock); 2183 2184 sTeamHash.Insert(team); 2185 teamLimitReached = sUsedTeams >= sMaxTeams; 2186 if (!teamLimitReached) 2187 sUsedTeams++; 2188 } 2189 2190 insert_team_into_parent(parentTeam, team); 2191 insert_team_into_group(parentTeam->group, team); 2192 2193 team->Unlock(); 2194 parentTeam->UnlockTeamAndProcessGroup(); 2195 2196 // notify team listeners 2197 sNotificationService.Notify(TEAM_ADDED, team); 2198 2199 if (teamLimitReached) { 2200 status = B_NO_MORE_TEAMS; 2201 goto err6; 2202 } 2203 2204 // create the main thread 2205 { 2206 ThreadCreationAttributes threadCreationAttributes(NULL, 2207 parentThread->name, parentThread->priority, NULL, team->id, thread); 2208 threadCreationAttributes.forkArgs = forkArgs; 2209 threadCreationAttributes.flags |= THREAD_CREATION_FLAG_DEFER_SIGNALS; 2210 threadID = thread_create_thread(threadCreationAttributes, false); 2211 if (threadID < 0) { 2212 status = threadID; 2213 goto err6; 2214 } 2215 } 2216 2217 // notify the debugger 2218 user_debug_team_created(team->id); 2219 2220 T(TeamForked(threadID)); 2221 2222 resume_thread(threadID); 2223 return threadID; 2224 2225 err6: 2226 // Remove the team structure from the process group, the parent team, and 2227 // the team hash table and delete the team structure. 2228 parentTeam->LockTeamAndProcessGroup(); 2229 team->Lock(); 2230 2231 remove_team_from_group(team); 2232 remove_team_from_parent(team->parent, team); 2233 2234 team->Unlock(); 2235 parentTeam->UnlockTeamAndProcessGroup(); 2236 2237 { 2238 InterruptsWriteSpinLocker teamsLocker(sTeamHashLock); 2239 sTeamHash.Remove(team); 2240 if (!teamLimitReached) 2241 sUsedTeams--; 2242 } 2243 2244 sNotificationService.Notify(TEAM_REMOVED, team); 2245 err5: 2246 remove_images(team); 2247 err4: 2248 team->address_space->RemoveAndPut(); 2249 err3: 2250 delete_realtime_sem_context(team->realtime_sem_context); 2251 err2: 2252 free(forkArgs); 2253 err1: 2254 team->ReleaseReference(); 2255 2256 return status; 2257 } 2258 2259 2260 /*! Returns if the specified team \a parent has any children belonging to the 2261 process group with the specified ID \a groupID. 2262 The caller must hold \a parent's lock. 2263 */ 2264 static bool 2265 has_children_in_group(Team* parent, pid_t groupID) 2266 { 2267 for (Team* child = parent->children; child != NULL; 2268 child = child->siblings_next) { 2269 TeamLocker childLocker(child); 2270 if (child->group_id == groupID) 2271 return true; 2272 } 2273 2274 return false; 2275 } 2276 2277 2278 /*! Returns the first job control entry from \a children, which matches \a id. 2279 \a id can be: 2280 - \code > 0 \endcode: Matching an entry with that team ID. 2281 - \code == -1 \endcode: Matching any entry. 2282 - \code < -1 \endcode: Matching any entry with a process group ID of \c -id. 2283 \c 0 is an invalid value for \a id. 2284 2285 The caller must hold the lock of the team that \a children belongs to. 2286 2287 \param children The job control entry list to check. 2288 \param id The match criterion. 2289 \return The first matching entry or \c NULL, if none matches. 2290 */ 2291 static job_control_entry* 2292 get_job_control_entry(team_job_control_children& children, pid_t id) 2293 { 2294 for (JobControlEntryList::Iterator it = children.entries.GetIterator(); 2295 job_control_entry* entry = it.Next();) { 2296 2297 if (id > 0) { 2298 if (entry->thread == id) 2299 return entry; 2300 } else if (id == -1) { 2301 return entry; 2302 } else { 2303 pid_t processGroup 2304 = (entry->team ? entry->team->group_id : entry->group_id); 2305 if (processGroup == -id) 2306 return entry; 2307 } 2308 } 2309 2310 return NULL; 2311 } 2312 2313 2314 /*! Returns the first job control entry from one of team's dead, continued, or 2315 stopped children which matches \a id. 2316 \a id can be: 2317 - \code > 0 \endcode: Matching an entry with that team ID. 2318 - \code == -1 \endcode: Matching any entry. 2319 - \code < -1 \endcode: Matching any entry with a process group ID of \c -id. 2320 \c 0 is an invalid value for \a id. 2321 2322 The caller must hold \a team's lock. 2323 2324 \param team The team whose dead, stopped, and continued child lists shall be 2325 checked. 2326 \param id The match criterion. 2327 \param flags Specifies which children shall be considered. Dead children 2328 are considered when \a flags is ORed bitwise with \c WEXITED, stopped 2329 children are considered when \a flags is ORed bitwise with \c WUNTRACED 2330 or \c WSTOPPED, continued children when \a flags is ORed bitwise with 2331 \c WCONTINUED. 2332 \return The first matching entry or \c NULL, if none matches. 2333 */ 2334 static job_control_entry* 2335 get_job_control_entry(Team* team, pid_t id, uint32 flags) 2336 { 2337 job_control_entry* entry = NULL; 2338 2339 if ((flags & WEXITED) != 0) 2340 entry = get_job_control_entry(team->dead_children, id); 2341 2342 if (entry == NULL && (flags & WCONTINUED) != 0) 2343 entry = get_job_control_entry(team->continued_children, id); 2344 2345 if (entry == NULL && (flags & (WUNTRACED | WSTOPPED)) != 0) 2346 entry = get_job_control_entry(team->stopped_children, id); 2347 2348 return entry; 2349 } 2350 2351 2352 job_control_entry::job_control_entry() 2353 : 2354 has_group_ref(false) 2355 { 2356 } 2357 2358 2359 job_control_entry::~job_control_entry() 2360 { 2361 if (has_group_ref) { 2362 InterruptsSpinLocker groupHashLocker(sGroupHashLock); 2363 2364 ProcessGroup* group = sGroupHash.Lookup(group_id); 2365 if (group == NULL) { 2366 panic("job_control_entry::~job_control_entry(): unknown group " 2367 "ID: %" B_PRId32, group_id); 2368 return; 2369 } 2370 2371 groupHashLocker.Unlock(); 2372 2373 group->ReleaseReference(); 2374 } 2375 } 2376 2377 2378 /*! Invoked when the owning team is dying, initializing the entry according to 2379 the dead state. 2380 2381 The caller must hold the owning team's lock and the scheduler lock. 2382 */ 2383 void 2384 job_control_entry::InitDeadState() 2385 { 2386 if (team != NULL) { 2387 ASSERT(team->exit.initialized); 2388 2389 group_id = team->group_id; 2390 team->group->AcquireReference(); 2391 has_group_ref = true; 2392 2393 thread = team->id; 2394 status = team->exit.status; 2395 reason = team->exit.reason; 2396 signal = team->exit.signal; 2397 signaling_user = team->exit.signaling_user; 2398 user_time = team->dead_threads_user_time 2399 + team->dead_children.user_time; 2400 kernel_time = team->dead_threads_kernel_time 2401 + team->dead_children.kernel_time; 2402 2403 team = NULL; 2404 } 2405 } 2406 2407 2408 job_control_entry& 2409 job_control_entry::operator=(const job_control_entry& other) 2410 { 2411 state = other.state; 2412 thread = other.thread; 2413 signal = other.signal; 2414 has_group_ref = false; 2415 signaling_user = other.signaling_user; 2416 team = other.team; 2417 group_id = other.group_id; 2418 status = other.status; 2419 reason = other.reason; 2420 user_time = other.user_time; 2421 kernel_time = other.kernel_time; 2422 2423 return *this; 2424 } 2425 2426 2427 /*! This is the kernel backend for waitid(). 2428 */ 2429 static thread_id 2430 wait_for_child(pid_t child, uint32 flags, siginfo_t& _info, 2431 team_usage_info& _usage_info) 2432 { 2433 Thread* thread = thread_get_current_thread(); 2434 Team* team = thread->team; 2435 struct job_control_entry foundEntry; 2436 struct job_control_entry* freeDeathEntry = NULL; 2437 status_t status = B_OK; 2438 2439 TRACE(("wait_for_child(child = %" B_PRId32 ", flags = %" B_PRId32 ")\n", 2440 child, flags)); 2441 2442 T(WaitForChild(child, flags)); 2443 2444 if ((flags & (WEXITED | WUNTRACED | WSTOPPED | WCONTINUED)) == 0) { 2445 T(WaitForChildDone(B_BAD_VALUE)); 2446 return B_BAD_VALUE; 2447 } 2448 2449 pid_t originalChild = child; 2450 2451 bool ignoreFoundEntries = false; 2452 bool ignoreFoundEntriesChecked = false; 2453 2454 while (true) { 2455 // lock the team 2456 TeamLocker teamLocker(team); 2457 2458 // A 0 child argument means to wait for all children in the process 2459 // group of the calling team. 2460 child = originalChild == 0 ? -team->group_id : originalChild; 2461 2462 // check whether any condition holds 2463 job_control_entry* entry = get_job_control_entry(team, child, flags); 2464 2465 // If we don't have an entry yet, check whether there are any children 2466 // complying to the process group specification at all. 2467 if (entry == NULL) { 2468 // No success yet -- check whether there are any children complying 2469 // to the process group specification at all. 2470 bool childrenExist = false; 2471 if (child == -1) { 2472 childrenExist = team->children != NULL; 2473 } else if (child < -1) { 2474 childrenExist = has_children_in_group(team, -child); 2475 } else if (child != team->id) { 2476 if (Team* childTeam = Team::Get(child)) { 2477 BReference<Team> childTeamReference(childTeam, true); 2478 TeamLocker childTeamLocker(childTeam); 2479 childrenExist = childTeam->parent == team; 2480 } 2481 } 2482 2483 if (!childrenExist) { 2484 // there is no child we could wait for 2485 status = ECHILD; 2486 } else { 2487 // the children we're waiting for are still running 2488 status = B_WOULD_BLOCK; 2489 } 2490 } else { 2491 // got something 2492 foundEntry = *entry; 2493 2494 // unless WNOWAIT has been specified, "consume" the wait state 2495 if ((flags & WNOWAIT) == 0 || ignoreFoundEntries) { 2496 if (entry->state == JOB_CONTROL_STATE_DEAD) { 2497 // The child is dead. Reap its death entry. 2498 freeDeathEntry = entry; 2499 team->dead_children.entries.Remove(entry); 2500 team->dead_children.count--; 2501 } else { 2502 // The child is well. Reset its job control state. 2503 team_set_job_control_state(entry->team, 2504 JOB_CONTROL_STATE_NONE, NULL); 2505 } 2506 } 2507 } 2508 2509 // If we haven't got anything yet, prepare for waiting for the 2510 // condition variable. 2511 ConditionVariableEntry deadWaitEntry; 2512 2513 if (status == B_WOULD_BLOCK && (flags & WNOHANG) == 0) 2514 team->dead_children.condition_variable.Add(&deadWaitEntry); 2515 2516 teamLocker.Unlock(); 2517 2518 // we got our entry and can return to our caller 2519 if (status == B_OK) { 2520 if (ignoreFoundEntries) { 2521 // ... unless we shall ignore found entries 2522 delete freeDeathEntry; 2523 freeDeathEntry = NULL; 2524 continue; 2525 } 2526 2527 break; 2528 } 2529 2530 if (status != B_WOULD_BLOCK || (flags & WNOHANG) != 0) { 2531 T(WaitForChildDone(status)); 2532 return status; 2533 } 2534 2535 status = deadWaitEntry.Wait(B_CAN_INTERRUPT); 2536 if (status == B_INTERRUPTED) { 2537 T(WaitForChildDone(status)); 2538 return status; 2539 } 2540 2541 // If SA_NOCLDWAIT is set or SIGCHLD is ignored, we shall wait until 2542 // all our children are dead and fail with ECHILD. We check the 2543 // condition at this point. 2544 if (!ignoreFoundEntriesChecked) { 2545 teamLocker.Lock(); 2546 2547 struct sigaction& handler = team->SignalActionFor(SIGCHLD); 2548 if ((handler.sa_flags & SA_NOCLDWAIT) != 0 2549 || handler.sa_handler == SIG_IGN) { 2550 ignoreFoundEntries = true; 2551 } 2552 2553 teamLocker.Unlock(); 2554 2555 ignoreFoundEntriesChecked = true; 2556 } 2557 } 2558 2559 delete freeDeathEntry; 2560 2561 // When we got here, we have a valid death entry, and already got 2562 // unregistered from the team or group. Fill in the returned info. 2563 memset(&_info, 0, sizeof(_info)); 2564 _info.si_signo = SIGCHLD; 2565 _info.si_pid = foundEntry.thread; 2566 _info.si_uid = foundEntry.signaling_user; 2567 // TODO: Fill in si_errno? 2568 2569 switch (foundEntry.state) { 2570 case JOB_CONTROL_STATE_DEAD: 2571 _info.si_code = foundEntry.reason; 2572 _info.si_status = foundEntry.reason == CLD_EXITED 2573 ? foundEntry.status : foundEntry.signal; 2574 _usage_info.user_time = foundEntry.user_time; 2575 _usage_info.kernel_time = foundEntry.kernel_time; 2576 break; 2577 case JOB_CONTROL_STATE_STOPPED: 2578 _info.si_code = CLD_STOPPED; 2579 _info.si_status = foundEntry.signal; 2580 break; 2581 case JOB_CONTROL_STATE_CONTINUED: 2582 _info.si_code = CLD_CONTINUED; 2583 _info.si_status = 0; 2584 break; 2585 case JOB_CONTROL_STATE_NONE: 2586 // can't happen 2587 break; 2588 } 2589 2590 // If SIGCHLD is blocked, we shall clear pending SIGCHLDs, if no other child 2591 // status is available. 2592 TeamLocker teamLocker(team); 2593 InterruptsSpinLocker signalLocker(team->signal_lock); 2594 SpinLocker threadCreationLocker(gThreadCreationLock); 2595 2596 if (is_team_signal_blocked(team, SIGCHLD)) { 2597 if (get_job_control_entry(team, child, flags) == NULL) 2598 team->RemovePendingSignals(SIGNAL_TO_MASK(SIGCHLD)); 2599 } 2600 2601 threadCreationLocker.Unlock(); 2602 signalLocker.Unlock(); 2603 teamLocker.Unlock(); 2604 2605 // When the team is dead, the main thread continues to live in the kernel 2606 // team for a very short time. To avoid surprises for the caller we rather 2607 // wait until the thread is really gone. 2608 if (foundEntry.state == JOB_CONTROL_STATE_DEAD) 2609 wait_for_thread(foundEntry.thread, NULL); 2610 2611 T(WaitForChildDone(foundEntry)); 2612 2613 return foundEntry.thread; 2614 } 2615 2616 2617 /*! Fills the team_info structure with information from the specified team. 2618 Interrupts must be enabled. The team must not be locked. 2619 */ 2620 static status_t 2621 fill_team_info(Team* team, team_info* info, size_t size) 2622 { 2623 if (size != sizeof(team_info)) 2624 return B_BAD_VALUE; 2625 2626 // TODO: Set more informations for team_info 2627 memset(info, 0, size); 2628 2629 info->team = team->id; 2630 // immutable 2631 info->image_count = count_images(team); 2632 // protected by sImageMutex 2633 2634 TeamLocker teamLocker(team); 2635 InterruptsSpinLocker debugInfoLocker(team->debug_info.lock); 2636 2637 info->thread_count = team->num_threads; 2638 //info->area_count = 2639 info->debugger_nub_thread = team->debug_info.nub_thread; 2640 info->debugger_nub_port = team->debug_info.nub_port; 2641 info->uid = team->effective_uid; 2642 info->gid = team->effective_gid; 2643 2644 strlcpy(info->args, team->Args(), sizeof(info->args)); 2645 info->argc = 1; 2646 2647 return B_OK; 2648 } 2649 2650 2651 /*! Returns whether the process group contains stopped processes. 2652 The caller must hold the process group's lock. 2653 */ 2654 static bool 2655 process_group_has_stopped_processes(ProcessGroup* group) 2656 { 2657 Team* team = group->teams; 2658 while (team != NULL) { 2659 // the parent team's lock guards the job control entry -- acquire it 2660 team->LockTeamAndParent(false); 2661 2662 if (team->job_control_entry != NULL 2663 && team->job_control_entry->state == JOB_CONTROL_STATE_STOPPED) { 2664 team->UnlockTeamAndParent(); 2665 return true; 2666 } 2667 2668 team->UnlockTeamAndParent(); 2669 2670 team = team->group_next; 2671 } 2672 2673 return false; 2674 } 2675 2676 2677 /*! Iterates through all process groups queued in team_remove_team() and signals 2678 those that are orphaned and have stopped processes. 2679 The caller must not hold any team or process group locks. 2680 */ 2681 static void 2682 orphaned_process_group_check() 2683 { 2684 // process as long as there are groups in the list 2685 while (true) { 2686 // remove the head from the list 2687 MutexLocker orphanedCheckLocker(sOrphanedCheckLock); 2688 2689 ProcessGroup* group = sOrphanedCheckProcessGroups.RemoveHead(); 2690 if (group == NULL) 2691 return; 2692 2693 group->UnsetOrphanedCheck(); 2694 BReference<ProcessGroup> groupReference(group); 2695 2696 orphanedCheckLocker.Unlock(); 2697 2698 AutoLocker<ProcessGroup> groupLocker(group); 2699 2700 // If the group is orphaned and contains stopped processes, we're 2701 // supposed to send SIGHUP + SIGCONT. 2702 if (group->IsOrphaned() && process_group_has_stopped_processes(group)) { 2703 Thread* currentThread = thread_get_current_thread(); 2704 2705 Signal signal(SIGHUP, SI_USER, B_OK, currentThread->team->id); 2706 send_signal_to_process_group_locked(group, signal, 0); 2707 2708 signal.SetNumber(SIGCONT); 2709 send_signal_to_process_group_locked(group, signal, 0); 2710 } 2711 } 2712 } 2713 2714 2715 static status_t 2716 common_get_team_usage_info(team_id id, int32 who, team_usage_info* info, 2717 uint32 flags) 2718 { 2719 if (who != B_TEAM_USAGE_SELF && who != B_TEAM_USAGE_CHILDREN) 2720 return B_BAD_VALUE; 2721 2722 // get the team 2723 Team* team = Team::GetAndLock(id); 2724 if (team == NULL) 2725 return B_BAD_TEAM_ID; 2726 BReference<Team> teamReference(team, true); 2727 TeamLocker teamLocker(team, true); 2728 2729 if ((flags & B_CHECK_PERMISSION) != 0) { 2730 uid_t uid = geteuid(); 2731 if (uid != 0 && uid != team->effective_uid) 2732 return B_NOT_ALLOWED; 2733 } 2734 2735 bigtime_t kernelTime = 0; 2736 bigtime_t userTime = 0; 2737 2738 switch (who) { 2739 case B_TEAM_USAGE_SELF: 2740 { 2741 Thread* thread = team->thread_list; 2742 2743 for (; thread != NULL; thread = thread->team_next) { 2744 InterruptsSpinLocker threadTimeLocker(thread->time_lock); 2745 kernelTime += thread->kernel_time; 2746 userTime += thread->user_time; 2747 } 2748 2749 kernelTime += team->dead_threads_kernel_time; 2750 userTime += team->dead_threads_user_time; 2751 break; 2752 } 2753 2754 case B_TEAM_USAGE_CHILDREN: 2755 { 2756 Team* child = team->children; 2757 for (; child != NULL; child = child->siblings_next) { 2758 TeamLocker childLocker(child); 2759 2760 Thread* thread = team->thread_list; 2761 2762 for (; thread != NULL; thread = thread->team_next) { 2763 InterruptsSpinLocker threadTimeLocker(thread->time_lock); 2764 kernelTime += thread->kernel_time; 2765 userTime += thread->user_time; 2766 } 2767 2768 kernelTime += child->dead_threads_kernel_time; 2769 userTime += child->dead_threads_user_time; 2770 } 2771 2772 kernelTime += team->dead_children.kernel_time; 2773 userTime += team->dead_children.user_time; 2774 break; 2775 } 2776 } 2777 2778 info->kernel_time = kernelTime; 2779 info->user_time = userTime; 2780 2781 return B_OK; 2782 } 2783 2784 2785 // #pragma mark - Private kernel API 2786 2787 2788 status_t 2789 team_init(kernel_args* args) 2790 { 2791 // create the team hash table 2792 new(&sTeamHash) TeamTable; 2793 if (sTeamHash.Init(64) != B_OK) 2794 panic("Failed to init team hash table!"); 2795 2796 new(&sGroupHash) ProcessGroupHashTable; 2797 if (sGroupHash.Init() != B_OK) 2798 panic("Failed to init process group hash table!"); 2799 2800 // create initial session and process groups 2801 2802 ProcessSession* session = new(std::nothrow) ProcessSession(1); 2803 if (session == NULL) 2804 panic("Could not create initial session.\n"); 2805 BReference<ProcessSession> sessionReference(session, true); 2806 2807 ProcessGroup* group = new(std::nothrow) ProcessGroup(1); 2808 if (group == NULL) 2809 panic("Could not create initial process group.\n"); 2810 BReference<ProcessGroup> groupReference(group, true); 2811 2812 group->Publish(session); 2813 2814 // create the kernel team 2815 sKernelTeam = Team::Create(1, "kernel_team", true); 2816 if (sKernelTeam == NULL) 2817 panic("could not create kernel team!\n"); 2818 sKernelTeam->SetArgs(sKernelTeam->Name()); 2819 sKernelTeam->state = TEAM_STATE_NORMAL; 2820 2821 sKernelTeam->saved_set_uid = 0; 2822 sKernelTeam->real_uid = 0; 2823 sKernelTeam->effective_uid = 0; 2824 sKernelTeam->saved_set_gid = 0; 2825 sKernelTeam->real_gid = 0; 2826 sKernelTeam->effective_gid = 0; 2827 sKernelTeam->supplementary_groups = NULL; 2828 sKernelTeam->supplementary_group_count = 0; 2829 2830 insert_team_into_group(group, sKernelTeam); 2831 2832 sKernelTeam->io_context = vfs_new_io_context(NULL, false); 2833 if (sKernelTeam->io_context == NULL) 2834 panic("could not create io_context for kernel team!\n"); 2835 2836 if (vfs_resize_fd_table(sKernelTeam->io_context, 4096) != B_OK) 2837 dprintf("Failed to resize FD table for kernel team!\n"); 2838 2839 // stick it in the team hash 2840 sTeamHash.Insert(sKernelTeam); 2841 2842 // check safe mode settings 2843 sDisableUserAddOns = get_safemode_boolean(B_SAFEMODE_DISABLE_USER_ADD_ONS, 2844 false); 2845 2846 add_debugger_command_etc("team", &dump_team_info, 2847 "Dump info about a particular team", 2848 "[ <id> | <address> | <name> ]\n" 2849 "Prints information about the specified team. If no argument is given\n" 2850 "the current team is selected.\n" 2851 " <id> - The ID of the team.\n" 2852 " <address> - The address of the team structure.\n" 2853 " <name> - The team's name.\n", 0); 2854 add_debugger_command_etc("teams", &dump_teams, "List all teams", 2855 "\n" 2856 "Prints a list of all existing teams.\n", 0); 2857 2858 new(&sNotificationService) TeamNotificationService(); 2859 2860 sNotificationService.Register(); 2861 2862 return B_OK; 2863 } 2864 2865 2866 int32 2867 team_max_teams(void) 2868 { 2869 return sMaxTeams; 2870 } 2871 2872 2873 int32 2874 team_used_teams(void) 2875 { 2876 InterruptsReadSpinLocker teamsLocker(sTeamHashLock); 2877 return sUsedTeams; 2878 } 2879 2880 2881 /*! Returns a death entry of a child team specified by ID (if any). 2882 The caller must hold the team's lock. 2883 2884 \param team The team whose dead children list to check. 2885 \param child The ID of the child for whose death entry to lock. Must be > 0. 2886 \param _deleteEntry Return variable, indicating whether the caller needs to 2887 delete the returned entry. 2888 \return The death entry of the matching team, or \c NULL, if no death entry 2889 for the team was found. 2890 */ 2891 job_control_entry* 2892 team_get_death_entry(Team* team, thread_id child, bool* _deleteEntry) 2893 { 2894 if (child <= 0) 2895 return NULL; 2896 2897 job_control_entry* entry = get_job_control_entry(team->dead_children, 2898 child); 2899 if (entry) { 2900 // remove the entry only, if the caller is the parent of the found team 2901 if (team_get_current_team_id() == entry->thread) { 2902 team->dead_children.entries.Remove(entry); 2903 team->dead_children.count--; 2904 *_deleteEntry = true; 2905 } else { 2906 *_deleteEntry = false; 2907 } 2908 } 2909 2910 return entry; 2911 } 2912 2913 2914 /*! Quick check to see if we have a valid team ID. */ 2915 bool 2916 team_is_valid(team_id id) 2917 { 2918 if (id <= 0) 2919 return false; 2920 2921 InterruptsReadSpinLocker teamsLocker(sTeamHashLock); 2922 return team_get_team_struct_locked(id) != NULL; 2923 } 2924 2925 2926 Team* 2927 team_get_team_struct_locked(team_id id) 2928 { 2929 return sTeamHash.Lookup(id); 2930 } 2931 2932 2933 void 2934 team_set_controlling_tty(int32 ttyIndex) 2935 { 2936 // lock the team, so its session won't change while we're playing with it 2937 Team* team = thread_get_current_thread()->team; 2938 TeamLocker teamLocker(team); 2939 2940 // get and lock the session 2941 ProcessSession* session = team->group->Session(); 2942 AutoLocker<ProcessSession> sessionLocker(session); 2943 2944 // set the session's fields 2945 session->controlling_tty = ttyIndex; 2946 session->foreground_group = -1; 2947 } 2948 2949 2950 int32 2951 team_get_controlling_tty() 2952 { 2953 // lock the team, so its session won't change while we're playing with it 2954 Team* team = thread_get_current_thread()->team; 2955 TeamLocker teamLocker(team); 2956 2957 // get and lock the session 2958 ProcessSession* session = team->group->Session(); 2959 AutoLocker<ProcessSession> sessionLocker(session); 2960 2961 // get the session's field 2962 return session->controlling_tty; 2963 } 2964 2965 2966 status_t 2967 team_set_foreground_process_group(int32 ttyIndex, pid_t processGroupID) 2968 { 2969 // lock the team, so its session won't change while we're playing with it 2970 Thread* thread = thread_get_current_thread(); 2971 Team* team = thread->team; 2972 TeamLocker teamLocker(team); 2973 2974 // get and lock the session 2975 ProcessSession* session = team->group->Session(); 2976 AutoLocker<ProcessSession> sessionLocker(session); 2977 2978 // check given TTY -- must be the controlling tty of the calling process 2979 if (session->controlling_tty != ttyIndex) 2980 return ENOTTY; 2981 2982 // check given process group -- must belong to our session 2983 { 2984 InterruptsSpinLocker groupHashLocker(sGroupHashLock); 2985 ProcessGroup* group = sGroupHash.Lookup(processGroupID); 2986 if (group == NULL || group->Session() != session) 2987 return B_BAD_VALUE; 2988 } 2989 2990 // If we are a background group, we can do that unharmed only when we 2991 // ignore or block SIGTTOU. Otherwise the group gets a SIGTTOU. 2992 if (session->foreground_group != -1 2993 && session->foreground_group != team->group_id 2994 && team->SignalActionFor(SIGTTOU).sa_handler != SIG_IGN 2995 && (thread->sig_block_mask & SIGNAL_TO_MASK(SIGTTOU)) == 0) { 2996 InterruptsSpinLocker signalLocker(team->signal_lock); 2997 2998 if (!is_team_signal_blocked(team, SIGTTOU)) { 2999 pid_t groupID = team->group_id; 3000 3001 signalLocker.Unlock(); 3002 sessionLocker.Unlock(); 3003 teamLocker.Unlock(); 3004 3005 Signal signal(SIGTTOU, SI_USER, B_OK, team->id); 3006 send_signal_to_process_group(groupID, signal, 0); 3007 return B_INTERRUPTED; 3008 } 3009 } 3010 3011 session->foreground_group = processGroupID; 3012 3013 return B_OK; 3014 } 3015 3016 3017 uid_t 3018 team_geteuid(team_id id) 3019 { 3020 InterruptsReadSpinLocker teamsLocker(sTeamHashLock); 3021 Team* team = team_get_team_struct_locked(id); 3022 if (team == NULL) 3023 return (uid_t)-1; 3024 return team->effective_uid; 3025 } 3026 3027 3028 /*! Removes the specified team from the global team hash, from its process 3029 group, and from its parent. 3030 It also moves all of its children to the kernel team. 3031 3032 The caller must hold the following locks: 3033 - \a team's process group's lock, 3034 - the kernel team's lock, 3035 - \a team's parent team's lock (might be the kernel team), and 3036 - \a team's lock. 3037 */ 3038 void 3039 team_remove_team(Team* team, pid_t& _signalGroup) 3040 { 3041 Team* parent = team->parent; 3042 3043 // remember how long this team lasted 3044 parent->dead_children.kernel_time += team->dead_threads_kernel_time 3045 + team->dead_children.kernel_time; 3046 parent->dead_children.user_time += team->dead_threads_user_time 3047 + team->dead_children.user_time; 3048 3049 // remove the team from the hash table 3050 InterruptsWriteSpinLocker teamsLocker(sTeamHashLock); 3051 sTeamHash.Remove(team); 3052 sUsedTeams--; 3053 teamsLocker.Unlock(); 3054 3055 // The team can no longer be accessed by ID. Navigation to it is still 3056 // possible from its process group and its parent and children, but that 3057 // will be rectified shortly. 3058 team->state = TEAM_STATE_DEATH; 3059 3060 // If we're a controlling process (i.e. a session leader with controlling 3061 // terminal), there's a bit of signalling we have to do. We can't do any of 3062 // the signaling here due to the bunch of locks we're holding, but we need 3063 // to determine, whom to signal. 3064 _signalGroup = -1; 3065 bool isSessionLeader = false; 3066 if (team->session_id == team->id 3067 && team->group->Session()->controlling_tty >= 0) { 3068 isSessionLeader = true; 3069 3070 ProcessSession* session = team->group->Session(); 3071 3072 AutoLocker<ProcessSession> sessionLocker(session); 3073 3074 session->controlling_tty = -1; 3075 _signalGroup = session->foreground_group; 3076 } 3077 3078 // remove us from our process group 3079 remove_team_from_group(team); 3080 3081 // move the team's children to the kernel team 3082 while (Team* child = team->children) { 3083 // remove the child from the current team and add it to the kernel team 3084 TeamLocker childLocker(child); 3085 3086 remove_team_from_parent(team, child); 3087 insert_team_into_parent(sKernelTeam, child); 3088 3089 // move job control entries too 3090 sKernelTeam->stopped_children.entries.MoveFrom( 3091 &team->stopped_children.entries); 3092 sKernelTeam->continued_children.entries.MoveFrom( 3093 &team->continued_children.entries); 3094 3095 // If the team was a session leader with controlling terminal, 3096 // we need to send SIGHUP + SIGCONT to all newly-orphaned process 3097 // groups with stopped processes. Due to locking complications we can't 3098 // do that here, so we only check whether we were a reason for the 3099 // child's process group not being an orphan and, if so, schedule a 3100 // later check (cf. orphaned_process_group_check()). 3101 if (isSessionLeader) { 3102 ProcessGroup* childGroup = child->group; 3103 if (childGroup->Session()->id == team->session_id 3104 && childGroup->id != team->group_id) { 3105 childGroup->ScheduleOrphanedCheck(); 3106 } 3107 } 3108 3109 // Note, we don't move the dead children entries. Those will be deleted 3110 // when the team structure is deleted. 3111 } 3112 3113 // remove us from our parent 3114 remove_team_from_parent(parent, team); 3115 } 3116 3117 3118 /*! Kills all threads but the main thread of the team and shuts down user 3119 debugging for it. 3120 To be called on exit of the team's main thread. No locks must be held. 3121 3122 \param team The team in question. 3123 \return The port of the debugger for the team, -1 if none. To be passed to 3124 team_delete_team(). 3125 */ 3126 port_id 3127 team_shutdown_team(Team* team) 3128 { 3129 ASSERT(thread_get_current_thread() == team->main_thread); 3130 3131 TeamLocker teamLocker(team); 3132 3133 // Make sure debugging changes won't happen anymore. 3134 port_id debuggerPort = -1; 3135 while (true) { 3136 // If a debugger change is in progress for the team, we'll have to 3137 // wait until it is done. 3138 ConditionVariableEntry waitForDebuggerEntry; 3139 bool waitForDebugger = false; 3140 3141 InterruptsSpinLocker debugInfoLocker(team->debug_info.lock); 3142 3143 if (team->debug_info.debugger_changed_condition != NULL) { 3144 team->debug_info.debugger_changed_condition->Add( 3145 &waitForDebuggerEntry); 3146 waitForDebugger = true; 3147 } else if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) { 3148 // The team is being debugged. That will stop with the termination 3149 // of the nub thread. Since we set the team state to death, no one 3150 // can install a debugger anymore. We fetch the debugger's port to 3151 // send it a message at the bitter end. 3152 debuggerPort = team->debug_info.debugger_port; 3153 } 3154 3155 debugInfoLocker.Unlock(); 3156 3157 if (!waitForDebugger) 3158 break; 3159 3160 // wait for the debugger change to be finished 3161 teamLocker.Unlock(); 3162 3163 waitForDebuggerEntry.Wait(); 3164 3165 teamLocker.Lock(); 3166 } 3167 3168 // Mark the team as shutting down. That will prevent new threads from being 3169 // created and debugger changes from taking place. 3170 team->state = TEAM_STATE_SHUTDOWN; 3171 3172 // delete all timers 3173 team->DeleteUserTimers(false); 3174 3175 // deactivate CPU time user timers for the team 3176 InterruptsSpinLocker timeLocker(team->time_lock); 3177 3178 if (team->HasActiveCPUTimeUserTimers()) 3179 team->DeactivateCPUTimeUserTimers(); 3180 3181 timeLocker.Unlock(); 3182 3183 // kill all threads but the main thread 3184 team_death_entry deathEntry; 3185 deathEntry.condition.Init(team, "team death"); 3186 3187 while (true) { 3188 team->death_entry = &deathEntry; 3189 deathEntry.remaining_threads = 0; 3190 3191 Thread* thread = team->thread_list; 3192 while (thread != NULL) { 3193 if (thread != team->main_thread) { 3194 Signal signal(SIGKILLTHR, SI_USER, B_OK, team->id); 3195 send_signal_to_thread(thread, signal, B_DO_NOT_RESCHEDULE); 3196 deathEntry.remaining_threads++; 3197 } 3198 3199 thread = thread->team_next; 3200 } 3201 3202 if (deathEntry.remaining_threads == 0) 3203 break; 3204 3205 // there are threads to wait for 3206 ConditionVariableEntry entry; 3207 deathEntry.condition.Add(&entry); 3208 3209 teamLocker.Unlock(); 3210 3211 entry.Wait(); 3212 3213 teamLocker.Lock(); 3214 } 3215 3216 team->death_entry = NULL; 3217 3218 return debuggerPort; 3219 } 3220 3221 3222 /*! Called on team exit to notify threads waiting on the team and free most 3223 resources associated with it. 3224 The caller shouldn't hold any locks. 3225 */ 3226 void 3227 team_delete_team(Team* team, port_id debuggerPort) 3228 { 3229 // Not quite in our job description, but work that has been left by 3230 // team_remove_team() and that can be done now that we're not holding any 3231 // locks. 3232 orphaned_process_group_check(); 3233 3234 team_id teamID = team->id; 3235 3236 ASSERT(team->num_threads == 0); 3237 3238 // If someone is waiting for this team to be loaded, but it dies 3239 // unexpectedly before being done, we need to notify the waiting 3240 // thread now. 3241 3242 TeamLocker teamLocker(team); 3243 3244 if (team->loading_info) { 3245 // there's indeed someone waiting 3246 struct team_loading_info* loadingInfo = team->loading_info; 3247 team->loading_info = NULL; 3248 3249 loadingInfo->result = B_ERROR; 3250 3251 // wake up the waiting thread 3252 loadingInfo->condition.NotifyAll(); 3253 } 3254 3255 // notify team watchers 3256 3257 { 3258 // we're not reachable from anyone anymore at this point, so we 3259 // can safely access the list without any locking 3260 struct team_watcher* watcher; 3261 while ((watcher = (struct team_watcher*)list_remove_head_item( 3262 &team->watcher_list)) != NULL) { 3263 watcher->hook(teamID, watcher->data); 3264 free(watcher); 3265 } 3266 } 3267 3268 teamLocker.Unlock(); 3269 3270 sNotificationService.Notify(TEAM_REMOVED, team); 3271 3272 // free team resources 3273 3274 delete_realtime_sem_context(team->realtime_sem_context); 3275 xsi_sem_undo(team); 3276 remove_images(team); 3277 team->address_space->RemoveAndPut(); 3278 3279 team->ReleaseReference(); 3280 3281 // notify the debugger, that the team is gone 3282 user_debug_team_deleted(teamID, debuggerPort); 3283 } 3284 3285 3286 Team* 3287 team_get_kernel_team(void) 3288 { 3289 return sKernelTeam; 3290 } 3291 3292 3293 team_id 3294 team_get_kernel_team_id(void) 3295 { 3296 if (!sKernelTeam) 3297 return 0; 3298 3299 return sKernelTeam->id; 3300 } 3301 3302 3303 team_id 3304 team_get_current_team_id(void) 3305 { 3306 return thread_get_current_thread()->team->id; 3307 } 3308 3309 3310 status_t 3311 team_get_address_space(team_id id, VMAddressSpace** _addressSpace) 3312 { 3313 if (id == sKernelTeam->id) { 3314 // we're the kernel team, so we don't have to go through all 3315 // the hassle (locking and hash lookup) 3316 *_addressSpace = VMAddressSpace::GetKernel(); 3317 return B_OK; 3318 } 3319 3320 InterruptsReadSpinLocker teamsLocker(sTeamHashLock); 3321 3322 Team* team = team_get_team_struct_locked(id); 3323 if (team == NULL) 3324 return B_BAD_VALUE; 3325 3326 team->address_space->Get(); 3327 *_addressSpace = team->address_space; 3328 return B_OK; 3329 } 3330 3331 3332 /*! Sets the team's job control state. 3333 The caller must hold the parent team's lock. Interrupts are allowed to be 3334 enabled or disabled. 3335 \a team The team whose job control state shall be set. 3336 \a newState The new state to be set. 3337 \a signal The signal the new state was caused by. Can \c NULL, if none. Then 3338 the caller is responsible for filling in the following fields of the 3339 entry before releasing the parent team's lock, unless the new state is 3340 \c JOB_CONTROL_STATE_NONE: 3341 - \c signal: The number of the signal causing the state change. 3342 - \c signaling_user: The real UID of the user sending the signal. 3343 */ 3344 void 3345 team_set_job_control_state(Team* team, job_control_state newState, 3346 Signal* signal) 3347 { 3348 if (team == NULL || team->job_control_entry == NULL) 3349 return; 3350 3351 // don't touch anything, if the state stays the same or the team is already 3352 // dead 3353 job_control_entry* entry = team->job_control_entry; 3354 if (entry->state == newState || entry->state == JOB_CONTROL_STATE_DEAD) 3355 return; 3356 3357 T(SetJobControlState(team->id, newState, signal)); 3358 3359 // remove from the old list 3360 switch (entry->state) { 3361 case JOB_CONTROL_STATE_NONE: 3362 // entry is in no list ATM 3363 break; 3364 case JOB_CONTROL_STATE_DEAD: 3365 // can't get here 3366 break; 3367 case JOB_CONTROL_STATE_STOPPED: 3368 team->parent->stopped_children.entries.Remove(entry); 3369 break; 3370 case JOB_CONTROL_STATE_CONTINUED: 3371 team->parent->continued_children.entries.Remove(entry); 3372 break; 3373 } 3374 3375 entry->state = newState; 3376 3377 if (signal != NULL) { 3378 entry->signal = signal->Number(); 3379 entry->signaling_user = signal->SendingUser(); 3380 } 3381 3382 // add to new list 3383 team_job_control_children* childList = NULL; 3384 switch (entry->state) { 3385 case JOB_CONTROL_STATE_NONE: 3386 // entry doesn't get into any list 3387 break; 3388 case JOB_CONTROL_STATE_DEAD: 3389 childList = &team->parent->dead_children; 3390 team->parent->dead_children.count++; 3391 break; 3392 case JOB_CONTROL_STATE_STOPPED: 3393 childList = &team->parent->stopped_children; 3394 break; 3395 case JOB_CONTROL_STATE_CONTINUED: 3396 childList = &team->parent->continued_children; 3397 break; 3398 } 3399 3400 if (childList != NULL) { 3401 childList->entries.Add(entry); 3402 team->parent->dead_children.condition_variable.NotifyAll(); 3403 } 3404 } 3405 3406 3407 /*! Inits the given team's exit information, if not yet initialized, to some 3408 generic "killed" status. 3409 The caller must not hold the team's lock. Interrupts must be enabled. 3410 3411 \param team The team whose exit info shall be initialized. 3412 */ 3413 void 3414 team_init_exit_info_on_error(Team* team) 3415 { 3416 TeamLocker teamLocker(team); 3417 3418 if (!team->exit.initialized) { 3419 team->exit.reason = CLD_KILLED; 3420 team->exit.signal = SIGKILL; 3421 team->exit.signaling_user = geteuid(); 3422 team->exit.status = 0; 3423 team->exit.initialized = true; 3424 } 3425 } 3426 3427 3428 /*! Adds a hook to the team that is called as soon as this team goes away. 3429 This call might get public in the future. 3430 */ 3431 status_t 3432 start_watching_team(team_id teamID, void (*hook)(team_id, void*), void* data) 3433 { 3434 if (hook == NULL || teamID < B_OK) 3435 return B_BAD_VALUE; 3436 3437 // create the watcher object 3438 team_watcher* watcher = (team_watcher*)malloc(sizeof(team_watcher)); 3439 if (watcher == NULL) 3440 return B_NO_MEMORY; 3441 3442 watcher->hook = hook; 3443 watcher->data = data; 3444 3445 // add watcher, if the team isn't already dying 3446 // get the team 3447 Team* team = Team::GetAndLock(teamID); 3448 if (team == NULL) { 3449 free(watcher); 3450 return B_BAD_TEAM_ID; 3451 } 3452 3453 list_add_item(&team->watcher_list, watcher); 3454 3455 team->UnlockAndReleaseReference(); 3456 3457 return B_OK; 3458 } 3459 3460 3461 status_t 3462 stop_watching_team(team_id teamID, void (*hook)(team_id, void*), void* data) 3463 { 3464 if (hook == NULL || teamID < 0) 3465 return B_BAD_VALUE; 3466 3467 // get team and remove watcher (if present) 3468 Team* team = Team::GetAndLock(teamID); 3469 if (team == NULL) 3470 return B_BAD_TEAM_ID; 3471 3472 // search for watcher 3473 team_watcher* watcher = NULL; 3474 while ((watcher = (team_watcher*)list_get_next_item( 3475 &team->watcher_list, watcher)) != NULL) { 3476 if (watcher->hook == hook && watcher->data == data) { 3477 // got it! 3478 list_remove_item(&team->watcher_list, watcher); 3479 break; 3480 } 3481 } 3482 3483 team->UnlockAndReleaseReference(); 3484 3485 if (watcher == NULL) 3486 return B_ENTRY_NOT_FOUND; 3487 3488 free(watcher); 3489 return B_OK; 3490 } 3491 3492 3493 /*! Allocates a user_thread structure from the team. 3494 The team lock must be held, unless the function is called for the team's 3495 main thread. Interrupts must be enabled. 3496 */ 3497 struct user_thread* 3498 team_allocate_user_thread(Team* team) 3499 { 3500 if (team->user_data == 0) 3501 return NULL; 3502 3503 // take an entry from the free list, if any 3504 if (struct free_user_thread* entry = team->free_user_threads) { 3505 user_thread* thread = entry->thread; 3506 team->free_user_threads = entry->next; 3507 free(entry); 3508 return thread; 3509 } 3510 3511 while (true) { 3512 // enough space left? 3513 size_t needed = ROUNDUP(sizeof(user_thread), CACHE_LINE_SIZE); 3514 if (team->user_data_size - team->used_user_data < needed) { 3515 // try to resize the area 3516 if (resize_area(team->user_data_area, 3517 team->user_data_size + B_PAGE_SIZE) != B_OK) { 3518 return NULL; 3519 } 3520 3521 // resized user area successfully -- try to allocate the user_thread 3522 // again 3523 team->user_data_size += B_PAGE_SIZE; 3524 continue; 3525 } 3526 3527 // allocate the user_thread 3528 user_thread* thread 3529 = (user_thread*)(team->user_data + team->used_user_data); 3530 team->used_user_data += needed; 3531 3532 return thread; 3533 } 3534 } 3535 3536 3537 /*! Frees the given user_thread structure. 3538 The team's lock must not be held. Interrupts must be enabled. 3539 \param team The team the user thread was allocated from. 3540 \param userThread The user thread to free. 3541 */ 3542 void 3543 team_free_user_thread(Team* team, struct user_thread* userThread) 3544 { 3545 if (userThread == NULL) 3546 return; 3547 3548 // create a free list entry 3549 free_user_thread* entry 3550 = (free_user_thread*)malloc(sizeof(free_user_thread)); 3551 if (entry == NULL) { 3552 // we have to leak the user thread :-/ 3553 return; 3554 } 3555 3556 // add to free list 3557 TeamLocker teamLocker(team); 3558 3559 entry->thread = userThread; 3560 entry->next = team->free_user_threads; 3561 team->free_user_threads = entry; 3562 } 3563 3564 3565 // #pragma mark - Associated data interface 3566 3567 3568 AssociatedData::AssociatedData() 3569 : 3570 fOwner(NULL) 3571 { 3572 } 3573 3574 3575 AssociatedData::~AssociatedData() 3576 { 3577 } 3578 3579 3580 void 3581 AssociatedData::OwnerDeleted(AssociatedDataOwner* owner) 3582 { 3583 } 3584 3585 3586 AssociatedDataOwner::AssociatedDataOwner() 3587 { 3588 mutex_init(&fLock, "associated data owner"); 3589 } 3590 3591 3592 AssociatedDataOwner::~AssociatedDataOwner() 3593 { 3594 mutex_destroy(&fLock); 3595 } 3596 3597 3598 bool 3599 AssociatedDataOwner::AddData(AssociatedData* data) 3600 { 3601 MutexLocker locker(fLock); 3602 3603 if (data->Owner() != NULL) 3604 return false; 3605 3606 data->AcquireReference(); 3607 fList.Add(data); 3608 data->SetOwner(this); 3609 3610 return true; 3611 } 3612 3613 3614 bool 3615 AssociatedDataOwner::RemoveData(AssociatedData* data) 3616 { 3617 MutexLocker locker(fLock); 3618 3619 if (data->Owner() != this) 3620 return false; 3621 3622 data->SetOwner(NULL); 3623 fList.Remove(data); 3624 3625 locker.Unlock(); 3626 3627 data->ReleaseReference(); 3628 3629 return true; 3630 } 3631 3632 3633 void 3634 AssociatedDataOwner::PrepareForDeletion() 3635 { 3636 MutexLocker locker(fLock); 3637 3638 // move all data to a temporary list and unset the owner 3639 DataList list; 3640 list.MoveFrom(&fList); 3641 3642 for (DataList::Iterator it = list.GetIterator(); 3643 AssociatedData* data = it.Next();) { 3644 data->SetOwner(NULL); 3645 } 3646 3647 locker.Unlock(); 3648 3649 // call the notification hooks and release our references 3650 while (AssociatedData* data = list.RemoveHead()) { 3651 data->OwnerDeleted(this); 3652 data->ReleaseReference(); 3653 } 3654 } 3655 3656 3657 /*! Associates data with the current team. 3658 When the team is deleted, the data object is notified. 3659 The team acquires a reference to the object. 3660 3661 \param data The data object. 3662 \return \c true on success, \c false otherwise. Fails only when the supplied 3663 data object is already associated with another owner. 3664 */ 3665 bool 3666 team_associate_data(AssociatedData* data) 3667 { 3668 return thread_get_current_thread()->team->AddData(data); 3669 } 3670 3671 3672 /*! Dissociates data from the current team. 3673 Balances an earlier call to team_associate_data(). 3674 3675 \param data The data object. 3676 \return \c true on success, \c false otherwise. Fails only when the data 3677 object is not associated with the current team. 3678 */ 3679 bool 3680 team_dissociate_data(AssociatedData* data) 3681 { 3682 return thread_get_current_thread()->team->RemoveData(data); 3683 } 3684 3685 3686 // #pragma mark - Public kernel API 3687 3688 3689 thread_id 3690 load_image(int32 argCount, const char** args, const char** env) 3691 { 3692 return load_image_etc(argCount, args, env, B_NORMAL_PRIORITY, 3693 B_CURRENT_TEAM, B_WAIT_TILL_LOADED); 3694 } 3695 3696 3697 thread_id 3698 load_image_etc(int32 argCount, const char* const* args, 3699 const char* const* env, int32 priority, team_id parentID, uint32 flags) 3700 { 3701 // we need to flatten the args and environment 3702 3703 if (args == NULL) 3704 return B_BAD_VALUE; 3705 3706 // determine total needed size 3707 int32 argSize = 0; 3708 for (int32 i = 0; i < argCount; i++) 3709 argSize += strlen(args[i]) + 1; 3710 3711 int32 envCount = 0; 3712 int32 envSize = 0; 3713 while (env != NULL && env[envCount] != NULL) 3714 envSize += strlen(env[envCount++]) + 1; 3715 3716 int32 size = (argCount + envCount + 2) * sizeof(char*) + argSize + envSize; 3717 if (size > MAX_PROCESS_ARGS_SIZE) 3718 return B_TOO_MANY_ARGS; 3719 3720 // allocate space 3721 char** flatArgs = (char**)malloc(size); 3722 if (flatArgs == NULL) 3723 return B_NO_MEMORY; 3724 3725 char** slot = flatArgs; 3726 char* stringSpace = (char*)(flatArgs + argCount + envCount + 2); 3727 3728 // copy arguments and environment 3729 for (int32 i = 0; i < argCount; i++) { 3730 int32 argSize = strlen(args[i]) + 1; 3731 memcpy(stringSpace, args[i], argSize); 3732 *slot++ = stringSpace; 3733 stringSpace += argSize; 3734 } 3735 3736 *slot++ = NULL; 3737 3738 for (int32 i = 0; i < envCount; i++) { 3739 int32 envSize = strlen(env[i]) + 1; 3740 memcpy(stringSpace, env[i], envSize); 3741 *slot++ = stringSpace; 3742 stringSpace += envSize; 3743 } 3744 3745 *slot++ = NULL; 3746 3747 thread_id thread = load_image_internal(flatArgs, size, argCount, envCount, 3748 B_NORMAL_PRIORITY, parentID, B_WAIT_TILL_LOADED, -1, 0); 3749 3750 free(flatArgs); 3751 // load_image_internal() unset our variable if it took over ownership 3752 3753 return thread; 3754 } 3755 3756 3757 status_t 3758 wait_for_team(team_id id, status_t* _returnCode) 3759 { 3760 // check whether the team exists 3761 InterruptsReadSpinLocker teamsLocker(sTeamHashLock); 3762 3763 Team* team = team_get_team_struct_locked(id); 3764 if (team == NULL) 3765 return B_BAD_TEAM_ID; 3766 3767 id = team->id; 3768 3769 teamsLocker.Unlock(); 3770 3771 // wait for the main thread (it has the same ID as the team) 3772 return wait_for_thread(id, _returnCode); 3773 } 3774 3775 3776 status_t 3777 kill_team(team_id id) 3778 { 3779 InterruptsReadSpinLocker teamsLocker(sTeamHashLock); 3780 3781 Team* team = team_get_team_struct_locked(id); 3782 if (team == NULL) 3783 return B_BAD_TEAM_ID; 3784 3785 id = team->id; 3786 3787 teamsLocker.Unlock(); 3788 3789 if (team == sKernelTeam) 3790 return B_NOT_ALLOWED; 3791 3792 // Just kill the team's main thread (it has same ID as the team). The 3793 // cleanup code there will take care of the team. 3794 return kill_thread(id); 3795 } 3796 3797 3798 status_t 3799 _get_team_info(team_id id, team_info* info, size_t size) 3800 { 3801 // get the team 3802 Team* team = Team::Get(id); 3803 if (team == NULL) 3804 return B_BAD_TEAM_ID; 3805 BReference<Team> teamReference(team, true); 3806 3807 // fill in the info 3808 return fill_team_info(team, info, size); 3809 } 3810 3811 3812 status_t 3813 _get_next_team_info(int32* cookie, team_info* info, size_t size) 3814 { 3815 int32 slot = *cookie; 3816 if (slot < 1) 3817 slot = 1; 3818 3819 InterruptsReadSpinLocker locker(sTeamHashLock); 3820 3821 team_id lastTeamID = peek_next_thread_id(); 3822 // TODO: This is broken, since the id can wrap around! 3823 3824 // get next valid team 3825 Team* team = NULL; 3826 while (slot < lastTeamID && !(team = team_get_team_struct_locked(slot))) 3827 slot++; 3828 3829 if (team == NULL) 3830 return B_BAD_TEAM_ID; 3831 3832 // get a reference to the team and unlock 3833 BReference<Team> teamReference(team); 3834 locker.Unlock(); 3835 3836 // fill in the info 3837 *cookie = ++slot; 3838 return fill_team_info(team, info, size); 3839 } 3840 3841 3842 status_t 3843 _get_team_usage_info(team_id id, int32 who, team_usage_info* info, size_t size) 3844 { 3845 if (size != sizeof(team_usage_info)) 3846 return B_BAD_VALUE; 3847 3848 return common_get_team_usage_info(id, who, info, 0); 3849 } 3850 3851 3852 pid_t 3853 getpid(void) 3854 { 3855 return thread_get_current_thread()->team->id; 3856 } 3857 3858 3859 pid_t 3860 getppid(void) 3861 { 3862 Team* team = thread_get_current_thread()->team; 3863 3864 TeamLocker teamLocker(team); 3865 3866 return team->parent->id; 3867 } 3868 3869 3870 pid_t 3871 getpgid(pid_t id) 3872 { 3873 if (id < 0) { 3874 errno = EINVAL; 3875 return -1; 3876 } 3877 3878 if (id == 0) { 3879 // get process group of the calling process 3880 Team* team = thread_get_current_thread()->team; 3881 TeamLocker teamLocker(team); 3882 return team->group_id; 3883 } 3884 3885 // get the team 3886 Team* team = Team::GetAndLock(id); 3887 if (team == NULL) { 3888 errno = ESRCH; 3889 return -1; 3890 } 3891 3892 // get the team's process group ID 3893 pid_t groupID = team->group_id; 3894 3895 team->UnlockAndReleaseReference(); 3896 3897 return groupID; 3898 } 3899 3900 3901 pid_t 3902 getsid(pid_t id) 3903 { 3904 if (id < 0) { 3905 errno = EINVAL; 3906 return -1; 3907 } 3908 3909 if (id == 0) { 3910 // get session of the calling process 3911 Team* team = thread_get_current_thread()->team; 3912 TeamLocker teamLocker(team); 3913 return team->session_id; 3914 } 3915 3916 // get the team 3917 Team* team = Team::GetAndLock(id); 3918 if (team == NULL) { 3919 errno = ESRCH; 3920 return -1; 3921 } 3922 3923 // get the team's session ID 3924 pid_t sessionID = team->session_id; 3925 3926 team->UnlockAndReleaseReference(); 3927 3928 return sessionID; 3929 } 3930 3931 3932 // #pragma mark - User syscalls 3933 3934 3935 status_t 3936 _user_exec(const char* userPath, const char* const* userFlatArgs, 3937 size_t flatArgsSize, int32 argCount, int32 envCount, mode_t umask) 3938 { 3939 // NOTE: Since this function normally doesn't return, don't use automatic 3940 // variables that need destruction in the function scope. 3941 char path[B_PATH_NAME_LENGTH]; 3942 3943 if (!IS_USER_ADDRESS(userPath) || !IS_USER_ADDRESS(userFlatArgs) 3944 || user_strlcpy(path, userPath, sizeof(path)) < B_OK) 3945 return B_BAD_ADDRESS; 3946 3947 // copy and relocate the flat arguments 3948 char** flatArgs; 3949 status_t error = copy_user_process_args(userFlatArgs, flatArgsSize, 3950 argCount, envCount, flatArgs); 3951 3952 if (error == B_OK) { 3953 error = exec_team(path, flatArgs, _ALIGN(flatArgsSize), argCount, 3954 envCount, umask); 3955 // this one only returns in case of error 3956 } 3957 3958 free(flatArgs); 3959 return error; 3960 } 3961 3962 3963 thread_id 3964 _user_fork(void) 3965 { 3966 return fork_team(); 3967 } 3968 3969 3970 pid_t 3971 _user_wait_for_child(thread_id child, uint32 flags, siginfo_t* userInfo, 3972 team_usage_info* usageInfo) 3973 { 3974 if (userInfo != NULL && !IS_USER_ADDRESS(userInfo)) 3975 return B_BAD_ADDRESS; 3976 if (usageInfo != NULL && !IS_USER_ADDRESS(usageInfo)) 3977 return B_BAD_ADDRESS; 3978 3979 siginfo_t info; 3980 team_usage_info usage_info; 3981 pid_t foundChild = wait_for_child(child, flags, info, usage_info); 3982 if (foundChild < 0) 3983 return syscall_restart_handle_post(foundChild); 3984 3985 // copy info back to userland 3986 if (userInfo != NULL && user_memcpy(userInfo, &info, sizeof(info)) != B_OK) 3987 return B_BAD_ADDRESS; 3988 // copy usage_info back to userland 3989 if (usageInfo != NULL && user_memcpy(usageInfo, &usage_info, 3990 sizeof(usage_info)) != B_OK) { 3991 return B_BAD_ADDRESS; 3992 } 3993 3994 return foundChild; 3995 } 3996 3997 3998 pid_t 3999 _user_process_info(pid_t process, int32 which) 4000 { 4001 // we only allow to return the parent of the current process 4002 if (which == PARENT_ID 4003 && process != 0 && process != thread_get_current_thread()->team->id) 4004 return B_BAD_VALUE; 4005 4006 pid_t result; 4007 switch (which) { 4008 case SESSION_ID: 4009 result = getsid(process); 4010 break; 4011 case GROUP_ID: 4012 result = getpgid(process); 4013 break; 4014 case PARENT_ID: 4015 result = getppid(); 4016 break; 4017 default: 4018 return B_BAD_VALUE; 4019 } 4020 4021 return result >= 0 ? result : errno; 4022 } 4023 4024 4025 pid_t 4026 _user_setpgid(pid_t processID, pid_t groupID) 4027 { 4028 // setpgid() can be called either by the parent of the target process or 4029 // by the process itself to do one of two things: 4030 // * Create a new process group with the target process' ID and the target 4031 // process as group leader. 4032 // * Set the target process' process group to an already existing one in the 4033 // same session. 4034 4035 if (groupID < 0) 4036 return B_BAD_VALUE; 4037 4038 Team* currentTeam = thread_get_current_thread()->team; 4039 if (processID == 0) 4040 processID = currentTeam->id; 4041 4042 // if the group ID is not specified, use the target process' ID 4043 if (groupID == 0) 4044 groupID = processID; 4045 4046 // We loop when running into the following race condition: We create a new 4047 // process group, because there isn't one with that ID yet, but later when 4048 // trying to publish it, we find that someone else created and published 4049 // a group with that ID in the meantime. In that case we just restart the 4050 // whole action. 4051 while (true) { 4052 // Look up the process group by ID. If it doesn't exist yet and we are 4053 // allowed to create a new one, do that. 4054 ProcessGroup* group = ProcessGroup::Get(groupID); 4055 bool newGroup = false; 4056 if (group == NULL) { 4057 if (groupID != processID) 4058 return B_NOT_ALLOWED; 4059 4060 group = new(std::nothrow) ProcessGroup(groupID); 4061 if (group == NULL) 4062 return B_NO_MEMORY; 4063 4064 newGroup = true; 4065 } 4066 BReference<ProcessGroup> groupReference(group, true); 4067 4068 // get the target team 4069 Team* team = Team::Get(processID); 4070 if (team == NULL) 4071 return ESRCH; 4072 BReference<Team> teamReference(team, true); 4073 4074 // lock the new process group and the team's current process group 4075 while (true) { 4076 // lock the team's current process group 4077 team->LockProcessGroup(); 4078 4079 ProcessGroup* oldGroup = team->group; 4080 if (oldGroup == group) { 4081 // it's the same as the target group, so just bail out 4082 oldGroup->Unlock(); 4083 return group->id; 4084 } 4085 4086 oldGroup->AcquireReference(); 4087 4088 // lock the target process group, if locking order allows it 4089 if (newGroup || group->id > oldGroup->id) { 4090 group->Lock(); 4091 break; 4092 } 4093 4094 // try to lock 4095 if (group->TryLock()) 4096 break; 4097 4098 // no dice -- unlock the team's current process group and relock in 4099 // the correct order 4100 oldGroup->Unlock(); 4101 4102 group->Lock(); 4103 oldGroup->Lock(); 4104 4105 // check whether things are still the same 4106 TeamLocker teamLocker(team); 4107 if (team->group == oldGroup) 4108 break; 4109 4110 // something changed -- unlock everything and retry 4111 teamLocker.Unlock(); 4112 oldGroup->Unlock(); 4113 group->Unlock(); 4114 oldGroup->ReleaseReference(); 4115 } 4116 4117 // we now have references and locks of both new and old process group 4118 BReference<ProcessGroup> oldGroupReference(team->group, true); 4119 AutoLocker<ProcessGroup> oldGroupLocker(team->group, true); 4120 AutoLocker<ProcessGroup> groupLocker(group, true); 4121 4122 // also lock the target team and its parent 4123 team->LockTeamAndParent(false); 4124 TeamLocker parentLocker(team->parent, true); 4125 TeamLocker teamLocker(team, true); 4126 4127 // perform the checks 4128 if (team == currentTeam) { 4129 // we set our own group 4130 4131 // we must not change our process group ID if we're a session leader 4132 if (is_session_leader(currentTeam)) 4133 return B_NOT_ALLOWED; 4134 } else { 4135 // Calling team != target team. The target team must be a child of 4136 // the calling team and in the same session. (If that's the case it 4137 // isn't a session leader either.) 4138 if (team->parent != currentTeam 4139 || team->session_id != currentTeam->session_id) { 4140 return B_NOT_ALLOWED; 4141 } 4142 4143 // The call is also supposed to fail on a child, when the child has 4144 // already executed exec*() [EACCES]. 4145 if ((team->flags & TEAM_FLAG_EXEC_DONE) != 0) 4146 return EACCES; 4147 } 4148 4149 // If we created a new process group, publish it now. 4150 if (newGroup) { 4151 InterruptsSpinLocker groupHashLocker(sGroupHashLock); 4152 if (sGroupHash.Lookup(groupID)) { 4153 // A group with the group ID appeared since we first checked. 4154 // Back to square one. 4155 continue; 4156 } 4157 4158 group->PublishLocked(team->group->Session()); 4159 } else if (group->Session()->id != team->session_id) { 4160 // The existing target process group belongs to a different session. 4161 // That's not allowed. 4162 return B_NOT_ALLOWED; 4163 } 4164 4165 // Everything is ready -- set the group. 4166 remove_team_from_group(team); 4167 insert_team_into_group(group, team); 4168 4169 // Changing the process group might have changed the situation for a 4170 // parent waiting in wait_for_child(). Hence we notify it. 4171 team->parent->dead_children.condition_variable.NotifyAll(); 4172 4173 return group->id; 4174 } 4175 } 4176 4177 4178 pid_t 4179 _user_setsid(void) 4180 { 4181 Team* team = thread_get_current_thread()->team; 4182 4183 // create a new process group and session 4184 ProcessGroup* group = new(std::nothrow) ProcessGroup(team->id); 4185 if (group == NULL) 4186 return B_NO_MEMORY; 4187 BReference<ProcessGroup> groupReference(group, true); 4188 AutoLocker<ProcessGroup> groupLocker(group); 4189 4190 ProcessSession* session = new(std::nothrow) ProcessSession(group->id); 4191 if (session == NULL) 4192 return B_NO_MEMORY; 4193 BReference<ProcessSession> sessionReference(session, true); 4194 4195 // lock the team's current process group, parent, and the team itself 4196 team->LockTeamParentAndProcessGroup(); 4197 BReference<ProcessGroup> oldGroupReference(team->group); 4198 AutoLocker<ProcessGroup> oldGroupLocker(team->group, true); 4199 TeamLocker parentLocker(team->parent, true); 4200 TeamLocker teamLocker(team, true); 4201 4202 // the team must not already be a process group leader 4203 if (is_process_group_leader(team)) 4204 return B_NOT_ALLOWED; 4205 4206 // remove the team from the old and add it to the new process group 4207 remove_team_from_group(team); 4208 group->Publish(session); 4209 insert_team_into_group(group, team); 4210 4211 // Changing the process group might have changed the situation for a 4212 // parent waiting in wait_for_child(). Hence we notify it. 4213 team->parent->dead_children.condition_variable.NotifyAll(); 4214 4215 return group->id; 4216 } 4217 4218 4219 status_t 4220 _user_wait_for_team(team_id id, status_t* _userReturnCode) 4221 { 4222 status_t returnCode; 4223 status_t status; 4224 4225 if (_userReturnCode != NULL && !IS_USER_ADDRESS(_userReturnCode)) 4226 return B_BAD_ADDRESS; 4227 4228 status = wait_for_team(id, &returnCode); 4229 if (status >= B_OK && _userReturnCode != NULL) { 4230 if (user_memcpy(_userReturnCode, &returnCode, sizeof(returnCode)) 4231 != B_OK) 4232 return B_BAD_ADDRESS; 4233 return B_OK; 4234 } 4235 4236 return syscall_restart_handle_post(status); 4237 } 4238 4239 4240 thread_id 4241 _user_load_image(const char* const* userFlatArgs, size_t flatArgsSize, 4242 int32 argCount, int32 envCount, int32 priority, uint32 flags, 4243 port_id errorPort, uint32 errorToken) 4244 { 4245 TRACE(("_user_load_image: argc = %" B_PRId32 "\n", argCount)); 4246 4247 if (argCount < 1) 4248 return B_BAD_VALUE; 4249 4250 // copy and relocate the flat arguments 4251 char** flatArgs; 4252 status_t error = copy_user_process_args(userFlatArgs, flatArgsSize, 4253 argCount, envCount, flatArgs); 4254 if (error != B_OK) 4255 return error; 4256 4257 thread_id thread = load_image_internal(flatArgs, _ALIGN(flatArgsSize), 4258 argCount, envCount, priority, B_CURRENT_TEAM, flags, errorPort, 4259 errorToken); 4260 4261 free(flatArgs); 4262 // load_image_internal() unset our variable if it took over ownership 4263 4264 return thread; 4265 } 4266 4267 4268 void 4269 _user_exit_team(status_t returnValue) 4270 { 4271 Thread* thread = thread_get_current_thread(); 4272 Team* team = thread->team; 4273 4274 // set this thread's exit status 4275 thread->exit.status = returnValue; 4276 4277 // set the team exit status 4278 TeamLocker teamLocker(team); 4279 4280 if (!team->exit.initialized) { 4281 team->exit.reason = CLD_EXITED; 4282 team->exit.signal = 0; 4283 team->exit.signaling_user = 0; 4284 team->exit.status = returnValue; 4285 team->exit.initialized = true; 4286 } 4287 4288 teamLocker.Unlock(); 4289 4290 // Stop the thread, if the team is being debugged and that has been 4291 // requested. 4292 if ((atomic_get(&team->debug_info.flags) & B_TEAM_DEBUG_PREVENT_EXIT) != 0) 4293 user_debug_stop_thread(); 4294 4295 // Send this thread a SIGKILL. This makes sure the thread will not return to 4296 // userland. The signal handling code forwards the signal to the main 4297 // thread (if that's not already this one), which will take the team down. 4298 Signal signal(SIGKILL, SI_USER, B_OK, team->id); 4299 send_signal_to_thread(thread, signal, 0); 4300 } 4301 4302 4303 status_t 4304 _user_kill_team(team_id team) 4305 { 4306 return kill_team(team); 4307 } 4308 4309 4310 status_t 4311 _user_get_team_info(team_id id, team_info* userInfo) 4312 { 4313 status_t status; 4314 team_info info; 4315 4316 if (!IS_USER_ADDRESS(userInfo)) 4317 return B_BAD_ADDRESS; 4318 4319 status = _get_team_info(id, &info, sizeof(team_info)); 4320 if (status == B_OK) { 4321 if (user_memcpy(userInfo, &info, sizeof(team_info)) < B_OK) 4322 return B_BAD_ADDRESS; 4323 } 4324 4325 return status; 4326 } 4327 4328 4329 status_t 4330 _user_get_next_team_info(int32* userCookie, team_info* userInfo) 4331 { 4332 status_t status; 4333 team_info info; 4334 int32 cookie; 4335 4336 if (!IS_USER_ADDRESS(userCookie) 4337 || !IS_USER_ADDRESS(userInfo) 4338 || user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK) 4339 return B_BAD_ADDRESS; 4340 4341 status = _get_next_team_info(&cookie, &info, sizeof(team_info)); 4342 if (status != B_OK) 4343 return status; 4344 4345 if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK 4346 || user_memcpy(userInfo, &info, sizeof(team_info)) < B_OK) 4347 return B_BAD_ADDRESS; 4348 4349 return status; 4350 } 4351 4352 4353 team_id 4354 _user_get_current_team(void) 4355 { 4356 return team_get_current_team_id(); 4357 } 4358 4359 4360 status_t 4361 _user_get_team_usage_info(team_id team, int32 who, team_usage_info* userInfo, 4362 size_t size) 4363 { 4364 if (size != sizeof(team_usage_info)) 4365 return B_BAD_VALUE; 4366 4367 team_usage_info info; 4368 status_t status = common_get_team_usage_info(team, who, &info, 4369 B_CHECK_PERMISSION); 4370 4371 if (userInfo == NULL || !IS_USER_ADDRESS(userInfo) 4372 || user_memcpy(userInfo, &info, size) != B_OK) { 4373 return B_BAD_ADDRESS; 4374 } 4375 4376 return status; 4377 } 4378 4379 4380 status_t 4381 _user_get_extended_team_info(team_id teamID, uint32 flags, void* buffer, 4382 size_t size, size_t* _sizeNeeded) 4383 { 4384 // check parameters 4385 if ((buffer != NULL && !IS_USER_ADDRESS(buffer)) 4386 || (buffer == NULL && size > 0) 4387 || _sizeNeeded == NULL || !IS_USER_ADDRESS(_sizeNeeded)) { 4388 return B_BAD_ADDRESS; 4389 } 4390 4391 KMessage info; 4392 4393 if ((flags & B_TEAM_INFO_BASIC) != 0) { 4394 // allocate memory for a copy of the needed team data 4395 struct ExtendedTeamData { 4396 team_id id; 4397 pid_t group_id; 4398 pid_t session_id; 4399 uid_t real_uid; 4400 gid_t real_gid; 4401 uid_t effective_uid; 4402 gid_t effective_gid; 4403 char name[B_OS_NAME_LENGTH]; 4404 } teamClone; 4405 4406 io_context* ioContext; 4407 { 4408 // get the team structure 4409 Team* team = Team::GetAndLock(teamID); 4410 if (team == NULL) 4411 return B_BAD_TEAM_ID; 4412 BReference<Team> teamReference(team, true); 4413 TeamLocker teamLocker(team, true); 4414 4415 // copy the data 4416 teamClone.id = team->id; 4417 strlcpy(teamClone.name, team->Name(), sizeof(teamClone.name)); 4418 teamClone.group_id = team->group_id; 4419 teamClone.session_id = team->session_id; 4420 teamClone.real_uid = team->real_uid; 4421 teamClone.real_gid = team->real_gid; 4422 teamClone.effective_uid = team->effective_uid; 4423 teamClone.effective_gid = team->effective_gid; 4424 4425 // also fetch a reference to the I/O context 4426 ioContext = team->io_context; 4427 vfs_get_io_context(ioContext); 4428 } 4429 CObjectDeleter<io_context> ioContextPutter(ioContext, 4430 &vfs_put_io_context); 4431 4432 // add the basic data to the info message 4433 if (info.AddInt32("id", teamClone.id) != B_OK 4434 || info.AddString("name", teamClone.name) != B_OK 4435 || info.AddInt32("process group", teamClone.group_id) != B_OK 4436 || info.AddInt32("session", teamClone.session_id) != B_OK 4437 || info.AddInt32("uid", teamClone.real_uid) != B_OK 4438 || info.AddInt32("gid", teamClone.real_gid) != B_OK 4439 || info.AddInt32("euid", teamClone.effective_uid) != B_OK 4440 || info.AddInt32("egid", teamClone.effective_gid) != B_OK) { 4441 return B_NO_MEMORY; 4442 } 4443 4444 // get the current working directory from the I/O context 4445 dev_t cwdDevice; 4446 ino_t cwdDirectory; 4447 { 4448 MutexLocker ioContextLocker(ioContext->io_mutex); 4449 vfs_vnode_to_node_ref(ioContext->cwd, &cwdDevice, &cwdDirectory); 4450 } 4451 4452 if (info.AddInt32("cwd device", cwdDevice) != B_OK 4453 || info.AddInt64("cwd directory", cwdDirectory) != B_OK) { 4454 return B_NO_MEMORY; 4455 } 4456 } 4457 4458 // TODO: Support the other flags! 4459 4460 // copy the needed size and, if it fits, the message back to userland 4461 size_t sizeNeeded = info.ContentSize(); 4462 if (user_memcpy(_sizeNeeded, &sizeNeeded, sizeof(sizeNeeded)) != B_OK) 4463 return B_BAD_ADDRESS; 4464 4465 if (sizeNeeded > size) 4466 return B_BUFFER_OVERFLOW; 4467 4468 if (user_memcpy(buffer, info.Buffer(), sizeNeeded) != B_OK) 4469 return B_BAD_ADDRESS; 4470 4471 return B_OK; 4472 } 4473