new fortune
[feisty_meow.git] / nucleus / library / processes / configured_applications.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : configured_applications
4 *  Author : Chris Koeritz
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 2000 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 "configured_applications.h"
16
17 #include <basis/astring.h>
18 #include <basis/mutex.h>
19 #include <configuration/ini_configurator.h>
20 #include <configuration/section_manager.h>
21 #include <configuration/variable_tokenizer.h>
22 #include <loggers/program_wide_logger.h>
23 #include <structures/set.h>
24 #include <structures/string_table.h>
25 #include <textual/parser_bits.h>
26
27 using namespace basis;
28 using namespace configuration;
29 using namespace loggers;
30 using namespace structures;
31 using namespace textual;
32
33 namespace processes {
34
35 //#define DEBUG_APP_CONFIG
36   // uncomment for noisier debugging version.
37
38 #undef LOG
39 #define LOG(to_print) program_wide_logger::get().log(to_print, ALWAYS_PRINT)
40
41 //////////////
42
43 const char *PRODUCT_HEADING() { return "product"; }
44   // the string used for our startup entries as a prefix to the product.
45
46 const char *ASSIGN_TOKEN() { return "="; }
47   // how we distinguish the key from the value for startup entries.
48
49 const char *SEPARATOR_TOKEN() { return ","; }
50   // the character between separate key/value pairs in the startup string.
51
52 const char *SEPARATOR_TEXT() { return ", "; }
53   // the string we use for the separator when printing it.
54
55 const char *PARMS_HEADING() { return "parms"; }
56   // the tag for parameters in the startup entry.
57
58 const char *ONESHOT_HEADING() { return "oneshot"; }
59   // the key name for startup entries' flag for once only execution.
60
61 //////////////
62
63 configured_applications::configured_applications(const astring &config_file,
64     const astring &basename)
65 : _lock(new mutex),
66   _config(new ini_configurator(config_file, ini_configurator::RETURN_ONLY,
67       ini_configurator::APPLICATION_DIRECTORY)),
68   _sector(new section_manager(*_config, astring(basename) + "_TOC",
69       astring(PRODUCT_HEADING()) + "_"))
70 {
71   FUNCDEF("constructor");
72   string_table startup_info;
73   if (!find_section(STARTUP_SECTION(), startup_info)) {
74     // if there's no startup section, we do nothing right now.
75     LOG(astring("the startup section doesn't exist yet; adding it now."));
76     astring entry = make_startup_entry(basename, "", false);
77     startup_info.add(STARTUP_APP_NAME(), entry);
78     add_section(STARTUP_SECTION(), startup_info);
79   }
80 }
81
82 configured_applications::~configured_applications()
83 {
84   WHACK(_sector);
85   WHACK(_config);
86   WHACK(_lock);
87 }
88
89 const char *configured_applications::STARTUP_SECTION()
90 { return "PRIVATE_STARTUP_LNCH1.0"; }
91
92 const char *configured_applications::STARTUP_APP_NAME()
93 { return "placeholder"; }
94
95 bool configured_applications::product_exists(const astring &product)
96 {
97   auto_synchronizer l(*_lock);
98   if (!_sector->section_exists(product)) return false;
99   return true;
100 }
101
102 astring configured_applications::find_program(const astring &product,
103     const astring &app_name, int &level)
104 {
105   auto_synchronizer l(*_lock);
106   astring heading = _sector->make_section_heading(product);
107   astring found = _sector->config().load(heading, app_name, "");
108 //////////////
109 //overly specific bits here...
110 //hmmm: add this in as a specialization provided by real owner of class.
111   if (!found) {
112     // we didn't find the entry under the section we wanted to find it in.
113     // there are a couple cases where we can kludge this section to a
114     // different name, based on legacy requirements, and still find the
115     // right item possibly.
116     if (product.iequals("supervisor")) {
117       // for some older installs, they say "supervisor" but mean "core".
118       heading = _sector->make_section_heading("core");
119       found = _sector->config().load(heading, app_name, "");
120     } else if (product.iequals("lightlink")) {
121       heading = _sector->make_section_heading("core");
122       found = _sector->config().load(heading, app_name, "");
123       if (!found) {
124         // we can take one more remedial step for this phrase.
125         heading = _sector->make_section_heading("server");
126         found = _sector->config().load(heading, app_name, "");
127       }
128     }
129   }
130 //end of overly specific.
131 //////////////
132   found = parser_bits::substitute_env_vars(found);
133
134   int comma_loc = found.find(",");
135   if (negative(comma_loc)) return "";  // couldn't find our priority.
136   level = found.convert(0);
137   found.zap(0, comma_loc);
138
139   return found;
140 }
141
142 bool configured_applications::add_program(const astring &product,
143     const astring &app_name, const astring &full_path, int level)
144 {
145 #ifdef DEBUG_APP_CONFIG
146   FUNCDEF("add_program");
147 #endif
148   auto_synchronizer l(*_lock);
149   bool existed = true;
150   // lookup the section, if it exists.
151   string_table info_table;
152   if (!_sector->section_exists(product)) {
153     existed = false;
154   } else
155     find_section(product, info_table);
156 #ifdef DEBUG_APP_CONFIG
157   if (existed) {
158     LOG(astring("section for ") + product + " found:");
159     for (int i = 0; i < info_table.symbols(); i++)
160       LOG(astring("key=") + info_table.name(i) + " value=" + info_table[i]);
161   } else LOG(astring("section for ") + product + " not found.");
162 #endif
163   // remove any existing entry.
164   info_table.whack(app_name);
165   // plug in our new entry.
166   a_sprintf full_entry("%d,%s", level, full_path.s());
167   info_table.add(app_name, full_entry);
168 #ifdef DEBUG_APP_CONFIG
169   LOG(astring("new section for ") + product + " has:");
170   for (int i = 0; i < info_table.symbols(); i++)
171     LOG(astring("key=") + info_table.name(i) + " value=" + info_table[i]);
172 #endif
173   // now call the proper storage function based on whether the section
174   // existed before or not.
175   if (existed) return replace_section(product, info_table);
176   else return add_section(product, info_table);
177 }
178
179 bool configured_applications::remove_program(const astring &product,
180     const astring &app_name)
181 {
182   FUNCDEF("remove_program");
183   auto_synchronizer l(*_lock);
184   // if the section's missing, there's nothing to remove...
185   string_table info_table;
186   if (!find_section(product, info_table)) return true;
187   // the section did exist, so remove any existing entry.
188   info_table.whack(app_name);
189   // now call the proper storage function based on whether the section
190   // existed before or not.
191   return replace_section(product, info_table);
192 }
193
194 bool configured_applications::find_section(const astring &section_name,
195     string_table &info_found)
196 {
197   FUNCDEF("find_section");
198   info_found.reset();
199   auto_synchronizer l(*_lock);
200   if (!_sector->find_section(section_name, info_found)) {
201     LOG(section_name + " was not found in the configuration.");
202     return false;
203   }
204   return true;
205 }
206
207 bool configured_applications::add_section(const astring &section_name,
208     const string_table &info_found)
209 {
210   auto_synchronizer l(*_lock);
211   return _sector->add_section(section_name, info_found);
212 }
213
214 bool configured_applications::replace_section(const astring &section_name,
215     const string_table &info_found)
216 {
217   auto_synchronizer l(*_lock);
218   return _sector->replace_section(section_name, info_found);
219 }
220
221 astring configured_applications::make_startup_entry(const astring &product,
222     const astring &parms, bool one_shot)
223 {
224   return astring(PRODUCT_HEADING()) + ASSIGN_TOKEN() + product
225       + SEPARATOR_TEXT() + PARMS_HEADING() + ASSIGN_TOKEN()
226       + parms + SEPARATOR_TEXT() + ONESHOT_HEADING() + ASSIGN_TOKEN()
227       + astring(astring::SPRINTF, "%d", one_shot);
228 }
229
230 bool configured_applications::parse_startup_entry(const astring &info,
231     astring &product, astring &parms, bool &one_shot)
232 {
233   FUNCDEF("parse_startup_section");
234   // parse the items that are in the entry for this program.
235   variable_tokenizer entry_parser(SEPARATOR_TOKEN(), ASSIGN_TOKEN());
236   entry_parser.parse(info);
237   // grab the pertinent bits for the program to be started.
238   product = entry_parser.find(PRODUCT_HEADING());
239   parms = entry_parser.find(PARMS_HEADING());
240 //LOG(astring("parms=") + parms);
241   astring once = entry_parser.find(ONESHOT_HEADING());
242   one_shot = (bool)once.convert(0);
243   // we require the product part at least.
244   if (!product) return false;
245   return true;
246 }
247
248 bool configured_applications::find_entry(const string_table &table,
249     const astring &name, astring &location)
250 {
251   // seek the entry in the table specified.
252   astring *found = table.find(name);
253   if (!found) return false;
254   // found the entry using the name.
255   location = *found;
256   return true;
257 }
258
259 bool configured_applications::add_startup_entry(const astring &product,
260     const astring &app_name, const astring &parameters, int one_shot)
261 {
262   FUNCDEF("add_startup_entry");
263   auto_synchronizer l(*_lock);
264
265   LOG(astring("product \"") + product + "\", application \"" + app_name
266       + (one_shot? astring("\", OneShot") : astring("\", MultiUse")));
267
268   string_table startup_info;
269   if (!find_section(STARTUP_SECTION(), startup_info)) {
270     // if there's no startup section, we can't go on.  that should have been
271     // created during startup of this program.
272     LOG(astring("internal startup section not found!"));
273     return false;
274   }
275
276   astring new_entry = make_startup_entry(product, parameters,
277       one_shot);
278   startup_info.add(app_name, new_entry);
279   if (!replace_section(STARTUP_SECTION(), startup_info))
280     return false;
281 //hmmm: that's a bogus error; this is really an internal fup error.
282
283   return true;
284 }
285
286 bool configured_applications::remove_startup_entry(const astring &product,
287     const astring &app_name)
288 {
289   FUNCDEF("remove_startup_entry");
290   auto_synchronizer l(*_lock);
291
292   LOG(astring("product \"") + product + "\", application \"" + app_name + "\"");
293
294   string_table startup_info;
295   if (!find_section(STARTUP_SECTION(), startup_info)) {
296     // if there's no startup section, we try to add one.
297     add_section(STARTUP_SECTION(), startup_info);
298     // if it still doesn't exist afterwards, we're hosed.
299     if (!find_section(STARTUP_SECTION(), startup_info)) {
300 ///      COMPLAIN_PRODUCT;
301 //massive fup of some unanticipated sort.
302 //complain.
303       return false;
304     }
305   }
306
307   // check that the entry already exists for this program.
308   astring entry_found;
309   if (!find_entry(startup_info, app_name, entry_found)) {
310 //    COMPLAIN_APPLICATION;
311     LOG(astring("no entry was found for ") + app_name);
312     return false;
313   }
314
315   startup_info.whack(app_name);
316   if (!replace_section(STARTUP_SECTION(), startup_info)) {
317 //what happened with that?
318     return false;
319   }
320
321   return true;
322 }
323
324 } //namespace.
325
326