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 <qdir.h>
00025 #include <qtimer.h>
00026 #include <qobjectdict.h>
00027
00028 #include "kapplication.h"
00029 #include "klibloader.h"
00030 #include "kstandarddirs.h"
00031 #include "kdebug.h"
00032 #include "klocale.h"
00033
00034 #include "ltdl.h"
00035
00036 template class QAsciiDict<KLibrary>;
00037
00038 #include <stdlib.h>
00039
00040
00041 #if HAVE_DLFCN_H
00042 # include <dlfcn.h>
00043 #endif
00044
00045 #ifdef RTLD_GLOBAL
00046 # define LT_GLOBAL RTLD_GLOBAL
00047 #else
00048 # ifdef DL_GLOBAL
00049 # define LT_GLOBAL DL_GLOBAL
00050 # endif
00051 #endif
00052 #ifndef LT_GLOBAL
00053 # define LT_GLOBAL 0
00054 #endif
00055
00056
00057 class KLibLoaderPrivate
00058 {
00059 public:
00060 QPtrList<KLibWrapPrivate> loaded_stack;
00061 QPtrList<KLibWrapPrivate> pending_close;
00062 enum {UNKNOWN, UNLOAD, DONT_UNLOAD} unload_mode;
00063
00064 QString errorMessage;
00065 };
00066
00067 KLibLoader* KLibLoader::s_self = 0;
00068
00069
00070
00071 KLibFactory::KLibFactory( QObject* parent, const char* name )
00072 : QObject( parent, name )
00073 {
00074 }
00075
00076 KLibFactory::~KLibFactory()
00077 {
00078
00079 }
00080
00081 QObject* KLibFactory::create( QObject* parent, const char* name, const char* classname, const QStringList &args )
00082 {
00083 QObject* obj = createObject( parent, name, classname, args );
00084 if ( obj )
00085 emit objectCreated( obj );
00086 return obj;
00087 }
00088
00089
00090 QObject* KLibFactory::createObject( QObject*, const char*, const char*, const QStringList &)
00091 {
00092 return 0;
00093 }
00094
00095
00096
00097
00098 KLibrary::KLibrary( const QString& libname, const QString& filename, void * handle )
00099 {
00100
00101 (void) KLibLoader::self();
00102 m_libname = libname;
00103 m_filename = filename;
00104 m_handle = handle;
00105 m_factory = 0;
00106 m_timer = 0;
00107 }
00108
00109 KLibrary::~KLibrary()
00110 {
00111
00112 if ( m_timer && m_timer->isActive() )
00113 m_timer->stop();
00114
00115
00116 if ( m_objs.count() > 0 )
00117 {
00118 QPtrListIterator<QObject> it( m_objs );
00119 for ( ; it.current() ; ++it )
00120 {
00121 kdDebug(150) << "Factory still has object " << it.current() << " " << it.current()->name () << " Library = " << m_libname << endl;
00122 disconnect( it.current(), SIGNAL( destroyed() ),
00123 this, SLOT( slotObjectDestroyed() ) );
00124 }
00125 m_objs.setAutoDelete(true);
00126 m_objs.clear();
00127 }
00128
00129 if ( m_factory ) {
00130
00131 delete m_factory;
00132 m_factory = 0L;
00133 }
00134 }
00135
00136 QString KLibrary::name() const
00137 {
00138 return m_libname;
00139 }
00140
00141 QString KLibrary::fileName() const
00142 {
00143 return m_filename;
00144 }
00145
00146 KLibFactory* KLibrary::factory()
00147 {
00148 if ( m_factory )
00149 return m_factory;
00150
00151 QCString symname;
00152 symname.sprintf("init_%s", name().latin1() );
00153
00154 void* sym = symbol( symname );
00155 if ( !sym )
00156 {
00157 KLibLoader::self()->d->errorMessage = i18n( "The library %1 does not offer an %2 function." ).arg( name(), "init_" + name() );
00158 kdWarning(150) << KLibLoader::self()->d->errorMessage << endl;
00159 return 0;
00160 }
00161
00162 typedef KLibFactory* (*t_func)();
00163 t_func func = (t_func)sym;
00164 m_factory = func();
00165
00166 if( !m_factory )
00167 {
00168 KLibLoader::self()->d->errorMessage = i18n( "The library %1 does not offer a KDE compatible factory." ).arg( name() );
00169 kdWarning(150) << KLibLoader::self()->d->errorMessage << endl;
00170 return 0;
00171 }
00172
00173 connect( m_factory, SIGNAL( objectCreated( QObject * ) ),
00174 this, SLOT( slotObjectCreated( QObject * ) ) );
00175
00176 return m_factory;
00177 }
00178
00179 void* KLibrary::symbol( const char* symname ) const
00180 {
00181 void* sym = lt_dlsym( (lt_dlhandle) m_handle, symname );
00182 if ( !sym )
00183 {
00184 KLibLoader::self()->d->errorMessage = "KLibrary: " + QString::fromLocal8Bit( lt_dlerror() );
00185 kdWarning(150) << KLibLoader::self()->d->errorMessage << endl;
00186 return 0;
00187 }
00188
00189 return sym;
00190 }
00191
00192 bool KLibrary::hasSymbol( const char* symname ) const
00193 {
00194 void* sym = lt_dlsym( (lt_dlhandle) m_handle, symname );
00195 return (sym != 0L );
00196 }
00197
00198 void KLibrary::unload() const
00199 {
00200 if (KLibLoader::s_self)
00201 KLibLoader::s_self->unloadLibrary(QFile::encodeName(name()));
00202 }
00203
00204 void KLibrary::slotObjectCreated( QObject *obj )
00205 {
00206 if ( !obj )
00207 return;
00208
00209 if ( m_timer && m_timer->isActive() )
00210 m_timer->stop();
00211
00212 if ( m_objs.containsRef( obj ) )
00213 return;
00214
00215 connect( obj, SIGNAL( destroyed() ),
00216 this, SLOT( slotObjectDestroyed() ) );
00217
00218 m_objs.append( obj );
00219 }
00220
00221 void KLibrary::slotObjectDestroyed()
00222 {
00223 m_objs.removeRef( sender() );
00224
00225 if ( m_objs.count() == 0 )
00226 {
00227
00228
00229
00230 if ( !m_timer )
00231 {
00232 m_timer = new QTimer( this, "klibrary_shutdown_timer" );
00233 connect( m_timer, SIGNAL( timeout() ),
00234 this, SLOT( slotTimeout() ) );
00235 }
00236
00237
00238
00239
00240 m_timer->start( 1000*10, true );
00241 }
00242 }
00243
00244 void KLibrary::slotTimeout()
00245 {
00246 if ( m_objs.count() != 0 )
00247 return;
00248
00249
00250
00251
00252
00253 delete this;
00254 }
00255
00256
00257
00258
00259
00260
00261 class KLibWrapPrivate
00262 {
00263 public:
00264 KLibWrapPrivate(KLibrary *l, lt_dlhandle h);
00265
00266 KLibrary *lib;
00267 enum {UNKNOWN, UNLOAD, DONT_UNLOAD} unload_mode;
00268 int ref_count;
00269 lt_dlhandle handle;
00270 QString name;
00271 QString filename;
00272 };
00273
00274 KLibWrapPrivate::KLibWrapPrivate(KLibrary *l, lt_dlhandle h)
00275 : lib(l), ref_count(1), handle(h), name(l->name()), filename(l->fileName())
00276 {
00277 unload_mode = UNKNOWN;
00278 if (lt_dlsym(handle, "__kde_do_not_unload") != 0) {
00279
00280 unload_mode = DONT_UNLOAD;
00281 } else if (lt_dlsym(handle, "__kde_do_unload") != 0) {
00282 unload_mode = UNLOAD;
00283 }
00284 }
00285
00286 KLibLoader* KLibLoader::self()
00287 {
00288 if ( !s_self )
00289 s_self = new KLibLoader;
00290 return s_self;
00291 }
00292
00293 void KLibLoader::cleanUp()
00294 {
00295 if ( !s_self )
00296 return;
00297
00298 delete s_self;
00299 s_self = 0L;
00300 }
00301
00302 KLibLoader::KLibLoader( QObject* parent, const char* name )
00303 : QObject( parent, name )
00304 {
00305 s_self = this;
00306 d = new KLibLoaderPrivate;
00307 lt_dlinit();
00308 d->unload_mode = KLibLoaderPrivate::UNKNOWN;
00309 if (getenv("KDE_NOUNLOAD") != 0)
00310 d->unload_mode = KLibLoaderPrivate::DONT_UNLOAD;
00311 else if (getenv("KDE_DOUNLOAD") != 0)
00312 d->unload_mode = KLibLoaderPrivate::UNLOAD;
00313 d->loaded_stack.setAutoDelete( true );
00314 }
00315
00316 KLibLoader::~KLibLoader()
00317 {
00318
00319
00320 QAsciiDictIterator<KLibWrapPrivate> it( m_libs );
00321 for (; it.current(); ++it )
00322 {
00323 kdDebug(150) << "The KLibLoader contains the library " << it.current()->name
00324 << " (" << it.current()->lib << ")" << endl;
00325 d->pending_close.append(it.current());
00326 }
00327
00328 close_pending(0);
00329
00330 delete d;
00331 d = 0L;
00332 }
00333
00334 static inline QCString makeLibName( const char* name )
00335 {
00336 QCString libname(name);
00337
00338
00339
00340 int pos = libname.findRev('/');
00341 if (pos < 0)
00342 pos = 0;
00343 if (libname.find('.', pos) < 0)
00344 libname += ".so";
00345 if (libname.right(3) == ".la")
00346 libname = libname.replace( libname.size() - 4, 3, ".so");
00347 return libname;
00348 }
00349
00350
00351 QString KLibLoader::findLibrary( const char * name, const KInstance * instance )
00352 {
00353 QCString libname = makeLibName( name );
00354
00355
00356
00357 QString libfile;
00358 if (!QDir::isRelativePath(libname))
00359 libfile = QFile::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" )
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
00400 wrap->ref_count++;
00401 return wrap->lib;
00402 }
00403
00404
00405
00406 QPtrListIterator<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
00416 wrap->lib = new KLibrary( name, wrap->filename, wrap->handle );
00417 }
00418 wrap->ref_count++;
00419 } else {
00420 QString libfile = findLibrary( name );
00421 if ( libfile.isEmpty() )
00422 {
00423 const QCString 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(libname);
00428 return 0;
00429 }
00430
00431 lt_dlhandle handle = lt_dlopen( QFile::encodeName(libfile) );
00432 if ( !handle )
00433 {
00434 const char* errmsg = lt_dlerror();
00435 if(errmsg)
00436 d->errorMessage = QString::fromLocal8Bit(errmsg);
00437 else
00438 d->errorMessage = QString::null;
00439 return 0;
00440 }
00441 else
00442 d->errorMessage = QString::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, SIGNAL( destroyed() ),
00451 this, SLOT( slotLibraryDestroyed() ) );
00452
00453 return wrap->lib;
00454 }
00455
00456 QString 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
00470
00471 m_libs.remove( libname );
00472
00473 disconnect( wrap->lib, SIGNAL( destroyed() ),
00474 this, 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 QAsciiDictIterator<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;
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
00509
00510 QPtrListIterator<KLibWrapPrivate> it(d->pending_close);
00511 for (; it.current(); ++it) {
00512 wrap = it.current();
00513 if (wrap->lib) {
00514 disconnect( wrap->lib, SIGNAL( destroyed() ),
00515 this, SLOT( slotLibraryDestroyed() ) );
00516 KLibrary* to_delete = wrap->lib;
00517 wrap->lib = 0L;
00518 delete to_delete;
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
00530
00531
00532
00533 if (d->unload_mode != KLibLoaderPrivate::UNLOAD
00534 && wrap->unload_mode != KLibWrapPrivate::UNLOAD)
00535 break;
00536
00537
00538
00539 if (!d->pending_close.containsRef( wrap )) {
00540 if (!deleted_one)
00541
00542
00543 break;
00544 }
00545
00546
00547
00548 if ( !deleted_one ) {
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558 if( kapp->clipboard()->ownsSelection()) {
00559 kapp->clipboard()->setText(
00560 kapp->clipboard()->text( QClipboard::Selection ), QClipboard::Selection );
00561 }
00562 if( kapp->clipboard()->ownsClipboard()) {
00563 kapp->clipboard()->setText(
00564 kapp->clipboard()->text( QClipboard::Clipboard ), QClipboard::Clipboard );
00565 }
00566 }
00567
00568 deleted_one = true;
00569 lt_dlclose(wrap->handle);
00570 d->pending_close.removeRef(wrap);
00571
00572 d->loaded_stack.remove();
00573 }
00574 }
00575
00576 void KLibLoader::virtual_hook( int, void* )
00577 { }
00578
00579 void KLibFactory::virtual_hook( int, void* )
00580 { }
00581
00582 #include "klibloader.moc"