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