Chris@2
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@2
|
2
|
Chris@2
|
3 #include "plugincandidates.h"
|
Chris@2
|
4
|
Chris@2
|
5 #include <set>
|
Chris@2
|
6 #include <stdexcept>
|
Chris@2
|
7 #include <iostream>
|
Chris@2
|
8
|
Chris@2
|
9 #include <QProcess>
|
Chris@2
|
10 #include <QDir>
|
Chris@2
|
11
|
Chris@4
|
12 #if defined(_WIN32)
|
Chris@2
|
13 #define PLUGIN_GLOB "*.dll"
|
Chris@4
|
14 #elif defined(__APPLE__)
|
Chris@2
|
15 #define PLUGIN_GLOB "*.dylib *.so"
|
Chris@2
|
16 #else
|
Chris@2
|
17 #define PLUGIN_GLOB "*.so"
|
Chris@2
|
18 #endif
|
Chris@2
|
19
|
Chris@2
|
20 using namespace std;
|
Chris@2
|
21
|
Chris@2
|
22 PluginCandidates::PluginCandidates(string helperExecutableName) :
|
Chris@2
|
23 m_helper(helperExecutableName)
|
Chris@2
|
24 {
|
Chris@2
|
25 }
|
Chris@2
|
26
|
Chris@2
|
27 vector<string>
|
Chris@4
|
28 PluginCandidates::getCandidateLibrariesFor(string tag) const
|
Chris@2
|
29 {
|
Chris@4
|
30 if (m_candidates.find(tag) == m_candidates.end()) return {};
|
Chris@4
|
31 else return m_candidates.at(tag);
|
Chris@2
|
32 }
|
Chris@2
|
33
|
Chris@2
|
34 vector<PluginCandidates::FailureRec>
|
Chris@4
|
35 PluginCandidates::getFailedLibrariesFor(string tag) const
|
Chris@2
|
36 {
|
Chris@4
|
37 if (m_failures.find(tag) == m_failures.end()) return {};
|
Chris@4
|
38 else return m_failures.at(tag);
|
Chris@2
|
39 }
|
Chris@2
|
40
|
Chris@2
|
41 vector<string>
|
Chris@2
|
42 PluginCandidates::getLibrariesInPath(vector<string> path)
|
Chris@2
|
43 {
|
Chris@2
|
44 vector<string> candidates;
|
Chris@2
|
45
|
Chris@2
|
46 for (string dirname: path) {
|
Chris@2
|
47
|
Chris@2
|
48 //#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
|
Chris@2
|
49 cerr << "getLibrariesInPath: scanning directory " << dirname << endl;
|
Chris@2
|
50 //#endif
|
Chris@2
|
51
|
Chris@2
|
52 QDir dir(dirname.c_str(), PLUGIN_GLOB,
|
Chris@2
|
53 QDir::Name | QDir::IgnoreCase,
|
Chris@2
|
54 QDir::Files | QDir::Readable);
|
Chris@2
|
55
|
Chris@2
|
56 for (unsigned int i = 0; i < dir.count(); ++i) {
|
Chris@2
|
57 QString soname = dir.filePath(dir[i]);
|
Chris@2
|
58 candidates.push_back(soname.toStdString());
|
Chris@2
|
59 }
|
Chris@2
|
60 }
|
Chris@2
|
61
|
Chris@2
|
62 return candidates;
|
Chris@2
|
63 }
|
Chris@2
|
64
|
Chris@2
|
65 void
|
Chris@2
|
66 PluginCandidates::scan(string tag,
|
Chris@2
|
67 vector<string> pluginPath,
|
Chris@4
|
68 string descriptorSymbolName)
|
Chris@2
|
69 {
|
Chris@2
|
70 vector<string> libraries = getLibrariesInPath(pluginPath);
|
Chris@2
|
71 vector<string> remaining = libraries;
|
Chris@2
|
72
|
Chris@2
|
73 int runlimit = 20;
|
Chris@2
|
74 int runcount = 0;
|
Chris@2
|
75
|
Chris@2
|
76 vector<string> result;
|
Chris@2
|
77
|
Chris@2
|
78 while (result.size() < libraries.size() && runcount < runlimit) {
|
Chris@4
|
79 vector<string> output = runHelper(remaining, descriptorSymbolName);
|
Chris@2
|
80 result.insert(result.end(), output.begin(), output.end());
|
Chris@2
|
81 int shortfall = int(remaining.size()) - int(output.size());
|
Chris@2
|
82 if (shortfall > 0) {
|
Chris@2
|
83 // Helper bailed out for some reason presumably associated
|
Chris@2
|
84 // with the plugin following the last one it reported
|
Chris@4
|
85 // on. Add a failure entry for that one and continue with
|
Chris@4
|
86 // the following ones.
|
Chris@4
|
87 cerr << "shortfall = " << shortfall << " (of " << remaining.size() << ")" << endl;
|
Chris@4
|
88 result.push_back("FAILURE|" +
|
Chris@4
|
89 *(remaining.rbegin() + shortfall - 1) +
|
Chris@4
|
90 "|Plugin load check failed");
|
Chris@4
|
91 remaining = vector<string>
|
Chris@4
|
92 (remaining.rbegin(), remaining.rbegin() + shortfall - 1);
|
Chris@2
|
93 }
|
Chris@2
|
94 ++runcount;
|
Chris@2
|
95 }
|
Chris@2
|
96
|
Chris@2
|
97 recordResult(tag, result);
|
Chris@2
|
98 }
|
Chris@2
|
99
|
Chris@2
|
100 vector<string>
|
Chris@2
|
101 PluginCandidates::runHelper(vector<string> libraries, string descriptor)
|
Chris@2
|
102 {
|
Chris@2
|
103 vector<string> output;
|
Chris@4
|
104 // cerr << "running helper with following library list:" << endl;
|
Chris@4
|
105 // for (auto &lib: libraries) cerr << lib << endl;
|
Chris@2
|
106
|
Chris@2
|
107 QProcess process;
|
Chris@2
|
108 process.setReadChannel(QProcess::StandardOutput);
|
Chris@4
|
109 process.setProcessChannelMode(QProcess::ForwardedErrorChannel);
|
Chris@2
|
110 process.start(m_helper.c_str(), { descriptor.c_str() });
|
Chris@2
|
111 if (!process.waitForStarted()) {
|
Chris@2
|
112 cerr << "helper failed to start" << endl;
|
Chris@2
|
113 throw runtime_error("plugin load helper failed to start");
|
Chris@2
|
114 }
|
Chris@2
|
115 for (auto &lib: libraries) {
|
Chris@2
|
116 process.write(lib.c_str(), lib.size());
|
Chris@2
|
117 process.write("\n", 1);
|
Chris@2
|
118 }
|
Chris@2
|
119
|
Chris@2
|
120 int buflen = 4096;
|
Chris@4
|
121 bool done = false;
|
Chris@4
|
122
|
Chris@4
|
123 while (!done) {
|
Chris@2
|
124 char buf[buflen];
|
Chris@2
|
125 qint64 linelen = process.readLine(buf, buflen);
|
Chris@4
|
126 if (linelen > 0) {
|
Chris@4
|
127 output.push_back(buf);
|
Chris@4
|
128 done = (output.size() == libraries.size());
|
Chris@4
|
129 } else if (linelen < 0) {
|
Chris@4
|
130 // error case
|
Chris@4
|
131 done = true;
|
Chris@4
|
132 } else {
|
Chris@4
|
133 // no error, but no line read (could just be between
|
Chris@4
|
134 // lines, or could be eof)
|
Chris@4
|
135 done = (process.state() == QProcess::NotRunning);
|
Chris@4
|
136 if (!done) process.waitForReadyRead(100);
|
Chris@2
|
137 }
|
Chris@4
|
138 }
|
Chris@4
|
139
|
Chris@4
|
140 if (process.state() != QProcess::NotRunning) {
|
Chris@4
|
141 process.close();
|
Chris@4
|
142 process.waitForFinished();
|
Chris@4
|
143 }
|
Chris@2
|
144
|
Chris@2
|
145 return output;
|
Chris@2
|
146 }
|
Chris@2
|
147
|
Chris@2
|
148 void
|
Chris@2
|
149 PluginCandidates::recordResult(string tag, vector<string> result)
|
Chris@2
|
150 {
|
Chris@4
|
151 for (auto &r: result) {
|
Chris@4
|
152
|
Chris@4
|
153 QString s(r.c_str());
|
Chris@4
|
154 QStringList bits = s.split("|");
|
Chris@4
|
155 if (bits.size() < 2 || bits.size() > 3) {
|
Chris@4
|
156 cerr << "Invalid helper output line: \"" << r << "\"" << endl;
|
Chris@4
|
157 continue;
|
Chris@4
|
158 }
|
Chris@4
|
159
|
Chris@4
|
160 string status = bits[0].toStdString();
|
Chris@4
|
161
|
Chris@4
|
162 string library = bits[1].toStdString();
|
Chris@4
|
163 if (bits.size() == 2) library = bits[1].trimmed().toStdString();
|
Chris@4
|
164
|
Chris@4
|
165 string message = "";
|
Chris@4
|
166 if (bits.size() > 2) message = bits[2].trimmed().toStdString();
|
Chris@4
|
167
|
Chris@4
|
168 if (status == "SUCCESS") {
|
Chris@4
|
169 m_candidates[tag].push_back(library);
|
Chris@4
|
170
|
Chris@4
|
171 } else if (status == "FAILURE") {
|
Chris@4
|
172 m_failures[tag].push_back({ library, message });
|
Chris@4
|
173
|
Chris@4
|
174 } else {
|
Chris@4
|
175 cerr << "Unexpected status " << status
|
Chris@4
|
176 << " in helper output line: \"" << r << "\"" << endl;
|
Chris@4
|
177 }
|
Chris@4
|
178 }
|
Chris@2
|
179 }
|
Chris@2
|
180
|