1 /*
2  * Copyright (c) 2011-2014, Intel Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this
9  * list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation and/or
13  * other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  * may be used to endorse or promote products derived from this software without
17  * specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 #include <dlfcn.h>
31 #include <dirent.h>
32 #include <algorithm>
33 #include <ctype.h>
34 #include "SystemClass.h"
35 #include "SubsystemLibrary.h"
36 #include "AutoLog.h"
37 #include "VirtualSubsystem.h"
38 #include "NamedElementBuilderTemplate.h"
39 #include <assert.h>
40 #include "PluginLocation.h"
41 #include "Utility.h"
42 
43 #define base CConfigurableElement
44 
45 using std::list;
46 using std::string;
47 
48 /**
49  * A plugin file name is of the form:
50  * lib<type>-subsystem.so or lib<type>-subsystem._host.so
51  *
52  * The plugin symbol is of the form:
53  * get<TYPE>SubsystemBuilder
54 */
55 // Plugin file naming
56 const char* gpcPluginSuffix = "-subsystem";
57 const char* gpcPluginPrefix = "lib";
58 
59 // Plugin symbol naming
60 const char* gpcPluginSymbolPrefix = "get";
61 const char* gpcPluginSymbolSuffix = "SubsystemBuilder";
62 
63 // Used by subsystem plugins
64 typedef void (*GetSubsystemBuilder)(CSubsystemLibrary*);
65 
CSystemClass()66 CSystemClass::CSystemClass() : _pSubsystemLibrary(new CSubsystemLibrary)
67 {
68 }
69 
~CSystemClass()70 CSystemClass::~CSystemClass()
71 {
72     delete _pSubsystemLibrary;
73 
74     // Destroy child subsystems *before* unloading the libraries (otherwise crashes will occur
75     // as unmapped code will be referenced)
76     clean();
77 
78     // Close all previously opened subsystem libraries
79     list<void*>::const_iterator it;
80 
81     for (it = _subsystemLibraryHandleList.begin(); it != _subsystemLibraryHandleList.end(); ++it) {
82 
83         dlclose(*it);
84     }
85 }
86 
childrenAreDynamic() const87 bool CSystemClass::childrenAreDynamic() const
88 {
89     return true;
90 }
91 
getKind() const92 string CSystemClass::getKind() const
93 {
94     return "SystemClass";
95 }
96 
loadSubsystems(string & strError,const CSubsystemPlugins * pSubsystemPlugins,bool bVirtualSubsystemFallback)97 bool CSystemClass::loadSubsystems(string& strError,
98                                   const CSubsystemPlugins* pSubsystemPlugins,
99                                   bool bVirtualSubsystemFallback)
100 {
101     CAutoLog autoLog_info(this, "Loading subsystem plugins");
102 
103     // Start clean
104     _pSubsystemLibrary->clean();
105 
106     // Add virtual subsystem builder
107     _pSubsystemLibrary->addElementBuilder("Virtual",
108                                           new TNamedElementBuilderTemplate<CVirtualSubsystem>());
109     // Set virtual subsytem as builder fallback if required
110     _pSubsystemLibrary->enableDefaultMechanism(bVirtualSubsystemFallback);
111 
112     // Add subsystem defined in shared libraries
113     list<string> lstrError;
114     bool bLoadPluginsSuccess = loadSubsystemsFromSharedLibraries(lstrError, pSubsystemPlugins);
115 
116     if (bLoadPluginsSuccess) {
117         log_info("All subsystem plugins successfully loaded");
118     } else {
119         // Log plugin as warning if no fallback available
120         log_table(!bVirtualSubsystemFallback, lstrError);
121     }
122 
123     if (!bVirtualSubsystemFallback) {
124         // Any problem reported is an error as there is no fallback.
125         // Fill strError for caller.
126         CUtility::asString(lstrError, strError);
127     }
128 
129     return bLoadPluginsSuccess || bVirtualSubsystemFallback;
130 }
131 
loadSubsystemsFromSharedLibraries(list<string> & lstrError,const CSubsystemPlugins * pSubsystemPlugins)132 bool CSystemClass::loadSubsystemsFromSharedLibraries(list<string>& lstrError,
133                                                      const CSubsystemPlugins* pSubsystemPlugins)
134 {
135     // Plugin list
136     list<string> lstrPluginFiles;
137 
138     uint32_t uiPluginLocation;
139 
140     for (uiPluginLocation = 0; uiPluginLocation <  pSubsystemPlugins->getNbChildren(); uiPluginLocation++) {
141 
142         // Get Folder for current Plugin Location
143         const CPluginLocation* pPluginLocation = static_cast<const CPluginLocation*>(pSubsystemPlugins->getChild(uiPluginLocation));
144 
145         string strFolder(pPluginLocation->getFolder());
146         if (!strFolder.empty()) {
147             strFolder += "/";
148         }
149         // Iterator on Plugin List:
150         list<string>::const_iterator it;
151 
152         const list<string>& pluginList = pPluginLocation->getPluginList();
153 
154         for (it = pluginList.begin(); it != pluginList.end(); ++it) {
155 
156             // Fill Plugin files list
157             lstrPluginFiles.push_back(strFolder + *it);
158         }
159     }
160 
161     // Actually load plugins
162     while (!lstrPluginFiles.empty()) {
163 
164         // Because plugins might depend on one another, loading will be done
165         // as an iteration process that finishes successfully when the remaining
166         // list of plugins to load gets empty or unsuccessfully if the loading
167         // process failed to load at least one of them
168 
169         // Attempt to load the complete list
170         if (!loadPlugins(lstrPluginFiles, lstrError)) {
171 
172             // Unable to load at least one plugin
173             break;
174         }
175     }
176 
177     if (!lstrPluginFiles.empty()) {
178         // Unable to load at least one plugin
179         string strPluginUnloaded;
180         CUtility::asString(lstrPluginFiles, strPluginUnloaded, ", ");
181 
182         lstrError.push_back("Unable to load the following plugins: " + strPluginUnloaded + ".");
183         return false;
184     }
185 
186     return true;
187 }
188 
189 // Plugin symbol computation
getPluginSymbol(const string & strPluginPath)190 string CSystemClass::getPluginSymbol(const string& strPluginPath)
191 {
192     // Extract plugin type out of file name
193     string strPluginSuffix = gpcPluginSuffix;
194     string strPluginPrefix = gpcPluginPrefix;
195 
196     // Remove folder and library prefix
197     size_t iPluginTypePos = strPluginPath.rfind('/') + 1 + strPluginPrefix.length();
198 
199     // Get index of -subsystem.so or -subsystem_host.so suffix
200     size_t iSubsystemPos = strPluginPath.find(strPluginSuffix, iPluginTypePos);
201 
202     // Get type (between iPluginTypePos and iSubsystemPos)
203     string strPluginType = strPluginPath.substr(iPluginTypePos, iSubsystemPos - iPluginTypePos);
204 
205     // Make it upper case
206     std::transform(strPluginType.begin(), strPluginType.end(), strPluginType.begin(), ::toupper);
207 
208     // Get plugin symbol
209     return gpcPluginSymbolPrefix + strPluginType + gpcPluginSymbolSuffix;
210 }
211 
212 // Plugin loading
loadPlugins(list<string> & lstrPluginFiles,list<string> & lstrError)213 bool CSystemClass::loadPlugins(list<string>& lstrPluginFiles, list<string>& lstrError)
214 {
215     assert(lstrPluginFiles.size());
216 
217     bool bAtLeastOneSubsystemPluginSuccessfullyLoaded = false;
218 
219     list<string>::iterator it = lstrPluginFiles.begin();
220 
221     while (it != lstrPluginFiles.end()) {
222 
223         string strPluginFileName = *it;
224 
225         log_info("Attempting to load subsystem plugin path \"%s\"", strPluginFileName.c_str());
226 
227         // Load attempt
228         void* lib_handle = dlopen(strPluginFileName.c_str(), RTLD_LAZY);
229 
230         if (!lib_handle) {
231 
232             const char *err = dlerror();
233             // Failed
234             if (err == NULL) {
235                 lstrError.push_back("dlerror failed");
236             } else {
237                 lstrError.push_back("Plugin load failed: " + string(err));
238             }
239             // Next plugin
240             ++it;
241 
242             continue;
243         }
244 
245         // Store libraries handles
246         _subsystemLibraryHandleList.push_back(lib_handle);
247 
248         // Get plugin symbol
249         string strPluginSymbol = getPluginSymbol(strPluginFileName);
250 
251         // Load symbol from library
252         GetSubsystemBuilder pfnGetSubsystemBuilder = (GetSubsystemBuilder)dlsym(lib_handle, strPluginSymbol.c_str());
253 
254         if (!pfnGetSubsystemBuilder) {
255 
256             lstrError.push_back("Subsystem plugin " + strPluginFileName +
257                                 " does not contain " + strPluginSymbol + " symbol.");
258 
259             continue;
260         }
261 
262         // Account for this success
263         bAtLeastOneSubsystemPluginSuccessfullyLoaded = true;
264 
265         // Fill library
266         pfnGetSubsystemBuilder(_pSubsystemLibrary);
267 
268         // Remove successfully loaded plugin from list and select next
269         lstrPluginFiles.erase(it++);
270     }
271 
272     return bAtLeastOneSubsystemPluginSuccessfullyLoaded;
273 }
274 
getSubsystemLibrary() const275 const CSubsystemLibrary* CSystemClass::getSubsystemLibrary() const
276 {
277     return _pSubsystemLibrary;
278 }
279 
checkForSubsystemsToResync(CSyncerSet & syncerSet)280 void CSystemClass::checkForSubsystemsToResync(CSyncerSet& syncerSet)
281 {
282     size_t uiNbChildren = getNbChildren();
283     size_t uiChild;
284 
285     for (uiChild = 0; uiChild < uiNbChildren; uiChild++) {
286 
287         CSubsystem* pSubsystem = static_cast<CSubsystem*>(getChild(uiChild));
288 
289         // Collect and consume the need for a resync
290         if (pSubsystem->needResync(true)) {
291 
292             log_info("Resynchronizing subsystem: %s", pSubsystem->getName().c_str());
293             // get all subsystem syncers
294             pSubsystem->fillSyncerSet(syncerSet);
295         }
296     }
297 }
298 
cleanSubsystemsNeedToResync()299 void CSystemClass::cleanSubsystemsNeedToResync()
300 {
301     size_t uiNbChildren = getNbChildren();
302     size_t uiChild;
303 
304     for (uiChild = 0; uiChild < uiNbChildren; uiChild++) {
305 
306         CSubsystem* pSubsystem = static_cast<CSubsystem*>(getChild(uiChild));
307 
308         // Consume the need for a resync
309         pSubsystem->needResync(true);
310     }
311 }
312