kdecore Library: klibloader.cpp Source File (kdecore Library)

| # kdecore Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# 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., 59 Temple Place - Suite 330,
    00017    Boston, MA 02111-1307, USA.
    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> //getenv
    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 /* !RTLD_GLOBAL */
    00052 #ifndef LT_GLOBAL
    00053 #  define LT_GLOBAL             0
    00054 #endif /* !LT_GLOBAL */
    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 //    kdDebug(150) << "Deleting KLibFactory " << this << endl;
    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     /* Make sure, we have a KLibLoader */
    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 //    kdDebug(150) << "Deleting KLibrary " << this << "  " << m_libname << endl;
    00112     if ( m_timer && m_timer->isActive() )
    00113     m_timer->stop();
    00114
    00115     // If any object is remaining, delete
    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 //  kdDebug(150) << " ... deleting the factory " << m_factory << endl;
    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() ).arg( "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::fromLatin1( 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; // we know this object already
    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 //    kdDebug(150) << "KLibrary: shutdown timer for " << name() << " started!"
    00228 //                 << endl;
    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     // as long as it's not stable make the timeout short, for debugging
    00238     // pleasure (matz)
    00239     //m_timer->start( 1000*60, true );
    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   /* Don't go through KLibLoader::unloadLibrary(), because that uses the
    00250      ref counter, but this timeout means to unconditionally close this library
    00251      The destroyed() signal will take care to remove us from all lists.
    00252   */
    00253   delete this;
    00254 }
    00255
    00256 // -------------------------------------------------
    00257
    00258 /* This helper class is needed, because KLibraries can go away without
    00259    being unloaded. So we need some info about KLibraries even after its
    00260    death. */
    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 //        kdDebug(150) << "Will not unload " << name << endl;
    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 //    kdDebug(150) << "Deleting KLibLoader " << this << "  " << name() << endl;
    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     // only append ".la" if there is no extension
    00338     // this allows to load non-libtool libraries as well
    00339     // (mhk, 20000228)
    00340     int pos = libname.findRev('/');
    00341     if (pos < 0)
    00342       pos = 0;
    00343     if (libname.find('.', pos) < 0)
    00344       libname += ".la";
    00345     return libname;
    00346 }
    00347
    00348 //static
    00349 QString KLibLoader::findLibrary( const char * name, const KInstance * instance )
    00350 {
    00351     QCString libname = makeLibName( name );
    00352
    00353     // only look up the file if it is not an absolute filename
    00354     // (mhk, 20000228)
    00355     QString libfile;
    00356     if (!QDir::isRelativePath(libname))
    00357       libfile = QFile::decodeName( libname );
    00358     else
    00359     {
    00360       libfile = instance->dirs()->findResource( "module", libname );
    00361       if ( libfile.isEmpty() )
    00362       {
    00363         libfile = instance->dirs()->findResource( "lib", libname );
    00364 #ifndef NDEBUG
    00365         if ( !libfile.isEmpty() && libname.left(3) == "lib" ) // don't warn for kdeinit modules
    00366           kdDebug(150) << "library " << libname << " not found under 'module' but under 'lib'" << endl;
    00367 #endif
    00368       }
    00369     }
    00370     return libfile;
    00371 }
    00372
    00373
    00374 KLibrary* KLibLoader::globalLibrary( const char *name )
    00375 {
    00376 KLibrary *tmp;
    00377 int olt_dlopen_flag = lt_dlopen_flag;
    00378
    00379    lt_dlopen_flag |= LT_GLOBAL;
    00380    kdDebug(150) << "Loading the next library global with flag "
    00381                 << lt_dlopen_flag
    00382                 << "." << endl;
    00383    tmp = library(name);
    00384    lt_dlopen_flag = olt_dlopen_flag;
    00385
    00386 return tmp;
    00387 }
    00388
    00389
    00390 KLibrary* KLibLoader::library( const char *name )
    00391 {
    00392     if (!name)
    00393         return 0;
    00394
    00395     KLibWrapPrivate* wrap = m_libs[name];
    00396     if (wrap) {
    00397       /* Nothing to do to load the library.  */
    00398       wrap->ref_count++;
    00399       return wrap->lib;
    00400     }
    00401
    00402     /* Test if this library was loaded at some time, but got
    00403        unloaded meanwhile, whithout being dlclose()'ed.  */
    00404     QPtrListIterator<KLibWrapPrivate> it(d->loaded_stack);
    00405     for (; it.current(); ++it) {
    00406       if (it.current()->name == name)
    00407         wrap = it.current();
    00408     }
    00409
    00410     if (wrap) {
    00411       d->pending_close.removeRef(wrap);
    00412       if (!wrap->lib) {
    00413         /* This lib only was in loaded_stack, but not in m_libs.  */
    00414         wrap->lib = new KLibrary( name, wrap->filename, wrap->handle );
    00415       }
    00416       wrap->ref_count++;
    00417     } else {
    00418       QString libfile = findLibrary( name );
    00419       if ( libfile.isEmpty() )
    00420       {
    00421         const QCString libname = makeLibName( name );
    00422 #ifndef NDEBUG
    00423         kdDebug(150) << "library=" << name << ": No file named " << libname << " found in paths." << endl;
    00424 #endif
    00425         d->errorMessage = i18n("Library files for \"%1\" not found in paths.").arg(libname);
    00426         return 0;
    00427       }
    00428
    00429       lt_dlhandle handle = lt_dlopen( QFile::encodeName(libfile) );
    00430       if ( !handle )
    00431       {
    00432         const char* errmsg = lt_dlerror();
    00433         if(errmsg)
    00434             d->errorMessage = QString::fromLatin1(errmsg);
    00435         else
    00436             d->errorMessage = QString::null;
    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 //  kdDebug(150) << "closing library " << libname << endl;
    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;  /* the KLibrary object is already away */
    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   /* First delete all KLibrary objects in pending_close, but _don't_ unload
    00507      the DSO behind it.  */
    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       KLibrary* to_delete = wrap->lib;
    00515       wrap->lib = 0L; // unset first, because KLibrary dtor can cause
    00516       delete to_delete; // recursive call to close_pending()
    00517     }
    00518   }
    00519
    00520   if (d->unload_mode == KLibLoaderPrivate::DONT_UNLOAD) {
    00521     d->pending_close.clear();
    00522     return;
    00523   }
    00524
    00525   bool deleted_one = false;
    00526   while ((wrap = d->loaded_stack.first())) {
    00527     /* Let's first see, if we want to try to unload this lib.
    00528        If the env. var KDE_DOUNLOAD is set, we try to unload every lib.
    00529        If not, we look at the lib itself, and unload it only, if it exports
    00530        the symbol __kde_do_unload. */
    00531     if (d->unload_mode != KLibLoaderPrivate::UNLOAD
    00532         && wrap->unload_mode != KLibWrapPrivate::UNLOAD)
    00533       break;
    00534
    00535     /* Now ensure, that the libs are only unloaded in the reverse direction
    00536        they were loaded.  */
    00537     if (!d->pending_close.containsRef( wrap )) {
    00538       if (!deleted_one)
    00539         /* Only diagnose, if we really haven't deleted anything. */
    00540 //        kdDebug(150) << "try to dlclose " << wrap->name << ": not yet" << endl;
    00541       break;
    00542     }
    00543
    00544 //    kdDebug(150) << "try to dlclose " << wrap->name << ": yes, done." << endl;
    00545
    00546     if ( !deleted_one ) {
    00547       /* Only do the hack once in this loop.
    00548          WABA: *HACK*
    00549          We need to make sure to clear the clipboard before unloading a DSO
    00550          because the DSO could have defined an object derived from QMimeSource
    00551          and placed that on the clipboard. */
    00552       /*kapp->clipboard()->clear();*/
    00553
    00554       /* Well.. let's do something more subtle... convert the clipboard context
    00555          to text. That should be safe as it only uses objects defined by Qt. */
    00556       if( kapp->clipboard()->ownsSelection()) {
    00557     kapp->clipboard()->setText(
    00558             kapp->clipboard()->text( QClipboard::Selection ), QClipboard::Selection );
    00559       }
    00560       if( kapp->clipboard()->ownsClipboard()) {
    00561     kapp->clipboard()->setText(
    00562             kapp->clipboard()->text( QClipboard::Clipboard ), QClipboard::Clipboard );
    00563       }
    00564     }
    00565
    00566     deleted_one = true;
    00567     lt_dlclose(wrap->handle);
    00568     d->pending_close.removeRef(wrap);
    00569     /* loaded_stack is AutoDelete, so wrap is freed */
    00570     d->loaded_stack.remove();
    00571   }
    00572 }
    00573
    00574 void KLibLoader::virtual_hook( int, void* )
    00575 { /*BASE::virtual_hook( id, data );*/ }
    00576
    00577 void KLibFactory::virtual_hook( int, void* )
    00578 { /*BASE::virtual_hook( id, data );*/ }
    00579
    00580 #include "klibloader.moc"

This file is part of the documentation for kdecore Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:18:09 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003