kdecore Library API Documentation

klibloader.cpp

00001 /* This file is part of the KDE libraries 00002 Copyright (C) 1999 Torben Weis <weis@kde.org> 00003 Copyright (C) 2000 Michael Matz <matz@kde.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License version 2 as published by the Free Software Foundation. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00017 Boston, MA 02111-1307, USA. 00018 */ 00019 #include "config.h" 00020 00021 #include <config.h> 00022 #include <qclipboard.h> 00023 #include <qfile.h> 00024 #include <qtimer.h> 00025 #include <qobjectdict.h> 00026 #include <qwidgetlist.h> 00027 #include <qwidget.h> 00028 00029 #include "kapplication.h" 00030 #include "klibloader.h" 00031 #include "kstandarddirs.h" 00032 #include "kdebug.h" 00033 #include "klocale.h" 00034 00035 #include "ltdl.h" 00036 00037 #if defined Q_WS_X11 && ! defined K_WS_QTONLY 00038 #include <X11/Xlib.h> // schroder 00039 #include <X11/Xatom.h> // schroder 00040 #endif 00041 00042 template class QAsciiDict<KLibrary>; 00043 00044 #include <stdlib.h> //getenv 00045 00046 00047 #if HAVE_DLFCN_H 00048 # include <dlfcn.h> 00049 #endif 00050 00051 #ifdef RTLD_GLOBAL 00052 # define LT_GLOBAL RTLD_GLOBAL 00053 #else 00054 # ifdef DL_GLOBAL 00055 # define LT_GLOBAL DL_GLOBAL 00056 # endif 00057 #endif /* !RTLD_GLOBAL */ 00058 #ifndef LT_GLOBAL 00059 # define LT_GLOBAL 0 00060 #endif /* !LT_GLOBAL */ 00061 00062 00063 extern "C" { 00064 extern int lt_dlopen_flag; 00065 } 00066 00067 class KLibLoaderPrivate 00068 { 00069 public: 00070 QPtrList<KLibWrapPrivate> loaded_stack; 00071 QPtrList<KLibWrapPrivate> pending_close; 00072 enum {UNKNOWN, UNLOAD, DONT_UNLOAD} unload_mode; 00073 00074 QString errorMessage; 00075 }; 00076 00077 KLibLoader* KLibLoader::s_self = 0; 00078 00079 // ------------------------------------------------------------------------- 00080 00081 KLibFactory::KLibFactory( QObject* parent, const char* name ) 00082 : QObject( parent, name ) 00083 { 00084 } 00085 00086 KLibFactory::~KLibFactory() 00087 { 00088 // kdDebug(150) << "Deleting KLibFactory " << this << endl; 00089 } 00090 00091 QObject* KLibFactory::create( QObject* parent, const char* name, const char* classname, const QStringList &args ) 00092 { 00093 QObject* obj = createObject( parent, name, classname, args ); 00094 if ( obj ) 00095 emit objectCreated( obj ); 00096 return obj; 00097 } 00098 00099 00100 QObject* KLibFactory::createObject( QObject*, const char*, const char*, const QStringList &) 00101 { 00102 return 0; 00103 } 00104 00105 00106 // ----------------------------------------------- 00107 00108 KLibrary::KLibrary( const QString& libname, const QString& filename, void * handle ) 00109 { 00110 /* Make sure, we have a KLibLoader */ 00111 (void) KLibLoader::self(); 00112 m_libname = libname; 00113 m_filename = filename; 00114 m_handle = handle; 00115 m_factory = 0; 00116 m_timer = 0; 00117 } 00118 00119 KLibrary::~KLibrary() 00120 { 00121 // kdDebug(150) << "Deleting KLibrary " << this << " " << m_libname << endl; 00122 if ( m_timer && m_timer->isActive() ) 00123 m_timer->stop(); 00124 00125 // If any object is remaining, delete 00126 if ( m_objs.count() > 0 ) 00127 { 00128 QPtrListIterator<QObject> it( m_objs ); 00129 for ( ; it.current() ; ++it ) 00130 { 00131 kdDebug(150) << "Factory still has object " << it.current() << " " << it.current()->name () << " Library = " << m_libname << endl; 00132 disconnect( it.current(), SIGNAL( destroyed() ), 00133 this, SLOT( slotObjectDestroyed() ) ); 00134 } 00135 m_objs.setAutoDelete(true); 00136 m_objs.clear(); 00137 } 00138 00139 if ( m_factory ) { 00140 // kdDebug(150) << " ... deleting the factory " << m_factory << endl; 00141 delete m_factory; 00142 m_factory = 0L; 00143 } 00144 } 00145 00146 QString KLibrary::name() const 00147 { 00148 return m_libname; 00149 } 00150 00151 QString KLibrary::fileName() const 00152 { 00153 return m_filename; 00154 } 00155 00156 KLibFactory* KLibrary::factory() 00157 { 00158 if ( m_factory ) 00159 return m_factory; 00160 00161 QCString symname; 00162 symname.sprintf("init_%s", name().latin1() ); 00163 00164 void* sym = symbol( symname ); 00165 if ( !sym ) 00166 { 00167 KLibLoader::self()->d->errorMessage = i18n( "The library %1 does not offer an %2 function." ).arg( name() ).arg( "init_" + name() ); 00168 kdWarning(150) << KLibLoader::self()->d->errorMessage << endl; 00169 return 0; 00170 } 00171 00172 typedef KLibFactory* (*t_func)(); 00173 t_func func = (t_func)sym; 00174 m_factory = func(); 00175 00176 if( !m_factory ) 00177 { 00178 KLibLoader::self()->d->errorMessage = i18n( "The library %1 does not offer a KDE compatible factory." ).arg( name() ); 00179 kdWarning(150) << KLibLoader::self()->d->errorMessage << endl; 00180 return 0; 00181 } 00182 00183 connect( m_factory, SIGNAL( objectCreated( QObject * ) ), 00184 this, SLOT( slotObjectCreated( QObject * ) ) ); 00185 00186 return m_factory; 00187 } 00188 00189 void* KLibrary::symbol( const char* symname ) const 00190 { 00191 void* sym = lt_dlsym( (lt_dlhandle) m_handle, symname ); 00192 if ( !sym ) 00193 { 00194 KLibLoader::self()->d->errorMessage = "KLibrary: " + QString::fromLatin1( lt_dlerror() ); 00195 kdWarning(150) << KLibLoader::self()->d->errorMessage << endl; 00196 return 0; 00197 } 00198 00199 return sym; 00200 } 00201 00202 bool KLibrary::hasSymbol( const char* symname ) const 00203 { 00204 void* sym = lt_dlsym( (lt_dlhandle) m_handle, symname ); 00205 return (sym != 0L ); 00206 } 00207 00208 void KLibrary::unload() const 00209 { 00210 if (KLibLoader::s_self) 00211 KLibLoader::s_self->unloadLibrary(QFile::encodeName(name())); 00212 } 00213 00214 void KLibrary::slotObjectCreated( QObject *obj ) 00215 { 00216 if ( !obj ) 00217 return; 00218 00219 if ( m_timer && m_timer->isActive() ) 00220 m_timer->stop(); 00221 00222 if ( m_objs.containsRef( obj ) ) 00223 return; // we know this object already 00224 00225 connect( obj, SIGNAL( destroyed() ), 00226 this, SLOT( slotObjectDestroyed() ) ); 00227 00228 m_objs.append( obj ); 00229 } 00230 00231 void KLibrary::slotObjectDestroyed() 00232 { 00233 m_objs.removeRef( sender() ); 00234 00235 if ( m_objs.count() == 0 ) 00236 { 00237 // kdDebug(150) << "KLibrary: shutdown timer for " << name() << " started!" 00238 // << endl; 00239 00240 if ( !m_timer ) 00241 { 00242 m_timer = new QTimer( this, "klibrary_shutdown_timer" ); 00243 connect( m_timer, SIGNAL( timeout() ), 00244 this, SLOT( slotTimeout() ) ); 00245 } 00246 00247 // as long as it's not stable make the timeout short, for debugging 00248 // pleasure (matz) 00249 //m_timer->start( 1000*60, true ); 00250 m_timer->start( 1000*10, true ); 00251 } 00252 } 00253 00254 void KLibrary::slotTimeout() 00255 { 00256 if ( m_objs.count() != 0 ) 00257 return; 00258 00259 /* Don't go through KLibLoader::unloadLibrary(), because that uses the 00260 ref counter, but this timeout means to unconditionally close this library 00261 The destroyed() signal will take care to remove us from all lists. 00262 */ 00263 delete this; 00264 } 00265 00266 // ------------------------------------------------- 00267 00268 /* This helper class is needed, because KLibraries can go away without 00269 being unloaded. So we need some info about KLibraries even after its 00270 death. */ 00271 class KLibWrapPrivate 00272 { 00273 public: 00274 KLibWrapPrivate(KLibrary *l, lt_dlhandle h); 00275 00276 KLibrary *lib; 00277 enum {UNKNOWN, UNLOAD, DONT_UNLOAD} unload_mode; 00278 int ref_count; 00279 lt_dlhandle handle; 00280 QString name; 00281 QString filename; 00282 }; 00283 00284 KLibWrapPrivate::KLibWrapPrivate(KLibrary *l, lt_dlhandle h) 00285 : lib(l), ref_count(1), handle(h), name(l->name()), filename(l->fileName()) 00286 { 00287 unload_mode = UNKNOWN; 00288 if (lt_dlsym(handle, "__kde_do_not_unload") != 0) { 00289 // kdDebug(150) << "Will not unload " << name << endl; 00290 unload_mode = DONT_UNLOAD; 00291 } else if (lt_dlsym(handle, "__kde_do_unload") != 0) { 00292 unload_mode = UNLOAD; 00293 } 00294 } 00295 00296 KLibLoader* KLibLoader::self() 00297 { 00298 if ( !s_self ) 00299 s_self = new KLibLoader; 00300 return s_self; 00301 } 00302 00303 void KLibLoader::cleanUp() 00304 { 00305 if ( !s_self ) 00306 return; 00307 00308 delete s_self; 00309 s_self = 0L; 00310 } 00311 00312 KLibLoader::KLibLoader( QObject* parent, const char* name ) 00313 : QObject( parent, name ) 00314 { 00315 s_self = this; 00316 d = new KLibLoaderPrivate; 00317 lt_dlinit(); 00318 d->unload_mode = KLibLoaderPrivate::UNKNOWN; 00319 if (getenv("KDE_NOUNLOAD") != 0) 00320 d->unload_mode = KLibLoaderPrivate::DONT_UNLOAD; 00321 else if (getenv("KDE_DOUNLOAD") != 0) 00322 d->unload_mode = KLibLoaderPrivate::UNLOAD; 00323 d->loaded_stack.setAutoDelete( true ); 00324 } 00325 00326 KLibLoader::~KLibLoader() 00327 { 00328 // kdDebug(150) << "Deleting KLibLoader " << this << " " << name() << endl; 00329 00330 QAsciiDictIterator<KLibWrapPrivate> it( m_libs ); 00331 for (; it.current(); ++it ) 00332 { 00333 kdDebug(150) << "The KLibLoader contains the library " << it.current()->name 00334 << " (" << it.current()->lib << ")" << endl; 00335 d->pending_close.append(it.current()); 00336 } 00337 00338 close_pending(0); 00339 00340 delete d; 00341 d = 0L; 00342 } 00343 00344 //static 00345 QString KLibLoader::findLibrary( const char * name, const KInstance * instance ) 00346 { 00347 QCString libname( name ); 00348 00349 // only append ".la" if there is no extension 00350 // this allows to load non-libtool libraries as well 00351 // (mhk, 20000228) 00352 int pos = libname.findRev('/'); 00353 if (pos < 0) 00354 pos = 0; 00355 if (libname.find('.', pos) < 0) 00356 libname += ".la"; 00357 00358 // only look up the file if it is not an absolute filename 00359 // (mhk, 20000228) 00360 QString libfile; 00361 if (libname[0] == '/') 00362 libfile = libname; 00363 else 00364 { 00365 libfile = instance->dirs()->findResource( "module", libname ); 00366 if ( libfile.isEmpty() ) 00367 { 00368 libfile = instance->dirs()->findResource( "lib", libname ); 00369 #ifndef NDEBUG 00370 if ( !libfile.isEmpty() && libname.left(3) == "lib" ) // don't warn for kdeinit modules 00371 kdDebug(150) << "library " << libname << " not found under 'module' but under 'lib'" << endl; 00372 #endif 00373 } 00374 if ( libfile.isEmpty() ) 00375 { 00376 #ifndef NDEBUG 00377 kdDebug(150) << "library=" << libname << ": No file names " << libname.data() << " found in paths." << endl; 00378 #endif 00379 self()->d->errorMessage = i18n("Library files for \"%1\" not found in paths").arg(libname); 00380 } 00381 else 00382 self()->d->errorMessage = QString::null; 00383 } 00384 return libfile; 00385 } 00386 00387 00388 KLibrary* KLibLoader::globalLibrary( const char *name ) 00389 { 00390 KLibrary *tmp; 00391 int olt_dlopen_flag = lt_dlopen_flag; 00392 00393 lt_dlopen_flag |= LT_GLOBAL; 00394 kdDebug(150) << "Loading the next library global with flag " 00395 << lt_dlopen_flag 00396 << "." << endl; 00397 tmp = library(name); 00398 lt_dlopen_flag = olt_dlopen_flag; 00399 00400 return tmp; 00401 } 00402 00403 00404 KLibrary* KLibLoader::library( const char *name ) 00405 { 00406 if (!name) 00407 return 0; 00408 00409 KLibWrapPrivate* wrap = m_libs[name]; 00410 if (wrap) { 00411 /* Nothing to do to load the library. */ 00412 wrap->ref_count++; 00413 return wrap->lib; 00414 } 00415 00416 /* Test if this library was loaded at some time, but got 00417 unloaded meanwhile, whithout being dlclose()'ed. */ 00418 QPtrListIterator<KLibWrapPrivate> it(d->loaded_stack); 00419 for (; it.current(); ++it) { 00420 if (it.current()->name == name) 00421 wrap = it.current(); 00422 } 00423 00424 if (wrap) { 00425 d->pending_close.removeRef(wrap); 00426 if (!wrap->lib) { 00427 /* This lib only was in loaded_stack, but not in m_libs. */ 00428 wrap->lib = new KLibrary( name, wrap->filename, wrap->handle ); 00429 } 00430 wrap->ref_count++; 00431 } else { 00432 QString libfile = findLibrary( name ); 00433 if ( libfile.isEmpty() ) 00434 return 0; 00435 00436 lt_dlhandle handle = lt_dlopen( libfile.latin1() ); 00437 if ( !handle ) 00438 { 00439 const char* errmsg = lt_dlerror(); 00440 if(errmsg) 00441 d->errorMessage = QString::fromLatin1(errmsg); 00442 else 00443 d->errorMessage = QString::null; 00444 return 0; 00445 } 00446 else 00447 d->errorMessage = QString::null; 00448 00449 KLibrary *lib = new KLibrary( name, libfile, handle ); 00450 wrap = new KLibWrapPrivate(lib, handle); 00451 d->loaded_stack.prepend(wrap); 00452 } 00453 m_libs.insert( name, wrap ); 00454 00455 connect( wrap->lib, SIGNAL( destroyed() ), 00456 this, SLOT( slotLibraryDestroyed() ) ); 00457 00458 return wrap->lib; 00459 } 00460 00461 QString KLibLoader::lastErrorMessage() const 00462 { 00463 return d->errorMessage; 00464 } 00465 00466 void KLibLoader::unloadLibrary( const char *libname ) 00467 { 00468 KLibWrapPrivate *wrap = m_libs[ libname ]; 00469 if (!wrap) 00470 return; 00471 if (--wrap->ref_count) 00472 return; 00473 00474 // kdDebug(150) << "closing library " << libname << endl; 00475 00476 m_libs.remove( libname ); 00477 00478 disconnect( wrap->lib, SIGNAL( destroyed() ), 00479 this, SLOT( slotLibraryDestroyed() ) ); 00480 close_pending( wrap ); 00481 } 00482 00483 KLibFactory* KLibLoader::factory( const char* name ) 00484 { 00485 KLibrary* lib = library( name ); 00486 if ( !lib ) 00487 return 0; 00488 00489 return lib->factory(); 00490 } 00491 00492 void KLibLoader::slotLibraryDestroyed() 00493 { 00494 const KLibrary *lib = static_cast<const KLibrary *>( sender() ); 00495 00496 QAsciiDictIterator<KLibWrapPrivate> it( m_libs ); 00497 for (; it.current(); ++it ) 00498 if ( it.current()->lib == lib ) 00499 { 00500 KLibWrapPrivate *wrap = it.current(); 00501 wrap->lib = 0; /* the KLibrary object is already away */ 00502 m_libs.remove( it.currentKey() ); 00503 close_pending( wrap ); 00504 return; 00505 } 00506 } 00507 00508 void KLibLoader::close_pending(KLibWrapPrivate *wrap) 00509 { 00510 if (wrap && !d->pending_close.containsRef( wrap )) 00511 d->pending_close.append( wrap ); 00512 00513 /* First delete all KLibrary objects in pending_close, but _don't_ unload 00514 the DSO behind it. */ 00515 QPtrListIterator<KLibWrapPrivate> it(d->pending_close); 00516 for (; it.current(); ++it) { 00517 wrap = it.current(); 00518 if (wrap->lib) { 00519 disconnect( wrap->lib, SIGNAL( destroyed() ), 00520 this, SLOT( slotLibraryDestroyed() ) ); 00521 KLibrary* to_delete = wrap->lib; 00522 wrap->lib = 0L; // unset first, because KLibrary dtor can cause 00523 delete to_delete; // recursive call to close_pending() 00524 } 00525 } 00526 00527 if (d->unload_mode == KLibLoaderPrivate::DONT_UNLOAD) { 00528 d->pending_close.clear(); 00529 return; 00530 } 00531 00532 bool deleted_one = false; 00533 while ((wrap = d->loaded_stack.first())) { 00534 /* Let's first see, if we want to try to unload this lib. 00535 If the env. var KDE_DOUNLOAD is set, we try to unload every lib. 00536 If not, we look at the lib itself, and unload it only, if it exports 00537 the symbol __kde_do_unload. */ 00538 if (d->unload_mode != KLibLoaderPrivate::UNLOAD 00539 && wrap->unload_mode != KLibWrapPrivate::UNLOAD) 00540 break; 00541 00542 /* Now ensure, that the libs are only unloaded in the reverse direction 00543 they were loaded. */ 00544 if (!d->pending_close.containsRef( wrap )) { 00545 if (!deleted_one) 00546 /* Only diagnose, if we really haven't deleted anything. */ 00547 // kdDebug(150) << "try to dlclose " << wrap->name << ": not yet" << endl; 00548 break; 00549 } 00550 00551 // kdDebug(150) << "try to dlclose " << wrap->name << ": yes, done." << endl; 00552 00553 #if defined Q_WS_X11 && ! defined K_WS_QTONLY 00554 //#ifndef Q_WS_QWS 00555 if ( !deleted_one ) { 00556 /* Only do the hack once in this loop. 00557 WABA: *HACK* 00558 We need to make sure to clear the clipboard before unloading a DSO 00559 because the DSO could have defined an object derived from QMimeSource 00560 and placed that on the clipboard. */ 00561 /*kapp->clipboard()->clear();*/ 00562 00563 /* Well.. let's do something more subtle... convert the clipboard context 00564 to text. That should be safe as it only uses objects defined by Qt. */ 00565 00566 QWidgetList *widgetlist = QApplication::topLevelWidgets(); 00567 QWidget *co = widgetlist->first(); 00568 while (co) { 00569 if (qstrcmp(co->name(), "internal clipboard owner") == 0) { 00570 if (XGetSelectionOwner(co->x11Display(), XA_PRIMARY) == co->winId()) 00571 kapp->clipboard()->setText(kapp->clipboard()->text()); 00572 00573 break; 00574 } 00575 co = widgetlist->next(); 00576 } 00577 delete widgetlist; 00578 } 00579 #else 00580 // FIXME(E): Implement in Qt Embedded 00581 #endif 00582 00583 deleted_one = true; 00584 lt_dlclose(wrap->handle); 00585 d->pending_close.removeRef(wrap); 00586 /* loaded_stack is AutoDelete, so wrap is freed */ 00587 d->loaded_stack.remove(); 00588 } 00589 } 00590 00591 void KLibLoader::virtual_hook( int, void* ) 00592 { /*BASE::virtual_hook( id, data );*/ } 00593 00594 void KLibFactory::virtual_hook( int, void* ) 00595 { /*BASE::virtual_hook( id, data );*/ } 00596 00597 #include "klibloader.moc"
KDE Logo
This file is part of the documentation for kdecore Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Aug 30 22:53:31 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003