2 Copyright (c) 2016 InternetWide.org and the ARPA2.net project
3 All rights reserved. See file LICENSE for exact terms (2-clause BSD license).
5 Adriaan de Groot <groot@kde.org>
10 #include "../pulleyback.h"
16 #include <unordered_map>
21 #ifndef PULLEY_BACKEND_DIR
23 #define PREFIX "/usr/local"
25 #define PULLEY_BACKEND_DIR PREFIX "/share/steamworks/pulleyback/"
28 static const char plugindir[] = PULLEY_BACKEND_DIR;
30 static_assert(sizeof(plugindir) > 1, "Prefix / plugin directory path is weird.");
32 // static_assert(plugindir[0] == '/', "Backend must be absolute dir.");
33 // static_assert(plugindir[sizeof(plugindir)-1] == '/', "Backend must end in /.");
37 * Helper class when loading the API from a DLL. Uses dlfunc()
38 * to resolve functions and stores them in referenced function
39 * pointers. If any of the name-resolvings fails, the referenced
40 * boolean valid is set to false. Use multiple FuncKeepers
41 * all referencing the same valid bool to load an entire
42 * API and check if it's valid.
44 template<typename funcptr> class FuncKeeper {
49 FuncKeeper(void *handle, const char *name, bool& valid, funcptr*& func) :
53 dlfunc_t f = dlfunc(handle, name);
56 func = reinterpret_cast<funcptr*>(f);
61 auto& log = SteamWorks::Logging::getLogger("steamworks.pulleyback");
62 log.warnStream() << "Function " << name << " not found.";
77 class SteamWorks::PulleyBack::Loader::Private
85 decltype(pulleyback_open)* m_pulleyback_open;
86 decltype(pulleyback_close)* m_pulleyback_close;
87 decltype(pulleyback_add)* m_pulleyback_add;
88 decltype(pulleyback_del)* m_pulleyback_del;
89 decltype(pulleyback_reset)* m_pulleyback_reset;
90 decltype(pulleyback_prepare)* m_pulleyback_prepare; // OPTIONAL
91 decltype(pulleyback_commit)* m_pulleyback_commit;
92 decltype(pulleyback_rollback)* m_pulleyback_rollback;
93 decltype(pulleyback_collaborate) *m_pulleyback_collaborate;
95 Private(const std::string& name) :
100 auto& log = SteamWorks::Logging::getLogger("steamworks.pulleyback");
101 log.debugStream() << "Trying to load backend '" << name << '\'';
105 snprintf(soname, sizeof(soname), "%s", name.c_str());
107 assert(plugindir[0] == '/');
108 assert(plugindir[sizeof(plugindir)-2] == '/'); // -1 is the NUL, -2 is trailing /
110 if (name.find('/') != std::string::npos)
112 log.errorStream() << " .. illegal / in backend-name.";
116 char soname[sizeof(plugindir) + 128];
117 snprintf(soname, sizeof(soname), "%spulleyback_%s.so", plugindir, name.c_str());
120 soname[sizeof(soname)-1] = 0;
121 log.debugStream() << "Trying to load '" << soname << '\'';
123 m_handle = dlopen(soname, RTLD_NOW | RTLD_LOCAL);
124 if (m_handle == nullptr)
126 log.errorStream() << " .. could not load backend shared-object. " << dlerror();
131 // As far as we know .. the FuncKeepers can modify it. Put them in a block
132 // so that their destructors have run before we (possibly) dlclose the
133 // shared library their function-pointers point into.
135 bool optionalvalid = true;
137 FuncKeeper<decltype(pulleyback_open)> fk0(m_handle, "pulleyback_open", m_valid, m_pulleyback_open);
138 FuncKeeper<decltype(pulleyback_close)> fk1(m_handle, "pulleyback_close", m_valid, m_pulleyback_close);
139 FuncKeeper<decltype(pulleyback_add)> fk2(m_handle, "pulleyback_add", m_valid, m_pulleyback_add);
140 FuncKeeper<decltype(pulleyback_del)> fk3(m_handle, "pulleyback_del", m_valid, m_pulleyback_del);
141 FuncKeeper<decltype(pulleyback_reset)> fk4(m_handle, "pulleyback_reset", m_valid, m_pulleyback_reset);
142 FuncKeeper<decltype(pulleyback_prepare)> fk5(m_handle, "pulleyback_prepare", optionalvalid, m_pulleyback_prepare);
143 FuncKeeper<decltype(pulleyback_commit)> fk6(m_handle, "pulleyback_commit", m_valid, m_pulleyback_commit);
144 FuncKeeper<decltype(pulleyback_rollback)> fk7(m_handle, "pulleyback_rollback", m_valid, m_pulleyback_rollback);
145 FuncKeeper<decltype(pulleyback_collaborate)> fk8(m_handle, "pulleyback_collaborate", m_valid, m_pulleyback_collaborate);
147 optionalvalid &= m_valid; // If any required func missing, the optionals are invalid too
150 // If any function has not been resolved, the FuncKeeper will have set m_valid to false
160 auto& log = SteamWorks::Logging::getLogger("steamworks.pulleyback");
161 log.debugStream() << "Unloaded priv " << name();
163 if (m_handle != nullptr)
171 bool is_valid() const { return m_valid; }
172 std::string name() const { return m_name; }
174 static std::shared_ptr<Private> get_loader_private(const std::string& name);
178 std::shared_ptr< SteamWorks::PulleyBack::Loader::Private > SteamWorks::PulleyBack::Loader::Private::get_loader_private(const std::string& name)
180 static std::unordered_map<std::string, std::weak_ptr<SteamWorks::PulleyBack::Loader::Private> > loaders;
182 auto p = loaders[name].lock();
185 p = std::make_shared<SteamWorks::PulleyBack::Loader::Private>(name);
192 SteamWorks::PulleyBack::Loader::Loader(const std::string& name) :
193 d(Private::get_loader_private(name))
195 auto& log = SteamWorks::Logging::getLogger("steamworks.pulleyback");
196 log.debugStream() << "Loaded " << name << " valid? " << (d->is_valid() ? "yes" : "no");
197 log.debugStream() << " .. open @" << (void *)d->m_pulleyback_open << " close @" << (void *)d->m_pulleyback_close;
198 log.debugStream() << " .. add @" << (void *)d->m_pulleyback_add;
201 SteamWorks::PulleyBack::Loader::~Loader()
203 auto& log = SteamWorks::Logging::getLogger("steamworks.pulleyback");
204 log.debugStream() << "Unloaded " << d->name();
207 SteamWorks::PulleyBack::Instance SteamWorks::PulleyBack::Loader::get_instance(int argc, char** argv, int varc)
209 return Instance(d, argc, argv, varc);
212 SteamWorks::PulleyBack::Instance SteamWorks::PulleyBack::Loader::get_instance(SteamWorks::PulleyScript::BackendParameters& parameters)
214 return Instance(d, parameters.argc, parameters.argv, parameters.varc);
217 bool SteamWorks::PulleyBack::Loader::is_valid() const
219 return d->is_valid();
224 SteamWorks::PulleyBack::Instance::Instance(std::shared_ptr<Loader::Private>& parent_d, int argc, char** argv, int varc) :
230 m_handle = d->m_pulleyback_open(argc, argv, varc);
231 auto& log = SteamWorks::Logging::getLogger("steamworks.pulleyback");
232 log.debugStream() << "Got instance handle @" << m_handle;
236 SteamWorks::PulleyBack::Instance::~Instance()
238 if (m_handle and d->is_valid())
240 d->m_pulleyback_close(m_handle);
244 bool SteamWorks::PulleyBack::Instance::is_valid() const
246 return m_handle && d->is_valid();
249 std::string SteamWorks::PulleyBack::Instance::name() const
254 int SteamWorks::PulleyBack::Instance::add(der_t* forkdata)
258 auto& log = SteamWorks::Logging::getLogger("steamworks.pulleyback");
259 log.debugStream() << "Calling into instance " << name() << " add@" << (void *)d->m_pulleyback_add << " handle@" << m_handle;
260 return d->m_pulleyback_add(m_handle, forkdata);
265 int SteamWorks::PulleyBack::Instance::del(der_t* forkdata)
269 auto& log = SteamWorks::Logging::getLogger("steamworks.pulleyback");
270 log.debugStream() << "Calling into instance " << name() << " del@" << (void *)d->m_pulleyback_del << " handle@" << m_handle;
271 return d->m_pulleyback_del(m_handle, forkdata);