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 #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> //getenv
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 /* !RTLD_GLOBAL */
00056 #ifndef LT_GLOBAL
00057 #  define LT_GLOBAL             0
00058 #endif /* !LT_GLOBAL */
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 //    kdDebug(150) << "Deleting KLibFactory " << this << endl;
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     /* Make sure, we have a KLibLoader */
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 //    kdDebug(150) << "Deleting KLibrary " << this << "  " << m_libname << endl;
00118     if ( m_timer && m_timer->isActive() )
00119     m_timer->stop();
00120 
00121     // If any object is remaining, delete
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 //  kdDebug(150) << " ... deleting the factory " << m_factory << endl;
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; // we know this object already
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 //    kdDebug(150) << "KLibrary: shutdown timer for " << name() << " started!"
00230 //                 << endl;
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     // as long as it's not stable make the timeout short, for debugging
00240     // pleasure (matz)
00241     //m_timer->start( 1000*60, true );
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   /* Don't go through KLibLoader::unloadLibrary(), because that uses the
00252      ref counter, but this timeout means to unconditionally close this library
00253      The destroyed() signal will take care to remove us from all lists.
00254   */
00255   delete this;
00256 }
00257 
00258 // -------------------------------------------------
00259 
00260 /* This helper class is needed, because KLibraries can go away without
00261    being unloaded. So we need some info about KLibraries even after its
00262    death. */
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 //        kdDebug(150) << "Will not unload " << name << endl;
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 //    kdDebug(150) << "Deleting KLibLoader " << this << "  " << name() << endl;
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 //static
00348 QString KLibLoader::findLibrary( const char * name, const KInstance * instance )
00349 {
00350     QCString libname( name );
00351 
00352     // only append ".la" if there is no extension
00353     // this allows to load non-libtool libraries as well
00354     // (mhk, 20000228)
00355     int pos = libname.findRev('/');
00356     if (pos < 0)
00357       pos = 0;
00358     if (libname.find('.', pos) < 0)
00359       libname += ".la";
00360 
00361     // only look up the file if it is not an absolute filename
00362     // (mhk, 20000228)
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" ) // don't warn for kdeinit modules
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       /* Nothing to do to load the library.  */
00415       wrap->ref_count++;
00416       return wrap->lib;
00417     }
00418 
00419     /* Test if this library was loaded at some time, but got
00420        unloaded meanwhile, whithout being dlclose()'ed.  */
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         /* This lib only was in loaded_stack, but not in m_libs.  */
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 //  kdDebug(150) << "closing library " << libname << endl;
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;  /* the KLibrary object is already away */
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   /* First delete all KLibrary objects in pending_close, but _don't_ unload
00518      the DSO behind it.  */
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     /* Let's first see, if we want to try to unload this lib.
00535        If the env. var KDE_DOUNLOAD is set, we try to unload every lib.
00536        If not, we look at the lib itself, and unload it only, if it exports
00537        the symbol __kde_do_unload. */
00538     if (d->unload_mode != KLibLoaderPrivate::UNLOAD
00539         && wrap->unload_mode != KLibWrapPrivate::UNLOAD)
00540       break;
00541 
00542     /* Now ensure, that the libs are only unloaded in the reverse direction
00543        they were loaded.  */
00544     if (!d->pending_close.containsRef( wrap )) {
00545       if (!deleted_one)
00546         /* Only diagnose, if we really haven't deleted anything. */
00547 //        kdDebug(150) << "try to dlclose " << wrap->name << ": not yet" << endl;
00548       break;
00549     }
00550 
00551 //    kdDebug(150) << "try to dlclose " << wrap->name << ": yes, done." << endl;
00552 
00553 #ifndef Q_WS_QWS
00554     if ( !deleted_one ) {
00555       /* Only do the hack once in this loop.
00556          WABA: *HACK*
00557          We need to make sure to clear the clipboard before unloading a DSO
00558          because the DSO could have defined an object derived from QMimeSource
00559          and placed that on the clipboard. */
00560       /*kapp->clipboard()->clear();*/
00561 
00562       /* Well.. let's do something more subtle... convert the clipboard context
00563          to text. That should be safe as it only uses objects defined by Qt. */
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     // FIXME(E): Implement in Qt Embedded
00580 #endif
00581 
00582     deleted_one = true;
00583     lt_dlclose(wrap->handle);
00584     d->pending_close.removeRef(wrap);
00585     /* loaded_stack is AutoDelete, so wrap is freed */
00586     d->loaded_stack.remove();
00587   }
00588 }
00589 
00590 void KLibLoader::virtual_hook( int, void* )
00591 { /*BASE::virtual_hook( id, data );*/ }
00592 
00593 void KLibFactory::virtual_hook( int, void* )
00594 { /*BASE::virtual_hook( id, data );*/ }
00595 
00596 #include "klibloader.moc"
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.0.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Oct 8 12:20:41 2003 by doxygen 1.2.18 written by Dimitri van Heesch, © 1997-2001