Add timemsg() for logging
[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 <time.h>
18 #include <signal.h>
19 #include <syslog.h>
20
21 #include "rahunasd.h"
22 #include "rh-xmlrpc-server.h"
23 #include "ipset-control.h"
24
25 /* Abstract functions */
26 int logmsg(int priority, const char *msg, ...); 
27 int getline(int fd, char *buf, size_t size);
28 int finish();
29 int ipset_flush();
30 int chk_set(struct rahunas_map *map);
31 int update_set(struct rahunas_map *map);
32
33 struct rahunas_map* rh_init_map();
34 int rh_init_members(struct rahunas_map *map);
35
36 int parse_set_header(const char *in, struct rahunas_map *map);
37 int parse_set_list(const char *in, struct rahunas_map *map);
38
39 int send_xmlrpc_stopacct(struct rahunas_map *map, uint32_t id);
40
41 /* Declaration */
42 struct rahunas_map *map = NULL;
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     logmsg(RH_LOG_DEBUG, "Could not convert IP: %s", ip);
53     return (-1);  
54   }
55
56   //logmsg(RH_LOG_DEBUG, "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   //logmsg(RH_LOG_DEBUG, "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 NULL;
71
72   sess_addr.s_addr = htonl((ntohl(map->first_ip) + id));
73
74   return inet_ntoa(sess_addr);
75 }
76
77 void *rh_malloc(size_t size)
78 {
79   void *p;
80   
81   if (size == 0)
82     return NULL;
83
84   if ((p = malloc(size)) == NULL) {
85     syslog(LOG_ERR, "RahuNASd: not enough memory");
86     exit(EXIT_FAILURE);
87   } 
88
89   return p;
90 }
91
92 void rh_free(void **data)
93 {
94   if (*data == NULL)
95     return;
96
97   free(*data);
98   *data = NULL;
99 }
100
101 int rh_openlog(const char *filename)
102 {
103   return open(filename, O_WRONLY | O_APPEND);
104 }
105
106 int rh_closelog(int fd)
107 {
108   if (close(fd) == 0)
109           return 1;
110         else
111           return 0;
112 }
113
114 int logmsg(int priority, const char *msg, ...) 
115 {
116   int n, size = 256;
117         va_list ap;
118         char *time_fmt = "%b %e %T";
119         char *p = NULL;
120   char *np = NULL;
121
122         if (priority < RH_LOG_LEVEL)
123           return 0;
124
125         if ((p = rh_malloc(size)) == NULL) {
126                 return (-1);
127         }
128
129         while (1) {
130     va_start(ap, msg);
131                 n = vsnprintf(p, size, msg, ap);
132                 va_end(ap);
133
134                 if (n > -1 && n < size)
135                   break;
136  
137     if (n > -1)
138                   size = n+1;
139                 else
140                   size *= 2;
141
142                 if ((np = realloc(p, size)) == NULL) {
143       free(p);
144       p = NULL;
145                         break;
146                 } else {
147       p = np;
148                 }
149         }
150
151         if (!p)
152           return (-1);
153
154         fprintf(stderr, "%s : %s\n", timemsg(), p);
155
156         rh_free(&p);
157         rh_free(&np);
158 }
159
160 void rh_shutdown(int sig)
161 {
162   ipset_flush();
163   finish();
164   rh_closelog(DEFAULT_LOG);
165 }
166
167 int ipset_flush()
168 {
169   logmsg(RH_LOG_NORMAL, "Flushing IPSET...");
170   return ctrl_flush();
171 }
172
173 int finish()
174 {
175   char *exitmsg = "Exit Gracefully";
176         struct rahunas_member *members = NULL;
177         int i;
178   int end;
179
180   if (map) {
181     if (map->members) {
182       members = map->members;
183       end = map->size;
184     } else {
185       end = 0;
186     }  
187
188           for (i=0; i < end; i++) {
189                           rh_free(&(members[i].username));
190                           rh_free(&(members[i].session_id));
191                 }
192
193                 rh_free(&(map->members));
194                 rh_free(&map);
195         }
196
197   logmsg(RH_LOG_NORMAL, exitmsg);
198         syslog(LOG_INFO, exitmsg);
199   return 0;
200 }
201
202 int getline(int fd, char *buf, size_t size)
203 {
204   char cbuf;
205         char *current;
206
207   if (!buf || fd < 0)
208     return 0;
209
210         current = buf;
211
212   while (read(fd, &cbuf, 1) > 0) {
213           *current = cbuf;
214           if (cbuf == '\n') {
215                   *current = '\0';
216                   break;
217                 } else if ((current - buf) < (size - 1)) {
218                   current++;
219           }
220         }
221
222         return (current - buf);
223 }
224
225
226 int parse_set_header(const char *in, struct rahunas_map *map)
227 {
228         in_addr_t first_ip;
229         in_addr_t last_ip;
230         char *ip_from_start = NULL;
231   char *ip_from_end = NULL;
232         char *ip_to_start = NULL;
233   char *ip_to_end = NULL;
234         char *ip_from = NULL;
235   char *ip_to = NULL;
236
237         if (!map || !in)
238           return (-1);
239
240   ip_from_start = strstr(in, "from:");
241         if (ip_from_start == NULL)
242           return (-1);
243
244         ip_to_start = strstr(in, "to:");
245         if (ip_to_start == NULL)
246           return (-1);
247
248   ip_from_start += 6;
249         ip_from_end = ip_to_start - 1;
250         ip_to_start += 4;
251         ip_to_end = in + strlen(in);
252
253         ip_from = strndup(ip_from_start, ip_from_end - ip_from_start);
254         ip_to   = strndup(ip_to_start, ip_to_end - ip_to_start);
255         logmsg(RH_LOG_NORMAL, "First IP: %s", ip_from);
256         logmsg(RH_LOG_NORMAL, "Last IP: %s", ip_to);
257
258         first_ip = inet_addr(ip_from);
259   memcpy(&map->first_ip, &first_ip, sizeof(in_addr_t));
260
261         last_ip = inet_addr(ip_to);
262   memcpy(&map->last_ip, &last_ip, sizeof(in_addr_t));
263
264         map->size = ntohl(map->last_ip) - ntohl(map->first_ip) + 1;
265
266         logmsg(RH_LOG_NORMAL, "Set Size: %lu", map->size);
267         free(ip_from);
268         free(ip_to);
269
270         return 0;
271 }
272
273 int parse_set_list(const char *in, struct rahunas_map *map)
274 {
275         char *sep = NULL;
276   char *sess_ip = NULL;
277         char *sess_idle_time = NULL;
278         uint32_t  id;
279
280         struct rahunas_member *members;
281
282         if (!map || !in)
283           return (-1);
284
285   if (!map->members)
286     return (-1);
287         
288         members = map->members;
289
290   // Found members
291   //logmsg(RH_LOG_DEBUG, in);
292         sep = strstr(in, ":");
293         sess_ip = strndup(in, sep - in);
294   if (sess_ip == NULL)
295     return (-1);
296         id = iptoid(map, sess_ip);
297  
298         sess_idle_time = strndup(sep + 1, strstr(in, "seconds") - sep - 1);
299   if (sess_idle_time == NULL)
300     return (-1);
301
302   members[id].flags = 1;
303   members[id].expired = atoi(sess_idle_time) < IDLE_THRESHOLD ? 0 : 1;
304
305   free(sess_ip);
306   free(sess_idle_time);
307         return id;
308 }
309
310 int chk_set(struct rahunas_map *map) 
311 {
312   struct rahunas_member *members = NULL;
313         unsigned short set_start = 0;
314         char  line[256];
315   int   pipefd[2];
316         pid_t cpid;
317         uint32_t  id;
318
319         members = map->members;
320
321         if (pipe(pipefd) == -1) {
322     syslog(LOG_ERR, "Could not create pipe");
323                 return -1;
324         }
325
326         cpid = fork();
327         if (cpid == 0) {
328           close(pipefd[0]);
329                 dup2(pipefd[1], STDOUT_FILENO);
330                 execlp("ipset", "ipset", "-nL", SET_NAME, NULL);
331                 return (-1);
332                 close(pipefd[1]);
333         }
334
335   wait(NULL);
336
337   close(pipefd[1]);
338
339         while (getline(pipefd[0], line, sizeof line) > 0) {
340
341     if (strstr(line, "Bindings:") != NULL)
342                   set_start = 0;
343
344                 if (set_start)
345                   parse_set_list(line, map);
346
347                 // Capture header
348     if (strstr(line, "Header:") != NULL)
349                   if (map != NULL && map->size == 0) {
350         parse_set_header(line, map);
351                                 break;
352                         }
353
354                 if (strstr(line, "Members:") != NULL)
355                   set_start = 1;
356         }
357         close(pipefd[0]); 
358
359         return 0;
360 }
361
362
363 int update_set(struct rahunas_map *map) 
364 {
365   struct rahunas_member *members = NULL;
366   unsigned int i;
367   unsigned short no_detail = 0;
368  
369   if (!map)
370           return (-1);
371         
372         members = map->members;
373
374         for (i=0; i < map->size; i++) {
375     no_detail = 0;
376     if (members[i].flags) {
377       if (!members[i].username || !members[i].session_id)
378         no_detail = 1;
379  
380     
381
382                   if (members[i].expired) {
383           if (!no_detail) {
384                     logmsg(RH_LOG_DEBUG, "IP %s, Client: Username %s, "
385                                          "Session-ID %s, Session-Start %d, "
386                                  "Expired %d",
387                                  idtoip(map, i),
388                                                                                                  members[i].username, 
389                                                                                                  members[i].session_id,
390                                                                                                  members[i].session_start,
391                                  members[i].expired);
392
393                               send_xmlrpc_stopacct(map, i);
394           }
395
396         if (ctrl_del_from_set(map, i) == 0) {
397           if (!no_detail) {
398             logmsg(RH_LOG_NORMAL, "Session Stop, User: %s, IP: %s, "
399                             "Session ID: %s",
400                             members[i].username, 
401                             idtoip(map, i), 
402                             members[i].session_id); 
403           }
404
405             rh_free(&(members[i].username));
406             rh_free(&(members[i].session_id));
407
408                             memset(&members[i], 0, sizeof(struct rahunas_member));
409                             logmsg(RH_LOG_NORMAL, "Client IP %s was removed!", idtoip(map,i));
410                           }
411                         }
412                 }
413         }
414   return 0;
415 }
416
417 int send_xmlrpc_stopacct(struct rahunas_map *map, uint32_t id) {
418   struct rahunas_member *members = NULL;
419   GNetXmlRpcClient *client = NULL;
420   gchar *reply  = NULL;
421         gchar *params = NULL;
422
423         if (!map)
424           return (-1);
425
426   if (!map->members)
427     return (-1);
428
429   if (id < 0 || id > (map->size - 1))
430     return (-1);
431         
432         members = map->members;
433
434   client = gnet_xmlrpc_client_new(XMLSERVICE_HOST, XMLSERVICE_URL, 
435                                         XMLSERVICE_PORT);
436
437   if (!client) {
438     logmsg(RH_LOG_ERROR, "Could not connect to XML-RPC service");
439     return (-1);
440   }
441         
442         params = g_strdup_printf("%s|%s|%d", 
443                                  members[id].username,
444                                                                                                          members[id].session_id,
445                                                                                                          members[id].session_start);
446
447   if (params == NULL)
448     return (-1);
449   
450   if (gnet_xmlrpc_client_call(client, "stopacct", params, &reply) == 0)
451     {
452       logmsg(RH_LOG_DEBUG, "stopacct reply = %s", reply);
453       g_free(reply);
454     }
455   else
456     logmsg(RH_LOG_DEBUG, "Failed executing stopacct!");
457         
458         g_free(params);
459
460   return 0;
461 }
462
463
464 struct rahunas_map* rh_init_map() {
465   struct rahunas_map *map = NULL;
466
467         map = (struct rahunas_map*)(rh_malloc(sizeof(struct rahunas_map)));
468
469   map->members = NULL;
470         map->size = 0;
471
472         return map;
473 }
474
475 int rh_init_members (struct rahunas_map* map)
476 {
477         struct rahunas_member *members = NULL;
478         int size;
479
480   if (!map)
481           return (-1);
482         
483         size = map->size == 0 ? MAX_MEMBERS : map->size;
484
485         members = 
486     (struct rahunas_member*)(rh_malloc(sizeof(struct rahunas_member)*size));
487
488         memset(members, 0, sizeof(struct rahunas_member)*size);
489
490         map->members = members;
491
492         return 0;
493 }
494
495 gboolean polling(gpointer data) {
496   struct rahunas_map *map = (struct rahunas_map *)data;
497         logmsg(RH_LOG_DEBUG, "Start polling!");
498         chk_set (map);
499         update_set (map);
500   return TRUE;
501 }
502
503 static void
504 watch_child(char *argv[])
505 {
506   char *prog = NULL;
507         int failcount = 0;
508         time_t start;
509         time_t stop;
510         int status;
511
512         int nullfd;
513
514   pid_t pid, sid;
515
516         if (*(argv[0]) == '(')
517           return;
518
519   signal(SIGINT, rh_shutdown);
520   signal(SIGTERM, rh_shutdown);
521   signal(SIGKILL, rh_shutdown);
522
523   pid = fork(); 
524         if (pid < 0) {
525           syslog(LOG_ALERT, "fork failed");
526                 exit(EXIT_FAILURE);
527         } else if (pid > 0) {
528           /* parent */
529           exit(EXIT_SUCCESS);
530         }
531
532   /* Change the file mode mask */
533   umask(0);
534
535
536
537         if ((sid = setsid()) < 0)
538           syslog(LOG_ALERT, "setsid failed");
539
540         if ((chdir("/")) < 0) {
541     exit(EXIT_FAILURE);
542   }
543     
544   /* Close out the standard file descriptors */
545         close(STDIN_FILENO);
546         close(STDOUT_FILENO);
547         close(STDERR_FILENO);
548
549   while(1) {
550
551           if ((pid = fork()) == 0) {
552       /* child */
553       prog = strdup(argv[0]);
554             argv[0] = strdup("(rahunasd)");
555                   execvp(prog, argv);
556                   syslog(LOG_ALERT, "execvp failed");
557                 }
558   
559     syslog(LOG_NOTICE, "RahuNASd Parent: child process %d started", pid);   
560
561     time(&start);
562           /* parent */
563         pid = wait3(&status, 0, NULL);
564         time(&stop);
565   
566         if (WIFEXITED(status)) {
567           syslog(LOG_NOTICE,
568                          "RahuNASd Parent: child process %d exited with status %d",
569                                                  pid, WEXITSTATUS(status));
570         } else if (WIFSIGNALED(status)) {
571       syslog(LOG_NOTICE,
572                          "RahuNASd Parent: child process %d exited due to signal %d",
573                                                  pid, WTERMSIG(status));
574         } else {
575       syslog(LOG_NOTICE, "RahuNASd Parent: child process %d exited", pid);
576         }
577   
578                 if (stop - start < 10)
579                   failcount++;
580                 else
581                   failcount = 0;
582   
583                 if (failcount == 5) {
584                   syslog(LOG_ALERT, "Exiting due to repeated, frequent failures");
585                         exit(EXIT_FAILURE);
586                 }
587   
588   
589         if (WIFEXITED(status))
590           if (WEXITSTATUS(status) == 0)
591                   exit(EXIT_SUCCESS);
592         
593         if (WIFSIGNALED(status)) {
594       switch (WTERMSIG(status)) {
595                   case SIGKILL:
596                           exit(0);
597                                 break;
598                         
599                         case SIGINT:
600                         case SIGTERM:
601                           syslog(LOG_ALERT, "Exiting due to unexpected forced shutdown");
602                                 exit(EXIT_FAILURE);
603                                 break;
604                         
605                         default:
606                           break;
607                 }
608         }
609         
610         sleep(3);
611   }
612 }
613
614 int main(int argc, char **argv) 
615 {
616         gchar* addr = "localhost";
617         int port    = 8123;
618   int fd_log;
619
620         char line[256];
621         char version[256];
622
623         GNetXmlRpcServer *server = NULL;
624         GMainLoop* main_loop     = NULL;
625
626         watch_child(argv);
627   
628   gnet_init();
629   main_loop = g_main_loop_new (NULL, FALSE);
630
631   sprintf(version, "Starting %s - Version %s", PROGRAM, VERSION);
632
633   /* Open log file */
634         if ((fd_log = rh_openlog(DEFAULT_LOG)) == (-1)) {
635     syslog(LOG_ERR, "Could not open log file %s", DEFAULT_LOG);
636     exit(EXIT_FAILURE);
637   }
638
639   dup2(fd_log, STDERR_FILENO);
640
641         logmsg(RH_LOG_NORMAL, version);
642   syslog(LOG_INFO, version);
643
644   map = rh_init_map();
645   chk_set(map);
646   rh_init_members(map);
647
648   /* XML RPC Server */
649         server = gnet_xmlrpc_server_new (addr, port);
650
651         if (!server) {
652     syslog(LOG_ERR, "Could not start XML-RPC server!");
653     ipset_flush();
654     finish(); 
655           exit (EXIT_FAILURE);
656         }
657
658         gnet_xmlrpc_server_register_command (server, 
659                                              "startsession", 
660                                                                                                                                            do_startsession, 
661                                                                                                                                                  map);
662
663         gnet_xmlrpc_server_register_command (server, 
664                                              "stopsession", 
665                                                                                                                                            do_stopsession, 
666                                                                                                                                                  map);
667
668   gnet_xmlrpc_server_register_command (server, 
669                                              "getsessioninfo", 
670                                                                                                                                            do_getsessioninfo, 
671                                                                                                                                                  map);
672
673   g_timeout_add_seconds (POLLING, polling, map);
674
675         g_main_loop_run(main_loop);
676
677         exit(EXIT_SUCCESS);
678         return 0;
679 }