2
* Copyright (C) 2013 Canonical Ltd
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License version 3 as
6
* published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Authored by: Michi Henning <michi.henning@canonical.com>
19
#include "FindFiles.h"
20
#include "SignalThread.h"
22
#include <scopes/internal/MiddlewareFactory.h>
23
#include <scopes/internal/RegistryConfig.h>
24
#include <scopes/internal/RegistryObject.h>
25
#include <scopes/internal/RuntimeConfig.h>
26
#include <scopes/internal/RuntimeImpl.h>
27
#include <scopes/internal/ScopeConfig.h>
28
#include <unity/UnityExceptions.h>
29
#include <unity/util/ResourcePtr.h>
39
using namespace scoperegistry;
41
using namespace unity;
42
using namespace unity::api::scopes;
43
using namespace unity::api::scopes::internal;
44
using namespace unity::util;
46
char const* prog_name;
51
void error(string const& msg)
54
cerr << prog_name << ": " << msg << endl;
57
string strip_suffix(string const& s, string const& suffix)
59
auto s_len = s.length();
60
auto suffix_len = suffix.length();
61
if (s_len >= suffix_len)
63
if (s.compare(s_len - suffix_len, suffix_len, suffix) == 0)
65
return string(s, 0, s_len - suffix_len);
71
// Scan group config dir for .ini files. Each file is assumed to define a single scope group, in a group with key "ScopeGroup".
72
// The key "Scopes" in the group is expected to contain an array of scope names.
73
// For each scope group, the returned vector contains a map with the scope and .ini file names for that group.
74
// If a particular scope appears in more than one group file, the first file found determines which group the scope
75
// belongs too. (Subsequent mentions of a scope already in a group print a warning.)
76
// all_scopes must be the map of all scopes that were originally found in config dir.
77
// For any scopes not in a group, the returned vector contains a map containing just that scope. In other words,
78
// the returned vector contains as many maps as there will be scoperunner processes, with each map containing
79
// the scope name(s) and scope config file(s) for a process.
81
vector<map<string, string>> create_scope_groups(string const& group_dir, map<string, string> all_scopes)
83
set<string> scopes_seen; // Names of scopes that are in a group so far
84
vector<map<string, string>> scope_groups; // One map per scope group
85
if (!group_dir.empty())
87
auto group_files = find_files(group_dir, ".ini");
88
for (auto file : group_files)
90
IniParser::SPtr parser;
93
parser = make_shared<IniParser>(file.c_str());
95
catch (FileException const& e)
97
error(e.to_string() + ": scope group file ignored");
101
vector<string> scopes;
104
scopes = parser->get_string_array("ScopeGroup", "Scopes");
106
catch (LogicException const& e)
108
error("group file \"" + file + ": file ignored: " + e.to_string());
112
// For each scope name in the group, push an element onto the vector of scope groups, but only if we have
113
// not seen that scope name in an earlier group.
115
for (auto scope : scopes)
117
if (scopes_seen.find(scope) != scopes_seen.end())
119
error("ignoring scope \"" + scope + "\" in group file " + file + ": scope is already part of a group");
122
auto it = all_scopes.find(scope);
123
if (it == all_scopes.end())
125
error("ignoring scope \"" + scope + "\" in group file " + file + ": cannot find configuration for this scope");
128
scopes_seen.insert(scope);
132
scope_groups.push_back(map<string, string>());
134
scope_groups.rbegin()->insert(make_pair(scope, it->second));
135
all_scopes.erase(it); // Any scope in a group is removed from all_scopes.
140
// Any scopes remaining in all_scopes at this point were not part of a group and therefore are in a map by themselves.
141
for (auto pair : all_scopes)
143
map<string, string> s;
145
scope_groups.push_back(s);
151
void run_scopes(SignalThread& sigthread,
152
string const& scoperunner_path,
153
string const& config_file,
154
vector<map<string, string>> const& groups)
156
// Cobble together an argv for each scope group so we can fork/exec the scope runner for the group.
157
for (auto group : groups)
159
unique_ptr<char const* []> argv(new char const*[groups.size() + 3]); // Includes room for final NULL element.
160
argv[0] = scoperunner_path.c_str();
161
argv[1] = config_file.c_str();
163
for (auto it = group.begin(); it != group.end(); ++it)
165
argv[i++] = it->second.c_str();
169
// Fork/exec the scoperunner.
171
switch (pid = fork())
175
throw SyscallException("cannot fork", errno);
179
sigthread.reset_sigs(); // Need default signal mask in the new process
180
execv(argv[0], const_cast<char* const*>(argv.get()));
181
throw SyscallException("cannot exec " + scoperunner_path, errno);
186
sigthread.add_child(pid, argv.get());
193
main(int argc, char* argv[])
195
prog_name = basename(argv[0]);
198
cerr << "usage: " << prog_name << " configfile" << endl;
201
char const* const config_file = argv[1];
205
// Run a separate thread to deal with SIGCHLD. This allows us to report when a scope process exits abnormally.
206
SignalThread signal_thread;
210
RuntimeImpl::UPtr runtime = RuntimeImpl::create("Registry", config_file);
212
string identity = runtime->registry_identity();
214
// Collect the registry config data.
218
string mw_configfile;
219
string scope_installdir;
220
string scope_group_configdir;
221
string oem_installdir;
222
string oem_group_configdir;
223
string scoperunner_path;
225
RegistryConfig c(identity, runtime->registry_configfile());
226
mw_kind = c.mw_kind();
227
mw_endpoint = c.endpoint();
228
mw_configfile = c.mw_configfile();
229
scope_installdir = c.scope_installdir();
230
scope_group_configdir = c.scope_group_configdir();
231
oem_installdir = c.oem_installdir();
232
oem_group_configdir = c.oem_group_configdir();
233
scoperunner_path = c.scoperunner_path();
234
} // Release memory for config parser
236
// Look in scope_installdir for scope configuration files.
237
// Scopes that do not permit themselves to be overridden are collected in fixed_scopes.
238
// Scopes that can be overridden are collected in overrideable_scopes.
239
// Each set contains file names (including the ".ini" suffix).
241
map<string, string> fixed_scopes; // Scopes that the OEM cannot override
242
map<string, string> overrideable_scopes; // Scopes that the OEM can override
244
auto config_files = find_scope_config_files(scope_installdir, ".ini");
245
for (auto path : config_files)
247
string file_name = basename(const_cast<char*>(string(path).c_str())); // basename() modifies its argument
248
string scope_name = strip_suffix(file_name, ".ini");
249
ScopeConfig config(path);
250
if (config.overrideable())
252
overrideable_scopes[scope_name] = path;
256
fixed_scopes[scope_name] = path;
260
map<string, string> oem_scopes; // Additional scopes provided by the OEM (including overriding ones)
261
if (!oem_installdir.empty())
263
auto oem_paths = find_scope_config_files(oem_installdir, ".ini");
264
for (auto path : oem_paths)
266
string file_name = basename(const_cast<char*>(string(path).c_str())); // basename() modifies its argument
267
string scope_name = strip_suffix(file_name, ".ini");
268
if (fixed_scopes.find(scope_name) == fixed_scopes.end())
270
overrideable_scopes.erase(scope_name); // Only keep scopes that are not overridden by OEM
271
oem_scopes[scope_name] = path;
275
error("ignoring non-overrideable scope config \"" + file_name + "\" in OEM directory " + oem_installdir);
280
// Combine fixed_scopes and overrideable scopes now.
281
// overrideable_scopes only contains scopes that were *not* overridden by the OEM.
282
map<string, string> all_scopes;
283
all_scopes.insert(overrideable_scopes.begin(), overrideable_scopes.end());
284
all_scopes.insert(fixed_scopes.begin(), fixed_scopes.end());
286
// Clear memory for original maps.
287
map<string, string>().swap(fixed_scopes);
288
map<string, string>().swap(overrideable_scopes);
290
// Create the set of scope groups for Canonical and OEM scopes.
291
auto canonical_groups = create_scope_groups(scope_group_configdir, all_scopes);
292
auto oem_groups = create_scope_groups(oem_group_configdir, all_scopes);
294
MiddlewareBase::SPtr middleware = runtime->factory()->create(identity, mw_kind, mw_configfile);
296
// Add the registry implementation to the middleware.
297
RegistryObject::SPtr registry(new RegistryObject);
298
middleware->add_registry_object(runtime->registry_identity(), registry);
300
// Add a proxy for each scope to the lookup table.
301
// We do this before starting any of the scopes, so aggregating scopes don't get a lookup failure if
302
// they look for another scope in the registry.
303
for (auto pair : all_scopes)
305
registry->add(pair.first, middleware->create_scope_proxy(pair.first));
308
// Start a scoperunner for each Canonical scope group and add the corresponding proxies to the registry
309
run_scopes(signal_thread, scoperunner_path, config_file, canonical_groups);
311
// Start a scoperunner for each OEM scope group and add the corresponding proxies to the registry
312
// TODO: run_scopes(signal_thread, scoperunner_path, config_file, oem_groups);
314
// Wait until we are done.
315
middleware->wait_for_shutdown();
317
catch (unity::Exception const& e)
319
error(e.to_string());
322
catch (std::exception const& e)
327
catch (string const& e)
329
error("fatal error: " + e);
332
catch (char const* e)
334
error(string("fatal error: ") + e);
339
error("terminated due to unknown exception");