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