another round of Makefile cleanup
[olsrd.git] / lib / powerinfo / src / olsrd_power.c
1
2 /*
3  * Copyright (c) 2004, Andreas T√łnnesen(andreto-at-olsr.org)
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met:
9  *
10  * * Redistributions of source code must retain the above copyright notice, 
11  *   this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright notice, 
13  *   this list of conditions and the following disclaimer in the documentation 
14  *   and/or other materials provided with the distribution.
15  * * Neither the name of the UniK olsr daemon nor the names of its contributors 
16  *   may be used to endorse or promote products derived from this software 
17  *   without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
22  * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
23  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
26  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
27  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
28  * OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  */
31
32 /* $Id: olsrd_power.c,v 1.10 2005/05/25 13:50:22 br1 Exp $ */
33
34 /*
35  * Dynamic linked library example for UniK OLSRd
36  */
37
38 #include "olsrd_power.h"
39 #include <stdio.h>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43
44 #ifdef OS
45 #undef OS
46 #endif
47
48 #ifdef WIN32
49 #define close(x) closesocket(x)
50 #define OS "Windows"
51 #endif
52 #ifdef linux
53 #define OS "GNU/Linux"
54 #endif
55 #ifdef __FreeBSD__
56 #define OS "FreeBSD"
57 #endif
58
59 #ifndef OS
60 #define OS "Undefined"
61 #endif
62
63 int ipc_socket;
64 int ipc_open;
65 int ipc_connection;
66 int ipc_connected;
67
68 int
69 ipc_send(char *, int);
70
71
72 /**
73  *Do initialization here
74  *
75  *This function is called by the my_init
76  *function in uolsrd_plugin.c
77  */
78 int
79 olsr_plugin_init()
80 {
81   int i;
82   struct olsr_apm_info apm_info;
83
84   if(ipversion != AF_INET)
85     {
86       fprintf(stderr, "This plugin only supports IPv4!\n");
87       return 0;
88     }
89   /* Initial IPC value */
90   ipc_open = 0;
91
92   /* Init list */
93   for(i = 0; i < HASHSIZE; i++)
94     {
95       list[i].next = &list[i];
96       list[i].prev = &list[i];
97     }
98
99   if(apm_read(&apm_info) < 0)
100     {
101       has_apm = 0;
102       olsr_printf(1, "No APM info avalible! This node will not generate powermessages!\n\n");
103     }
104   else
105     {
106       olsr_printf(1, "Node has APM info!\n");
107       has_apm = 1;
108     }
109
110   /* Register functions with olsrd */
111   olsr_parser_add_function(&olsr_parser, PARSER_TYPE, 1);
112
113   olsr_register_timeout_function(&olsr_timeout);
114
115   olsr_register_scheduler_event(&olsr_event, NULL, EMISSION_INTERVAL, 0, NULL);
116
117   return 1;
118 }
119
120 int
121 plugin_ipc_init()
122 {
123   struct sockaddr_in sin;
124   olsr_u32_t yes = 1;
125
126   /* Init ipc socket */
127   if ((ipc_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
128     {
129       perror("IPC socket");
130       return 0;
131     }
132   else
133     {
134       if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) < 0) 
135       {
136         perror("SO_REUSEADDR failed");
137         return 0;
138       }
139
140 #ifdef __FreeBSD__
141       if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *)&yes, sizeof(yes)) < 0) 
142       {
143         perror("SO_REUSEADDR failed");
144         return 0;
145       }
146 #endif
147
148       /* Bind the socket */
149       
150       /* complete the socket structure */
151       memset(&sin, 0, sizeof(sin));
152       sin.sin_family = AF_INET;
153       sin.sin_addr.s_addr = INADDR_ANY;
154       sin.sin_port = htons(IPC_PORT);
155       
156       /* bind the socket to the port number */
157       if (bind(ipc_socket, (struct sockaddr *) &sin, sizeof(sin)) == -1) 
158         {
159           perror("IPC bind");
160           return 0;
161         }
162       
163       /* show that we are willing to listen */
164       if (listen(ipc_socket, 1) == -1) 
165         {
166           perror("IPC listen");
167           return 0;
168         }
169
170       /* Register with olsrd */
171       add_olsr_socket(ipc_socket, &ipc_action);
172
173     }
174
175   ipc_open = 1;
176   return 1;
177 }
178
179 void
180 ipc_action(int fd)
181 {
182   struct sockaddr_in pin;
183   socklen_t addrlen;
184   char *addr;  
185
186   addrlen = sizeof(struct sockaddr_in);
187
188   if ((ipc_connection = accept(ipc_socket, (struct sockaddr *)  &pin, &addrlen)) == -1)
189     {
190       perror("IPC accept");
191       exit(1);
192     }
193   else
194     {
195       addr = inet_ntoa(pin.sin_addr);
196       if(ntohl(pin.sin_addr.s_addr) != INADDR_LOOPBACK)
197         {
198           olsr_printf(1, "Front end-connection from foregin host(%s) not allowed!\n", addr);
199           close(ipc_connection);
200           return;
201         }
202       else
203         {
204           ipc_connected = 1;
205           olsr_printf(1, "POWER: Connection from %s\n",addr);
206         }
207     }
208
209 }
210
211 /*
212  * destructor - called at unload
213  */
214 void
215 olsr_plugin_exit()
216 {
217   if(ipc_open)
218     close(ipc_socket);
219 }
220
221
222
223 /* Mulitpurpose funtion */
224 int
225 plugin_io(int cmd, void *data, size_t size)
226 {
227
228   switch(cmd)
229     {
230     default:
231       return 0;
232     }
233   
234   return 1;
235 }
236
237
238
239 /**
240  *A timeoutfunction called every time
241  *the scheduler is polled
242  */
243 void
244 olsr_timeout()
245 {
246   //printf("PLUGIN Timeout!\n");
247   struct pwrentry *tmp_list;
248   struct pwrentry *entry_to_delete;
249   int index;
250
251
252   for(index=0;index<HASHSIZE;index++)
253     {
254       tmp_list = list[index].next;
255       /*Traverse MID list*/
256       while(tmp_list != &list[index])
257         {
258           /*Check if the entry is timed out*/
259           if(olsr_timed_out(&tmp_list->timer))
260             {
261               entry_to_delete = tmp_list;
262               tmp_list = tmp_list->next;
263               olsr_printf(1, "POWER info for %s timed out.. deleting it\n", 
264                           olsr_ip_to_string(&entry_to_delete->originator));
265               /* Dequeue */
266               entry_to_delete->prev->next = entry_to_delete->next;
267               entry_to_delete->next->prev = entry_to_delete->prev;
268
269               /* Delete */
270               free(entry_to_delete);
271             }
272           else
273               tmp_list = tmp_list->next;
274         }
275     }
276
277   return;
278 }
279
280
281 /**
282  *Scheduled event
283  */
284 void
285 olsr_event(void *foo)
286 {
287   union olsr_message *message = (union olsr_message*)buffer;
288   struct interface *ifn;
289
290   /* If we can't produce power info we do nothing */ 
291   if(!has_apm)
292     return;
293
294   olsr_printf(3, "PLUG-IN: Generating package - ");
295
296   /* looping trough interfaces */
297   for (ifn = ifs; ifn ; ifn = ifn->int_next) 
298     {
299       olsr_printf(3, "[%s]  ", ifn->int_name);
300       /* Fill message */
301       if(ipversion == AF_INET)
302         {
303           /* IPv4 */
304           message->v4.olsr_msgtype = MESSAGE_TYPE;
305           message->v4.olsr_vtime = double_to_me(7.5);
306           message->v4.olsr_msgsize = htons(sizeof(struct olsrmsg));
307           memcpy(&message->v4.originator, main_addr, ipsize);
308           message->v4.ttl = MAX_TTL;
309           message->v4.hopcnt = 0;
310           message->v4.seqno = htons(get_msg_seqno());
311           
312           get_powerstatus(&message->v4.msg);
313
314           if(net_outbuffer_push(ifn, (olsr_u8_t *)message, sizeof(struct olsrmsg)) != sizeof(struct olsrmsg))
315             {
316
317               /* Send data and try again */
318               net_output(ifn);
319               if(net_outbuffer_push(ifn, (olsr_u8_t *)message, sizeof(struct olsrmsg)) != sizeof(struct olsrmsg))
320                 olsr_printf(1, "Powerplugin: could not write to buffer for interface: %s\n", ifn->int_name);
321             }
322
323         }
324       else
325         {
326           /* IPv6 */
327           message->v6.olsr_msgtype = MESSAGE_TYPE;
328           message->v6.olsr_vtime = double_to_me(7.5);
329           message->v6.olsr_msgsize = htons(sizeof(struct olsrmsg));
330           memcpy(&message->v6.originator, main_addr, ipsize);
331           message->v6.ttl = MAX_TTL;
332           message->v6.hopcnt = 0;
333           message->v6.seqno = htons(get_msg_seqno());
334           
335           get_powerstatus(&message->v6.msg);
336
337           if(net_outbuffer_push(ifn, (olsr_u8_t *)message, sizeof(struct olsrmsg6)) != sizeof(struct olsrmsg6))
338             {
339               /* Send data and try again */
340               net_output(ifn);
341               if(net_outbuffer_push(ifn, (olsr_u8_t *)message, sizeof(struct olsrmsg6)) != sizeof(struct olsrmsg6))
342                 olsr_printf(1, "Powerplugin: could not write to buffer for interface: %s\n", ifn->int_name);
343             }
344
345         }
346
347     }
348   olsr_printf(2, "\n");
349
350   /* Try to set up IPC socket if not already up */
351   if(!ipc_open)
352     plugin_ipc_init();
353
354   print_power_table();
355
356   return;
357 }
358
359
360
361 void
362 olsr_parser(union olsr_message *m, struct interface *in_if, union olsr_ip_addr *in_addr)
363 {
364   struct  powermsg *message;
365   union olsr_ip_addr originator;
366   double vtime;
367
368   /* Fetch the originator of the messsage */
369   memcpy(&originator, &m->v4.originator, ipsize);
370
371   /* Fetch the message based on IP version */
372   if(ipversion == AF_INET)
373     {
374       message = &m->v4.msg;
375       vtime = ME_TO_DOUBLE(m->v4.olsr_vtime);
376     }
377   else
378     {
379       message = &m->v6.msg;
380       vtime = ME_TO_DOUBLE(m->v6.olsr_vtime);
381     }
382
383   /* Check if message originated from this node */
384   if(memcmp(&originator, main_addr, ipsize) == 0)
385     /* If so - back off */
386     return;
387
388   /* Check that the neighbor this message was received
389      from is symmetric */
390   if(check_neighbor_link(in_addr) != SYM_LINK)
391     {
392       /* If not symmetric - back off */
393       olsr_printf(3, "Received POWER from NON SYM neighbor %s\n", olsr_ip_to_string(in_addr));
394       return;
395     }
396
397   /* Check if this message has been processed before
398    * Remeber that this also registeres the message as
399    * processed if nessecary
400    */
401   if(!check_dup_proc(&originator,
402                      ntohs(m->v4.seqno))) /* REMEMBER NTOHS!! */
403     {
404       /* If so - do not process */
405       goto forward;
406     }
407
408   /* Process */
409
410   olsr_printf(3, "POWER PLUG-IN: Processing PWR from %s seqno: %d\n",
411               olsr_ip_to_string(&originator),
412               ntohs(m->v4.seqno));
413
414   /* Call a function that updates the database entry */
415   update_power_entry(&originator, message, vtime);
416
417
418  forward:
419   /* Forward the message if nessecary
420    * default_fwd does all the work for us!
421    */
422   default_fwd(m,
423               &originator,
424               ntohs(m->v4.seqno), /* IMPORTANT!!! */
425               in_if,
426               in_addr);
427
428 }
429
430
431
432 /**
433  *Update or register a new power entry
434  */
435 int
436 update_power_entry(union olsr_ip_addr *originator, struct powermsg *message, double vtime)
437 {
438   int hash;
439   struct pwrentry *entry;
440
441   hash = olsr_hashing(originator);
442
443   /* Check for the entry */
444   for(entry = list[hash].next;
445       entry != &list[hash];
446       entry = entry->next)
447     {
448       if(memcmp(originator, &entry->originator, ipsize) == 0)
449         {
450           entry->source_type = message->source_type;
451           entry->percentage = message->percentage;
452           entry->time_left = message->time_left;
453
454           olsr_get_timestamp(vtime * 1000, &entry->timer);
455
456           return 0;
457         }
458     }
459
460   olsr_printf(1, "New power entry %s: ", olsr_ip_to_string(originator));
461
462   if(message->source_type == OLSR_BATTERY_POWERED)
463     olsr_printf(1, "BATTERY P: %d%% T: %d mins\n",
464            message->percentage,
465            message->time_left);
466   else
467     olsr_printf(1, "AC\n");
468
469   entry = olsr_malloc(sizeof(struct pwrentry), "POWERPLUGIN: new power entry");
470      
471   /* Fill struct */
472
473   memcpy(&entry->originator, originator, ipsize);
474
475   entry->source_type = message->source_type;
476   entry->percentage = message->percentage;
477   entry->time_left = message->time_left;
478   
479   olsr_get_timestamp(vtime * 1000, &entry->timer);
480
481   /* Queue */
482   entry->next = list[hash].next->prev;
483   entry->prev = &list[hash];
484   list[hash].next->prev = entry;
485   list[hash].next = entry;
486
487   return 1;
488 }
489
490
491 /**
492  *Print all registered power entries
493  */
494
495 void
496 print_power_table()
497 {
498   int hash;
499   struct pwrentry *entry;
500   char buf[200];
501
502   if(!ipc_connection)
503     return;
504
505   ipc_send("--POWERTABLE--\n", 15);
506
507   for(hash = 0; hash < HASHSIZE; hash++)
508     /* Check for the entry */
509     for(entry = list[hash].next;
510         entry != &list[hash];
511         entry = entry->next)
512       {
513         sprintf(buf, "[%s]: ", olsr_ip_to_string(&entry->originator));
514         ipc_send(buf, strlen(buf));
515
516         if(entry->source_type == OLSR_BATTERY_POWERED)
517           {
518             sprintf(buf,
519                     "BATTERY P: %d%% T: %d mins\n",
520                     entry->percentage,
521                     entry->time_left);
522             ipc_send(buf, strlen(buf));
523           }
524         else
525           ipc_send("AC\n", 3);
526       }
527
528   ipc_send("--------------\n", 15);
529
530 }
531
532
533
534 int
535 ipc_send(char *data, int size)
536 {
537   if(!ipc_connected)
538     return 0;
539
540 #ifdef __FreeBSD__
541   if (send(ipc_connection, data, size, 0) < 0) 
542 #else
543   if (send(ipc_connection, data, size, MSG_NOSIGNAL) < 0) 
544 #endif
545     {
546       //perror("send - IPC");
547       olsr_printf(1, "(OUTPUT)IPC connection lost!\n");
548       close(ipc_connection);
549       //use_ipc = 0;
550       ipc_connected = 0;
551       return -1;
552     }
553
554   return 1;
555 }
556
557 /**
558  *Fill a powermsg struct with power data
559  */
560 int
561 get_powerstatus(struct powermsg *msg)
562 {
563   struct olsr_apm_info apm_info;
564   
565   if(apm_read(&apm_info) < 0)
566     {
567       has_apm = 0;
568       olsr_printf(1, "No APM info avalible! This node will not generate powermessages!\n\n");
569     }
570
571   if(apm_info.ac_line_status)
572     {
573       msg->source_type = OLSR_AC_POWERED;
574       msg->percentage = 0;
575       msg->time_left = 0;
576     }
577   else
578     {
579       msg->source_type = OLSR_BATTERY_POWERED;
580       msg->percentage = apm_info.battery_percentage;
581       msg->time_left = apm_info.battery_time_left;
582     }
583
584   return 1;
585 }
586
587
588
589
590 /*************************************************************
591  *                 TOOLS DERIVED FROM OLSRD                  *
592  *************************************************************/
593
594
595 /**
596  *Hashing function. Creates a key based on
597  *an 32-bit address.
598  *@param address the address to hash
599  *@return the hash(a value in the 0-31 range)
600  */
601 olsr_u32_t
602 olsr_hashing(union olsr_ip_addr *address)
603 {
604   olsr_u32_t hash;
605   char *tmp;
606
607   if(ipversion == AF_INET)
608     /* IPv4 */  
609     hash = (ntohl(address->v4));
610   else
611     {
612       /* IPv6 */
613       tmp = (char *) &address->v6;
614       hash = (ntohl(*tmp));
615     }
616
617   //hash &= 0x7fffffff; 
618   hash &= HASHMASK;
619
620   return hash;
621 }
622
623
624
625 /**
626  *Checks if a timer has times out. That means
627  *if it is smaller than present time.
628  *@param timer the timeval struct to evaluate
629  *@return positive if the timer has not timed out,
630  *0 if it matches with present time and negative
631  *if it is timed out.
632  */
633 int
634 olsr_timed_out(struct timeval *timer)
635 {
636   return(timercmp(timer, now, <));
637 }
638
639
640
641 /**
642  *Initiates a "timer", wich is a timeval structure,
643  *with the value given in time_value.
644  *@param time_value the value to initialize the timer with
645  *@param hold_timer the timer itself
646  *@return nada
647  */
648 void
649 olsr_init_timer(olsr_u32_t time_value, struct timeval *hold_timer)
650
651   olsr_u16_t  time_value_sec;
652   olsr_u16_t  time_value_msec;
653
654   time_value_sec = time_value/1000;
655   time_value_msec = time_value-(time_value_sec*1000);
656
657   hold_timer->tv_sec = time_value_sec;
658   hold_timer->tv_usec = time_value_msec*1000;   
659 }
660
661
662
663
664
665 /**
666  *Generaties a timestamp a certain number of milliseconds
667  *into the future.
668  *
669  *@param time_value how many milliseconds from now
670  *@param hold_timer the timer itself
671  *@return nada
672  */
673 void
674 olsr_get_timestamp(olsr_u32_t delay, struct timeval *hold_timer)
675
676   olsr_u16_t  time_value_sec;
677   olsr_u16_t  time_value_msec;
678
679   time_value_sec = delay/1000;
680   time_value_msec= delay - (delay*1000);
681
682   hold_timer->tv_sec = now->tv_sec + time_value_sec;
683   hold_timer->tv_usec = now->tv_usec + (time_value_msec*1000);   
684 }
685
686
687 /**
688  *Converts a olsr_ip_addr to a string
689  *Goes for both IPv4 and IPv6
690  *
691  *NON REENTRANT! If you need to use this
692  *function twice in e.g. the same printf
693  *it will not work.
694  *You must use it in different calls e.g.
695  *two different printfs
696  *
697  *@param the IP to convert
698  *@return a pointer to a static string buffer
699  *representing the address in "dots and numbers"
700  *
701  */
702 char *
703 olsr_ip_to_string(union olsr_ip_addr *addr)
704 {
705
706   char *ret;
707   struct in_addr in;
708   
709   if(ipversion == AF_INET)
710     {
711       in.s_addr=addr->v4;
712       ret = inet_ntoa(in);
713     }
714   else
715     {
716       /* IPv6 */
717       ret = (char *)inet_ntop(AF_INET6, &addr->v6, ipv6_buf, sizeof(ipv6_buf));
718     }
719
720   return ret;
721 }
722
723