]> git.stg.codes - stg.git/blob - projects/stargazer/main.cpp
Stargazer (#6)
[stg.git] / projects / stargazer / main.cpp
1 /*
2  *    This program is free software; you can redistribute it and/or modify
3  *    it under the terms of the GNU General Public License as published by
4  *    the Free Software Foundation; either version 2 of the License, or
5  *    (at your option) any later version.
6  *
7  *    This program is distributed in the hope that it will be useful,
8  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *    GNU General Public License for more details.
11  *
12  *    You should have received a copy of the GNU General Public License
13  *    along with this program; if not, write to the Free Software
14  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
15  */
16
17 /*
18  *    Author : Boris Mikhailenko <stg34@stargazer.dp.ua>
19  */
20
21 #include "store_loader.h"
22 #include "plugin_mgr.h"
23 #include "plugin_runner.h"
24 #include "users_impl.h"
25 #include "admins_impl.h"
26 #include "tariffs_impl.h"
27 #include "services_impl.h"
28 #include "corps_impl.h"
29 #include "traffcounter_impl.h"
30 #include "settings_impl.h"
31 #include "pidfile.h"
32 #include "async_pool.h"
33 #include "stg_timer.h"
34
35 #include "stg/user.h"
36 #include "stg/common.h"
37 #include "stg/plugin.h"
38 #include "stg/logger.h"
39 #include "stg/scriptexecuter.h"
40 #include "stg/version.h"
41
42 #include <fstream>
43 #include <vector>
44 #include <string>
45 #include <set>
46 #include <csignal>
47 #include <cerrno>
48 #include <cstdio>
49 #include <cstdlib> // srandom, exit
50
51 #include <unistd.h>
52 #include <sys/ipc.h>
53 #include <sys/msg.h>
54 #include <sys/types.h>
55 #include <sys/wait.h>
56 #include <sys/stat.h> // S_IRUSR
57 #include <fcntl.h> // create
58
59 #define START_FILE "/._ST_ART_ED_"
60
61 using STG::SettingsImpl;
62 using STG::AdminsImpl;
63 using STG::TraffCounterImpl;
64 using STG::UsersImpl;
65 using STG::TariffsImpl;
66 using STG::ServicesImpl;
67 using STG::CorporationsImpl;
68 using STG::StoreLoader;
69
70 namespace
71 {
72 std::set<pid_t> executers;
73
74 void StartTimer();
75 int StartScriptExecuter(char* procName, int msgKey, int* msgID);
76 int ForkAndWait(const std::string& confDir);
77 void KillExecuters();
78
79 //-----------------------------------------------------------------------------
80 void StartTimer()
81 {
82     auto& WriteServLog = STG::Logger::get();
83
84     if (RunStgTimer())
85     {
86         WriteServLog("Cannot start timer. Fatal.");
87         exit(1);
88     }
89     else
90         WriteServLog("Timer thread started successfully.");
91 }
92 //-----------------------------------------------------------------------------
93 #if defined(LINUX) || defined(DARWIN)
94 int StartScriptExecuter(char* procName, int msgKey, int* msgID)
95 #else
96 int StartScriptExecuter(char*, int msgKey, int* msgID)
97 #endif
98 {
99     auto& WriteServLog = STG::Logger::get();
100
101     if (*msgID == -11)   // If msgID == -11 - first call. Create queue
102     {
103         for (int i = 0; i < 2; i++)
104         {
105             *msgID = msgget(msgKey, IPC_CREAT | IPC_EXCL | 0600);
106
107             if (*msgID == -1)
108             {
109                 *msgID = msgget(msgKey, 0);
110                 if (*msgID == -1)
111                 {
112                     WriteServLog("Message queue not created.");
113                     return -1;
114                 }
115                 else
116                     msgctl(*msgID, IPC_RMID, NULL);
117             }
118             else
119             {
120                 WriteServLog("Message queue created successfully. msgKey=%d msgID=%d", msgKey, *msgID);
121                 break;
122             }
123         }
124     }
125
126     const auto pid = fork();
127
128     switch (pid)
129     {
130         case -1:
131             WriteServLog("Fork error!");
132             return -1;
133
134         case 0:
135 #if defined(LINUX) || defined(DARWIN)
136             Executer(*msgID, pid, procName);
137 #else
138             Executer(*msgID, pid);
139 #endif
140             return 1;
141
142         default:
143             if (executers.empty()) {
144 #if defined(LINUX) || defined(DARWIN)
145                 Executer(*msgID, pid, NULL);
146 #else
147                 Executer(*msgID, pid);
148 #endif
149             }
150             executers.insert(pid);
151     }
152     return 0;
153 }
154 //-----------------------------------------------------------------------------
155 int ForkAndWait(const std::string& confDir)
156 {
157     const auto pid = fork();
158     const auto startFile = confDir + START_FILE;
159     unlink(startFile.c_str());
160
161     switch (pid)
162     {
163         case -1:
164             return -1;
165             break;
166
167         case 0:
168             close(1);
169             close(2);
170             setsid();
171             break;
172
173         default:
174             struct timespec ts = {0, 200000000};
175             for (int i = 0; i < 120 * 5; i++)
176             {
177                 if (access(startFile.c_str(), F_OK) == 0)
178                 {
179                     unlink(startFile.c_str());
180                     exit(0);
181                 }
182
183                 nanosleep(&ts, NULL);
184             }
185             unlink(startFile.c_str());
186             exit(1);
187             break;
188     }
189     return 0;
190 }
191
192 //-----------------------------------------------------------------------------
193 void KillExecuters()
194 {
195     auto pid = executers.begin();
196     while (pid != executers.end())
197     {
198         printfd(__FILE__, "KillExecuters pid=%d\n", *pid);
199         kill(*pid, SIGUSR1);
200         ++pid;
201     }
202 }
203
204 void PrintHelp(const std::string& programName)
205 {
206     std::cout << "Usage: " << programName << "[-h/--help] [-v/--version] [-f/--foreground] [<conf-dir-path>]\n"
207               << "\t --help, -h            - print this help;\n"
208               << "\t --version, -v         - print version;\n"
209               << "\t --foreground, -f      - do not go into background;\n"
210               << "\t <conf-dir-path>       - path to the directory where the configuration file is located.\n";
211 }
212
213 void PrintVersion(const std::string& programName)
214 {
215     std::cout << programName << "\n"
216               << "Stargazer version" <<  " " << SERVER_VERSION << "\n";
217 }
218 //-----------------------------------------------------------------------------
219 } // namespace anonymous
220 //-----------------------------------------------------------------------------
221 int main(int argc, char* argv[])
222 {
223     int msgID = -11;
224
225     STG::Logger::get().setFileName("/var/log/stargazer.log");
226
227     if (getuid())
228     {
229         printf("You must be root. Exit.\n");
230         return 1;
231     }
232
233     std::string path;
234     bool noDaemon(false);
235
236     if (argc == 1)
237         path = "";
238     else
239     {
240         for (int i = 1; i < argc; ++i)
241         {
242             const std::string arg(argv[i]);
243             if (arg == "--help" || arg == "-h")
244             {
245                 PrintHelp(argv[0]);
246                 return 0;
247             }
248             if (arg == "--version" || arg == "-v")
249             {
250                 PrintVersion(argv[0]);
251                 return 0;
252             }
253             if (arg == "--foreground" || arg == "-f")
254                 noDaemon = true;
255             else
256                 path = arg;
257         }
258     }
259
260     SettingsImpl settings(path);
261
262     if (settings.ReadSettings())
263     {
264         auto& WriteServLog = STG::Logger::get();
265
266         if (settings.GetLogFileName() != "")
267             WriteServLog.setFileName(settings.GetLogFileName());
268
269         WriteServLog("ReadSettings error. %s", settings.GetStrError().c_str());
270         return -1;
271     }
272
273     if (!noDaemon)
274     {
275         if (ForkAndWait(settings.GetConfDir()) < 0)
276         {
277             STG::Logger::get()("Fork error!");
278             return -1;
279         }
280     }
281
282     auto& WriteServLog = STG::Logger::get();
283     WriteServLog.setFileName(settings.GetLogFileName());
284     WriteServLog("Stg v. %s", SERVER_VERSION);
285
286     for (size_t i = 0; i < settings.GetExecutersNum(); i++)
287     {
288         auto ret = StartScriptExecuter(argv[0], settings.GetExecMsgKey(), &msgID);
289         if (ret < 0)
290         {
291             STG::Logger::get()("Start Script Executer error!");
292             return -1;
293         }
294         if (ret == 1)
295             return 0;
296     }
297
298     PIDFile pidFile(settings.GetPIDFileName());
299
300     struct sigaction sa;
301     memset(&sa, 0, sizeof(sa));
302     sa.sa_handler = SIG_DFL;
303     sigaction(SIGHUP, &sa, NULL); // Apparently FreeBSD ignores SIGHUP by default when launched from rc.d at bot time.
304
305     sigset_t signalSet;
306     sigfillset(&signalSet);
307     pthread_sigmask(SIG_BLOCK, &signalSet, NULL);
308
309     StartTimer();
310     WaitTimer();
311     if (!IsStgTimerRunning())
312     {
313         printfd(__FILE__, "Timer thread not started in 1 sec!\n");
314         WriteServLog("Timer thread not started in 1 sec!");
315         return -1;
316     }
317
318     STG::AsyncPoolST::start();
319
320     StoreLoader storeLoader(settings);
321     if (storeLoader.load())
322     {
323         printfd(__FILE__, "Storage plugin: '%s'\n", storeLoader.GetStrError().c_str());
324         WriteServLog("Storage plugin: '%s'", storeLoader.GetStrError().c_str());
325         return -1;
326     }
327
328     auto& store = storeLoader.get();
329     WriteServLog("Storage plugin: %s. Loading successfull.", store.GetVersion().c_str());
330
331     AdminsImpl admins(store);
332     TariffsImpl tariffs(&store);
333     tariffs.ReadTariffs();
334     ServicesImpl services(&store);
335     CorporationsImpl corps(&store);
336     UsersImpl users(&settings, &store, &tariffs, services, admins.sysAdmin());
337     TraffCounterImpl traffCnt(&users, settings.GetRulesFileName());
338     traffCnt.SetMonitorDir(settings.GetMonitorDir());
339
340     if (users.Start())
341         return -1;
342
343     WriteServLog("Users started successfully.");
344
345     if (traffCnt.Start())
346         return -1;
347
348     WriteServLog("Traffcounter started successfully.");
349
350     STG::PluginManager manager(settings, store, admins, tariffs, services, corps, users, traffCnt);
351
352     srandom(static_cast<unsigned int>(stgTime));
353
354     WriteServLog("Stg started successfully.");
355     WriteServLog("+++++++++++++++++++++++++++++++++++++++++++++");
356
357     if (!noDaemon)
358     {
359         const auto startFile = settings.GetConfDir() + START_FILE;
360         creat(startFile.c_str(), S_IRUSR);
361     }
362
363     bool running = true;
364     while (running)
365     {
366         sigfillset(&signalSet);
367         int sig = 0;
368         sigwait(&signalSet, &sig);
369         int status;
370         switch (sig)
371         {
372             case SIGHUP:
373             {
374                 SettingsImpl newSettings(settings);
375                 if (newSettings.ReadSettings())
376                     WriteServLog("ReadSettings error. %s", newSettings.GetStrError().c_str());
377                 else
378                     settings = newSettings;
379                 WriteServLog.setFileName(settings.GetLogFileName());
380                 traffCnt.Reload();
381                 manager.reload(settings);
382                 break;
383             }
384             case SIGTERM:
385                 running = false;
386                 break;
387             case SIGINT:
388                 running = false;
389                 break;
390             case SIGPIPE:
391                 WriteServLog("Broken pipe!");
392                 break;
393             case SIGCHLD:
394                 executers.erase(waitpid(-1, &status, WNOHANG));
395                 if (executers.empty())
396                     running = false;
397                 break;
398             default:
399                 WriteServLog("Ignore signal %d", sig);
400                 break;
401         }
402     }
403
404     WriteServLog("+++++++++++++++++++++++++++++++++++++++++++++");
405
406     manager.stop();
407
408     STG::AsyncPoolST::stop();
409
410     if (!traffCnt.Stop())
411         WriteServLog("Traffcounter: Stop successfull.");
412
413     if (!users.Stop())
414         WriteServLog("Users: Stop successfull.");
415
416     sleep(1);
417     int res = msgctl(msgID, IPC_RMID, NULL);
418     if (res)
419         WriteServLog("Queue was not removed. id=%d", msgID);
420     else
421         WriteServLog("Queue removed successfully.");
422
423     KillExecuters();
424
425     StopStgTimer();
426     WriteServLog("StgTimer: Stop successfull.");
427
428     WriteServLog("Stg stopped successfully.");
429     WriteServLog("---------------------------------------------");
430
431     return 0;
432 }
433 //-----------------------------------------------------------------------------