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