9fe89effe23e58d0492c4fb05c37c0538212039c
[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 SIGINT:
156     case SIGTERM:
157     case SIGKILL:
158       if (pid == 0) {
159         rh_exit();
160         exit(EXIT_SUCCESS);
161       }
162
163       if (pid != 0) {
164         syslog(LOG_NOTICE, "Kill Child PID %d", pid);
165         kill(pid, SIGTERM);
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
266   pid = fork(); 
267         if (pid < 0) {
268           syslog(LOG_ALERT, "fork failed");
269                 exit(EXIT_FAILURE);
270         } else if (pid > 0) {
271           /* parent */
272     pidfd = open(DEFAULT_PID, O_WRONLY | O_TRUNC | O_CREAT);
273     if (pidfd) {
274       dup2(pidfd, STDOUT_FILENO);
275       fprintf(stdout, "%d\n", pid);
276       close(pidfd);
277     }
278           exit(EXIT_SUCCESS);
279         }
280
281   /* Change the file mode mask */
282   umask(0);
283
284         if ((sid = setsid()) < 0)
285           syslog(LOG_ALERT, "setsid failed");
286
287         if ((chdir("/")) < 0) {
288     exit(EXIT_FAILURE);
289   }
290     
291   /* Close out the standard file descriptors */
292         close(STDIN_FILENO);
293         close(STDOUT_FILENO);
294         close(STDERR_FILENO);
295
296
297   while(1) {
298
299           if ((pid = fork()) == 0) {
300       /* child */
301       prog = strdup(argv[0]);
302             argv[0] = strdup("(rahunasd)");
303                   execvp(prog, argv);
304                   syslog(LOG_ALERT, "execvp failed");
305                 }
306   
307     syslog(LOG_NOTICE, "RahuNASd Parent: child process %d started", pid);   
308
309     time(&start);
310           /* parent */
311         pid = waitpid(-1, &status, 0);
312         time(&stop);
313
314         if (WIFEXITED(status)) {
315           syslog(LOG_NOTICE,
316                          "RahuNASd Parent: child process %d exited with status %d",
317                                                  pid, WEXITSTATUS(status));
318         } else if (WIFSIGNALED(status)) {
319       syslog(LOG_NOTICE,
320                          "RahuNASd Parent: child process %d exited due to signal %d",
321                                                  pid, WTERMSIG(status));
322         } else {
323       syslog(LOG_NOTICE, "RahuNASd Parent: child process %d exited", pid);
324         }
325   
326     if (stop - start < 10)
327       failcount++;
328     else
329       failcount = 0;
330   
331     if (failcount == 5) {
332       syslog(LOG_ALERT, "Exiting due to repeated, frequent failures");
333       exit(EXIT_FAILURE);
334     }
335   
336   
337         if (WIFEXITED(status))
338           if (WEXITSTATUS(status) == 0)
339                   exit(EXIT_SUCCESS);
340         
341         sleep(3);
342   }
343 }
344
345 int main(int argc, char **argv) 
346 {
347         gchar* addr = "localhost";
348         int port    = 8123;
349   int fd_log;
350
351         char line[256];
352         char version[256];
353
354         GNetXmlRpcServer *server = NULL;
355         GMainLoop* main_loop     = NULL;
356
357    
358
359   signal(SIGTERM, rh_sighandler);
360   signal(SIGKILL, rh_sighandler);
361
362         watch_child(argv);
363
364   /* Get configuration from config file */
365   if (config_init(&rh_config) < 0) {
366           syslog(LOG_ERR, "Could not open config file %s", CONFIG_FILE);
367     exit(EXIT_FAILURE);
368   }
369
370   /* Open log file */
371         if ((fd_log = rh_openlog(rh_config.log_file)) < 0) {
372     syslog(LOG_ERR, "Could not open log file %s", rh_config.log_file);
373     exit(EXIT_FAILURE);
374   }
375
376   dup2(fd_log, STDERR_FILENO);
377
378   sprintf(version, "Starting %s - Version %s", PROGRAM, RAHUNAS_VERSION);
379         logmsg(RH_LOG_NORMAL, version);
380   syslog(LOG_INFO, version);
381
382   rh_task_init();
383
384   gnet_init();
385   main_loop = g_main_loop_new (NULL, FALSE);
386
387   /* XML RPC Server */
388         server = gnet_xmlrpc_server_new (addr, port);
389
390         if (!server) {
391     syslog(LOG_ERR, "Could not start XML-RPC server!");
392     rh_task_stopservice(map);
393           exit (EXIT_FAILURE);
394         }
395
396         gnet_xmlrpc_server_register_command (server, 
397                                              "startsession", 
398                                                                                                                                            do_startsession, 
399                                                                                                                                                  map);
400
401         gnet_xmlrpc_server_register_command (server, 
402                                              "stopsession", 
403                                                                                                                                            do_stopsession, 
404                                                                                                                                                  map);
405
406   gnet_xmlrpc_server_register_command (server, 
407                                              "getsessioninfo", 
408                                                                                                                                            do_getsessioninfo, 
409                                                                                                                                                  map);
410
411   g_timeout_add_seconds (rh_config.polling_interval, polling, map);
412
413   rh_task_startservice(map);
414
415         g_main_loop_run(main_loop);
416
417         exit(EXIT_SUCCESS);
418 }