742633d1edf502173090f957453dd53f23e007d4
[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/stat.h>
9 #include <sys/wait.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <stdarg.h>
13 #include <string.h>
14 #include <fcntl.h>
15 #include <errno.h>
16 #include <unistd.h>
17 #include <signal.h>
18 #include <syslog.h>
19
20 #include "rahunasd.h"
21 #include "rh-xmlrpc-server.h"
22 #include "rh-xmlrpc-cmd.h"
23 #include "rh-ipset.h"
24 #include "rh-utils.h"
25 #include "rh-task.h"
26
27 /* Abstract functions */
28 int logmsg(int priority, const char *msg, ...); 
29 int getline(int fd, char *buf, size_t size);
30
31 size_t expired_check(void *data);
32
33 /* Declaration */
34 struct rahunas_config rh_config;
35 struct rahunas_map *map = NULL;
36 struct set *rahunas_set = NULL;
37
38 struct set **set_list = NULL;
39 ip_set_id_t max_sets = 0;
40
41 const char *termstring = '\0';
42 pid_t pid, sid;
43
44 uint32_t iptoid(struct rahunas_map *map, const char *ip) {
45   uint32_t ret;
46   struct in_addr req_ip;
47
48   if (!map || !ip)
49     return (-1);
50
51   if (!(inet_aton(ip, &req_ip))) {
52     DP("Could not convert IP: %s", ip);
53     return (-1);  
54   }
55
56   DP("Request IP: %s", ip);
57   
58   ret = ntohl(req_ip.s_addr) - ntohl(map->first_ip);
59   if (ret < 0 || ret > (map->size - 1))
60     ret = (-1);
61
62   DP("Request Index: %lu", ret);
63   return ret; 
64 }
65
66 char *idtoip(struct rahunas_map *map, uint32_t id) {
67   struct in_addr sess_addr;
68
69   if (!map)
70     return termstring;
71
72   if (id < 0)
73     return termstring;
74
75   sess_addr.s_addr = htonl((ntohl(map->first_ip) + id));
76
77   return inet_ntoa(sess_addr);
78 }
79
80 void rh_free_member (struct rahunas_member *member)
81 {
82   if (member->username && member->username != termstring)
83     free(member->username);
84
85   if (member->session_id && member->session_id != termstring)
86     free(member->session_id);
87   
88   memset(member, 0, sizeof(struct rahunas_member));
89   member->username = termstring;
90   member->session_id = termstring;
91 }
92
93 int rh_openlog(const char *filename)
94 {
95   return open(filename, O_WRONLY | O_APPEND | O_CREAT);
96 }
97
98 int rh_closelog(int fd)
99 {
100   if (close(fd) == 0)
101     return 1;
102   else
103     return 0;
104 }
105
106 int logmsg(int priority, const char *msg, ...) 
107 {
108   int n, size = 256;
109   va_list ap;
110   char *time_fmt = "%b %e %T";
111   char *p = NULL;
112   char *np = NULL;
113
114   if (priority < RH_LOG_LEVEL)
115     return 0;
116
117   if ((p = rh_malloc(size)) == NULL) {
118     return (-1);
119   }
120
121   while (1) {
122     va_start(ap, msg);
123     n = vsnprintf(p, size, msg, ap);
124     va_end(ap);
125
126     if (n > -1 && n < size)
127       break;
128  
129     if (n > -1)
130       size = n+1;
131     else
132       size *= 2;
133
134     if ((np = realloc(p, size)) == NULL) {
135       free(p);
136       p = NULL;
137       break;
138     } else {
139       p = np;
140     }
141   }
142
143   if (!p)
144     return (-1);
145
146   fprintf(stderr, "%s : %s\n", timemsg(), p);
147
148   rh_free(&p);
149   rh_free(&np);
150 }
151
152 void rh_sighandler(int sig)
153 {
154   switch (sig) {
155     case SIGTERM:
156     case SIGKILL:
157       if (pid == 0) {
158         rh_exit();
159         syslog(LOG_NOTICE, "Exit Gracefully", pid);
160         exit(EXIT_SUCCESS);
161       }
162
163       if (pid > 0) {
164         syslog(LOG_NOTICE, "Kill Child PID %d", pid);
165         kill(pid, SIGKILL);
166       }
167       break;
168   }
169 }
170
171 int getline(int fd, char *buf, size_t size)
172 {
173   char cbuf;
174   char *current;
175
176   if (!buf || fd < 0)
177     return 0;
178
179   current = buf;
180
181   while (read(fd, &cbuf, 1) > 0) {
182     *current = cbuf;
183     if (cbuf == '\n') {
184       *current = '\0';
185       break;
186     } else if ((current - buf) < (size - 1)) {
187       current++;
188     }
189   }
190
191   return (current - buf);
192 }
193
194 gboolean polling(gpointer data) {
195   struct rahunas_map *map = (struct rahunas_map *)data;
196   DP("%s", "Start polling!");
197   walk_through_set(&expired_check);
198   return TRUE;
199 }
200
201 size_t expired_check(void *data)
202 {
203   struct ip_set_list *setlist = (struct ip_set_list *) data;
204   struct set *set = set_list[setlist->index];
205   size_t offset;
206   struct ip_set_rahunas *table = NULL;
207   struct rahunas_member *members = map->members;
208   struct task_req req;
209   unsigned int i;
210   char *ip = NULL;
211   int res  = 0;
212
213   offset = sizeof(struct ip_set_list) + setlist->header_size;
214   table = (struct ip_set_rahunas *)(data + offset);
215
216   DP("Map size %d", map->size);
217  
218   for (i = 0; i < map->size; i++) {
219     if (test_bit(IPSET_RAHUNAS_ISSET, (void *)&table[i].flags)) {
220       if ((time(NULL) - table[i].timestamp) > rh_config.idle_threshold) {
221         // Idle Timeout
222         DP("Found IP: %s idle timeout", idtoip(map, i));
223         req.id = i;
224         memcpy(req.mac_address, &table[i].ethernet, ETH_ALEN);
225         req.req_opt = RH_RADIUS_TERM_IDLE_TIMEOUT;
226         send_xmlrpc_stopacct(map, i, RH_RADIUS_TERM_IDLE_TIMEOUT);
227         res = rh_task_stopsess(map, &req);
228       } else if (members[i].session_timeout != 0 && 
229                    time(NULL) > members[i].session_timeout) {
230         // Session Timeout (Expired)
231         DP("Found IP: %s session timeout", idtoip(map, i));
232         req.id = i;
233         memcpy(req.mac_address, &table[i].ethernet, ETH_ALEN);
234         req.req_opt = RH_RADIUS_TERM_SESSION_TIMEOUT;
235         send_xmlrpc_stopacct(map, i, RH_RADIUS_TERM_SESSION_TIMEOUT);
236         res = rh_task_stopsess(map, &req);
237       }
238     }
239   }
240 }
241
242 void rh_exit()
243 {
244   syslog(LOG_ALERT, "Child Exiting ..");
245   rh_task_stopservice(map);
246   rh_task_cleanup();
247   rh_closelog(rh_config.log_file);
248 }
249
250 static void
251 watch_child(char *argv[])
252 {
253   char *prog = NULL;
254   int failcount = 0;
255   time_t start;
256   time_t stop;
257   int status;
258
259   int nullfd;
260   int pidfd;
261   
262   if (*(argv[0]) == '(')
263     return;
264
265   pid = fork(); 
266   if (pid < 0) {
267     syslog(LOG_ALERT, "fork failed");
268     exit(EXIT_FAILURE);
269   } else if (pid > 0) {
270     /* parent */
271     pidfd = open(DEFAULT_PID, O_WRONLY | O_TRUNC | O_CREAT);
272     if (pidfd) {
273       dup2(pidfd, STDOUT_FILENO);
274       fprintf(stdout, "%d\n", pid);
275       close(pidfd);
276     }
277     exit(EXIT_SUCCESS);
278   }
279
280   /* Change the file mode mask */
281   umask(0);
282
283   if ((sid = setsid()) < 0)
284     syslog(LOG_ALERT, "setsid failed");
285
286   if ((chdir("/")) < 0) {
287     exit(EXIT_FAILURE);
288   }
289     
290   /* Close out the standard file descriptors */
291   close(STDIN_FILENO);
292   close(STDOUT_FILENO);
293   close(STDERR_FILENO);
294
295
296   while(1) {
297
298     if ((pid = fork()) == 0) {
299       /* child */
300       prog = strdup(argv[0]);
301       argv[0] = strdup("(rahunasd)");
302       execvp(prog, argv);
303       syslog(LOG_ALERT, "execvp failed");
304     }
305   
306     syslog(LOG_NOTICE, "RahuNASd Parent: child process %d started", pid);   
307
308     time(&start);
309     /* parent */
310     pid = waitpid(-1, &status, 0);
311     time(&stop);
312
313     if (WIFEXITED(status)) {
314       syslog(LOG_NOTICE,
315                "RahuNASd Parent: child process %d exited with status %d",
316                pid, WEXITSTATUS(status));
317     } else if (WIFSIGNALED(status)) {
318       syslog(LOG_NOTICE,
319                "RahuNASd Parent: child process %d exited due to signal %d",
320                pid, WTERMSIG(status));
321     } else {
322       syslog(LOG_NOTICE, "RahuNASd Parent: child process %d exited", pid);
323     }
324   
325     if (stop - start < 10)
326       failcount++;
327     else
328       failcount = 0;
329   
330     if (failcount == 5) {
331       syslog(LOG_ALERT, "Exiting due to repeated, frequent failures");
332       exit(EXIT_FAILURE);
333     }
334   
335   
336     if (WIFEXITED(status))
337       if (WEXITSTATUS(status) == 0)
338         exit(EXIT_SUCCESS);
339     
340     sleep(3);
341   }
342 }
343
344 int main(int argc, char **argv) 
345 {
346   gchar* addr = "localhost";
347   int port    = 8123;
348   int fd_log;
349
350   char line[256];
351   char version[256];
352
353   GNetXmlRpcServer *server = NULL;
354   GMainLoop* main_loop     = NULL;
355
356    
357
358   signal(SIGTERM, rh_sighandler);
359   signal(SIGKILL, rh_sighandler);
360
361   watch_child(argv);
362
363   /* Get configuration from config file */
364   if (config_init(&rh_config) < 0) {
365     syslog(LOG_ERR, "Could not open config file %s", CONFIG_FILE);
366     exit(EXIT_FAILURE);
367   }
368
369   /* Open log file */
370   if ((fd_log = rh_openlog(rh_config.log_file)) < 0) {
371     syslog(LOG_ERR, "Could not open log file %s", rh_config.log_file);
372     exit(EXIT_FAILURE);
373   }
374
375   dup2(fd_log, STDERR_FILENO);
376
377   sprintf(version, "Starting %s - Version %s", PROGRAM, RAHUNAS_VERSION);
378   logmsg(RH_LOG_NORMAL, version);
379   syslog(LOG_INFO, version);
380
381   rh_task_init();
382
383   gnet_init();
384   main_loop = g_main_loop_new (NULL, FALSE);
385
386   /* XML RPC Server */
387   server = gnet_xmlrpc_server_new (addr, port);
388
389   if (!server) {
390     syslog(LOG_ERR, "Could not start XML-RPC server!");
391     rh_task_stopservice(map);
392     exit (EXIT_FAILURE);
393   }
394
395   gnet_xmlrpc_server_register_command (server, 
396                                        "startsession", 
397                                        do_startsession, 
398                                        map);
399
400   gnet_xmlrpc_server_register_command (server, 
401                                        "stopsession", 
402                                        do_stopsession, 
403                                        map);
404
405   gnet_xmlrpc_server_register_command (server, 
406                                        "getsessioninfo", 
407                                        do_getsessioninfo, 
408                                        map);
409
410   g_timeout_add_seconds (rh_config.polling_interval, polling, map);
411
412   rh_task_startservice(map);
413
414   g_main_loop_run(main_loop);
415
416   exit(EXIT_SUCCESS);
417 }