libyui  3.9.3
YUILoader.cc
1 /*
2  Copyright (C) 2000-2017 Novell, Inc
3  This library is free software; you can redistribute it and/or modify
4  it under the terms of the GNU Lesser General Public License as
5  published by the Free Software Foundation; either version 2.1 of the
6  License, or (at your option) version 3.0 of the License. This library
7  is distributed in the hope that it will be useful, but WITHOUT ANY
8  WARRANTY; without even the implied warranty of MERCHANTABILITY or
9  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
10  License for more details. You should have received a copy of the GNU
11  Lesser General Public License along with this library; if not, write
12  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
13  Floor, Boston, MA 02110-1301 USA
14 */
15 
16 
17 /*-/
18 
19  File: YUILoader.cc
20 
21  Author: Stefan Hundhammer <sh@suse.de>
22 
23 /-*/
24 
25 #include <stdlib.h> // getenv()
26 #include <unistd.h> // isatty()a
27 #include <sys/stat.h>
28 #include <string.h>
29 
30 #define YUILogComponent "ui"
31 #include "YUILog.h"
32 #include "YCommandLine.h"
33 #include "YUILoader.h"
34 #include "YUIPlugin.h"
35 #include "YUIException.h"
36 #include "YPath.h"
37 #include "YSettings.h"
38 
39 #include "Libyui_config.h"
40 
41 using std::string;
42 
43 
44 bool rest_enabled()
45 {
46  const char *env = getenv("YUI_HTTP_PORT");
47  return env && atoi(env) > 0;
48 }
49 
50 
51 void YUILoader::loadUI( bool withThreads )
52 {
53  bool isGtk = false;
54  const char * envDesktop = getenv( "XDG_CURRENT_DESKTOP" ) ?: "";
55  const char * envDisplay = getenv( "DISPLAY" ) ?: "";
56  const char * envPreset = getenv( "YUI_PREFERED_BACKEND" ) ?: "";
57 
58  string wantedGUI;
59 
60  yuiMilestone () << "DISPLAY: \"" << envDisplay << "\"" << endl;
61  yuiMilestone () << "XDG_CURRENT_DESKTOP: \"" << envDesktop << "\"" << endl;
62  yuiMilestone () << "YUI_PREFERED_BACKEND: \"" << envPreset << "\"" << endl;
63 
64  // Taken from: https://specifications.freedesktop.org/menu-spec/menu-spec-1.1.html#onlyshowin-registry
65  isGtk = ( ( strstr( envDesktop, "Cinnamon" ) != NULL ) || isGtk );
66  isGtk = ( ( strstr( envDesktop, "GNOME" ) != NULL ) || isGtk );
67  isGtk = ( ( strstr( envDesktop, "LXDE" ) != NULL ) || isGtk );
68  isGtk = ( ( strstr( envDesktop, "MATE" ) != NULL ) || isGtk );
69  isGtk = ( ( strstr( envDesktop, "Pantheon" ) != NULL ) || isGtk );
70  isGtk = ( ( strstr( envDesktop, "ROX" ) != NULL ) || isGtk );
71  isGtk = ( ( strstr( envDesktop, "Unity" ) != NULL ) || isGtk );
72  isGtk = ( ( strstr( envDesktop, "XFCE" ) != NULL ) || isGtk );
73 
74  if ( isGtk ) yuiMilestone () << "Detected a Gtk-based desktop environment." << endl
75  << "Prefering Gtk-UI if available and no" << endl
76  << "user-selected override is present." << endl;
77 
78  YCommandLine cmdline;
79 
80  bool wantGtk = ( cmdline.find( "--gtk" ) != -1 );
81  bool wantNcurses = ( cmdline.find( "--ncurses" ) != -1 );
82  bool wantQt = ( cmdline.find( "--qt" ) != -1 );
83  bool haveUIPreset = ( wantGtk || wantNcurses || wantQt );
84 
85  if ( !haveUIPreset )
86  {
87  wantGtk = ( strcmp( envPreset, YUIPlugin_Gtk ) == 0 );
88  wantNcurses = ( strcmp( envPreset, YUIPlugin_NCurses ) == 0 );
89  wantQt = ( strcmp( envPreset, YUIPlugin_Qt ) == 0 );
90  }
91 
92  if ( wantGtk ) wantedGUI = YUIPlugin_Gtk;
93  if ( wantNcurses ) wantedGUI = YUIPlugin_NCurses;
94  if ( wantQt ) wantedGUI = YUIPlugin_Qt;
95 
96  yuiMilestone () << "User-selected UI-plugin: \"" << wantedGUI << "\"" << endl;
97 
98  bool haveGtk = pluginExists( YUIPlugin_Gtk );
99  bool haveNcurses = pluginExists( YUIPlugin_NCurses );
100  bool haveQt = pluginExists( YUIPlugin_Qt );
101 
102  // This reset is intentional, so the loader can work it's magic
103  // selecting an UI-plugin as described in the documentation.
104  wantedGUI="";
105 
106  // Set the UI-Plugin
107  if ( ( haveGtk || haveQt ) && strcmp ( envDisplay, "" ) &&
108  ( !wantNcurses || !isatty( STDOUT_FILENO ) ) )
109  {
110  // Qt is default if available.
111  if ( haveQt )
112  wantedGUI = YUIPlugin_Qt;
113 
114  // Do we want to use Gtk instead?
115  if ( haveGtk && ( ( ( isGtk || wantGtk ) && !wantQt ) || !haveQt ) )
116  wantedGUI = YUIPlugin_Gtk;
117  }
118 
119  else if ( haveNcurses && isatty( STDOUT_FILENO ) )
120  {
121  // We use NCurses.
122  wantedGUI = YUIPlugin_NCurses;
123  }
124 
125  // Load the wanted UI-plugin.
126  if ( wantedGUI != "" )
127  {
128  yuiMilestone () << "Using UI-plugin: \"" << wantedGUI << "\""<< endl;
129  YSettings::loadedUI( wantedGUI, true );
130 
131  try
132  {
133  // Load integration testing framework plugin, which load required UI
134  // There is no support for GTK planned, so not loading rest api
135  // plugin in case gtk was requested
136  if ( rest_enabled() && wantedGUI != YUIPlugin_Gtk )
137  {
138  loadRestAPIPlugin( wantedGUI, withThreads );
139  }
140  else
141  {
142  loadPlugin( wantedGUI, withThreads );
143  }
144 
145  return;
146  }
147 
148  catch ( YUIException & ex )
149  {
150  YUI_CAUGHT( ex );
151 
152  // Default to NCurses, if possible.
153  if ( wantedGUI != YUIPlugin_NCurses && haveNcurses && isatty( STDOUT_FILENO ) )
154  {
155  yuiWarning () << "Defaulting to: \"" << YUIPlugin_NCurses << "\""<< endl;
156  YSettings::loadedUI( YUIPlugin_NCurses, true );
157 
158  try
159  {
160  loadPlugin( YUIPlugin_NCurses, withThreads );
161  return;
162  }
163 
164  catch ( YUIException & ex )
165  {
166  YUI_CAUGHT( ex );
167  }
168  }
169 
170  YUI_RETHROW( ex ); // what else to do here?
171  }
172  }
173 
174  else
175  {
176  YUI_THROW( YUICantLoadAnyUIException() );
177  }
178 }
179 
180 void YUILoader::loadRestAPIPlugin( const string & wantedGUI, bool withThreads )
181 {
182  // Do not try to load if variable is not set
183  yuiMilestone () << "Requested to start http server to control UI." << endl;
184 
185  if ( pluginExists( YUIPlugin_RestAPI ) )
186  {
187  YUIPlugin uiRestPlugin( YUIPlugin_RestAPI );
188  createUIFunction_t createUI = nullptr;
189  yuiMilestone () << "User-selected underlying UI-plugin: \"" << wantedGUI << "\"" << endl;
190 
191  if (wantedGUI == YUIPlugin_Qt)
192  {
193  YUIPlugin uiPluginQT( YUIPlugin_Qt );
194  YUIPlugin uiPluginQTRest( YUIPlugin_Qt_RestAPI );
195 
196  if ( uiPluginQT.success() && uiRestPlugin.success() && uiPluginQTRest.success() )
197  {
198  yuiMilestone () << "Loading the http server to control the Qt UI" << endl;
199  createUI = (createUIFunction_t) uiPluginQTRest.locateSymbol( "createYQHttpUI" );
200  }
201  else
202  yuiError() << "Cannot load Qt REST API UI" << endl;
203  }
204 
205  // fallback to ncurses + REST API if Qt does not work
206  if (wantedGUI == YUIPlugin_NCurses || createUI == nullptr)
207  {
208  YUIPlugin uiPluginNC( YUIPlugin_NCurses );
209  YUIPlugin uiPluginNCRest( YUIPlugin_Ncurses_RestAPI );
210  if ( uiPluginNC.success() && uiRestPlugin.success() && uiPluginNCRest.success())
211  {
212  yuiMilestone () << "Loading the http server to control the ncurses UI" << endl;
213  createUI = (createUIFunction_t) uiPluginNCRest.locateSymbol( "createYNCHttpUI" );
214  }
215  }
216 
217  if ( createUI )
218  {
219  YUI * ui = createUI( withThreads );
220  // Same as in loadPlugin
221  atexit(deleteUI);
222 
223  if ( ui ) return;
224  }
225  }
226  // Throw an exception if loading of the plugin failed
227  YUI_THROW ( YUIPluginException ( YUIPlugin_RestAPI ) );
228 }
229 
231 {
232  if ( YUI::_ui )
233  {
234  yuiMilestone() << "Shutting down UI" << endl;
235  delete YUI::_ui;
236 
237  YUI::_ui = 0;
238  }
239 }
240 
241 void YUILoader::loadPlugin( const string & name, bool withThreads )
242 {
243  if (rest_enabled() && (name == YUIPlugin_NCurses || name == YUIPlugin_Qt))
244  {
245  loadRestAPIPlugin(name, withThreads);
246  return;
247  }
248 
249  YUIPlugin uiPlugin( name.c_str() );
250 
251  if ( uiPlugin.success() )
252  {
253  createUIFunction_t createUI = (createUIFunction_t) uiPlugin.locateSymbol( "_Z8createUIb" ); // createUI(bool)
254 
255  if ( createUI )
256  {
257  YUI * ui = createUI( withThreads ); // no threads
258 
259  // At this point the concrete UI will have loaded its own
260  // internal plugins and registered their destructors.
261  // Our destructor must get called before those get dlclose'd.
262  //
263  // Formerly ~YUI was called quite late, which called ~YQUI
264  // and that ran code in the already unloaded Qt internal plugins.
265  atexit(deleteUI);
266 
267  if ( ui )
268  return;
269  }
270  }
271 
272  YUI_THROW( YUIPluginException( name ) );
273 }
274 
275 void YUILoader::loadExternalWidgetsPlugin ( const string& name, const string& plugin_name, const string& symbol )
276 {
277  YUIPlugin uiPlugin ( plugin_name.c_str() );
278 
279  if ( uiPlugin.success() )
280  {
281  createEWFunction_t createEW = ( createEWFunction_t ) uiPlugin.locateSymbol ( symbol.c_str() );
282 
283  if ( createEW )
284  {
285  YExternalWidgets * we = createEW ( name.c_str() );
286 
287  if ( we )
288  return;
289  }
290  }
291 
292  YUI_THROW ( YUIPluginException ( plugin_name ) );
293 }
294 
295 void YUILoader::loadExternalWidgets ( const string& name, const string& symbol )
296 {
297  string wantedGUI = name;
298  wantedGUI.append( "-" );
299  wantedGUI.append( YSettings::loadedUI() );
300 
301  bool haveExternal = pluginExists( wantedGUI );
302 
303  if ( haveExternal )
304  {
305  try
306  {
307  loadExternalWidgetsPlugin(name, wantedGUI, symbol );
308  return;
309  }
310  catch ( YUIException & ex )
311  {
312  YUI_CAUGHT( ex );
313  YUI_RETHROW( ex ); // what else to do here?
314  }
315  }
316 
317  else
318  {
319  YUI_THROW( YUICantLoadAnyUIException() );
320  }
321 }
322 
323 bool YUILoader::pluginExists( const string & pluginBaseName )
324 {
325  struct stat fileinfo;
326  string pluginName = PLUGIN_PREFIX;
327 
328  pluginName.append( pluginBaseName );
329  pluginName.append( PLUGIN_SUFFIX );
330 
331  YPath plugin ( PLUGINDIR, pluginName );
332 
333  return stat( plugin.path().c_str(), &fileinfo) == 0;
334 
335 }
int find(const std::string &argName) const
Find a command line argument &#39;argName&#39; ("-display" etc.).
Abstract base class of a libYUI user interface.
Definition: YUI.h:48
static void loadPlugin(const std::string &name, bool withThreads=false)
Load a UI plug-in.
Definition: YUILoader.cc:241
static std::string loadedUI()
Returns the value of the loaded UI-backend.
Definition: YSettings.cc:202
static void loadExternalWidgets(const std::string &name, const std::string &symbol="_Z21createExternalWidgetsPKc")
Load the given External Widgets plugin followed by its graphical extension implementation in the foll...
Definition: YUILoader.cc:295
static void loadRestAPIPlugin(const std::string &wantedGUI, bool withThreads=false)
Method handles loading integration test framework and load underlying GUI using hints from loadUI...
Definition: YUILoader.cc:180
void * locateSymbol(const char *symbol)
Try to locate the specified symbol (function or global variable) in the plugin library.
Definition: YUIPlugin.cc:89
Exception class for plugin load failure.
Definition: YUIException.h:875
static void deleteUI()
This will make sure the UI singleton is deleted.
Definition: YUILoader.cc:230
Finds files (e.g.
Definition: YPath.h:43
Abstract base class of a libYUI Widget Extension interface.
bool success() const
Returns &#39;true&#39; if there was no error loading the plugin.
Definition: YUIPlugin.cc:113
Utility class to access /proc/<pid>/cmdline to retrieve argc and argv.
Definition: YCommandLine.h:37
Exception class for UI plugin load failure.
Definition: YUIException.h:890
Wrapper class for dlopen() and related.
Definition: YUIPlugin.h:35
Base class for UI Exceptions.
Definition: YUIException.h:297
static void loadUI(bool withThreads=false)
Load any of the available UI-plugins by this order and criteria:
Definition: YUILoader.cc:51