new home directory
[feisty_meow.git] / nucleus / library / application / launch_manager.cpp
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
18 #include <application/windoze_helper.h>
19 #include <basis/astring.h>
20 #include <basis/mutex.h>
21 #include <configuration/configurator.h>
22 #include <configuration/section_manager.h>
23 #include <filesystem/filename.h>
24 #include <loggers/critical_events.h>
25 #include <loggers/program_wide_logger.h>
26 #include <processes/configured_applications.h>
27 #include <processes/ethread.h>
28 #include <processes/launch_process.h>
29 #include <structures/set.h>
30 #include <structures/string_table.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
60 const int MAXIMUM_INITIAL_APP_WAIT = 4 * SECOND_ms;
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
64 const int STARTUP_APPS_DELAY_PERIOD = 2 * SECOND_ms;
65   // we delay for this long before the initial apps are started.
66
67 const int MAXIMUM_REQUEST_PAUSE = 42 * SECOND_ms;
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
87 //////////////
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
105 //////////////
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
123 //////////////
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
148 launch_manager::~launch_manager()
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
166 void launch_manager::add_gag_exclusion(const astring &exclusion)
167 { *_gag_exclusions += exclusion; }
168
169 void launch_manager::add_tracking_exclusion(const astring &exclusion)
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
187 void launch_manager::stop_everything()
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
305 outcome launch_manager::launch_now(const astring &product,
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.
336         COMPLAIN_PRODUCT;
337         return NO_PRODUCT;
338       }
339       COMPLAIN_APPLICATION;
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;
376     time_stamp give_it_up(MAXIMUM_INITIAL_APP_WAIT);
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
430 outcome launch_manager::launch_at_startup(const astring &product,
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.
447       COMPLAIN_PRODUCT;
448       return NO_PRODUCT;
449     }
450     COMPLAIN_APPLICATION;
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
462 outcome launch_manager::remove_from_startup(const astring &product,
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.
478       COMPLAIN_PRODUCT;
479       return NO_PRODUCT;
480     }
481     COMPLAIN_APPLICATION;
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
494 outcome launch_manager::query_application(const astring &product,
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.
510         COMPLAIN_PRODUCT;
511         return NO_PRODUCT;
512       }
513       COMPLAIN_APPLICATION;
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();
583   process_entry_array processes;
584   if (!get_processes(processes)) return false;
585   return process_control::find_process_in_list(processes, app_name_in, pids);
586 }
587
588 outcome launch_manager::zap_process(const astring &product,
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.
615         COMPLAIN_PRODUCT;
616         return NO_PRODUCT;
617       }
618       COMPLAIN_APPLICATION;
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
662 outcome launch_manager::shut_down_launching_services(const astring &secret_word)
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
677 outcome launch_manager::reenable_launching_services(const astring &secret_word)
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
700 void launch_manager::push_timed_activities(process_entry_array &processes)
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