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 68struct team_key { 69 team_id id; 70}; 71 72struct 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 84namespace { 85 86 87class TeamNotificationService : public DefaultNotificationService { 88public: 89 TeamNotificationService(); 90 91 void Notify(uint32 eventCode, Team* team); 92}; 93 94 95// #pragma mark - TeamTable 96 97 98typedef BKernel::TeamThreadTable<Team> TeamTable; 99 100 101// #pragma mark - ProcessGroupHashDefinition 102 103 104struct 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 129typedef 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 139static TeamTable sTeamHash; 140static spinlock sTeamHashLock = B_SPINLOCK_INITIALIZER; 141 142// the pid_t -> ProcessGroup hash table and the lock protecting it 143static ProcessGroupHashTable sGroupHash; 144static spinlock sGroupHashLock = B_SPINLOCK_INITIALIZER; 145 146static 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. 150static ProcessGroupList sOrphanedCheckProcessGroups; 151static 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) 156static int32 sMaxTeams = 2048; 157static int32 sUsedTeams = 1; 158 159static TeamNotificationService sNotificationService; 160 161 162// #pragma mark - TeamListIterator 163 164 165TeamListIterator::TeamListIterator() 166{ 167 // queue the entry 168 InterruptsSpinLocker locker(sTeamHashLock); 169 sTeamHash.InsertIteratorEntry(&fEntry); 170} 171 172 173TeamListIterator::~TeamListIterator() 174{ 175 // remove the entry 176 InterruptsSpinLocker locker(sTeamHashLock); 177 sTeamHash.RemoveIteratorEntry(&fEntry); 178} 179 180 181Team* 182TeamListIterator::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 198namespace TeamTracing { 199 200class TeamForked : public AbstractTraceEntry { 201public: 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 214private: 215 thread_id fForkedThread; 216}; 217 218 219class ExecTeam : public AbstractTraceEntry { 220public: 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 267private: 268 char* fPath; 269 int32 fArgCount; 270 char* fArgs; 271}; 272 273 274static const char* 275job_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 292class SetJobControlState : public AbstractTraceEntry { 293public: 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 310private: 311 team_id fTeam; 312 job_control_state fNewState; 313 int fSignal; 314}; 315 316 317class WaitForChild : public AbstractTraceEntry { 318public: 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 333private: 334 pid_t fChild; 335 uint32 fFlags; 336}; 337 338 339class WaitForChildDone : public AbstractTraceEntry { 340public: 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 372private: 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 391TeamNotificationService::TeamNotificationService() 392 : DefaultNotificationService("teams") 393{ 394} 395 396 397void 398TeamNotificationService::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 414Team::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 495Team::~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* 534Team::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* 569Team::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* 590Team::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*/ 621void 622Team::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*/ 657void 658Team::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*/ 672void 673Team::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*/ 691void 692Team::UnlockTeamParentAndProcessGroup() 693{ 694 group->Unlock(); 695 696 if (parent != NULL) 697 parent->Unlock(); 698 699 Unlock(); 700} 701 702 703void 704Team::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 736void 737Team::UnlockTeamAndProcessGroup() 738{ 739 group->Unlock(); 740 Unlock(); 741} 742 743 744void 745Team::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 754void 755Team::SetArgs(const char* args) 756{ 757 strlcpy(fArgs, args, sizeof(fArgs)); 758} 759 760 761void 762Team::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 773void 774Team::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 795void 796Team::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*/ 812status_t 813Team::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*/ 837void 838Team::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*/ 855void 856Team::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*/ 866bool 867Team::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*/ 882void 883Team::UserDefinedTimersRemoved(int32 count) 884{ 885 atomic_add(&fUserDefinedTimerCount, -count); 886} 887 888 889void 890Team::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*/ 911bigtime_t 912Team::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*/ 941bigtime_t 942Team::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 964ProcessGroup::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 977ProcessGroup::~ProcessGroup() 978{ 979 TRACE(("ProcessGroup::~ProcessGroup(): id = %" B_PRId32 "\n", 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* 1003ProcessGroup::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*/ 1016void 1017ProcessGroup::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*/ 1027void 1028ProcessGroup::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*/ 1043bool 1044ProcessGroup::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 1070void 1071ProcessGroup::ScheduleOrphanedCheck() 1072{ 1073 MutexLocker orphanedCheckLocker(sOrphanedCheckLock); 1074 1075 if (!fInOrphanedCheckList) { 1076 sOrphanedCheckProcessGroups.Add(this); 1077 fInOrphanedCheckList = true; 1078 } 1079} 1080 1081 1082void 1083ProcessGroup::UnsetOrphanedCheck() 1084{ 1085 fInOrphanedCheckList = false; 1086} 1087 1088 1089// #pragma mark - ProcessSession 1090 1091 1092ProcessSession::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 1104ProcessSession::~ProcessSession() 1105{ 1106 mutex_destroy(&fLock); 1107} 1108 1109 1110// #pragma mark - KDL functions 1111 1112 1113static void 1114_dump_team_info(Team* team) 1115{ 1116 kprintf("TEAM: %p\n", team); 1117 kprintf("id: %" B_PRId32 " (%#" B_PRIx32 ")\n", team->id, 1118 team->id); 1119 kprintf("serial_number: %" B_PRId64 "\n", team->serial_number); 1120 kprintf("name: '%s'\n", team->Name()); 1121 kprintf("args: '%s'\n", team->Args()); 1122 kprintf("hash_next: %p\n", team->hash_next); 1123 kprintf("parent: %p", team->parent); 1124 if (team->parent != NULL) { 1125 kprintf(" (id = %" B_PRId32 ")\n", team->parent->id); 1126 } else 1127 kprintf("\n"); 1128 1129 kprintf("children: %p\n", team->children); 1130 kprintf("num_threads: %d\n", team->num_threads); 1131 kprintf("state: %d\n", team->state); 1132 kprintf("flags: 0x%" B_PRIx32 "\n", team->flags); 1133 kprintf("io_context: %p\n", team->io_context); 1134 if (team->address_space) 1135 kprintf("address_space: %p\n", team->address_space); 1136 kprintf("user data: %p (area %" B_PRId32 ")\n", 1137 (void*)team->user_data, team->user_data_area); 1138 kprintf("free user thread: %p\n", team->free_user_threads); 1139 kprintf("main_thread: %p\n", team->main_thread); 1140 kprintf("thread_list: %p\n", team->thread_list); 1141 kprintf("group_id: %" B_PRId32 "\n", team->group_id); 1142 kprintf("session_id: %" B_PRId32 "\n", team->session_id); 1143} 1144 1145 1146static int 1147dump_team_info(int argc, char** argv) 1148{ 1149 ulong arg; 1150 bool found = false; 1151 1152 if (argc < 2) { 1153 Thread* thread = thread_get_current_thread(); 1154 if (thread != NULL && thread->team != NULL) 1155 _dump_team_info(thread->team); 1156 else 1157 kprintf("No current team!\n"); 1158 return 0; 1159 } 1160 1161 arg = strtoul(argv[1], NULL, 0); 1162 if (IS_KERNEL_ADDRESS(arg)) { 1163 // semi-hack 1164 _dump_team_info((Team*)arg); 1165 return 0; 1166 } 1167 1168 // walk through the thread list, trying to match name or id 1169 for (TeamTable::Iterator it = sTeamHash.GetIterator(); 1170 Team* team = it.Next();) { 1171 if ((team->Name() && strcmp(argv[1], team->Name()) == 0) 1172 || team->id == (team_id)arg) { 1173 _dump_team_info(team); 1174 found = true; 1175 break; 1176 } 1177 } 1178 1179 if (!found) 1180 kprintf("team \"%s\" (%" B_PRId32 ") doesn't exist!\n", argv[1], (team_id)arg); 1181 return 0; 1182} 1183 1184 1185static int 1186dump_teams(int argc, char** argv) 1187{ 1188 kprintf("%-*s id %-*s name\n", B_PRINTF_POINTER_WIDTH, "team", 1189 B_PRINTF_POINTER_WIDTH, "parent"); 1190 1191 for (TeamTable::Iterator it = sTeamHash.GetIterator(); 1192 Team* team = it.Next();) { 1193 kprintf("%p%7" B_PRId32 " %p %s\n", team, team->id, team->parent, team->Name()); 1194 } 1195 1196 return 0; 1197} 1198 1199 1200// #pragma mark - Private functions 1201 1202 1203/*! Inserts team \a team into the child list of team \a parent. 1204 1205 The caller must hold the lock of both \a parent and \a team. 1206 1207 \param parent The parent team. 1208 \param team The team to be inserted into \a parent's child list. 1209*/ 1210static void 1211insert_team_into_parent(Team* parent, Team* team) 1212{ 1213 ASSERT(parent != NULL); 1214 1215 team->siblings_next = parent->children; 1216 parent->children = team; 1217 team->parent = parent; 1218} 1219 1220 1221/*! Removes team \a team from the child list of team \a parent. 1222 1223 The caller must hold the lock of both \a parent and \a team. 1224 1225 \param parent The parent team. 1226 \param team The team to be removed from \a parent's child list. 1227*/ 1228static void 1229remove_team_from_parent(Team* parent, Team* team) 1230{ 1231 Team* child; 1232 Team* last = NULL; 1233 1234 for (child = parent->children; child != NULL; 1235 child = child->siblings_next) { 1236 if (child == team) { 1237 if (last == NULL) 1238 parent->children = child->siblings_next; 1239 else 1240 last->siblings_next = child->siblings_next; 1241 1242 team->parent = NULL; 1243 break; 1244 } 1245 last = child; 1246 } 1247} 1248 1249 1250/*! Returns whether the given team is a session leader. 1251 The caller must hold the team's lock or its process group's lock. 1252*/ 1253static bool 1254is_session_leader(Team* team) 1255{ 1256 return team->session_id == team->id; 1257} 1258 1259 1260/*! Returns whether the given team is a process group leader. 1261 The caller must hold the team's lock or its process group's lock. 1262*/ 1263static bool 1264is_process_group_leader(Team* team) 1265{ 1266 return team->group_id == team->id; 1267} 1268 1269 1270/*! Inserts the given team into the given process group. 1271 The caller must hold the process group's lock, the team's lock, and the 1272 team's parent's lock. 1273*/ 1274static void 1275insert_team_into_group(ProcessGroup* group, Team* team) 1276{ 1277 team->group = group; 1278 team->group_id = group->id; 1279 team->session_id = group->Session()->id; 1280 1281 team->group_next = group->teams; 1282 group->teams = team; 1283 group->AcquireReference(); 1284} 1285 1286 1287/*! Removes the given team from its process group. 1288 1289 The caller must hold the process group's lock, the team's lock, and the 1290 team's parent's lock. Interrupts must be enabled. 1291 1292 \param team The team that'll be removed from its process group. 1293*/ 1294static void 1295remove_team_from_group(Team* team) 1296{ 1297 ProcessGroup* group = team->group; 1298 Team* current; 1299 Team* last = NULL; 1300 1301 // the team must be in a process group to let this function have any effect 1302 if (group == NULL) 1303 return; 1304 1305 for (current = group->teams; current != NULL; 1306 current = current->group_next) { 1307 if (current == team) { 1308 if (last == NULL) 1309 group->teams = current->group_next; 1310 else 1311 last->group_next = current->group_next; 1312 1313 team->group = NULL; 1314 break; 1315 } 1316 last = current; 1317 } 1318 1319 team->group = NULL; 1320 team->group_next = NULL; 1321 1322 group->ReleaseReference(); 1323} 1324 1325 1326static status_t 1327create_team_user_data(Team* team) 1328{ 1329 void* address; 1330 size_t size = 4 * B_PAGE_SIZE; 1331 virtual_address_restrictions virtualRestrictions = {}; 1332 virtualRestrictions.address = (void*)KERNEL_USER_DATA_BASE; 1333 virtualRestrictions.address_specification = B_BASE_ADDRESS; 1334 physical_address_restrictions physicalRestrictions = {}; 1335 team->user_data_area = create_area_etc(team->id, "user area", size, 1336 B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA, 0, 0, &virtualRestrictions, 1337 &physicalRestrictions, &address); 1338 if (team->user_data_area < 0) 1339 return team->user_data_area; 1340 1341 team->user_data = (addr_t)address; 1342 team->used_user_data = 0; 1343 team->user_data_size = size; 1344 team->free_user_threads = NULL; 1345 1346 return B_OK; 1347} 1348 1349 1350static void 1351delete_team_user_data(Team* team) 1352{ 1353 if (team->user_data_area >= 0) { 1354 vm_delete_area(team->id, team->user_data_area, true); 1355 team->user_data = 0; 1356 team->used_user_data = 0; 1357 team->user_data_size = 0; 1358 team->user_data_area = -1; 1359 while (free_user_thread* entry = team->free_user_threads) { 1360 team->free_user_threads = entry->next; 1361 free(entry); 1362 } 1363 } 1364} 1365 1366 1367static status_t 1368copy_user_process_args(const char* const* userFlatArgs, size_t flatArgsSize, 1369 int32 argCount, int32 envCount, char**& _flatArgs) 1370{ 1371 if (argCount < 0 || envCount < 0) 1372 return B_BAD_VALUE; 1373 1374 if (flatArgsSize > MAX_PROCESS_ARGS_SIZE) 1375 return B_TOO_MANY_ARGS; 1376 if ((argCount + envCount + 2) * sizeof(char*) > flatArgsSize) 1377 return B_BAD_VALUE; 1378 1379 if (!IS_USER_ADDRESS(userFlatArgs)) 1380 return B_BAD_ADDRESS; 1381 1382 // allocate kernel memory 1383 char** flatArgs = (char**)malloc(_ALIGN(flatArgsSize)); 1384 if (flatArgs == NULL) 1385 return B_NO_MEMORY; 1386 1387 if (user_memcpy(flatArgs, userFlatArgs, flatArgsSize) != B_OK) { 1388 free(flatArgs); 1389 return B_BAD_ADDRESS; 1390 } 1391 1392 // check and relocate the array 1393 status_t error = B_OK; 1394 const char* stringBase = (char*)flatArgs + argCount + envCount + 2; 1395 const char* stringEnd = (char*)flatArgs + flatArgsSize; 1396 for (int32 i = 0; i < argCount + envCount + 2; i++) { 1397 if (i == argCount || i == argCount + envCount + 1) { 1398 // check array null termination 1399 if (flatArgs[i] != NULL) { 1400 error = B_BAD_VALUE; 1401 break; 1402 } 1403 } else { 1404 // check string 1405 char* arg = (char*)flatArgs + (flatArgs[i] - (char*)userFlatArgs); 1406 size_t maxLen = stringEnd - arg; 1407 if (arg < stringBase || arg >= stringEnd 1408 || strnlen(arg, maxLen) == maxLen) { 1409 error = B_BAD_VALUE; 1410 break; 1411 } 1412 1413 flatArgs[i] = arg; 1414 } 1415 } 1416 1417 if (error == B_OK) 1418 _flatArgs = flatArgs; 1419 else 1420 free(flatArgs); 1421 1422 return error; 1423} 1424 1425 1426static void 1427free_team_arg(struct team_arg* teamArg) 1428{ 1429 if (teamArg != NULL) { 1430 free(teamArg->flat_args); 1431 free(teamArg->path); 1432 free(teamArg); 1433 } 1434} 1435 1436 1437static status_t 1438create_team_arg(struct team_arg** _teamArg, const char* path, char** flatArgs, 1439 size_t flatArgsSize, int32 argCount, int32 envCount, mode_t umask, 1440 port_id port, uint32 token) 1441{ 1442 struct team_arg* teamArg = (struct team_arg*)malloc(sizeof(team_arg)); 1443 if (teamArg == NULL) 1444 return B_NO_MEMORY; 1445 1446 teamArg->path = strdup(path); 1447 if (teamArg->path == NULL) { 1448 free(teamArg); 1449 return B_NO_MEMORY; 1450 } 1451 1452 // copy the args over 1453 1454 teamArg->flat_args = flatArgs; 1455 teamArg->flat_args_size = flatArgsSize; 1456 teamArg->arg_count = argCount; 1457 teamArg->env_count = envCount; 1458 teamArg->umask = umask; 1459 teamArg->error_port = port; 1460 teamArg->error_token = token; 1461 1462 *_teamArg = teamArg; 1463 return B_OK; 1464} 1465 1466// FATELF_REVIEW: VFS expert. This needs to be reviewed for correctness, 1467// and it may be better to hoist some of this out into vfs API. 1468static status_t 1469team_create_thread_start_determine_arch(Team* team, const char* path, 1470 struct elf_image_arch* image_arch) 1471{ 1472 io_context* ioContext; 1473 struct vnode* cwd; 1474 int dirfd; 1475 int fd; 1476 status_t err; 1477 1478 // Open the target executable, with a path relative to the team's cwd 1479 { 1480 team->Lock(); 1481 ioContext = team->io_context; 1482 vfs_get_io_context(ioContext); 1483 team->Unlock(); 1484 } 1485 1486 { 1487 MutexLocker ioContextLocker(ioContext->io_mutex); 1488 cwd = ioContext->cwd; 1489 vfs_acquire_vnode(cwd); 1490 } 1491 1492 // our reference to cwd will be claimed by vfs_open_vnode 1493 // open success. otherwise, we're responsible for releasing it. 1494 dirfd = vfs_open_vnode(ioContext->cwd, O_RDONLY, true); 1495 if (dirfd < B_OK) { 1496 vfs_put_vnode(cwd); 1497 return dirfd; 1498 } 1499 1500 fd = _kern_open(dirfd, path, O_RDONLY, 0); 1501 if (fd < B_OK) { 1502 err = fd; 1503 goto finished; 1504 } 1505 1506 // determine the preferred architecture 1507 struct elf_fat_arch_section fat_arch_section; 1508 err = elf_find_best_fat_section(fd, &fat_arch_section); 1509 if (err != B_OK) 1510 goto finished; 1511 1512 *image_arch = fat_arch_section.arch; 1513 err = B_OK; 1514 1515finished: 1516 vfs_put_io_context(ioContext); 1517 _kern_close(dirfd); 1518 1519 if (fd >= 0) 1520 _kern_close(fd); 1521 1522 return err; 1523} 1524 1525static status_t 1526team_create_thread_start_internal(void* args) 1527{ 1528 status_t err; 1529 Thread* thread; 1530 Team* team; 1531 struct team_arg* teamArgs = (struct team_arg*)args; 1532 const char* path; 1533 addr_t entry; 1534 char** userArgs; 1535 char** userEnv; 1536 struct user_space_program_args* programArgs; 1537 uint32 argCount, envCount; 1538 1539 thread = thread_get_current_thread(); 1540 team = thread->team; 1541 cache_node_launched(teamArgs->arg_count, teamArgs->flat_args); 1542 1543 TRACE(("team_create_thread_start: entry thread %" B_PRId32 "\n", 1544 thread->id)); 1545 1546 // Main stack area layout is currently as follows (starting from 0): 1547 // 1548 // size | usage 1549 // ---------------------------------+-------------------------------- 1550 // USER_MAIN_THREAD_STACK_SIZE | actual stack 1551 // TLS_SIZE | TLS data 1552 // sizeof(user_space_program_args) | argument structure for the runtime 1553 // | loader 1554 // flat arguments size | flat process arguments and environment 1555 1556 // TODO: ENV_SIZE is a) limited, and b) not used after libroot copied it to 1557 // the heap 1558 // TODO: we could reserve the whole USER_STACK_REGION upfront... 1559 1560 argCount = teamArgs->arg_count; 1561 envCount = teamArgs->env_count; 1562 1563 programArgs = (struct user_space_program_args*)(thread->user_stack_base 1564 + thread->user_stack_size + TLS_SIZE); 1565 1566 userArgs = (char**)(programArgs + 1); 1567 userEnv = userArgs + argCount + 1; 1568 path = teamArgs->path; 1569 1570 if (user_strlcpy(programArgs->program_path, path, 1571 sizeof(programArgs->program_path)) < B_OK 1572 || user_memcpy(&programArgs->arg_count, &argCount, sizeof(int32)) < B_OK 1573 || user_memcpy(&programArgs->args, &userArgs, sizeof(char**)) < B_OK 1574 || user_memcpy(&programArgs->env_count, &envCount, sizeof(int32)) < B_OK 1575 || user_memcpy(&programArgs->env, &userEnv, sizeof(char**)) < B_OK 1576 || user_memcpy(&programArgs->error_port, &teamArgs->error_port, 1577 sizeof(port_id)) < B_OK 1578 || user_memcpy(&programArgs->error_token, &teamArgs->error_token, 1579 sizeof(uint32)) < B_OK 1580 || user_memcpy(&programArgs->umask, &teamArgs->umask, sizeof(mode_t)) < B_OK 1581 || user_memcpy(userArgs, teamArgs->flat_args, 1582 teamArgs->flat_args_size) < B_OK) { 1583 // the team deletion process will clean this mess 1584 free_team_arg(teamArgs); 1585 return B_BAD_ADDRESS; 1586 } 1587 1588 TRACE(("team_create_thread_start: loading elf binary '%s'\n", path)); 1589 1590 // determine the target binary architecture; on failure, we just allow 1591 // any arch and let the runtime_loader report an appropriate error. 1592 struct elf_image_arch *requiredArch; 1593 struct elf_image_arch archResult; 1594 { 1595 err = team_create_thread_start_determine_arch(team, path, &archResult); 1596 if (err == B_OK) { 1597 requiredArch = &archResult; 1598 } else { 1599 TRACE(("team_create_thread_start: elf_find_best_fat_arch() failed:" 1600 "%s\n", strerror(err))); 1601 requiredArch = NULL; 1602 } 1603 } 1604 1605 // set team args and update state 1606 team->Lock(); 1607 team->SetArgs(path, teamArgs->flat_args + 1, argCount - 1); 1608 team->state = TEAM_STATE_NORMAL; 1609 team->Unlock(); 1610 1611 free_team_arg(teamArgs); 1612 // the arguments are already on the user stack, we no longer need 1613 // them in this form 1614 1615 // NOTE: Normally arch_thread_enter_userspace() never returns, that is 1616 // automatic variables with function scope will never be destroyed. 1617 { 1618 // find runtime_loader path 1619 KPath runtimeLoaderPath; 1620 err = find_directory(B_SYSTEM_DIRECTORY, gBootDevice, false, 1621 runtimeLoaderPath.LockBuffer(), runtimeLoaderPath.BufferSize()); 1622 if (err < B_OK) { 1623 TRACE(("team_create_thread_start: find_directory() failed: %s\n", 1624 strerror(err))); 1625 return err; 1626 } 1627 runtimeLoaderPath.UnlockBuffer(); 1628 err = runtimeLoaderPath.Append("runtime_loader"); 1629 1630 if (err == B_OK) { 1631 err = elf_load_user_image(runtimeLoaderPath.Path(), team, 0, 1632 &entry, requiredArch); 1633 } 1634 } 1635 1636 if (err < B_OK) { 1637 // Luckily, we don't have to clean up the mess we created - that's 1638 // done for us by the normal team deletion process 1639 TRACE(("team_create_thread_start: elf_load_user_image() failed: " 1640 "%s\n", strerror(err))); 1641 return err; 1642 } 1643 1644 TRACE(("team_create_thread_start: loaded elf. entry = %#lx\n", entry)); 1645 1646 // enter userspace -- returns only in case of error 1647 return thread_enter_userspace_new_team(thread, (addr_t)entry, 1648 programArgs, NULL); 1649} 1650 1651 1652static status_t 1653team_create_thread_start(void* args) 1654{ 1655 team_create_thread_start_internal(args); 1656 team_init_exit_info_on_error(thread_get_current_thread()->team); 1657 thread_exit(); 1658 // does not return 1659 return B_OK; 1660} 1661 1662 1663static thread_id 1664load_image_internal(char**& _flatArgs, size_t flatArgsSize, int32 argCount, 1665 int32 envCount, int32 priority, team_id parentID, uint32 flags, 1666 port_id errorPort, uint32 errorToken) 1667{ 1668 char** flatArgs = _flatArgs; 1669 thread_id thread; 1670 status_t status; 1671 struct team_arg* teamArgs; 1672 struct team_loading_info loadingInfo; 1673 io_context* parentIOContext = NULL; 1674 team_id teamID; 1675 1676 if (flatArgs == NULL || argCount == 0) 1677 return B_BAD_VALUE; 1678 1679 const char* path = flatArgs[0]; 1680 1681 TRACE(("load_image_internal: name '%s', args = %p, argCount = %" B_PRId32 1682 "\n", path, flatArgs, argCount)); 1683 1684 // cut the path from the main thread name 1685 const char* threadName = strrchr(path, '/'); 1686 if (threadName != NULL) 1687 threadName++; 1688 else 1689 threadName = path; 1690 1691 // create the main thread object 1692 Thread* mainThread; 1693 status = Thread::Create(threadName, mainThread); 1694 if (status != B_OK) 1695 return status; 1696 BReference<Thread> mainThreadReference(mainThread, true); 1697 1698 // create team object 1699 Team* team = Team::Create(mainThread->id, path, false); 1700 if (team == NULL) 1701 return B_NO_MEMORY; 1702 BReference<Team> teamReference(team, true); 1703 1704 if (flags & B_WAIT_TILL_LOADED) { 1705 loadingInfo.thread = thread_get_current_thread(); 1706 loadingInfo.result = B_ERROR; 1707 loadingInfo.done = false; 1708 team->loading_info = &loadingInfo; 1709 } 1710 1711 // get the parent team 1712 Team* parent = Team::Get(parentID); 1713 if (parent == NULL) 1714 return B_BAD_TEAM_ID; 1715 BReference<Team> parentReference(parent, true); 1716 1717 parent->LockTeamAndProcessGroup(); 1718 team->Lock(); 1719 1720 // inherit the parent's user/group 1721 inherit_parent_user_and_group(team, parent); 1722 1723 InterruptsSpinLocker teamsLocker(sTeamHashLock); 1724 1725 sTeamHash.Insert(team); 1726 sUsedTeams++; 1727 1728 teamsLocker.Unlock(); 1729 1730 insert_team_into_parent(parent, team); 1731 insert_team_into_group(parent->group, team); 1732 1733 // get a reference to the parent's I/O context -- we need it to create ours 1734 parentIOContext = parent->io_context; 1735 vfs_get_io_context(parentIOContext); 1736 1737 team->Unlock(); 1738 parent->UnlockTeamAndProcessGroup(); 1739 1740 // notify team listeners 1741 sNotificationService.Notify(TEAM_ADDED, team); 1742 1743 // check the executable's set-user/group-id permission 1744 update_set_id_user_and_group(team, path); 1745 1746 status = create_team_arg(&teamArgs, path, flatArgs, flatArgsSize, argCount, 1747 envCount, (mode_t)-1, errorPort, errorToken); 1748 if (status != B_OK) 1749 goto err1; 1750 1751 _flatArgs = NULL; 1752 // args are owned by the team_arg structure now 1753 1754 // create a new io_context for this team 1755 team->io_context = vfs_new_io_context(parentIOContext, true); 1756 if (!team->io_context) { 1757 status = B_NO_MEMORY; 1758 goto err2; 1759 } 1760 1761 // We don't need the parent's I/O context any longer. 1762 vfs_put_io_context(parentIOContext); 1763 parentIOContext = NULL; 1764 1765 // remove any fds that have the CLOEXEC flag set (emulating BeOS behaviour) 1766 vfs_exec_io_context(team->io_context); 1767 1768 // create an address space for this team 1769 status = VMAddressSpace::Create(team->id, USER_BASE, USER_SIZE, false, 1770 &team->address_space); 1771 if (status != B_OK) 1772 goto err3; 1773 1774 // create the user data area 1775 status = create_team_user_data(team); 1776 if (status != B_OK) 1777 goto err4; 1778 1779 // In case we start the main thread, we shouldn't access the team object 1780 // afterwards, so cache the team's ID. 1781 teamID = team->id; 1782 1783 // Create a kernel thread, but under the context of the new team 1784 // The new thread will take over ownership of teamArgs. 1785 { 1786 ThreadCreationAttributes threadAttributes(team_create_thread_start, 1787 threadName, B_NORMAL_PRIORITY, teamArgs, teamID, mainThread); 1788 threadAttributes.additional_stack_size = sizeof(user_space_program_args) 1789 + teamArgs->flat_args_size; 1790 thread = thread_create_thread(threadAttributes, false); 1791 if (thread < 0) { 1792 status = thread; 1793 goto err5; 1794 } 1795 } 1796 1797 // The team has been created successfully, so we keep the reference. Or 1798 // more precisely: It's owned by the team's main thread, now. 1799 teamReference.Detach(); 1800 1801 // wait for the loader of the new team to finish its work 1802 if ((flags & B_WAIT_TILL_LOADED) != 0) { 1803 InterruptsSpinLocker schedulerLocker(gSchedulerLock); 1804 1805 // resume the team's main thread 1806 if (mainThread != NULL && mainThread->state == B_THREAD_SUSPENDED) 1807 scheduler_enqueue_in_run_queue(mainThread); 1808 1809 // Now suspend ourselves until loading is finished. We will be woken 1810 // either by the thread, when it finished or aborted loading, or when 1811 // the team is going to die (e.g. is killed). In either case the one 1812 // setting `loadingInfo.done' is responsible for removing the info from 1813 // the team structure. 1814 while (!loadingInfo.done) { 1815 thread_get_current_thread()->next_state = B_THREAD_SUSPENDED; 1816 scheduler_reschedule(); 1817 } 1818 1819 schedulerLocker.Unlock(); 1820 1821 if (loadingInfo.result < B_OK) 1822 return loadingInfo.result; 1823 } 1824 1825 // notify the debugger 1826 user_debug_team_created(teamID); 1827 1828 return thread; 1829 1830err5: 1831 delete_team_user_data(team); 1832err4: 1833 team->address_space->Put(); 1834err3: 1835 vfs_put_io_context(team->io_context); 1836err2: 1837 free_team_arg(teamArgs); 1838err1: 1839 if (parentIOContext != NULL) 1840 vfs_put_io_context(parentIOContext); 1841 1842 // Remove the team structure from the process group, the parent team, and 1843 // the team hash table and delete the team structure. 1844 parent->LockTeamAndProcessGroup(); 1845 team->Lock(); 1846 1847 remove_team_from_group(team); 1848 remove_team_from_parent(team->parent, team); 1849 1850 team->Unlock(); 1851 parent->UnlockTeamAndProcessGroup(); 1852 1853 teamsLocker.Lock(); 1854 sTeamHash.Remove(team); 1855 teamsLocker.Unlock(); 1856 1857 sNotificationService.Notify(TEAM_REMOVED, team); 1858 1859 return status; 1860} 1861 1862 1863/*! Almost shuts down the current team and loads a new image into it. 1864 If successful, this function does not return and will takeover ownership of 1865 the arguments provided. 1866 This function may only be called in a userland team (caused by one of the 1867 exec*() syscalls). 1868*/ 1869static status_t 1870exec_team(const char* path, char**& _flatArgs, size_t flatArgsSize, 1871 int32 argCount, int32 envCount, mode_t umask) 1872{ 1873 // NOTE: Since this function normally doesn't return, don't use automatic 1874 // variables that need destruction in the function scope. 1875 char** flatArgs = _flatArgs; 1876 Team* team = thread_get_current_thread()->team; 1877 struct team_arg* teamArgs; 1878 const char* threadName; 1879 thread_id nubThreadID = -1; 1880 1881 TRACE(("exec_team(path = \"%s\", argc = %" B_PRId32 ", envCount = %" 1882 B_PRId32 "): team %" B_PRId32 "\n", path, argCount, envCount, 1883 team->id)); 1884 1885 T(ExecTeam(path, argCount, flatArgs, envCount, flatArgs + argCount + 1)); 1886 1887 // switching the kernel at run time is probably not a good idea :) 1888 if (team == team_get_kernel_team()) 1889 return B_NOT_ALLOWED; 1890 1891 // we currently need to be single threaded here 1892 // TODO: maybe we should just kill all other threads and 1893 // make the current thread the team's main thread? 1894 Thread* currentThread = thread_get_current_thread(); 1895 if (currentThread != team->main_thread) 1896 return B_NOT_ALLOWED; 1897 1898 // The debug nub thread, a pure kernel thread, is allowed to survive. 1899 // We iterate through the thread list to make sure that there's no other 1900 // thread. 1901 TeamLocker teamLocker(team); 1902 InterruptsSpinLocker debugInfoLocker(team->debug_info.lock); 1903 1904 if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) 1905 nubThreadID = team->debug_info.nub_thread; 1906 1907 debugInfoLocker.Unlock(); 1908 1909 for (Thread* thread = team->thread_list; thread != NULL; 1910 thread = thread->team_next) { 1911 if (thread != team->main_thread && thread->id != nubThreadID) 1912 return B_NOT_ALLOWED; 1913 } 1914 1915 team->DeleteUserTimers(true); 1916 team->ResetSignalsOnExec(); 1917 1918 teamLocker.Unlock(); 1919 1920 status_t status = create_team_arg(&teamArgs, path, flatArgs, flatArgsSize, 1921 argCount, envCount, umask, -1, 0); 1922 if (status != B_OK) 1923 return status; 1924 1925 _flatArgs = NULL; 1926 // args are owned by the team_arg structure now 1927 1928 // TODO: remove team resources if there are any left 1929 // thread_atkernel_exit() might not be called at all 1930 1931 thread_reset_for_exec(); 1932 1933 user_debug_prepare_for_exec(); 1934 1935 delete_team_user_data(team); 1936 vm_delete_areas(team->address_space, false); 1937 xsi_sem_undo(team); 1938 delete_owned_ports(team); 1939 sem_delete_owned_sems(team); 1940 remove_images(team); 1941 vfs_exec_io_context(team->io_context); 1942 delete_realtime_sem_context(team->realtime_sem_context); 1943 team->realtime_sem_context = NULL; 1944 1945 status = create_team_user_data(team); 1946 if (status != B_OK) { 1947 // creating the user data failed -- we're toast 1948 // TODO: We should better keep the old user area in the first place. 1949 free_team_arg(teamArgs); 1950 exit_thread(status); 1951 return status; 1952 } 1953 1954 user_debug_finish_after_exec(); 1955 1956 // rename the team 1957 1958 team->Lock(); 1959 team->SetName(path); 1960 team->Unlock(); 1961 1962 // cut the path from the team name and rename the main thread, too 1963 threadName = strrchr(path, '/'); 1964 if (threadName != NULL) 1965 threadName++; 1966 else 1967 threadName = path; 1968 rename_thread(thread_get_current_thread_id(), threadName); 1969 1970 atomic_or(&team->flags, TEAM_FLAG_EXEC_DONE); 1971 1972 // Update user/group according to the executable's set-user/group-id 1973 // permission. 1974 update_set_id_user_and_group(team, path); 1975 1976 user_debug_team_exec(); 1977 1978 // notify team listeners 1979 sNotificationService.Notify(TEAM_EXEC, team); 1980 1981 // get a user thread for the thread 1982 user_thread* userThread = team_allocate_user_thread(team); 1983 // cannot fail (the allocation for the team would have failed already) 1984 ThreadLocker currentThreadLocker(currentThread); 1985 currentThread->user_thread = userThread; 1986 currentThreadLocker.Unlock(); 1987 1988 // create the user stack for the thread 1989 status = thread_create_user_stack(currentThread->team, currentThread, NULL, 1990 0, sizeof(user_space_program_args) + teamArgs->flat_args_size); 1991 if (status == B_OK) { 1992 // prepare the stack, load the runtime loader, and enter userspace 1993 team_create_thread_start(teamArgs); 1994 // does never return 1995 } else 1996 free_team_arg(teamArgs); 1997 1998 // Sorry, we have to kill ourselves, there is no way out anymore 1999 // (without any areas left and all that). 2000 exit_thread(status); 2001 2002 // We return a status here since the signal that is sent by the 2003 // call above is not immediately handled. 2004 return B_ERROR; 2005} 2006 2007 2008static thread_id 2009fork_team(void) 2010{ 2011 Thread* parentThread = thread_get_current_thread(); 2012 Team* parentTeam = parentThread->team; 2013 Team* team; 2014 arch_fork_arg* forkArgs; 2015 struct area_info info; 2016 thread_id threadID; 2017 status_t status; 2018 ssize_t areaCookie; 2019 int32 imageCookie; 2020 2021 TRACE(("fork_team(): team %" B_PRId32 "\n", parentTeam->id)); 2022 2023 if (parentTeam == team_get_kernel_team()) 2024 return B_NOT_ALLOWED; 2025 2026 // create a new team 2027 // TODO: this is very similar to load_image_internal() - maybe we can do 2028 // something about it :) 2029 2030 // create the main thread object 2031 Thread* thread; 2032 status = Thread::Create(parentThread->name, thread); 2033 if (status != B_OK) 2034 return status; 2035 BReference<Thread> threadReference(thread, true); 2036 2037 // create the team object 2038 team = Team::Create(thread->id, NULL, false); 2039 if (team == NULL) 2040 return B_NO_MEMORY; 2041 2042 parentTeam->LockTeamAndProcessGroup(); 2043 team->Lock(); 2044 2045 team->SetName(parentTeam->Name()); 2046 team->SetArgs(parentTeam->Args()); 2047 2048 // Inherit the parent's user/group. 2049 inherit_parent_user_and_group(team, parentTeam); 2050 2051 // inherit signal handlers 2052 team->InheritSignalActions(parentTeam); 2053 2054 InterruptsSpinLocker teamsLocker(sTeamHashLock); 2055 2056 sTeamHash.Insert(team); 2057 sUsedTeams++; 2058 2059 teamsLocker.Unlock(); 2060 2061 insert_team_into_parent(parentTeam, team); 2062 insert_team_into_group(parentTeam->group, team); 2063 2064 team->Unlock(); 2065 parentTeam->UnlockTeamAndProcessGroup(); 2066 2067 // notify team listeners 2068 sNotificationService.Notify(TEAM_ADDED, team); 2069 2070 // inherit some team debug flags 2071 team->debug_info.flags |= atomic_get(&parentTeam->debug_info.flags) 2072 & B_TEAM_DEBUG_INHERITED_FLAGS; 2073 2074 forkArgs = (arch_fork_arg*)malloc(sizeof(arch_fork_arg)); 2075 if (forkArgs == NULL) { 2076 status = B_NO_MEMORY; 2077 goto err1; 2078 } 2079 2080 // create a new io_context for this team 2081 team->io_context = vfs_new_io_context(parentTeam->io_context, false); 2082 if (!team->io_context) { 2083 status = B_NO_MEMORY; 2084 goto err2; 2085 } 2086 2087 // duplicate the realtime sem context 2088 if (parentTeam->realtime_sem_context) { 2089 team->realtime_sem_context = clone_realtime_sem_context( 2090 parentTeam->realtime_sem_context); 2091 if (team->realtime_sem_context == NULL) { 2092 status = B_NO_MEMORY; 2093 goto err25; 2094 } 2095 } 2096 2097 // create an address space for this team 2098 status = VMAddressSpace::Create(team->id, USER_BASE, USER_SIZE, false, 2099 &team->address_space); 2100 if (status < B_OK) 2101 goto err3; 2102 2103 // copy all areas of the team 2104 // TODO: should be able to handle stack areas differently (ie. don't have 2105 // them copy-on-write) 2106 2107 areaCookie = 0; 2108 while (get_next_area_info(B_CURRENT_TEAM, &areaCookie, &info) == B_OK) { 2109 if (info.area == parentTeam->user_data_area) { 2110 // don't clone the user area; just create a new one 2111 status = create_team_user_data(team); 2112 if (status != B_OK) 2113 break; 2114 2115 thread->user_thread = team_allocate_user_thread(team); 2116 } else { 2117 void* address; 2118 area_id area = vm_copy_area(team->address_space->ID(), info.name, 2119 &address, B_CLONE_ADDRESS, info.protection, info.area); 2120 if (area < B_OK) { 2121 status = area; 2122 break; 2123 } 2124 2125 if (info.area == parentThread->user_stack_area) 2126 thread->user_stack_area = area; 2127 } 2128 } 2129 2130 if (status < B_OK) 2131 goto err4; 2132 2133 if (thread->user_thread == NULL) { 2134#if KDEBUG 2135 panic("user data area not found, parent area is %" B_PRId32, 2136 parentTeam->user_data_area); 2137#endif 2138 status = B_ERROR; 2139 goto err4; 2140 } 2141 2142 thread->user_stack_base = parentThread->user_stack_base; 2143 thread->user_stack_size = parentThread->user_stack_size; 2144 thread->user_local_storage = parentThread->user_local_storage; 2145 thread->sig_block_mask = parentThread->sig_block_mask; 2146 thread->signal_stack_base = parentThread->signal_stack_base; 2147 thread->signal_stack_size = parentThread->signal_stack_size; 2148 thread->signal_stack_enabled = parentThread->signal_stack_enabled; 2149 2150 arch_store_fork_frame(forkArgs); 2151 2152 // copy image list 2153 image_info imageInfo; 2154 imageCookie = 0; 2155 while (get_next_image_info(parentTeam->id, &imageCookie, &imageInfo) 2156 == B_OK) { 2157 image_id image = register_image(team, &imageInfo, sizeof(imageInfo)); 2158 if (image < 0) 2159 goto err5; 2160 } 2161 2162 // create the main thread 2163 { 2164 ThreadCreationAttributes threadCreationAttributes(NULL, 2165 parentThread->name, parentThread->priority, NULL, team->id, thread); 2166 threadCreationAttributes.forkArgs = forkArgs; 2167 threadID = thread_create_thread(threadCreationAttributes, false); 2168 if (threadID < 0) { 2169 status = threadID; 2170 goto err5; 2171 } 2172 } 2173 2174 // notify the debugger 2175 user_debug_team_created(team->id); 2176 2177 T(TeamForked(threadID)); 2178 2179 resume_thread(threadID); 2180 return threadID; 2181 2182err5: 2183 remove_images(team); 2184err4: 2185 team->address_space->RemoveAndPut(); 2186err3: 2187 delete_realtime_sem_context(team->realtime_sem_context); 2188err25: 2189 vfs_put_io_context(team->io_context); 2190err2: 2191 free(forkArgs); 2192err1: 2193 // Remove the team structure from the process group, the parent team, and 2194 // the team hash table and delete the team structure. 2195 parentTeam->LockTeamAndProcessGroup(); 2196 team->Lock(); 2197 2198 remove_team_from_group(team); 2199 remove_team_from_parent(team->parent, team); 2200 2201 team->Unlock(); 2202 parentTeam->UnlockTeamAndProcessGroup(); 2203 2204 teamsLocker.Lock(); 2205 sTeamHash.Remove(team); 2206 teamsLocker.Unlock(); 2207 2208 sNotificationService.Notify(TEAM_REMOVED, team); 2209 2210 team->ReleaseReference(); 2211 2212 return status; 2213} 2214 2215 2216/*! Returns if the specified team \a parent has any children belonging to the 2217 process group with the specified ID \a groupID. 2218 The caller must hold \a parent's lock. 2219*/ 2220static bool 2221has_children_in_group(Team* parent, pid_t groupID) 2222{ 2223 for (Team* child = parent->children; child != NULL; 2224 child = child->siblings_next) { 2225 TeamLocker childLocker(child); 2226 if (child->group_id == groupID) 2227 return true; 2228 } 2229 2230 return false; 2231} 2232 2233 2234/*! Returns the first job control entry from \a children, which matches \a id. 2235 \a id can be: 2236 - \code > 0 \endcode: Matching an entry with that team ID. 2237 - \code == -1 \endcode: Matching any entry. 2238 - \code < -1 \endcode: Matching any entry with a process group ID of \c -id. 2239 \c 0 is an invalid value for \a id. 2240 2241 The caller must hold the lock of the team that \a children belongs to. 2242 2243 \param children The job control entry list to check. 2244 \param id The match criterion. 2245 \return The first matching entry or \c NULL, if none matches. 2246*/ 2247static job_control_entry* 2248get_job_control_entry(team_job_control_children& children, pid_t id) 2249{ 2250 for (JobControlEntryList::Iterator it = children.entries.GetIterator(); 2251 job_control_entry* entry = it.Next();) { 2252 2253 if (id > 0) { 2254 if (entry->thread == id) 2255 return entry; 2256 } else if (id == -1) { 2257 return entry; 2258 } else { 2259 pid_t processGroup 2260 = (entry->team ? entry->team->group_id : entry->group_id); 2261 if (processGroup == -id) 2262 return entry; 2263 } 2264 } 2265 2266 return NULL; 2267} 2268 2269 2270/*! Returns the first job control entry from one of team's dead, continued, or 2271 stopped children which matches \a id. 2272 \a id can be: 2273 - \code > 0 \endcode: Matching an entry with that team ID. 2274 - \code == -1 \endcode: Matching any entry. 2275 - \code < -1 \endcode: Matching any entry with a process group ID of \c -id. 2276 \c 0 is an invalid value for \a id. 2277 2278 The caller must hold \a team's lock. 2279 2280 \param team The team whose dead, stopped, and continued child lists shall be 2281 checked. 2282 \param id The match criterion. 2283 \param flags Specifies which children shall be considered. Dead children 2284 always are. Stopped children are considered when \a flags is ORed 2285 bitwise with \c WUNTRACED, continued children when \a flags is ORed 2286 bitwise with \c WCONTINUED. 2287 \return The first matching entry or \c NULL, if none matches. 2288*/ 2289static job_control_entry* 2290get_job_control_entry(Team* team, pid_t id, uint32 flags) 2291{ 2292 job_control_entry* entry = get_job_control_entry(team->dead_children, id); 2293 2294 if (entry == NULL && (flags & WCONTINUED) != 0) 2295 entry = get_job_control_entry(team->continued_children, id); 2296 2297 if (entry == NULL && (flags & WUNTRACED) != 0) 2298 entry = get_job_control_entry(team->stopped_children, id); 2299 2300 return entry; 2301} 2302 2303 2304job_control_entry::job_control_entry() 2305 : 2306 has_group_ref(false) 2307{ 2308} 2309 2310 2311job_control_entry::~job_control_entry() 2312{ 2313 if (has_group_ref) { 2314 InterruptsSpinLocker groupHashLocker(sGroupHashLock); 2315 2316 ProcessGroup* group = sGroupHash.Lookup(group_id); 2317 if (group == NULL) { 2318 panic("job_control_entry::~job_control_entry(): unknown group " 2319 "ID: %" B_PRId32, group_id); 2320 return; 2321 } 2322 2323 groupHashLocker.Unlock(); 2324 2325 group->ReleaseReference(); 2326 } 2327} 2328 2329 2330/*! Invoked when the owning team is dying, initializing the entry according to 2331 the dead state. 2332 2333 The caller must hold the owning team's lock and the scheduler lock. 2334*/ 2335void 2336job_control_entry::InitDeadState() 2337{ 2338 if (team != NULL) { 2339 ASSERT(team->exit.initialized); 2340 2341 group_id = team->group_id; 2342 team->group->AcquireReference(); 2343 has_group_ref = true; 2344 2345 thread = team->id; 2346 status = team->exit.status; 2347 reason = team->exit.reason; 2348 signal = team->exit.signal; 2349 signaling_user = team->exit.signaling_user; 2350 2351 team = NULL; 2352 } 2353} 2354 2355 2356job_control_entry& 2357job_control_entry::operator=(const job_control_entry& other) 2358{ 2359 state = other.state; 2360 thread = other.thread; 2361 signal = other.signal; 2362 has_group_ref = false; 2363 signaling_user = other.signaling_user; 2364 team = other.team; 2365 group_id = other.group_id; 2366 status = other.status; 2367 reason = other.reason; 2368 2369 return *this; 2370} 2371 2372 2373/*! This is the kernel backend for waitid(). 2374*/ 2375static thread_id 2376wait_for_child(pid_t child, uint32 flags, siginfo_t& _info) 2377{ 2378 Thread* thread = thread_get_current_thread(); 2379 Team* team = thread->team; 2380 struct job_control_entry foundEntry; 2381 struct job_control_entry* freeDeathEntry = NULL; 2382 status_t status = B_OK; 2383 2384 TRACE(("wait_for_child(child = %" B_PRId32 ", flags = %" B_PRId32 ")\n", 2385 child, flags)); 2386 2387 T(WaitForChild(child, flags)); 2388 2389 pid_t originalChild = child; 2390 2391 bool ignoreFoundEntries = false; 2392 bool ignoreFoundEntriesChecked = false; 2393 2394 while (true) { 2395 // lock the team 2396 TeamLocker teamLocker(team); 2397 2398 // A 0 child argument means to wait for all children in the process 2399 // group of the calling team. 2400 child = originalChild == 0 ? -team->group_id : originalChild; 2401 2402 // check whether any condition holds 2403 job_control_entry* entry = get_job_control_entry(team, child, flags); 2404 2405 // If we don't have an entry yet, check whether there are any children 2406 // complying to the process group specification at all. 2407 if (entry == NULL) { 2408 // No success yet -- check whether there are any children complying 2409 // to the process group specification at all. 2410 bool childrenExist = false; 2411 if (child == -1) { 2412 childrenExist = team->children != NULL; 2413 } else if (child < -1) { 2414 childrenExist = has_children_in_group(team, -child); 2415 } else { 2416 if (Team* childTeam = Team::Get(child)) { 2417 BReference<Team> childTeamReference(childTeam, true); 2418 TeamLocker childTeamLocker(childTeam); 2419 childrenExist = childTeam->parent == team; 2420 } 2421 } 2422 2423 if (!childrenExist) { 2424 // there is no child we could wait for 2425 status = ECHILD; 2426 } else { 2427 // the children we're waiting for are still running 2428 status = B_WOULD_BLOCK; 2429 } 2430 } else { 2431 // got something 2432 foundEntry = *entry; 2433 2434 // unless WNOWAIT has been specified, "consume" the wait state 2435 if ((flags & WNOWAIT) == 0 || ignoreFoundEntries) { 2436 if (entry->state == JOB_CONTROL_STATE_DEAD) { 2437 // The child is dead. Reap its death entry. 2438 freeDeathEntry = entry; 2439 team->dead_children.entries.Remove(entry); 2440 team->dead_children.count--; 2441 } else { 2442 // The child is well. Reset its job control state. 2443 team_set_job_control_state(entry->team, 2444 JOB_CONTROL_STATE_NONE, NULL, false); 2445 } 2446 } 2447 } 2448 2449 // If we haven't got anything yet, prepare for waiting for the 2450 // condition variable. 2451 ConditionVariableEntry deadWaitEntry; 2452 2453 if (status == B_WOULD_BLOCK && (flags & WNOHANG) == 0) 2454 team->dead_children.condition_variable.Add(&deadWaitEntry); 2455 2456 teamLocker.Unlock(); 2457 2458 // we got our entry and can return to our caller 2459 if (status == B_OK) { 2460 if (ignoreFoundEntries) { 2461 // ... unless we shall ignore found entries 2462 delete freeDeathEntry; 2463 freeDeathEntry = NULL; 2464 continue; 2465 } 2466 2467 break; 2468 } 2469 2470 if (status != B_WOULD_BLOCK || (flags & WNOHANG) != 0) { 2471 T(WaitForChildDone(status)); 2472 return status; 2473 } 2474 2475 status = deadWaitEntry.Wait(B_CAN_INTERRUPT); 2476 if (status == B_INTERRUPTED) { 2477 T(WaitForChildDone(status)); 2478 return status; 2479 } 2480 2481 // If SA_NOCLDWAIT is set or SIGCHLD is ignored, we shall wait until 2482 // all our children are dead and fail with ECHILD. We check the 2483 // condition at this point. 2484 if (!ignoreFoundEntriesChecked) { 2485 teamLocker.Lock(); 2486 2487 struct sigaction& handler = team->SignalActionFor(SIGCHLD); 2488 if ((handler.sa_flags & SA_NOCLDWAIT) != 0 2489 || handler.sa_handler == SIG_IGN) { 2490 ignoreFoundEntries = true; 2491 } 2492 2493 teamLocker.Unlock(); 2494 2495 ignoreFoundEntriesChecked = true; 2496 } 2497 } 2498 2499 delete freeDeathEntry; 2500 2501 // When we got here, we have a valid death entry, and already got 2502 // unregistered from the team or group. Fill in the returned info. 2503 memset(&_info, 0, sizeof(_info)); 2504 _info.si_signo = SIGCHLD; 2505 _info.si_pid = foundEntry.thread; 2506 _info.si_uid = foundEntry.signaling_user; 2507 // TODO: Fill in si_errno? 2508 2509 switch (foundEntry.state) { 2510 case JOB_CONTROL_STATE_DEAD: 2511 _info.si_code = foundEntry.reason; 2512 _info.si_status = foundEntry.reason == CLD_EXITED 2513 ? foundEntry.status : foundEntry.signal; 2514 break; 2515 case JOB_CONTROL_STATE_STOPPED: 2516 _info.si_code = CLD_STOPPED; 2517 _info.si_status = foundEntry.signal; 2518 break; 2519 case JOB_CONTROL_STATE_CONTINUED: 2520 _info.si_code = CLD_CONTINUED; 2521 _info.si_status = 0; 2522 break; 2523 case JOB_CONTROL_STATE_NONE: 2524 // can't happen 2525 break; 2526 } 2527 2528 // If SIGCHLD is blocked, we shall clear pending SIGCHLDs, if no other child 2529 // status is available. 2530 TeamLocker teamLocker(team); 2531 InterruptsSpinLocker schedulerLocker(gSchedulerLock); 2532 2533 if (is_team_signal_blocked(team, SIGCHLD)) { 2534 if (get_job_control_entry(team, child, flags) == NULL) 2535 team->RemovePendingSignals(SIGNAL_TO_MASK(SIGCHLD)); 2536 } 2537 2538 schedulerLocker.Unlock(); 2539 teamLocker.Unlock(); 2540 2541 // When the team is dead, the main thread continues to live in the kernel 2542 // team for a very short time. To avoid surprises for the caller we rather 2543 // wait until the thread is really gone. 2544 if (foundEntry.state == JOB_CONTROL_STATE_DEAD) 2545 wait_for_thread(foundEntry.thread, NULL); 2546 2547 T(WaitForChildDone(foundEntry)); 2548 2549 return foundEntry.thread; 2550} 2551 2552 2553/*! Fills the team_info structure with information from the specified team. 2554 Interrupts must be enabled. The team must not be locked. 2555*/ 2556static status_t 2557fill_team_info(Team* team, team_info* info, size_t size) 2558{ 2559 if (size != sizeof(team_info)) 2560 return B_BAD_VALUE; 2561 2562 // TODO: Set more informations for team_info 2563 memset(info, 0, size); 2564 2565 info->team = team->id; 2566 // immutable 2567 info->image_count = count_images(team); 2568 // protected by sImageMutex 2569 2570 TeamLocker teamLocker(team); 2571 InterruptsSpinLocker debugInfoLocker(team->debug_info.lock); 2572 2573 info->thread_count = team->num_threads; 2574 //info->area_count = 2575 info->debugger_nub_thread = team->debug_info.nub_thread; 2576 info->debugger_nub_port = team->debug_info.nub_port; 2577 info->uid = team->effective_uid; 2578 info->gid = team->effective_gid; 2579 2580 strlcpy(info->args, team->Args(), sizeof(info->args)); 2581 info->argc = 1; 2582 2583 return B_OK; 2584} 2585 2586 2587/*! Returns whether the process group contains stopped processes. 2588 The caller must hold the process group's lock. 2589*/ 2590static bool 2591process_group_has_stopped_processes(ProcessGroup* group) 2592{ 2593 Team* team = group->teams; 2594 while (team != NULL) { 2595 // the parent team's lock guards the job control entry -- acquire it 2596 team->LockTeamAndParent(false); 2597 2598 if (team->job_control_entry != NULL 2599 && team->job_control_entry->state == JOB_CONTROL_STATE_STOPPED) { 2600 team->UnlockTeamAndParent(); 2601 return true; 2602 } 2603 2604 team->UnlockTeamAndParent(); 2605 2606 team = team->group_next; 2607 } 2608 2609 return false; 2610} 2611 2612 2613/*! Iterates through all process groups queued in team_remove_team() and signals 2614 those that are orphaned and have stopped processes. 2615 The caller must not hold any team or process group locks. 2616*/ 2617static void 2618orphaned_process_group_check() 2619{ 2620 // process as long as there are groups in the list 2621 while (true) { 2622 // remove the head from the list 2623 MutexLocker orphanedCheckLocker(sOrphanedCheckLock); 2624 2625 ProcessGroup* group = sOrphanedCheckProcessGroups.RemoveHead(); 2626 if (group == NULL) 2627 return; 2628 2629 group->UnsetOrphanedCheck(); 2630 BReference<ProcessGroup> groupReference(group); 2631 2632 orphanedCheckLocker.Unlock(); 2633 2634 AutoLocker<ProcessGroup> groupLocker(group); 2635 2636 // If the group is orphaned and contains stopped processes, we're 2637 // supposed to send SIGHUP + SIGCONT. 2638 if (group->IsOrphaned() && process_group_has_stopped_processes(group)) { 2639 Thread* currentThread = thread_get_current_thread(); 2640 2641 Signal signal(SIGHUP, SI_USER, B_OK, currentThread->team->id); 2642 send_signal_to_process_group_locked(group, signal, 0); 2643 2644 signal.SetNumber(SIGCONT); 2645 send_signal_to_process_group_locked(group, signal, 0); 2646 } 2647 } 2648} 2649 2650 2651static status_t 2652common_get_team_usage_info(team_id id, int32 who, team_usage_info* info, 2653 uint32 flags) 2654{ 2655 if (who != B_TEAM_USAGE_SELF && who != B_TEAM_USAGE_CHILDREN) 2656 return B_BAD_VALUE; 2657 2658 // get the team 2659 Team* team = Team::GetAndLock(id); 2660 if (team == NULL) 2661 return B_BAD_TEAM_ID; 2662 BReference<Team> teamReference(team, true); 2663 TeamLocker teamLocker(team, true); 2664 2665 if ((flags & B_CHECK_PERMISSION) != 0) { 2666 uid_t uid = geteuid(); 2667 if (uid != 0 && uid != team->effective_uid) 2668 return B_NOT_ALLOWED; 2669 } 2670 2671 bigtime_t kernelTime = 0; 2672 bigtime_t userTime = 0; 2673 2674 switch (who) { 2675 case B_TEAM_USAGE_SELF: 2676 { 2677 Thread* thread = team->thread_list; 2678 2679 for (; thread != NULL; thread = thread->team_next) { 2680 InterruptsSpinLocker threadTimeLocker(thread->time_lock); 2681 kernelTime += thread->kernel_time; 2682 userTime += thread->user_time; 2683 } 2684 2685 kernelTime += team->dead_threads_kernel_time; 2686 userTime += team->dead_threads_user_time; 2687 break; 2688 } 2689 2690 case B_TEAM_USAGE_CHILDREN: 2691 { 2692 Team* child = team->children; 2693 for (; child != NULL; child = child->siblings_next) { 2694 TeamLocker childLocker(child); 2695 2696 Thread* thread = team->thread_list; 2697 2698 for (; thread != NULL; thread = thread->team_next) { 2699 InterruptsSpinLocker threadTimeLocker(thread->time_lock); 2700 kernelTime += thread->kernel_time; 2701 userTime += thread->user_time; 2702 } 2703 2704 kernelTime += child->dead_threads_kernel_time; 2705 userTime += child->dead_threads_user_time; 2706 } 2707 2708 kernelTime += team->dead_children.kernel_time; 2709 userTime += team->dead_children.user_time; 2710 break; 2711 } 2712 } 2713 2714 info->kernel_time = kernelTime; 2715 info->user_time = userTime; 2716 2717 return B_OK; 2718} 2719 2720 2721// #pragma mark - Private kernel API 2722 2723 2724status_t 2725team_init(kernel_args* args) 2726{ 2727 // create the team hash table 2728 new(&sTeamHash) TeamTable; 2729 if (sTeamHash.Init(64) != B_OK) 2730 panic("Failed to init team hash table!"); 2731 2732 new(&sGroupHash) ProcessGroupHashTable; 2733 if (sGroupHash.Init() != B_OK) 2734 panic("Failed to init process group hash table!"); 2735 2736 // create initial session and process groups 2737 2738 ProcessSession* session = new(std::nothrow) ProcessSession(1); 2739 if (session == NULL) 2740 panic("Could not create initial session.\n"); 2741 BReference<ProcessSession> sessionReference(session, true); 2742 2743 ProcessGroup* group = new(std::nothrow) ProcessGroup(1); 2744 if (group == NULL) 2745 panic("Could not create initial process group.\n"); 2746 BReference<ProcessGroup> groupReference(group, true); 2747 2748 group->Publish(session); 2749 2750 // create the kernel team 2751 sKernelTeam = Team::Create(1, "kernel_team", true); 2752 if (sKernelTeam == NULL) 2753 panic("could not create kernel team!\n"); 2754 sKernelTeam->SetArgs(sKernelTeam->Name()); 2755 sKernelTeam->state = TEAM_STATE_NORMAL; 2756 2757 sKernelTeam->saved_set_uid = 0; 2758 sKernelTeam->real_uid = 0; 2759 sKernelTeam->effective_uid = 0; 2760 sKernelTeam->saved_set_gid = 0; 2761 sKernelTeam->real_gid = 0; 2762 sKernelTeam->effective_gid = 0; 2763 sKernelTeam->supplementary_groups = NULL; 2764 sKernelTeam->supplementary_group_count = 0; 2765 2766 insert_team_into_group(group, sKernelTeam); 2767 2768 sKernelTeam->io_context = vfs_new_io_context(NULL, false); 2769 if (sKernelTeam->io_context == NULL) 2770 panic("could not create io_context for kernel team!\n"); 2771 2772 // stick it in the team hash 2773 sTeamHash.Insert(sKernelTeam); 2774 2775 add_debugger_command_etc("team", &dump_team_info, 2776 "Dump info about a particular team", 2777 "[ <id> | <address> | <name> ]\n" 2778 "Prints information about the specified team. If no argument is given\n" 2779 "the current team is selected.\n" 2780 " <id> - The ID of the team.\n" 2781 " <address> - The address of the team structure.\n" 2782 " <name> - The team's name.\n", 0); 2783 add_debugger_command_etc("teams", &dump_teams, "List all teams", 2784 "\n" 2785 "Prints a list of all existing teams.\n", 0); 2786 2787 new(&sNotificationService) TeamNotificationService(); 2788 2789 return B_OK; 2790} 2791 2792 2793int32 2794team_max_teams(void) 2795{ 2796 return sMaxTeams; 2797} 2798 2799 2800int32 2801team_used_teams(void) 2802{ 2803 InterruptsSpinLocker teamsLocker(sTeamHashLock); 2804 return sUsedTeams; 2805} 2806 2807 2808/*! Returns a death entry of a child team specified by ID (if any). 2809 The caller must hold the team's lock. 2810 2811 \param team The team whose dead children list to check. 2812 \param child The ID of the child for whose death entry to lock. Must be > 0. 2813 \param _deleteEntry Return variable, indicating whether the caller needs to 2814 delete the returned entry. 2815 \return The death entry of the matching team, or \c NULL, if no death entry 2816 for the team was found. 2817*/ 2818job_control_entry* 2819team_get_death_entry(Team* team, thread_id child, bool* _deleteEntry) 2820{ 2821 if (child <= 0) 2822 return NULL; 2823 2824 job_control_entry* entry = get_job_control_entry(team->dead_children, 2825 child); 2826 if (entry) { 2827 // remove the entry only, if the caller is the parent of the found team 2828 if (team_get_current_team_id() == entry->thread) { 2829 team->dead_children.entries.Remove(entry); 2830 team->dead_children.count--; 2831 *_deleteEntry = true; 2832 } else { 2833 *_deleteEntry = false; 2834 } 2835 } 2836 2837 return entry; 2838} 2839 2840 2841/*! Quick check to see if we have a valid team ID. */ 2842bool 2843team_is_valid(team_id id) 2844{ 2845 if (id <= 0) 2846 return false; 2847 2848 InterruptsSpinLocker teamsLocker(sTeamHashLock); 2849 2850 return team_get_team_struct_locked(id) != NULL; 2851} 2852 2853 2854Team* 2855team_get_team_struct_locked(team_id id) 2856{ 2857 return sTeamHash.Lookup(id); 2858} 2859 2860 2861void 2862team_set_controlling_tty(int32 ttyIndex) 2863{ 2864 // lock the team, so its session won't change while we're playing with it 2865 Team* team = thread_get_current_thread()->team; 2866 TeamLocker teamLocker(team); 2867 2868 // get and lock the session 2869 ProcessSession* session = team->group->Session(); 2870 AutoLocker<ProcessSession> sessionLocker(session); 2871 2872 // set the session's fields 2873 session->controlling_tty = ttyIndex; 2874 session->foreground_group = -1; 2875} 2876 2877 2878int32 2879team_get_controlling_tty() 2880{ 2881 // lock the team, so its session won't change while we're playing with it 2882 Team* team = thread_get_current_thread()->team; 2883 TeamLocker teamLocker(team); 2884 2885 // get and lock the session 2886 ProcessSession* session = team->group->Session(); 2887 AutoLocker<ProcessSession> sessionLocker(session); 2888 2889 // get the session's field 2890 return session->controlling_tty; 2891} 2892 2893 2894status_t 2895team_set_foreground_process_group(int32 ttyIndex, pid_t processGroupID) 2896{ 2897 // lock the team, so its session won't change while we're playing with it 2898 Thread* thread = thread_get_current_thread(); 2899 Team* team = thread->team; 2900 TeamLocker teamLocker(team); 2901 2902 // get and lock the session 2903 ProcessSession* session = team->group->Session(); 2904 AutoLocker<ProcessSession> sessionLocker(session); 2905 2906 // check given TTY -- must be the controlling tty of the calling process 2907 if (session->controlling_tty != ttyIndex) 2908 return ENOTTY; 2909 2910 // check given process group -- must belong to our session 2911 { 2912 InterruptsSpinLocker groupHashLocker(sGroupHashLock); 2913 ProcessGroup* group = sGroupHash.Lookup(processGroupID); 2914 if (group == NULL || group->Session() != session) 2915 return B_BAD_VALUE; 2916 } 2917 2918 // If we are a background group, we can do that unharmed only when we 2919 // ignore or block SIGTTOU. Otherwise the group gets a SIGTTOU. 2920 if (session->foreground_group != -1 2921 && session->foreground_group != team->group_id 2922 && team->SignalActionFor(SIGTTOU).sa_handler != SIG_IGN) { 2923 InterruptsSpinLocker schedulerLocker(gSchedulerLock); 2924 2925 if (!is_team_signal_blocked(team, SIGTTOU)) { 2926 pid_t groupID = team->group_id; 2927 2928 schedulerLocker.Unlock(); 2929 sessionLocker.Unlock(); 2930 teamLocker.Unlock(); 2931 2932 Signal signal(SIGTTOU, SI_USER, B_OK, team->id); 2933 send_signal_to_process_group(groupID, signal, 0); 2934 return B_INTERRUPTED; 2935 } 2936 } 2937 2938 session->foreground_group = processGroupID; 2939 2940 return B_OK; 2941} 2942 2943 2944/*! Removes the specified team from the global team hash, from its process 2945 group, and from its parent. 2946 It also moves all of its children to the kernel team. 2947 2948 The caller must hold the following locks: 2949 - \a team's process group's lock, 2950 - the kernel team's lock, 2951 - \a team's parent team's lock (might be the kernel team), and 2952 - \a team's lock. 2953*/ 2954void 2955team_remove_team(Team* team, pid_t& _signalGroup) 2956{ 2957 Team* parent = team->parent; 2958 2959 // remember how long this team lasted 2960 parent->dead_children.kernel_time += team->dead_threads_kernel_time 2961 + team->dead_children.kernel_time; 2962 parent->dead_children.user_time += team->dead_threads_user_time 2963 + team->dead_children.user_time; 2964 2965 // remove the team from the hash table 2966 InterruptsSpinLocker teamsLocker(sTeamHashLock); 2967 sTeamHash.Remove(team); 2968 sUsedTeams--; 2969 teamsLocker.Unlock(); 2970 2971 // The team can no longer be accessed by ID. Navigation to it is still 2972 // possible from its process group and its parent and children, but that 2973 // will be rectified shortly. 2974 team->state = TEAM_STATE_DEATH; 2975 2976 // If we're a controlling process (i.e. a session leader with controlling 2977 // terminal), there's a bit of signalling we have to do. We can't do any of 2978 // the signaling here due to the bunch of locks we're holding, but we need 2979 // to determine, whom to signal. 2980 _signalGroup = -1; 2981 bool isSessionLeader = false; 2982 if (team->session_id == team->id 2983 && team->group->Session()->controlling_tty >= 0) { 2984 isSessionLeader = true; 2985 2986 ProcessSession* session = team->group->Session(); 2987 2988 AutoLocker<ProcessSession> sessionLocker(session); 2989 2990 session->controlling_tty = -1; 2991 _signalGroup = session->foreground_group; 2992 } 2993 2994 // remove us from our process group 2995 remove_team_from_group(team); 2996 2997 // move the team's children to the kernel team 2998 while (Team* child = team->children) { 2999 // remove the child from the current team and add it to the kernel team 3000 TeamLocker childLocker(child); 3001 3002 remove_team_from_parent(team, child); 3003 insert_team_into_parent(sKernelTeam, child); 3004 3005 // move job control entries too 3006 sKernelTeam->stopped_children.entries.MoveFrom( 3007 &team->stopped_children.entries); 3008 sKernelTeam->continued_children.entries.MoveFrom( 3009 &team->continued_children.entries); 3010 3011 // If the team was a session leader with controlling terminal, 3012 // we need to send SIGHUP + SIGCONT to all newly-orphaned process 3013 // groups with stopped processes. Due to locking complications we can't 3014 // do that here, so we only check whether we were a reason for the 3015 // child's process group not being an orphan and, if so, schedule a 3016 // later check (cf. orphaned_process_group_check()). 3017 if (isSessionLeader) { 3018 ProcessGroup* childGroup = child->group; 3019 if (childGroup->Session()->id == team->session_id 3020 && childGroup->id != team->group_id) { 3021 childGroup->ScheduleOrphanedCheck(); 3022 } 3023 } 3024 3025 // Note, we don't move the dead children entries. Those will be deleted 3026 // when the team structure is deleted. 3027 } 3028 3029 // remove us from our parent 3030 remove_team_from_parent(parent, team); 3031} 3032 3033 3034/*! Kills all threads but the main thread of the team and shuts down user 3035 debugging for it. 3036 To be called on exit of the team's main thread. No locks must be held. 3037 3038 \param team The team in question. 3039 \return The port of the debugger for the team, -1 if none. To be passed to 3040 team_delete_team(). 3041*/ 3042port_id 3043team_shutdown_team(Team* team) 3044{ 3045 ASSERT(thread_get_current_thread() == team->main_thread); 3046 3047 TeamLocker teamLocker(team); 3048 3049 // Make sure debugging changes won't happen anymore. 3050 port_id debuggerPort = -1; 3051 while (true) { 3052 // If a debugger change is in progress for the team, we'll have to 3053 // wait until it is done. 3054 ConditionVariableEntry waitForDebuggerEntry; 3055 bool waitForDebugger = false; 3056 3057 InterruptsSpinLocker debugInfoLocker(team->debug_info.lock); 3058 3059 if (team->debug_info.debugger_changed_condition != NULL) { 3060 team->debug_info.debugger_changed_condition->Add( 3061 &waitForDebuggerEntry); 3062 waitForDebugger = true; 3063 } else if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) { 3064 // The team is being debugged. That will stop with the termination 3065 // of the nub thread. Since we set the team state to death, no one 3066 // can install a debugger anymore. We fetch the debugger's port to 3067 // send it a message at the bitter end. 3068 debuggerPort = team->debug_info.debugger_port; 3069 } 3070 3071 debugInfoLocker.Unlock(); 3072 3073 if (!waitForDebugger) 3074 break; 3075 3076 // wait for the debugger change to be finished 3077 teamLocker.Unlock(); 3078 3079 waitForDebuggerEntry.Wait(); 3080 3081 teamLocker.Lock(); 3082 } 3083 3084 // Mark the team as shutting down. That will prevent new threads from being 3085 // created and debugger changes from taking place. 3086 team->state = TEAM_STATE_SHUTDOWN; 3087 3088 // delete all timers 3089 team->DeleteUserTimers(false); 3090 3091 // deactivate CPU time user timers for the team 3092 InterruptsSpinLocker schedulerLocker(gSchedulerLock); 3093 3094 if (team->HasActiveCPUTimeUserTimers()) 3095 team->DeactivateCPUTimeUserTimers(); 3096 3097 schedulerLocker.Unlock(); 3098 3099 // kill all threads but the main thread 3100 team_death_entry deathEntry; 3101 deathEntry.condition.Init(team, "team death"); 3102 3103 while (true) { 3104 team->death_entry = &deathEntry; 3105 deathEntry.remaining_threads = 0; 3106 3107 Thread* thread = team->thread_list; 3108 while (thread != NULL) { 3109 if (thread != team->main_thread) { 3110 Signal signal(SIGKILLTHR, SI_USER, B_OK, team->id); 3111 send_signal_to_thread(thread, signal, B_DO_NOT_RESCHEDULE); 3112 deathEntry.remaining_threads++; 3113 } 3114 3115 thread = thread->team_next; 3116 } 3117 3118 if (deathEntry.remaining_threads == 0) 3119 break; 3120 3121 // there are threads to wait for 3122 ConditionVariableEntry entry; 3123 deathEntry.condition.Add(&entry); 3124 3125 teamLocker.Unlock(); 3126 3127 entry.Wait(); 3128 3129 teamLocker.Lock(); 3130 } 3131 3132 team->death_entry = NULL; 3133 3134 return debuggerPort; 3135} 3136 3137 3138/*! Called on team exit to notify threads waiting on the team and free most 3139 resources associated with it. 3140 The caller shouldn't hold any locks. 3141*/ 3142void 3143team_delete_team(Team* team, port_id debuggerPort) 3144{ 3145 // Not quite in our job description, but work that has been left by 3146 // team_remove_team() and that can be done now that we're not holding any 3147 // locks. 3148 orphaned_process_group_check(); 3149 3150 team_id teamID = team->id; 3151 3152 ASSERT(team->num_threads == 0); 3153 3154 // If someone is waiting for this team to be loaded, but it dies 3155 // unexpectedly before being done, we need to notify the waiting 3156 // thread now. 3157 3158 TeamLocker teamLocker(team); 3159 3160 if (team->loading_info) { 3161 // there's indeed someone waiting 3162 struct team_loading_info* loadingInfo = team->loading_info; 3163 team->loading_info = NULL; 3164 3165 loadingInfo->result = B_ERROR; 3166 loadingInfo->done = true; 3167 3168 InterruptsSpinLocker schedulerLocker(gSchedulerLock); 3169 3170 // wake up the waiting thread 3171 if (loadingInfo->thread->state == B_THREAD_SUSPENDED) 3172 scheduler_enqueue_in_run_queue(loadingInfo->thread); 3173 } 3174 3175 // notify team watchers 3176 3177 { 3178 // we're not reachable from anyone anymore at this point, so we 3179 // can safely access the list without any locking 3180 struct team_watcher* watcher; 3181 while ((watcher = (struct team_watcher*)list_remove_head_item( 3182 &team->watcher_list)) != NULL) { 3183 watcher->hook(teamID, watcher->data); 3184 free(watcher); 3185 } 3186 } 3187 3188 teamLocker.Unlock(); 3189 3190 sNotificationService.Notify(TEAM_REMOVED, team); 3191 3192 // free team resources 3193 3194 delete_realtime_sem_context(team->realtime_sem_context); 3195 xsi_sem_undo(team); 3196 remove_images(team); 3197 team->address_space->RemoveAndPut(); 3198 3199 team->ReleaseReference(); 3200 3201 // notify the debugger, that the team is gone 3202 user_debug_team_deleted(teamID, debuggerPort); 3203} 3204 3205 3206Team* 3207team_get_kernel_team(void) 3208{ 3209 return sKernelTeam; 3210} 3211 3212 3213team_id 3214team_get_kernel_team_id(void) 3215{ 3216 if (!sKernelTeam) 3217 return 0; 3218 3219 return sKernelTeam->id; 3220} 3221 3222 3223team_id 3224team_get_current_team_id(void) 3225{ 3226 return thread_get_current_thread()->team->id; 3227} 3228 3229 3230status_t 3231team_get_address_space(team_id id, VMAddressSpace** _addressSpace) 3232{ 3233 if (id == sKernelTeam->id) { 3234 // we're the kernel team, so we don't have to go through all 3235 // the hassle (locking and hash lookup) 3236 *_addressSpace = VMAddressSpace::GetKernel(); 3237 return B_OK; 3238 } 3239 3240 InterruptsSpinLocker teamsLocker(sTeamHashLock); 3241 3242 Team* team = team_get_team_struct_locked(id); 3243 if (team == NULL) 3244 return B_BAD_VALUE; 3245 3246 team->address_space->Get(); 3247 *_addressSpace = team->address_space; 3248 return B_OK; 3249} 3250 3251 3252/*! Sets the team's job control state. 3253 The caller must hold the parent team's lock. Interrupts are allowed to be 3254 enabled or disabled. In the latter case the scheduler lock may be held as 3255 well. 3256 \a team The team whose job control state shall be set. 3257 \a newState The new state to be set. 3258 \a signal The signal the new state was caused by. Can \c NULL, if none. Then 3259 the caller is responsible for filling in the following fields of the 3260 entry before releasing the parent team's lock, unless the new state is 3261 \c JOB_CONTROL_STATE_NONE: 3262 - \c signal: The number of the signal causing the state change. 3263 - \c signaling_user: The real UID of the user sending the signal. 3264 \a schedulerLocked indicates whether the scheduler lock is being held, too. 3265*/ 3266void 3267team_set_job_control_state(Team* team, job_control_state newState, 3268 Signal* signal, bool schedulerLocked) 3269{ 3270 if (team == NULL || team->job_control_entry == NULL) 3271 return; 3272 3273 // don't touch anything, if the state stays the same or the team is already 3274 // dead 3275 job_control_entry* entry = team->job_control_entry; 3276 if (entry->state == newState || entry->state == JOB_CONTROL_STATE_DEAD) 3277 return; 3278 3279 T(SetJobControlState(team->id, newState, signal)); 3280 3281 // remove from the old list 3282 switch (entry->state) { 3283 case JOB_CONTROL_STATE_NONE: 3284 // entry is in no list ATM 3285 break; 3286 case JOB_CONTROL_STATE_DEAD: 3287 // can't get here 3288 break; 3289 case JOB_CONTROL_STATE_STOPPED: 3290 team->parent->stopped_children.entries.Remove(entry); 3291 break; 3292 case JOB_CONTROL_STATE_CONTINUED: 3293 team->parent->continued_children.entries.Remove(entry); 3294 break; 3295 } 3296 3297 entry->state = newState; 3298 3299 if (signal != NULL) { 3300 entry->signal = signal->Number(); 3301 entry->signaling_user = signal->SendingUser(); 3302 } 3303 3304 // add to new list 3305 team_job_control_children* childList = NULL; 3306 switch (entry->state) { 3307 case JOB_CONTROL_STATE_NONE: 3308 // entry doesn't get into any list 3309 break; 3310 case JOB_CONTROL_STATE_DEAD: 3311 childList = &team->parent->dead_children; 3312 team->parent->dead_children.count++; 3313 break; 3314 case JOB_CONTROL_STATE_STOPPED: 3315 childList = &team->parent->stopped_children; 3316 break; 3317 case JOB_CONTROL_STATE_CONTINUED: 3318 childList = &team->parent->continued_children; 3319 break; 3320 } 3321 3322 if (childList != NULL) { 3323 childList->entries.Add(entry); 3324 team->parent->dead_children.condition_variable.NotifyAll( 3325 schedulerLocked); 3326 } 3327} 3328 3329 3330/*! Inits the given team's exit information, if not yet initialized, to some 3331 generic "killed" status. 3332 The caller must not hold the team's lock. Interrupts must be enabled. 3333 3334 \param team The team whose exit info shall be initialized. 3335*/ 3336void 3337team_init_exit_info_on_error(Team* team) 3338{ 3339 TeamLocker teamLocker(team); 3340 3341 if (!team->exit.initialized) { 3342 team->exit.reason = CLD_KILLED; 3343 team->exit.signal = SIGKILL; 3344 team->exit.signaling_user = geteuid(); 3345 team->exit.status = 0; 3346 team->exit.initialized = true; 3347 } 3348} 3349 3350 3351/*! Adds a hook to the team that is called as soon as this team goes away. 3352 This call might get public in the future. 3353*/ 3354status_t 3355start_watching_team(team_id teamID, void (*hook)(team_id, void*), void* data) 3356{ 3357 if (hook == NULL || teamID < B_OK) 3358 return B_BAD_VALUE; 3359 3360 // create the watcher object 3361 team_watcher* watcher = (team_watcher*)malloc(sizeof(team_watcher)); 3362 if (watcher == NULL) 3363 return B_NO_MEMORY; 3364 3365 watcher->hook = hook; 3366 watcher->data = data; 3367 3368 // add watcher, if the team isn't already dying 3369 // get the team 3370 Team* team = Team::GetAndLock(teamID); 3371 if (team == NULL) { 3372 free(watcher); 3373 return B_BAD_TEAM_ID; 3374 } 3375 3376 list_add_item(&team->watcher_list, watcher); 3377 3378 team->UnlockAndReleaseReference(); 3379 3380 return B_OK; 3381} 3382 3383 3384status_t 3385stop_watching_team(team_id teamID, void (*hook)(team_id, void*), void* data) 3386{ 3387 if (hook == NULL || teamID < 0) 3388 return B_BAD_VALUE; 3389 3390 // get team and remove watcher (if present) 3391 Team* team = Team::GetAndLock(teamID); 3392 if (team == NULL) 3393 return B_BAD_TEAM_ID; 3394 3395 // search for watcher 3396 team_watcher* watcher = NULL; 3397 while ((watcher = (team_watcher*)list_get_next_item( 3398 &team->watcher_list, watcher)) != NULL) { 3399 if (watcher->hook == hook && watcher->data == data) { 3400 // got it! 3401 list_remove_item(&team->watcher_list, watcher); 3402 break; 3403 } 3404 } 3405 3406 team->UnlockAndReleaseReference(); 3407 3408 if (watcher == NULL) 3409 return B_ENTRY_NOT_FOUND; 3410 3411 free(watcher); 3412 return B_OK; 3413} 3414 3415 3416/*! Allocates a user_thread structure from the team. 3417 The team lock must be held, unless the function is called for the team's 3418 main thread. Interrupts must be enabled. 3419*/ 3420struct user_thread* 3421team_allocate_user_thread(Team* team) 3422{ 3423 if (team->user_data == 0) 3424 return NULL; 3425 3426 // take an entry from the free list, if any 3427 if (struct free_user_thread* entry = team->free_user_threads) { 3428 user_thread* thread = entry->thread; 3429 team->free_user_threads = entry->next; 3430 free(entry); 3431 return thread; 3432 } 3433 3434 while (true) { 3435 // enough space left? 3436 size_t needed = ROUNDUP(sizeof(user_thread), 8); 3437 if (team->user_data_size - team->used_user_data < needed) { 3438 // try to resize the area 3439 if (resize_area(team->user_data_area, 3440 team->user_data_size + B_PAGE_SIZE) != B_OK) { 3441 return NULL; 3442 } 3443 3444 // resized user area successfully -- try to allocate the user_thread 3445 // again 3446 team->user_data_size += B_PAGE_SIZE; 3447 continue; 3448 } 3449 3450 // allocate the user_thread 3451 user_thread* thread 3452 = (user_thread*)(team->user_data + team->used_user_data); 3453 team->used_user_data += needed; 3454 3455 return thread; 3456 } 3457} 3458 3459 3460/*! Frees the given user_thread structure. 3461 The team's lock must not be held. Interrupts must be enabled. 3462 \param team The team the user thread was allocated from. 3463 \param userThread The user thread to free. 3464*/ 3465void 3466team_free_user_thread(Team* team, struct user_thread* userThread) 3467{ 3468 if (userThread == NULL) 3469 return; 3470 3471 // create a free list entry 3472 free_user_thread* entry 3473 = (free_user_thread*)malloc(sizeof(free_user_thread)); 3474 if (entry == NULL) { 3475 // we have to leak the user thread :-/ 3476 return; 3477 } 3478 3479 // add to free list 3480 TeamLocker teamLocker(team); 3481 3482 entry->thread = userThread; 3483 entry->next = team->free_user_threads; 3484 team->free_user_threads = entry; 3485} 3486 3487 3488// #pragma mark - Associated data interface 3489 3490 3491AssociatedData::AssociatedData() 3492 : 3493 fOwner(NULL) 3494{ 3495} 3496 3497 3498AssociatedData::~AssociatedData() 3499{ 3500} 3501 3502 3503void 3504AssociatedData::OwnerDeleted(AssociatedDataOwner* owner) 3505{ 3506} 3507 3508 3509AssociatedDataOwner::AssociatedDataOwner() 3510{ 3511 mutex_init(&fLock, "associated data owner"); 3512} 3513 3514 3515AssociatedDataOwner::~AssociatedDataOwner() 3516{ 3517 mutex_destroy(&fLock); 3518} 3519 3520 3521bool 3522AssociatedDataOwner::AddData(AssociatedData* data) 3523{ 3524 MutexLocker locker(fLock); 3525 3526 if (data->Owner() != NULL) 3527 return false; 3528 3529 data->AcquireReference(); 3530 fList.Add(data); 3531 data->SetOwner(this); 3532 3533 return true; 3534} 3535 3536 3537bool 3538AssociatedDataOwner::RemoveData(AssociatedData* data) 3539{ 3540 MutexLocker locker(fLock); 3541 3542 if (data->Owner() != this) 3543 return false; 3544 3545 data->SetOwner(NULL); 3546 fList.Remove(data); 3547 3548 locker.Unlock(); 3549 3550 data->ReleaseReference(); 3551 3552 return true; 3553} 3554 3555 3556void 3557AssociatedDataOwner::PrepareForDeletion() 3558{ 3559 MutexLocker locker(fLock); 3560 3561 // move all data to a temporary list and unset the owner 3562 DataList list; 3563 list.MoveFrom(&fList); 3564 3565 for (DataList::Iterator it = list.GetIterator(); 3566 AssociatedData* data = it.Next();) { 3567 data->SetOwner(NULL); 3568 } 3569 3570 locker.Unlock(); 3571 3572 // call the notification hooks and release our references 3573 while (AssociatedData* data = list.RemoveHead()) { 3574 data->OwnerDeleted(this); 3575 data->ReleaseReference(); 3576 } 3577} 3578 3579 3580/*! Associates data with the current team. 3581 When the team is deleted, the data object is notified. 3582 The team acquires a reference to the object. 3583 3584 \param data The data object. 3585 \return \c true on success, \c false otherwise. Fails only when the supplied 3586 data object is already associated with another owner. 3587*/ 3588bool 3589team_associate_data(AssociatedData* data) 3590{ 3591 return thread_get_current_thread()->team->AddData(data); 3592} 3593 3594 3595/*! Dissociates data from the current team. 3596 Balances an earlier call to team_associate_data(). 3597 3598 \param data The data object. 3599 \return \c true on success, \c false otherwise. Fails only when the data 3600 object is not associated with the current team. 3601*/ 3602bool 3603team_dissociate_data(AssociatedData* data) 3604{ 3605 return thread_get_current_thread()->team->RemoveData(data); 3606} 3607 3608 3609// #pragma mark - Public kernel API 3610 3611 3612thread_id 3613load_image(int32 argCount, const char** args, const char** env) 3614{ 3615 return load_image_etc(argCount, args, env, B_NORMAL_PRIORITY, 3616 B_CURRENT_TEAM, B_WAIT_TILL_LOADED); 3617} 3618 3619 3620thread_id 3621load_image_etc(int32 argCount, const char* const* args, 3622 const char* const* env, int32 priority, team_id parentID, uint32 flags) 3623{ 3624 // we need to flatten the args and environment 3625 3626 if (args == NULL) 3627 return B_BAD_VALUE; 3628 3629 // determine total needed size 3630 int32 argSize = 0; 3631 for (int32 i = 0; i < argCount; i++) 3632 argSize += strlen(args[i]) + 1; 3633 3634 int32 envCount = 0; 3635 int32 envSize = 0; 3636 while (env != NULL && env[envCount] != NULL) 3637 envSize += strlen(env[envCount++]) + 1; 3638 3639 int32 size = (argCount + envCount + 2) * sizeof(char*) + argSize + envSize; 3640 if (size > MAX_PROCESS_ARGS_SIZE) 3641 return B_TOO_MANY_ARGS; 3642 3643 // allocate space 3644 char** flatArgs = (char**)malloc(size); 3645 if (flatArgs == NULL) 3646 return B_NO_MEMORY; 3647 3648 char** slot = flatArgs; 3649 char* stringSpace = (char*)(flatArgs + argCount + envCount + 2); 3650 3651 // copy arguments and environment 3652 for (int32 i = 0; i < argCount; i++) { 3653 int32 argSize = strlen(args[i]) + 1; 3654 memcpy(stringSpace, args[i], argSize); 3655 *slot++ = stringSpace; 3656 stringSpace += argSize; 3657 } 3658 3659 *slot++ = NULL; 3660 3661 for (int32 i = 0; i < envCount; i++) { 3662 int32 envSize = strlen(env[i]) + 1; 3663 memcpy(stringSpace, env[i], envSize); 3664 *slot++ = stringSpace; 3665 stringSpace += envSize; 3666 } 3667 3668 *slot++ = NULL; 3669 3670 thread_id thread = load_image_internal(flatArgs, size, argCount, envCount, 3671 B_NORMAL_PRIORITY, parentID, B_WAIT_TILL_LOADED, -1, 0); 3672 3673 free(flatArgs); 3674 // load_image_internal() unset our variable if it took over ownership 3675 3676 return thread; 3677} 3678 3679 3680status_t 3681wait_for_team(team_id id, status_t* _returnCode) 3682{ 3683 // check whether the team exists 3684 InterruptsSpinLocker teamsLocker(sTeamHashLock); 3685 3686 Team* team = team_get_team_struct_locked(id); 3687 if (team == NULL) 3688 return B_BAD_TEAM_ID; 3689 3690 id = team->id; 3691 3692 teamsLocker.Unlock(); 3693 3694 // wait for the main thread (it has the same ID as the team) 3695 return wait_for_thread(id, _returnCode); 3696} 3697 3698 3699status_t 3700kill_team(team_id id) 3701{ 3702 InterruptsSpinLocker teamsLocker(sTeamHashLock); 3703 3704 Team* team = team_get_team_struct_locked(id); 3705 if (team == NULL) 3706 return B_BAD_TEAM_ID; 3707 3708 id = team->id; 3709 3710 teamsLocker.Unlock(); 3711 3712 if (team == sKernelTeam) 3713 return B_NOT_ALLOWED; 3714 3715 // Just kill the team's main thread (it has same ID as the team). The 3716 // cleanup code there will take care of the team. 3717 return kill_thread(id); 3718} 3719 3720 3721status_t 3722_get_team_info(team_id id, team_info* info, size_t size) 3723{ 3724 // get the team 3725 Team* team = Team::Get(id); 3726 if (team == NULL) 3727 return B_BAD_TEAM_ID; 3728 BReference<Team> teamReference(team, true); 3729 3730 // fill in the info 3731 return fill_team_info(team, info, size); 3732} 3733 3734 3735status_t 3736_get_next_team_info(int32* cookie, team_info* info, size_t size) 3737{ 3738 int32 slot = *cookie; 3739 if (slot < 1) 3740 slot = 1; 3741 3742 InterruptsSpinLocker locker(sTeamHashLock); 3743 3744 team_id lastTeamID = peek_next_thread_id(); 3745 // TODO: This is broken, since the id can wrap around! 3746 3747 // get next valid team 3748 Team* team = NULL; 3749 while (slot < lastTeamID && !(team = team_get_team_struct_locked(slot))) 3750 slot++; 3751 3752 if (team == NULL) 3753 return B_BAD_TEAM_ID; 3754 3755 // get a reference to the team and unlock 3756 BReference<Team> teamReference(team); 3757 locker.Unlock(); 3758 3759 // fill in the info 3760 *cookie = ++slot; 3761 return fill_team_info(team, info, size); 3762} 3763 3764 3765status_t 3766_get_team_usage_info(team_id id, int32 who, team_usage_info* info, size_t size) 3767{ 3768 if (size != sizeof(team_usage_info)) 3769 return B_BAD_VALUE; 3770 3771 return common_get_team_usage_info(id, who, info, 0); 3772} 3773 3774 3775pid_t 3776getpid(void) 3777{ 3778 return thread_get_current_thread()->team->id; 3779} 3780 3781 3782pid_t 3783getppid(void) 3784{ 3785 Team* team = thread_get_current_thread()->team; 3786 3787 TeamLocker teamLocker(team); 3788 3789 return team->parent->id; 3790} 3791 3792 3793pid_t 3794getpgid(pid_t id) 3795{ 3796 if (id < 0) { 3797 errno = EINVAL; 3798 return -1; 3799 } 3800 3801 if (id == 0) { 3802 // get process group of the calling process 3803 Team* team = thread_get_current_thread()->team; 3804 TeamLocker teamLocker(team); 3805 return team->group_id; 3806 } 3807 3808 // get the team 3809 Team* team = Team::GetAndLock(id); 3810 if (team == NULL) { 3811 errno = ESRCH; 3812 return -1; 3813 } 3814 3815 // get the team's process group ID 3816 pid_t groupID = team->group_id; 3817 3818 team->UnlockAndReleaseReference(); 3819 3820 return groupID; 3821} 3822 3823 3824pid_t 3825getsid(pid_t id) 3826{ 3827 if (id < 0) { 3828 errno = EINVAL; 3829 return -1; 3830 } 3831 3832 if (id == 0) { 3833 // get session of the calling process 3834 Team* team = thread_get_current_thread()->team; 3835 TeamLocker teamLocker(team); 3836 return team->session_id; 3837 } 3838 3839 // get the team 3840 Team* team = Team::GetAndLock(id); 3841 if (team == NULL) { 3842 errno = ESRCH; 3843 return -1; 3844 } 3845 3846 // get the team's session ID 3847 pid_t sessionID = team->session_id; 3848 3849 team->UnlockAndReleaseReference(); 3850 3851 return sessionID; 3852} 3853 3854 3855// #pragma mark - User syscalls 3856 3857 3858status_t 3859_user_exec(const char* userPath, const char* const* userFlatArgs, 3860 size_t flatArgsSize, int32 argCount, int32 envCount, mode_t umask) 3861{ 3862 // NOTE: Since this function normally doesn't return, don't use automatic 3863 // variables that need destruction in the function scope. 3864 char path[B_PATH_NAME_LENGTH]; 3865 3866 if (!IS_USER_ADDRESS(userPath) || !IS_USER_ADDRESS(userFlatArgs) 3867 || user_strlcpy(path, userPath, sizeof(path)) < B_OK) 3868 return B_BAD_ADDRESS; 3869 3870 // copy and relocate the flat arguments 3871 char** flatArgs; 3872 status_t error = copy_user_process_args(userFlatArgs, flatArgsSize, 3873 argCount, envCount, flatArgs); 3874 3875 if (error == B_OK) { 3876 error = exec_team(path, flatArgs, _ALIGN(flatArgsSize), argCount, 3877 envCount, umask); 3878 // this one only returns in case of error 3879 } 3880 3881 free(flatArgs); 3882 return error; 3883} 3884 3885 3886thread_id 3887_user_fork(void) 3888{ 3889 return fork_team(); 3890} 3891 3892 3893pid_t 3894_user_wait_for_child(thread_id child, uint32 flags, siginfo_t* userInfo) 3895{ 3896 if (userInfo != NULL && !IS_USER_ADDRESS(userInfo)) 3897 return B_BAD_ADDRESS; 3898 3899 siginfo_t info; 3900 pid_t foundChild = wait_for_child(child, flags, info); 3901 if (foundChild < 0) 3902 return syscall_restart_handle_post(foundChild); 3903 3904 // copy info back to userland 3905 if (userInfo != NULL && user_memcpy(userInfo, &info, sizeof(info)) != B_OK) 3906 return B_BAD_ADDRESS; 3907 3908 return foundChild; 3909} 3910 3911 3912pid_t 3913_user_process_info(pid_t process, int32 which) 3914{ 3915 // we only allow to return the parent of the current process 3916 if (which == PARENT_ID 3917 && process != 0 && process != thread_get_current_thread()->team->id) 3918 return B_BAD_VALUE; 3919 3920 pid_t result; 3921 switch (which) { 3922 case SESSION_ID: 3923 result = getsid(process); 3924 break; 3925 case GROUP_ID: 3926 result = getpgid(process); 3927 break; 3928 case PARENT_ID: 3929 result = getppid(); 3930 break; 3931 default: 3932 return B_BAD_VALUE; 3933 } 3934 3935 return result >= 0 ? result : errno; 3936} 3937 3938 3939pid_t 3940_user_setpgid(pid_t processID, pid_t groupID) 3941{ 3942 // setpgid() can be called either by the parent of the target process or 3943 // by the process itself to do one of two things: 3944 // * Create a new process group with the target process' ID and the target 3945 // process as group leader. 3946 // * Set the target process' process group to an already existing one in the 3947 // same session. 3948 3949 if (groupID < 0) 3950 return B_BAD_VALUE; 3951 3952 Team* currentTeam = thread_get_current_thread()->team; 3953 if (processID == 0) 3954 processID = currentTeam->id; 3955 3956 // if the group ID is not specified, use the target process' ID 3957 if (groupID == 0) 3958 groupID = processID; 3959 3960 // We loop when running into the following race condition: We create a new 3961 // process group, because there isn't one with that ID yet, but later when 3962 // trying to publish it, we find that someone else created and published 3963 // a group with that ID in the meantime. In that case we just restart the 3964 // whole action. 3965 while (true) { 3966 // Look up the process group by ID. If it doesn't exist yet and we are 3967 // allowed to create a new one, do that. 3968 ProcessGroup* group = ProcessGroup::Get(groupID); 3969 bool newGroup = false; 3970 if (group == NULL) { 3971 if (groupID != processID) 3972 return B_NOT_ALLOWED; 3973 3974 group = new(std::nothrow) ProcessGroup(groupID); 3975 if (group == NULL) 3976 return B_NO_MEMORY; 3977 3978 newGroup = true; 3979 } 3980 BReference<ProcessGroup> groupReference(group, true); 3981 3982 // get the target team 3983 Team* team = Team::Get(processID); 3984 if (team == NULL) 3985 return ESRCH; 3986 BReference<Team> teamReference(team, true); 3987 3988 // lock the new process group and the team's current process group 3989 while (true) { 3990 // lock the team's current process group 3991 team->LockProcessGroup(); 3992 3993 ProcessGroup* oldGroup = team->group; 3994 if (oldGroup == group) { 3995 // it's the same as the target group, so just bail out 3996 oldGroup->Unlock(); 3997 return group->id; 3998 } 3999 4000 oldGroup->AcquireReference(); 4001 4002 // lock the target process group, if locking order allows it 4003 if (newGroup || group->id > oldGroup->id) { 4004 group->Lock(); 4005 break; 4006 } 4007 4008 // try to lock 4009 if (group->TryLock()) 4010 break; 4011 4012 // no dice -- unlock the team's current process group and relock in 4013 // the correct order 4014 oldGroup->Unlock(); 4015 4016 group->Lock(); 4017 oldGroup->Lock(); 4018 4019 // check whether things are still the same 4020 TeamLocker teamLocker(team); 4021 if (team->group == oldGroup) 4022 break; 4023 4024 // something changed -- unlock everything and retry 4025 teamLocker.Unlock(); 4026 oldGroup->Unlock(); 4027 group->Unlock(); 4028 oldGroup->ReleaseReference(); 4029 } 4030 4031 // we now have references and locks of both new and old process group 4032 BReference<ProcessGroup> oldGroupReference(team->group, true); 4033 AutoLocker<ProcessGroup> oldGroupLocker(team->group, true); 4034 AutoLocker<ProcessGroup> groupLocker(group, true); 4035 4036 // also lock the target team and its parent 4037 team->LockTeamAndParent(false); 4038 TeamLocker parentLocker(team->parent, true); 4039 TeamLocker teamLocker(team, true); 4040 4041 // perform the checks 4042 if (team == currentTeam) { 4043 // we set our own group 4044 4045 // we must not change our process group ID if we're a session leader 4046 if (is_session_leader(currentTeam)) 4047 return B_NOT_ALLOWED; 4048 } else { 4049 // Calling team != target team. The target team must be a child of 4050 // the calling team and in the same session. (If that's the case it 4051 // isn't a session leader either.) 4052 if (team->parent != currentTeam 4053 || team->session_id != currentTeam->session_id) { 4054 return B_NOT_ALLOWED; 4055 } 4056 4057 // The call is also supposed to fail on a child, when the child has 4058 // already executed exec*() [EACCES]. 4059 if ((team->flags & TEAM_FLAG_EXEC_DONE) != 0) 4060 return EACCES; 4061 } 4062 4063 // If we created a new process group, publish it now. 4064 if (newGroup) { 4065 InterruptsSpinLocker groupHashLocker(sGroupHashLock); 4066 if (sGroupHash.Lookup(groupID)) { 4067 // A group with the group ID appeared since we first checked. 4068 // Back to square one. 4069 continue; 4070 } 4071 4072 group->PublishLocked(team->group->Session()); 4073 } else if (group->Session()->id != team->session_id) { 4074 // The existing target process group belongs to a different session. 4075 // That's not allowed. 4076 return B_NOT_ALLOWED; 4077 } 4078 4079 // Everything is ready -- set the group. 4080 remove_team_from_group(team); 4081 insert_team_into_group(group, team); 4082 4083 // Changing the process group might have changed the situation for a 4084 // parent waiting in wait_for_child(). Hence we notify it. 4085 team->parent->dead_children.condition_variable.NotifyAll(false); 4086 4087 return group->id; 4088 } 4089} 4090 4091 4092pid_t 4093_user_setsid(void) 4094{ 4095 Team* team = thread_get_current_thread()->team; 4096 4097 // create a new process group and session 4098 ProcessGroup* group = new(std::nothrow) ProcessGroup(team->id); 4099 if (group == NULL) 4100 return B_NO_MEMORY; 4101 BReference<ProcessGroup> groupReference(group, true); 4102 AutoLocker<ProcessGroup> groupLocker(group); 4103 4104 ProcessSession* session = new(std::nothrow) ProcessSession(group->id); 4105 if (session == NULL) 4106 return B_NO_MEMORY; 4107 BReference<ProcessSession> sessionReference(session, true); 4108 4109 // lock the team's current process group, parent, and the team itself 4110 team->LockTeamParentAndProcessGroup(); 4111 BReference<ProcessGroup> oldGroupReference(team->group); 4112 AutoLocker<ProcessGroup> oldGroupLocker(team->group, true); 4113 TeamLocker parentLocker(team->parent, true); 4114 TeamLocker teamLocker(team, true); 4115 4116 // the team must not already be a process group leader 4117 if (is_process_group_leader(team)) 4118 return B_NOT_ALLOWED; 4119 4120 // remove the team from the old and add it to the new process group 4121 remove_team_from_group(team); 4122 group->Publish(session); 4123 insert_team_into_group(group, team); 4124 4125 // Changing the process group might have changed the situation for a 4126 // parent waiting in wait_for_child(). Hence we notify it. 4127 team->parent->dead_children.condition_variable.NotifyAll(false); 4128 4129 return group->id; 4130} 4131 4132 4133status_t 4134_user_wait_for_team(team_id id, status_t* _userReturnCode) 4135{ 4136 status_t returnCode; 4137 status_t status; 4138 4139 if (_userReturnCode != NULL && !IS_USER_ADDRESS(_userReturnCode)) 4140 return B_BAD_ADDRESS; 4141 4142 status = wait_for_team(id, &returnCode); 4143 if (status >= B_OK && _userReturnCode != NULL) { 4144 if (user_memcpy(_userReturnCode, &returnCode, sizeof(returnCode)) 4145 != B_OK) 4146 return B_BAD_ADDRESS; 4147 return B_OK; 4148 } 4149 4150 return syscall_restart_handle_post(status); 4151} 4152 4153 4154thread_id 4155_user_load_image(const char* const* userFlatArgs, size_t flatArgsSize, 4156 int32 argCount, int32 envCount, int32 priority, uint32 flags, 4157 port_id errorPort, uint32 errorToken) 4158{ 4159 TRACE(("_user_load_image: argc = %" B_PRId32 "\n", argCount)); 4160 4161 if (argCount < 1) 4162 return B_BAD_VALUE; 4163 4164 // copy and relocate the flat arguments 4165 char** flatArgs; 4166 status_t error = copy_user_process_args(userFlatArgs, flatArgsSize, 4167 argCount, envCount, flatArgs); 4168 if (error != B_OK) 4169 return error; 4170 4171 thread_id thread = load_image_internal(flatArgs, _ALIGN(flatArgsSize), 4172 argCount, envCount, priority, B_CURRENT_TEAM, flags, errorPort, 4173 errorToken); 4174 4175 free(flatArgs); 4176 // load_image_internal() unset our variable if it took over ownership 4177 4178 return thread; 4179} 4180 4181 4182void 4183_user_exit_team(status_t returnValue) 4184{ 4185 Thread* thread = thread_get_current_thread(); 4186 Team* team = thread->team; 4187 4188 // set this thread's exit status 4189 thread->exit.status = returnValue; 4190 4191 // set the team exit status 4192 TeamLocker teamLocker(team); 4193 4194 if (!team->exit.initialized) { 4195 team->exit.reason = CLD_EXITED; 4196 team->exit.signal = 0; 4197 team->exit.signaling_user = 0; 4198 team->exit.status = returnValue; 4199 team->exit.initialized = true; 4200 } 4201 4202 teamLocker.Unlock(); 4203 4204 // Stop the thread, if the team is being debugged and that has been 4205 // requested. 4206 if ((atomic_get(&team->debug_info.flags) & B_TEAM_DEBUG_PREVENT_EXIT) != 0) 4207 user_debug_stop_thread(); 4208 4209 // Send this thread a SIGKILL. This makes sure the thread will not return to 4210 // userland. The signal handling code forwards the signal to the main 4211 // thread (if that's not already this one), which will take the team down. 4212 Signal signal(SIGKILL, SI_USER, B_OK, team->id); 4213 send_signal_to_thread(thread, signal, 0); 4214} 4215 4216 4217status_t 4218_user_kill_team(team_id team) 4219{ 4220 return kill_team(team); 4221} 4222 4223 4224status_t 4225_user_get_team_info(team_id id, team_info* userInfo) 4226{ 4227 status_t status; 4228 team_info info; 4229 4230 if (!IS_USER_ADDRESS(userInfo)) 4231 return B_BAD_ADDRESS; 4232 4233 status = _get_team_info(id, &info, sizeof(team_info)); 4234 if (status == B_OK) { 4235 if (user_memcpy(userInfo, &info, sizeof(team_info)) < B_OK) 4236 return B_BAD_ADDRESS; 4237 } 4238 4239 return status; 4240} 4241 4242 4243status_t 4244_user_get_next_team_info(int32* userCookie, team_info* userInfo) 4245{ 4246 status_t status; 4247 team_info info; 4248 int32 cookie; 4249 4250 if (!IS_USER_ADDRESS(userCookie) 4251 || !IS_USER_ADDRESS(userInfo) 4252 || user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK) 4253 return B_BAD_ADDRESS; 4254 4255 status = _get_next_team_info(&cookie, &info, sizeof(team_info)); 4256 if (status != B_OK) 4257 return status; 4258 4259 if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK 4260 || user_memcpy(userInfo, &info, sizeof(team_info)) < B_OK) 4261 return B_BAD_ADDRESS; 4262 4263 return status; 4264} 4265 4266 4267team_id 4268_user_get_current_team(void) 4269{ 4270 return team_get_current_team_id(); 4271} 4272 4273 4274status_t 4275_user_get_team_usage_info(team_id team, int32 who, team_usage_info* userInfo, 4276 size_t size) 4277{ 4278 if (size != sizeof(team_usage_info)) 4279 return B_BAD_VALUE; 4280 4281 team_usage_info info; 4282 status_t status = common_get_team_usage_info(team, who, &info, 4283 B_CHECK_PERMISSION); 4284 4285 if (userInfo == NULL || !IS_USER_ADDRESS(userInfo) 4286 || user_memcpy(userInfo, &info, size) != B_OK) { 4287 return B_BAD_ADDRESS; 4288 } 4289 4290 return status; 4291} 4292 4293 4294status_t 4295_user_get_extended_team_info(team_id teamID, uint32 flags, void* buffer, 4296 size_t size, size_t* _sizeNeeded) 4297{ 4298 // check parameters 4299 if ((buffer != NULL && !IS_USER_ADDRESS(buffer)) 4300 || (buffer == NULL && size > 0) 4301 || _sizeNeeded == NULL || !IS_USER_ADDRESS(_sizeNeeded)) { 4302 return B_BAD_ADDRESS; 4303 } 4304 4305 KMessage info; 4306 4307 if ((flags & B_TEAM_INFO_BASIC) != 0) { 4308 // allocate memory for a copy of the needed team data 4309 struct ExtendedTeamData { 4310 team_id id; 4311 pid_t group_id; 4312 pid_t session_id; 4313 uid_t real_uid; 4314 gid_t real_gid; 4315 uid_t effective_uid; 4316 gid_t effective_gid; 4317 char name[B_OS_NAME_LENGTH]; 4318 }; 4319 4320 ExtendedTeamData* teamClone 4321 = (ExtendedTeamData*)malloc(sizeof(ExtendedTeamData)); 4322 // It would be nicer to use new, but then we'd have to use 4323 // ObjectDeleter and declare the structure outside of the function 4324 // due to template parameter restrictions. 4325 if (teamClone == NULL) 4326 return B_NO_MEMORY; 4327 MemoryDeleter teamCloneDeleter(teamClone); 4328 4329 io_context* ioContext; 4330 { 4331 // get the team structure 4332 Team* team = Team::GetAndLock(teamID); 4333 if (team == NULL) 4334 return B_BAD_TEAM_ID; 4335 BReference<Team> teamReference(team, true); 4336 TeamLocker teamLocker(team, true); 4337 4338 // copy the data 4339 teamClone->id = team->id; 4340 strlcpy(teamClone->name, team->Name(), sizeof(teamClone->name)); 4341 teamClone->group_id = team->group_id; 4342 teamClone->session_id = team->session_id; 4343 teamClone->real_uid = team->real_uid; 4344 teamClone->real_gid = team->real_gid; 4345 teamClone->effective_uid = team->effective_uid; 4346 teamClone->effective_gid = team->effective_gid; 4347 4348 // also fetch a reference to the I/O context 4349 ioContext = team->io_context; 4350 vfs_get_io_context(ioContext); 4351 } 4352 CObjectDeleter<io_context> ioContextPutter(ioContext, 4353 &vfs_put_io_context); 4354 4355 // add the basic data to the info message 4356 if (info.AddInt32("id", teamClone->id) != B_OK 4357 || info.AddString("name", teamClone->name) != B_OK 4358 || info.AddInt32("process group", teamClone->group_id) != B_OK 4359 || info.AddInt32("session", teamClone->session_id) != B_OK 4360 || info.AddInt32("uid", teamClone->real_uid) != B_OK 4361 || info.AddInt32("gid", teamClone->real_gid) != B_OK 4362 || info.AddInt32("euid", teamClone->effective_uid) != B_OK 4363 || info.AddInt32("egid", teamClone->effective_gid) != B_OK) { 4364 return B_NO_MEMORY; 4365 } 4366 4367 // get the current working directory from the I/O context 4368 dev_t cwdDevice; 4369 ino_t cwdDirectory; 4370 { 4371 MutexLocker ioContextLocker(ioContext->io_mutex); 4372 vfs_vnode_to_node_ref(ioContext->cwd, &cwdDevice, &cwdDirectory); 4373 } 4374 4375 if (info.AddInt32("cwd device", cwdDevice) != B_OK 4376 || info.AddInt64("cwd directory", cwdDirectory) != B_OK) { 4377 return B_NO_MEMORY; 4378 } 4379 } 4380 4381 // TODO: Support the other flags! 4382 4383 // copy the needed size and, if it fits, the message back to userland 4384 size_t sizeNeeded = info.ContentSize(); 4385 if (user_memcpy(_sizeNeeded, &sizeNeeded, sizeof(sizeNeeded)) != B_OK) 4386 return B_BAD_ADDRESS; 4387 4388 if (sizeNeeded > size) 4389 return B_BUFFER_OVERFLOW; 4390 4391 if (user_memcpy(buffer, info.Buffer(), sizeNeeded) != B_OK) 4392 return B_BAD_ADDRESS; 4393 4394 return B_OK; 4395} 4396