• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdecore
 

tdecore

  • tdecore
  • network
kresolvermanager.cpp
1 /*
2  * Copyright (C) 2003-2005 Thiago Macieira <thiago.macieira@kdemail.net>
3  *
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include "config.h"
26 
27 #include <sys/types.h>
28 #include <netinet/in.h>
29 #include <limits.h>
30 #include <unistd.h> // only needed for pid_t
31 
32 #ifdef HAVE_RES_INIT
33 # include <sys/stat.h>
34 extern "C" {
35 # include <arpa/nameser.h>
36 }
37 # include <time.h>
38 # include <resolv.h>
39 #endif
40 
41 #include <tqapplication.h>
42 #include <tqstring.h>
43 #include <tqcstring.h>
44 #include <tqptrlist.h>
45 #include <tqtimer.h>
46 #include <tqmutex.h>
47 #include <tqthread.h>
48 #include <tqwaitcondition.h>
49 #include <tqsemaphore.h>
50 
51 #include <kde_file.h>
52 #include <kdebug.h>
53 #include "kresolver.h"
54 #include "kresolver_p.h"
55 #include "kresolverworkerbase.h"
56 
57 namespace KNetwork
58 {
59  namespace Internal
60  {
61  void initSrvWorker();
62  void initStandardWorkers();
63  }
64 }
65 
66 using namespace KNetwork;
67 using namespace KNetwork::Internal;
68 
69 /*
70  * Explanation on how the resolver system works
71 
72  When KResolver::start is called, it calls KResolverManager::enqueue to add
73  an entry to the queue. KResolverManager::enqueue will verify the availability
74  of a worker thread: if one is available, it will dispatch the request to it.
75  If no threads are available, it will then decide whether to launch a thread
76  or to queue for the future.
77 
78  (This process is achieved by always queueing the new request, starting a
79  new thread if necessary and then notifying of the availability of data
80  to all worker threads).
81 
82  * Worker thread
83  A new thread, when started, will enter its event loop
84  immediately. That is, it'll first try to acquire new data to
85  process, which means it will lock and unlock the manager mutex in
86  the process.
87 
88  If it finds no new data, it'll wait on the feedWorkers condition
89  for a certain maximum time. If that time expires and there's still
90  no data, the thread will exit, in order to save system resources.
91 
92  If it finds data, however, it'll set up and call the worker class
93  that has been selected by the manager. Once that worker is done,
94  the thread releases the data through KResolverManager::releaseData.
95 
96  * Data requesting/releasing
97  A worker thread always calls upon functions on the resolver manager
98  in order to acquire and release data.
99 
100  When data is being requested, the KResolverManager::requestData
101  function will look the currentRequests list and return the first
102  Queued request it finds, while marking it to be InProgress.
103 
104  When the worker class has returned, the worker thread will release
105  that data through the KResolverManager::releaseData function. If the
106  worker class has requested no further data (nRequests == 0), the
107  request's status is marked to be Done. It'll then look at the
108  requestor for that data: if it was requested by another worker,
109  it'll decrement the requests count for that one and add the results
110  to a list. And, finally, if the requests count for the requestor
111  becomes 0, it'll repeat this process for the requestor as well
112  (change status to Done, check for a requestor).
113  */
114 
115 namespace
116 {
117 
118 /*
119  * This class is used to control the access to the
120  * system's resolver API.
121  *
122  * It is necessary to periodically poll /etc/resolv.conf and reload
123  * it if any changes are noticed. This class does exactly that.
124  *
125  * However, there's also the problem of reloading the structure while
126  * some threads are in progress. Therefore, we keep a usage reference count.
127  */
128 class ResInitUsage
129 {
130 public:
131 
132 #ifdef HAVE_RES_INIT
133  time_t mTime;
134  int useCount;
135 
136 # ifndef RES_INIT_THREADSAFE
137  TQWaitCondition cond;
138  TQMutex mutex;
139 # endif
140 
141  bool shouldResInit()
142  {
143  // check if /etc/resolv.conf has changed
144  KDE_struct_stat st;
145  if (KDE_stat("/etc/resolv.conf", &st) != 0)
146  return false;
147 
148  if (mTime != st.st_mtime)
149  {
150  kdDebug(179) << "shouldResInit: /etc/resolv.conf updated" << endl;
151  return true;
152  }
153  return false;
154  }
155 
156  void callResInit()
157  {
158  if (mTime != 0)
159  {
160  // don't call it the first time
161  // let it be initialised naturally
162  kdDebug(179) << "callResInit: calling res_init()" << endl;
163  res_init();
164  }
165 
166  KDE_struct_stat st;
167  if (KDE_stat("/etc/resolv.conf", &st) == 0)
168  mTime = st.st_mtime;
169  }
170 
171  ResInitUsage()
172  : mTime(0), useCount(0)
173  { }
174 
175  /*
176  * Marks the end of usage to the resolver tools
177  */
178  void release()
179  {
180 # ifndef RES_INIT_THREADSAFE
181  TQMutexLocker locker(&mutex);
182  if (--useCount == 0)
183  {
184  if (shouldResInit())
185  callResInit();
186 
187  // we've reached 0, wake up anyone that's waiting to call res_init
188  cond.wakeAll();
189  }
190 # else
191  // do nothing
192 # endif
193  }
194 
195  /*
196  * Marks the beginning of usage of the resolver API
197  */
198  void acquire()
199  {
200 # ifndef RES_INIT_THREADSAFE
201  mutex.lock();
202 
203  if (shouldResInit())
204  {
205  if (useCount)
206  {
207  // other threads are already using the API, so wait till
208  // it's all clear
209  // the thread that emits this condition will also call res_init
210  //tqDebug("ResInitUsage: waiting for libresolv to be clear");
211  cond.wait(&mutex);
212  }
213  else
214  // we're clear
215  callResInit();
216  }
217  useCount++;
218  mutex.unlock();
219 
220 # else
221  if (shouldResInit())
222  callResInit();
223 
224 # endif
225  }
226 
227 #else
228  ResInitUsage()
229  { }
230 
231  bool shouldResInit()
232  { return false; }
233 
234  void acquire()
235  { }
236 
237  void release()
238  { }
239 #endif
240 
241 } resInit;
242 
243 } // anonymous namespace
244 
245 /*
246  * parameters
247  */
248 // a thread will try maxThreadRetries to get data, waiting at most
249 // maxThreadWaitTime milliseconds between each attempt. After that, it'll
250 // exit
251 static const int maxThreadWaitTime = 2000; // 2 seconds
252 static const int maxThreads = 5;
253 
254 static pid_t pid; // FIXME -- disable when everything is ok
255 
256 KResolverThread::KResolverThread()
257  : data(0L)
258 {
259 }
260 
261 // remember! This function runs in a separate thread!
262 void KResolverThread::run()
263 {
264  // initialisation
265  // enter the loop already
266 
267  //tqDebug("KResolverThread(thread %u/%p): started", pid, (void*)TQThread::currentThread());
268  KResolverManager::manager()->registerThread(this);
269  while (true)
270  {
271  data = KResolverManager::manager()->requestData(this, ::maxThreadWaitTime);
272  //tqDebug("KResolverThread(thread %u/%p) got data %p", KResolverManager::pid,
273  // (void*)TQThread::currentThread(), (void*)data);
274  if (data)
275  {
276  // yes, we got data
277  // process it!
278 
279  // 1) set up
280  ;
281 
282  // 2) run it
283  data->worker->run();
284 
285  // 3) release data
286  KResolverManager::manager()->releaseData(this, data);
287 
288  // now go back to the loop
289  }
290  else
291  break;
292  }
293 
294  KResolverManager::manager()->unregisterThread(this);
295  //tqDebug("KResolverThread(thread %u/%p): exiting", pid, (void*)TQThread::currentThread());
296 }
297 
298 bool KResolverThread::checkResolver()
299 {
300  return resInit.shouldResInit();
301 }
302 
303 void KResolverThread::acquireResolver()
304 {
305 #if defined(NEED_MUTEX) && !defined(Q_OS_FREEBSD)
306  getXXbyYYmutex.lock();
307 #endif
308 
309  resInit.acquire();
310 }
311 
312 void KResolverThread::releaseResolver()
313 {
314 #if defined(NEED_MUTEX) && !defined(Q_OS_FREEBSD)
315  getXXbyYYmutex.unlock();
316 #endif
317 
318  resInit.release();
319 }
320 
321 static KResolverManager *globalManager;
322 
323 KResolverManager* KResolverManager::manager()
324 {
325  if (globalManager == 0L)
326  new KResolverManager();
327  return globalManager;
328 }
329 
330 KResolverManager::KResolverManager()
331  : runningThreads(0), availableThreads(0)
332 {
333  globalManager = this;
334  workers.setAutoDelete(true);
335  currentRequests.setAutoDelete(true);
336  initSrvWorker();
337  initStandardWorkers();
338 
339  pid = getpid();
340 }
341 
342 KResolverManager::~KResolverManager()
343 {
344  // this should never be called
345 
346  // kill off running threads
347  for (workers.first(); workers.current(); workers.next())
348  workers.current()->terminate();
349 }
350 
351 void KResolverManager::registerThread(KResolverThread* )
352 {
353 }
354 
355 void KResolverManager::unregisterThread(KResolverThread*)
356 {
357  runningThreads--;
358 }
359 
360 // this function is called by KResolverThread::run
361 RequestData* KResolverManager::requestData(KResolverThread *th, int maxWaitTime)
362 {
364  // This function is called in a worker thread!!
366 
367  // lock the mutex, so that the manager thread or other threads won't
368  // interfere.
369  TQMutexLocker locker(&mutex);
370  RequestData *data = findData(th);
371 
372  if (data)
373  // it found something, that's good
374  return data;
375 
376  // nope, nothing found; sleep for a while
377  availableThreads++;
378  feedWorkers.wait(&mutex, maxWaitTime);
379  availableThreads--;
380 
381  data = findData(th);
382  return data;
383 }
384 
385 RequestData* KResolverManager::findData(KResolverThread* th)
386 {
388  // This function is called by @ref requestData above and must
389  // always be called with a locked mutex
391 
392  // now find data to be processed
393  for (RequestData *curr = newRequests.first(); curr; curr = newRequests.next())
394  if (!curr->worker->m_finished)
395  {
396  // found one
397  if (curr->obj)
398  curr->obj->status = KResolver::InProgress;
399  curr->worker->th = th;
400 
401  // move it to the currentRequests list
402  currentRequests.append(newRequests.take());
403 
404  return curr;
405  }
406 
407  // found nothing!
408  return 0L;
409 }
410 
411 // this function is called by KResolverThread::run
412 void KResolverManager::releaseData(KResolverThread *, RequestData* data)
413 {
415  // This function is called in a worker thread!!
417 
418  //tqDebug("KResolverManager::releaseData(%u/%p): %p has been released", pid,
419 // (void*)TQThread::currentThread(), (void*)data);
420 
421  if (data->obj)
422  {
423  data->obj->status = KResolver::PostProcessing;
424  }
425 
426  data->worker->m_finished = true;
427  data->worker->th = 0L; // this releases the object
428 
429  // handle finished requests
430  handleFinished();
431 }
432 
433 // this function is called by KResolverManager::releaseData above
434 void KResolverManager::handleFinished()
435 {
436  bool redo = false;
437  TQPtrQueue<RequestData> doneRequests;
438 
439  mutex.lock();
440 
441  // loop over all items on the currently running list
442  // we loop from the last to the first so that we catch requests with "requestors" before
443  // we catch the requestor itself.
444  RequestData *curr = currentRequests.last();
445  while (curr)
446  {
447  if (curr->worker->th == 0L)
448  {
449  if (handleFinishedItem(curr))
450  {
451  doneRequests.enqueue(currentRequests.take());
452  if (curr->requestor &&
453  curr->requestor->nRequests == 0 &&
454  curr->requestor->worker->m_finished)
455  // there's a requestor that is now finished
456  redo = true;
457  }
458  }
459 
460  curr = currentRequests.prev();
461  }
462 
463  //tqDebug("KResolverManager::handleFinished(%u): %d requests to notify", pid, doneRequests.count());
464  while (RequestData *d = doneRequests.dequeue())
465  doNotifying(d);
466 
467  mutex.unlock();
468 
469  if (redo)
470  {
471  //tqDebug("KResolverManager::handleFinished(%u): restarting processing to catch requestor",
472  // pid);
473  handleFinished();
474  }
475 }
476 
477 // This function is called by KResolverManager::handleFinished above
478 bool KResolverManager::handleFinishedItem(RequestData* curr)
479 
480 {
481  // for all items that aren't currently running, remove from the list
482  // this includes all finished or cancelled requests
483 
484  if (curr->worker->m_finished && curr->nRequests == 0)
485  {
486  // this one has finished
487  if (curr->obj)
488  curr->obj->status = KResolver::PostProcessing; // post-processing is run in doNotifying()
489 
490  if (curr->requestor)
491  --curr->requestor->nRequests;
492 
493  //tqDebug("KResolverManager::handleFinishedItem(%u): removing %p since it's done",
494  // pid, (void*)curr);
495  return true;
496  }
497  return false;
498 }
499 
500 
501 
502 void KResolverManager::registerNewWorker(KResolverWorkerFactoryBase *factory)
503 {
504  workerFactories.append(factory);
505 }
506 
507 KResolverWorkerBase* KResolverManager::findWorker(KResolverPrivate* p)
508 {
510  // this function can be called on any user thread
512 
513  // this function is called with an unlocked mutex and it's expected to be
514  // thread-safe!
515  // but the factory list is expected not to be changed asynchronously
516 
517  // This function is responsible for finding a suitable worker for the given
518  // input. That means we have to do a costly operation to create each worker
519  // class and call their preprocessing functions. The first one that
520  // says they can process (i.e., preprocess() returns true) will get the job.
521 
522  KResolverWorkerBase *worker;
523  for (KResolverWorkerFactoryBase *factory = workerFactories.first(); factory;
524  factory = workerFactories.next())
525  {
526  worker = factory->create();
527 
528  // set up the data the worker needs to preprocess
529  worker->input = &p->input;
530 
531  if (worker->preprocess())
532  {
533  // good, this one says it can process
534  if (worker->m_finished)
535  p->status = KResolver::PostProcessing;
536  else
537  p->status = KResolver::Queued;
538  return worker;
539  }
540 
541  // no, try again
542  delete worker;
543  }
544 
545  // found no worker
546  return 0L;
547 }
548 
549 void KResolverManager::doNotifying(RequestData *p)
550 {
552  // This function may be called on any thread
553  // any thread at all: user threads, GUI thread, manager thread or worker thread
555 
556  // Notification and finalisation
557  //
558  // Once a request has finished the normal processing, we call the
559  // post processing function.
560  //
561  // After that is done, we will consolidate all results in the object's
562  // KResolverResults and then post an event indicating that the signal
563  // be emitted
564  //
565  // In case we detect that the object is waiting for completion, we do not
566  // post the event, for KResolver::wait will take care of emitting the
567  // signal.
568  //
569  // Once we release the mutex on the object, we may no longer reference it
570  // for it might have been deleted.
571 
572  // "User" objects are those that are not created by the manager. Note that
573  // objects created by worker threads are considered "user" objects. Objects
574  // created by the manager are those created for KResolver::resolveAsync.
575  // We should delete them.
576 
577  if (p->obj)
578  {
579  // lock the object
580  p->obj->mutex.lock();
581  KResolver* parent = p->obj->parent; // is 0 for non-"user" objects
582  KResolverResults& r = p->obj->results;
583 
584  if (p->obj->status == KResolver::Canceled)
585  {
586  p->obj->status = KResolver::Canceled;
587  p->obj->errorcode = KResolver::Canceled;
588  p->obj->syserror = 0;
589  r.setError(KResolver::Canceled, 0);
590  }
591  else if (p->worker)
592  {
593  // post processing
594  p->worker->postprocess(); // ignore the result
595 
596  // copy the results from the worker thread to the final
597  // object
598  r = p->worker->results;
599 
600  // reset address
601  r.setAddress(p->input->node, p->input->service);
602 
603  //tqDebug("KResolverManager::doNotifying(%u/%p): for %p whose status is %d and has %d results",
604  //pid, (void*)TQThread::currentThread(), (void*)p, p->obj->status, r.count());
605 
606  p->obj->errorcode = r.error();
607  p->obj->syserror = r.systemError();
608  p->obj->status = !r.isEmpty() ?
609  KResolver::Success : KResolver::Failed;
610  }
611  else
612  {
613  r.empty();
614  r.setError(p->obj->errorcode, p->obj->syserror);
615  }
616 
617  // check whether there's someone waiting
618  if (!p->obj->waiting && parent)
619  // no, so we must post an event requesting that the signal be emitted
620  // sorry for the C-style cast, but neither static nor reintepret cast work
621  // here; I'd have to do two casts
622  TQApplication::postEvent(parent, new TQEvent((TQEvent::Type)(ResolutionCompleted)));
623 
624  // release the mutex
625  p->obj->mutex.unlock();
626  }
627  else
628  {
629  // there's no object!
630  if (p->worker)
631  p->worker->postprocess();
632  }
633 
634  delete p->worker;
635 
636  // ignore p->requestor and p->nRequests
637  // they have been dealt with by the main loop
638 
639  delete p;
640 
641  // notify any objects waiting in KResolver::wait
642  notifyWaiters.wakeAll();
643 }
644 
645 // enqueue a new request
646 // this function is called from KResolver::start and
647 // from KResolverWorkerBase::enqueue
648 void KResolverManager::enqueue(KResolver *obj, RequestData *requestor)
649 {
650  RequestData *newrequest = new RequestData;
651  newrequest->nRequests = 0;
652  newrequest->obj = obj->d;
653  newrequest->input = &obj->d->input;
654  newrequest->requestor = requestor;
655 
656  // when processing a new request, find the most
657  // suitable worker
658  if ((newrequest->worker = findWorker(obj->d)) == 0L)
659  {
660  // oops, problem
661  // cannot find a worker class for this guy
662  obj->d->status = KResolver::Failed;
663  obj->d->errorcode = KResolver::UnsupportedFamily;
664  obj->d->syserror = 0;
665 
666  doNotifying(newrequest);
667  return;
668  }
669 
670  // no, queue it
671  // p->status was set in findWorker!
672  if (requestor)
673  requestor->nRequests++;
674 
675  if (!newrequest->worker->m_finished)
676  dispatch(newrequest);
677  else if (newrequest->nRequests > 0)
678  {
679  mutex.lock();
680  currentRequests.append(newrequest);
681  mutex.unlock();
682  }
683  else
684  // already done
685  doNotifying(newrequest);
686 }
687 
688 // a new request has been created
689 // dispatch it
690 void KResolverManager::dispatch(RequestData *data)
691 {
692  // As stated in the beginning of the file, this function
693  // is supposed to verify the availability of threads, start
694  // any if necessary
695 
696  TQMutexLocker locker(&mutex);
697 
698  // add to the queue
699  newRequests.append(data);
700 
701  // check if we need to start a new thread
702  //
703  // we depend on the variables availableThreads and runningThreads to
704  // know if we are supposed to start any threads:
705  // - if availableThreads > 0, then there is at least one thread waiting,
706  // blocked in KResolverManager::requestData. It can't unblock
707  // while we are holding the mutex locked, therefore we are sure that
708  // our event will be handled
709  // - if availableThreads == 0:
710  // - if runningThreads < maxThreads
711  // we will start a new thread, which will certainly block in
712  // KResolverManager::requestData because we are holding the mutex locked
713  // - if runningThreads == maxThreads
714  // This situation generally means that we have already maxThreads running
715  // and that all of them are processing. We will not start any new threads,
716  // but will instead wait for one to finish processing and request new data
717  //
718  // There's a possible race condition here, which goes unhandled: if one of
719  // threads has timed out waiting for new data and is in the process of
720  // exiting. In that case, availableThreads == 0 and runningThreads will not
721  // have decremented yet. This means that we will not start a new thread
722  // that we could have. However, since there are other threads working, our
723  // event should be handled soon.
724  // It won't be handled if and only if ALL threads are in the process of
725  // exiting. That situation is EXTREMELY unlikely and is not handled either.
726  //
727  if (availableThreads == 0 && runningThreads < maxThreads)
728  {
729  // yes, a new thread should be started
730 
731  // find if there's a finished one
732  KResolverThread *th = workers.first();
733  while (th && th->running())
734  th = workers.next();
735 
736  if (th == 0L)
737  // no, create one
738  th = new KResolverThread;
739  else
740  workers.take();
741 
742  th->start();
743  workers.append(th);
744  runningThreads++;
745  }
746 
747  feedWorkers.wakeAll();
748 
749  // clean up idle threads
750  workers.first();
751  while (workers.current())
752  {
753  if (!workers.current()->running())
754  workers.remove();
755  else
756  workers.next();
757  }
758 }
759 
760 // this function is called by KResolverManager::dequeue
761 bool KResolverManager::dequeueNew(KResolver* obj)
762 {
763  // This function must be called with a locked mutex
764  // Deadlock warning:
765  // always lock the global mutex first if both mutexes must be locked
766 
767  KResolverPrivate *d = obj->d;
768 
769  // check if it's in the new request list
770  RequestData *curr = newRequests.first();
771  while (curr)
772  if (curr->obj == d)
773  {
774  // yes, this object is still in the list
775  // but it has never been processed
776  d->status = KResolver::Canceled;
777  d->errorcode = KResolver::Canceled;
778  d->syserror = 0;
779  newRequests.take();
780 
781  delete curr->worker;
782  delete curr;
783 
784  return true;
785  }
786  else
787  curr = newRequests.next();
788 
789  // check if it's running
790  curr = currentRequests.first();
791  while (curr)
792  if (curr->obj == d)
793  {
794  // it's running. We cannot simply take it out of the list.
795  // it will be handled when the thread that is working on it finishes
796  d->mutex.lock();
797 
798  d->status = KResolver::Canceled;
799  d->errorcode = KResolver::Canceled;
800  d->syserror = 0;
801 
802  // disengage from the running threads
803  curr->obj = 0L;
804  curr->input = 0L;
805  if (curr->worker)
806  curr->worker->input = 0L;
807 
808  d->mutex.unlock();
809  }
810  else
811  curr = currentRequests.next();
812 
813  return false;
814 }
815 
816 // this function is called by KResolver::cancel
817 // it's expected to be thread-safe
818 void KResolverManager::dequeue(KResolver *obj)
819 {
820  TQMutexLocker locker(&mutex);
821  dequeueNew(obj);
822 }
KNetwork::KResolverResults
Name and service resolution results.
Definition: kresolver.h:198
KNetwork::KResolverResults::setAddress
void setAddress(const TQString &host, const TQString &service)
Sets the new nodename and service name.
Definition: kresolver.cpp:278
KNetwork::KResolverResults::systemError
int systemError() const
Retrieves the system error code, if any.
Definition: kresolver.cpp:253
KNetwork::KResolverResults::error
int error() const
Retrieves the error code associated with this resolution.
Definition: kresolver.cpp:247
KNetwork::KResolverResults::setError
void setError(int errorcode, int systemerror=0)
Sets the error codes.
Definition: kresolver.cpp:259
KNetwork::KResolver
Name and service resolution class.
Definition: kresolver.h:296
endl
kndbgstream & endl(kndbgstream &s)
Does nothing.
Definition: kdebug.h:583
KNetwork
A namespace to store all networking-related (socket) classes.
Definition: kbufferedsocket.h:36
KStdAction::redo
TDEAction * redo(const TQObject *recvr, const char *slot, TDEActionCollection *parent, const char *name=0)

tdecore

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

tdecore

Skip menu "tdecore"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdecore by doxygen 1.9.1
This website is maintained by Timothy Pearson.