Add new iptables handle task
[rahunas] / src / rahunasd.c
1 /**
2  * RahuNASd
3  * Author: Neutron Soutmun <neo.neutron@gmail.com>
4  * Date:   2008-08-07
5  */
6
7 #include <sys/types.h>
8 #include <sys/wait.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdarg.h>
12 #include <string.h>
13 #include <errno.h>
14 #include <unistd.h>
15 #include <signal.h>
16 #include <syslog.h>
17
18 #include "rahunasd.h"
19 #include "rh-server.h"
20 #include "rh-xmlrpc-server.h"
21 #include "rh-xmlrpc-cmd.h"
22 #include "rh-ipset.h"
23 #include "rh-utils.h"
24 #include "rh-task.h"
25
26 const char *termstring = '\0';
27 pid_t pid, sid;
28
29 struct main_server rh_main_server_instance = {
30   .vserver_list = NULL,
31   .task_list = NULL,
32 };
33
34 struct main_server *rh_main_server = &rh_main_server_instance;
35
36 int getline(int fd, char *buf, size_t size);
37 size_t expired_check(void *data);
38
39 void rh_sighandler(int sig)
40 {
41   switch (sig) {
42     case SIGTERM:
43       if (pid == 0) {
44         rh_exit();
45         exit(EXIT_SUCCESS);
46       } else if (pid > 0) {
47         syslog(LOG_NOTICE, "Kill Child PID %d", pid);
48         kill(pid, SIGTERM);
49       } else {
50         syslog(LOG_ERR, "Invalid PID");
51         exit(EXIT_FAILURE);
52       }
53       break;
54   }
55 }
56
57 int getline(int fd, char *buf, size_t size)
58 {
59   char cbuf;
60   char *current;
61
62   if (!buf || fd < 0)
63     return 0;
64
65   current = buf;
66
67   while (read(fd, &cbuf, 1) > 0) {
68     *current = cbuf;
69     if (cbuf == '\n') {
70       *current = '\0';
71       break;
72     } else if ((current - buf) < (size - 1)) {
73       current++;
74     }
75   }
76
77   return (current - buf);
78 }
79
80 size_t expired_check(void *data)
81 {
82   struct processing_set *process = (struct processing_set *) data;
83   struct ip_set_list *setlist = (struct ip_set_list *) process->list;
84   size_t offset;
85   struct ip_set_rahunas *table = NULL;
86   struct task_req req;
87   unsigned int id;
88   char *ip = NULL;
89   int res  = 0;
90   GList *runner = g_list_first(process->vs->v_map->members);
91   struct rahunas_member *member = NULL;
92
93   if (process == NULL)
94     return (-1);
95
96   if (process->list == NULL)
97     return (-1); 
98
99   offset = sizeof(struct ip_set_list) + setlist->header_size;
100   table = (struct ip_set_rahunas *)(process->list + offset);
101
102   while (runner != NULL) {
103     member = (struct rahunas_member *)runner->data;
104     runner = g_list_next(runner);
105
106     id = member->id;
107
108     DP("Processing id = %d", id);
109
110     DP("Time diff = %d, idle_timeout=%d", (time(NULL) - table[id].timestamp),
111          process->vs->vserver_config->idle_timeout);
112
113     if ((time(NULL) - table[id].timestamp) > 
114          process->vs->vserver_config->idle_timeout) {
115       // Idle Timeout
116       DP("Found IP: %s idle timeout", idtoip(process->vs->v_map, id));
117       req.id = id;
118       memcpy(req.mac_address, &table[id].ethernet, ETH_ALEN);
119       req.req_opt = RH_RADIUS_TERM_IDLE_TIMEOUT;
120       send_xmlrpc_stopacct(process->vs, id, 
121                            RH_RADIUS_TERM_IDLE_TIMEOUT);
122       res = rh_task_stopsess(process->vs, &req);
123     } else if (member->session_timeout != 0 && 
124                time(NULL) > member->session_timeout) {
125       // Session Timeout (Expired)
126       DP("Found IP: %s session timeout", idtoip(process->vs->v_map, id));
127       req.id = id;
128       memcpy(req.mac_address, &table[id].ethernet, ETH_ALEN);
129       req.req_opt = RH_RADIUS_TERM_SESSION_TIMEOUT;
130       send_xmlrpc_stopacct(process->vs, id, 
131                            RH_RADIUS_TERM_SESSION_TIMEOUT);
132       res = rh_task_stopsess(process->vs, &req);
133     }
134   }
135 }
136
137 gboolean polling_expired_check(struct main_server *ms, struct vserver *vs) {
138   walk_through_set(&expired_check, vs);
139   return TRUE;
140 }
141
142 gboolean polling(gpointer data) {
143   struct main_server *ms = (struct main_server *)data;
144   struct vserver *vs = NULL;
145   DP("%s", "Start polling!");
146   walk_through_vserver(&polling_expired_check, ms);
147   return TRUE;
148 }
149
150 void rh_exit()
151 {
152   walk_through_vserver(&rh_task_cleanup, rh_main_server);
153   rh_task_stopservice(rh_main_server);
154   rh_task_unregister(rh_main_server);
155   rh_closelog(rh_main_server->main_config->log_file);
156 }
157
158 static void
159 watch_child(char *argv[])
160 {
161   char *prog = NULL;
162   int failcount = 0;
163   time_t start;
164   time_t stop;
165   int status;
166   int nullfd;
167   
168   if (*(argv[0]) == '(')
169     return;
170
171   pid = fork(); 
172   if (pid < 0) {
173     syslog(LOG_ALERT, "fork failed");
174     exit(EXIT_FAILURE);
175   } else if (pid > 0) {
176     /* parent */
177     rh_writepid(DEFAULT_PID, pid);
178     exit(EXIT_SUCCESS);
179   }
180
181   /* Change the file mode mask */
182   umask(0);
183
184   if ((sid = setsid()) < 0)
185     syslog(LOG_ALERT, "setsid failed");
186
187   if ((chdir("/")) < 0) {
188     exit(EXIT_FAILURE);
189   }
190     
191   /* Close out the standard file descriptors */
192   close(STDIN_FILENO);
193   close(STDOUT_FILENO);
194   close(STDERR_FILENO);
195
196
197   while(1) {
198     pid = fork();
199     if (pid == 0) {
200       /* child */
201       prog = strdup(argv[0]);
202       argv[0] = strdup("(rahunasd)");
203       execvp(prog, argv);
204       syslog(LOG_ALERT, "execvp failed");
205     } else if (pid < 0) {
206       syslog(LOG_ERR, "Could not fork the child process");   
207       exit(EXIT_FAILURE);
208     }
209   
210     /* parent */
211     syslog(LOG_NOTICE, "RahuNASd Parent: child process %d started", pid);   
212
213     time(&start);
214
215     pid = waitpid(-1, &status, 0);
216     time(&stop);
217
218     if (WIFEXITED(status)) {
219       syslog(LOG_NOTICE,
220                "RahuNASd Parent: child process %d exited with status %d",
221                pid, WEXITSTATUS(status));
222     } else if (WIFSIGNALED(status)) {
223       syslog(LOG_NOTICE,
224                "RahuNASd Parent: child process %d exited due to signal %d",
225                pid, WTERMSIG(status));
226     } else {
227       syslog(LOG_NOTICE, "RahuNASd Parent: child process %d exited", pid);
228     }
229   
230     if (stop - start < 10)
231       failcount++;
232     else
233       failcount = 0;
234   
235     if (failcount == 5) {
236       syslog(LOG_ALERT, "Exiting due to repeated, frequent failures");
237       exit(EXIT_FAILURE);
238     }
239   
240     if (WIFEXITED(status) && (WEXITSTATUS(status) == 0)) {
241         syslog(LOG_NOTICE, "Exit Gracefully");
242         exit(EXIT_SUCCESS);
243     }
244     
245     sleep(3);
246   }
247 }
248
249 void rh_free_member (struct rahunas_member *member)
250 {
251   if (member->username && member->username != termstring)
252     free(member->username);
253
254   if (member->session_id && member->session_id != termstring)
255     free(member->session_id);
256 }
257
258 int main(int argc, char **argv) 
259 {
260   gchar* addr = "localhost";
261   int port    = 8123;
262   int fd_log;
263
264   char version[256];
265
266   char line[256];
267
268   union rahunas_config rh_main_config = {
269     .rh_main.conf_dir = NULL,
270     .rh_main.log_file = NULL,
271     .rh_main.dhcp = NULL,
272     .rh_main.polling_interval = POLLING,
273     .rh_main.bandwidth_shape = BANDWIDTH_SHAPE,
274   };
275
276   GNetXmlRpcServer *server = NULL;
277   GMainLoop* main_loop     = NULL;
278
279   signal(SIGTERM, rh_sighandler);
280
281   watch_child(argv);
282
283   /* Get main server config */
284   get_config(CONFIG_FILE, &rh_main_config);
285   rh_main_server->main_config = (struct rahunas_main_config *) &rh_main_config;
286
287   /* Open and select main log file */
288   if (rh_main_server->main_config->log_file != NULL) {
289     syslog(LOG_INFO, "Open log file: %s", 
290            rh_main_server->main_config->log_file);
291     rh_main_server->log_fd = rh_openlog(rh_main_server->main_config->log_file);
292
293     if (!rh_main_server->log_fd) {
294       syslog(LOG_ERR, "Could not open log file %s\n", 
295              rh_main_server->main_config->log_file);
296       exit(EXIT_FAILURE);
297     }
298
299     rh_logselect(rh_main_server->log_fd);
300   }
301
302   syslog(LOG_INFO, "Config directory: %s", rh_main_server->main_config->conf_dir);
303
304   /* Get vserver(s) config */
305   if (rh_main_server->main_config->conf_dir != NULL) {
306     get_vservers_config(rh_main_server->main_config->conf_dir, rh_main_server);
307   } else {
308     syslog(LOG_ERR, "The main configuration file is incompleted, lack of conf_dir\n");
309     exit(EXIT_FAILURE);
310   }
311
312   sprintf(version, "Starting %s - Version %s", PROGRAM, RAHUNAS_VERSION);
313   logmsg(RH_LOG_NORMAL, version);
314
315   rh_task_register(rh_main_server);
316   rh_task_startservice(rh_main_server);
317
318   walk_through_vserver(&rh_task_init, rh_main_server);
319
320   gnet_init();
321   main_loop = g_main_loop_new (NULL, FALSE);
322
323   /* XML RPC Server */
324   server = gnet_xmlrpc_server_new (addr, port);
325
326   if (!server) {
327     syslog(LOG_ERR, "Could not start XML-RPC server!");
328     walk_through_vserver(&rh_task_stopservice, rh_main_server);
329     exit (EXIT_FAILURE);
330   }
331
332   gnet_xmlrpc_server_register_command (server, 
333                                        "startsession", 
334                                        do_startsession, 
335                                        rh_main_server);
336
337   gnet_xmlrpc_server_register_command (server, 
338                                        "stopsession", 
339                                        do_stopsession, 
340                                        rh_main_server);
341
342   gnet_xmlrpc_server_register_command (server, 
343                                        "getsessioninfo", 
344                                        do_getsessioninfo, 
345                                        rh_main_server);
346
347   DP("Polling interval = %d", rh_main_server->main_config->polling_interval);
348
349   g_timeout_add_seconds (rh_main_server->main_config->polling_interval, 
350                          polling, rh_main_server);
351
352
353   logmsg(RH_LOG_NORMAL, "Ready to serve...");
354   g_main_loop_run(main_loop);
355
356   exit(EXIT_SUCCESS);
357 }