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