00001 #ifndef PROCESS_MANAGER_IMPLEMENTATION_FILE
00002 #define PROCESS_MANAGER_IMPLEMENTATION_FILE
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "anchor_window.h"
00019 #include "application_config.h"
00020 #include "process_control.h"
00021 #include "process_entry.h"
00022 #include "process_manager.h"
00023
00024 #include <basis/auto_synch.h>
00025 #include <basis/istring.h>
00026 #include <basis/log_base.h>
00027 #include <basis/mutex.h>
00028 #include <basis/portable.h>
00029 #include <basis/set.cpp>
00030 #include <basis/version_record.h>
00031 #include <data_struct/configurator.h>
00032 #include <data_struct/section_manager.h>
00033 #include <data_struct/string_table.h>
00034 #include <data_struct/unique_id.h>
00035 #include <mechanisms/ithread.h>
00036 #include <mechanisms/time_stamp.h>
00037 #include <opsystem/filename.h>
00038
00039 #define DEBUG_PROCESS_MANAGER
00040
00041
00042 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger(), s)
00043
00044 const int CHECK_INTERVAL = 4 * SECOND_ms;
00045
00046
00047
00048 const int GRACEFUL_SLACK = 90 * SECOND_ms;
00049
00050
00051
00052 const int MAXIMUM_INITIAL_APP_WAIT = 4 * SECOND_ms;
00053
00054
00055
00056 const int STARTUP_APPS_DELAY_PERIOD = 2 * SECOND_ms;
00057
00058
00059 const int MAXIMUM_REQUEST_PAUSE = 42 * SECOND_ms;
00060
00061
00062
00063
00064 #define LOCK_CONFIG auto_synchronizer l(*_config_lock)
00065 #define LOCK_ZOMBIES auto_synchronizer l(*_zombie_lock)
00066 #define LOCK_KIDS auto_synchronizer l(*_scamp_lock)
00067
00068
00069 #ifdef DEBUG_PROCESS_MANAGER
00070 #define COMPLAIN_APPLICATION \
00071 LOG(istring("the application called ") + app_name + " could not be found.")
00072 #define COMPLAIN_PRODUCT \
00073 LOG(istring("the section for ") + product + " could not be found.")
00074 #else
00075 #define COMPLAIN_APPLICATION {}
00076 #define COMPLAIN_PRODUCT {}
00077 #endif
00078
00080
00081 class process_manager_thread : public ithread
00082 {
00083 public:
00084 process_manager_thread(process_manager &parent)
00085 : ithread(CHECK_INTERVAL, ithread::SLACK_INTERVAL),
00086 _parent(parent) {}
00087 virtual ~process_manager_thread() {}
00088
00089 virtual void perform_activity(void *)
00090 { _parent.push_timed_activities(_processes); }
00091
00092 private:
00093 process_manager &_parent;
00094 process_entry_array _processes;
00095 };
00096
00098
00099 class graceful_record
00100 {
00101 public:
00102 istring _product;
00103 istring _app_name;
00104 time_stamp _started;
00105 int _pid;
00106 int _level;
00107
00108 graceful_record(int pid = 0, const istring &product = "",
00109 const istring &app_name = "", int level = 0)
00110 : _product(product), _app_name(app_name), _pid(pid), _level(level) {}
00111 };
00112
00113 class graceful_array : public array<graceful_record> {};
00114
00116
00117 process_manager::process_manager(application_config &config)
00118 : _configs(config),
00119 _started_initial_apps(false),
00120 _checker(new process_manager_thread(*this)),
00121 _config_lock(new mutex),
00122 _going_down(new graceful_array),
00123 _zombie_lock(new mutex),
00124 _our_kids(new graceful_array),
00125 _scamp_lock(new mutex),
00126 _stop_launching(false),
00127 _startup_time(new time_stamp(STARTUP_APPS_DELAY_PERIOD)),
00128 _procs(new process_control),
00129 _gag_exclusions(new string_set),
00130 _tracking_exclusions(new string_set)
00131 {
00132 FUNCDEF("constructor");
00133
00134
00135 _checker->start(NIL);
00136
00137 _checker->reschedule(200);
00138 }
00139
00140 process_manager::~process_manager()
00141 {
00142 FUNCDEF("destructor");
00143 stop_everything();
00144
00145 WHACK(_checker);
00146 WHACK(_going_down);
00147 WHACK(_our_kids);
00148 WHACK(_scamp_lock);
00149 WHACK(_zombie_lock);
00150 WHACK(_config_lock);
00151 WHACK(_startup_time);
00152 WHACK(_procs);
00153 WHACK(_gag_exclusions);
00154 WHACK(_tracking_exclusions);
00155 LOG("process_manager is now stopped.");
00156 }
00157
00158 void process_manager::add_gag_exclusion(const istring &exclusion)
00159 { *_gag_exclusions += exclusion; }
00160
00161 void process_manager::add_tracking_exclusion(const istring &exclusion)
00162 { *_tracking_exclusions += exclusion; }
00163
00164 const char *process_manager::outcome_name(const outcome &to_name)
00165 {
00166 switch (to_name.value()) {
00167 case FILE_NOT_FOUND: return "FILE_NOT_FOUND";
00168 case NO_ANCHOR: return "NO_ANCHOR";
00169 case NO_PRODUCT: return "NO_PRODUCT";
00170 case NO_APPLICATION: return "NO_APPLICATION";
00171 case BAD_PROGRAM: return "BAD_PROGRAM";
00172 case LAUNCH_FAILED: return "LAUNCH_FAILED";
00173 case NOT_RUNNING: return "NOT_RUNNING";
00174 case FROZEN: return "FROZEN";
00175 default: return common::outcome_name(to_name);
00176 }
00177 }
00178
00179 void process_manager::stop_everything()
00180 {
00181 _stop_launching = true;
00182 stop_all_kids();
00183 _checker->stop();
00184 }
00185
00186 void process_manager::stop_all_kids()
00187 {
00188 FUNCDEF("stop_all_kids");
00189 _stop_launching = true;
00190 LOG("zapping any active sub-processes prior to exit.");
00191
00192
00193
00194
00195
00196 for (int lev = 100; lev >= 0; lev--) {
00197
00198 #ifdef DEBUG_PROCESS_MANAGER
00199 LOG(isprintf("level %d", lev));
00200 #endif
00201 bool zapped_any = false;
00202 {
00203
00204 LOCK_KIDS;
00205 for (int i = _our_kids->length() - 1; i >= 0; i--) {
00206
00207 graceful_record &grace = (*_our_kids)[i];
00208 if (lev == grace._level) {
00209
00210 zap_process(grace._product, grace._app_name, true);
00211
00212 _our_kids->zap(i, i);
00213 zapped_any = true;
00214 }
00215 }
00216 }
00217 int num_dying = 1;
00218
00219 #ifdef DEBUG_PROCESS_MANAGER
00220 time_stamp next_print(4 * SECOND_ms);
00221 #endif
00222
00223 while (num_dying) {
00224 #ifdef DEBUG_PROCESS_MANAGER
00225 if (time_stamp() >= next_print) {
00226 LOG("waiting...");
00227 next_print.reset(4 * SECOND_ms);
00228 }
00229 #endif
00230
00231
00232
00233
00234
00235 {
00236 LOCK_ZOMBIES;
00237 num_dying = _going_down->length();
00238 }
00239
00240 if (!num_dying) break;
00241
00242 _checker->reschedule(0);
00243
00244 portable::sleep_ms(40);
00245 }
00246 #ifdef DEBUG_PROCESS_MANAGER
00247 LOG("done waiting...");
00248 #endif
00249 }
00250 }
00251
00252 void process_manager::launch_startup_apps()
00253 {
00254 FUNCDEF("launch_startup_apps");
00255
00256
00257 string_table startup_info;
00258 {
00259 LOCK_CONFIG;
00260 if (!_configs.find_section(_configs.STARTUP_SECTION(), startup_info)) {
00261
00262 LOG("the startup section was not found!");
00263 return;
00264 }
00265 }
00266 #ifdef DEBUG_PROCESS_MANAGER
00267 LOG(istring("table has: ") + startup_info.text_form());
00268 #endif
00269
00270 for (int i = 0; i < startup_info.symbols(); i++) {
00271 istring app = startup_info.name(i);
00272 if (app == application_config::STARTUP_APP_NAME()) continue;
00273
00274 istring info = startup_info[i];
00275 LOG(istring("launching application ") + app + "...");
00276
00277 istring product, parms;
00278 bool one_shot;
00279 if (!application_config::parse_startup_entry(info, product, parms,
00280 one_shot)) {
00281 LOG("the startup entry was not malformed; we could not parse it!");
00282 continue;
00283 }
00284
00285 LOG(app + isprintf(" is %ssingle shot.", one_shot? "" : "not "));
00286
00287
00288 launch_now(product, app, parms);
00289
00290 if (one_shot) {
00291
00292 remove_from_startup(product, app);
00293 }
00294 }
00295 }
00296
00297 outcome process_manager::launch_now(const istring &product,
00298 const istring &app_name, const istring ¶meters)
00299 {
00300 FUNCDEF("launch_now");
00301 LOG(istring("product \"") + product + "\", application \"" + app_name
00302 + (parameters.length() ? istring("\"")
00303 : istring("\", parms=") + parameters));
00304
00305 if (_stop_launching) {
00306
00307
00308 if (_gag_exclusions->member(app_name)) {
00309
00310 } else {
00311 LOG(istring("application \"") + app_name + "\" cannot be launched;"
00312 + log_base::platform_ending() + "launching services have been "
00313 "shut down.");
00314 return LAUNCH_FAILED;
00315 }
00316 }
00317
00318 istring entry_found;
00319 int level;
00320 {
00321 LOCK_CONFIG;
00322
00323
00324 entry_found = _configs.find_program(product, app_name, level);
00325 if (!entry_found) {
00326 if (!_configs.product_exists(product)) {
00327
00328 COMPLAIN_PRODUCT;
00329 return NO_PRODUCT;
00330 }
00331 COMPLAIN_APPLICATION;
00332 return NO_APPLICATION;
00333 }
00334 }
00335
00336 filename existence_check(entry_found);
00337 if (!existence_check.exists()) {
00338 LOG(istring("file or path wasn't found for ") + entry_found + ".");
00339 return FILE_NOT_FOUND;
00340 }
00341
00342 u_int kid = 0;
00343 int ret = portable::launch_process(entry_found, parameters,
00344 portable::RETURN_IMMEDIATELY | portable::HIDE_APP_WINDOW, kid);
00345 if (!ret) {
00346
00347
00348 if (_tracking_exclusions->member(app_name)) {
00349
00350
00351 return OKAY;
00352 }
00353
00354 if (kid) {
00355
00356 LOCK_KIDS;
00357 LOG(isprintf("adding given process id %d for app %s at level %d.",
00358 kid, app_name.s(), level));
00359 graceful_record to_add(kid, product, app_name, level);
00360 *_our_kids += to_add;
00361 return OKAY;
00362 }
00363 #ifdef DEBUG_PROCESS_MANAGER
00364 LOG("was not told child process id!!!");
00365 #endif
00366
00367 int_set pids;
00368 time_stamp give_it_up(MAXIMUM_INITIAL_APP_WAIT);
00369 while (give_it_up > time_stamp()) {
00370
00371 if (find_process(app_name, pids)) break;
00372 portable::sleep_ms(10);
00373 }
00374
00375 if (time_stamp() >= give_it_up) {
00376
00377
00378
00379
00380 LOG(istring("no process found for product \"") + product
00381 + "\", application \"" + app_name + "\"; not adding "
00382 "tracking record.");
00383 return OKAY;
00384 }
00385
00386 LOCK_KIDS;
00387
00388
00389 for (int i = 0; i < pids.elements(); i++) {
00390 LOG(isprintf("adding process id %d for app %s at level %d.",
00391 pids[i], app_name.s(), level));
00392 graceful_record to_add(pids[i], product, app_name, level);
00393 *_our_kids += to_add;
00394 }
00395 return OKAY;
00396 }
00397
00398
00399
00400
00401 #ifdef __WIN32__
00402 if (ret == NO_ERROR) return OKAY;
00403 else if ( (ret == ERROR_FILE_NOT_FOUND) || (ret == ERROR_PATH_NOT_FOUND) ) {
00404 LOG(istring("file or path wasn't found for ") + app_name + ".");
00405 return FILE_NOT_FOUND;
00406 } else if (ret == ERROR_BAD_FORMAT) {
00407 LOG(istring(app_name) + " was not in EXE format.");
00408 return BAD_PROGRAM;
00409 } else {
00410 LOG(istring("there was an unknown error while trying to run ")
00411 + app_name + "; the error is:" + log_base::platform_ending()
00412 + portable::system_error_text(ret));
00413 return LAUNCH_FAILED;
00414 }
00415 #else
00416 LOG(istring("error ") + portable::system_error_text(ret)
00417 + " occurred attempting to run: " + app_name + " " + parameters);
00418 return FILE_NOT_FOUND;
00419 #endif
00420 }
00421
00422 outcome process_manager::launch_at_startup(const istring &product,
00423 const istring &app_name, const istring ¶meters, int one_shot)
00424 {
00425 FUNCDEF("launch_at_startup");
00426 LOCK_CONFIG;
00427
00428 #ifdef DEBUG_PROCESS_MANAGER
00429 LOG(istring("product \"") + product + "\", application \"" + app_name
00430 + (one_shot? istring("\", OneShot") : istring("\", MultiUse")));
00431 #endif
00432
00433
00434 int level;
00435 istring entry_found = _configs.find_program(product, app_name, level);
00436 if (!entry_found) {
00437 if (!_configs.product_exists(product)) {
00438
00439 COMPLAIN_PRODUCT;
00440 return NO_PRODUCT;
00441 }
00442 COMPLAIN_APPLICATION;
00443 return NO_APPLICATION;
00444 }
00445
00446 if (!_configs.add_startup_entry(product, app_name, parameters, one_shot)) {
00447
00448 return EXISTING;
00449 }
00450
00451 return OKAY;
00452 }
00453
00454 outcome process_manager::remove_from_startup(const istring &product,
00455 const istring &app_name)
00456 {
00457 FUNCDEF("remove_from_startup");
00458 LOCK_CONFIG;
00459
00460 #ifdef DEBUG_PROCESS_MANAGER
00461 LOG(istring("product \"") + product + "\", application \"" + app_name + "\"");
00462 #endif
00463
00464
00465 int level;
00466 istring entry_found = _configs.find_program(product, app_name, level);
00467 if (!entry_found) {
00468 if (!_configs.product_exists(product)) {
00469
00470 COMPLAIN_PRODUCT;
00471 return NO_PRODUCT;
00472 }
00473 COMPLAIN_APPLICATION;
00474 return NO_APPLICATION;
00475 }
00476
00477
00478 if (!_configs.remove_startup_entry(product, app_name)) {
00479
00480 return NO_APPLICATION;
00481 }
00482
00483 return OKAY;
00484 }
00485
00486 outcome process_manager::query_application(const istring &product,
00487 const istring &app_name)
00488 {
00489 FUNCDEF("query_application");
00490 #ifdef DEBUG_PROCESS_MANAGER
00491 LOG(istring("product \"") + product + "\", application \"" + app_name + "\"");
00492 #endif
00493
00494 {
00495 LOCK_CONFIG;
00496
00497 int level;
00498 istring entry_found = _configs.find_program(product, app_name, level);
00499 if (!entry_found) {
00500 if (!_configs.product_exists(product)) {
00501
00502 COMPLAIN_PRODUCT;
00503 return NO_PRODUCT;
00504 }
00505 COMPLAIN_APPLICATION;
00506 return NO_APPLICATION;
00507 }
00508 }
00509
00510
00511 istring program_name(app_name);
00512 program_name.to_lower();
00513
00514 int_set pids;
00515 if (!find_process(app_name, pids))
00516 return NOT_RUNNING;
00517 return OKAY;
00518 }
00519
00520 outcome process_manager::start_graceful_close(const istring &product,
00521 const istring &app_name)
00522 {
00523 FUNCDEF("start_graceful_close");
00524
00525
00526 {
00527
00528 LOCK_ZOMBIES;
00529
00530 for (int i = _going_down->length() - 1; i >= 0; i--) {
00531 graceful_record &grace = (*_going_down)[i];
00532 if (grace._app_name.iequals(app_name)) {
00533 return OKAY;
00534 }
00535 }
00536 }
00537
00538 if (!anchor_window::close_app_window(app_name))
00539 return NO_ANCHOR;
00540
00541 int_set pids;
00542 if (!find_process(app_name, pids)) {
00543 LOG(istring("Failed to find process id for [") + app_name
00544 + istring("], assuming it has already exited."));
00545 return OKAY;
00546 }
00547
00548 {
00549
00550
00551 LOCK_ZOMBIES;
00552 for (int i = 0; i < pids.elements(); i++) {
00553 graceful_record to_add(pids[i], product, app_name);
00554 *_going_down += to_add;
00555 }
00556 }
00557
00558 return OKAY;
00559 }
00560
00561 bool process_manager::get_processes(process_entry_array &processes)
00562 {
00563 FUNCDEF("get_processes");
00564 if (!_procs->query_processes(processes)) {
00565 LOG("failed to query processes!");
00566 return false;
00567 }
00568 return true;
00569 }
00570
00571 bool process_manager::find_process(const istring &app_name_in, int_set &pids)
00572 {
00573 FUNCDEF("find_process");
00574 pids.clear();
00575 process_entry_array processes;
00576 if (!get_processes(processes)) return false;
00577 return process_control::find_process_in_list(processes, app_name_in, pids);
00578 }
00579
00580 outcome process_manager::zap_process(const istring &product,
00581 const istring &app_name, bool graceful)
00582 {
00583 FUNCDEF("zap_process");
00584
00585 #ifdef DEBUG_PROCESS_MANAGER
00586 LOG(istring("product \"") + product + "\", application \"" + app_name
00587 + (graceful? "\", Graceful Close" : "\", Brutal Close"));
00588 #endif
00589
00590 if (_tracking_exclusions->member(app_name)) {
00591
00592
00593 return NOT_RUNNING;
00594 }
00595
00596 istring entry_found;
00597 {
00598 LOCK_CONFIG;
00599
00600
00601 int level;
00602 istring entry_found = _configs.find_program(product, app_name, level);
00603 if (!entry_found) {
00604 if (!_configs.product_exists(product)) {
00605
00606 COMPLAIN_PRODUCT;
00607 return NO_PRODUCT;
00608 }
00609 COMPLAIN_APPLICATION;
00610 return NO_APPLICATION;
00611 }
00612 }
00613
00614
00615
00616
00617 outcome to_return = NOT_RUNNING;
00618 if (graceful)
00619 to_return = start_graceful_close(product, app_name);
00620 if (to_return == OKAY)
00621 return OKAY;
00622
00623
00624 istring program_name(app_name);
00625 int_set pids;
00626 if (!find_process(program_name, pids)) {
00627 #ifdef DEBUG_PROCESS_MANAGER
00628 LOG(program_name + " process was not running.")
00629 #endif
00630 return NOT_RUNNING;
00631 }
00632
00633
00634 bool failed = false;
00635 for (int i = 0; i < pids.elements(); i++) {
00636 bool ret = _procs->zap_process(pids[i]);
00637 if (ret) {
00638 LOG(istring(istring::SPRINTF, "Killed process %d [",
00639 pids[i]) + program_name + istring("]"));
00640 } else {
00641 LOG(istring(istring::SPRINTF, "Failed to zap process %d [",
00642 pids[i]) + program_name + istring("]"));
00643 }
00644 if (!ret) failed = true;
00645 }
00646
00647
00648 return failed? ACCESS_DENIED : OKAY;
00649 }
00650
00651 outcome process_manager::shut_down_launching_services(const istring &secret_word)
00652 {
00653 FUNCDEF("shut_down_launching_services");
00654 LOG("checking secret word...");
00655 if (secret_word == "KrustyDoesn'tDoThat.") {
00656 LOG("it's correct; ending launch capabilities.");
00657 } else {
00658 LOG("the secret word is wrong. continuing normal operation.");
00659 return BAD_PROGRAM;
00660 }
00661 _stop_launching = true;
00662 return OKAY;
00663 }
00664
00665 outcome process_manager::reenable_launching_services(const istring &secret_word)
00666 {
00667 FUNCDEF("reenable_launching_services");
00668 LOG("checking secret word...");
00669 if (secret_word == "KrustyDoesn'tDoThat.") {
00670 LOG("it's correct; resuming launch capabilities.");
00671 } else {
00672 LOG("the secret word is wrong. continuing with prior mode.");
00673 return BAD_PROGRAM;
00674 }
00675 _stop_launching = false;
00676 return OKAY;
00677 }
00678
00679 #define GET_PROCESSES \
00680 if (!retrieved_processes) { \
00681 retrieved_processes = true; \
00682 if (!get_processes(processes)) { \
00683 LOG("failed to retrieve process list from OS!"); \
00684 return; \
00685 } \
00686 }
00687
00688 void process_manager::push_timed_activities(process_entry_array &processes)
00689 {
00690 FUNCDEF("push_timed_activities");
00691
00692
00693
00694
00695
00696 if (!_started_initial_apps && (*_startup_time <= time_stamp()) ) {
00697
00698 LOG("starting up the tasks registered for system initiation.");
00699 launch_startup_apps();
00700 _started_initial_apps = true;
00701 }
00702
00703 if (!_started_initial_apps) {
00704 _checker->reschedule(200);
00705
00706
00707 }
00708
00709 bool retrieved_processes = false;
00710
00711 {
00712
00713 LOCK_ZOMBIES;
00714
00715 for (int i = _going_down->length() - 1; i >= 0; i--) {
00716 graceful_record &grace = (*_going_down)[i];
00717
00718 GET_PROCESSES;
00719
00720 int_set pids;
00721 if (!process_control::find_process_in_list(processes, grace._app_name,
00722 pids)) {
00723
00724 #ifdef DEBUG_PROCESS_MANAGER
00725 LOG(istring("cannot find app ") + grace._app_name
00726 + " as running still; removing its dying record");
00727 #endif
00728 _going_down->zap(i, i);
00729 continue;
00730 }
00731 if (!pids.member(grace._pid)) {
00732
00733 #ifdef DEBUG_PROCESS_MANAGER
00734 LOG(istring("app ") + grace._app_name
00735 + " exited on its own; removing its dying record");
00736 #endif
00737 _going_down->zap(i, i);
00738 continue;
00739 }
00740
00741 if (grace._started <= time_stamp(-GRACEFUL_SLACK)) {
00742
00743 LOG(istring("Application ") + grace._app_name + " is unresponsive to "
00744 "the graceful shutdown request. Now zapping it instead.");
00745 if (!_procs->zap_process(grace._pid))
00746 LOG("Devolved graceful shutdown failed as zap_process also.");
00747 _going_down->zap(i, i);
00748 continue;
00749 }
00750
00751
00752 }
00753 }
00754
00755 {
00756
00757
00758 LOCK_KIDS;
00759
00760 for (int i = _our_kids->length() - 1; i >= 0; i--) {
00761 graceful_record &grace = (*_our_kids)[i];
00762
00763 GET_PROCESSES;
00764
00765 int_set pids;
00766 if (!process_control::find_process_in_list(processes, grace._app_name,
00767 pids)) {
00768
00769 #ifdef DEBUG_PROCESS_MANAGER
00770 LOG(istring("cannot find kid ") + grace._app_name
00771 + " as still running; removing its normal record");
00772 #endif
00773 _our_kids->zap(i, i);
00774 continue;
00775 }
00776 if (!pids.member(grace._pid)) {
00777
00778 #ifdef DEBUG_PROCESS_MANAGER
00779 LOG(istring("kid ") + grace._app_name
00780 + " exited on its own; removing its normal record");
00781 #endif
00782 _our_kids->zap(i, i);
00783 continue;
00784 }
00785
00786
00787 }
00788 }
00789 }
00790
00791
00792 #endif //PROCESS_MANAGER_IMPLEMENTATION_FILE
00793