00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <config.h>
00020 #include <qclipboard.h>
00021 #include <qfile.h>
00022 #include <qtimer.h>
00023 #include <qobjectdict.h>
00024 #include <qwidgetlist.h>
00025 #include <qwidget.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 #ifdef Q_WS_X11
00036 #include <X11/Xlib.h>
00037 #include <X11/Xatom.h>
00038 #endif
00039
00040 template class QAsciiDict<KLibrary>;
00041
00042 #include <stdlib.h>
00043
00044
00045 #if HAVE_DLFCN_H
00046 # include <dlfcn.h>
00047 #endif
00048
00049 #ifdef RTLD_GLOBAL
00050 # define LT_GLOBAL RTLD_GLOBAL
00051 #else
00052 # ifdef DL_GLOBAL
00053 # define LT_GLOBAL DL_GLOBAL
00054 # endif
00055 #endif
00056 #ifndef LT_GLOBAL
00057 # define LT_GLOBAL 0
00058 #endif
00059
00060
00061 extern "C" {
00062 extern int lt_dlopen_flag;
00063 }
00064
00065
00066 KLibFactory::KLibFactory( QObject* parent, const char* name )
00067 : QObject( parent, name )
00068 {
00069 }
00070
00071 KLibFactory::~KLibFactory()
00072 {
00073
00074 }
00075
00076 QObject* KLibFactory::create( QObject* parent, const char* name, const char* classname, const QStringList &args )
00077 {
00078 QObject* obj = createObject( parent, name, classname, args );
00079 if ( obj )
00080 emit objectCreated( obj );
00081 return obj;
00082 }
00083
00084
00085 QObject* KLibFactory::createObject( QObject*, const char*, const char*, const QStringList &)
00086 {
00087 return 0;
00088 }
00089
00090
00091
00092
00093 KLibrary::KLibrary( const QString& libname, const QString& filename, void * handle )
00094 {
00095
00096 (void) KLibLoader::self();
00097 m_libname = libname;
00098 m_filename = filename;
00099 m_handle = handle;
00100 m_factory = 0;
00101 m_timer = 0;
00102 }
00103
00104 KLibrary::~KLibrary()
00105 {
00106
00107 if ( m_timer && m_timer->isActive() )
00108 m_timer->stop();
00109
00110
00111 if ( m_objs.count() > 0 )
00112 {
00113 QPtrListIterator<QObject> it( m_objs );
00114 for ( ; it.current() ; ++it )
00115 {
00116 kdDebug(150) << "Factory still has object " << it.current() << " " << it.current()->name () << " Library = " << m_libname << endl;
00117 disconnect( it.current(), SIGNAL( destroyed() ),
00118 this, SLOT( slotObjectDestroyed() ) );
00119 }
00120 m_objs.setAutoDelete(true);
00121 m_objs.clear();
00122 }
00123
00124 if ( m_factory ) {
00125
00126 delete m_factory;
00127 }
00128 }
00129
00130 QString KLibrary::name() const
00131 {
00132 return m_libname;
00133 }
00134
00135 QString KLibrary::fileName() const
00136 {
00137 return m_filename;
00138 }
00139
00140 KLibFactory* KLibrary::factory()
00141 {
00142 if ( m_factory )
00143 return m_factory;
00144
00145 QCString symname;
00146 symname.sprintf("init_%s", name().latin1() );
00147
00148 void* sym = symbol( symname );
00149 if ( !sym )
00150 {
00151 kdWarning(150) << "KLibrary: The library " << name() << " does not offer an init_" << name() << " function" << endl;
00152 return 0;
00153 }
00154
00155 typedef KLibFactory* (*t_func)();
00156 t_func func = (t_func)sym;
00157 m_factory = func();
00158
00159 if( !m_factory )
00160 {
00161 kdWarning(150) << "KLibrary: The library " << name() << " does not offer a KDE compatible factory" << endl;
00162 return 0;
00163 }
00164
00165 connect( m_factory, SIGNAL( objectCreated( QObject * ) ),
00166 this, SLOT( slotObjectCreated( QObject * ) ) );
00167
00168 return m_factory;
00169 }
00170
00171 void* KLibrary::symbol( const char* symname ) const
00172 {
00173 void* sym = lt_dlsym( (lt_dlhandle) m_handle, symname );
00174 if ( !sym )
00175 {
00176 kdWarning(150) << "KLibrary: " << lt_dlerror() << endl;
00177 return 0;
00178 }
00179
00180 return sym;
00181 }
00182
00183 bool KLibrary::hasSymbol( const char* symname ) const
00184 {
00185 void* sym = lt_dlsym( (lt_dlhandle) m_handle, symname );
00186 return (sym != 0L );
00187 }
00188
00189 void KLibrary::unload() const
00190 {
00191 if (KLibLoader::s_self)
00192 KLibLoader::s_self->unloadLibrary(QFile::encodeName(name()));
00193 }
00194
00195 void KLibrary::slotObjectCreated( QObject *obj )
00196 {
00197 if ( !obj )
00198 return;
00199
00200 if ( m_timer && m_timer->isActive() )
00201 m_timer->stop();
00202
00203 if ( m_objs.containsRef( obj ) )
00204 return;
00205
00206 connect( obj, SIGNAL( destroyed() ),
00207 this, SLOT( slotObjectDestroyed() ) );
00208
00209 m_objs.append( obj );
00210 }
00211
00212 void KLibrary::slotObjectDestroyed()
00213 {
00214 m_objs.removeRef( sender() );
00215
00216 if ( m_objs.count() == 0 )
00217 {
00218
00219
00220
00221 if ( !m_timer )
00222 {
00223 m_timer = new QTimer( this, "klibrary_shutdown_timer" );
00224 connect( m_timer, SIGNAL( timeout() ),
00225 this, SLOT( slotTimeout() ) );
00226 }
00227
00228
00229
00230
00231 m_timer->start( 1000*10, true );
00232 }
00233 }
00234
00235 void KLibrary::slotTimeout()
00236 {
00237 if ( m_objs.count() != 0 )
00238 return;
00239
00240
00241
00242
00243
00244 delete this;
00245 }
00246
00247
00248
00249
00250
00251
00252 class KLibWrapPrivate
00253 {
00254 public:
00255 KLibWrapPrivate(KLibrary *l, lt_dlhandle h);
00256
00257 KLibrary *lib;
00258 enum {UNKNOWN, UNLOAD, DONT_UNLOAD} unload_mode;
00259 int ref_count;
00260 lt_dlhandle handle;
00261 QString name;
00262 QString filename;
00263 };
00264
00265 KLibWrapPrivate::KLibWrapPrivate(KLibrary *l, lt_dlhandle h)
00266 : lib(l), ref_count(1), handle(h), name(l->name()), filename(l->fileName())
00267 {
00268 unload_mode = UNKNOWN;
00269 if (lt_dlsym(handle, "__kde_do_not_unload") != 0) {
00270
00271 unload_mode = DONT_UNLOAD;
00272 } else if (lt_dlsym(handle, "__kde_do_unload") != 0) {
00273 unload_mode = UNLOAD;
00274 }
00275 }
00276
00277 class KLibLoaderPrivate
00278 {
00279 public:
00280 QPtrList<KLibWrapPrivate> loaded_stack;
00281 QPtrList<KLibWrapPrivate> pending_close;
00282 enum {UNKNOWN, UNLOAD, DONT_UNLOAD} unload_mode;
00283
00284 QString errorMessage;
00285 };
00286
00287 KLibLoader* KLibLoader::s_self = 0;
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 = 0;
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 }
00335
00336
00337 QString KLibLoader::findLibrary( const char * name, const KInstance * instance )
00338 {
00339 QCString libname( name );
00340
00341
00342
00343
00344 int pos = libname.findRev('/');
00345 if (pos < 0)
00346 pos = 0;
00347 if (libname.find('.', pos) < 0)
00348 libname += ".la";
00349
00350
00351
00352 QString libfile;
00353 if (libname[0] == '/')
00354 libfile = libname;
00355 else
00356 {
00357 libfile = instance->dirs()->findResource( "module", libname );
00358 if ( libfile.isEmpty() )
00359 {
00360 libfile = instance->dirs()->findResource( "lib", libname );
00361 #ifndef NDEBUG
00362 if ( !libfile.isEmpty() && libname.left(3) == "lib" )
00363 kdDebug(150) << "library " << libname << " not found under 'module' but under 'lib'" << endl;
00364 #endif
00365 }
00366 if ( libfile.isEmpty() )
00367 {
00368 #ifndef NDEBUG
00369 kdDebug(150) << "library=" << libname << ": No file names " << libname.data() << " found in paths." << endl;
00370 #endif
00371 self()->d->errorMessage = i18n("Library files for \"%1\" not found in paths").arg(libname);
00372 }
00373 else
00374 self()->d->errorMessage = QString::null;
00375 }
00376 return libfile;
00377 }
00378
00379
00380 KLibrary* KLibLoader::globalLibrary( const char *name )
00381 {
00382 KLibrary *tmp;
00383 int olt_dlopen_flag = lt_dlopen_flag;
00384
00385 lt_dlopen_flag |= LT_GLOBAL;
00386 kdDebug(150) << "Loading the next library global with flag "
00387 << lt_dlopen_flag
00388 << "." << endl;
00389 tmp = library(name);
00390 lt_dlopen_flag = olt_dlopen_flag;
00391
00392 return tmp;
00393 }
00394
00395
00396 KLibrary* KLibLoader::library( const char *name )
00397 {
00398 if (!name)
00399 return 0;
00400
00401 KLibWrapPrivate* wrap = m_libs[name];
00402 if (wrap) {
00403
00404 wrap->ref_count++;
00405 return wrap->lib;
00406 }
00407
00408
00409
00410 QPtrListIterator<KLibWrapPrivate> it(d->loaded_stack);
00411 for (; it.current(); ++it) {
00412 if (it.current()->name == name)
00413 wrap = it.current();
00414 }
00415
00416 if (wrap) {
00417 d->pending_close.removeRef(wrap);
00418 if (!wrap->lib) {
00419
00420 wrap->lib = new KLibrary( name, wrap->filename, wrap->handle );
00421 }
00422 wrap->ref_count++;
00423 } else {
00424 QString libfile = findLibrary( name );
00425 if ( libfile.isEmpty() )
00426 return 0;
00427
00428 lt_dlhandle handle = lt_dlopen( libfile.latin1() );
00429 if ( !handle )
00430 {
00431 const char* errmsg = lt_dlerror();
00432 if(errmsg)
00433 d->errorMessage = QString::fromLatin1(errmsg);
00434 else
00435 d->errorMessage = QString::null;
00436 kdWarning(150) << "library=" << name << ": file=" << libfile << ": " << d->errorMessage << endl;
00437 return 0;
00438 }
00439 else
00440 d->errorMessage = QString::null;
00441
00442 KLibrary *lib = new KLibrary( name, libfile, handle );
00443 wrap = new KLibWrapPrivate(lib, handle);
00444 d->loaded_stack.prepend(wrap);
00445 }
00446 m_libs.insert( name, wrap );
00447
00448 connect( wrap->lib, SIGNAL( destroyed() ),
00449 this, SLOT( slotLibraryDestroyed() ) );
00450
00451 return wrap->lib;
00452 }
00453
00454 QString KLibLoader::lastErrorMessage() const
00455 {
00456 return d->errorMessage;
00457 }
00458
00459 void KLibLoader::unloadLibrary( const char *libname )
00460 {
00461 KLibWrapPrivate *wrap = m_libs[ libname ];
00462 if (!wrap)
00463 return;
00464 if (--wrap->ref_count)
00465 return;
00466
00467
00468
00469 m_libs.remove( libname );
00470
00471 disconnect( wrap->lib, SIGNAL( destroyed() ),
00472 this, SLOT( slotLibraryDestroyed() ) );
00473 close_pending( wrap );
00474 }
00475
00476 KLibFactory* KLibLoader::factory( const char* name )
00477 {
00478 KLibrary* lib = library( name );
00479 if ( !lib )
00480 return 0;
00481
00482 return lib->factory();
00483 }
00484
00485 void KLibLoader::slotLibraryDestroyed()
00486 {
00487 const KLibrary *lib = static_cast<const KLibrary *>( sender() );
00488
00489 QAsciiDictIterator<KLibWrapPrivate> it( m_libs );
00490 for (; it.current(); ++it )
00491 if ( it.current()->lib == lib )
00492 {
00493 KLibWrapPrivate *wrap = it.current();
00494 wrap->lib = 0;
00495 m_libs.remove( it.currentKey() );
00496 close_pending( wrap );
00497 return;
00498 }
00499 }
00500
00501 void KLibLoader::close_pending(KLibWrapPrivate *wrap)
00502 {
00503 if (wrap && !d->pending_close.containsRef( wrap ))
00504 d->pending_close.append( wrap );
00505
00506
00507
00508 QPtrListIterator<KLibWrapPrivate> it(d->pending_close);
00509 for (; it.current(); ++it) {
00510 wrap = it.current();
00511 if (wrap->lib) {
00512 disconnect( wrap->lib, SIGNAL( destroyed() ),
00513 this, SLOT( slotLibraryDestroyed() ) );
00514 delete wrap->lib;
00515 wrap->lib = 0;
00516 }
00517 }
00518
00519 if (d->unload_mode == KLibLoaderPrivate::DONT_UNLOAD) return;
00520
00521 bool deleted_one = false;
00522 while ((wrap = d->loaded_stack.first())) {
00523
00524
00525
00526
00527 if (d->unload_mode != KLibLoaderPrivate::UNLOAD
00528 && wrap->unload_mode != KLibWrapPrivate::UNLOAD)
00529 break;
00530
00531
00532
00533 if (!d->pending_close.containsRef( wrap )) {
00534 if (!deleted_one)
00535
00536
00537 break;
00538 }
00539
00540
00541
00542 #ifndef Q_WS_QWS
00543 if ( !deleted_one ) {
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554 QWidgetList *widgetlist = QApplication::topLevelWidgets();
00555 QWidget *co = widgetlist->first();
00556 while (co) {
00557 if (qstrcmp(co->name(), "internal clipboard owner") == 0) {
00558 if (XGetSelectionOwner(co->x11Display(), XA_PRIMARY) == co->winId())
00559 kapp->clipboard()->setText(kapp->clipboard()->text());
00560
00561 break;
00562 }
00563 co = widgetlist->next();
00564 }
00565 delete widgetlist;
00566 }
00567 #else
00568
00569 #endif
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"