feisty meow concerns codebase  2.140
launch_manager.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * *
3 * Name : launch_manager
4 * Author : Chris Koeritz
5 * *
6 *******************************************************************************
7 * Copyright (c) 2000-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
14 
15 #include "hoople_service.h"
16 #include "launch_manager.h"
17 
19 #include <basis/astring.h>
20 #include <basis/mutex.h>
23 #include <filesystem/filename.h>
27 #include <processes/ethread.h>
29 #include <structures/set.h>
31 #include <structures/unique_id.h>
32 #include <textual/parser_bits.h>
33 #include <timely/time_control.h>
34 #include <timely/time_stamp.h>
35 
36 using namespace basis;
37 using namespace configuration;
38 using namespace filesystem;
39 using namespace loggers;
40 using namespace processes;
41 using namespace structures;
42 using namespace textual;
43 using namespace timely;
44 
45 namespace application {
46 
47 #define DEBUG_PROCESS_MANAGER
48  // uncomment for verbose diagnostics.
49 
50 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
51 
52 const int CHECK_INTERVAL = 4 * SECOND_ms;
53  // this is how frequently the checking thread executes to ensure that
54  // processes are gone when they should be.
55 
56 const int GRACEFUL_SLACK = 90 * SECOND_ms;
57  // the length of time before a graceful shutdown devolves into a forced
58  // shutdown.
59 
61  // this is the longest we bother to wait for a process we just started.
62  // if it hasn't begun by then, we decide it will never do so.
63 
65  // we delay for this long before the initial apps are started.
66 
68  // the longest we will ever wait for a response to be generated based on
69  // our last request.
70 
71 // these are concurrency control macros for the lists managed here.
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)
75 
76 // error messages.
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.")
82 #else
83  #define COMPLAIN_APPLICATION {}
84  #define COMPLAIN_PRODUCT {}
85 #endif
86 
88 
89 class launch_manager_thread : public ethread
90 {
91 public:
92  launch_manager_thread(launch_manager &parent)
93  : ethread(CHECK_INTERVAL, ethread::SLACK_INTERVAL),
94  _parent(parent) {}
95  virtual ~launch_manager_thread() {}
96 
97  virtual void perform_activity(void *)
98  { _parent.push_timed_activities(_processes); }
99 
100 private:
101  launch_manager &_parent; // the owner of the object.
102  process_entry_array _processes; // will be filled as needed.
103 };
104 
106 
107 class graceful_record
108 {
109 public:
110  astring _product; // the product name the app is listed under.
111  astring _app_name; // the application's name.
112  time_stamp _started; // when the graceful shutdown started.
113  int _pid; // the particular process id for this app.
114  int _level; // the shutdown ordering specifier.
115 
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) {}
119 };
120 
121 class graceful_array : public array<graceful_record> {};
122 
124 
125 launch_manager::launch_manager(configured_applications &config)
126 : _configs(config),
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),
135  _startup_time(new time_stamp(STARTUP_APPS_DELAY_PERIOD)),
136  _procs(new process_control),
137  _gag_exclusions(new string_set),
138  _tracking_exclusions(new string_set)
139 {
140  FUNCDEF("constructor");
141 
142  // start the application checking thread.
143  _checker->start(NULL_POINTER);
144 
145  _checker->reschedule(200); // make it start pretty quickly.
146 }
147 
149 {
150  FUNCDEF("destructor");
151  stop_everything();
152 
153  WHACK(_checker);
154  WHACK(_going_down);
155  WHACK(_our_kids);
156  WHACK(_scamp_lock);
157  WHACK(_zombie_lock);
158  WHACK(_config_lock);
159  WHACK(_startup_time);
160  WHACK(_procs);
161  WHACK(_gag_exclusions);
162  WHACK(_tracking_exclusions);
163  LOG("launch_manager is now stopped.");
164 }
165 
167 { *_gag_exclusions += exclusion; }
168 
170 { *_tracking_exclusions += exclusion; }
171 
172 const char *launch_manager::outcome_name(const outcome &to_name)
173 {
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);
184  }
185 }
186 
188 {
189  _stop_launching = true; // at least deny any connected clients.
190  stop_all_kids(); // shut down all programs that we started.
191  _checker->stop(); // stop our thread.
192 }
193 
194 void launch_manager::stop_all_kids()
195 {
196  FUNCDEF("stop_all_kids");
197  _stop_launching = true; // set this for good measure to keep clients out.
198  LOG("zapping any active sub-processes prior to exit.");
199 
200  // now we wait for the process closures to take effect. we are relying on
201  // the graceful shutdown devolving to a process zap and the timing is
202  // rooted around that assumption.
203 
204  for (int lev = 100; lev >= 0; lev--) {
205  // loop from our highest level to our lowest for the shutdown.
206 #ifdef DEBUG_PROCESS_MANAGER
207  LOG(a_sprintf("level %d", lev));
208 #endif
209  bool zapped_any = false;
210  {
211  // this shuts down all the child processes we've started at this level.
212  LOCK_KIDS; // lock within this scope.
213  for (int i = _our_kids->length() - 1; i >= 0; i--) {
214  // now check each record and see if it's at the appropriate level.
215  graceful_record &grace = (*_our_kids)[i];
216  if (lev == grace._level) {
217  // start a graceful shutdown.
218  zap_process(grace._product, grace._app_name, true);
219  // remove it from our list.
220  _our_kids->zap(i, i);
221  zapped_any = true; // set our flag.
222  }
223  }
224  }
225  int num_dying = 1; // go into the loop once at least.
226 
227 #ifdef DEBUG_PROCESS_MANAGER
228  time_stamp next_print(4 * SECOND_ms);
229 #endif
230 
231  while (num_dying) {
232 #ifdef DEBUG_PROCESS_MANAGER
233  if (time_stamp() >= next_print) {
234  LOG("waiting...");
235  next_print.reset(4 * SECOND_ms);
236  }
237 #endif
238 
239  // while there are any pending process zaps, we will wait here. this
240  // will hose us but good if the processes aren't eventually cleared up,
241  // but that shouldn't happen.
242 
243  {
244  LOCK_ZOMBIES;
245  num_dying = _going_down->length();
246  }
247 
248  if (!num_dying) break; // jump out of loop.
249 
250  _checker->reschedule(0); // make thread check as soon as possible.
251 
252  time_control::sleep_ms(40);
253  }
254 #ifdef DEBUG_PROCESS_MANAGER
255  LOG("done waiting...");
256 #endif
257  }
258 }
259 
260 void launch_manager::launch_startup_apps()
261 {
262  FUNCDEF("launch_startup_apps");
263 
264  // read the startup section.
265  string_table startup_info;
266  {
267  LOCK_CONFIG;
268  if (!_configs.find_section(_configs.STARTUP_SECTION(), startup_info)) {
269  // if there's no startup section, we do nothing right now.
270  LOG("the startup section was not found!");
271  return;
272  }
273  }
274 #ifdef DEBUG_PROCESS_MANAGER
275  LOG(astring("table has: ") + startup_info.text_form());
276 #endif
277 
278  for (int i = 0; i < startup_info.symbols(); i++) {
279  astring app = startup_info.name(i);
280  if (app.equal_to(configured_applications::STARTUP_APP_NAME())) continue;
281  // skip bogus name that keeps the section present.
282  astring info = startup_info[i];
283  LOG(astring("launching application ") + app + "...");
284  // parse the items that are in the entry for this program.
285  astring product, parms;
286  bool one_shot;
287  if (!configured_applications::parse_startup_entry(info, product, parms,
288  one_shot)) {
289  LOG("the startup entry was not malformed; we could not parse it!");
290  continue;
291  }
292 
293  LOG(app + a_sprintf(" is %ssingle shot.", one_shot? "" : "not "));
294 
295  // now try to send the program off on its way.
296  launch_now(product, app, parms);
297 
298  if (one_shot) {
299  // it's only supposed to be started once, so now toss out the entry.
300  remove_from_startup(product, app);
301  }
302  }
303 }
304 
306  const astring &app_name, const astring &parameters)
307 {
308  FUNCDEF("launch_now");
309  LOG(astring("product \"") + product + "\", application \"" + app_name
310  + (parameters.length() ? astring("\"")
311  : astring("\", parms=") + parameters));
312 
313  if (_stop_launching) {
314  // if the application is one of the exceptions to the gag rule, then
315  // we will still launch it. otherwise, we'll ignore this request.
316  if (_gag_exclusions->member(app_name)) {
317  // this is one of the apps that can still be launched when gagged.
318  } else {
319  LOG(astring("application \"") + app_name + "\" cannot be launched;"
320  + parser_bits::platform_eol_to_chars() + "launching services have been "
321  "shut down.");
322  return LAUNCH_FAILED;
323  }
324  }
325 
326  astring entry_found;
327  int level;
328  {
329  LOCK_CONFIG;
330 
331  // get the specific entry for the program they want.
332  entry_found = _configs.find_program(product, app_name, level);
333  if (!entry_found) {
334  if (!_configs.product_exists(product)) {
335  // return more specific error for missing product.
337  return NO_PRODUCT;
338  }
340  return NO_APPLICATION;
341  }
342  }
343 
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;
348  }
349 
350  basis::un_int kid = 0;
351  int ret = launch_process::run(entry_found, parameters,
352  launch_process::RETURN_IMMEDIATELY | launch_process::HIDE_APP_WINDOW, kid);
353  if (!ret) {
354  // hey, it worked! so now make sure we track its lifetime...
355 
356  if (_tracking_exclusions->member(app_name)) {
357  // this is one of the apps that we don't track. if it's still
358  // running when we're shutting down, it's not our problem.
359  return OKAY;
360  }
361 
362  if (kid) {
363  // we were told the specific id for the process we started.
364  LOCK_KIDS;
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;
369  return OKAY;
370  }
371 #ifdef DEBUG_PROCESS_MANAGER
372  LOG("was not told child process id!!!");
373 #endif
374  // we weren't told the process id; let's see if we can search for it.
375  int_set pids;
377  while (give_it_up > time_stamp()) {
378  // find the process id for the program we just started.
379  if (find_process(app_name, pids)) break;
380  time_control::sleep_ms(10); // pause to see if we can find it yet.
381  }
382 
383  if (time_stamp() >= give_it_up) {
384  // we could not launch it for some reason, or our querier has failed.
385  // however, as far as we know, it launched successfully. if the id is
386  // missing, then that's not really our fault. we will just not be able
387  // to track the program, possibly because it's already gone.
388  LOG(astring("no process found for product \"") + product
389  + "\", application \"" + app_name + "\"; not adding "
390  "tracking record.");
391  return OKAY;
392  }
393 
394  LOCK_KIDS;
395 
396  // add all the processes we found under that name.
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;
402  }
403  return OKAY;
404  }
405 
406  // if we reached here, things are not good. we must not have been able to
407  // start the process.
408 
409 #ifdef __WIN32__
410  if (ret == NO_ERROR) return OKAY; // how would that happen?
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.");
416  return BAD_PROGRAM;
417  } else {
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;
422  }
423 #else
424  LOG(astring("error ") + critical_events::system_error_text(ret)
425  + " occurred attempting to run: " + app_name + " " + parameters);
426  return FILE_NOT_FOUND;
427 #endif
428 }
429 
431  const astring &app_name, const astring &parameters, int one_shot)
432 {
433  FUNCDEF("launch_at_startup");
434  LOCK_CONFIG; // this whole function modifies the config file.
435 
436 #ifdef DEBUG_PROCESS_MANAGER
437  LOG(astring("product \"") + product + "\", application \"" + app_name
438  + (one_shot? astring("\", OneShot") : astring("\", MultiUse")));
439 #endif
440 
441  // get the specific entry for the program they want.
442  int level;
443  astring entry_found = _configs.find_program(product, app_name, level);
444  if (!entry_found) {
445  if (!_configs.product_exists(product)) {
446  // return more specific error for missing product.
448  return NO_PRODUCT;
449  }
451  return NO_APPLICATION;
452  }
453 
454  if (!_configs.add_startup_entry(product, app_name, parameters, one_shot)) {
455  // most likely problem is that it was already there.
456  return EXISTING;
457  }
458 
459  return OKAY;
460 }
461 
463  const astring &app_name)
464 {
465  FUNCDEF("remove_from_startup");
466  LOCK_CONFIG; // this whole function modifies the config file.
467 
468 #ifdef DEBUG_PROCESS_MANAGER
469  LOG(astring("product \"") + product + "\", application \"" + app_name + "\"");
470 #endif
471 
472  // get the specific entry for the program they want.
473  int level;
474  astring entry_found = _configs.find_program(product, app_name, level);
475  if (!entry_found) {
476  if (!_configs.product_exists(product)) {
477  // return more specific error for missing product.
479  return NO_PRODUCT;
480  }
482  return NO_APPLICATION;
483  }
484 //hmmm: is product required for this for real?
485 
486  if (!_configs.remove_startup_entry(product, app_name)) {
487  // the entry couldn't be removed, probably doesn't exist.
488  return NO_APPLICATION;
489  }
490 
491  return OKAY;
492 }
493 
495  const astring &app_name)
496 {
497  FUNCDEF("query_application");
498 #ifdef DEBUG_PROCESS_MANAGER
499  LOG(astring("product \"") + product + "\", application \"" + app_name + "\"");
500 #endif
501 
502  {
503  LOCK_CONFIG;
504  // get the specific entry for the program they want.
505  int level;
506  astring entry_found = _configs.find_program(product, app_name, level);
507  if (!entry_found) {
508  if (!_configs.product_exists(product)) {
509  // return more specific error for missing product.
511  return NO_PRODUCT;
512  }
514  return NO_APPLICATION;
515  }
516  }
517 
518  // now seek that program name in the current list of processes.
519  astring program_name(app_name);
520  program_name.to_lower();
521 
522  int_set pids;
523  if (!find_process(app_name, pids))
524  return NOT_RUNNING;
525  return OKAY;
526 }
527 
528 outcome launch_manager::start_graceful_close(const astring &product,
529  const astring &app_name)
530 {
531  FUNCDEF("start_graceful_close");
532 //hmmm: record this app as one we need to watch.
533 
534  {
535  // find that program name to make sure it's not already shutting down.
536  LOCK_ZOMBIES;
537 
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)) {
541  return OKAY;
542  }
543  }
544  }
545 
546  if (!hoople_service::close_application(app_name))
547  return NO_ANCHOR;
548 
549  int_set pids;
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."));
553  return OKAY;
554  }
555 
556  {
557  // add all the process ids, just in case there were multiple instances
558  // of the application somehow.
559  LOCK_ZOMBIES;
560  for (int i = 0; i < pids.elements(); i++) {
561  graceful_record to_add(pids[i], product, app_name);
562  *_going_down += to_add;
563  }
564  }
565 
566  return OKAY;
567 }
568 
569 bool launch_manager::get_processes(process_entry_array &processes)
570 {
571  FUNCDEF("get_processes");
572  if (!_procs->query_processes(processes)) {
573  LOG("failed to query processes!");
574  return false;
575  }
576  return true;
577 }
578 
579 bool launch_manager::find_process(const astring &app_name_in, int_set &pids)
580 {
581  FUNCDEF("find_process");
582  pids.clear();
584  if (!get_processes(processes)) return false;
585  return process_control::find_process_in_list(processes, app_name_in, pids);
586 }
587 
589  const astring &app_name_key, bool graceful)
590 {
591  FUNCDEF("zap_process");
592 
593 #ifdef DEBUG_PROCESS_MANAGER
594  LOG(astring("product \"") + product + "\", application \"" + app_name_key
595  + (graceful? "\", Graceful Close" : "\", Brutal Close"));
596 #endif
597 
598  if (_tracking_exclusions->member(app_name_key)) {
599  // the non-tracked applications are never reported as running since they're
600  // not allowed to be zapped anyhow.
601  return NOT_RUNNING;
602  }
603 
604  // use the real program name from here onward.
605  astring app_name;
606  {
607  LOCK_CONFIG;
608 
609  // get the specific entry for the program they want.
610  int level;
611  app_name = _configs.find_program(product, app_name_key, level);
612  if (!app_name) {
613  if (!_configs.product_exists(product)) {
614  // return more specific error for missing product.
616  return NO_PRODUCT;
617  }
619  return NO_APPLICATION;
620  }
621  // truncate the directory and just use the base.
622  app_name = filename(app_name).basename();
623  }
624 
625  // if they want a graceful close, start by trying that. if it appears to
626  // have succeeded, then we exit and let the thread take care of ensuring
627  // the app goes down.
628  outcome to_return = NOT_RUNNING;
629  if (graceful)
630  to_return = start_graceful_close(product, app_name);
631  if (to_return == OKAY)
632  return OKAY; // maybe finished the close.
633 
634  // they want a harsh close of this application or the graceful close failed.
635  astring program_name(app_name);
636  int_set pids;
637  if (!find_process(program_name, pids)) {
638 #ifdef DEBUG_PROCESS_MANAGER
639  LOG(program_name + " process was not running.")
640 #endif
641  return NOT_RUNNING;
642  }
643 
644  // search for the application in the process list.
645  bool failed = false;
646  for (int i = 0; i < pids.elements(); i++) {
647  bool ret = _procs->zap_process(pids[i]);
648  if (ret) {
649  LOG(astring(astring::SPRINTF, "Killed process %d [",
650  pids[i]) + program_name + astring("]"));
651  } else {
652  LOG(astring(astring::SPRINTF, "Failed to zap process %d [",
653  pids[i]) + program_name + astring("]"));
654  }
655  if (!ret) failed = true;
656  }
657 // kind of a bizarre return, but whatever. it really should be some
658 // failure result since the zap failed.
659  return failed? ACCESS_DENIED : OKAY;
660 }
661 
663 {
664  FUNCDEF("shut_down_launching_services");
665  LOG("checking secret word...");
666 //hmmm: changing the secret word here.
667  if (secret_word.equal_to("MarblesAreRoundishYo")) {
668  LOG("it's correct; ending launch capabilities.");
669  } else {
670  LOG("the secret word is wrong. continuing normal operation.");
671  return BAD_PROGRAM;
672  }
673  _stop_launching = true;
674  return OKAY;
675 }
676 
678 {
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.");
683  } else {
684  LOG("the secret word is wrong. continuing with prior mode.");
685  return BAD_PROGRAM;
686  }
687  _stop_launching = false;
688  return OKAY;
689 }
690 
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!"); \
696  return; /* badness. */ \
697  } \
698  }
699 
701 {
702  FUNCDEF("push_timed_activities");
703 
704  // make sure we started the applications that were slated for execution at
705  // system startup time. we wait on this until the first thread activation
706  // to give other processes some breathing room right at startup time.
707  // also, it then doesn't block the main service thread.
708  if (!_started_initial_apps && (*_startup_time <= time_stamp()) ) {
709  // launch all the apps that are listed for system startup.
710  LOG("starting up the tasks registered for system initiation.");
711  launch_startup_apps();
712  _started_initial_apps = true;
713  }
714 
715  if (!_started_initial_apps) {
716  _checker->reschedule(200);
717  // keep hitting this function until we know we can relax since the
718  // startup apps have been sent off.
719  }
720 
721  bool retrieved_processes = false;
722 
723  {
724  // loop over the death records we've got and check on the soon to be gone.
725  LOCK_ZOMBIES;
726 
727  for (int i = _going_down->length() - 1; i >= 0; i--) {
728  graceful_record &grace = (*_going_down)[i];
729 
730  GET_PROCESSES; // load them if they hadn't been.
731 
732  int_set pids;
733  if (!process_control::find_process_in_list(processes, grace._app_name,
734  pids)) {
735  // the app can't be found as running, so whack the record for it.
736 #ifdef DEBUG_PROCESS_MANAGER
737  LOG(astring("cannot find app ") + grace._app_name
738  + " as running still; removing its dying record");
739 #endif
740  _going_down->zap(i, i);
741  continue;
742  }
743  if (!pids.member(grace._pid)) {
744  // that particular instance exited on its own, so whack the record.
745 #ifdef DEBUG_PROCESS_MANAGER
746  LOG(astring("app ") + grace._app_name
747  + " exited on its own; removing its dying record");
748 #endif
749  _going_down->zap(i, i);
750  continue;
751  }
752 
753  if (grace._started <= time_stamp(-GRACEFUL_SLACK)) {
754  // time to force it.
755  LOG(astring("Application ") + grace._app_name + " is unresponsive to "
756  "the graceful shutdown request. Now zapping it instead.");
757  if (!_procs->zap_process(grace._pid))
758  LOG("Devolved graceful shutdown failed as zap_process also.");
759  _going_down->zap(i, i);
760  continue;
761  }
762 
763  // it's not time to dump this one yet, so keep looking at others.
764  }
765  }
766 
767  {
768  // now loop over the list of our active kids and make sure that they are
769  // all still running. if they aren't, then toss the record out.
770  LOCK_KIDS;
771 
772  for (int i = _our_kids->length() - 1; i >= 0; i--) {
773  graceful_record &grace = (*_our_kids)[i];
774 
775  GET_PROCESSES; // load them if they hadn't been.
776 
777  int_set pids;
778  if (!process_control::find_process_in_list(processes, grace._app_name,
779  pids)) {
780  // the app can't be found as running, so whack the record for it.
781 #ifdef DEBUG_PROCESS_MANAGER
782  LOG(astring("cannot find kid ") + grace._app_name
783  + " as still running; removing its normal record");
784 #endif
785  _our_kids->zap(i, i);
786  continue;
787  }
788  if (!pids.member(grace._pid)) {
789  // that particular instance exited on its own, so whack the record.
790 #ifdef DEBUG_PROCESS_MANAGER
791  LOG(astring("kid ") + grace._app_name
792  + " exited on its own; removing its normal record");
793 #endif
794  _our_kids->zap(i, i);
795  continue;
796  }
797 
798  // this kid is still going, so keep its record.
799  }
800  }
801 }
802 
803 } //namespace.
804 
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 &parameters)
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".
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 &parameters, 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.
Definition: astring.h:440
Represents a sequential, ordered, contiguous collection of objects.
Definition: array.h:54
Provides a dynamically resizable ASCII character string.
Definition: astring.h:35
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
Definition: astring.h:113
bool equal_to(const char *that) const
returns true if "that" is equal to this.
Definition: astring.cpp:159
int length() const
Returns the current length of the string.
Definition: astring.cpp:132
void to_lower()
to_lower modifies "this" by replacing capitals with lower-case.
Definition: astring.cpp:528
Outcomes describe the state of completion for an operation.
Definition: outcome.h:31
int value() const
Definition: outcome.h:51
Provides operations commonly needed on file names.
Definition: filename.h:64
bool exists() const
returns true if the file exists.
Definition: filename.cpp:426
filename basename() const
returns the base of the filename; no directory.
Definition: filename.cpp:385
Manages the initialization file for a set of registered applications.
bool remove_startup_entry(const basis::astring &product, const basis::astring &app_name)
takes an existing entry for the "app_name" out of the startup section.
static const char * STARTUP_SECTION()
the section where startup info is stored.
bool add_startup_entry(const basis::astring &product, const basis::astring &app_name, const basis::astring &parameters, int one_shot)
establishes the "app_name" as a program launched at object startup.
bool product_exists(const basis::astring &product)
returns true if the section for "product" exists in the TOC.
basis::astring find_program(const basis::astring &product, const basis::astring &app_name, int &level)
seeks out the entry for the "product" and "app_name" in our info.
bool find_section(const basis::astring &section_name, structures::string_table &info_found)
locates the entries for "section_name" and stores them in "info_found".
Provides a platform-independent object for adding threads to a program.
Definition: ethread.h:36
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.
Definition: process_entry.h:60
A simple object that wraps a templated set of ints.
Definition: set.h:156
int elements() const
Returns the number of elements in this set.
Definition: set.h:47
bool member(const contents &to_test) const
Returns true if the item "to_test" is a member of this set.
Definition: set.h:223
void clear()
Empties out this set.
Definition: set.h:55
A simple object that wraps a templated set of strings.
Definition: set.h:168
Provides a symbol_table that holds strings as the content.
Definition: string_table.h:32
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".
Definition: symbol_table.h:272
int symbols() const
returns the number of symbols listed in the table.
Definition: symbol_table.h:241
Represents a point in time relative to the operating system startup time.
Definition: time_stamp.h:38
#define NULL_POINTER
The value representing a pointer to nothing.
Definition: definitions.h:32
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:57
#define LOCK_CONFIG
#define LOG(s)
#define LOCK_ZOMBIES
#define LOCK_KIDS
#define COMPLAIN_APPLICATION
#define GET_PROCESSES
#define COMPLAIN_PRODUCT
Implements an application lock to ensure only one is running at once.
const int CHECK_INTERVAL
const int MAXIMUM_REQUEST_PAUSE
const int GRACEFUL_SLACK
const int MAXIMUM_INITIAL_APP_WAIT
const int STARTUP_APPS_DELAY_PERIOD
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
Definition: functions.h:121
const int SECOND_ms
Number of milliseconds in a second.
Definition: definitions.h:120
unsigned int un_int
Abbreviated name for unsigned integers.
Definition: definitions.h:62
A platform independent way to obtain the timestamp of a file.
Definition: byte_filer.cpp:37
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.
Definition: amorph.h:55
#include <time.h>
Definition: earth_time.cpp:37
Aids in achievement of platform independence.