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