1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 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 #include "tray-monitor.h"
21 #include <QInputDialog>
22 #include <QDir>
23 
24 /* Static variables */
25 char *configfile = NULL;
26 static MONITOR *monitor = NULL;
27 static CONFIG *config = NULL;
28 static TrayUI *mainwidget = NULL;
29 static TSched *scheduler = NULL;
30 
31 #define CONFIG_FILE "./bacula-tray-monitor.conf"     /* default configuration file */
32 
33 #ifdef HAVE_WIN32
34 #define HOME_VAR "APPDATA"
35 #define CONFIG_FILE_HOME "bacula-tray-monitor.conf" /* In $HOME */
36 #else
37 #define HOME_VAR "HOME"
38 #define CONFIG_FILE_HOME ".bacula-tray-monitor.conf" /* In $HOME */
39 #endif
40 
usage()41 static void usage()
42 {
43    fprintf(stderr, _(
44 PROG_COPYRIGHT
45 "\n%sVersion: %s (%s) %s %s %s\n\n"
46 "Usage: tray-monitor [-c config_file] [-d debug_level]\n"
47 "       -c <file>     set configuration file to file\n"
48 "       -d <nn>       set debug level to <nn>\n"
49 "       -dt           print timestamp in debug output\n"
50 "       -t            test - read configuration and exit\n"
51 "       -W 0/1        force the detection of the systray\n"
52 "       -?            print this message.\n"
53 "\n"), 2004, BDEMO, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
54 }
55 
refresh_tray(TrayUI * t)56 void refresh_tray(TrayUI *t)
57 {
58    RESMON *r;
59    MONITOR *mon;
60 
61    if (!t) {
62       return;
63    }
64 
65    t->clearTabs();
66    if (!config) {
67       return;
68    }
69 
70    mon = (MONITOR *) GetNextRes(R_MONITOR, NULL);
71    t->spinRefresh->setValue(mon?mon->RefreshInterval:60);
72 
73    foreach_res(r, R_CLIENT) {
74       t->addTab(r);
75    }
76    foreach_res(r, R_DIRECTOR) {
77       t->addTab(r);
78    }
79    foreach_res(r, R_STORAGE) {
80       t->addTab(r);
81    }
82 }
83 
display_error(const char * fmt,...)84 void display_error(const char *fmt, ...)
85 {
86    va_list  arg_ptr;
87    POOL_MEM tmp(PM_MESSAGE);
88    QMessageBox msgBox;
89    int maxlen;
90 
91    if (!fmt || !*fmt) {
92       return;
93    }
94 
95    maxlen = tmp.size() - 1;
96    va_start(arg_ptr, fmt);
97    bvsnprintf(tmp.c_str(), maxlen, fmt, arg_ptr);
98    va_end(arg_ptr);
99 
100    msgBox.setIcon(QMessageBox::Critical);
101    msgBox.setText(tmp.c_str());
102    msgBox.exec();
103 }
104 
error_handler(const char * file,int line,LEX *,const char * msg,...)105 void error_handler(const char *file, int line, LEX */* lc */, const char *msg, ...)
106 {
107    POOL_MEM tmp;
108    va_list arg_ptr;
109    va_start(arg_ptr, msg);
110    vsnprintf(tmp.c_str(), tmp.size(), msg, arg_ptr);
111    va_end(arg_ptr);
112    display_error("Error %s:%d %s\n", file, line, tmp.c_str());
113 }
114 
tls_pem_callback(char * buf,int size,const void *)115 int tls_pem_callback(char *buf, int size, const void * /*userdata*/)
116 {
117    bool ok;
118    QString text = QInputDialog::getText(mainwidget, _("TLS PassPhrase"),
119                                         buf, QLineEdit::Normal,
120                                         QDir::home().dirName(), &ok);
121    if (ok) {
122       bstrncpy(buf, text.toUtf8().data(), size);
123       return 1;
124    } else {
125       return 0;
126    }
127 }
128 
reload()129 bool reload()
130 {
131    bool displaycfg=false;
132    int  nitems = 0;
133    struct stat sp;
134 
135    Dmsg0(50, "reload the configuration!\n");
136    scheduler->stop();
137    if (config) {
138       delete config;
139    }
140    config = NULL;
141    monitor = NULL;
142 
143    if (stat(configfile, &sp) != 0) {
144       berrno be;
145       Dmsg2(50, "Unable to find %s. ERR=%s\n", configfile, be.bstrerror());
146       displaycfg = true;
147       goto bail_out;
148    }
149 
150    config = New(CONFIG());
151    if (!parse_tmon_config(config, configfile, M_ERROR)) {
152       Dmsg1(50, "Error while parsing %s\n", configfile);
153       // TODO: Display a warning message an open the configuration
154       //       window
155       displaycfg = true;
156    }
157 
158    LockRes();
159    foreach_res(monitor, R_MONITOR) {
160       nitems++;
161    }
162    if (!displaycfg && nitems != 1) {
163       Mmsg(config->m_errmsg,
164            _("Error: %d Monitor resources defined in %s. "
165              "You must define one Monitor resource.\n"),
166            nitems, configfile);
167       displaycfg = true;
168    }
169    monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
170    UnlockRes();
171    if (displaycfg) {
172       display_error(config->m_errmsg);
173    }
174    refresh_tray(mainwidget);
175    if (monitor && monitor->command_dir) {
176       scheduler->init(monitor->command_dir);
177       scheduler->start();
178    } else {
179       Dmsg0(50, "Do not start the scheduler\n");
180    }
181 bail_out:
182    return displaycfg;
183 }
184 
185 /*********************************************************************
186  *
187  *         Main Bacula Tray Monitor -- User Interface Program
188  *
189  */
main(int argc,char * argv[])190 int main(int argc, char *argv[])
191 {
192    QApplication    app(argc, argv);
193    int ch;
194    bool test_config = false, display_cfg = false;
195    TrayUI tray;
196    TSched sched;
197 
198    setlocale(LC_ALL, "");
199    bindtextdomain("bacula", LOCALEDIR);
200    textdomain("bacula");
201 
202    init_stack_dump();
203    my_name_is(argc, argv, "tray-monitor");
204    lmgr_init_thread();
205    init_msg(NULL, NULL, NULL);
206 #ifdef HAVE_WIN32
207    working_directory = getenv("TMP");
208 #endif
209    if (working_directory == NULL) {
210       working_directory = "/tmp";
211    }
212    start_watchdog();
213 
214 #ifndef HAVE_WIN32
215    struct sigaction sigignore;
216    sigignore.sa_flags = 0;
217    sigignore.sa_handler = SIG_IGN;
218    sigfillset(&sigignore.sa_mask);
219    sigaction(SIGPIPE, &sigignore, NULL);
220 #endif
221 
222    while ((ch = getopt(argc, argv, "c:d:th?TW:")) != -1) {
223       switch (ch) {
224       case 'c':                    /* configuration file */
225          if (configfile != NULL) {
226             free(configfile);
227          }
228          configfile = bstrdup(optarg);
229          break;
230 
231       case 'W':
232          tray.have_systray = (atoi(optarg) != 0);
233          break;
234 
235       case 'T':
236          set_trace(true);
237          break;
238 
239       case 'd':
240          if (*optarg == 't') {
241             dbg_timestamp = true;
242          } else {
243             debug_level = atoi(optarg);
244             if (debug_level <= 0) {
245                debug_level = 1;
246             }
247          }
248          break;
249 
250       case 't':
251          test_config = true;
252          break;
253 
254       case 'h':
255       case '?':
256       default:
257          usage();
258          exit(1);
259       }
260    }
261    argc -= optind;
262    //argv += optind;
263 
264    if (argc) {
265       usage();
266       exit(1);
267    }
268 
269    /* Keep generated files for ourself */
270    umask(0077);
271 
272    if (configfile == NULL) {
273       if (getenv(HOME_VAR) != NULL) {
274          int len = strlen(getenv(HOME_VAR)) + strlen(CONFIG_FILE_HOME) + 5;
275          configfile = (char *) malloc(len);
276          bsnprintf(configfile, len, "%s/%s", getenv(HOME_VAR), CONFIG_FILE_HOME);
277 
278       } else {
279          configfile = bstrdup(CONFIG_FILE);
280       }
281    }
282    Dmsg1(50, "configfile=%s\n", configfile);
283 
284    // We need to initialize the scheduler before the reload() command
285    scheduler = &sched;
286 
287    OSDependentInit();               /* Initialize Windows path handling */
288    (void)WSA_Init();                /* Initialize Windows sockets */
289 
290    display_cfg = reload();
291 
292    if (test_config) {
293       exit(0);
294    }
295    /* If we have a systray, we always keep the application*/
296    if (tray.have_systray) {
297       app.setQuitOnLastWindowClosed(false);
298 
299    } else {  /* Without a systray, we quit when we close */
300       app.setQuitOnLastWindowClosed(true);
301    }
302    tray.setupUi(&tray, monitor);
303    refresh_tray(&tray);
304    mainwidget = &tray;
305    if (display_cfg) {
306       new Conf();
307    }
308    app.exec();
309    sched.stop();
310    stop_watchdog();
311    (void)WSACleanup();               /* Cleanup Windows sockets */
312 
313    if (config) {
314       delete config;
315    }
316    config = NULL;
317    bfree_and_null(configfile);
318    term_msg();
319    return 0;
320 }
321