]> git.stg.codes - stg.git/blob - projects/rscriptd/main.cpp
2f5f0baa5a642e29c7d59b5b80a9b8725e737e6b
[stg.git] / projects / rscriptd / 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  *    Author : Maxim Mamontov <faust@stargazer.dp.ua>
20  */
21
22  /*
23  $Revision: 1.19 $
24  $Author: faust $
25  $Date: 2010/09/10 06:37:45 $
26  */
27
28 #include <sys/types.h>
29 #include <sys/ipc.h>
30 #include <sys/msg.h>
31 #include <sys/stat.h>
32 #include <sys/wait.h>
33 #include <fcntl.h> // creat
34 #include <unistd.h>
35
36 #include <cstdlib>
37 #include <cstdio>
38 #include <csignal>
39 #include <set>
40
41 #include "stg/common.h"
42 #include "stg/stg_logger.h"
43 #include "stg/script_executer.h"
44 #include "stg/conffiles.h"
45 #include "stg/version.h"
46 #include "listener.h"
47 #include "pidfile.h"
48
49 using namespace std;
50
51 #ifdef DEBUG
52 # define MAIN_DEBUG  1
53 # define NO_DAEMON    1
54 #endif
55
56 #define START_FILE "/._ST_ART_ED_"
57
58 static bool childExited = false;
59 set<pid_t> executersPid;
60 static pid_t stgChildPid;
61 volatile time_t stgTime = time(NULL);
62
63 class STG_STOPPER
64 {
65 public:
66     STG_STOPPER() { nonstop = true; }
67     bool GetStatus() const { return nonstop; };
68     void Stop(const char * __file__, int __line__)
69         {
70         #ifdef NO_DAEMON
71         printfd(__FILE__, "rscriptd stopped at %s:%d\n", __file__, __line__);
72         #endif
73         nonstop = false;
74         }
75 private:
76     bool nonstop;
77 };
78 //-----------------------------------------------------------------------------
79 STG_STOPPER nonstop;
80 //-----------------------------------------------------------------------------
81 void CatchPROF(int)
82 {
83 }
84 //-----------------------------------------------------------------------------
85 void CatchUSR1(int)
86 {
87 }
88 //-----------------------------------------------------------------------------
89 void CatchTERM(int sig)
90 {
91 /*
92  *Function Name:CatchINT
93  *Parameters: sig_num - signal number
94  *Description: INT signal handler
95  *Returns: Nothing
96  */
97 STG_LOGGER & WriteServLog = GetStgLogger();
98 WriteServLog("Shutting down... %d", sig);
99
100 nonstop.Stop(__FILE__, __LINE__);
101
102 struct sigaction newsa, oldsa;
103 sigset_t sigmask;
104
105 sigemptyset(&sigmask);
106 sigaddset(&sigmask, SIGTERM);
107 newsa.sa_handler = SIG_IGN;
108 newsa.sa_mask = sigmask;
109 newsa.sa_flags = 0;
110 sigaction(SIGTERM, &newsa, &oldsa);
111
112 sigemptyset(&sigmask);
113 sigaddset(&sigmask, SIGINT);
114 newsa.sa_handler = SIG_IGN;
115 newsa.sa_mask = sigmask;
116 newsa.sa_flags = 0;
117 sigaction(SIGINT, &newsa, &oldsa);
118 }
119 //-----------------------------------------------------------------------------
120 void CatchPIPE(int)
121 {
122 STG_LOGGER & WriteServLog = GetStgLogger();
123 WriteServLog("Broken pipe!");
124 }
125 //-----------------------------------------------------------------------------
126 void CatchHUP(int)
127 {
128 }
129 //-----------------------------------------------------------------------------
130 void CatchCHLD(int)
131 {
132 int status;
133 pid_t childPid;
134 childPid = waitpid(-1, &status, WNOHANG);
135
136 set<pid_t>::iterator pid;
137 pid = executersPid.find(childPid);
138 if (pid != executersPid.end())
139     {
140     executersPid.erase(pid);
141     }
142 if (childPid == stgChildPid)
143     {
144     childExited = true;
145     }
146 }
147 //-----------------------------------------------------------------------------
148 void SetSignalHandlers()
149 {
150 struct sigaction newsa, oldsa;
151 sigset_t sigmask;
152
153 sigemptyset(&sigmask);
154 sigaddset(&sigmask, SIGTERM);
155 newsa.sa_handler = CatchTERM;
156 newsa.sa_mask = sigmask;
157 newsa.sa_flags = 0;
158 sigaction(SIGTERM, &newsa, &oldsa);
159
160 sigemptyset(&sigmask);
161 sigaddset(&sigmask, SIGUSR1);
162 newsa.sa_handler = CatchUSR1;
163 newsa.sa_mask = sigmask;
164 newsa.sa_flags = 0;
165 sigaction(SIGUSR1, &newsa, &oldsa);
166
167 sigemptyset(&sigmask);
168 sigaddset(&sigmask, SIGINT);
169 newsa.sa_handler = CatchTERM;
170 newsa.sa_mask = sigmask;
171 newsa.sa_flags = 0;
172 sigaction(SIGINT, &newsa, &oldsa);
173
174 sigemptyset(&sigmask);
175 sigaddset(&sigmask, SIGPIPE);
176 newsa.sa_handler = CatchPIPE;
177 newsa.sa_mask = sigmask;
178 newsa.sa_flags = 0;
179 sigaction(SIGPIPE, &newsa, &oldsa);
180
181 sigemptyset(&sigmask);
182 sigaddset(&sigmask, SIGHUP);
183 newsa.sa_handler = CatchHUP;
184 newsa.sa_mask = sigmask;
185 newsa.sa_flags = 0;
186 sigaction(SIGHUP, &newsa, &oldsa);
187
188 sigemptyset(&sigmask);
189 sigaddset(&sigmask, SIGCHLD);
190 newsa.sa_handler = CatchCHLD;
191 newsa.sa_mask = sigmask;
192 newsa.sa_flags = 0;
193 sigaction(SIGCHLD, &newsa, &oldsa);
194 }
195 //-----------------------------------------------------------------------------
196 int StartScriptExecuter(char * procName, int msgKey, int * msgID)
197 {
198 STG_LOGGER & WriteServLog = GetStgLogger();
199
200 if (*msgID == -11)   // If msgID == -11 - first call. Create queue
201     {
202     for (int i = 0; i < 2; i++)
203         {
204         *msgID = msgget(msgKey, IPC_CREAT | IPC_EXCL | 0600);
205
206         if (*msgID == -1)
207             {
208             *msgID = msgget(msgKey, 0);
209             if (*msgID == -1)
210                 {
211                 WriteServLog("Message queue not created.");
212                 return -1;
213                 }
214             else
215                 {
216                 msgctl(*msgID, IPC_RMID, NULL);
217                 }
218             }
219         else
220             {
221             WriteServLog("Message queue created successfully. msgKey=%d msgID=%d", msgKey, *msgID);
222             break;
223             }
224         }
225     }
226
227 pid_t executerPid = fork();
228
229 switch (executerPid)
230     {
231     case -1:    // Failure
232         WriteServLog("Fork error!");
233         return -1;
234
235     case 0:     // Child
236         //close(0);
237         //close(1);
238         //close(2);
239         //setsid();
240         Executer(msgKey, *msgID, executerPid, procName);
241         return 1;
242
243     default:    // Parent
244         if (executersPid.empty())
245             Executer(msgKey, *msgID, executerPid, NULL);
246         executersPid.insert(executerPid);
247     }
248 return 0;
249 }
250 //-----------------------------------------------------------------------------
251 #ifdef NO_DAEMON
252 int ForkAndWait(const string &)
253 #else
254 int ForkAndWait(const string & confDir)
255 #endif
256 {
257 #ifndef NO_DAEMON
258 stgChildPid = fork();
259 string startFile = confDir + START_FILE;
260 unlink(startFile.c_str());
261
262 switch (stgChildPid)
263     {
264     case -1:    // Failure
265         return -1;
266         break;
267
268     case 0:     // Child
269         //close(0);
270         close(1);
271         close(2);
272         setsid();
273         break;
274
275     default:    // Parent
276         for (int i = 0; i < 120 * 5; i++)
277             {
278             if (access(startFile.c_str(), F_OK) == 0)
279                 {
280                 //printf("Fork successfull. Exit.\n");
281                 unlink(startFile.c_str());
282                 exit(0);
283                 }
284
285             if (childExited)
286                 {
287                 unlink(startFile.c_str());
288                 exit(1);
289                 }
290             usleep(200000);
291             }
292         unlink(startFile.c_str());
293         exit(1);
294         break;
295     }
296 #endif
297 return 0;
298 }
299 //-----------------------------------------------------------------------------
300 void KillExecuters()
301 {
302 set<pid_t>::iterator pid;
303 pid = executersPid.begin();
304 while (pid != executersPid.end())
305     {
306     printfd(__FILE__, "KillExecuters pid=%d\n", *pid);
307     kill(*pid, SIGUSR1);
308     ++pid;
309     }
310 }
311 //-----------------------------------------------------------------------------
312 int main(int argc, char * argv[])
313 {
314
315 /*
316   Initialization order:
317   - Logger
318   - Config
319   - Set signal nandlers
320   - Fork and exit
321  */
322
323 CONFIGFILE * cfg = NULL;
324 LISTENER * listener = NULL;
325 int msgID = -11;
326 int execNum = 0;
327 int execMsgKey = 0;
328
329 string logFileName;
330 string confDir;
331 string password;
332 string onConnect;
333 string onDisconnect;
334 int port;
335 int userTimeout;
336
337 if (getuid())
338     {
339     printf("You must be root. Exit.\n");
340     exit(1);
341     }
342
343 if (argc == 2)
344     cfg = new CONFIGFILE(argv[1]);
345 else
346     cfg = new CONFIGFILE("/etc/rscriptd/rscriptd.conf");
347
348 if (cfg->Error())
349     {
350     STG_LOGGER & WriteServLog = GetStgLogger();
351     WriteServLog.SetLogFileName("/var/log/rscriptd.log");
352     WriteServLog("Error reading config file!");
353     delete cfg;
354     return EXIT_FAILURE;
355     }
356
357 cfg->ReadString("LogFileName", &logFileName, "/var/log/rscriptd.log");
358 cfg->ReadInt("ExecutersNum", &execNum, 1);
359 cfg->ReadInt("ExecMsgKey", &execMsgKey, 5555);
360 cfg->ReadString("ConfigDir", &confDir, "/etc/rscriptd");
361 cfg->ReadString("Password", &password, "");
362 cfg->ReadInt("Port", &port, 5555);
363 cfg->ReadInt("UserTimeout", &userTimeout, 60);
364 cfg->ReadString("ScriptOnConnect", &onConnect, "/etc/rscriptd/OnConnect");
365 cfg->ReadString("ScriptOnDisconnect", &onDisconnect, "/etc/rscriptd/OnDisconnect");
366
367 SetSignalHandlers();
368 if (ForkAndWait(confDir) < 0)
369     {
370     STG_LOGGER & WriteServLog = GetStgLogger();
371     WriteServLog("Fork error!");
372     delete cfg;
373     return EXIT_FAILURE;
374     }
375
376 STG_LOGGER & WriteServLog = GetStgLogger();
377 PIDFile pidFile("/var/run/rscriptd.pid");
378 WriteServLog.SetLogFileName(logFileName);
379 WriteServLog("rscriptd v. %s", SERVER_VERSION);
380
381 for (int i = 0; i < execNum; i++)
382     {
383     int ret = StartScriptExecuter(argv[0], execMsgKey, &msgID);
384     if (ret < 0)
385         {
386         STG_LOGGER & WriteServLog = GetStgLogger();
387         WriteServLog("Start Script Executer error!");
388         delete cfg;
389         return EXIT_FAILURE;
390         }
391     if (ret == 1)
392         {
393         delete cfg;
394         return EXIT_SUCCESS;
395         }
396     }
397
398 listener = new LISTENER();
399 listener->SetPort(port);
400 listener->SetPassword(password);
401 listener->SetUserTimeout(userTimeout);
402 listener->SetScriptOnConnect(onConnect);
403 listener->SetScriptOnDisconnect(onDisconnect);
404
405 listener->Start();
406
407 WriteServLog("rscriptd started successfully.");
408 WriteServLog("+++++++++++++++++++++++++++++++++++++++++++++");
409
410 #ifndef NO_DAEMON
411 string startFile(confDir + START_FILE);
412 creat(startFile.c_str(), S_IRUSR);
413 #endif
414
415 while (nonstop.GetStatus())
416     {
417     usleep(100000);
418     }
419
420 listener->Stop();
421
422 WriteServLog("+++++++++++++++++++++++++++++++++++++++++++++");
423
424 int res = msgctl(msgID, IPC_RMID, NULL);
425 if (res)
426     WriteServLog("Queue was not removed. id=%d", msgID);
427 else
428     WriteServLog("Queue removed successfully.");
429
430 KillExecuters();
431
432 WriteServLog("rscriptd stopped successfully.");
433 WriteServLog("---------------------------------------------");
434
435 delete listener;
436 delete cfg;
437 return EXIT_SUCCESS;
438 }
439 //-----------------------------------------------------------------------------