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