kdecore Library API Documentation

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 <qtimer.h>
00025 #include <qobjectdict.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 template class QAsciiDict<KLibrary>;
00036 
00037 #include <stdlib.h> //getenv
00038 
00039 
00040 #if HAVE_DLFCN_H
00041 #  include <dlfcn.h>
00042 #endif
00043 
00044 #ifdef RTLD_GLOBAL
00045 #  define LT_GLOBAL             RTLD_GLOBAL
00046 #else
00047 #  ifdef DL_GLOBAL
00048 #    define LT_GLOBAL           DL_GLOBAL
00049 #  endif
00050 #endif /* !RTLD_GLOBAL */
00051 #ifndef LT_GLOBAL
00052 #  define LT_GLOBAL             0
00053 #endif /* !LT_GLOBAL */
00054 
00055 
00056 extern "C" {
00057 extern int lt_dlopen_flag;
00058 }
00059 
00060 class KLibLoaderPrivate
00061 {
00062 public:
00063     QPtrList<KLibWrapPrivate> loaded_stack;
00064     QPtrList<KLibWrapPrivate> pending_close;
00065     enum {UNKNOWN, UNLOAD, DONT_UNLOAD} unload_mode;
00066 
00067     QString errorMessage;
00068 };
00069 
00070 KLibLoader* KLibLoader::s_self = 0;
00071 
00072 // -------------------------------------------------------------------------
00073 
00074 KLibFactory::KLibFactory( QObject* parent, const char* name )
00075     : QObject( parent, name )
00076 {
00077 }
00078 
00079 KLibFactory::~KLibFactory()
00080 {
00081 //    kdDebug(150) << "Deleting KLibFactory " << this << endl;
00082 }
00083 
00084 QObject* KLibFactory::create( QObject* parent, const char* name, const char* classname, const QStringList &args )
00085 {
00086     QObject* obj = createObject( parent, name, classname, args );
00087     if ( obj )
00088     emit objectCreated( obj );
00089     return obj;
00090 }
00091 
00092 
00093 QObject* KLibFactory::createObject( QObject*, const char*, const char*, const QStringList &)
00094 {
00095     return 0;
00096 }
00097 
00098 
00099 // -----------------------------------------------
00100 
00101 KLibrary::KLibrary( const QString& libname, const QString& filename, void * handle )
00102 {
00103     /* Make sure, we have a KLibLoader */
00104     (void) KLibLoader::self();
00105     m_libname = libname;
00106     m_filename = filename;
00107     m_handle = handle;
00108     m_factory = 0;
00109     m_timer = 0;
00110 }
00111 
00112 KLibrary::~KLibrary()
00113 {
00114 //    kdDebug(150) << "Deleting KLibrary " << this << "  " << m_libname << endl;
00115     if ( m_timer && m_timer->isActive() )
00116     m_timer->stop();
00117 
00118     // If any object is remaining, delete
00119     if ( m_objs.count() > 0 )
00120     {
00121         QPtrListIterator<QObject> it( m_objs );
00122         for ( ; it.current() ; ++it )
00123         {
00124             kdDebug(150) << "Factory still has object " << it.current() << " " << it.current()->name () << " Library = " << m_libname << endl;
00125             disconnect( it.current(), SIGNAL( destroyed() ),
00126                 this, SLOT( slotObjectDestroyed() ) );
00127         }
00128         m_objs.setAutoDelete(true);
00129         m_objs.clear();
00130     }
00131 
00132     if ( m_factory ) {
00133 //  kdDebug(150) << " ... deleting the factory " << m_factory << endl;
00134     delete m_factory;
00135         m_factory = 0L;
00136     }
00137 }
00138 
00139 QString KLibrary::name() const
00140 {
00141     return m_libname;
00142 }
00143 
00144 QString KLibrary::fileName() const
00145 {
00146     return m_filename;
00147 }
00148 
00149 KLibFactory* KLibrary::factory()
00150 {
00151     if ( m_factory )
00152         return m_factory;
00153 
00154         QCString symname;
00155         symname.sprintf("init_%s", name().latin1() );
00156 
00157     void* sym = symbol( symname );
00158         if ( !sym )
00159         {
00160             KLibLoader::self()->d->errorMessage = i18n( "The library %1 does not offer an %2 function." ).arg( name() ).arg( "init_" + name() );
00161             kdWarning(150) << KLibLoader::self()->d->errorMessage << endl;
00162             return 0;
00163     }
00164 
00165     typedef KLibFactory* (*t_func)();
00166     t_func func = (t_func)sym;
00167     m_factory = func();
00168 
00169     if( !m_factory )
00170     {
00171         KLibLoader::self()->d->errorMessage = i18n( "The library %1 does not offer a KDE compatible factory." ).arg( name() );
00172         kdWarning(150) << KLibLoader::self()->d->errorMessage << endl;
00173         return 0;
00174     }
00175 
00176     connect( m_factory, SIGNAL( objectCreated( QObject * ) ),
00177              this, SLOT( slotObjectCreated( QObject * ) ) );
00178 
00179     return m_factory;
00180 }
00181 
00182 void* KLibrary::symbol( const char* symname ) const
00183 {
00184     void* sym = lt_dlsym( (lt_dlhandle) m_handle, symname );
00185     if ( !sym )
00186     {
00187         KLibLoader::self()->d->errorMessage = "KLibrary: " + QString::fromLatin1( lt_dlerror() );
00188         kdWarning(150) << KLibLoader::self()->d->errorMessage << 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    if (KLibLoader::s_self)
00204       KLibLoader::s_self->unloadLibrary(QFile::encodeName(name()));
00205 }
00206 
00207 void KLibrary::slotObjectCreated( QObject *obj )
00208 {
00209   if ( !obj )
00210     return;
00211 
00212   if ( m_timer && m_timer->isActive() )
00213     m_timer->stop();
00214 
00215   if ( m_objs.containsRef( obj ) )
00216       return; // we know this object already
00217 
00218   connect( obj, SIGNAL( destroyed() ),
00219            this, SLOT( slotObjectDestroyed() ) );
00220 
00221   m_objs.append( obj );
00222 }
00223 
00224 void KLibrary::slotObjectDestroyed()
00225 {
00226   m_objs.removeRef( sender() );
00227 
00228   if ( m_objs.count() == 0 )
00229   {
00230 //    kdDebug(150) << "KLibrary: shutdown timer for " << name() << " started!"
00231 //                 << endl;
00232 
00233     if ( !m_timer )
00234     {
00235       m_timer = new QTimer( this, "klibrary_shutdown_timer" );
00236       connect( m_timer, SIGNAL( timeout() ),
00237                this, SLOT( slotTimeout() ) );
00238     }
00239 
00240     // as long as it's not stable make the timeout short, for debugging
00241     // pleasure (matz)
00242     //m_timer->start( 1000*60, true );
00243     m_timer->start( 1000*10, true );
00244   }
00245 }
00246 
00247 void KLibrary::slotTimeout()
00248 {
00249   if ( m_objs.count() != 0 )
00250     return;
00251 
00252   /* Don't go through KLibLoader::unloadLibrary(), because that uses the
00253      ref counter, but this timeout means to unconditionally close this library
00254      The destroyed() signal will take care to remove us from all lists.
00255   */
00256   delete this;
00257 }
00258 
00259 // -------------------------------------------------
00260 
00261 /* This helper class is needed, because KLibraries can go away without
00262    being unloaded. So we need some info about KLibraries even after its
00263    death. */
00264 class KLibWrapPrivate
00265 {
00266 public:
00267     KLibWrapPrivate(KLibrary *l, lt_dlhandle h);
00268 
00269     KLibrary *lib;
00270     enum {UNKNOWN, UNLOAD, DONT_UNLOAD} unload_mode;
00271     int ref_count;
00272     lt_dlhandle handle;
00273     QString name;
00274     QString filename;
00275 };
00276 
00277 KLibWrapPrivate::KLibWrapPrivate(KLibrary *l, lt_dlhandle h)
00278  : lib(l), ref_count(1), handle(h), name(l->name()), filename(l->fileName())
00279 {
00280     unload_mode = UNKNOWN;
00281     if (lt_dlsym(handle, "__kde_do_not_unload") != 0) {
00282 //        kdDebug(150) << "Will not unload " << name << endl;
00283         unload_mode = DONT_UNLOAD;
00284     } else if (lt_dlsym(handle, "__kde_do_unload") != 0) {
00285         unload_mode = UNLOAD;
00286     }
00287 }
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 = 0L;
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 //    kdDebug(150) << "Deleting KLibLoader " << this << "  " << name() << endl;
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     d = 0L;
00335 }
00336 
00337 static inline QCString makeLibName( const char* name )
00338 {
00339     QCString libname(name);
00340     // only append ".la" if there is no extension
00341     // this allows to load non-libtool libraries as well
00342     // (mhk, 20000228)
00343     int pos = libname.findRev('/');
00344     if (pos < 0)
00345       pos = 0;
00346     if (libname.find('.', pos) < 0)
00347       libname += ".so";
00348     if (libname.right(3) == ".la")
00349       libname = libname.replace( qstrlen(libname) - 4, 3, ".so");
00350     return libname;
00351 }
00352 
00353 //static
00354 QString KLibLoader::findLibrary( const char * name, const KInstance * instance )
00355 {
00356     QCString libname = makeLibName( name );
00357 
00358     // only look up the file if it is not an absolute filename
00359     // (mhk, 20000228)
00360     QString libfile;
00361     if (libname[0] == '/')
00362       libfile = QFile::decodeName( libname );
00363     else
00364     {
00365       libfile = instance->dirs()->findResource( "module", libname );
00366       if ( libfile.isEmpty() )
00367       {
00368         libfile = instance->dirs()->findResource( "lib", libname );
00369 #ifndef NDEBUG
00370         if ( !libfile.isEmpty() && libname.left(3) == "lib" ) // don't warn for kdeinit modules
00371           kdDebug(150) << "library " << libname << " not found under 'module' but under 'lib'" << endl;
00372 #endif
00373       }
00374     }
00375     return libfile;
00376 }
00377 
00378 
00379 KLibrary* KLibLoader::globalLibrary( const char *name )
00380 {
00381 KLibrary *tmp;
00382 int olt_dlopen_flag = lt_dlopen_flag;
00383 
00384    lt_dlopen_flag |= LT_GLOBAL;
00385    kdDebug(150) << "Loading the next library global with flag "
00386                 << lt_dlopen_flag
00387                 << "." << endl;
00388    tmp = library(name);
00389    lt_dlopen_flag = olt_dlopen_flag;
00390 
00391 return tmp;
00392 }
00393 
00394 
00395 KLibrary* KLibLoader::library( const char *name )
00396 {
00397     if (!name)
00398         return 0;
00399 
00400     KLibWrapPrivate* wrap = m_libs[name];
00401     if (wrap) {
00402       /* Nothing to do to load the library.  */
00403       wrap->ref_count++;
00404       return wrap->lib;
00405     }
00406 
00407     /* Test if this library was loaded at some time, but got
00408        unloaded meanwhile, whithout being dlclose()'ed.  */
00409     QPtrListIterator<KLibWrapPrivate> it(d->loaded_stack);
00410     for (; it.current(); ++it) {
00411       if (it.current()->name == name)
00412         wrap = it.current();
00413     }
00414 
00415     if (wrap) {
00416       d->pending_close.removeRef(wrap);
00417       if (!wrap->lib) {
00418         /* This lib only was in loaded_stack, but not in m_libs.  */
00419         wrap->lib = new KLibrary( name, wrap->filename, wrap->handle );
00420       }
00421       wrap->ref_count++;
00422     } else {
00423       QString libfile = findLibrary( name );
00424       if ( libfile.isEmpty() )
00425       {
00426         const QCString libname = makeLibName( name );
00427 #ifndef NDEBUG
00428         kdDebug(150) << "library=" << name << ": No file named " << libname << " found in paths." << endl;
00429 #endif
00430         d->errorMessage = i18n("Library files for \"%1\" not found in paths.").arg(libname);
00431         return 0;
00432       }
00433 
00434       lt_dlhandle handle = lt_dlopen( libfile.latin1() );
00435       if ( !handle )
00436       {
00437         const char* errmsg = lt_dlerror();
00438         if(errmsg)
00439             d->errorMessage = QString::fromLatin1(errmsg);
00440         else
00441             d->errorMessage = QString::null;
00442         return 0;
00443       }
00444       else
00445         d->errorMessage = QString::null;
00446 
00447       KLibrary *lib = new KLibrary( name, libfile, handle );
00448       wrap = new KLibWrapPrivate(lib, handle);
00449       d->loaded_stack.prepend(wrap);
00450     }
00451     m_libs.insert( name, wrap );
00452 
00453     connect( wrap->lib, SIGNAL( destroyed() ),
00454              this, SLOT( slotLibraryDestroyed() ) );
00455 
00456     return wrap->lib;
00457 }
00458 
00459 QString KLibLoader::lastErrorMessage() const
00460 {
00461     return d->errorMessage;
00462 }
00463 
00464 void KLibLoader::unloadLibrary( const char *libname )
00465 {
00466   KLibWrapPrivate *wrap = m_libs[ libname ];
00467   if (!wrap)
00468     return;
00469   if (--wrap->ref_count)
00470     return;
00471 
00472 //  kdDebug(150) << "closing library " << libname << endl;
00473 
00474   m_libs.remove( libname );
00475 
00476   disconnect( wrap->lib, SIGNAL( destroyed() ),
00477               this, SLOT( slotLibraryDestroyed() ) );
00478   close_pending( wrap );
00479 }
00480 
00481 KLibFactory* KLibLoader::factory( const char* name )
00482 {
00483     KLibrary* lib = library( name );
00484     if ( !lib )
00485         return 0;
00486 
00487     return lib->factory();
00488 }
00489 
00490 void KLibLoader::slotLibraryDestroyed()
00491 {
00492   const KLibrary *lib = static_cast<const KLibrary *>( sender() );
00493 
00494   QAsciiDictIterator<KLibWrapPrivate> it( m_libs );
00495   for (; it.current(); ++it )
00496     if ( it.current()->lib == lib )
00497     {
00498       KLibWrapPrivate *wrap = it.current();
00499       wrap->lib = 0;  /* the KLibrary object is already away */
00500       m_libs.remove( it.currentKey() );
00501       close_pending( wrap );
00502       return;
00503     }
00504 }
00505 
00506 void KLibLoader::close_pending(KLibWrapPrivate *wrap)
00507 {
00508   if (wrap && !d->pending_close.containsRef( wrap ))
00509     d->pending_close.append( wrap );
00510 
00511   /* First delete all KLibrary objects in pending_close, but _don't_ unload
00512      the DSO behind it.  */
00513   QPtrListIterator<KLibWrapPrivate> it(d->pending_close);
00514   for (; it.current(); ++it) {
00515     wrap = it.current();
00516     if (wrap->lib) {
00517       disconnect( wrap->lib, SIGNAL( destroyed() ),
00518                   this, SLOT( slotLibraryDestroyed() ) );
00519       KLibrary* to_delete = wrap->lib;
00520       wrap->lib = 0L; // unset first, because KLibrary dtor can cause
00521       delete to_delete; // recursive call to close_pending()
00522     }
00523   }
00524 
00525   if (d->unload_mode == KLibLoaderPrivate::DONT_UNLOAD) {
00526     d->pending_close.clear();
00527     return;
00528   }
00529 
00530   bool deleted_one = false;
00531   while ((wrap = d->loaded_stack.first())) {
00532     /* Let's first see, if we want to try to unload this lib.
00533        If the env. var KDE_DOUNLOAD is set, we try to unload every lib.
00534        If not, we look at the lib itself, and unload it only, if it exports
00535        the symbol __kde_do_unload. */
00536     if (d->unload_mode != KLibLoaderPrivate::UNLOAD
00537         && wrap->unload_mode != KLibWrapPrivate::UNLOAD)
00538       break;
00539 
00540     /* Now ensure, that the libs are only unloaded in the reverse direction
00541        they were loaded.  */
00542     if (!d->pending_close.containsRef( wrap )) {
00543       if (!deleted_one)
00544         /* Only diagnose, if we really haven't deleted anything. */
00545 //        kdDebug(150) << "try to dlclose " << wrap->name << ": not yet" << endl;
00546       break;
00547     }
00548 
00549 //    kdDebug(150) << "try to dlclose " << wrap->name << ": yes, done." << endl;
00550 
00551     if ( !deleted_one ) {
00552       /* Only do the hack once in this loop.
00553          WABA: *HACK*
00554          We need to make sure to clear the clipboard before unloading a DSO
00555          because the DSO could have defined an object derived from QMimeSource
00556          and placed that on the clipboard. */
00557       /*kapp->clipboard()->clear();*/
00558 
00559       /* Well.. let's do something more subtle... convert the clipboard context
00560          to text. That should be safe as it only uses objects defined by Qt. */
00561       if( kapp->clipboard()->ownsSelection()) {
00562     kapp->clipboard()->setText(
00563             kapp->clipboard()->text( QClipboard::Selection ), QClipboard::Selection );
00564       }
00565       if( kapp->clipboard()->ownsClipboard()) {
00566     kapp->clipboard()->setText(
00567             kapp->clipboard()->text( QClipboard::Clipboard ), QClipboard::Clipboard );
00568       }
00569     }
00570 
00571     deleted_one = true;
00572     lt_dlclose(wrap->handle);
00573     d->pending_close.removeRef(wrap);
00574     /* loaded_stack is AutoDelete, so wrap is freed */
00575     d->loaded_stack.remove();
00576   }
00577 }
00578 
00579 void KLibLoader::virtual_hook( int, void* )
00580 { /*BASE::virtual_hook( id, data );*/ }
00581 
00582 void KLibFactory::virtual_hook( int, void* )
00583 { /*BASE::virtual_hook( id, data );*/ }
00584 
00585 #include "klibloader.moc"
KDE Logo
This file is part of the documentation for kdecore Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Jul 22 10:16:18 2005 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003