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 class KLibFactoryPrivate {
00067 public:
00068 };
00069
00070 KLibFactory::KLibFactory( QObject* parent, const char* name )
00071 : QObject( parent, name )
00072 {
00073 d = new KLibFactoryPrivate;
00074 }
00075
00076 KLibFactory::~KLibFactory()
00077 {
00078
00079 delete d;
00080 }
00081
00082 QObject* KLibFactory::create( QObject* parent, const char* name, const char* classname, const QStringList &args )
00083 {
00084 QObject* obj = createObject( parent, name, classname, args );
00085 if ( obj )
00086 emit objectCreated( obj );
00087 return obj;
00088 }
00089
00090
00091 QObject* KLibFactory::createObject( QObject*, const char*, const char*, const QStringList &)
00092 {
00093 return 0;
00094 }
00095
00096
00097
00098
00099 class KLibraryPrivate {
00100 public:
00101 };
00102
00103 KLibrary::KLibrary( const QString& libname, const QString& filename, void * handle )
00104 {
00105
00106 (void) KLibLoader::self();
00107 m_libname = libname;
00108 m_filename = filename;
00109 m_handle = handle;
00110 m_factory = 0;
00111 m_timer = 0;
00112 d = new KLibraryPrivate;
00113 }
00114
00115 KLibrary::~KLibrary()
00116 {
00117
00118 if ( m_timer && m_timer->isActive() )
00119 m_timer->stop();
00120
00121
00122 if ( m_objs.count() > 0 )
00123 {
00124 QPtrListIterator<QObject> it( m_objs );
00125 for ( ; it.current() ; ++it )
00126 {
00127 kdDebug(150) << "Factory still has object " << it.current() << " " << it.current()->name () << " Library = " << m_libname << endl;
00128 disconnect( it.current(), SIGNAL( destroyed() ),
00129 this, SLOT( slotObjectDestroyed() ) );
00130 }
00131 m_objs.setAutoDelete(true);
00132 m_objs.clear();
00133 }
00134
00135 if ( m_factory ) {
00136
00137 delete m_factory;
00138 }
00139 delete d;
00140 }
00141
00142 QString KLibrary::name() const
00143 {
00144 return m_libname;
00145 }
00146
00147 QString KLibrary::fileName() const
00148 {
00149 return m_filename;
00150 }
00151
00152 KLibFactory* KLibrary::factory()
00153 {
00154 if ( m_factory )
00155 return m_factory;
00156
00157 QCString symname;
00158 symname.sprintf("init_%s", name().latin1() );
00159
00160 void* sym = symbol( symname );
00161 if ( !sym )
00162 {
00163 kdWarning(150) << "KLibrary: The library " << name() << " does not offer an init_" << name() << " function" << endl;
00164 return 0;
00165 }
00166
00167 typedef KLibFactory* (*t_func)();
00168 t_func func = (t_func)sym;
00169 m_factory = func();
00170
00171 if( !m_factory )
00172 {
00173 kdWarning(150) << "KLibrary: The library " << name() << " does not offer a KDE compatible factory" << endl;
00174 return 0;
00175 }
00176
00177 connect( m_factory, SIGNAL( objectCreated( QObject * ) ),
00178 this, SLOT( slotObjectCreated( QObject * ) ) );
00179
00180 return m_factory;
00181 }
00182
00183 void* KLibrary::symbol( const char* symname ) const
00184 {
00185 void* sym = lt_dlsym( (lt_dlhandle) m_handle, symname );
00186 if ( !sym )
00187 {
00188 kdWarning(150) << "KLibrary: " << lt_dlerror() << 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 KLibLoader::self()->unloadLibrary(QFile::encodeName(name()));
00204 }
00205
00206 void KLibrary::slotObjectCreated( QObject *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;
00216
00217 connect( obj, SIGNAL( destroyed() ),
00218 this, SLOT( slotObjectDestroyed() ) );
00219
00220 m_objs.append( obj );
00221 }
00222
00223 void KLibrary::slotObjectDestroyed()
00224 {
00225 m_objs.removeRef( sender() );
00226
00227 if ( m_objs.count() == 0 )
00228 {
00229
00230
00231
00232 if ( !m_timer )
00233 {
00234 m_timer = new QTimer( this, "klibrary_shutdown_timer" );
00235 connect( m_timer, SIGNAL( timeout() ),
00236 this, SLOT( slotTimeout() ) );
00237 }
00238
00239
00240
00241
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
00252
00253
00254
00255 delete this;
00256 }
00257
00258
00259
00260
00261
00262
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 QString name;
00273 QString 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
00282 unload_mode = DONT_UNLOAD;
00283 } else if (lt_dlsym(handle, "__kde_do_unload") != 0) {
00284 unload_mode = UNLOAD;
00285 }
00286 }
00287
00288 class KLibLoaderPrivate
00289 {
00290 public:
00291 QPtrList<KLibWrapPrivate> loaded_stack;
00292 QPtrList<KLibWrapPrivate> pending_close;
00293 enum {UNKNOWN, UNLOAD, DONT_UNLOAD} unload_mode;
00294
00295 QString errorMessage;
00296 };
00297
00298 KLibLoader* KLibLoader::s_self = 0;
00299
00300 KLibLoader* KLibLoader::self()
00301 {
00302 if ( !s_self )
00303 s_self = new KLibLoader;
00304 return s_self;
00305 }
00306
00307 void KLibLoader::cleanUp()
00308 {
00309 if ( !s_self )
00310 return;
00311
00312 delete s_self;
00313 s_self = 0;
00314 }
00315
00316 KLibLoader::KLibLoader( QObject* parent, const char* name )
00317 : QObject( parent, name )
00318 {
00319 s_self = this;
00320 d = new KLibLoaderPrivate;
00321 lt_dlinit();
00322 d->unload_mode = KLibLoaderPrivate::UNKNOWN;
00323 if (getenv("KDE_NOUNLOAD") != 0)
00324 d->unload_mode = KLibLoaderPrivate::DONT_UNLOAD;
00325 else if (getenv("KDE_DOUNLOAD") != 0)
00326 d->unload_mode = KLibLoaderPrivate::UNLOAD;
00327 d->loaded_stack.setAutoDelete( true );
00328 }
00329
00330 KLibLoader::~KLibLoader()
00331 {
00332
00333
00334 QAsciiDictIterator<KLibWrapPrivate> it( m_libs );
00335 for (; it.current(); ++it )
00336 {
00337 kdDebug(150) << "The KLibLoader contains the library " << it.current()->name
00338 << " (" << it.current()->lib << ")" << endl;
00339 d->pending_close.append(it.current());
00340 }
00341
00342 close_pending(0);
00343
00344 delete d;
00345 }
00346
00347
00348 QString KLibLoader::findLibrary( const char * name, const KInstance * instance )
00349 {
00350 QCString libname( name );
00351
00352
00353
00354
00355 int pos = libname.findRev('/');
00356 if (pos < 0)
00357 pos = 0;
00358 if (libname.find('.', pos) < 0)
00359 libname += ".la";
00360
00361
00362
00363 QString libfile;
00364 if (libname[0] == '/')
00365 libfile = libname;
00366 else
00367 {
00368 libfile = instance->dirs()->findResource( "module", libname );
00369 if ( libfile.isEmpty() )
00370 {
00371 libfile = instance->dirs()->findResource( "lib", libname );
00372 #ifndef NDEBUG
00373 if ( !libfile.isEmpty() && libname.left(3) == "lib" )
00374 kdDebug(150) << "library " << libname << " not found under 'module' but under 'lib'" << endl;
00375 #endif
00376 }
00377 if ( libfile.isEmpty() )
00378 {
00379 #ifndef NDEBUG
00380 kdDebug(150) << "library=" << libname << ": No file names " << libname.data() << " found in paths." << endl;
00381 #endif
00382 self()->d->errorMessage = i18n("Library files for \"%1\" not found in paths").arg(libname);
00383 }
00384 else
00385 self()->d->errorMessage = QString::null;
00386 }
00387 return libfile;
00388 }
00389
00390
00391 KLibrary* KLibLoader::globalLibrary( const char *name )
00392 {
00393 KLibrary *tmp;
00394 int olt_dlopen_flag = lt_dlopen_flag;
00395
00396 lt_dlopen_flag |= LT_GLOBAL;
00397 kdDebug(150) << "Loading the next library global with flag "
00398 << lt_dlopen_flag
00399 << "." << endl;
00400 tmp = library(name);
00401 lt_dlopen_flag = olt_dlopen_flag;
00402
00403 return tmp;
00404 }
00405
00406
00407 KLibrary* KLibLoader::library( const char *name )
00408 {
00409 if (!name)
00410 return 0;
00411
00412 KLibWrapPrivate* wrap = m_libs[name];
00413 if (wrap) {
00414
00415 wrap->ref_count++;
00416 return wrap->lib;
00417 }
00418
00419
00420
00421 QPtrListIterator<KLibWrapPrivate> it(d->loaded_stack);
00422 for (; it.current(); ++it) {
00423 if (it.current()->name == name)
00424 wrap = it.current();
00425 }
00426
00427 if (wrap) {
00428 d->pending_close.removeRef(wrap);
00429 if (!wrap->lib) {
00430
00431 wrap->lib = new KLibrary( name, wrap->filename, wrap->handle );
00432 }
00433 wrap->ref_count++;
00434 } else {
00435 QString libfile = findLibrary( name );
00436 if ( libfile.isEmpty() )
00437 return 0;
00438
00439 lt_dlhandle handle = lt_dlopen( libfile.latin1() );
00440 if ( !handle )
00441 {
00442 const char* errmsg = lt_dlerror();
00443 if(errmsg)
00444 d->errorMessage = QString::fromLatin1(errmsg);
00445 else
00446 d->errorMessage = QString::null;
00447 kdWarning(150) << "library=" << name << ": file=" << libfile << ": " << d->errorMessage << endl;
00448 return 0;
00449 }
00450 else
00451 d->errorMessage = QString::null;
00452
00453 KLibrary *lib = new KLibrary( name, libfile, handle );
00454 wrap = new KLibWrapPrivate(lib, handle);
00455 d->loaded_stack.prepend(wrap);
00456 }
00457 m_libs.insert( name, wrap );
00458
00459 connect( wrap->lib, SIGNAL( destroyed() ),
00460 this, SLOT( slotLibraryDestroyed() ) );
00461
00462 return wrap->lib;
00463 }
00464
00465 QString KLibLoader::lastErrorMessage() const
00466 {
00467 return d->errorMessage;
00468 }
00469
00470 void KLibLoader::unloadLibrary( const char *libname )
00471 {
00472 KLibWrapPrivate *wrap = m_libs[ libname ];
00473 if (!wrap)
00474 return;
00475 if (--wrap->ref_count)
00476 return;
00477
00478
00479
00480 m_libs.remove( libname );
00481
00482 disconnect( wrap->lib, SIGNAL( destroyed() ),
00483 this, SLOT( slotLibraryDestroyed() ) );
00484 close_pending( wrap );
00485 }
00486
00487 KLibFactory* KLibLoader::factory( const char* name )
00488 {
00489 KLibrary* lib = library( name );
00490 if ( !lib )
00491 return 0;
00492
00493 return lib->factory();
00494 }
00495
00496 void KLibLoader::slotLibraryDestroyed()
00497 {
00498 const KLibrary *lib = static_cast<const KLibrary *>( sender() );
00499
00500 QAsciiDictIterator<KLibWrapPrivate> it( m_libs );
00501 for (; it.current(); ++it )
00502 if ( it.current()->lib == lib )
00503 {
00504 KLibWrapPrivate *wrap = it.current();
00505 wrap->lib = 0;
00506 m_libs.remove( it.currentKey() );
00507 close_pending( wrap );
00508 return;
00509 }
00510 }
00511
00512 void KLibLoader::close_pending(KLibWrapPrivate *wrap)
00513 {
00514 if (wrap && !d->pending_close.containsRef( wrap ))
00515 d->pending_close.append( wrap );
00516
00517
00518
00519 QPtrListIterator<KLibWrapPrivate> it(d->pending_close);
00520 for (; it.current(); ++it) {
00521 wrap = it.current();
00522 if (wrap->lib) {
00523 disconnect( wrap->lib, SIGNAL( destroyed() ),
00524 this, SLOT( slotLibraryDestroyed() ) );
00525 delete wrap->lib;
00526 wrap->lib = 0;
00527 }
00528 }
00529
00530 if (d->unload_mode == KLibLoaderPrivate::DONT_UNLOAD) return;
00531
00532 bool deleted_one = false;
00533 while ((wrap = d->loaded_stack.first())) {
00534
00535
00536
00537
00538 if (d->unload_mode != KLibLoaderPrivate::UNLOAD
00539 && wrap->unload_mode != KLibWrapPrivate::UNLOAD)
00540 break;
00541
00542
00543
00544 if (!d->pending_close.containsRef( wrap )) {
00545 if (!deleted_one)
00546
00547
00548 break;
00549 }
00550
00551
00552
00553 #ifndef Q_WS_QWS
00554 if ( !deleted_one ) {
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565 QWidgetList *widgetlist = QApplication::topLevelWidgets();
00566 QWidget *co = widgetlist->first();
00567 while (co) {
00568 if (qstrcmp(co->name(), "internal clipboard owner") == 0) {
00569 if (XGetSelectionOwner(co->x11Display(), XA_PRIMARY) == co->winId())
00570 kapp->clipboard()->setText(kapp->clipboard()->text());
00571
00572 break;
00573 }
00574 co = widgetlist->next();
00575 }
00576 delete widgetlist;
00577 }
00578 #else
00579
00580 #endif
00581
00582 deleted_one = true;
00583 lt_dlclose(wrap->handle);
00584 d->pending_close.removeRef(wrap);
00585
00586 d->loaded_stack.remove();
00587 }
00588 }
00589
00590 void KLibLoader::virtual_hook( int, void* )
00591 { }
00592
00593 void KLibFactory::virtual_hook( int, void* )
00594 { }
00595
00596 #include "klibloader.moc"