~unity-api-team/unity-scopes-api/child-scopes-option

« back to all changes in this revision

Viewing changes to scoperegistry/scoperegistry.cpp

  • Committer: Michi Henning
  • Date: 2013-11-19 02:51:46 UTC
  • mto: This revision was merged to the branch mainline in revision 62.
  • Revision ID: michi.henning@canonical.com-20131119025146-kglcalpphl4ozzk7
Fixed scoperegistry to use new scoperunner and to figure out which scopes to run from config files.
Still to do:
- deal with overrides and OEM scopes, particularly the grouping aspect.
- SignalThread needs to invoke a callback for clean shut-down on receipt of SIGINT.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2013 Canonical Ltd
 
3
 *
 
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.
 
7
 *
 
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.
 
12
 *
 
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/>.
 
15
 *
 
16
 * Authored by: Michi Henning <michi.henning@canonical.com>
 
17
 */
 
18
 
 
19
#include "FindFiles.h"
 
20
#include "SignalThread.h"
 
21
 
 
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>
 
30
 
 
31
#include <algorithm>
 
32
#include <cassert>
 
33
#include <iostream>
 
34
#include <map>
 
35
#include <set>
 
36
#include <libgen.h>
 
37
#include <unistd.h>
 
38
 
 
39
using namespace scoperegistry;
 
40
using namespace std;
 
41
using namespace unity;
 
42
using namespace unity::api::scopes;
 
43
using namespace unity::api::scopes::internal;
 
44
using namespace unity::util;
 
45
 
 
46
char const* prog_name;
 
47
 
 
48
namespace
 
49
{
 
50
 
 
51
void error(string const& msg)
 
52
{
 
53
    assert(!msg.empty());
 
54
    cerr << prog_name << ": " << msg << endl;
 
55
}
 
56
 
 
57
string strip_suffix(string const& s, string const& suffix)
 
58
{
 
59
    auto s_len = s.length();
 
60
    auto suffix_len = suffix.length();
 
61
    if (s_len >= suffix_len)
 
62
    {
 
63
        if (s.compare(s_len - suffix_len, suffix_len, suffix) == 0)
 
64
        {
 
65
            return string(s, 0, s_len - suffix_len);
 
66
        }
 
67
    }
 
68
    return s;
 
69
}
 
70
 
 
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.
 
80
 
 
81
vector<map<string, string>> create_scope_groups(string const& group_dir, map<string, string> all_scopes)
 
82
{
 
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())
 
86
    {
 
87
        auto group_files = find_files(group_dir, ".ini");
 
88
        for (auto file : group_files)
 
89
        {
 
90
            IniParser::SPtr parser;
 
91
            try
 
92
            {
 
93
                parser = make_shared<IniParser>(file.c_str());
 
94
            }
 
95
            catch (FileException const& e)
 
96
            {
 
97
                error(e.to_string() + ": scope group file ignored");
 
98
                continue;
 
99
            }
 
100
 
 
101
            vector<string> scopes;
 
102
            try
 
103
            {
 
104
                scopes = parser->get_string_array("ScopeGroup", "Scopes");
 
105
            }
 
106
            catch (LogicException const& e)
 
107
            {
 
108
                error("group file \"" + file + ": file ignored: " + e.to_string());
 
109
                continue;
 
110
            }
 
111
 
 
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.
 
114
            bool once = false;
 
115
            for (auto scope : scopes)
 
116
            {
 
117
                if (scopes_seen.find(scope) != scopes_seen.end())
 
118
                {
 
119
                    error("ignoring scope \"" + scope + "\" in group file " + file + ": scope is already part of a group");
 
120
                    continue;
 
121
                }
 
122
                auto it = all_scopes.find(scope);
 
123
                if (it == all_scopes.end())
 
124
                {
 
125
                    error("ignoring scope \"" + scope + "\" in group file " + file + ": cannot find configuration for this scope");
 
126
                    continue;
 
127
                }
 
128
                scopes_seen.insert(scope);
 
129
                if (!once)
 
130
                {
 
131
                    once = true;
 
132
                    scope_groups.push_back(map<string, string>());
 
133
                }
 
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.
 
136
            }
 
137
        }
 
138
    }
 
139
 
 
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)
 
142
    {
 
143
        map<string, string> s;
 
144
        s.insert(pair);
 
145
        scope_groups.push_back(s);
 
146
    }
 
147
 
 
148
    return scope_groups;
 
149
}
 
150
 
 
151
void run_scopes(SignalThread& sigthread,
 
152
                string const& scoperunner_path,
 
153
                string const& config_file,
 
154
                vector<map<string, string>> const& groups)
 
155
{
 
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)
 
158
    {
 
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();
 
162
        int i = 2;
 
163
        for (auto it = group.begin(); it != group.end(); ++it)
 
164
        {
 
165
            argv[i++] = it->second.c_str();
 
166
        }
 
167
        argv[i] = NULL;
 
168
 
 
169
        // Fork/exec the scoperunner.
 
170
        pid_t pid;
 
171
        switch (pid = fork())
 
172
        {
 
173
            case -1:
 
174
            {
 
175
                throw SyscallException("cannot fork", errno);
 
176
            }
 
177
            case 0: // child
 
178
            {
 
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);
 
182
            }
 
183
        }
 
184
 
 
185
        // Parent
 
186
        sigthread.add_child(pid, argv.get());
 
187
    }
 
188
}
 
189
 
 
190
} // namespace
 
191
 
 
192
int
 
193
main(int argc, char* argv[])
 
194
{
 
195
    prog_name = basename(argv[0]);
 
196
    if (argc != 2)
 
197
    {
 
198
        cerr << "usage: " << prog_name << " configfile" << endl;
 
199
        return 2;
 
200
    }
 
201
    char const* const config_file = argv[1];
 
202
 
 
203
    int exit_status = 0;
 
204
 
 
205
    // Run a separate thread to deal with SIGCHLD. This allows us to report when a scope process exits abnormally.
 
206
    SignalThread signal_thread;
 
207
 
 
208
    try
 
209
    {
 
210
        RuntimeImpl::UPtr runtime = RuntimeImpl::create("Registry", config_file);
 
211
 
 
212
        string identity = runtime->registry_identity();
 
213
 
 
214
        // Collect the registry config data.
 
215
 
 
216
        string mw_kind;
 
217
        string mw_endpoint;
 
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;
 
224
        {
 
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
 
235
 
 
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).
 
240
 
 
241
        map<string, string> fixed_scopes;           // Scopes that the OEM cannot override
 
242
        map<string, string> overrideable_scopes;    // Scopes that the OEM can override
 
243
 
 
244
        auto config_files = find_scope_config_files(scope_installdir, ".ini");
 
245
        for (auto path : config_files)
 
246
        {
 
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())
 
251
            {
 
252
                overrideable_scopes[scope_name] = path;
 
253
            }
 
254
            else
 
255
            {
 
256
                fixed_scopes[scope_name] = path;
 
257
            }
 
258
        }
 
259
 
 
260
        map<string, string> oem_scopes;             // Additional scopes provided by the OEM (including overriding ones)
 
261
        if (!oem_installdir.empty())
 
262
        {
 
263
            auto oem_paths = find_scope_config_files(oem_installdir, ".ini");
 
264
            for (auto path : oem_paths)
 
265
            {
 
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())
 
269
                {
 
270
                    overrideable_scopes.erase(scope_name);                // Only keep scopes that are not overridden by OEM
 
271
                    oem_scopes[scope_name] = path;
 
272
                }
 
273
                else
 
274
                {
 
275
                    error("ignoring non-overrideable scope config \"" + file_name + "\" in OEM directory " + oem_installdir);
 
276
                }
 
277
            }
 
278
        }
 
279
 
 
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());
 
285
 
 
286
        // Clear memory for original maps.
 
287
        map<string, string>().swap(fixed_scopes);
 
288
        map<string, string>().swap(overrideable_scopes);
 
289
 
 
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);
 
293
 
 
294
        MiddlewareBase::SPtr middleware = runtime->factory()->create(identity, mw_kind, mw_configfile);
 
295
 
 
296
        // Add the registry implementation to the middleware.
 
297
        RegistryObject::SPtr registry(new RegistryObject);
 
298
        middleware->add_registry_object(runtime->registry_identity(), registry);
 
299
 
 
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)
 
304
        {
 
305
            registry->add(pair.first, middleware->create_scope_proxy(pair.first));
 
306
        }
 
307
 
 
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);
 
310
 
 
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);
 
313
 
 
314
        // Wait until we are done.
 
315
        middleware->wait_for_shutdown();
 
316
    }
 
317
    catch (unity::Exception const& e)
 
318
    {
 
319
        error(e.to_string());
 
320
        exit_status = 1;
 
321
    }
 
322
    catch (std::exception const& e)
 
323
    {
 
324
        error(e.what());
 
325
        exit_status = 1;
 
326
    }
 
327
    catch (string const& e)
 
328
    {
 
329
        error("fatal error: " + e);
 
330
        exit_status = 1;
 
331
    }
 
332
    catch (char const* e)
 
333
    {
 
334
        error(string("fatal error: ") + e);
 
335
        exit_status = 1;
 
336
    }
 
337
    catch (...)
 
338
    {
 
339
        error("terminated due to unknown exception");
 
340
        exit_status = 1;
 
341
    }
 
342
 
 
343
    return exit_status;
 
344
}