]> git.stg.codes - stg.git/blob - projects/stargazer/main.cpp
Add async pool (to replace EVENT_LOOP).
[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 "eventloop.h"
34 #include "stg_timer.h"
35
36 #include "stg/user.h"
37 #include "stg/common.h"
38 #include "stg/plugin.h"
39 #include "stg/logger.h"
40 #include "stg/scriptexecuter.h"
41 #include "stg/version.h"
42
43 #include <fstream>
44 #include <vector>
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 #ifdef DEBUG
60     #define NO_DAEMON  (1)
61 #endif
62
63 #define START_FILE "/._ST_ART_ED_"
64
65 using STG::SettingsImpl;
66 using STG::AdminsImpl;
67 using STG::TraffCounterImpl;
68 using STG::UsersImpl;
69 using STG::TariffsImpl;
70 using STG::ServicesImpl;
71 using STG::CorporationsImpl;
72 using STG::StoreLoader;
73
74 namespace
75 {
76 std::set<pid_t> executers;
77
78 void StartTimer();
79 int StartScriptExecuter(char* procName, int msgKey, int* msgID);
80 int ForkAndWait(const std::string& confDir);
81 void KillExecuters();
82
83 //-----------------------------------------------------------------------------
84 void StartTimer()
85 {
86     auto& WriteServLog = STG::Logger::get();
87
88     if (RunStgTimer())
89     {
90         WriteServLog("Cannot start timer. Fatal.");
91         exit(1);
92     }
93     else
94         WriteServLog("Timer thread started successfully.");
95 }
96 //-----------------------------------------------------------------------------
97 #if defined(LINUX) || defined(DARWIN)
98 int StartScriptExecuter(char* procName, int msgKey, int* msgID)
99 #else
100 int StartScriptExecuter(char*, int msgKey, int* msgID)
101 #endif
102 {
103     auto& WriteServLog = STG::Logger::get();
104
105     if (*msgID == -11)   // If msgID == -11 - first call. Create queue
106     {
107         for (int i = 0; i < 2; i++)
108         {
109             *msgID = msgget(msgKey, IPC_CREAT | IPC_EXCL | 0600);
110
111             if (*msgID == -1)
112             {
113                 *msgID = msgget(msgKey, 0);
114                 if (*msgID == -1)
115                 {
116                     WriteServLog("Message queue not created.");
117                     return -1;
118                 }
119                 else
120                     msgctl(*msgID, IPC_RMID, NULL);
121             }
122             else
123             {
124                 WriteServLog("Message queue created successfully. msgKey=%d msgID=%d", msgKey, *msgID);
125                 break;
126             }
127         }
128     }
129
130     const auto pid = fork();
131
132     switch (pid)
133     {
134         case -1:
135             WriteServLog("Fork error!");
136             return -1;
137
138         case 0:
139 #if defined(LINUX) || defined(DARWIN)
140             Executer(*msgID, pid, procName);
141 #else
142             Executer(*msgID, pid);
143 #endif
144             return 1;
145
146         default:
147             if (executers.empty()) {
148 #if defined(LINUX) || defined(DARWIN)
149                 Executer(*msgID, pid, NULL);
150 #else
151                 Executer(*msgID, pid);
152 #endif
153             }
154             executers.insert(pid);
155     }
156     return 0;
157 }
158 //-----------------------------------------------------------------------------
159 #ifndef NO_DAEMON
160 int ForkAndWait(const std::string& confDir)
161 #else
162 int ForkAndWait(const std::string&)
163 #endif
164 {
165 #ifndef NO_DAEMON
166     const auto pid = fork();
167     const auto startFile = confDir + START_FILE;
168     unlink(startFile.c_str());
169
170     switch (pid)
171     {
172         case -1:
173             return -1;
174             break;
175
176         case 0:
177             close(1);
178             close(2);
179             setsid();
180             break;
181
182         default:
183             struct timespec ts = {0, 200000000};
184             for (int i = 0; i < 120 * 5; i++)
185             {
186                 if (access(startFile.c_str(), F_OK) == 0)
187                 {
188                     unlink(startFile.c_str());
189                     exit(0);
190                 }
191
192                 nanosleep(&ts, NULL);
193             }
194             unlink(startFile.c_str());
195             exit(1);
196             break;
197     }
198 #endif
199     return 0;
200 }
201 //-----------------------------------------------------------------------------
202 void KillExecuters()
203 {
204     auto pid = executers.begin();
205     while (pid != executers.end())
206     {
207         printfd(__FILE__, "KillExecuters pid=%d\n", *pid);
208         kill(*pid, SIGUSR1);
209         ++pid;
210     }
211 }
212 //-----------------------------------------------------------------------------
213 } // namespace anonymous
214 //-----------------------------------------------------------------------------
215 int main(int argc, char* argv[])
216 {
217     int msgID = -11;
218
219     STG::Logger::get().setFileName("/var/log/stargazer.log");
220
221     if (getuid())
222     {
223         printf("You must be root. Exit.\n");
224         return 1;
225     }
226
227     SettingsImpl settings(argc == 2 ? argv[1] : "");
228
229     if (settings.ReadSettings())
230     {
231         auto& WriteServLog = STG::Logger::get();
232
233         if (settings.GetLogFileName() != "")
234             WriteServLog.setFileName(settings.GetLogFileName());
235
236         WriteServLog("ReadSettings error. %s", settings.GetStrError().c_str());
237         return -1;
238     }
239
240 #ifndef NO_DAEMON
241     const auto startFile = settings.GetConfDir() + START_FILE;
242 #endif
243
244     if (ForkAndWait(settings.GetConfDir()) < 0)
245     {
246         STG::Logger::get()("Fork error!");
247         return -1;
248     }
249
250     auto& WriteServLog = STG::Logger::get();
251     WriteServLog.setFileName(settings.GetLogFileName());
252     WriteServLog("Stg v. %s", SERVER_VERSION);
253
254     for (size_t i = 0; i < settings.GetExecutersNum(); i++)
255     {
256         auto ret = StartScriptExecuter(argv[0], settings.GetExecMsgKey(), &msgID);
257         if (ret < 0)
258         {
259             STG::Logger::get()("Start Script Executer error!");
260             return -1;
261         }
262         if (ret == 1)
263             return 0;
264     }
265
266     PIDFile pidFile(settings.GetPIDFileName());
267
268     struct sigaction sa;
269     memset(&sa, 0, sizeof(sa));
270     sa.sa_handler = SIG_DFL;
271     sigaction(SIGHUP, &sa, NULL); // Apparently FreeBSD ignores SIGHUP by default when launched from rc.d at bot time.
272
273     sigset_t signalSet;
274     sigfillset(&signalSet);
275     pthread_sigmask(SIG_BLOCK, &signalSet, NULL);
276
277     StartTimer();
278     WaitTimer();
279     if (!IsStgTimerRunning())
280     {
281         printfd(__FILE__, "Timer thread not started in 1 sec!\n");
282         WriteServLog("Timer thread not started in 1 sec!");
283         return -1;
284     }
285
286     auto& loop = EVENT_LOOP::instance();
287     STG::AsyncPoolST::start();
288
289     StoreLoader storeLoader(settings);
290     if (storeLoader.load())
291     {
292         printfd(__FILE__, "Storage plugin: '%s'\n", storeLoader.GetStrError().c_str());
293         WriteServLog("Storage plugin: '%s'", storeLoader.GetStrError().c_str());
294         return -1;
295     }
296
297     if (loop.Start())
298     {
299         printfd(__FILE__, "Event loop not started.\n");
300         WriteServLog("Event loop not started.");
301         return -1;
302     }
303
304     auto& store = storeLoader.get();
305     WriteServLog("Storage plugin: %s. Loading successfull.", store.GetVersion().c_str());
306
307     AdminsImpl admins(store);
308     TariffsImpl tariffs(&store);
309     tariffs.ReadTariffs();
310     ServicesImpl services(&store);
311     CorporationsImpl corps(&store);
312     UsersImpl users(&settings, &store, &tariffs, services, admins.sysAdmin());
313     TraffCounterImpl traffCnt(&users, settings.GetRulesFileName());
314     traffCnt.SetMonitorDir(settings.GetMonitorDir());
315
316     if (users.Start())
317         return -1;
318
319     WriteServLog("Users started successfully.");
320
321     if (traffCnt.Start())
322         return -1;
323
324     WriteServLog("Traffcounter started successfully.");
325
326     STG::PluginManager manager(settings, store, admins, tariffs, services, corps, users, traffCnt);
327
328     srandom(static_cast<unsigned int>(stgTime));
329
330     WriteServLog("Stg started successfully.");
331     WriteServLog("+++++++++++++++++++++++++++++++++++++++++++++");
332
333 #ifndef NO_DAEMON
334     creat(startFile.c_str(), S_IRUSR);
335 #endif
336
337     bool running = true;
338     while (running)
339     {
340         sigfillset(&signalSet);
341         int sig = 0;
342         sigwait(&signalSet, &sig);
343         int status;
344         switch (sig)
345         {
346             case SIGHUP:
347             {
348                 SettingsImpl newSettings(settings);
349                 if (newSettings.ReadSettings())
350                     WriteServLog("ReadSettings error. %s", newSettings.GetStrError().c_str());
351                 else
352                     settings = newSettings;
353                 WriteServLog.setFileName(settings.GetLogFileName());
354                 traffCnt.Reload();
355                 manager.reload(settings);
356                 break;
357             }
358             case SIGTERM:
359                 running = false;
360                 break;
361             case SIGINT:
362                 running = false;
363                 break;
364             case SIGPIPE:
365                 WriteServLog("Broken pipe!");
366                 break;
367             case SIGCHLD:
368                 executers.erase(waitpid(-1, &status, WNOHANG));
369                 if (executers.empty())
370                     running = false;
371                 break;
372             default:
373                 WriteServLog("Ignore signal %d", sig);
374                 break;
375         }
376     }
377
378     WriteServLog("+++++++++++++++++++++++++++++++++++++++++++++");
379
380     manager.stop();
381
382     STG::AsyncPoolST::stop();
383     if (loop.Stop())
384         WriteServLog("Event loop not stopped.");
385
386     if (!traffCnt.Stop())
387         WriteServLog("Traffcounter: Stop successfull.");
388
389     if (!users.Stop())
390         WriteServLog("Users: Stop successfull.");
391
392     sleep(1);
393     int res = msgctl(msgID, IPC_RMID, NULL);
394     if (res)
395         WriteServLog("Queue was not removed. id=%d", msgID);
396     else
397         WriteServLog("Queue removed successfully.");
398
399     KillExecuters();
400
401     StopStgTimer();
402     WriteServLog("StgTimer: Stop successfull.");
403
404     WriteServLog("Stg stopped successfully.");
405     WriteServLog("---------------------------------------------");
406
407     return 0;
408 }
409 //-----------------------------------------------------------------------------