• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdelibs API Reference
  • KDE Home
  • Contact Us
 

KIO

  • sources
  • kde-4.14
  • kdelibs
  • kio
  • kio
scheduler.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE libraries
2  Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
3  Waldo Bastian <bastian@kde.org>
4  Copyright (C) 2009, 2010 Andreas Hartmetz <ahartmetz@gmail.com>
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License version 2 as published by the Free Software Foundation.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Library General Public License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19 */
20 
21 #include "scheduler.h"
22 #include "scheduler_p.h"
23 
24 #include "sessiondata.h"
25 #include "slaveconfig.h"
26 #include "authinfo.h"
27 #include "slave.h"
28 #include "connection.h"
29 #include "job_p.h"
30 
31 #include <kdebug.h>
32 #include <kprotocolmanager.h>
33 #include <kprotocolinfo.h>
34 #include <assert.h>
35 
36 #include <QtCore/QHash>
37 #include <QtGui/QWidget>
38 #include <QtDBus/QtDBus>
39 
40 // Slaves may be idle for a certain time (3 minutes) before they are killed.
41 static const int s_idleSlaveLifetime = 3 * 60;
42 
43 
44 using namespace KIO;
45 
46 #ifndef KDE_USE_FINAL // already defined in job.cpp
47 static inline Slave *jobSlave(SimpleJob *job)
48 {
49  return SimpleJobPrivate::get(job)->m_slave;
50 }
51 #endif
52 
53 static inline int jobCommand(SimpleJob *job)
54 {
55  return SimpleJobPrivate::get(job)->m_command;
56 }
57 
58 static inline void startJob(SimpleJob *job, Slave *slave)
59 {
60  SimpleJobPrivate::get(job)->start(slave);
61 }
62 
63 // here be uglies
64 // forward declaration to break cross-dependency of SlaveKeeper and SchedulerPrivate
65 static void setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol,
66  const QStringList &proxyList, bool newSlave, const KIO::MetaData *config = 0);
67 // same reason as above
68 static Scheduler *scheduler();
69 static Slave *heldSlaveForJob(SimpleJob *job);
70 
71 
72 int SerialPicker::changedPrioritySerial(int oldSerial, int newPriority) const
73 {
74  Q_ASSERT(newPriority >= -10 && newPriority <= 10);
75  newPriority = qBound(-10, newPriority, 10);
76  int unbiasedSerial = oldSerial % m_jobsPerPriority;
77  return unbiasedSerial + newPriority * m_jobsPerPriority;
78 }
79 
80 
81 SlaveKeeper::SlaveKeeper()
82 {
83  m_grimTimer.setSingleShot(true);
84  connect (&m_grimTimer, SIGNAL(timeout()), SLOT(grimReaper()));
85 }
86 
87 void SlaveKeeper::returnSlave(Slave *slave)
88 {
89  Q_ASSERT(slave);
90  slave->setIdle();
91  m_idleSlaves.insert(slave->host(), slave);
92  scheduleGrimReaper();
93 }
94 
95 Slave *SlaveKeeper::takeSlaveForJob(SimpleJob *job)
96 {
97  Slave *slave = heldSlaveForJob(job);
98  if (slave) {
99  return slave;
100  }
101 
102  KUrl url = SimpleJobPrivate::get(job)->m_url;
103  // TODO take port, username and password into account
104  QMultiHash<QString, Slave *>::Iterator it = m_idleSlaves.find(url.host());
105  if (it == m_idleSlaves.end()) {
106  it = m_idleSlaves.begin();
107  }
108  if (it == m_idleSlaves.end()) {
109  return 0;
110  }
111  slave = it.value();
112  m_idleSlaves.erase(it);
113  return slave;
114 }
115 
116 bool SlaveKeeper::removeSlave(Slave *slave)
117 {
118  // ### performance not so great
119  QMultiHash<QString, Slave *>::Iterator it = m_idleSlaves.begin();
120  for (; it != m_idleSlaves.end(); ++it) {
121  if (it.value() == slave) {
122  m_idleSlaves.erase(it);
123  return true;
124  }
125  }
126  return false;
127 }
128 
129 QList<Slave *> SlaveKeeper::allSlaves() const
130 {
131  return m_idleSlaves.values();
132 }
133 
134 void SlaveKeeper::scheduleGrimReaper()
135 {
136  if (!m_grimTimer.isActive()) {
137  m_grimTimer.start((s_idleSlaveLifetime / 2) * 1000);
138  }
139 }
140 
141 //private slot
142 void SlaveKeeper::grimReaper()
143 {
144  QMultiHash<QString, Slave *>::Iterator it = m_idleSlaves.begin();
145  while (it != m_idleSlaves.end()) {
146  Slave *slave = it.value();
147  if (slave->idleTime() >= s_idleSlaveLifetime) {
148  it = m_idleSlaves.erase(it);
149  if (slave->job()) {
150  kDebug (7006) << "Idle slave" << slave << "still has job" << slave->job();
151  }
152  slave->kill();
153  // avoid invoking slotSlaveDied() because its cleanup services are not needed
154  slave->deref();
155  } else {
156  ++it;
157  }
158  }
159  if (!m_idleSlaves.isEmpty()) {
160  scheduleGrimReaper();
161  }
162 }
163 
164 
165 int HostQueue::lowestSerial() const
166 {
167  QMap<int, SimpleJob*>::ConstIterator first = m_queuedJobs.constBegin();
168  if (first != m_queuedJobs.constEnd()) {
169  return first.key();
170  }
171  return SerialPicker::maxSerial;
172 }
173 
174 void HostQueue::queueJob(SimpleJob *job)
175 {
176  const int serial = SimpleJobPrivate::get(job)->m_schedSerial;
177  Q_ASSERT(serial != 0);
178  Q_ASSERT(!m_queuedJobs.contains(serial));
179  Q_ASSERT(!m_runningJobs.contains(job));
180  m_queuedJobs.insert(serial, job);
181 }
182 
183 SimpleJob *HostQueue::takeFirstInQueue()
184 {
185  Q_ASSERT(!m_queuedJobs.isEmpty());
186  QMap<int, SimpleJob *>::iterator first = m_queuedJobs.begin();
187  SimpleJob *job = first.value();
188  m_queuedJobs.erase(first);
189  m_runningJobs.insert(job);
190  return job;
191 }
192 
193 bool HostQueue::removeJob(SimpleJob *job)
194 {
195  const int serial = SimpleJobPrivate::get(job)->m_schedSerial;
196  if (m_runningJobs.remove(job)) {
197  Q_ASSERT(!m_queuedJobs.contains(serial));
198  return true;
199  }
200  if (m_queuedJobs.remove(serial)) {
201  return true;
202  }
203  return false;
204 }
205 
206 QList<Slave *> HostQueue::allSlaves() const
207 {
208  QList<Slave *> ret;
209  Q_FOREACH (SimpleJob *job, m_runningJobs) {
210  Slave *slave = jobSlave(job);
211  Q_ASSERT(slave);
212  ret.append(slave);
213  }
214  return ret;
215 }
216 
217 
218 
219 ConnectedSlaveQueue::ConnectedSlaveQueue()
220 {
221  m_startJobsTimer.setSingleShot(true);
222  connect (&m_startJobsTimer, SIGNAL(timeout()), SLOT(startRunnableJobs()));
223 }
224 
225 bool ConnectedSlaveQueue::queueJob(SimpleJob *job, Slave *slave)
226 {
227  QHash<Slave *, PerSlaveQueue>::Iterator it = m_connectedSlaves.find(slave);
228  if (it == m_connectedSlaves.end()) {
229  return false;
230  }
231  SimpleJobPrivate::get(job)->m_slave = slave;
232 
233  PerSlaveQueue &jobs = it.value();
234  jobs.waitingList.append(job);
235  if (!jobs.runningJob) {
236  // idle slave now has a job to run
237  m_runnableSlaves.insert(slave);
238  m_startJobsTimer.start();
239  }
240  return true;
241 }
242 
243 bool ConnectedSlaveQueue::removeJob(SimpleJob *job)
244 {
245  Slave *slave = jobSlave(job);
246  Q_ASSERT(slave);
247  QHash<Slave *, PerSlaveQueue>::Iterator it = m_connectedSlaves.find(slave);
248  if (it == m_connectedSlaves.end()) {
249  return false;
250  }
251  PerSlaveQueue &jobs = it.value();
252  if (jobs.runningJob || jobs.waitingList.isEmpty()) {
253  // a slave that was busy running a job was not runnable.
254  // a slave that has no waiting job(s) was not runnable either.
255  Q_ASSERT(!m_runnableSlaves.contains(slave));
256  }
257 
258  const bool removedRunning = jobs.runningJob == job;
259  const bool removedWaiting = jobs.waitingList.removeAll(job) != 0;
260  if (removedRunning) {
261  jobs.runningJob = 0;
262  Q_ASSERT(!removedWaiting);
263  }
264  const bool removedTheJob = removedRunning || removedWaiting;
265 
266  if (!slave->isAlive()) {
267  removeSlave(slave);
268  return removedTheJob;
269  }
270 
271  if (removedRunning && jobs.waitingList.count()) {
272  m_runnableSlaves.insert(slave);
273  m_startJobsTimer.start();
274  }
275  if (removedWaiting && jobs.waitingList.isEmpty()) {
276  m_runnableSlaves.remove(slave);
277  }
278  return removedTheJob;
279 }
280 
281 void ConnectedSlaveQueue::addSlave(Slave *slave)
282 {
283  Q_ASSERT(slave);
284  if (!m_connectedSlaves.contains(slave)) {
285  m_connectedSlaves.insert(slave, PerSlaveQueue());
286  }
287 }
288 
289 bool ConnectedSlaveQueue::removeSlave(Slave *slave)
290 {
291  QHash<Slave *, PerSlaveQueue>::Iterator it = m_connectedSlaves.find(slave);
292  if (it == m_connectedSlaves.end()) {
293  return false;
294  }
295  PerSlaveQueue &jobs = it.value();
296  Q_FOREACH (SimpleJob *job, jobs.waitingList) {
297  // ### for compatibility with the old scheduler we don't touch the running job, if any.
298  // make sure that the job doesn't call back into Scheduler::cancelJob(); this would
299  // a) crash and b) be unnecessary because we clean up just fine.
300  SimpleJobPrivate::get(job)->m_schedSerial = 0;
301  job->kill();
302  }
303  m_connectedSlaves.erase(it);
304  m_runnableSlaves.remove(slave);
305 
306  slave->kill();
307  return true;
308 }
309 
310 // KDE5: only one caller, for doubtful reasons. remove this if possible.
311 bool ConnectedSlaveQueue::isIdle(Slave *slave)
312 {
313  QHash<Slave *, PerSlaveQueue>::Iterator it = m_connectedSlaves.find(slave);
314  if (it == m_connectedSlaves.end()) {
315  return false;
316  }
317  return it.value().runningJob == 0;
318 }
319 
320 
321 //private slot
322 void ConnectedSlaveQueue::startRunnableJobs()
323 {
324  QSet<Slave *>::Iterator it = m_runnableSlaves.begin();
325  while (it != m_runnableSlaves.end()) {
326  Slave *slave = *it;
327  if (!slave->isConnected()) {
328  // this polling is somewhat inefficient...
329  m_startJobsTimer.start();
330  ++it;
331  continue;
332  }
333  it = m_runnableSlaves.erase(it);
334  PerSlaveQueue &jobs = m_connectedSlaves[slave];
335  SimpleJob *job = jobs.waitingList.takeFirst();
336  Q_ASSERT(!jobs.runningJob);
337  jobs.runningJob = job;
338 
339  const KUrl url = job->url();
340  // no port is -1 in QUrl, but in kde3 we used 0 and the kioslaves assume that.
341  const int port = url.port() == -1 ? 0 : url.port();
342 
343  if (slave->host() == "<reset>") {
344  MetaData configData = SlaveConfig::self()->configData(url.protocol(), url.host());
345  slave->setConfig(configData);
346  slave->setProtocol(url.protocol());
347  slave->setHost(url.host(), port, url.user(), url.pass());
348  }
349 
350  Q_ASSERT(slave->protocol() == url.protocol());
351  Q_ASSERT(slave->host() == url.host());
352  Q_ASSERT(slave->port() == port);
353  startJob(job, slave);
354  }
355 }
356 
357 
358 static void ensureNoDuplicates(QMap<int, HostQueue *> *queuesBySerial)
359 {
360  Q_UNUSED(queuesBySerial);
361 #ifdef SCHEDULER_DEBUG
362  // a host queue may *never* be in queuesBySerial twice.
363  QSet<HostQueue *> seen;
364  Q_FOREACH (HostQueue *hq, *queuesBySerial) {
365  Q_ASSERT(!seen.contains(hq));
366  seen.insert(hq);
367  }
368 #endif
369 }
370 
371 static void verifyRunningJobsCount(QHash<QString, HostQueue> *queues, int runningJobsCount)
372 {
373  Q_UNUSED(queues);
374  Q_UNUSED(runningJobsCount);
375 #ifdef SCHEDULER_DEBUG
376  int realRunningJobsCount = 0;
377  Q_FOREACH (const HostQueue &hq, *queues) {
378  realRunningJobsCount += hq.runningJobsCount();
379  }
380  Q_ASSERT(realRunningJobsCount == runningJobsCount);
381 
382  // ...and of course we may never run the same job twice!
383  QSet<SimpleJob *> seenJobs;
384  Q_FOREACH (const HostQueue &hq, *queues) {
385  Q_FOREACH (SimpleJob *job, hq.runningJobs()) {
386  Q_ASSERT(!seenJobs.contains(job));
387  seenJobs.insert(job);
388  }
389  }
390 #endif
391 }
392 
393 
394 ProtoQueue::ProtoQueue(SchedulerPrivate *sp, int maxSlaves, int maxSlavesPerHost)
395  : m_schedPrivate(sp),
396  m_maxConnectionsPerHost(maxSlavesPerHost ? maxSlavesPerHost : maxSlaves),
397  m_maxConnectionsTotal(qMax(maxSlaves, maxSlavesPerHost)),
398  m_runningJobsCount(0)
399 
400 {
401  kDebug(7006) << "m_maxConnectionsTotal:" << m_maxConnectionsTotal
402  << "m_maxConnectionsPerHost:" << m_maxConnectionsPerHost;
403  Q_ASSERT(m_maxConnectionsPerHost >= 1);
404  Q_ASSERT(maxSlaves >= maxSlavesPerHost);
405  m_startJobTimer.setSingleShot(true);
406  connect (&m_startJobTimer, SIGNAL(timeout()), SLOT(startAJob()));
407 }
408 
409 ProtoQueue::~ProtoQueue()
410 {
411  Q_FOREACH (Slave *slave, allSlaves()) {
412  // kill the slave process, then remove the interface in our process
413  slave->kill();
414  slave->deref();
415  }
416 }
417 
418 void ProtoQueue::queueJob(SimpleJob *job)
419 {
420  QString hostname = SimpleJobPrivate::get(job)->m_url.host();
421  HostQueue &hq = m_queuesByHostname[hostname];
422  const int prevLowestSerial = hq.lowestSerial();
423  Q_ASSERT(hq.runningJobsCount() <= m_maxConnectionsPerHost);
424 
425  // nevert insert a job twice
426  Q_ASSERT(SimpleJobPrivate::get(job)->m_schedSerial == 0);
427  SimpleJobPrivate::get(job)->m_schedSerial = m_serialPicker.next();
428 
429  const bool wasQueueEmpty = hq.isQueueEmpty();
430  hq.queueJob(job);
431  // note that HostQueue::queueJob() into an empty queue changes its lowestSerial() too...
432  // the queue's lowest serial job may have changed, so update the ordered list of queues.
433  // however, we ignore all jobs that would cause more connections to a host than allowed.
434  if (prevLowestSerial != hq.lowestSerial()) {
435  if (hq.runningJobsCount() < m_maxConnectionsPerHost) {
436  // if the connection limit didn't keep the HQ unscheduled it must have been lack of jobs
437  if (m_queuesBySerial.remove(prevLowestSerial) == 0) {
438  Q_UNUSED(wasQueueEmpty);
439  Q_ASSERT(wasQueueEmpty);
440  }
441  m_queuesBySerial.insert(hq.lowestSerial(), &hq);
442  } else {
443 #ifdef SCHEDULER_DEBUG
444  // ### this assertion may fail if the limits were modified at runtime!
445  // if the per-host connection limit is already reached the host queue's lowest serial
446  // should not be queued.
447  Q_ASSERT(!m_queuesBySerial.contains(prevLowestSerial));
448 #endif
449  }
450  }
451  // just in case; startAJob() will refuse to start a job if it shouldn't.
452  m_startJobTimer.start();
453 
454  ensureNoDuplicates(&m_queuesBySerial);
455 }
456 
457 void ProtoQueue::changeJobPriority(SimpleJob *job, int newPrio)
458 {
459  SimpleJobPrivate *jobPriv = SimpleJobPrivate::get(job);
460  QHash<QString, HostQueue>::Iterator it = m_queuesByHostname.find(jobPriv->m_url.host());
461  if (it == m_queuesByHostname.end()) {
462  return;
463  }
464  HostQueue &hq = it.value();
465  const int prevLowestSerial = hq.lowestSerial();
466  if (hq.isJobRunning(job) || !hq.removeJob(job)) {
467  return;
468  }
469  jobPriv->m_schedSerial = m_serialPicker.changedPrioritySerial(jobPriv->m_schedSerial, newPrio);
470  hq.queueJob(job);
471  const bool needReinsert = hq.lowestSerial() != prevLowestSerial;
472  // the host queue might be absent from m_queuesBySerial because the connections per host limit
473  // for that host has been reached.
474  if (needReinsert && m_queuesBySerial.remove(prevLowestSerial)) {
475  m_queuesBySerial.insert(hq.lowestSerial(), &hq);
476  }
477  ensureNoDuplicates(&m_queuesBySerial);
478 }
479 
480 void ProtoQueue::removeJob(SimpleJob *job)
481 {
482  SimpleJobPrivate *jobPriv = SimpleJobPrivate::get(job);
483  HostQueue &hq = m_queuesByHostname[jobPriv->m_url.host()];
484  const int prevLowestSerial = hq.lowestSerial();
485  const int prevRunningJobs = hq.runningJobsCount();
486 
487  Q_ASSERT(hq.runningJobsCount() <= m_maxConnectionsPerHost);
488 
489  if (hq.removeJob(job)) {
490  if (hq.lowestSerial() != prevLowestSerial) {
491  // we have dequeued the not yet running job with the lowest serial
492  Q_ASSERT(!jobPriv->m_slave);
493  Q_ASSERT(prevRunningJobs == hq.runningJobsCount());
494  if (m_queuesBySerial.remove(prevLowestSerial) == 0) {
495  // make sure that the queue was not scheduled for a good reason
496  Q_ASSERT(hq.runningJobsCount() == m_maxConnectionsPerHost);
497  }
498  } else {
499  if (prevRunningJobs != hq.runningJobsCount()) {
500  // we have dequeued a previously running job
501  Q_ASSERT(prevRunningJobs - 1 == hq.runningJobsCount());
502  m_runningJobsCount--;
503  Q_ASSERT(m_runningJobsCount >= 0);
504  }
505  }
506  if (!hq.isQueueEmpty() && hq.runningJobsCount() < m_maxConnectionsPerHost) {
507  // this may be a no-op, but it's faster than first checking if it's already in.
508  m_queuesBySerial.insert(hq.lowestSerial(), &hq);
509  }
510 
511  if (hq.isEmpty()) {
512  // no queued jobs, no running jobs. this destroys hq from above.
513  m_queuesByHostname.remove(jobPriv->m_url.host());
514  }
515 
516  if (jobPriv->m_slave && jobPriv->m_slave->isAlive()) {
517  m_slaveKeeper.returnSlave(jobPriv->m_slave);
518  }
519  // just in case; startAJob() will refuse to start a job if it shouldn't.
520  m_startJobTimer.start();
521  } else {
522  // should be a connected slave
523  // if the assertion fails the job has probably changed the host part of its URL while
524  // running, so we can't find it by hostname. don't do this.
525  const bool removed = m_connectedSlaveQueue.removeJob(job);
526  Q_UNUSED(removed);
527  Q_ASSERT(removed);
528  }
529 
530  ensureNoDuplicates(&m_queuesBySerial);
531 }
532 
533 Slave *ProtoQueue::createSlave(const QString &protocol, SimpleJob *job, const KUrl &url)
534 {
535  int error;
536  QString errortext;
537  Slave *slave = Slave::createSlave(protocol, url, error, errortext);
538  if (slave) {
539  scheduler()->connect(slave, SIGNAL(slaveDied(KIO::Slave*)),
540  SLOT(slotSlaveDied(KIO::Slave*)));
541  scheduler()->connect(slave, SIGNAL(slaveStatus(pid_t,QByteArray,QString,bool)),
542  SLOT(slotSlaveStatus(pid_t,QByteArray,QString,bool)));
543  } else {
544  kError() << "couldn't create slave:" << errortext;
545  if (job) {
546  job->slotError(error, errortext);
547  }
548  }
549  return slave;
550 }
551 
552 bool ProtoQueue::removeSlave (KIO::Slave *slave)
553 {
554  const bool removedConnected = m_connectedSlaveQueue.removeSlave(slave);
555  const bool removedUnconnected = m_slaveKeeper.removeSlave(slave);
556  Q_ASSERT(!(removedConnected && removedUnconnected));
557  return removedConnected || removedUnconnected;
558 }
559 
560 QList<Slave *> ProtoQueue::allSlaves() const
561 {
562  QList<Slave *> ret(m_slaveKeeper.allSlaves());
563  Q_FOREACH (const HostQueue &hq, m_queuesByHostname) {
564  ret.append(hq.allSlaves());
565  }
566  ret.append(m_connectedSlaveQueue.allSlaves());
567  return ret;
568 }
569 
570 //private slot
571 void ProtoQueue::startAJob()
572 {
573  ensureNoDuplicates(&m_queuesBySerial);
574  verifyRunningJobsCount(&m_queuesByHostname, m_runningJobsCount);
575 
576 #ifdef SCHEDULER_DEBUG
577  kDebug(7006) << "m_runningJobsCount:" << m_runningJobsCount;
578  Q_FOREACH (const HostQueue &hq, m_queuesByHostname) {
579  Q_FOREACH (SimpleJob *job, hq.runningJobs()) {
580  kDebug(7006) << SimpleJobPrivate::get(job)->m_url;
581  }
582  }
583 #endif
584  if (m_runningJobsCount >= m_maxConnectionsTotal) {
585 #ifdef SCHEDULER_DEBUG
586  kDebug(7006) << "not starting any jobs because maxConnectionsTotal has been reached.";
587 #endif
588  return;
589  }
590 
591  QMap<int, HostQueue *>::iterator first = m_queuesBySerial.begin();
592  if (first != m_queuesBySerial.end()) {
593  // pick a job and maintain the queue invariant: lower serials first
594  HostQueue *hq = first.value();
595  const int prevLowestSerial = first.key();
596  Q_UNUSED(prevLowestSerial);
597  Q_ASSERT(hq->lowestSerial() == prevLowestSerial);
598  // the following assertions should hold due to queueJob(), takeFirstInQueue() and
599  // removeJob() being correct
600  Q_ASSERT(hq->runningJobsCount() < m_maxConnectionsPerHost);
601  SimpleJob *startingJob = hq->takeFirstInQueue();
602  Q_ASSERT(hq->runningJobsCount() <= m_maxConnectionsPerHost);
603  Q_ASSERT(hq->lowestSerial() != prevLowestSerial);
604 
605  m_queuesBySerial.erase(first);
606  // we've increased hq's runningJobsCount() by calling nexStartingJob()
607  // so we need to check again.
608  if (!hq->isQueueEmpty() && hq->runningJobsCount() < m_maxConnectionsPerHost) {
609  m_queuesBySerial.insert(hq->lowestSerial(), hq);
610  }
611 
612  // always increase m_runningJobsCount because it's correct if there is a slave and if there
613  // is no slave, removeJob() will balance the number again. removeJob() would decrease the
614  // number too much otherwise.
615  // Note that createSlave() can call slotError() on a job which in turn calls removeJob(),
616  // so increase the count here already.
617  m_runningJobsCount++;
618 
619  bool isNewSlave = false;
620  Slave *slave = m_slaveKeeper.takeSlaveForJob(startingJob);
621  SimpleJobPrivate *jobPriv = SimpleJobPrivate::get(startingJob);
622  if (!slave) {
623  isNewSlave = true;
624  slave = createSlave(jobPriv->m_protocol, startingJob, jobPriv->m_url);
625  }
626 
627  if (slave) {
628  jobPriv->m_slave = slave;
629  setupSlave(slave, jobPriv->m_url, jobPriv->m_protocol, jobPriv->m_proxyList, isNewSlave);
630  startJob(startingJob, slave);
631  } else {
632  // dispose of our records about the job and mark the job as unknown
633  // (to prevent crashes later)
634  // note that the job's slotError() can have called removeJob() first, so check that
635  // it's not a ghost job with null serial already.
636  if (jobPriv->m_schedSerial) {
637  removeJob(startingJob);
638  jobPriv->m_schedSerial = 0;
639  }
640  }
641  } else {
642 #ifdef SCHEDULER_DEBUG
643  kDebug(7006) << "not starting any jobs because there is no queued job.";
644 #endif
645  }
646 
647  if (!m_queuesBySerial.isEmpty()) {
648  m_startJobTimer.start();
649  }
650 }
651 
652 
653 
654 class KIO::SchedulerPrivate
655 {
656 public:
657  SchedulerPrivate()
658  : q(new Scheduler()),
659  m_slaveOnHold(0),
660  m_checkOnHold(true), // !! Always check with KLauncher for the first request
661  m_ignoreConfigReparse(false)
662  {
663  }
664 
665  ~SchedulerPrivate()
666  {
667  delete q;
668  q = 0;
669  Q_FOREACH (ProtoQueue *p, m_protocols) {
670  Q_FOREACH (Slave *slave, p->allSlaves()) {
671  slave->kill();
672  }
673  p->deleteLater();
674  }
675  }
676  Scheduler *q;
677 
678  Slave *m_slaveOnHold;
679  KUrl m_urlOnHold;
680  bool m_checkOnHold;
681  bool m_ignoreConfigReparse;
682 
683  SessionData sessionData;
684  QMap<QObject *,WId> m_windowList;
685 
686  void doJob(SimpleJob *job);
687 #ifndef KDE_NO_DEPRECATED
688  void scheduleJob(SimpleJob *job);
689 #endif
690  void setJobPriority(SimpleJob *job, int priority);
691  void cancelJob(SimpleJob *job);
692  void jobFinished(KIO::SimpleJob *job, KIO::Slave *slave);
693  void putSlaveOnHold(KIO::SimpleJob *job, const KUrl &url);
694  void removeSlaveOnHold();
695  Slave *getConnectedSlave(const KUrl &url, const KIO::MetaData &metaData);
696  bool assignJobToSlave(KIO::Slave *slave, KIO::SimpleJob *job);
697  bool disconnectSlave(KIO::Slave *slave);
698  void checkSlaveOnHold(bool b);
699  void publishSlaveOnHold();
700  Slave *heldSlaveForJob(KIO::SimpleJob *job);
701  bool isSlaveOnHoldFor(const KUrl& url);
702  void registerWindow(QWidget *wid);
703  void updateInternalMetaData(SimpleJob* job);
704 
705  MetaData metaDataFor(const QString &protocol, const QStringList &proxyList, const KUrl &url);
706  void setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol,
707  const QStringList &proxyList, bool newSlave, const KIO::MetaData *config = 0);
708 
709  void slotSlaveDied(KIO::Slave *slave);
710  void slotSlaveStatus(pid_t pid, const QByteArray &protocol,
711  const QString &host, bool connected);
712 
713  void slotReparseSlaveConfiguration(const QString &, const QDBusMessage&);
714  void slotSlaveOnHoldListChanged();
715 
716  void slotSlaveConnected();
717  void slotSlaveError(int error, const QString &errorMsg);
718  void slotUnregisterWindow(QObject *);
719 
720  ProtoQueue *protoQ(const QString& protocol, const QString& host)
721  {
722  ProtoQueue *pq = m_protocols.value(protocol, 0);
723  if (!pq) {
724  kDebug(7006) << "creating ProtoQueue instance for" << protocol;
725 
726  const int maxSlaves = KProtocolInfo::maxSlaves(protocol);
727  int maxSlavesPerHost = -1;
728  if (!host.isEmpty()) {
729  bool ok = false;
730  const int value = SlaveConfig::self()->configData(protocol, host, QLatin1String("MaxConnections")).toInt(&ok);
731  if (ok)
732  maxSlavesPerHost = value;
733  }
734  if (maxSlavesPerHost == -1) {
735  maxSlavesPerHost = KProtocolInfo::maxSlavesPerHost(protocol);
736  }
737  // Never allow maxSlavesPerHost to exceed maxSlaves.
738  pq = new ProtoQueue(this, maxSlaves, qMin(maxSlaves, maxSlavesPerHost));
739  m_protocols.insert(protocol, pq);
740  }
741  return pq;
742  }
743 private:
744  QHash<QString, ProtoQueue *> m_protocols;
745 };
746 
747 
748 K_GLOBAL_STATIC(SchedulerPrivate, schedulerPrivate)
749 
750 Scheduler *Scheduler::self()
751 {
752  return schedulerPrivate->q;
753 }
754 
755 SchedulerPrivate *Scheduler::d_func()
756 {
757  return schedulerPrivate;
758 }
759 
760 //static
761 Scheduler *scheduler()
762 {
763  return schedulerPrivate->q;
764 }
765 
766 //static
767 Slave *heldSlaveForJob(SimpleJob *job)
768 {
769  return schedulerPrivate->heldSlaveForJob(job);
770 }
771 
772 
773 Scheduler::Scheduler()
774  : removeMe(0)
775 {
776  setObjectName( "scheduler" );
777 
778  const QString dbusPath = "/KIO/Scheduler";
779  const QString dbusInterface = "org.kde.KIO.Scheduler";
780  QDBusConnection dbus = QDBusConnection::sessionBus();
781  dbus.registerObject( "/KIO/Scheduler", this, QDBusConnection::ExportScriptableSlots |
782  QDBusConnection::ExportScriptableSignals );
783  dbus.connect(QString(), dbusPath, dbusInterface, "reparseSlaveConfiguration",
784  this, SLOT(slotReparseSlaveConfiguration(QString,QDBusMessage)));
785  dbus.connect(QString(), dbusPath, dbusInterface, "slaveOnHoldListChanged",
786  this, SLOT(slotSlaveOnHoldListChanged()));
787 }
788 
789 Scheduler::~Scheduler()
790 {
791 }
792 
793 void Scheduler::doJob(SimpleJob *job)
794 {
795  schedulerPrivate->doJob(job);
796 }
797 
798 #ifndef KDE_NO_DEPRECATED
799 void Scheduler::scheduleJob(SimpleJob *job)
800 {
801  schedulerPrivate->scheduleJob(job);
802 }
803 #endif
804 
805 void Scheduler::setJobPriority(SimpleJob *job, int priority)
806 {
807  schedulerPrivate->setJobPriority(job, priority);
808 }
809 
810 void Scheduler::cancelJob(SimpleJob *job)
811 {
812  schedulerPrivate->cancelJob(job);
813 }
814 
815 void Scheduler::jobFinished(KIO::SimpleJob *job, KIO::Slave *slave)
816 {
817  schedulerPrivate->jobFinished(job, slave);
818 }
819 
820 void Scheduler::putSlaveOnHold(KIO::SimpleJob *job, const KUrl &url)
821 {
822  schedulerPrivate->putSlaveOnHold(job, url);
823 }
824 
825 void Scheduler::removeSlaveOnHold()
826 {
827  schedulerPrivate->removeSlaveOnHold();
828 }
829 
830 void Scheduler::publishSlaveOnHold()
831 {
832  schedulerPrivate->publishSlaveOnHold();
833 }
834 
835 bool Scheduler::isSlaveOnHoldFor(const KUrl& url)
836 {
837  return schedulerPrivate->isSlaveOnHoldFor(url);
838 }
839 
840 void Scheduler::updateInternalMetaData(SimpleJob* job)
841 {
842  schedulerPrivate->updateInternalMetaData(job);
843 }
844 
845 KIO::Slave *Scheduler::getConnectedSlave(const KUrl &url,
846  const KIO::MetaData &config )
847 {
848  return schedulerPrivate->getConnectedSlave(url, config);
849 }
850 
851 bool Scheduler::assignJobToSlave(KIO::Slave *slave, KIO::SimpleJob *job)
852 {
853  return schedulerPrivate->assignJobToSlave(slave, job);
854 }
855 
856 bool Scheduler::disconnectSlave(KIO::Slave *slave)
857 {
858  return schedulerPrivate->disconnectSlave(slave);
859 }
860 
861 void Scheduler::registerWindow(QWidget *wid)
862 {
863  schedulerPrivate->registerWindow(wid);
864 }
865 
866 void Scheduler::unregisterWindow(QObject *wid)
867 {
868  schedulerPrivate->slotUnregisterWindow(wid);
869 }
870 
871 bool Scheduler::connect( const char *signal, const QObject *receiver,
872  const char *member)
873 {
874  return QObject::connect(self(), signal, receiver, member);
875 }
876 
877 bool Scheduler::connect( const QObject* sender, const char* signal,
878  const QObject* receiver, const char* member )
879 {
880  return QObject::connect(sender, signal, receiver, member);
881 }
882 
883 bool Scheduler::disconnect( const QObject* sender, const char* signal,
884  const QObject* receiver, const char* member )
885 {
886  return QObject::disconnect(sender, signal, receiver, member);
887 }
888 
889 bool Scheduler::connect( const QObject *sender, const char *signal,
890  const char *member )
891 {
892  return QObject::connect(sender, signal, member);
893 }
894 
895 void Scheduler::checkSlaveOnHold(bool b)
896 {
897  schedulerPrivate->checkSlaveOnHold(b);
898 }
899 
900 void Scheduler::emitReparseSlaveConfiguration()
901 {
902  // Do it immediately in this process, otherwise we might send a request before reparsing
903  // (e.g. when changing useragent in the plugin)
904  schedulerPrivate->slotReparseSlaveConfiguration(QString(), QDBusMessage());
905 
906  schedulerPrivate->m_ignoreConfigReparse = true;
907  emit self()->reparseSlaveConfiguration( QString() );
908 }
909 
910 
911 void SchedulerPrivate::slotReparseSlaveConfiguration(const QString &proto, const QDBusMessage&)
912 {
913  if (m_ignoreConfigReparse) {
914  kDebug(7006) << "Ignoring signal sent by myself";
915  m_ignoreConfigReparse = false;
916  return;
917  }
918 
919  kDebug(7006) << "proto=" << proto;
920  KProtocolManager::reparseConfiguration();
921  SlaveConfig::self()->reset();
922  sessionData.reset();
923  NetRC::self()->reload();
924 
925  QHash<QString, ProtoQueue *>::ConstIterator it = proto.isEmpty() ? m_protocols.constBegin() :
926  m_protocols.constFind(proto);
927  // not found?
928  if (it == m_protocols.constEnd()) {
929  return;
930  }
931  QHash<QString, ProtoQueue *>::ConstIterator endIt = proto.isEmpty() ? m_protocols.constEnd() :
932  it + 1;
933  for (; it != endIt; ++it) {
934  Q_FOREACH(Slave *slave, (*it)->allSlaves()) {
935  slave->send(CMD_REPARSECONFIGURATION);
936  slave->resetHost();
937  }
938  }
939 }
940 
941 void SchedulerPrivate::slotSlaveOnHoldListChanged()
942 {
943  m_checkOnHold = true;
944 }
945 
946 static bool mayReturnContent(int cmd, const QString& protocol)
947 {
948  if (cmd == CMD_GET)
949  return true;
950 
951  if (cmd == CMD_MULTI_GET)
952  return true;
953 
954  if (cmd == CMD_SPECIAL && protocol.startsWith(QLatin1String("http"), Qt::CaseInsensitive))
955  return true;
956 
957  return false;
958 }
959 
960 void SchedulerPrivate::doJob(SimpleJob *job)
961 {
962  kDebug(7006) << job;
963  if (QThread::currentThread() != QCoreApplication::instance()->thread()) {
964  kWarning(7006) << "KIO is not thread-safe.";
965  }
966 
967  KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
968  jobPriv->m_proxyList.clear();
969  jobPriv->m_protocol = KProtocolManager::slaveProtocol(job->url(), jobPriv->m_proxyList);
970 
971  if (mayReturnContent(jobCommand(job), jobPriv->m_protocol)) {
972  jobPriv->m_checkOnHold = m_checkOnHold;
973  m_checkOnHold = false;
974  }
975 
976  ProtoQueue *proto = protoQ(jobPriv->m_protocol, job->url().host());
977  proto->queueJob(job);
978 }
979 
980 #ifndef KDE_NO_DEPRECATED
981 void SchedulerPrivate::scheduleJob(SimpleJob *job)
982 {
983  kDebug(7006) << job;
984  setJobPriority(job, 1);
985 }
986 #endif
987 
988 void SchedulerPrivate::setJobPriority(SimpleJob *job, int priority)
989 {
990  kDebug(7006) << job << priority;
991  ProtoQueue *proto = protoQ(SimpleJobPrivate::get(job)->m_protocol, job->url().host());
992  proto->changeJobPriority(job, priority);
993 }
994 
995 void SchedulerPrivate::cancelJob(SimpleJob *job)
996 {
997  // this method is called all over the place in job.cpp, so just do this check here to avoid
998  // much boilerplate in job code.
999  if (SimpleJobPrivate::get(job)->m_schedSerial == 0) {
1000  //kDebug(7006) << "Doing nothing because I don't know job" << job;
1001  return;
1002  }
1003  Slave *slave = jobSlave(job);
1004  kDebug(7006) << job << slave;
1005  if (slave) {
1006  kDebug(7006) << "Scheduler: killing slave " << slave->slave_pid();
1007  slave->kill();
1008  }
1009  jobFinished(job, slave);
1010 }
1011 
1012 void SchedulerPrivate::jobFinished(SimpleJob *job, Slave *slave)
1013 {
1014  kDebug(7006) << job << slave;
1015  if (QThread::currentThread() != QCoreApplication::instance()->thread()) {
1016  kWarning(7006) << "KIO is not thread-safe.";
1017  }
1018 
1019  KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
1020 
1021  // make sure that we knew about the job!
1022  Q_ASSERT(jobPriv->m_schedSerial);
1023 
1024  ProtoQueue *pq = m_protocols.value(jobPriv->m_protocol);
1025  if (pq) {
1026  pq->removeJob(job);
1027  }
1028 
1029  if (slave) {
1030  // If we have internal meta-data, tell existing ioslaves to reload
1031  // their configuration.
1032  if (jobPriv->m_internalMetaData.count()) {
1033  kDebug(7006) << "Updating ioslaves with new internal metadata information";
1034  ProtoQueue * queue = m_protocols.value(slave->protocol());
1035  if (queue) {
1036  QListIterator<Slave*> it (queue->allSlaves());
1037  while (it.hasNext()) {
1038  Slave* runningSlave = it.next();
1039  if (slave->host() == runningSlave->host()) {
1040  slave->setConfig(metaDataFor(slave->protocol(), jobPriv->m_proxyList, job->url()));
1041  kDebug(7006) << "Updated configuration of" << slave->protocol()
1042  << "ioslave, pid=" << slave->slave_pid();
1043  }
1044  }
1045  }
1046  }
1047  slave->setJob(0);
1048  slave->disconnect(job);
1049  }
1050  jobPriv->m_schedSerial = 0; // this marks the job as unscheduled again
1051  jobPriv->m_slave = 0;
1052  // Clear the values in the internal metadata container since they have
1053  // already been taken care of above...
1054  jobPriv->m_internalMetaData.clear();
1055 }
1056 
1057 // static
1058 void setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol,
1059  const QStringList &proxyList , bool newSlave, const KIO::MetaData *config)
1060 {
1061  schedulerPrivate->setupSlave(slave, url, protocol, proxyList, newSlave, config);
1062 }
1063 
1064 MetaData SchedulerPrivate::metaDataFor(const QString &protocol, const QStringList &proxyList, const KUrl &url)
1065 {
1066  const QString host = url.host();
1067  MetaData configData = SlaveConfig::self()->configData(protocol, host);
1068  sessionData.configDataFor( configData, protocol, host );
1069  if (proxyList.isEmpty()) {
1070  configData.remove(QLatin1String("UseProxy"));
1071  configData.remove(QLatin1String("ProxyUrls"));
1072  } else {
1073  configData[QLatin1String("UseProxy")] = proxyList.first();
1074  configData[QLatin1String("ProxyUrls")] = proxyList.join(QLatin1String(","));
1075  }
1076 
1077  if ( configData.contains("EnableAutoLogin") &&
1078  configData.value("EnableAutoLogin").compare("true", Qt::CaseInsensitive) == 0 )
1079  {
1080  NetRC::AutoLogin l;
1081  l.login = url.user();
1082  bool usern = (protocol == "ftp");
1083  if ( NetRC::self()->lookup( url, l, usern) )
1084  {
1085  configData["autoLoginUser"] = l.login;
1086  configData["autoLoginPass"] = l.password;
1087  if ( usern )
1088  {
1089  QString macdef;
1090  QMap<QString, QStringList>::ConstIterator it = l.macdef.constBegin();
1091  for ( ; it != l.macdef.constEnd(); ++it )
1092  macdef += it.key() + '\\' + it.value().join( "\\" ) + '\n';
1093  configData["autoLoginMacro"] = macdef;
1094  }
1095  }
1096  }
1097 
1098  return configData;
1099 }
1100 
1101 void SchedulerPrivate::setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol,
1102  const QStringList &proxyList, bool newSlave, const KIO::MetaData *config)
1103 {
1104  int port = url.port();
1105  if ( port == -1 ) // no port is -1 in QUrl, but in kde3 we used 0 and the kioslaves assume that.
1106  port = 0;
1107  const QString host = url.host();
1108  const QString user = url.user();
1109  const QString passwd = url.pass();
1110 
1111  if (newSlave || slave->host() != host || slave->port() != port ||
1112  slave->user() != user || slave->passwd() != passwd) {
1113 
1114  MetaData configData = metaDataFor(protocol, proxyList, url);
1115  if (config)
1116  configData += *config;
1117 
1118  slave->setConfig(configData);
1119  slave->setProtocol(url.protocol());
1120  slave->setHost(host, port, user, passwd);
1121  }
1122 }
1123 
1124 
1125 void SchedulerPrivate::slotSlaveStatus(pid_t, const QByteArray&, const QString &, bool)
1126 {
1127 }
1128 
1129 
1130 void SchedulerPrivate::slotSlaveDied(KIO::Slave *slave)
1131 {
1132  kDebug(7006) << slave;
1133  Q_ASSERT(slave);
1134  Q_ASSERT(!slave->isAlive());
1135  ProtoQueue *pq = m_protocols.value(slave->protocol());
1136  if (pq) {
1137  if (slave->job()) {
1138  pq->removeJob(slave->job());
1139  }
1140  // in case this was a connected slave...
1141  pq->removeSlave(slave);
1142  }
1143  if (slave == m_slaveOnHold) {
1144  m_slaveOnHold = 0;
1145  m_urlOnHold.clear();
1146  }
1147  slave->deref(); // Delete slave
1148 }
1149 
1150 void SchedulerPrivate::putSlaveOnHold(KIO::SimpleJob *job, const KUrl &url)
1151 {
1152  Slave *slave = jobSlave(job);
1153  kDebug(7006) << job << url << slave;
1154  slave->disconnect(job);
1155  // prevent the fake death of the slave from trying to kill the job again;
1156  // cf. Slave::hold(const KUrl &url) called in SchedulerPrivate::publishSlaveOnHold().
1157  slave->setJob(0);
1158  SimpleJobPrivate::get(job)->m_slave = 0;
1159 
1160  if (m_slaveOnHold) {
1161  m_slaveOnHold->kill();
1162  }
1163  m_slaveOnHold = slave;
1164  m_urlOnHold = url;
1165  m_slaveOnHold->suspend();
1166 }
1167 
1168 void SchedulerPrivate::publishSlaveOnHold()
1169 {
1170  kDebug(7006) << m_slaveOnHold;
1171  if (!m_slaveOnHold)
1172  return;
1173 
1174  m_slaveOnHold->hold(m_urlOnHold);
1175  emit q->slaveOnHoldListChanged();
1176 }
1177 
1178 bool SchedulerPrivate::isSlaveOnHoldFor(const KUrl& url)
1179 {
1180  if (url.isValid() && m_urlOnHold.isValid() && url == m_urlOnHold)
1181  return true;
1182 
1183  return Slave::checkForHeldSlave(url);
1184 }
1185 
1186 Slave *SchedulerPrivate::heldSlaveForJob(SimpleJob *job)
1187 {
1188  Slave *slave = 0;
1189  KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
1190 
1191  if (jobPriv->m_checkOnHold) {
1192  slave = Slave::holdSlave(jobPriv->m_protocol, job->url());
1193  }
1194 
1195  if (!slave && m_slaveOnHold) {
1196  // Make sure that the job wants to do a GET or a POST, and with no offset
1197  const int cmd = jobPriv->m_command;
1198  bool canJobReuse = (cmd == CMD_GET || cmd == CMD_MULTI_GET);
1199 
1200  if (KIO::TransferJob *tJob = qobject_cast<KIO::TransferJob *>(job)) {
1201  canJobReuse = ( canJobReuse || cmd == CMD_SPECIAL );
1202  if (canJobReuse) {
1203  KIO::MetaData outgoing = tJob->outgoingMetaData();
1204  const QString resume = outgoing.value("resume");
1205  kDebug(7006) << "Resume metadata is" << resume;
1206  canJobReuse = (resume.isEmpty() || resume == "0");
1207  }
1208  }
1209 
1210  if (job->url() == m_urlOnHold) {
1211  if (canJobReuse) {
1212  kDebug(7006) << "HOLD: Reusing held slave (" << m_slaveOnHold << ")";
1213  slave = m_slaveOnHold;
1214  } else {
1215  kDebug(7006) << "HOLD: Discarding held slave (" << m_slaveOnHold << ")";
1216  m_slaveOnHold->kill();
1217  }
1218  m_slaveOnHold = 0;
1219  m_urlOnHold.clear();
1220  }
1221  } else if (slave) {
1222  kDebug(7006) << "HOLD: Reusing klauncher held slave (" << slave << ")";
1223  }
1224 
1225  return slave;
1226 }
1227 
1228 void SchedulerPrivate::removeSlaveOnHold()
1229 {
1230  kDebug(7006) << m_slaveOnHold;
1231  if (m_slaveOnHold) {
1232  m_slaveOnHold->kill();
1233  }
1234  m_slaveOnHold = 0;
1235  m_urlOnHold.clear();
1236 }
1237 
1238 Slave *SchedulerPrivate::getConnectedSlave(const KUrl &url, const KIO::MetaData &config)
1239 {
1240  QStringList proxyList;
1241  const QString protocol = KProtocolManager::slaveProtocol(url, proxyList);
1242  ProtoQueue *pq = protoQ(protocol, url.host());
1243 
1244  Slave *slave = pq->createSlave(protocol, /* job */0, url);
1245  if (slave) {
1246  setupSlave(slave, url, protocol, proxyList, true, &config);
1247  pq->m_connectedSlaveQueue.addSlave(slave);
1248 
1249  slave->send( CMD_CONNECT );
1250  q->connect(slave, SIGNAL(connected()),
1251  SLOT(slotSlaveConnected()));
1252  q->connect(slave, SIGNAL(error(int,QString)),
1253  SLOT(slotSlaveError(int,QString)));
1254  }
1255  kDebug(7006) << url << slave;
1256  return slave;
1257 }
1258 
1259 
1260 void SchedulerPrivate::slotSlaveConnected()
1261 {
1262  kDebug(7006);
1263  Slave *slave = static_cast<Slave *>(q->sender());
1264  slave->setConnected(true);
1265  q->disconnect(slave, SIGNAL(connected()), q, SLOT(slotSlaveConnected()));
1266  emit q->slaveConnected(slave);
1267 }
1268 
1269 void SchedulerPrivate::slotSlaveError(int errorNr, const QString &errorMsg)
1270 {
1271  Slave *slave = static_cast<Slave *>(q->sender());
1272  kDebug(7006) << slave << errorNr << errorMsg;
1273  ProtoQueue *pq = protoQ(slave->protocol(), slave->host());
1274  if (!slave->isConnected() || pq->m_connectedSlaveQueue.isIdle(slave)) {
1275  // Only forward to application if slave is idle or still connecting.
1276  // ### KDE5: can we remove this apparently arbitrary behavior and just always emit SlaveError?
1277  emit q->slaveError(slave, errorNr, errorMsg);
1278  }
1279 }
1280 
1281 bool SchedulerPrivate::assignJobToSlave(KIO::Slave *slave, SimpleJob *job)
1282 {
1283  kDebug(7006) << slave << job;
1284  // KDE5: queueing of jobs can probably be removed, it provides very little benefit
1285  ProtoQueue *pq = m_protocols.value(slave->protocol());
1286  if (pq) {
1287  pq->removeJob(job);
1288  return pq->m_connectedSlaveQueue.queueJob(job, slave);
1289  }
1290  return false;
1291 }
1292 
1293 bool SchedulerPrivate::disconnectSlave(KIO::Slave *slave)
1294 {
1295  kDebug(7006) << slave;
1296  ProtoQueue *pq = m_protocols.value(slave->protocol());
1297  return (pq ? pq->m_connectedSlaveQueue.removeSlave(slave) : false);
1298 }
1299 
1300 void SchedulerPrivate::checkSlaveOnHold(bool b)
1301 {
1302  kDebug(7006) << b;
1303  m_checkOnHold = b;
1304 }
1305 
1306 /*
1307  Returns the top most window associated with widget.
1308 
1309  Unlike QWidget::window(), this function does its best to find and return the
1310  main application window associated with the given widget.
1311 
1312  If widget itself is a dialog or its parent is a dialog, and that dialog has a
1313  parent widget then this function will iterate through all those widgets to
1314  find the top most window, which most of the time is the main window of the
1315  application. By contrast, QWidget::window() would simply return the first
1316  file dialog it encountered since it is the "next ancestor widget that has (or
1317  could have) a window-system frame".
1318 */
1319 static QWidget* topLevelWindow(QWidget* widget)
1320 {
1321  QWidget* w = widget;
1322  while (w && w->parentWidget()) {
1323  w = w->parentWidget();
1324  }
1325  return (w ? w->window() : 0);
1326 }
1327 
1328 void SchedulerPrivate::registerWindow(QWidget *wid)
1329 {
1330  if (!wid)
1331  return;
1332 
1333  QWidget* window = topLevelWindow(wid);
1334  QObject *obj = static_cast<QObject *>(window);
1335 
1336  if (!m_windowList.contains(obj))
1337  {
1338  // We must store the window Id because by the time
1339  // the destroyed signal is emitted we can no longer
1340  // access QWidget::winId() (already destructed)
1341  WId windowId = window->winId();
1342  m_windowList.insert(obj, windowId);
1343  q->connect(window, SIGNAL(destroyed(QObject*)),
1344  SLOT(slotUnregisterWindow(QObject*)));
1345  QDBusInterface("org.kde.kded", "/kded", "org.kde.kded").
1346  call(QDBus::NoBlock, "registerWindowId", qlonglong(windowId));
1347  }
1348 }
1349 
1350 void SchedulerPrivate::slotUnregisterWindow(QObject *obj)
1351 {
1352  if (!obj)
1353  return;
1354 
1355  QMap<QObject *, WId>::Iterator it = m_windowList.find(obj);
1356  if (it == m_windowList.end())
1357  return;
1358  WId windowId = it.value();
1359  q->disconnect(it.key(), SIGNAL(destroyed(QObject*)),
1360  q, SLOT(slotUnregisterWindow(QObject*)));
1361  m_windowList.erase( it );
1362  QDBusInterface("org.kde.kded", "/kded", "org.kde.kded").
1363  call(QDBus::NoBlock, "unregisterWindowId", qlonglong(windowId));
1364 }
1365 
1366 void SchedulerPrivate::updateInternalMetaData(SimpleJob* job)
1367 {
1368  KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
1369  // Preserve all internal meta-data so they can be sent back to the
1370  // ioslaves as needed...
1371  const KUrl jobUrl = job->url();
1372  kDebug(7006) << job << jobPriv->m_internalMetaData;
1373  QMapIterator<QString, QString> it (jobPriv->m_internalMetaData);
1374  while (it.hasNext()) {
1375  it.next();
1376  if (it.key().startsWith(QLatin1String("{internal~currenthost}"), Qt::CaseInsensitive)) {
1377  SlaveConfig::self()->setConfigData(jobUrl.protocol(), jobUrl.host(), it.key().mid(22), it.value());
1378  } else if (it.key().startsWith(QLatin1String("{internal~allhosts}"), Qt::CaseInsensitive)) {
1379  SlaveConfig::self()->setConfigData(jobUrl.protocol(), QString(), it.key().mid(19), it.value());
1380  }
1381  }
1382 }
1383 
1384 
1385 #include "scheduler.moc"
1386 #include "scheduler_p.moc"
KIO::Scheduler::removeSlaveOnHold
static void removeSlaveOnHold()
Removes any slave that might have been put on hold.
Definition: scheduler.cpp:825
KIO::HostQueue::isJobRunning
bool isJobRunning(KIO::SimpleJob *job) const
Definition: scheduler_p.h:63
KCompositeJob::kill
bool kill(KillVerbosity verbosity=Quietly)
KIO::Scheduler::scheduleJob
static void scheduleJob(SimpleJob *job)
Schedules job scheduled for later execution.
Definition: scheduler.cpp:799
QList::clear
void clear()
KIO::ProtoQueue::~ProtoQueue
~ProtoQueue()
Definition: scheduler.cpp:409
KIO::SlaveConfig::configData
MetaData configData(const QString &protocol, const QString &host)
Query slave configuration for slaves of type protocol when dealing with host.
Definition: slaveconfig.cpp:187
QWidget
KIO::Scheduler::doJob
static void doJob(SimpleJob *job)
Register job with the scheduler.
Definition: scheduler.cpp:793
KProtocolInfo::maxSlaves
static int maxSlaves(const QString &protocol)
KIO::ConnectedSlaveQueue::isIdle
bool isIdle(KIO::Slave *slave)
Definition: scheduler.cpp:311
KIO::Scheduler::getConnectedSlave
static KIO::Slave * getConnectedSlave(const KUrl &url, const KIO::MetaData &config=MetaData())
Requests a slave for use in connection-oriented mode.
Definition: scheduler.cpp:845
KIO::ProtoQueue::removeJob
void removeJob(KIO::SimpleJob *job)
Definition: scheduler.cpp:480
QMap::contains
bool contains(const Key &key) const
KProtocolManager::reparseConfiguration
static void reparseConfiguration()
Force a reload of the general config file of io-slaves ( kioslaverc).
Definition: kprotocolmanager.cpp:232
kdebug.h
KIO::JobPrivate::m_internalMetaData
MetaData m_internalMetaData
Definition: job_p.h:61
KIO::SimpleJobPrivate::m_proxyList
QStringList m_proxyList
Definition: job_p.h:126
KIO::NetRC::AutoLogin::password
QString password
Definition: authinfo.h:337
QWidget::window
QWidget * window() const
QByteArray
KIO::Scheduler::setJobPriority
static void setJobPriority(SimpleJob *job, int priority)
Changes the priority of job; jobs of the same priority run in the order in which they were created...
Definition: scheduler.cpp:805
connection.h
KIO::NetRC::self
static NetRC * self()
A reference to the instance of the class.
Definition: authinfo.cpp:286
KIO::SlaveConfig::setConfigData
void setConfigData(const QString &protocol, const QString &host, const QString &key, const QString &value)
Configure slaves of type protocol by setting key to value.
Definition: slaveconfig.cpp:155
timeout
int timeout
KIO::Scheduler::disconnectSlave
static bool disconnectSlave(KIO::Slave *slave)
Disconnects slave.
Definition: scheduler.cpp:856
slave.h
K_GLOBAL_STATIC
#define K_GLOBAL_STATIC(TYPE, NAME)
QMap::constBegin
const_iterator constBegin() const
KIO::SimpleJobPrivate::m_checkOnHold
bool m_checkOnHold
Definition: job_p.h:127
QMap
Definition: netaccess.h:36
QUrl::host
QString host() const
QDBusConnection::registerObject
bool registerObject(const QString &path, QObject *object, QFlags< QDBusConnection::RegisterOption > options)
scheduler_p.h
KIO::ProtoQueue::changeJobPriority
void changeJobPriority(KIO::SimpleJob *job, int newPriority)
Definition: scheduler.cpp:457
KIO::SimpleJob::slotError
void slotError(int, const QString &)
Definition: job.cpp:500
KIO::SimpleJob::url
const KUrl & url() const
Returns the SimpleJob's URL.
Definition: job.cpp:341
kError
static QDebug kError(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
KIO::PerSlaveQueue
Definition: scheduler_p.h:75
KIO::NetRC::AutoLogin::login
QString login
Definition: authinfo.h:336
KIO::SlaveKeeper::SlaveKeeper
SlaveKeeper()
Definition: scheduler.cpp:81
QDBusConnection
KIO::Scheduler::emitReparseSlaveConfiguration
static void emitReparseSlaveConfiguration()
Definition: scheduler.cpp:900
QDBusConnection::sessionBus
QDBusConnection sessionBus()
KIO::ConnectedSlaveQueue::ConnectedSlaveQueue
ConnectedSlaveQueue()
Definition: scheduler.cpp:219
QStringList::join
QString join(const QString &separator) const
QSet::insert
const_iterator insert(const T &value)
KIO::PerSlaveQueue::waitingList
QList< SimpleJob * > waitingList
Definition: scheduler_p.h:78
QUrl::port
int port() const
KIO::CMD_CONNECT
Definition: global.h:152
KIO::CMD_SPECIAL
Definition: global.h:169
KIO::Slave::kill
void kill()
Force termination.
Definition: slave.cpp:361
QMap::clear
void clear()
kDebug
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
QObject::disconnect
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
KProtocolInfo::maxSlavesPerHost
static int maxSlavesPerHost(const QString &protocol)
KIO::SimpleJobPrivate
Definition: job_p.h:81
KIO::MetaData
MetaData is a simple map of key/value strings.
Definition: global.h:396
KIO::SlaveConfig::reset
void reset()
Undo any changes made by calls to setConfigData.
Definition: slaveconfig.cpp:211
KIO::Scheduler::unregisterWindow
static void unregisterWindow(QObject *wid)
Definition: scheduler.cpp:866
KIO::Scheduler::updateInternalMetaData
static void updateInternalMetaData(SimpleJob *job)
Updates the internal metadata from job.
Definition: scheduler.cpp:840
KUrl
KIO::Slave
Definition: slave.h:48
KIO::ProtoQueue::queueJob
void queueJob(KIO::SimpleJob *job)
Definition: scheduler.cpp:418
kprotocolmanager.h
scheduler.h
KIO::HostQueue::lowestSerial
int lowestSerial() const
Definition: scheduler.cpp:165
KIO::Slave::holdSlave
static Slave * holdSlave(const QString &protocol, const KUrl &url)
Requests a slave on hold for ths url, from klauncher, if there is such a job.
Definition: slave.cpp:484
KIO::Scheduler::checkSlaveOnHold
static void checkSlaveOnHold(bool b)
When true, the next job will check whether KLauncher has a slave on hold that is suitable for the job...
Definition: scheduler.cpp:895
KIO::Scheduler::assignJobToSlave
static bool assignJobToSlave(KIO::Slave *slave, KIO::SimpleJob *job)
Uses slave to do job.
Definition: scheduler.cpp:851
KIO::ConnectedSlaveQueue::addSlave
void addSlave(KIO::Slave *slave)
Definition: scheduler.cpp:281
setupSlave
static void setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol, const QStringList &proxyList, bool newSlave, const KIO::MetaData *config=0)
Definition: scheduler.cpp:1058
KIO::Scheduler::jobFinished
static void jobFinished(KIO::SimpleJob *job, KIO::Slave *slave)
Called when a job is done.
Definition: scheduler.cpp:815
KIO::Slave::port
quint16 port()
Definition: slave.cpp:199
QList::append
void append(const T &value)
KIO::Slave::host
QString host()
Definition: slave.cpp:193
sessiondata.h
KIO::Slave::setHost
virtual void setHost(const QString &host, quint16 port, const QString &user, const QString &passwd)
Set host for url.
Definition: slave.cpp:378
QMapIterator
KIO::Slave::deref
void deref()
Definition: slave.cpp:241
mayReturnContent
static bool mayReturnContent(int cmd, const QString &protocol)
Definition: scheduler.cpp:946
QHash::constEnd
const_iterator constEnd() const
KIO::Slave::protocol
QString protocol()
The protocol this slave handles.
Definition: slave.cpp:175
QHash
s_idleSlaveLifetime
static const int s_idleSlaveLifetime
Definition: scheduler.cpp:41
KIO::Slave::job
KIO::SimpleJob * job() const
Definition: slave.cpp:282
KIO::ProtoQueue::createSlave
KIO::Slave * createSlave(const QString &protocol, KIO::SimpleJob *job, const KUrl &url)
Definition: scheduler.cpp:533
KIO::SerialPicker::next
int next()
Definition: scheduler_p.h:118
QObject
KUrl::user
QString user() const
KUrl::protocol
QString protocol() const
KIO::ProtoQueue::allSlaves
QList< KIO::Slave * > allSlaves() const
Definition: scheduler.cpp:560
KUrl::pass
QString pass() const
QList::isEmpty
bool isEmpty() const
KIO::Slave::resetHost
void resetHost()
Clear host info.
Definition: slave.cpp:394
scheduler
static Scheduler * scheduler()
Definition: scheduler.cpp:761
QString::isEmpty
bool isEmpty() const
startJob
static void startJob(SimpleJob *job, Slave *slave)
Definition: scheduler.cpp:58
KIO::Slave::setProtocol
void setProtocol(const QString &protocol)
Definition: slave.cpp:181
QMap::constEnd
const_iterator constEnd() const
KIO::HostQueue::allSlaves
QList< KIO::Slave * > allSlaves() const
Definition: scheduler.cpp:206
QString::startsWith
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
verifyRunningJobsCount
static void verifyRunningJobsCount(QHash< QString, HostQueue > *queues, int runningJobsCount)
Definition: scheduler.cpp:371
KIO::Scheduler::cancelJob
static void cancelJob(SimpleJob *job)
Stop the execution of a job.
Definition: scheduler.cpp:810
KIO::ConnectedSlaveQueue::queueJob
bool queueJob(KIO::SimpleJob *job, KIO::Slave *slave)
Definition: scheduler.cpp:225
KIO::Scheduler::isSlaveOnHoldFor
static bool isSlaveOnHoldFor(const KUrl &url)
Returns true if there is a slave on hold for url.
Definition: scheduler.cpp:835
jobCommand
static int jobCommand(SimpleJob *job)
Definition: scheduler.cpp:53
KIO::SimpleJobPrivate::start
virtual void start(KIO::Slave *slave)
Definition: job.cpp:385
QCoreApplication::instance
QCoreApplication * instance()
QSet
KIO::ConnectedSlaveQueue::removeJob
bool removeJob(KIO::SimpleJob *job)
Definition: scheduler.cpp:243
QWidget::winId
WId winId() const
QObject::deleteLater
void deleteLater()
QList::first
T & first()
KIO::HostQueue::runningJobsCount
int runningJobsCount() const
Definition: scheduler_p.h:59
KIO::Slave::send
virtual void send(int cmd, const QByteArray &arr=QByteArray())
Sends the given command to the kioslave.
Definition: slave.cpp:332
QString
QList
QMap::end
iterator end()
KIO::SimpleJobPrivate::m_protocol
QString m_protocol
Definition: job_p.h:125
KIO::ConnectedSlaveQueue::allSlaves
QList< KIO::Slave * > allSlaves() const
Definition: scheduler_p.h:97
KIO::SessionData
Definition: sessiondata.h:35
QStringList
KIO::NetRC::AutoLogin::macdef
QMap< QString, QStringList > macdef
Definition: authinfo.h:338
authinfo.h
KIO::Slave::idleTime
time_t idleTime()
Definition: slave.cpp:252
QDBusInterface
jobSlave
static Slave * jobSlave(SimpleJob *job)
Definition: scheduler.cpp:47
KIO::SlaveKeeper::returnSlave
void returnSlave(KIO::Slave *slave)
Definition: scheduler.cpp:87
QHash::value
const T value(const Key &key) const
KIO::Scheduler::putSlaveOnHold
static void putSlaveOnHold(KIO::SimpleJob *job, const KUrl &url)
Puts a slave on notice.
Definition: scheduler.cpp:820
kprotocolinfo.h
ok
KGuiItem ok()
QSet::contains
bool contains(const T &value) const
KIO::HostQueue::removeJob
bool removeJob(KIO::SimpleJob *job)
Definition: scheduler.cpp:193
QMap::key
const Key key(const T &value) const
KIO::ProtoQueue::m_connectedSlaveQueue
ConnectedSlaveQueue m_connectedSlaveQueue
Definition: scheduler_p.h:149
KProtocolManager::slaveProtocol
static QString slaveProtocol(const KUrl &url, QString &proxy)
Return the protocol to use in order to handle the given url It's usually the same, except that FTP, when handled by a proxy, needs an HTTP ioslave.
Definition: kprotocolmanager.cpp:525
KIO::Slave::user
QString user()
Definition: slave.cpp:205
job_p.h
KIO::SimpleJobPrivate::m_slave
Slave * m_slave
Definition: job_p.h:104
KIO::SimpleJobPrivate::get
static SimpleJobPrivate * get(KIO::SimpleJob *job)
Definition: job_p.h:199
QUrl::isValid
bool isValid() const
KIO::Scheduler
The KIO::Scheduler manages io-slaves for the application.
Definition: scheduler.h:109
KIO::HostQueue::isQueueEmpty
bool isQueueEmpty() const
Definition: scheduler_p.h:57
KIO::SerialPicker::changedPrioritySerial
int changedPrioritySerial(int oldSerial, int newPriority) const
Definition: scheduler.cpp:72
QDBusMessage
QLatin1String
QThread::currentThread
QThread * currentThread()
KIO::Slave::setConfig
virtual void setConfig(const MetaData &config)
Configure slave.
Definition: slave.cpp:401
QWidget::parentWidget
QWidget * parentWidget() const
ensureNoDuplicates
static void ensureNoDuplicates(QMap< int, HostQueue * > *queuesBySerial)
Definition: scheduler.cpp:358
heldSlaveForJob
static Slave * heldSlaveForJob(SimpleJob *job)
Definition: scheduler.cpp:767
KIO::ProtoQueue::ProtoQueue
ProtoQueue(KIO::SchedulerPrivate *sp, int maxSlaves, int maxSlavesPerHost)
Definition: scheduler.cpp:394
kWarning
static QDebug kWarning(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
KIO::SimpleJobPrivate::m_schedSerial
int m_schedSerial
Definition: job_p.h:128
KIO::Scheduler::registerWindow
static void registerWindow(QWidget *wid)
Register the mainwindow wid with the KIO subsystem Do not call this, it is called automatically from ...
Definition: scheduler.cpp:861
KIO::NetRC::reload
void reload()
Reloads the auto login information.
Definition: authinfo.cpp:370
QTimer::start
void start(int msec)
KIO::SerialPicker::maxSerial
static const int maxSerial
Definition: scheduler_p.h:132
QListIterator
KIO::HostQueue
Definition: scheduler_p.h:52
KIO::HostQueue::isEmpty
bool isEmpty() const
Definition: scheduler_p.h:58
KIO::SlaveKeeper::removeSlave
bool removeSlave(KIO::Slave *slave)
Definition: scheduler.cpp:116
slaveconfig.h
KIO::HostQueue::queueJob
void queueJob(KIO::SimpleJob *job)
Definition: scheduler.cpp:174
KIO::Slave::passwd
QString passwd()
Definition: slave.cpp:211
KIO::Slave::setIdle
void setIdle()
Marks this slave as idle.
Definition: slave.cpp:217
KIO::Slave::createSlave
static Slave * createSlave(const QString &protocol, const KUrl &url, int &error, QString &error_text)
Creates a new slave.
Definition: slave.cpp:410
KIO::SimpleJobPrivate::m_command
int m_command
Definition: job_p.h:108
KIO::NetRC::lookup
bool lookup(const KUrl &url, AutoLogin &login, bool userealnetrc=false, const QString &type=QString(), LookUpMode mode=LookUpMode(exactOnly)|defaultOnly)
Looks up the login information for the given url.
Definition: authinfo.cpp:293
KIO::Slave::isAlive
bool isAlive()
Definition: slave.cpp:288
KIO::Slave::isConnected
bool isConnected()
Definition: slave.cpp:223
KIO::Scheduler::reparseSlaveConfiguration
Q_SCRIPTABLE void reparseSlaveConfiguration(const QString &)
KIO::TransferJob
The transfer job pumps data into and/or out of a Slave.
Definition: jobclasses.h:555
KIO::ProtoQueue::removeSlave
bool removeSlave(KIO::Slave *slave)
Definition: scheduler.cpp:552
QDBusConnection::connect
bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, QObject *receiver, const char *slot)
KIO::Scheduler::publishSlaveOnHold
static void publishSlaveOnHold()
Send the slave that was put on hold back to KLauncher.
Definition: scheduler.cpp:830
KIO::CMD_MULTI_GET
Definition: global.h:178
KIO::Scheduler::disconnect
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
Definition: scheduler.cpp:883
KIO::SlaveConfig::self
static SlaveConfig * self()
Definition: slaveconfig.cpp:137
QTimer::isActive
bool isActive() const
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
KIO::SimpleJobPrivate::m_url
KUrl m_url
Definition: job_p.h:106
KIO::Scheduler::connect
static bool connect(const char *signal, const QObject *receiver, const char *member)
Function to connect signals emitted by the scheduler.
Definition: scheduler.cpp:871
QString::compare
int compare(const QString &other) const
KIO::PerSlaveQueue::runningJob
SimpleJob * runningJob
Definition: scheduler_p.h:79
KIO::HostQueue::takeFirstInQueue
KIO::SimpleJob * takeFirstInQueue()
Definition: scheduler.cpp:183
KIO::Slave::checkForHeldSlave
static bool checkForHeldSlave(const KUrl &url)
Returns true if klauncher is holding a slave for url.
Definition: slave.cpp:508
QMap::count
int count(const Key &key) const
QMap::find
iterator find(const Key &key)
KIO::ConnectedSlaveQueue::removeSlave
bool removeSlave(KIO::Slave *slave)
Definition: scheduler.cpp:289
KIO::CMD_REPARSECONFIGURATION
Definition: global.h:171
QMap::iterator
KIO::Slave::setConnected
void setConnected(bool c)
Definition: slave.cpp:229
KIO::SlaveKeeper::allSlaves
QList< KIO::Slave * > allSlaves() const
Definition: scheduler.cpp:129
QMultiHash
topLevelWindow
static QWidget * topLevelWindow(QWidget *widget)
Definition: scheduler.cpp:1319
KIO::ProtoQueue
Definition: scheduler_p.h:136
QMap::value
const T value(const Key &key) const
QMap::remove
int remove(const Key &key)
QTimer::setSingleShot
void setSingleShot(bool singleShot)
KIO::NetRC::AutoLogin
Contains auto login information.
Definition: authinfo.h:332
KIO::CMD_GET
Definition: global.h:159
KIO::SlaveKeeper::takeSlaveForJob
KIO::Slave * takeSlaveForJob(KIO::SimpleJob *job)
Definition: scheduler.cpp:95
KIO::SimpleJob
A simple job (one url and one command).
Definition: jobclasses.h:322
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:24:53 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KIO

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

kdelibs API Reference

Skip menu "kdelibs API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal