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 474 // dead children 475 dead_children.count = 0; 476 dead_children.kernel_time = 0; 477 dead_children.user_time = 0; 478 479 // job control entry 480 job_control_entry = new(nothrow) ::job_control_entry; 481 if (job_control_entry != NULL) { 482 job_control_entry->state = JOB_CONTROL_STATE_NONE; 483 job_control_entry->thread = id; 484 job_control_entry->team = this; 485 } 486 487 // exit status -- setting initialized to false suffices 488 exit.initialized = false; 489 490 list_init(&sem_list); 491 list_init_etc(&port_list, port_team_link_offset()); 492 list_init(&image_list); 493 list_init(&watcher_list); 494 495 clear_team_debug_info(&debug_info, true); 496 497 // init dead/stopped/continued children condition vars 498 dead_children.condition_variable.Init(&dead_children, "team children"); 499 500 B_INITIALIZE_SPINLOCK(&time_lock); 501 B_INITIALIZE_SPINLOCK(&signal_lock); 502 503 fQueuedSignalsCounter = new(std::nothrow) BKernel::QueuedSignalsCounter( 504 kernel ? -1 : MAX_QUEUED_SIGNALS); 505 memset(fSignalActions, 0, sizeof(fSignalActions)); 506 507 fUserDefinedTimerCount = 0; 508 509 fCoreDumpCondition = NULL; 510 } 511 512 513 Team::~Team() 514 { 515 // get rid of all associated data 516 PrepareForDeletion(); 517 518 if (io_context != NULL) 519 vfs_put_io_context(io_context); 520 delete_owned_ports(this); 521 sem_delete_owned_sems(this); 522 523 DeleteUserTimers(false); 524 525 fPendingSignals.Clear(); 526 527 if (fQueuedSignalsCounter != NULL) 528 fQueuedSignalsCounter->ReleaseReference(); 529 530 while (thread_death_entry* threadDeathEntry 531 = (thread_death_entry*)list_remove_head_item(&dead_threads)) { 532 free(threadDeathEntry); 533 } 534 535 while (::job_control_entry* entry = dead_children.entries.RemoveHead()) 536 delete entry; 537 538 while (free_user_thread* entry = free_user_threads) { 539 free_user_threads = entry->next; 540 free(entry); 541 } 542 543 malloc_referenced_release(supplementary_groups); 544 545 delete job_control_entry; 546 // usually already NULL and transferred to the parent 547 548 mutex_destroy(&fLock); 549 } 550 551 552 /*static*/ Team* 553 Team::Create(team_id id, const char* name, bool kernel) 554 { 555 // create the team object 556 Team* team = new(std::nothrow) Team(id, kernel); 557 if (team == NULL) 558 return NULL; 559 ObjectDeleter<Team> teamDeleter(team); 560 561 if (name != NULL) 562 team->SetName(name); 563 564 // check initialization 565 if (team->job_control_entry == NULL || team->fQueuedSignalsCounter == NULL) 566 return NULL; 567 568 // finish initialization (arch specifics) 569 if (arch_team_init_team_struct(team, kernel) != B_OK) 570 return NULL; 571 572 if (!kernel) { 573 status_t error = user_timer_create_team_timers(team); 574 if (error != B_OK) 575 return NULL; 576 } 577 578 // everything went fine 579 return teamDeleter.Detach(); 580 } 581 582 583 /*! \brief Returns the team with the given ID. 584 Returns a reference to the team. 585 Team and thread spinlock must not be held. 586 */ 587 /*static*/ Team* 588 Team::Get(team_id id) 589 { 590 if (id == B_CURRENT_TEAM) { 591 Team* team = thread_get_current_thread()->team; 592 team->AcquireReference(); 593 return team; 594 } 595 596 InterruptsReadSpinLocker locker(sTeamHashLock); 597 Team* team = sTeamHash.Lookup(id); 598 if (team != NULL) 599 team->AcquireReference(); 600 return team; 601 } 602 603 604 /*! \brief Returns the team with the given ID in a locked state. 605 Returns a reference to the team. 606 Team and thread spinlock must not be held. 607 */ 608 /*static*/ Team* 609 Team::GetAndLock(team_id id) 610 { 611 // get the team 612 Team* team = Get(id); 613 if (team == NULL) 614 return NULL; 615 616 // lock it 617 team->Lock(); 618 619 // only return the team, when it isn't already dying 620 if (team->state >= TEAM_STATE_SHUTDOWN) { 621 team->Unlock(); 622 team->ReleaseReference(); 623 return NULL; 624 } 625 626 return team; 627 } 628 629 630 /*! Locks the team and its parent team (if any). 631 The caller must hold a reference to the team or otherwise make sure that 632 it won't be deleted. 633 If the team doesn't have a parent, only the team itself is locked. If the 634 team's parent is the kernel team and \a dontLockParentIfKernel is \c true, 635 only the team itself is locked. 636 637 \param dontLockParentIfKernel If \c true, the team's parent team is only 638 locked, if it is not the kernel team. 639 */ 640 void 641 Team::LockTeamAndParent(bool dontLockParentIfKernel) 642 { 643 // The locking order is parent -> child. Since the parent can change as long 644 // as we don't lock the team, we need to do a trial and error loop. 645 Lock(); 646 647 while (true) { 648 // If the team doesn't have a parent, we're done. Otherwise try to lock 649 // the parent.This will succeed in most cases, simplifying things. 650 Team* parent = this->parent; 651 if (parent == NULL || (dontLockParentIfKernel && parent == sKernelTeam) 652 || parent->TryLock()) { 653 return; 654 } 655 656 // get a temporary reference to the parent, unlock this team, lock the 657 // parent, and re-lock this team 658 BReference<Team> parentReference(parent); 659 660 Unlock(); 661 parent->Lock(); 662 Lock(); 663 664 // If the parent hasn't changed in the meantime, we're done. 665 if (this->parent == parent) 666 return; 667 668 // The parent has changed -- unlock and retry. 669 parent->Unlock(); 670 } 671 } 672 673 674 /*! Unlocks the team and its parent team (if any). 675 */ 676 void 677 Team::UnlockTeamAndParent() 678 { 679 if (parent != NULL) 680 parent->Unlock(); 681 682 Unlock(); 683 } 684 685 686 /*! Locks the team, its parent team (if any), and the team's process group. 687 The caller must hold a reference to the team or otherwise make sure that 688 it won't be deleted. 689 If the team doesn't have a parent, only the team itself is locked. 690 */ 691 void 692 Team::LockTeamParentAndProcessGroup() 693 { 694 LockTeamAndProcessGroup(); 695 696 // We hold the group's and the team's lock, but not the parent team's lock. 697 // If we have a parent, try to lock it. 698 if (this->parent == NULL || this->parent->TryLock()) 699 return; 700 701 // No success -- unlock the team and let LockTeamAndParent() do the rest of 702 // the job. 703 Unlock(); 704 LockTeamAndParent(false); 705 } 706 707 708 /*! Unlocks the team, its parent team (if any), and the team's process group. 709 */ 710 void 711 Team::UnlockTeamParentAndProcessGroup() 712 { 713 group->Unlock(); 714 715 if (parent != NULL) 716 parent->Unlock(); 717 718 Unlock(); 719 } 720 721 722 void 723 Team::LockTeamAndProcessGroup() 724 { 725 // The locking order is process group -> child. Since the process group can 726 // change as long as we don't lock the team, we need to do a trial and error 727 // loop. 728 Lock(); 729 730 while (true) { 731 // Try to lock the group. This will succeed in most cases, simplifying 732 // things. 733 ProcessGroup* group = this->group; 734 if (group->TryLock()) 735 return; 736 737 // get a temporary reference to the group, unlock this team, lock the 738 // group, and re-lock this team 739 BReference<ProcessGroup> groupReference(group); 740 741 Unlock(); 742 group->Lock(); 743 Lock(); 744 745 // If the group hasn't changed in the meantime, we're done. 746 if (this->group == group) 747 return; 748 749 // The group has changed -- unlock and retry. 750 group->Unlock(); 751 } 752 } 753 754 755 void 756 Team::UnlockTeamAndProcessGroup() 757 { 758 group->Unlock(); 759 Unlock(); 760 } 761 762 763 void 764 Team::SetName(const char* name) 765 { 766 if (const char* lastSlash = strrchr(name, '/')) 767 name = lastSlash + 1; 768 769 strlcpy(fName, name, B_OS_NAME_LENGTH); 770 } 771 772 773 void 774 Team::SetArgs(const char* args) 775 { 776 strlcpy(fArgs, args, sizeof(fArgs)); 777 } 778 779 780 void 781 Team::SetArgs(const char* path, const char* const* otherArgs, int otherArgCount) 782 { 783 fArgs[0] = '\0'; 784 strlcpy(fArgs, path, sizeof(fArgs)); 785 for (int i = 0; i < otherArgCount; i++) { 786 strlcat(fArgs, " ", sizeof(fArgs)); 787 strlcat(fArgs, otherArgs[i], sizeof(fArgs)); 788 } 789 } 790 791 792 void 793 Team::ResetSignalsOnExec() 794 { 795 // We are supposed to keep pending signals. Signal actions shall be reset 796 // partially: SIG_IGN and SIG_DFL dispositions shall be kept as they are 797 // (for SIGCHLD it's implementation-defined). Others shall be reset to 798 // SIG_DFL. SA_ONSTACK shall be cleared. There's no mention of the other 799 // flags, but since there aren't any handlers, they make little sense, so 800 // we clear them. 801 802 for (uint32 i = 1; i <= MAX_SIGNAL_NUMBER; i++) { 803 struct sigaction& action = SignalActionFor(i); 804 if (action.sa_handler != SIG_IGN && action.sa_handler != SIG_DFL) 805 action.sa_handler = SIG_DFL; 806 807 action.sa_mask = 0; 808 action.sa_flags = 0; 809 action.sa_userdata = NULL; 810 } 811 } 812 813 814 void 815 Team::InheritSignalActions(Team* parent) 816 { 817 memcpy(fSignalActions, parent->fSignalActions, sizeof(fSignalActions)); 818 } 819 820 821 /*! Adds the given user timer to the team and, if user-defined, assigns it an 822 ID. 823 824 The caller must hold the team's lock. 825 826 \param timer The timer to be added. If it doesn't have an ID yet, it is 827 considered user-defined and will be assigned an ID. 828 \return \c B_OK, if the timer was added successfully, another error code 829 otherwise. 830 */ 831 status_t 832 Team::AddUserTimer(UserTimer* timer) 833 { 834 // don't allow addition of timers when already shutting the team down 835 if (state >= TEAM_STATE_SHUTDOWN) 836 return B_BAD_TEAM_ID; 837 838 // If the timer is user-defined, check timer limit and increment 839 // user-defined count. 840 if (timer->ID() < 0 && !CheckAddUserDefinedTimer()) 841 return EAGAIN; 842 843 fUserTimers.AddTimer(timer); 844 845 return B_OK; 846 } 847 848 849 /*! Removes the given user timer from the team. 850 851 The caller must hold the team's lock. 852 853 \param timer The timer to be removed. 854 855 */ 856 void 857 Team::RemoveUserTimer(UserTimer* timer) 858 { 859 fUserTimers.RemoveTimer(timer); 860 861 if (timer->ID() >= USER_TIMER_FIRST_USER_DEFINED_ID) 862 UserDefinedTimersRemoved(1); 863 } 864 865 866 /*! Deletes all (or all user-defined) user timers of the team. 867 868 Timer's belonging to the team's threads are not affected. 869 The caller must hold the team's lock. 870 871 \param userDefinedOnly If \c true, only the user-defined timers are deleted, 872 otherwise all timers are deleted. 873 */ 874 void 875 Team::DeleteUserTimers(bool userDefinedOnly) 876 { 877 int32 count = fUserTimers.DeleteTimers(userDefinedOnly); 878 UserDefinedTimersRemoved(count); 879 } 880 881 882 /*! If not at the limit yet, increments the team's user-defined timer count. 883 \return \c true, if the limit wasn't reached yet, \c false otherwise. 884 */ 885 bool 886 Team::CheckAddUserDefinedTimer() 887 { 888 int32 oldCount = atomic_add(&fUserDefinedTimerCount, 1); 889 if (oldCount >= MAX_USER_TIMERS_PER_TEAM) { 890 atomic_add(&fUserDefinedTimerCount, -1); 891 return false; 892 } 893 894 return true; 895 } 896 897 898 /*! Subtracts the given count for the team's user-defined timer count. 899 \param count The count to subtract. 900 */ 901 void 902 Team::UserDefinedTimersRemoved(int32 count) 903 { 904 atomic_add(&fUserDefinedTimerCount, -count); 905 } 906 907 908 void 909 Team::DeactivateCPUTimeUserTimers() 910 { 911 while (TeamTimeUserTimer* timer = fCPUTimeUserTimers.Head()) 912 timer->Deactivate(); 913 914 while (TeamUserTimeUserTimer* timer = fUserTimeUserTimers.Head()) 915 timer->Deactivate(); 916 } 917 918 919 /*! Returns the team's current total CPU time (kernel + user + offset). 920 921 The caller must hold \c time_lock. 922 923 \param ignoreCurrentRun If \c true and the current thread is one team's 924 threads, don't add the time since the last time \c last_time was 925 updated. Should be used in "thread unscheduled" scheduler callbacks, 926 since although the thread is still running at that time, its time has 927 already been stopped. 928 \return The team's current total CPU time. 929 */ 930 bigtime_t 931 Team::CPUTime(bool ignoreCurrentRun, Thread* lockedThread) const 932 { 933 bigtime_t time = cpu_clock_offset + dead_threads_kernel_time 934 + dead_threads_user_time; 935 936 Thread* currentThread = thread_get_current_thread(); 937 bigtime_t now = system_time(); 938 939 for (Thread* thread = thread_list; thread != NULL; 940 thread = thread->team_next) { 941 bool alreadyLocked = thread == lockedThread; 942 SpinLocker threadTimeLocker(thread->time_lock, alreadyLocked); 943 time += thread->kernel_time + thread->user_time; 944 945 if (thread->last_time != 0) { 946 if (!ignoreCurrentRun || thread != currentThread) 947 time += now - thread->last_time; 948 } 949 950 if (alreadyLocked) 951 threadTimeLocker.Detach(); 952 } 953 954 return time; 955 } 956 957 958 /*! Returns the team's current user CPU time. 959 960 The caller must hold \c time_lock. 961 962 \return The team's current user CPU time. 963 */ 964 bigtime_t 965 Team::UserCPUTime() const 966 { 967 bigtime_t time = dead_threads_user_time; 968 969 bigtime_t now = system_time(); 970 971 for (Thread* thread = thread_list; thread != NULL; 972 thread = thread->team_next) { 973 SpinLocker threadTimeLocker(thread->time_lock); 974 time += thread->user_time; 975 976 if (thread->last_time != 0 && !thread->in_kernel) 977 time += now - thread->last_time; 978 } 979 980 return time; 981 } 982 983 984 // #pragma mark - ProcessGroup 985 986 987 ProcessGroup::ProcessGroup(pid_t id) 988 : 989 id(id), 990 teams(NULL), 991 fSession(NULL), 992 fInOrphanedCheckList(false) 993 { 994 char lockName[32]; 995 snprintf(lockName, sizeof(lockName), "Group:%" B_PRId32, id); 996 mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME); 997 } 998 999 1000 ProcessGroup::~ProcessGroup() 1001 { 1002 TRACE(("ProcessGroup::~ProcessGroup(): id = %" B_PRId32 "\n", id)); 1003 1004 // If the group is in the orphaned check list, remove it. 1005 MutexLocker orphanedCheckLocker(sOrphanedCheckLock); 1006 1007 if (fInOrphanedCheckList) 1008 sOrphanedCheckProcessGroups.Remove(this); 1009 1010 orphanedCheckLocker.Unlock(); 1011 1012 // remove group from the hash table and from the session 1013 if (fSession != NULL) { 1014 InterruptsSpinLocker groupHashLocker(sGroupHashLock); 1015 sGroupHash.RemoveUnchecked(this); 1016 groupHashLocker.Unlock(); 1017 1018 fSession->ReleaseReference(); 1019 } 1020 1021 mutex_destroy(&fLock); 1022 } 1023 1024 1025 /*static*/ ProcessGroup* 1026 ProcessGroup::Get(pid_t id) 1027 { 1028 InterruptsSpinLocker groupHashLocker(sGroupHashLock); 1029 ProcessGroup* group = sGroupHash.Lookup(id); 1030 if (group != NULL) 1031 group->AcquireReference(); 1032 return group; 1033 } 1034 1035 1036 /*! Adds the group the given session and makes it publicly accessible. 1037 The caller must not hold the process group hash lock. 1038 */ 1039 void 1040 ProcessGroup::Publish(ProcessSession* session) 1041 { 1042 InterruptsSpinLocker groupHashLocker(sGroupHashLock); 1043 PublishLocked(session); 1044 } 1045 1046 1047 /*! Adds the group to the given session and makes it publicly accessible. 1048 The caller must hold the process group hash lock. 1049 */ 1050 void 1051 ProcessGroup::PublishLocked(ProcessSession* session) 1052 { 1053 ASSERT(sGroupHash.Lookup(this->id) == NULL); 1054 1055 fSession = session; 1056 fSession->AcquireReference(); 1057 1058 sGroupHash.InsertUnchecked(this); 1059 } 1060 1061 1062 /*! Checks whether the process group is orphaned. 1063 The caller must hold the group's lock. 1064 \return \c true, if the group is orphaned, \c false otherwise. 1065 */ 1066 bool 1067 ProcessGroup::IsOrphaned() const 1068 { 1069 // Orphaned Process Group: "A process group in which the parent of every 1070 // member is either itself a member of the group or is not a member of the 1071 // group's session." (Open Group Base Specs Issue 7) 1072 bool orphaned = true; 1073 1074 Team* team = teams; 1075 while (orphaned && team != NULL) { 1076 team->LockTeamAndParent(false); 1077 1078 Team* parent = team->parent; 1079 if (parent != NULL && parent->group_id != id 1080 && parent->session_id == fSession->id) { 1081 orphaned = false; 1082 } 1083 1084 team->UnlockTeamAndParent(); 1085 1086 team = team->group_next; 1087 } 1088 1089 return orphaned; 1090 } 1091 1092 1093 void 1094 ProcessGroup::ScheduleOrphanedCheck() 1095 { 1096 MutexLocker orphanedCheckLocker(sOrphanedCheckLock); 1097 1098 if (!fInOrphanedCheckList) { 1099 sOrphanedCheckProcessGroups.Add(this); 1100 fInOrphanedCheckList = true; 1101 } 1102 } 1103 1104 1105 void 1106 ProcessGroup::UnsetOrphanedCheck() 1107 { 1108 fInOrphanedCheckList = false; 1109 } 1110 1111 1112 // #pragma mark - ProcessSession 1113 1114 1115 ProcessSession::ProcessSession(pid_t id) 1116 : 1117 id(id), 1118 controlling_tty(-1), 1119 foreground_group(-1) 1120 { 1121 char lockName[32]; 1122 snprintf(lockName, sizeof(lockName), "Session:%" B_PRId32, id); 1123 mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME); 1124 } 1125 1126 1127 ProcessSession::~ProcessSession() 1128 { 1129 mutex_destroy(&fLock); 1130 } 1131 1132 1133 // #pragma mark - KDL functions 1134 1135 1136 static void 1137 _dump_team_info(Team* team) 1138 { 1139 kprintf("TEAM: %p\n", team); 1140 kprintf("id: %" B_PRId32 " (%#" B_PRIx32 ")\n", team->id, 1141 team->id); 1142 kprintf("serial_number: %" B_PRId64 "\n", team->serial_number); 1143 kprintf("name: '%s'\n", team->Name()); 1144 kprintf("args: '%s'\n", team->Args()); 1145 kprintf("hash_next: %p\n", team->hash_next); 1146 kprintf("parent: %p", team->parent); 1147 if (team->parent != NULL) { 1148 kprintf(" (id = %" B_PRId32 ")\n", team->parent->id); 1149 } else 1150 kprintf("\n"); 1151 1152 kprintf("children: %p\n", team->children); 1153 kprintf("num_threads: %d\n", team->num_threads); 1154 kprintf("state: %d\n", team->state); 1155 kprintf("flags: 0x%" B_PRIx32 "\n", team->flags); 1156 kprintf("io_context: %p\n", team->io_context); 1157 if (team->address_space) 1158 kprintf("address_space: %p\n", team->address_space); 1159 kprintf("user data: %p (area %" B_PRId32 ")\n", 1160 (void*)team->user_data, team->user_data_area); 1161 kprintf("free user thread: %p\n", team->free_user_threads); 1162 kprintf("main_thread: %p\n", team->main_thread); 1163 kprintf("thread_list: %p\n", team->thread_list); 1164 kprintf("group_id: %" B_PRId32 "\n", team->group_id); 1165 kprintf("session_id: %" B_PRId32 "\n", team->session_id); 1166 } 1167 1168 1169 static int 1170 dump_team_info(int argc, char** argv) 1171 { 1172 ulong arg; 1173 bool found = false; 1174 1175 if (argc < 2) { 1176 Thread* thread = thread_get_current_thread(); 1177 if (thread != NULL && thread->team != NULL) 1178 _dump_team_info(thread->team); 1179 else 1180 kprintf("No current team!\n"); 1181 return 0; 1182 } 1183 1184 arg = strtoul(argv[1], NULL, 0); 1185 if (IS_KERNEL_ADDRESS(arg)) { 1186 // semi-hack 1187 _dump_team_info((Team*)arg); 1188 return 0; 1189 } 1190 1191 // walk through the thread list, trying to match name or id 1192 for (TeamTable::Iterator it = sTeamHash.GetIterator(); 1193 Team* team = it.Next();) { 1194 if ((team->Name() && strcmp(argv[1], team->Name()) == 0) 1195 || team->id == (team_id)arg) { 1196 _dump_team_info(team); 1197 found = true; 1198 break; 1199 } 1200 } 1201 1202 if (!found) 1203 kprintf("team \"%s\" (%" B_PRId32 ") doesn't exist!\n", argv[1], (team_id)arg); 1204 return 0; 1205 } 1206 1207 1208 static int 1209 dump_teams(int argc, char** argv) 1210 { 1211 kprintf("%-*s id %-*s name\n", B_PRINTF_POINTER_WIDTH, "team", 1212 B_PRINTF_POINTER_WIDTH, "parent"); 1213 1214 for (TeamTable::Iterator it = sTeamHash.GetIterator(); 1215 Team* team = it.Next();) { 1216 kprintf("%p%7" B_PRId32 " %p %s\n", team, team->id, team->parent, team->Name()); 1217 } 1218 1219 return 0; 1220 } 1221 1222 1223 // #pragma mark - Private functions 1224 1225 1226 /*! Inserts team \a team into the child list of team \a parent. 1227 1228 The caller must hold the lock of both \a parent and \a team. 1229 1230 \param parent The parent team. 1231 \param team The team to be inserted into \a parent's child list. 1232 */ 1233 static void 1234 insert_team_into_parent(Team* parent, Team* team) 1235 { 1236 ASSERT(parent != NULL); 1237 1238 team->siblings_next = parent->children; 1239 parent->children = team; 1240 team->parent = parent; 1241 } 1242 1243 1244 /*! Removes team \a team from the child list of team \a parent. 1245 1246 The caller must hold the lock of both \a parent and \a team. 1247 1248 \param parent The parent team. 1249 \param team The team to be removed from \a parent's child list. 1250 */ 1251 static void 1252 remove_team_from_parent(Team* parent, Team* team) 1253 { 1254 Team* child; 1255 Team* last = NULL; 1256 1257 for (child = parent->children; child != NULL; 1258 child = child->siblings_next) { 1259 if (child == team) { 1260 if (last == NULL) 1261 parent->children = child->siblings_next; 1262 else 1263 last->siblings_next = child->siblings_next; 1264 1265 team->parent = NULL; 1266 break; 1267 } 1268 last = child; 1269 } 1270 } 1271 1272 1273 /*! Returns whether the given team is a session leader. 1274 The caller must hold the team's lock or its process group's lock. 1275 */ 1276 static bool 1277 is_session_leader(Team* team) 1278 { 1279 return team->session_id == team->id; 1280 } 1281 1282 1283 /*! Returns whether the given team is a process group leader. 1284 The caller must hold the team's lock or its process group's lock. 1285 */ 1286 static bool 1287 is_process_group_leader(Team* team) 1288 { 1289 return team->group_id == team->id; 1290 } 1291 1292 1293 /*! Inserts the given team into the given process group. 1294 The caller must hold the process group's lock, the team's lock, and the 1295 team's parent's lock. 1296 */ 1297 static void 1298 insert_team_into_group(ProcessGroup* group, Team* team) 1299 { 1300 team->group = group; 1301 team->group_id = group->id; 1302 team->session_id = group->Session()->id; 1303 1304 team->group_next = group->teams; 1305 group->teams = team; 1306 group->AcquireReference(); 1307 } 1308 1309 1310 /*! Removes the given team from its process group. 1311 1312 The caller must hold the process group's lock, the team's lock, and the 1313 team's parent's lock. Interrupts must be enabled. 1314 1315 \param team The team that'll be removed from its process group. 1316 */ 1317 static void 1318 remove_team_from_group(Team* team) 1319 { 1320 ProcessGroup* group = team->group; 1321 Team* current; 1322 Team* last = NULL; 1323 1324 // the team must be in a process group to let this function have any effect 1325 if (group == NULL) 1326 return; 1327 1328 for (current = group->teams; current != NULL; 1329 current = current->group_next) { 1330 if (current == team) { 1331 if (last == NULL) 1332 group->teams = current->group_next; 1333 else 1334 last->group_next = current->group_next; 1335 1336 team->group = NULL; 1337 break; 1338 } 1339 last = current; 1340 } 1341 1342 team->group = NULL; 1343 team->group_next = NULL; 1344 1345 group->ReleaseReference(); 1346 } 1347 1348 1349 static status_t 1350 create_team_user_data(Team* team, void* exactAddress = NULL) 1351 { 1352 void* address; 1353 uint32 addressSpec; 1354 1355 if (exactAddress != NULL) { 1356 address = exactAddress; 1357 addressSpec = B_EXACT_ADDRESS; 1358 } else { 1359 address = (void*)KERNEL_USER_DATA_BASE; 1360 addressSpec = B_RANDOMIZED_BASE_ADDRESS; 1361 } 1362 1363 status_t result = vm_reserve_address_range(team->id, &address, addressSpec, 1364 kTeamUserDataReservedSize, RESERVED_AVOID_BASE); 1365 1366 virtual_address_restrictions virtualRestrictions = {}; 1367 if (result == B_OK || exactAddress != NULL) { 1368 if (exactAddress != NULL) 1369 virtualRestrictions.address = exactAddress; 1370 else 1371 virtualRestrictions.address = address; 1372 virtualRestrictions.address_specification = B_EXACT_ADDRESS; 1373 } else { 1374 virtualRestrictions.address = (void*)KERNEL_USER_DATA_BASE; 1375 virtualRestrictions.address_specification = B_RANDOMIZED_BASE_ADDRESS; 1376 } 1377 1378 physical_address_restrictions physicalRestrictions = {}; 1379 team->user_data_area = create_area_etc(team->id, "user area", 1380 kTeamUserDataInitialSize, B_FULL_LOCK, 1381 B_READ_AREA | B_WRITE_AREA | B_KERNEL_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.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