aboutsummaryrefslogtreecommitdiff
path: root/mod/build-config-module.cxx
blob: 1f4ad42d647c876f257d3b0b67d81f8a9e3e8eac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// file      : mod/build-config-module.cxx -*- C++ -*-
// license   : MIT; see accompanying LICENSE file

#include <mod/build-config-module.hxx>

#include <errno.h> // EIO

#include <map>
#include <sstream>

#include <libbutl/sha256.hxx>
#include <libbutl/utility.hxx>      // throw_generic_error()
#include <libbutl/openssl.hxx>
#include <libbutl/filesystem.hxx>   // dir_iterator, dir_entry

namespace brep
{
  using namespace std;
  using namespace butl;
  using namespace bpkg;

  // Return pointer to the shared build target configurations instance,
  // creating one on the first call. Throw tab_parsing on parsing error,
  // io_error on the underlying OS error. Note: not thread-safe.
  //
  static shared_ptr<const build_target_configs>
  shared_build_config (const path& p)
  {
    static map<path, weak_ptr<build_target_configs>> configs;

    auto i (configs.find (p));
    if (i != configs.end ())
    {
      if (shared_ptr<build_target_configs> c = i->second.lock ())
        return c;
    }

    shared_ptr<build_target_configs> c (
      make_shared<build_target_configs> (bbot::parse_buildtab (p)));

    configs[p] = c;
    return c;
  }

  // Return pointer to the shared build bot agent public keys map, creating
  // one on the first call. Throw system_error on the underlying openssl or OS
  // error. Note: not thread-safe.
  //
  using bot_agent_key_map = map<string, path>;

  static shared_ptr<const bot_agent_key_map>
  shared_bot_agent_keys (const options::openssl_options& o, const dir_path& d)
  {
    static map<dir_path, weak_ptr<bot_agent_key_map>> keys;

    auto i (keys.find (d));
    if (i != keys.end ())
    {
      if (shared_ptr<bot_agent_key_map> k = i->second.lock ())
        return k;
    }

    shared_ptr<bot_agent_key_map> ak (make_shared<bot_agent_key_map> ());

    // Intercept exception handling to make error descriptions more
    // informative.
    //
    // Path of the key being converted. Used for diagnostics.
    //
    path p;

    try
    {
      for (const dir_entry& de: dir_iterator (d, dir_iterator::no_follow))
      {
        if (de.path ().extension () == "pem" &&
            de.type () == entry_type::regular)
        {
          p = d / de.path ();

          openssl os (p, path ("-"), 2,
                      process_env (o.openssl (), o.openssl_envvar ()),
                      "pkey",
                      o.openssl_option (), "-pubin", "-outform", "DER");

          string fp (sha256 (os.in).string ());
          os.in.close ();

          if (!os.wait ())
            throw io_error ("");

          ak->emplace (move (fp), move (p));
        }
      }
    }
    catch (const io_error&)
    {
      ostringstream os;
      os << "unable to convert bbot agent pubkey " << p;
      throw_generic_error (EIO, os.str ().c_str ());
    }
    catch (const process_error& e)
    {
      ostringstream os;
      os << "unable to convert bbot agent pubkey " << p;
      throw_generic_error (e.code ().value (), os.str ().c_str ());
    }
    catch (const system_error& e)
    {
      ostringstream os;
      os << "unable to iterate over agents keys directory '" << d << "'";
      throw_generic_error (e.code ().value (), os.str ().c_str ());
    }

    keys[d] = ak;
    return ak;
  }

  void build_config_module::
  init (const options::build& bo)
  {
    try
    {
      target_conf_ = shared_build_config (bo.build_config ());
    }
    catch (const io_error& e)
    {
      ostringstream os;
      os << "unable to read build configuration '" << bo.build_config ()
         << "': " << e;

      throw_generic_error (EIO, os.str ().c_str ());
    }

    if (bo.build_bot_agent_keys_specified ())
      bot_agent_key_map_ =
        shared_bot_agent_keys (bo, bo.build_bot_agent_keys ());

    using conf_map_type = map<build_target_config_id,
                              const build_target_config*>;

    conf_map_type conf_map;

    for (const auto& c: *target_conf_)
      conf_map[build_target_config_id {c.target, c.name}] = &c;

    target_conf_map_ = make_shared<conf_map_type> (move (conf_map));
  }

  bool build_config_module::
  derived (const string& c, const char* bc) const
  {
    if (c == bc)
      return true;

    // Go through base classes.
    //
    const map<string, string>& im (target_conf_->class_inheritance_map);

    for (auto i (im.find (c)); i != im.end (); )
    {
      const string& base (i->second);

      if (base == bc)
        return true;

      i = im.find (base);
    }

    return false;
  }

  bool build_config_module::
  belongs (const build_target_config& cfg, const char* cls) const
  {
    for (const string& c: cfg.classes)
    {
      if (derived (c, cls))
        return true;
    }

    return false;
  }
}