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