36 using namespace basis;
47 #define DEBUG_PROCESS_MANAGER
50 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
72 #define LOCK_CONFIG auto_synchronizer l(*_config_lock)
73 #define LOCK_ZOMBIES auto_synchronizer l(*_zombie_lock)
74 #define LOCK_KIDS auto_synchronizer l(*_scamp_lock)
77 #ifdef DEBUG_PROCESS_MANAGER
78 #define COMPLAIN_APPLICATION \
79 LOG(astring("the application called ") + app_name + " could not be found.")
80 #define COMPLAIN_PRODUCT \
81 LOG(astring("the section for ") + product + " could not be found.")
83 #define COMPLAIN_APPLICATION {}
84 #define COMPLAIN_PRODUCT {}
89 class launch_manager_thread :
public ethread
92 launch_manager_thread(launch_manager &parent)
95 virtual ~launch_manager_thread() {}
97 virtual void perform_activity(
void *)
98 { _parent.push_timed_activities(_processes); }
101 launch_manager &_parent;
107 class graceful_record
116 graceful_record(
int pid = 0,
const astring &product =
"",
117 const astring &app_name =
"",
int level = 0)
118 : _product(product), _app_name(app_name), _pid(pid), _level(level) {}
121 class graceful_array :
public array<graceful_record> {};
127 _started_initial_apps(false),
128 _checker(new launch_manager_thread(*this)),
129 _config_lock(new
mutex),
130 _going_down(new graceful_array),
131 _zombie_lock(new
mutex),
132 _our_kids(new graceful_array),
133 _scamp_lock(new
mutex),
134 _stop_launching(false),
145 _checker->reschedule(200);
159 WHACK(_startup_time);
161 WHACK(_gag_exclusions);
162 WHACK(_tracking_exclusions);
163 LOG(
"launch_manager is now stopped.");
167 { *_gag_exclusions += exclusion; }
170 { *_tracking_exclusions += exclusion; }
174 switch (to_name.
value()) {
175 case FILE_NOT_FOUND:
return "FILE_NOT_FOUND";
176 case NO_ANCHOR:
return "NO_ANCHOR";
177 case NO_PRODUCT:
return "NO_PRODUCT";
178 case NO_APPLICATION:
return "NO_APPLICATION";
179 case BAD_PROGRAM:
return "BAD_PROGRAM";
180 case LAUNCH_FAILED:
return "LAUNCH_FAILED";
181 case NOT_RUNNING:
return "NOT_RUNNING";
182 case FROZEN:
return "FROZEN";
183 default:
return common::outcome_name(to_name);
189 _stop_launching =
true;
194 void launch_manager::stop_all_kids()
197 _stop_launching =
true;
198 LOG(
"zapping any active sub-processes prior to exit.");
204 for (
int lev = 100; lev >= 0; lev--) {
206 #ifdef DEBUG_PROCESS_MANAGER
209 bool zapped_any =
false;
213 for (
int i = _our_kids->length() - 1; i >= 0; i--) {
215 graceful_record &grace = (*_our_kids)[i];
216 if (lev == grace._level) {
218 zap_process(grace._product, grace._app_name,
true);
220 _our_kids->zap(i, i);
227 #ifdef DEBUG_PROCESS_MANAGER
232 #ifdef DEBUG_PROCESS_MANAGER
245 num_dying = _going_down->length();
248 if (!num_dying)
break;
250 _checker->reschedule(0);
252 time_control::sleep_ms(40);
254 #ifdef DEBUG_PROCESS_MANAGER
255 LOG(
"done waiting...");
260 void launch_manager::launch_startup_apps()
262 FUNCDEF(
"launch_startup_apps");
270 LOG(
"the startup section was not found!");
274 #ifdef DEBUG_PROCESS_MANAGER
278 for (
int i = 0; i < startup_info.
symbols(); i++) {
280 if (app.
equal_to(configured_applications::STARTUP_APP_NAME()))
continue;
282 astring info = startup_info[i];
283 LOG(
astring(
"launching application ") + app +
"...");
287 if (!configured_applications::parse_startup_entry(info, product, parms,
289 LOG(
"the startup entry was not malformed; we could not parse it!");
293 LOG(app +
a_sprintf(
" is %ssingle shot.", one_shot?
"" :
"not "));
309 LOG(
astring(
"product \"") + product +
"\", application \"" + app_name
311 :
astring(
"\", parms=") + parameters));
313 if (_stop_launching) {
316 if (_gag_exclusions->
member(app_name)) {
319 LOG(
astring(
"application \"") + app_name +
"\" cannot be launched;"
320 + parser_bits::platform_eol_to_chars() +
"launching services have been "
322 return LAUNCH_FAILED;
332 entry_found = _configs.
find_program(product, app_name, level);
340 return NO_APPLICATION;
344 filename existence_check(entry_found);
345 if (!existence_check.
exists()) {
346 LOG(
astring(
"file or path wasn't found for ") + entry_found +
".");
347 return FILE_NOT_FOUND;
351 int ret = launch_process::run(entry_found, parameters,
352 launch_process::RETURN_IMMEDIATELY | launch_process::HIDE_APP_WINDOW, kid);
356 if (_tracking_exclusions->
member(app_name)) {
365 LOG(
a_sprintf(
"adding given process id %d for app %s at level %d.",
366 kid, app_name.
s(), level));
367 graceful_record to_add(kid, product, app_name, level);
368 *_our_kids += to_add;
371 #ifdef DEBUG_PROCESS_MANAGER
372 LOG(
"was not told child process id!!!");
379 if (find_process(app_name, pids))
break;
380 time_control::sleep_ms(10);
388 LOG(
astring(
"no process found for product \"") + product
389 +
"\", application \"" + app_name +
"\"; not adding "
397 for (
int i = 0; i < pids.
elements(); i++) {
398 LOG(
a_sprintf(
"adding process id %d for app %s at level %d.",
399 pids[i], app_name.
s(), level));
400 graceful_record to_add(pids[i], product, app_name, level);
401 *_our_kids += to_add;
410 if (ret == NO_ERROR)
return OKAY;
411 else if ( (ret == ERROR_FILE_NOT_FOUND) || (ret == ERROR_PATH_NOT_FOUND) ) {
412 LOG(
astring(
"file or path wasn't found for ") + app_name +
".");
413 return FILE_NOT_FOUND;
414 }
else if (ret == ERROR_BAD_FORMAT) {
415 LOG(
astring(app_name) +
" was not in EXE format.");
418 LOG(
astring(
"there was an unknown error while trying to run ")
419 + app_name +
"; the error is:" + parser_bits::platform_eol_to_chars()
420 + critical_events::system_error_text(ret));
421 return LAUNCH_FAILED;
424 LOG(
astring(
"error ") + critical_events::system_error_text(ret)
425 +
" occurred attempting to run: " + app_name +
" " + parameters);
426 return FILE_NOT_FOUND;
436 #ifdef DEBUG_PROCESS_MANAGER
437 LOG(
astring(
"product \"") + product +
"\", application \"" + app_name
451 return NO_APPLICATION;
465 FUNCDEF(
"remove_from_startup");
468 #ifdef DEBUG_PROCESS_MANAGER
469 LOG(
astring(
"product \"") + product +
"\", application \"" + app_name +
"\"");
482 return NO_APPLICATION;
488 return NO_APPLICATION;
498 #ifdef DEBUG_PROCESS_MANAGER
499 LOG(
astring(
"product \"") + product +
"\", application \"" + app_name +
"\"");
514 return NO_APPLICATION;
519 astring program_name(app_name);
523 if (!find_process(app_name, pids))
528 outcome launch_manager::start_graceful_close(
const astring &product,
531 FUNCDEF(
"start_graceful_close");
538 for (
int i = _going_down->length() - 1; i >= 0; i--) {
539 graceful_record &grace = (*_going_down)[i];
540 if (grace._app_name.iequals(app_name)) {
550 if (!find_process(app_name, pids)) {
551 LOG(
astring(
"Failed to find process id for [") + app_name
552 +
astring(
"], assuming it has already exited."));
560 for (
int i = 0; i < pids.
elements(); i++) {
561 graceful_record to_add(pids[i], product, app_name);
562 *_going_down += to_add;
573 LOG(
"failed to query processes!");
579 bool launch_manager::find_process(
const astring &app_name_in,
int_set &pids)
584 if (!get_processes(
processes))
return false;
585 return process_control::find_process_in_list(
processes, app_name_in, pids);
589 const astring &app_name_key,
bool graceful)
593 #ifdef DEBUG_PROCESS_MANAGER
594 LOG(
astring(
"product \"") + product +
"\", application \"" + app_name_key
595 + (graceful?
"\", Graceful Close" :
"\", Brutal Close"));
598 if (_tracking_exclusions->
member(app_name_key)) {
611 app_name = _configs.
find_program(product, app_name_key, level);
619 return NO_APPLICATION;
628 outcome to_return = NOT_RUNNING;
630 to_return = start_graceful_close(product, app_name);
631 if (to_return ==
OKAY)
635 astring program_name(app_name);
637 if (!find_process(program_name, pids)) {
638 #ifdef DEBUG_PROCESS_MANAGER
639 LOG(program_name +
" process was not running.")
646 for (
int i = 0; i < pids.
elements(); i++) {
649 LOG(
astring(astring::SPRINTF,
"Killed process %d [",
650 pids[i]) + program_name +
astring(
"]"));
652 LOG(
astring(astring::SPRINTF,
"Failed to zap process %d [",
653 pids[i]) + program_name +
astring(
"]"));
655 if (!ret) failed =
true;
664 FUNCDEF(
"shut_down_launching_services");
665 LOG(
"checking secret word...");
667 if (secret_word.
equal_to(
"MarblesAreRoundishYo")) {
668 LOG(
"it's correct; ending launch capabilities.");
670 LOG(
"the secret word is wrong. continuing normal operation.");
673 _stop_launching =
true;
679 FUNCDEF(
"reenable_launching_services");
680 LOG(
"checking secret word...");
681 if (secret_word.
equal_to(
"MarblesAreRoundishYo")) {
682 LOG(
"it's correct; resuming launch capabilities.");
684 LOG(
"the secret word is wrong. continuing with prior mode.");
687 _stop_launching =
false;
691 #define GET_PROCESSES \
692 if (!retrieved_processes) { \
693 retrieved_processes = true; \
694 if (!get_processes(processes)) { \
695 LOG("failed to retrieve process list from OS!"); \
702 FUNCDEF(
"push_timed_activities");
708 if (!_started_initial_apps && (*_startup_time <=
time_stamp()) ) {
710 LOG(
"starting up the tasks registered for system initiation.");
711 launch_startup_apps();
712 _started_initial_apps =
true;
715 if (!_started_initial_apps) {
716 _checker->reschedule(200);
721 bool retrieved_processes =
false;
727 for (
int i = _going_down->length() - 1; i >= 0; i--) {
728 graceful_record &grace = (*_going_down)[i];
733 if (!process_control::find_process_in_list(
processes, grace._app_name,
736 #ifdef DEBUG_PROCESS_MANAGER
737 LOG(
astring(
"cannot find app ") + grace._app_name
738 +
" as running still; removing its dying record");
740 _going_down->zap(i, i);
743 if (!pids.
member(grace._pid)) {
745 #ifdef DEBUG_PROCESS_MANAGER
747 +
" exited on its own; removing its dying record");
749 _going_down->zap(i, i);
755 LOG(
astring(
"Application ") + grace._app_name +
" is unresponsive to "
756 "the graceful shutdown request. Now zapping it instead.");
758 LOG(
"Devolved graceful shutdown failed as zap_process also.");
759 _going_down->zap(i, i);
772 for (
int i = _our_kids->length() - 1; i >= 0; i--) {
773 graceful_record &grace = (*_our_kids)[i];
778 if (!process_control::find_process_in_list(
processes, grace._app_name,
781 #ifdef DEBUG_PROCESS_MANAGER
782 LOG(
astring(
"cannot find kid ") + grace._app_name
783 +
" as still running; removing its normal record");
785 _our_kids->zap(i, i);
788 if (!pids.
member(grace._pid)) {
790 #ifdef DEBUG_PROCESS_MANAGER
792 +
" exited on its own; removing its normal record");
794 _our_kids->zap(i, i);
static bool close_application(const basis::astring &app_name)
attempts to close the application named "app_name".
basis::outcome launch_now(const basis::astring &product, const basis::astring &app_name, const basis::astring ¶meters)
starts the application "app_name" now.
basis::outcome query_application(const basis::astring &product, const basis::astring &app_name)
retrieves the current state of the program with "app_name".
basis::outcome shut_down_launching_services(const basis::astring &secret_word)
closes down the ability of clients to launch applications.
void add_gag_exclusion(const basis::astring &exclusion)
add an application that isn't subject to gagging.
static const char * outcome_name(const basis::outcome &to_name)
returns the text associated with "to_name".
virtual ~launch_manager()
void stop_everything()
closes down the operation of this object.
basis::outcome launch_at_startup(const basis::astring &product, const basis::astring &app_name, const basis::astring ¶meters, int one_shot)
records an entry for the "app_name" to be launched at startup.
basis::outcome zap_process(const basis::astring &product, const basis::astring &app_name, bool graceful)
zaps the process named "app_name".
@ EXISTING
the entry already exists and overwriting is disallowed.
@ ACCESS_DENIED
the requested operation was not permitted.
void push_timed_activities(processes::process_entry_array &processes)
keeps any periodic activities going.
void add_tracking_exclusion(const basis::astring &exclusion)
apps that aren't tracked when running.
basis::outcome reenable_launching_services(const basis::astring &secret_word)
undoes the gagging that the above "shut_down" function does.
basis::outcome remove_from_startup(const basis::astring &product, const basis::astring &app_name)
takes the "app_name" out of the startup list.
a_sprintf is a specialization of astring that provides printf style support.
Represents a sequential, ordered, contiguous collection of objects.
Provides a dynamically resizable ASCII character string.
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
bool equal_to(const char *that) const
returns true if "that" is equal to this.
int length() const
Returns the current length of the string.
void to_lower()
to_lower modifies "this" by replacing capitals with lower-case.
Outcomes describe the state of completion for an operation.
Provides operations commonly needed on file names.
bool exists() const
returns true if the file exists.
filename basename() const
returns the base of the filename; no directory.
Provides a platform-independent object for adding threads to a program.
Provides a bridge to the operating system for information on processes.
bool zap_process(basis::un_int to_zap)
preemptively zaps the process "to_zap".
bool query_processes(process_entry_array &to_fill)
finds the processes that are running and drops them into "to_fill".
a handy class that implements an array of process entries.
A simple object that wraps a templated set of ints.
int elements() const
Returns the number of elements in this set.
bool member(const contents &to_test) const
Returns true if the item "to_test" is a member of this set.
void clear()
Empties out this set.
A simple object that wraps a templated set of strings.
Provides a symbol_table that holds strings as the content.
basis::astring text_form() const
prints the contents of the table into the returned string.
const basis::astring & name(int index) const
returns the name held at the "index".
int symbols() const
returns the number of symbols listed in the table.
Represents a point in time relative to the operating system startup time.
#define NULL_POINTER
The value representing a pointer to nothing.
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
#define COMPLAIN_APPLICATION
Implements an application lock to ensure only one is running at once.
const int MAXIMUM_REQUEST_PAUSE
const int MAXIMUM_INITIAL_APP_WAIT
const int STARTUP_APPS_DELAY_PERIOD
The guards collection helps in testing preconditions and reporting errors.
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
const int SECOND_ms
Number of milliseconds in a second.
unsigned int un_int
Abbreviated name for unsigned integers.
A platform independent way to obtain the timestamp of a file.
A logger that sends to the console screen using the standard output device.
A dynamic container class that holds any kind of object via pointers.
Aids in achievement of platform independence.