-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathhcv_plugins.cc
238 lines (220 loc) · 9.61 KB
/
hcv_plugins.cc
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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/****************************************************************
* file hcv_plugins.cc
*
* Description:
* Plugins machinery of https://github.com/bstarynk/helpcovid
*
* Author(s):
* © Copyright 2020
* Basile Starynkevitch <[email protected]>
* Abhishek Chakravarti <[email protected]>
*
*
* License:
* This HELPCOVID program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "hcv_header.hh"
extern "C" const char hcv_plugins_gitid[] = HELPCOVID_GITID;
extern "C" const char hcv_plugins_date[] = __DATE__;
static std::recursive_mutex hcv_plugin_mtx;
struct Hcv_plugin
{
std::string hcvpl_name;
void* hcvpl_handle; // result of dlopen
const char* hcvpl_arg; // argument passed to plugin
std::string hcvpl_gitid;
std::string hcvpl_license;
hcvplugin_web_initializer_sig_t* hcvpl_initweb;
hcvplugin_database_initializer_sig_t* hcvpl_initdatabase;
};
std::vector<Hcv_plugin> hcv_plugin_vect;
std::vector<std::string>
hcv_get_loaded_plugins_vector(void)
{
std::vector<std::string> res;
std::lock_guard<std::recursive_mutex> guplug(hcv_plugin_mtx);
res.reserve(hcv_plugin_vect.size());
for (auto &pl: hcv_plugin_vect)
res.push_back(pl.hcvpl_name);
return res;
} // end hcv_get_loaded_plugins_vector
void hcv_load_plugin(const char*plugin_name, const char*plugin_arg)
{
if (!plugin_name || !plugin_name[0])
HCV_FATALOUT("hcv_load_plugin: missing plugin name");
if (strlen(plugin_name) > HCV_PLUGIN_NAME_MAXLEN)
HCV_FATALOUT("too long plugin name: " << plugin_name);
for (const char*pc = plugin_name; *pc; pc++)
if (!isalnum(*pc) && *pc != '_')
HCV_FATALOUT("invalid plugin name " << plugin_name);
std::lock_guard<std::recursive_mutex> guplug(hcv_plugin_mtx);
HCV_DEBUGOUT("hcv_load_plugin " << plugin_name << " starting");
std::string pluginstr(plugin_name);
for (auto& pl : hcv_plugin_vect)
if (pl.hcvpl_name == pluginstr)
HCV_FATALOUT("hcv_load_plugin duplicate plugin " << plugin_name);
char sobuf[HCV_PLUGIN_NAME_MAXLEN + 48];
memset(sobuf, 0, sizeof(sobuf));
snprintf(sobuf, sizeof(sobuf), HCV_PLUGIN_PREFIX "%s" HCV_PLUGIN_SUFFIX,
plugin_name);
HCV_ASSERT(sobuf[sizeof(sobuf)-1]==(char)0);
HCV_ASSERT(strstr(sobuf, ".so") != nullptr);
HCV_DEBUGOUT("hcv_load_plugin " << plugin_name << " sobuf=" << sobuf);
/// Notice that dlopen(3) handles specially paths without /; in that case we prepend
/// "./" if file exists and is an ELF shared library for 64 bits
/// (x86-64 a.k.a. amd64 ABI)
if (!strchr(sobuf, '/') && strlen(sobuf)<sizeof(sobuf)-4)
{
bool needprepend = false;
if (FILE* fplug = fopen(sobuf, "r"))
{
Elf64_Ehdr elfheader;
memset (&elfheader, 0, sizeof(elfheader));
if (fread(&elfheader, sizeof(elfheader), 1, fplug) > 0)
{
needprepend =
elfheader.e_ident[EI_MAG0] == ELFMAG0
&& elfheader.e_ident[EI_MAG1] == ELFMAG1
&& elfheader.e_ident[EI_MAG2] == ELFMAG2
&& elfheader.e_ident[EI_MAG3] == ELFMAG3
&& elfheader.e_ident[EI_CLASS] == ELFCLASS64 /*since x86-64*/
&& elfheader.e_type == ET_DYN;
};
if (needprepend)
{
memmove(sobuf+2, sobuf, strlen(sobuf));
sobuf[0] = '.';
sobuf[1] = '/';
HCV_DEBUGOUT("hcv_load_plugin prepended sobuf=" << sobuf);
}
else
HCV_DEBUGOUT("hcv_load_plugin no prepending kept sobuf=" << sobuf);
fclose(fplug);
}
}
HCV_SYSLOGOUT(LOG_INFO, "hcv_load_plugin " << plugin_name << " is dlopen-ing " << sobuf);
void* dlh = dlopen(sobuf, RTLD_NOW | RTLD_DEEPBIND);
if (!dlh)
HCV_FATALOUT("hcv_load_plugin " << plugin_name << " failed to dlopen " << sobuf
<< " : " << dlerror());
HCV_DEBUGOUT("hcv_load_plugin dlopened " << sobuf);
const char* plgname
= reinterpret_cast<const char*>(dlsym(dlh,
"hcvplugin_name"));
if (!plgname)
HCV_FATALOUT("hcv_load_plugin " << plugin_name << " plugin " << sobuf
<< " has no symbol hcvplugin_name: " << dlerror());
if (strcmp(plgname, plugin_name))
HCV_FATALOUT("hcv_load_plugin " << plugin_name << " plugin has unexpected hcvplugin_name " << plgname);
const char* plglicense
= reinterpret_cast<const char*>(dlsym(dlh,
"hcvplugin_gpl_compatible_license"));
if (!plglicense)
HCV_FATALOUT("hcv_load_plugin " << plugin_name << " plugin " << sobuf
<< " has no symbol hcvplugin_gpl_compatible_license: " << dlerror());
const char* plgapi
= reinterpret_cast<const char*>(dlsym(dlh, "hcvplugin_gitapi"));
if (!plgapi)
HCV_FATALOUT("hcv_load_plugin " << plugin_name << " plugin " << sobuf
<< " has no symbol hcvplugin_gitapi: " << dlerror());
const char* plgversion
= reinterpret_cast<const char*>( dlsym(dlh, "hcvplugin_version"));
if (!plgversion)
HCV_FATALOUT("hcv_load_plugin " << plugin_name << " plugin " << sobuf
<< " has no symbol hcvplugin_version: " << dlerror());
void* plgwebinit = dlsym(dlh, "hcvplugin_initialize_web");
if (!plgwebinit)
HCV_FATALOUT("hcv_load_plugin " << plugin_name << " plugin " << sobuf
<< " has no symbol hcvplugin_initialize_web: " << dlerror());
HCV_SYSLOGOUT(LOG_NOTICE, "hcv_load_plugin " << plugin_name
<< " dlopened " << sobuf << " with license " << plglicense
<< " gitapi " << plgapi << " and version " << plgversion);
if (strncmp(plgapi, hcv_gitid, 24))
HCV_SYSLOGOUT(LOG_WARNING, "hcv_load_plugin " << plugin_name
<< " dlopened " << sobuf
<< " with gitapi mismatch - expected " << hcv_gitid
<< " but got " << plgapi);
void* plgdatabaseinit = dlsym(dlh, "hcvplugin_initialize_database");
if (plgdatabaseinit)
HCV_SYSLOGOUT(LOG_NOTICE, "hcv_load_plugin " << plugin_name
<< " has database initializer");
else
HCV_SYSLOGOUT(LOG_NOTICE, "hcv_load_plugin " << plugin_name
<< " without database initializer: " << dlerror());
hcv_plugin_vect.emplace_back
(Hcv_plugin
{
.hcvpl_name = pluginstr,
.hcvpl_handle= dlh,
.hcvpl_arg= plugin_arg,
.hcvpl_gitid= std::string(plgapi),
.hcvpl_license = std::string(plglicense),
.hcvpl_initweb = reinterpret_cast<hcvplugin_web_initializer_sig_t*>(plgwebinit),
.hcvpl_initdatabase = reinterpret_cast<hcvplugin_database_initializer_sig_t*>(plgdatabaseinit)
});
////
HCV_DEBUGOUT("hcv_load_plugin done " << pluginstr << " rank#"
<< hcv_plugin_vect.size());
} // end hcv_load_plugin
void
hcv_initialize_plugins_for_web(httplib::Server*webserv)
{
HCV_ASSERT(webserv != nullptr);
std::lock_guard<std::recursive_mutex> guplug(hcv_plugin_mtx);
auto nbplugins = hcv_plugin_vect.size();
HCV_DEBUGOUT("hcv_initialize_plugins_for_web starting with " << nbplugins
<< " plugins");
if (nbplugins == 0)
return;
for (auto& pl : hcv_plugin_vect)
{
HCV_DEBUGOUT("hcv_initialize_plugins_for_web initializing " << pl.hcvpl_name
<< (pl.hcvpl_arg?" with argument ":" without argument")
<< (pl.hcvpl_arg?:"."));
(*pl.hcvpl_initweb)(webserv,pl.hcvpl_arg);
HCV_SYSLOGOUT(LOG_INFO, "hcv_initialize_plugins_for_web initialized plugin "
<< pl.hcvpl_name << (pl.hcvpl_arg?" with argument ":" without argument")
<< (pl.hcvpl_arg?:"."));
};
HCV_SYSLOGOUT(LOG_INFO, "hcv_initialize_plugins_for_web done with " << nbplugins
<< " plugins");
} // end hcv_initialize_plugins_for_web
void
hcv_initialize_plugins_for_database(pqxx::connection*dbconn)
{
HCV_ASSERT(dbconn != nullptr);
std::lock_guard<std::recursive_mutex> guplug(hcv_plugin_mtx);
auto nbplugins = hcv_plugin_vect.size();
HCV_DEBUGOUT("hcv_initialize_plugins_for_database starting with " << nbplugins
<< " plugins");
if (nbplugins == 0)
return;
int cnt = 0;
for (auto& pl : hcv_plugin_vect)
{
if (!pl.hcvpl_initdatabase)
continue;
HCV_DEBUGOUT("hcv_initialize_plugins_for_database initializing " << pl.hcvpl_name
<< (pl.hcvpl_arg?" with argument ":" without argument")
<< (pl.hcvpl_arg?:"."));
(*pl.hcvpl_initdatabase)(dbconn,pl.hcvpl_arg);
cnt++;
HCV_SYSLOGOUT(LOG_INFO, "hcv_initialize_plugins_for_database initialized plugin "
<< pl.hcvpl_name << (pl.hcvpl_arg?" with argument ":" without argument")
<< (pl.hcvpl_arg?:"."));
};
HCV_SYSLOGOUT(LOG_INFO, "hcv_initialize_plugins_for_database done with " << nbplugins
<< " plugins " << " with " << cnt << " database initializations");
} // end hcv_initialize_plugins_for_database
/****************** end of file hcv_plugins.cc of github.com/bstarynk/helpcovid **/