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>
32#include <textual/parser_bits.h>
33#include <timely/time_control.h>
34#include <timely/time_stamp.h>
35
36using namespace basis;
37using namespace configuration;
38using namespace filesystem;
39using namespace loggers;
40using namespace processes;
41using namespace structures;
42using namespace textual;
43using namespace timely;
44
45namespace 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
52const 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
56const 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
89class launch_manager_thread : public ethread
90{
91public:
92 launch_manager_thread(launch_manager &parent)
94 _parent(parent) {}
95 virtual ~launch_manager_thread() {}
96
97 virtual void perform_activity(void *)
98 { _parent.push_timed_activities(_processes); }
99
100private:
101 launch_manager &_parent; // the owner of the object.
102 process_entry_array _processes; // will be filled as needed.
103};
104
106
107class graceful_record
108{
109public:
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
121class graceful_array : public array<graceful_record> {};
122
124
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");
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
172const 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
194void 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 {
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
253 }
254#ifdef DEBUG_PROCESS_MANAGER
255 LOG("done waiting...");
256#endif
257 }
258}
259
260void launch_manager::launch_startup_apps()
261{
262 FUNCDEF("launch_startup_apps");
263
264 // read the startup section.
265 string_table startup_info;
266 {
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);
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 {
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,
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()
421 return LAUNCH_FAILED;
422 }
423#else
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 {
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
528outcome 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.
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
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.
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
569bool 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
579bool 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 {
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.
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;
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;
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
#define LOG(s)
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.
launch_manager(processes::configured_applications &config)
the launch_manager needs a configuration set to work with.
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:531
static const char * outcome_name(const outcome &to_name)
Returns a string representation of the outcome "to_name".
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
static basis::astring system_error_text(basis::un_int error_to_show)
returns the OS's string form of the "error_to_show".
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.
static const char * STARTUP_APP_NAME()
a special placeholder name that will appear in the startup list.
static bool parse_startup_entry(const basis::astring &info, basis::astring &product, basis::astring &parms, bool &one_shot)
processes the items in "info" as an application startup list.
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
ethread()
creates a single-shot thread object.
Definition ethread.cpp:86
@ RETURN_IMMEDIATELY
starts the application and comes right back to the caller.
@ HIDE_APP_WINDOW
launches the application invisibly if possible.
static basis::un_int run(const basis::astring &app_name, const basis::astring &command_line, int flag, basis::un_int &child_id)
starts an application using the "app_name" as the executable to run.
Provides a bridge to the operating system for information on processes.
static bool find_process_in_list(const process_entry_array &processes, const basis::astring &app_name, structures::int_set &pids)
uses a pre-existing list of "processes" to search for the "app_name".
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.
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.
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.
static const char * platform_eol_to_chars()
provides the characters that make up this platform's line ending.
static void sleep_ms(basis::un_int msec)
a system independent name for a forced snooze measured in milliseconds.
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:54
#define LOCK_CONFIG
#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.
unsigned int un_int
Abbreviated name for unsigned integers.
Definition definitions.h:62
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.
Definition amorph.h:55
#include <time.h>
Aids in achievement of platform independence.