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