00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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>
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
00051 #ifndef LT_GLOBAL
00052 # define LT_GLOBAL 0
00053 #endif
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
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
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
00115 if ( m_timer && m_timer->isActive() )
00116 m_timer->stop();
00117
00118
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
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;
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
00231
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
00241
00242
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
00253
00254
00255
00256 delete this;
00257 }
00258
00259
00260
00261
00262
00263
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
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
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
00341
00342
00343 int pos = libname.findRev('/');
00344 if (pos < 0)
00345 pos = 0;
00346 if (libname.find('.', pos) < 0)
00347 libname += ".so";
00348 if (libname.right(3) == ".la")
00349 libname = libname.replace( qstrlen(libname) - 4, 3, ".so");
00350 return libname;
00351 }
00352
00353
00354 QString KLibLoader::findLibrary( const char * name, const KInstance * instance )
00355 {
00356 QCString libname = makeLibName( name );
00357
00358
00359
00360 QString libfile;
00361 if (libname[0] == '/')
00362 libfile = QFile::decodeName( 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" )
00371 kdDebug(150) << "library " << libname << " not found under 'module' but under 'lib'" << endl;
00372 #endif
00373 }
00374 }
00375 return libfile;
00376 }
00377
00378
00379 KLibrary* KLibLoader::globalLibrary( const char *name )
00380 {
00381 KLibrary *tmp;
00382 int olt_dlopen_flag = lt_dlopen_flag;
00383
00384 lt_dlopen_flag |= LT_GLOBAL;
00385 kdDebug(150) << "Loading the next library global with flag "
00386 << lt_dlopen_flag
00387 << "." << endl;
00388 tmp = library(name);
00389 lt_dlopen_flag = olt_dlopen_flag;
00390
00391 return tmp;
00392 }
00393
00394
00395 KLibrary* KLibLoader::library( const char *name )
00396 {
00397 if (!name)
00398 return 0;
00399
00400 KLibWrapPrivate* wrap = m_libs[name];
00401 if (wrap) {
00402
00403 wrap->ref_count++;
00404 return wrap->lib;
00405 }
00406
00407
00408
00409 QPtrListIterator<KLibWrapPrivate> it(d->loaded_stack);
00410 for (; it.current(); ++it) {
00411 if (it.current()->name == name)
00412 wrap = it.current();
00413 }
00414
00415 if (wrap) {
00416 d->pending_close.removeRef(wrap);
00417 if (!wrap->lib) {
00418
00419 wrap->lib = new KLibrary( name, wrap->filename, wrap->handle );
00420 }
00421 wrap->ref_count++;
00422 } else {
00423 QString libfile = findLibrary( name );
00424 if ( libfile.isEmpty() )
00425 {
00426 const QCString libname = makeLibName( name );
00427 #ifndef NDEBUG
00428 kdDebug(150) << "library=" << name << ": No file named " << libname << " found in paths." << endl;
00429 #endif
00430 d->errorMessage = i18n("Library files for \"%1\" not found in paths.").arg(libname);
00431 return 0;
00432 }
00433
00434 lt_dlhandle handle = lt_dlopen( libfile.latin1() );
00435 if ( !handle )
00436 {
00437 const char* errmsg = lt_dlerror();
00438 if(errmsg)
00439 d->errorMessage = QString::fromLatin1(errmsg);
00440 else
00441 d->errorMessage = QString::null;
00442 return 0;
00443 }
00444 else
00445 d->errorMessage = QString::null;
00446
00447 KLibrary *lib = new KLibrary( name, libfile, handle );
00448 wrap = new KLibWrapPrivate(lib, handle);
00449 d->loaded_stack.prepend(wrap);
00450 }
00451 m_libs.insert( name, wrap );
00452
00453 connect( wrap->lib, SIGNAL( destroyed() ),
00454 this, SLOT( slotLibraryDestroyed() ) );
00455
00456 return wrap->lib;
00457 }
00458
00459 QString KLibLoader::lastErrorMessage() const
00460 {
00461 return d->errorMessage;
00462 }
00463
00464 void KLibLoader::unloadLibrary( const char *libname )
00465 {
00466 KLibWrapPrivate *wrap = m_libs[ libname ];
00467 if (!wrap)
00468 return;
00469 if (--wrap->ref_count)
00470 return;
00471
00472
00473
00474 m_libs.remove( libname );
00475
00476 disconnect( wrap->lib, SIGNAL( destroyed() ),
00477 this, SLOT( slotLibraryDestroyed() ) );
00478 close_pending( wrap );
00479 }
00480
00481 KLibFactory* KLibLoader::factory( const char* name )
00482 {
00483 KLibrary* lib = library( name );
00484 if ( !lib )
00485 return 0;
00486
00487 return lib->factory();
00488 }
00489
00490 void KLibLoader::slotLibraryDestroyed()
00491 {
00492 const KLibrary *lib = static_cast<const KLibrary *>( sender() );
00493
00494 QAsciiDictIterator<KLibWrapPrivate> it( m_libs );
00495 for (; it.current(); ++it )
00496 if ( it.current()->lib == lib )
00497 {
00498 KLibWrapPrivate *wrap = it.current();
00499 wrap->lib = 0;
00500 m_libs.remove( it.currentKey() );
00501 close_pending( wrap );
00502 return;
00503 }
00504 }
00505
00506 void KLibLoader::close_pending(KLibWrapPrivate *wrap)
00507 {
00508 if (wrap && !d->pending_close.containsRef( wrap ))
00509 d->pending_close.append( wrap );
00510
00511
00512
00513 QPtrListIterator<KLibWrapPrivate> it(d->pending_close);
00514 for (; it.current(); ++it) {
00515 wrap = it.current();
00516 if (wrap->lib) {
00517 disconnect( wrap->lib, SIGNAL( destroyed() ),
00518 this, SLOT( slotLibraryDestroyed() ) );
00519 KLibrary* to_delete = wrap->lib;
00520 wrap->lib = 0L;
00521 delete to_delete;
00522 }
00523 }
00524
00525 if (d->unload_mode == KLibLoaderPrivate::DONT_UNLOAD) {
00526 d->pending_close.clear();
00527 return;
00528 }
00529
00530 bool deleted_one = false;
00531 while ((wrap = d->loaded_stack.first())) {
00532
00533
00534
00535
00536 if (d->unload_mode != KLibLoaderPrivate::UNLOAD
00537 && wrap->unload_mode != KLibWrapPrivate::UNLOAD)
00538 break;
00539
00540
00541
00542 if (!d->pending_close.containsRef( wrap )) {
00543 if (!deleted_one)
00544
00545
00546 break;
00547 }
00548
00549
00550
00551 if ( !deleted_one ) {
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561 if( kapp->clipboard()->ownsSelection()) {
00562 kapp->clipboard()->setText(
00563 kapp->clipboard()->text( QClipboard::Selection ), QClipboard::Selection );
00564 }
00565 if( kapp->clipboard()->ownsClipboard()) {
00566 kapp->clipboard()->setText(
00567 kapp->clipboard()->text( QClipboard::Clipboard ), QClipboard::Clipboard );
00568 }
00569 }
00570
00571 deleted_one = true;
00572 lt_dlclose(wrap->handle);
00573 d->pending_close.removeRef(wrap);
00574
00575 d->loaded_stack.remove();
00576 }
00577 }
00578
00579 void KLibLoader::virtual_hook( int, void* )
00580 { }
00581
00582 void KLibFactory::virtual_hook( int, void* )
00583 { }
00584
00585 #include "klibloader.moc"