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