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