]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/console/console.cpp
Backport from BEE
[bacula/bacula] / bacula / src / qt-console / console / console.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2014 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from many
7    others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    Bacula® is a registered trademark of Kern Sibbald.
15 */
16 /*
17  *  Console Class
18  *
19  *   Written by Kern Sibbald, January MMVII
20  *
21  */
22
23 #include "bat.h"
24 #include "console.h"
25 #include "restore.h"
26 #include "select.h"
27 #include "run/run.h"
28
29 Console::Console(QTabWidget *parent) : Pages()
30 {
31    QFont font;
32    m_name = tr("Console");
33    m_messages_pending = false;
34    m_parent = parent;
35    m_closeable = false;
36    m_console = this;
37    m_warningPrevent = false;
38    m_dircommCounter = 0;
39
40    /*
41     * Create a connection to the Director and put it in a hash table
42     */
43    m_dircommHash.insert(m_dircommCounter, new DirComm(this, m_dircommCounter));
44
45    setupUi(this);
46    m_textEdit = textEdit;   /* our console screen */
47    m_cursor = new QTextCursor(m_textEdit->document());
48    mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
49
50    m_timer = NULL;
51    m_contextActions.append(actionStatusDir);
52    m_contextActions.append(actionConsoleHelp);
53    m_contextActions.append(actionRequestMessages);
54    m_contextActions.append(actionConsoleReload);
55    connect(actionStatusDir, SIGNAL(triggered()), this, SLOT(status_dir()));
56    connect(actionConsoleHelp, SIGNAL(triggered()), this, SLOT(consoleHelp()));
57    connect(actionConsoleReload, SIGNAL(triggered()), this, SLOT(consoleReload()));
58    connect(actionRequestMessages, SIGNAL(triggered()), this, SLOT(messages()));
59 }
60
61 Console::~Console()
62 {
63 }
64
65 void Console::startTimer()
66 {
67    m_timer = new QTimer(this);
68    QWidget::connect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
69    m_timer->start(mainWin->m_checkMessagesInterval*30000);
70 }
71
72 void Console::stopTimer()
73 {
74    if (m_timer) {
75       QWidget::disconnect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
76       m_timer->stop();
77       delete m_timer;
78       m_timer = NULL;
79    }
80 }
81
82 /* slot connected to the timer
83  * requires preferences of check messages and operates at interval */
84 void Console::poll_messages()
85 {
86    int conn;
87
88    /* Do not poll if notifier off */
89    if (!mainWin->m_notify) {
90       return;
91    }
92
93    /*
94     * Note if we call getDirComm here, we continuously consume
95     *  file descriptors.
96     */
97    if (!findDirComm(conn)) {    /* find a free DirComm */
98       return;                   /* try later */
99    }
100
101    DirComm *dircomm = m_dircommHash.value(conn);
102    if (mainWin->m_checkMessages && dircomm->m_at_main_prompt && hasFocus() && !mainWin->getWaitState()){
103       dircomm->write(".messages");
104       displayToPrompt(conn);
105       messagesPending(false);
106    }
107 }
108
109 /*
110  * Connect to Director.  This does not connect to the director, dircomm does.
111  * This creates the first and possibly 2nd dircomm instance
112  */
113 void Console::connect_dir()
114 {
115    DirComm *dircomm = m_dircommHash.value(0);
116
117    if (!m_console->m_dir) {
118       mainWin->set_status( tr("No Director found."));
119       return;
120    }
121
122    m_textEdit = textEdit;   /* our console screen */
123
124    if (dircomm->connect_dir()) {
125       if (mainWin->m_connDebug) {
126          Pmsg1(000, "DirComm 0 Seems to have Connected %s\n", m_dir->name());
127       }
128       beginNewCommand(0);
129    }
130    mainWin->set_status(_("Connected"));
131
132    startTimer();                      /* start message timer */
133 }
134
135 /*
136  * A function created to separate out the population of the lists
137  * from the Console::connect_dir function
138  */
139 void Console::populateLists(bool /*forcenew*/)
140 {
141    int conn;
142    if (!getDirComm(conn)) {
143       if (mainWin->m_connDebug) Pmsg0(000, "call newDirComm\n");
144       if (!newDirComm(conn)) {
145          Emsg1(M_INFO, 0, "Failed to connect to %s for populateLists.\n", m_dir->name());
146          return;
147       }
148    }
149    populateLists(conn);
150    notify(conn, true);
151 }
152
153 void Console::populateLists(int conn)
154 {
155    job_list.clear();
156    restore_list.clear();
157    client_list.clear();
158    fileset_list.clear();
159    messages_list.clear();
160    pool_list.clear();
161    storage_list.clear();
162    type_list.clear();
163    level_list.clear();
164    volstatus_list.clear();
165    mediatype_list.clear();
166    dir_cmd(conn, ".jobs", job_list);
167    dir_cmd(conn, ".jobs type=R", restore_list);
168    dir_cmd(conn, ".clients", client_list);
169    dir_cmd(conn, ".filesets", fileset_list);
170    dir_cmd(conn, ".msgs", messages_list);
171    dir_cmd(conn, ".pools", pool_list);
172    dir_cmd(conn, ".storage", storage_list);
173    dir_cmd(conn, ".types", type_list);
174    dir_cmd(conn, ".levels", level_list);
175    dir_cmd(conn, ".volstatus", volstatus_list);
176    dir_cmd(conn, ".mediatypes", mediatype_list);
177    dir_cmd(conn, ".locations", location_list);
178
179    if (mainWin->m_connDebug) {
180       QString dbgmsg = QString("jobs=%1 clients=%2 filesets=%3 msgs=%4 pools=%5 storage=%6 types=%7 levels=%8 conn=%9 %10\n")
181         .arg(job_list.count()).arg(client_list.count()).arg(fileset_list.count()).arg(messages_list.count())
182         .arg(pool_list.count()).arg(storage_list.count()).arg(type_list.count()).arg(level_list.count())
183         .arg(conn).arg(m_dir->name());
184       Pmsg1(000, "%s", dbgmsg.toUtf8().data());
185    }
186    job_list.sort();
187    client_list.sort();
188    fileset_list.sort();
189    messages_list.sort();
190    pool_list.sort();
191    storage_list.sort();
192    type_list.sort();
193    level_list.sort();
194 }
195
196 /*
197  *  Overload function for dir_cmd with a QString
198  *  Ease of use
199  */
200 bool Console::dir_cmd(QString &cmd, QStringList &results)
201 {
202    return dir_cmd(cmd.toUtf8().data(), results);
203 }
204
205 /*
206  *  Overload function for dir_cmd, this is if connection is not worried about
207  */
208 bool Console::dir_cmd(const char *cmd, QStringList &results)
209 {
210    int conn;
211    if (getDirComm(conn)) {
212       dir_cmd(conn, cmd, results);
213       return true;
214    } else {
215       Pmsg1(000, "dir_cmd failed to connect to %s\n", m_dir->name());
216       return false;
217    }
218 }
219
220 /*
221  * Send a command to the Director, and return the
222  *  results in a QStringList.
223  */
224 bool Console::dir_cmd(int conn, const char *cmd, QStringList &results)
225 {
226    mainWin->waitEnter();
227    DirComm *dircomm = m_dircommHash.value(conn);
228    int stat;
229    bool prev_notify = is_notify_enabled(conn);
230
231    if (mainWin->m_connDebug) {
232       QString dbgmsg = QString("dir_cmd conn %1 %2 %3\n").arg(conn).arg(m_dir->name()).arg(cmd);
233       Pmsg1(000, "%s", dbgmsg.toUtf8().data());
234    }
235    if (prev_notify) {
236       notify(conn, false);
237    }
238    dircomm->write(cmd);
239    while ((stat = dircomm->read()) > 0 && dircomm->is_in_command()) {
240       if (mainWin->m_displayAll) display_text(dircomm->msg());
241       strip_trailing_junk(dircomm->msg());
242       results << dircomm->msg();
243    }
244    if (stat > 0 && mainWin->m_displayAll) display_text(dircomm->msg());
245    if (prev_notify) {
246       notify(conn, true);         /* turn it back on */
247    }
248    discardToPrompt(conn);
249    mainWin->waitExit();
250    return true;                  /* ***FIXME*** return any command error */
251 }
252
253 /*
254  * OverLoads for sql_cmd
255  */
256 bool Console::sql_cmd(int &conn, QString &query, QStringList &results)
257 {
258    return sql_cmd(conn, query.toUtf8().data(), results, false);
259 }
260
261 bool Console::sql_cmd(QString &query, QStringList &results)
262 {
263    int conn;
264    if (!getDirComm(conn)) {
265       return false;
266    }
267    return sql_cmd(conn, query.toUtf8().data(), results, true);
268 }
269
270 bool Console::sql_cmd(const char *query, QStringList &results)
271 {
272    int conn;
273    if (!getDirComm(conn)) {
274       return false;
275    }
276    return sql_cmd(conn, query, results, true);
277 }
278
279 /*
280  * Send an sql query to the Director, and return the
281  *  results in a QStringList.
282  */
283 bool Console::sql_cmd(int &conn, const char *query, QStringList &results, bool donotify)
284 {
285    DirComm *dircomm = m_dircommHash.value(conn);
286    int stat;
287    POOL_MEM cmd(PM_MESSAGE);
288    bool prev_notify = is_notify_enabled(conn);
289
290    if (!is_connectedGui()) {
291       return false;
292    }
293
294    if (mainWin->m_connDebug) Pmsg2(000, "sql_cmd conn %i %s\n", conn, query);
295    if (donotify) {
296       dircomm->notify(false);
297    }
298    mainWin->waitEnter();
299
300    pm_strcpy(cmd, ".sql query=\"");
301    pm_strcat(cmd, query);
302    pm_strcat(cmd, "\"");
303    dircomm->write(cmd.c_str());
304    while ((stat = dircomm->read()) > 0) {
305       bool first = true;
306       if (mainWin->m_displayAll) {
307          display_text(dircomm->msg());
308          display_text("\n");
309       }
310       strip_trailing_junk(dircomm->msg());
311       bool doappend = true;
312       if (first) {
313          QString dum = dircomm->msg();
314          if ((dum.left(6) == "*None*")) doappend = false;
315       }
316       if (doappend) {
317          results << dircomm->msg();
318       }
319       first = false;
320    }
321    if (donotify && prev_notify) {
322       dircomm->notify(true);
323    }
324    discardToPrompt(conn);
325    mainWin->waitExit();
326    if (donotify && prev_notify) {
327       dircomm->notify(true);
328    }
329    return !mainWin->isClosing();      /* return false if closing */
330 }
331
332 /*
333  * Overloads for
334  * Sending a command to the Director
335  */
336 int Console::write_dir(const char *msg)
337 {
338    int conn;
339    if (getDirComm(conn)) {
340       write_dir(conn, msg);
341    }
342    return conn;
343 }
344
345 int Console::write_dir(const char *msg, bool dowait)
346 {
347    int conn;
348    if (getDirComm(conn)) {
349       write_dir(conn, msg, dowait);
350    }
351    return conn;
352 }
353
354 void Console::write_dir(int conn, const char *msg)
355 {
356    write_dir(conn, msg, true);
357 }
358
359 /*
360  * Send a command to the Director
361  */
362 void Console::write_dir(int conn, const char *msg, bool dowait)
363 {
364    DirComm *dircomm = m_dircommHash.value(conn);
365
366    if (dircomm->m_sock) {
367       mainWin->set_status(_("Processing command ..."));
368       if (dowait)
369          mainWin->waitEnter();
370       dircomm->write(msg);
371       if (dowait)
372          mainWin->waitExit();
373    } else {
374       mainWin->set_status( tr(" Director not connected. Click on connect button."));
375       mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
376       QBrush redBrush(Qt::red);
377       QTreeWidgetItem *item = mainWin->getFromHash(this);
378       item->setForeground(0, redBrush);
379       dircomm->m_at_prompt = false;
380       dircomm->m_at_main_prompt = false;
381    }
382 }
383
384 /*
385  * get_job_defaults overload
386  */
387 bool Console::get_job_defaults(struct job_defaults &job_defs)
388 {
389    int conn;
390    getDirComm(conn);
391    return get_job_defaults(conn, job_defs, true);
392 }
393
394 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs)
395 {
396    return get_job_defaults(conn, job_defs, true);
397 }
398
399 /*
400  * Send a job name to the director, and read all the resulting
401  *  defaults.
402  */
403 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs, bool donotify)
404 {
405    QString scmd;
406    int stat;
407    char *def;
408    bool prev_notify = is_notify_enabled(conn);
409    bool rtn = false;
410    DirComm *dircomm = m_dircommHash.value(conn);
411
412    if (donotify) {
413       dircomm->notify(false);
414    }
415    beginNewCommand(conn);
416    bool prevWaitState = mainWin->getWaitState();
417    if (!prevWaitState)
418       mainWin->waitEnter();
419    if (mainWin->m_connDebug)
420       Pmsg2(000, "job_defaults conn %i %s\n", conn, m_dir->name());
421    scmd = QString(".defaults job=\"%1\"").arg(job_defs.job_name);
422    dircomm->write(scmd);
423    while ((stat = dircomm->read()) > 0) {
424       if (mainWin->m_displayAll) display_text(dircomm->msg());
425       def = strchr(dircomm->msg(), '=');
426       if (!def) {
427          continue;
428       }
429       /* Pointer to default value */
430       *def++ = 0;
431       strip_trailing_junk(def);
432
433       if (strcmp(dircomm->msg(), "job") == 0) {
434          if (strcmp(def, job_defs.job_name.toUtf8().data()) != 0) {
435             goto bail_out;
436          }
437          continue;
438       }
439       if (strcmp(dircomm->msg(), "pool") == 0) {
440          job_defs.pool_name = def;
441          continue;
442       }
443       if (strcmp(dircomm->msg(), "messages") == 0) {
444          job_defs.messages_name = def;
445          continue;
446       }
447       if (strcmp(dircomm->msg(), "client") == 0) {
448          job_defs.client_name = def;
449          continue;
450       }
451       if (strcmp(dircomm->msg(), "storage") == 0) {
452          job_defs.store_name = def;
453          continue;
454       }
455       if (strcmp(dircomm->msg(), "where") == 0) {
456          job_defs.where = def;
457          continue;
458       }
459       if (strcmp(dircomm->msg(), "level") == 0) {
460          job_defs.level = def;
461          continue;
462       }
463       if (strcmp(dircomm->msg(), "type") == 0) {
464          job_defs.type = def;
465          continue;
466       }
467       if (strcmp(dircomm->msg(), "fileset") == 0) {
468          job_defs.fileset_name = def;
469          continue;
470       }
471       if (strcmp(dircomm->msg(), "catalog") == 0) {
472          job_defs.catalog_name = def;
473          continue;
474       }
475       if (strcmp(dircomm->msg(), "enabled") == 0) {
476          job_defs.enabled = *def == '1' ? true : false;
477          continue;
478       }
479    }
480    rtn = true;
481    /* Fall through wanted */
482 bail_out:
483    if (donotify && prev_notify) {
484       notify(conn, true);
485    }
486    if (!prevWaitState) {
487       mainWin->waitExit();
488    }
489    return rtn;
490 }
491
492
493 /*
494  * Save user settings associated with this console
495  */
496 void Console::writeSettings()
497 {
498    QFont font = get_font();
499
500    QSettings settings(m_dir->name(), "bat");
501    settings.beginGroup("Console");
502    settings.setValue("consoleFont", font.family());
503    settings.setValue("consolePointSize", font.pointSize());
504    settings.setValue("consoleFixedPitch", font.fixedPitch());
505    settings.endGroup();
506 }
507
508 /*
509  * Read and restore user settings associated with this console
510  */
511 void Console::readSettings()
512 {
513    QFont font = get_font();
514
515    QSettings settings(m_dir->name(), "bat");
516    settings.beginGroup("Console");
517    font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
518    font.setPointSize(settings.value("consolePointSize", 10).toInt());
519    font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
520    settings.endGroup();
521    m_textEdit->setFont(font);
522 }
523
524 /*
525  * Set the console textEdit font
526  */
527 void Console::set_font()
528 {
529    bool ok;
530    QFont font = QFontDialog::getFont(&ok, QFont(m_textEdit->font()), this);
531    if (ok) {
532       m_textEdit->setFont(font);
533    }
534 }
535
536 /*
537  * Get the console text edit font
538  */
539 const QFont Console::get_font()
540 {
541    return m_textEdit->font();
542 }
543
544 /*
545  * Slot for responding to status dir button on button bar
546  */
547 void Console::status_dir()
548 {
549    QString cmd("status dir");
550    consoleCommand(cmd);
551 }
552
553 /*
554  * Slot for responding to messages button on button bar
555  * Here we want to bring the console to the front so use pages' consoleCommand
556  */
557 void Console::messages()
558 {
559    QString cmd(".messages");
560    consoleCommand(cmd);
561    messagesPending(false);
562 }
563
564 /*
565  * Put text into the console window
566  */
567 void Console::display_textf(const char *fmt, ...)
568 {
569    va_list arg_ptr;
570    char buf[1000];
571    va_start(arg_ptr, fmt);
572    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
573    va_end(arg_ptr);
574    display_text(buf);
575 }
576
577 void Console::display_text(const QString buf)
578 {
579    if (buf.size() != 0) {
580       m_cursor->insertText(buf);
581       update_cursor();
582    }
583 }
584
585
586 void Console::display_text(const char *buf)
587 {
588    if (*buf != 0) {
589       m_cursor->insertText(buf);
590       update_cursor();
591    }
592 }
593
594 void Console::display_html(const QString buf)
595 {
596    if (buf.size() != 0) {
597       m_cursor->insertHtml(buf);
598       update_cursor();
599    }
600 }
601
602 /* Position cursor to end of screen */
603 void Console::update_cursor()
604 {
605    m_textEdit->moveCursor(QTextCursor::End);
606    m_textEdit->ensureCursorVisible();
607 }
608
609 void Console::beginNewCommand(int conn)
610 {
611    DirComm *dircomm = m_dircommHash.value(conn);
612
613    for (int i=0; i < 3; i++) {
614       dircomm->write(".");
615       while (dircomm->read() > 0) {
616          if (mainWin->m_commDebug) Pmsg2(000, "begin new command loop %i %s\n", i, m_dir->name());
617          if (mainWin->m_displayAll) display_text(dircomm->msg());
618       }
619       if (dircomm->m_at_main_prompt) {
620          break;
621       }
622    }
623    display_text("\n");
624 }
625
626 void Console::displayToPrompt(int conn)
627 {
628    DirComm *dircomm = m_dircommHash.value(conn);
629
630    int stat = 0;
631    QString buf;
632    if (mainWin->m_commDebug) Pmsg1(000, "DisplaytoPrompt %s\n", m_dir->name());
633    while (!dircomm->m_at_prompt) {
634       if ((stat=dircomm->read()) > 0) {
635          buf += dircomm->msg();
636          if (buf.size() >= 8196 || m_messages_pending) {
637             display_text(buf);
638             buf.clear();
639             messagesPending(false);
640          }
641       }
642    }
643    display_text(buf);
644    if (mainWin->m_commDebug) Pmsg2(000, "endDisplaytoPrompt=%d %s\n", stat, m_dir->name());
645 }
646
647 void Console::discardToPrompt(int conn)
648 {
649    DirComm *dircomm = m_dircommHash.value(conn);
650
651    int stat = 0;
652    if (mainWin->m_commDebug) Pmsg1(000, "discardToPrompt %s\n", m_dir->name());
653    if (mainWin->m_displayAll) {
654       displayToPrompt(conn);
655    } else {
656       while (!dircomm->m_at_prompt) {
657          stat = dircomm->read();
658          if (stat < 0) {
659             break;
660          }
661       }
662    }
663    if (mainWin->m_commDebug) {
664       Pmsg2(000, "endDiscardToPrompt conn=%i %s\n", conn, m_dir->name());
665    }
666 }
667
668 QString Console::returnFromPrompt(int conn)
669 {
670    DirComm *dircomm = m_dircommHash.value(conn);
671    QString text("");
672
673    int stat = 0;
674    text = "";
675    dircomm->read();
676    text += dircomm->msg();
677    if (mainWin->m_commDebug) Pmsg1(000, "returnFromPrompt %s\n", m_dir->name());
678    while (!dircomm->m_at_prompt) {
679       if ((stat=dircomm->read()) > 0) {
680          text += dircomm->msg();
681       }
682    }
683    if (mainWin->m_commDebug) Pmsg2(000, "endreturnFromPrompt=%d %s\n", stat, m_dir->name());
684    return text;
685 }
686
687 /*
688  * When the notifier is enabled, read_dir() will automatically be
689  * called by the Qt event loop when ever there is any output
690  * from the Director, and read_dir() will then display it on
691  * the console.
692  *
693  * When we are in a bat dialog, we want to control *all* output
694  * from the Director, so we set notify to off.
695  *    m_console->notifiy(false);
696  */
697
698 /* dual purpose function to turn notify off and return a connection */
699 int Console::notifyOff()
700 {
701    int conn = 0;
702    if (getDirComm(conn)) {
703       notify(conn, false);
704    }
705    return conn;
706 }
707
708 /* knowing a connection, turn notify off or on */
709 bool Console::notify(int conn, bool enable)
710 {
711    DirComm *dircomm = m_dircommHash.value(conn);
712    if (dircomm) {
713       return dircomm->notify(enable);
714    } else {
715       return false;
716    }
717 }
718
719 /* knowing a connection, return notify state */
720 bool Console::is_notify_enabled(int conn) const
721 {
722    DirComm *dircomm = m_dircommHash.value(conn);
723    if (dircomm) {
724       return dircomm->is_notify_enabled();
725    } else {
726       return false;
727    }
728 }
729
730 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
731 {
732    m_directorTreeItem = item;
733 }
734
735 void Console::setDirRes(DIRRES *dir)
736 {
737    m_dir = dir;
738 }
739
740 /*
741  * To have the ability to get the name of the director resource.
742  */
743 void Console::getDirResName(QString &name_returned)
744 {
745    name_returned = m_dir->name();
746 }
747
748 /* Slot for responding to page selectors status help command */
749 void Console::consoleHelp()
750 {
751    QString cmd("help");
752    consoleCommand(cmd);
753 }
754
755 /* Slot for responding to page selectors reload bacula-dir.conf */
756 void Console::consoleReload()
757 {
758    QString cmd("reload");
759    consoleCommand(cmd);
760 }
761
762 /* For suppressing .messages
763  * This may be rendered not needed if the multiple connections feature gets working */
764 bool Console::hasFocus()
765 {
766    if (mainWin->tabWidget->currentIndex() == mainWin->tabWidget->indexOf(this))
767       return true;
768    else
769       return false;
770 }
771
772 /* For adding feature to have the gui's messages button change when
773  * messages are pending */
774 bool Console::messagesPending(bool pend)
775 {
776    bool prev = m_messages_pending;
777    m_messages_pending = pend;
778    mainWin->setMessageIcon();
779    return prev;
780 }
781
782 /* terminate all existing connections */
783 void Console::terminate()
784 {
785    foreach(DirComm* dircomm,  m_dircommHash) {
786       dircomm->terminate();
787    }
788    m_console->stopTimer();
789 }
790
791 /* Maybe this should be checking the list, for the moment lets check 0 which should be connected */
792 bool Console::is_connectedGui()
793 {
794    if (is_connected(0)) {
795       return true;
796    } else {
797       QString message = tr("Director is currently disconnected\nPlease reconnect!");
798       QMessageBox::warning(this, "Bat", message, QMessageBox::Ok );
799       return false;
800    }
801 }
802
803 int Console::read(int conn)
804 {
805    DirComm *dircomm = m_dircommHash.value(conn);
806    return dircomm->read();
807 }
808
809 char *Console::msg(int conn)
810 {
811    DirComm *dircomm = m_dircommHash.value(conn);
812    return dircomm->msg();
813 }
814
815 int Console::write(int conn, const QString msg)
816 {
817    DirComm *dircomm = m_dircommHash.value(conn);
818    mainWin->waitEnter();
819    int ret = dircomm->write(msg);
820    mainWin->waitExit();
821    return ret;
822 }
823
824 int Console::write(int conn, const char *msg)
825 {
826    DirComm *dircomm = m_dircommHash.value(conn);
827    mainWin->waitEnter();
828    int ret = dircomm->write(msg);
829    mainWin->waitExit();
830    return ret;
831 }
832
833 /* This checks to see if any is connected */
834 bool Console::is_connected()
835 {
836    bool connected = false;
837    foreach(DirComm* dircomm,  m_dircommHash) {
838       if (dircomm->is_connected())
839          return true;
840    }
841    return connected;
842 }
843
844 /* knowing the connection id, is it connected */
845 bool Console::is_connected(int conn)
846 {
847    DirComm *dircomm = m_dircommHash.value(conn);
848    return dircomm->is_connected();
849 }
850
851 /*
852  * Need a connection.  Check existing connections or create one
853  */
854 bool Console::getDirComm(int &conn)
855 {
856    if (findDirComm(conn)) {
857       return true;
858    }
859    if (mainWin->m_connDebug) Pmsg0(000, "call newDirComm\n");
860    return newDirComm(conn);
861 }
862
863
864 /*
865  * Try to find a free (unused but established) connection
866  * KES: Note, I think there is a problem here because for
867  *   some reason, the notifier is often turned off on file
868  *   descriptors that seem to me to be available.  That means
869  *   that we do not use a free descriptor and thus we will create
870  *   a new connection that is maybe not necessary.  Someone needs
871  *   to look into whether or not notify() is correctly turned on
872  *   when we are back at the command prompt and idle.
873  *
874  */
875 bool Console::findDirComm(int &conn)
876 {
877    QHash<int, DirComm*>::const_iterator iter = m_dircommHash.constBegin();
878    while (iter != m_dircommHash.constEnd()) {
879       DirComm *dircomm = iter.value();
880       if (dircomm->m_at_prompt && dircomm->m_at_main_prompt && dircomm->is_notify_enabled()) {
881          conn = dircomm->m_conn;
882          return true;
883       }
884       if (mainWin->m_connDebug) {
885          Pmsg4(000, "currentDirComm=%d at_prompt=%d at_main=%d && notify=%d\n",
886             dircomm->m_conn, dircomm->m_at_prompt, dircomm->m_at_main_prompt, dircomm->is_notify_enabled());
887       }
888       ++iter;
889    }
890    return false;
891 }
892
893 /*
894  *  Create a new connection
895  */
896 bool Console::newDirComm(int &conn)
897 {
898    m_dircommCounter++;
899    if (mainWin->m_connDebug) {
900       Pmsg2(000, "newDirComm=%i to: %s\n", m_dircommCounter, m_dir->name());
901    }
902    DirComm *dircomm = new DirComm(this, m_dircommCounter);
903    m_dircommHash.insert(m_dircommCounter, dircomm);
904    bool success = dircomm->connect_dir();
905    if (mainWin->m_connDebug) {
906       if (success) {
907          Pmsg2(000, "newDirComm=%i Connected %s\n", m_dircommCounter, m_dir->name());
908       } else {
909          Emsg2(M_ERROR, 0, "DirComm=%i. Unable to connect to %s\n",
910                m_dircommCounter, m_dir->name());
911       }
912    }
913    if (!success) {
914       m_dircommHash.remove(m_dircommCounter);
915       delete dircomm;
916       m_dircommCounter--;
917    }
918    conn = m_dircommCounter;
919    return success;
920 }