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