1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2004-2011 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2012 Planets Communications B.V.
6    Copyright (C) 2013-2020 Bareos GmbH & Co. KG
7 
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    Affero 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 
24 #include <QDebug>
25 #include <QList>
26 #include <QPointer>
27 #include <QApplication>
28 #include <QMessageBox>
29 
30 #include "mainwindow.h"
31 #include "tray-monitor.h"
32 #include "authenticate.h"
33 #include "monitoritem.h"
34 #include "monitoritemthread.h"
35 #include "lib/bsignal.h"
36 #include "lib/parse_conf.h"
37 
38 ConfigurationParser* my_config = nullptr;
39 
40 /* QCoreApplication* app will be initialized with:
41  * - QApplication for normal execution with a GUI or
42  * - QCoreApplication for tests when no GUI must be used */
43 static QCoreApplication* app = nullptr;
44 
usage()45 static void usage()
46 {
47   std::vector<char> copyright(1024);
48   kBareosVersionStrings.FormatCopyrightWithFsfAndPlanets(
49       copyright.data(), copyright.size(), 2004);
50   QString out =
51       QString(_("%1"
52                 "Usage: tray-monitor [options]\n"
53                 "        -c <path>   use <path> as configuration file or "
54                 "directory\n"
55                 "        -d <nn>     set debug level to <nn>\n"
56                 "        -dt         print timestamp in debug output\n"
57                 "        -t          test - read configuration and exit\n"
58                 "        -rc         test - do connection test\n"
59                 "        -xc         print configuration and exit\n"
60                 "        -xs         print configuration file schema in JSON "
61                 "format "
62                 "and exit\n"
63                 "        -?          print this message.\n"
64                 "\n"))
65           .arg(copyright.data());
66 
67 #if HAVE_WIN32
68   QMessageBox::information(0, "Help", out);
69 #else
70   fprintf(stderr, "%s", out.toUtf8().data());
71 #endif
72 }
73 
ParseCommandLine(int argc,char * argv[],cl_opts & cl)74 static void ParseCommandLine(int argc, char* argv[], cl_opts& cl)
75 {
76   int ch;
77   while ((ch = getopt(argc, argv, "bc:d:th?f:r:s:x:")) != -1) {
78     switch (ch) {
79       case 'c': /* configuration file */
80         if (cl.configfile_) { free(static_cast<void*>(cl.configfile_)); }
81         cl.configfile_ = strdup(optarg);
82         break;
83 
84       case 'd':
85         if (*optarg == 't') {
86           dbg_timestamp = true;
87         } else {
88           debug_level = atoi(optarg);
89           if (debug_level <= 0) { debug_level = 1; }
90         }
91         break;
92 
93       case 't':
94         cl.test_config_only_ = true;
95         break;
96 
97       case 'x': /* export configuration/schema and exit */
98         if (*optarg == 's') {
99           cl.export_config_schema_ = true;
100         } else if (*optarg == 'c') {
101           cl.export_config_ = true;
102         } else {
103           usage();
104           exit(1);
105         }
106         break;
107 
108       case 'r':
109         if ((*optarg) == 'c') {
110           cl.do_connection_test_only_ = true;
111         } else {
112           usage();
113           exit(1);
114         }
115         break;
116 
117       case 'h':
118       case '?':
119       default:
120         usage();
121         exit(1);
122     }
123   }
124   argc -= optind;
125   // argv += optind;
126 
127   if (argc) {
128     usage();
129     exit(1);
130   }
131 }
132 
setupQtObjects()133 static void setupQtObjects()
134 {
135   MonitorItemThread* thr = MonitorItemThread::instance();
136   MainWindow* win = MainWindow::instance();
137 
138   QObject::connect(win, SIGNAL(refreshItems()), thr, SLOT(onRefreshItems()),
139                    Qt::QueuedConnection);
140 
141   // move the thread exec handler
142   // into its own context
143   thr->moveToThread(thr);
144 }
145 
cleanup()146 static void cleanup()
147 {
148   static bool terminated = false;
149 
150   if (terminated) {
151     // don't call it twice
152     return;
153   }
154 
155   terminated = true;
156 
157   MonitorItemThread::destruct();  // disconnects network
158   MainWindow::destruct();         // destroys the tray-icon
159 
160   if (app) {
161     delete app;
162     app = nullptr;
163   }
164 
165   if (my_config) {
166     delete my_config;
167     my_config = nullptr;
168   }
169 
170   WSACleanup(); /* Cleanup Windows sockets */
171 }
172 
intHandler(int)173 void intHandler(int) { exit(0); }
174 
InitEnvironment(int argc,char * argv[])175 static void InitEnvironment(int argc, char* argv[])
176 {
177   setlocale(LC_ALL, "");
178   tzset();
179   bindtextdomain("bareos", LOCALEDIR);
180   textdomain("bareos");
181 
182   InitStackDump();
183   MyNameIs(argc, argv, "tray-monitor");
184   InitMsg(NULL, NULL);
185   signal(SIGINT, intHandler);
186   working_directory = "/tmp";
187   OSDependentInit();
188   WSA_Init(); /* Initialize Windows sockets */
189 }
190 
191 /*********************************************************************
192  *
193  *         Main Bareos Tray Monitor -- User Interface Program
194  *
195  */
main(int argc,char * argv[])196 int main(int argc, char* argv[])
197 {
198   InitEnvironment(argc, argv);
199 
200   cl_opts cl;  // remember some command line options
201   ParseCommandLine(argc, argv, cl);
202 
203   if (cl.export_config_schema_) {
204     PoolMem buffer;
205 
206     my_config = InitTmonConfig(cl.configfile_, M_ERROR_TERM);
207     PrintConfigSchemaJson(buffer);
208     printf("%s\n", buffer.c_str());
209     fflush(stdout);
210     exit(0);
211   }
212 
213   // read the config file
214   my_config = InitTmonConfig(cl.configfile_, M_ERROR_TERM);
215   my_config->ParseConfig();
216 
217   if (cl.export_config_) {
218     my_config->DumpResources(PrintMessage, NULL);
219     exit(0);
220   }
221 
222   if (InitCrypto() != 0) {
223     Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
224   }
225 
226   if (cl.do_connection_test_only_) {
227     /* do not initialize a GUI */
228     app = new QCoreApplication(argc, argv);
229   } else {
230     QApplication* p = new QApplication(argc, argv);
231     p->setQuitOnLastWindowClosed(false);
232     app = p;
233   }
234 
235   if (!cl.do_connection_test_only_) { setupQtObjects(); }
236 
237   QStringList tabRefs = MonitorItemThread::instance()->createRes(cl);
238 
239   int ret = 0;
240   if (cl.do_connection_test_only_) {
241     ret = MonitorItemThread::instance()->doConnectionTest() ? 0 : 1;
242   } else {
243     MainWindow::instance()->addTabs(tabRefs);
244 
245     if (cl.test_config_only_) { exit(0); }
246 
247     MonitorItemThread::instance()->start();
248 
249     ret = app->exec();
250   }
251 
252   cleanup();
253 
254   return ret;
255 }
256 /* This is a replacement for the std::exit() handler */
exit(int status)257 void exit(int status)
258 {
259   /* avoid to call the std::exit() cleanup handlers
260    * via atexit() since exit() destroys objects that
261    * are used by the QApplication class and this would
262    * lead to a sementation fault when QApplication
263    * in turn wants to destroy its child-objects. */
264 
265   // first do the Qt cleanup
266   cleanup();
267 
268   // do the kernel cleanup
269   _exit(status);
270 }
271