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