• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KDECore

klibloader.cpp

Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor,
00017    Boston, MA 02110-1301, 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(), "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; // 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::fromLocal8Bit(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"

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal