status_t TRoster::AddAppInfo(AppInfoList& apps, team_id team) { BAutolock _(fLock); for (AppInfoList::Iterator it(fRegisteredApps.It()); RosterAppInfo* info = *it; ++it) { if (info->team == team) { RosterAppInfo* clonedInfo = info->Clone(); status_t error = B_NO_MEMORY; if (clonedInfo != NULL) { if (!apps.AddInfo(clonedInfo)) delete clonedInfo; else error = B_OK; } return error; } } return B_BAD_TEAM_ID; }
/*! \brief Checks whether the (pre-)registered applications are still running. This is necessary, since killed applications don't unregister properly. */ void TRoster::CheckSanity() { BAutolock _(fLock); // not early (pre-)registered applications AppInfoList obsoleteApps; for (AppInfoList::Iterator it = fRegisteredApps.It(); it.IsValid(); ++it) { if (!(*it)->IsRunning()) obsoleteApps.AddInfo(*it); } // remove the apps for (AppInfoList::Iterator it = obsoleteApps.It(); it.IsValid(); ++it) { RemoveApp(*it); delete *it; } obsoleteApps.MakeEmpty(false); // don't delete infos a second time // early pre-registered applications bigtime_t timeLimit = system_time() - kMaximalEarlyPreRegistrationPeriod; for (AppInfoList::Iterator it = fEarlyPreRegisteredApps.It(); it.IsValid(); ++it) { if ((*it)->registration_time < timeLimit) obsoleteApps.AddInfo(*it); } // remove the apps for (AppInfoList::Iterator it = obsoleteApps.It(); it.IsValid(); ++it) { fEarlyPreRegisteredApps.RemoveInfo(*it); delete *it; } obsoleteApps.MakeEmpty(false); // don't delete infos a second time }
void ShutdownProcess::_AddShutdownWindowApps(AppInfoList& infos) { if (!fHasGUI) return; for (AppInfoList::Iterator it = infos.It(); it.IsValid(); ++it) { RosterAppInfo* info = *it; // init an app file info BFile file; status_t error = file.SetTo(&info->ref, B_READ_ONLY); if (error != B_OK) { WARNING(("ShutdownProcess::_AddShutdownWindowApps(): Failed to " "open file for app %s: %s\n", info->signature, strerror(error))); continue; } BAppFileInfo appFileInfo; error = appFileInfo.SetTo(&file); if (error != B_OK) { WARNING(("ShutdownProcess::_AddShutdownWindowApps(): Failed to " "init app file info for app %s: %s\n", info->signature, strerror(error))); } // get the application icons #ifdef __HAIKU__ color_space format = B_RGBA32; #else color_space format = B_CMAP8; #endif // mini icon BBitmap* miniIcon = new(nothrow) BBitmap(BRect(0, 0, 15, 15), format); if (miniIcon != NULL) { error = miniIcon->InitCheck(); if (error == B_OK) error = appFileInfo.GetTrackerIcon(miniIcon, B_MINI_ICON); if (error != B_OK) { delete miniIcon; miniIcon = NULL; } } // mini icon BBitmap* largeIcon = new(nothrow) BBitmap(BRect(0, 0, 31, 31), format); if (largeIcon != NULL) { error = largeIcon->InitCheck(); if (error == B_OK) error = appFileInfo.GetTrackerIcon(largeIcon, B_LARGE_ICON); if (error != B_OK) { delete largeIcon; largeIcon = NULL; } } // add the app error = fWindow->AddApp(info->team, miniIcon, largeIcon); if (error != B_OK) { WARNING(("ShutdownProcess::_AddShutdownWindowApps(): Failed to " "add app to the shutdown window: %s\n", strerror(error))); } } }
void ShutdownProcess::_QuitBlockingApp(AppInfoList& list, team_id team, const char* appName, bool cancelAllowed) { bool debugged = false; bool modal = false; { BAutolock _(fWorkerLock); if (fDebuggedTeams.find(team) != fDebuggedTeams.end()) debugged = true; } if (!debugged) modal = BPrivate::is_app_showing_modal_window(team); if (modal) { // app blocks on a modal window BString buffer = B_TRANSLATE("The application \"%appName%\" might be " "blocked on a modal panel."); buffer.ReplaceFirst("%appName%", appName); _SetShutdownWindowText(buffer.String()); _SetShutdownWindowCurrentApp(team); _SetShutdownWindowKillButtonEnabled(true); } if (modal || debugged) { // wait for something to happen bool appGone = false; while (true) { uint32 event; team_id eventTeam; int32 phase; status_t error = _GetNextEvent(event, eventTeam, phase, true); if (error != B_OK) throw_error(error); if ((event == APP_QUIT_EVENT) && eventTeam == team) { appGone = true; break; } if (event == KILL_APP_EVENT && eventTeam == team) break; if (event == ABORT_EVENT) { if (cancelAllowed || debugged) { PRINT(("ShutdownProcess::_QuitBlockingApp(): shutdown " "cancelled by team %ld (-1 => user)\n", eventTeam)); if (!debugged) _DisplayAbortingApp(eventTeam); throw_error(B_SHUTDOWN_CANCELLED); } // If the app requests aborting the shutdown, we don't need // to wait any longer. It has processed the request and // won't quit by itself. We'll have to kill it. if (eventTeam == team) break; } } _SetShutdownWindowKillButtonEnabled(false); if (appGone) return; } // kill the app PRINT((" killing team %ld\n", team)); kill_team(team); // remove the app (the roster will note eventually and send us // a notification, but we want to be sure) { BAutolock _(fWorkerLock); if (RosterAppInfo* info = list.InfoFor(team)) { list.RemoveInfo(info); delete info; } } }
void ShutdownProcess::_QuitApps(AppInfoList& list, bool systemApps) { PRINT(("ShutdownProcess::_QuitApps(%s)\n", (systemApps ? "system" : "user"))); if (systemApps) { _SetShutdownWindowCancelButtonEnabled(false); // check one last time for abort events uint32 event; do { team_id team; int32 phase; status_t error = _GetNextEvent(event, team, phase, false); if (error != B_OK) throw_error(error); if (event == ABORT_EVENT) { PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled by " "team %ld (-1 => user)\n", team)); _DisplayAbortingApp(team); throw_error(B_SHUTDOWN_CANCELLED); } } while (event != NO_EVENT); } // prepare the shutdown message BMessage message; _PrepareShutdownMessage(message); // now iterate through the list of apps while (true) { // eat events uint32 event; do { team_id team; int32 phase; status_t error = _GetNextEvent(event, team, phase, false); if (error != B_OK) throw_error(error); if (!systemApps && event == ABORT_EVENT) { PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled by " "team %ld (-1 => user)\n", team)); _DisplayAbortingApp(team); throw_error(B_SHUTDOWN_CANCELLED); } } while (event != NO_EVENT); // get the first app to quit team_id team = -1; port_id port = -1; char appName[B_FILE_NAME_LENGTH]; { BAutolock _(fWorkerLock); while (!list.IsEmpty()) { RosterAppInfo* info = *list.It(); team = info->team; port = info->port; strcpy(appName, info->ref.name); if (info->IsRunning()) break; list.RemoveInfo(info); delete info; } } if (team < 0) { PRINT(("ShutdownProcess::_QuitApps() done\n")); return; } // set window text BString buffer = B_TRANSLATE("Asking \"%appName%\" to quit."); buffer.ReplaceFirst("%appName%", appName); _SetShutdownWindowText(buffer.String()); _SetShutdownWindowCurrentApp(team); // send the shutdown message to the app PRINT((" sending team %ld (port: %ld) a shutdown message\n", team, port)); SingleMessagingTargetSet target(port, B_PREFERRED_TOKEN); MessageDeliverer::Default()->DeliverMessage(&message, target); // schedule a timeout event _ScheduleTimeoutEvent(kAppQuitTimeout, team); // wait for the app to die or for the timeout to occur bool appGone = _WaitForApp(team, &list, systemApps); if (appGone) { // fine: the app finished in an orderly manner } else { // the app is either blocking on a model alert or blocks for another // reason if (!systemApps) _QuitBlockingApp(list, team, appName, true); else { // This is a system app: remove it from the list BAutolock _(fWorkerLock); if (RosterAppInfo* info = list.InfoFor(team)) { list.RemoveInfo(info); delete info; } } } } }
/*! \brief Returns lists of applications to be asked to quit on shutdown. \param userApps List of RosterAppInfos identifying the user applications. Those will be ask to quit first. \param systemApps List of RosterAppInfos identifying the system applications (like Tracker and Deskbar), which will be asked to quit after the user applications are gone. \param vitalSystemApps A set of team_ids identifying teams that must not be terminated (app server and registrar). \return \c B_OK, if everything went fine, another error code otherwise. */ status_t TRoster::GetShutdownApps(AppInfoList& userApps, AppInfoList& systemApps, AppInfoList& backgroundApps, hash_set<team_id>& vitalSystemApps) { BAutolock _(fLock); status_t error = B_OK; // get the vital system apps: // * ourself // * kernel team // * app server // * debug server // ourself vitalSystemApps.insert(be_app->Team()); // kernel team team_info teamInfo; if (get_team_info(B_SYSTEM_TEAM, &teamInfo) == B_OK) vitalSystemApps.insert(teamInfo.team); // app server RosterAppInfo* info = fRegisteredApps.InfoFor("application/x-vnd.haiku-app_server"); if (info != NULL) vitalSystemApps.insert(info->team); // debug server info = fRegisteredApps.InfoFor("application/x-vnd.haiku-debug_server"); if (info != NULL) vitalSystemApps.insert(info->team); // populate the other groups for (AppInfoList::Iterator it(fRegisteredApps.It()); RosterAppInfo* info = *it; ++it) { if (vitalSystemApps.find(info->team) == vitalSystemApps.end()) { RosterAppInfo* clonedInfo = info->Clone(); if (clonedInfo) { if (_IsSystemApp(info)) { if (!systemApps.AddInfo(clonedInfo)) error = B_NO_MEMORY; } else if (info->flags & B_BACKGROUND_APP) { if (!backgroundApps.AddInfo(clonedInfo)) error = B_NO_MEMORY; } else { if (!userApps.AddInfo(clonedInfo)) error = B_NO_MEMORY; } if (error != B_OK) delete clonedInfo; } else error = B_NO_MEMORY; } if (error != B_OK) break; } // Special case, we add the input server to vital apps here so it is // not excluded in the lists above info = fRegisteredApps.InfoFor("application/x-vnd.Be-input_server"); if (info != NULL) vitalSystemApps.insert(info->team); // clean up on error if (error != B_OK) { userApps.MakeEmpty(true); systemApps.MakeEmpty(true); } return error; }