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