changes
[oweals/gnunet.git] / src / sysmon / gnunet-service-sysmon.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file sysmon/gnunet-service-sysmon.c
23  * @brief system monitoring service
24  * @author Matthias Wachs
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_statistics_service.h"
29
30 enum operation
31 {
32   o_internal,
33   o_ligbtop,
34   o_command
35 };
36
37
38 enum type
39 {
40   t_static,
41   t_continous
42 };
43
44 enum value
45 {
46   v_numeric,
47   v_string
48 };
49
50 /**
51  * A system property to monitor
52  */
53 struct SysmonProperty
54 {
55   /**
56    * Next element in in the DLL
57    */
58   struct SysmonProperty *next;
59
60   /**
61    * Previous element in in the DLL
62    */
63   struct SysmonProperty *prev;
64
65         struct SysmonGtopProcProperty *gtop_proc_head;
66         struct SysmonGtopProcProperty *gtop_proc_tail;
67
68   /**
69    * Description used for statistics valuesd
70    */
71   char * desc;
72
73   /**
74    * Type
75    */
76   int type;
77
78   /**
79    * Value type
80    */
81   int value_type;
82
83   /**
84    * Execution interval
85    */
86   struct GNUNET_TIME_Relative interval;
87
88   /**
89    * Command
90    */
91   char * cmd;
92
93   /**
94    * Command arguments
95    */
96   char * cmd_args;
97
98   /**
99    * Command execution handle
100    */
101   void * cmd_exec_handle;
102
103   /**
104    * Numerical value
105    */
106   uint64_t num_val;
107
108   /**
109    * String value
110    */
111   char * str_val;
112
113   /**
114    * Task id
115    */
116   GNUNET_SCHEDULER_TaskIdentifier task_id;
117
118   /**
119    * Task handle
120    */
121   GNUNET_SCHEDULER_Task task;
122
123 };
124
125 /**
126  * A system property to monitor
127  */
128 struct SysmonGtopProcProperty
129 {
130         struct SysmonGtopProcProperty *prev;
131         struct SysmonGtopProcProperty *next;
132         char * binary;
133 };
134
135
136 /**
137  * Final status code.
138  */
139 static int ret;
140
141 /**
142  * Configuration handle
143  */
144 const struct GNUNET_CONFIGURATION_Handle *cfg;
145
146
147 /**
148  * Statistics handle
149  */
150 struct GNUNET_STATISTICS_Handle *stats;
151
152 /**
153  * Shutdown task
154  */
155 GNUNET_SCHEDULER_TaskIdentifier end_task;
156
157 struct SysmonProperty *sp_head;
158 struct SysmonProperty *sp_tail;
159
160 struct SysmonGtopProcProperty *pp_head;
161 struct SysmonGtopProcProperty *pp_tail;
162
163 static void
164 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
165 {
166   struct SysmonProperty *sp;
167   struct SysmonProperty *next;
168
169   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sysdaemon stopping ... \n");
170
171   end_task = GNUNET_SCHEDULER_NO_TASK;
172
173   if (NULL != stats)
174   {
175     GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
176     stats = NULL;
177   }
178
179   next = sp_head;
180   while (NULL != (sp = next))
181   {
182       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stopping `%s' \n", sp->desc);
183       GNUNET_CONTAINER_DLL_remove (sp_head, sp_tail, sp);
184       next = sp->next;
185       if (GNUNET_SCHEDULER_NO_TASK != sp->task_id)
186       {
187         GNUNET_SCHEDULER_cancel (sp->task_id);
188         sp->task_id = GNUNET_SCHEDULER_NO_TASK;
189       }
190       GNUNET_free_non_null (sp->cmd);
191       GNUNET_free_non_null (sp->cmd_args);
192       GNUNET_free (sp->desc);
193       GNUNET_free (sp);
194   }
195
196 }
197
198 static void
199 shutdown_now (void)
200 {
201   if (GNUNET_SCHEDULER_NO_TASK != end_task)
202     GNUNET_SCHEDULER_cancel (end_task);
203   GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
204 }
205
206 static void
207 to_lower_str (char * str)
208 {
209   int c;
210   for (c = 0; c <= strlen (str); c++)
211     str[c] = tolower(str[c]);
212 }
213
214 static int
215 put_property (struct SysmonProperty *sp)
216 {
217   if (v_numeric ==sp->value_type)
218   {
219                 fprintf (stderr, "%s : %s : %llu\n",
220                                 GNUNET_STRINGS_absolute_time_to_string(GNUNET_TIME_absolute_get()),
221                                 sp->desc, (unsigned long long) sp->num_val);
222   }
223   else if (v_string ==sp->value_type)
224   {
225                 fprintf (stderr, "%s : %s : %s\n",
226                                 GNUNET_STRINGS_absolute_time_to_string(GNUNET_TIME_absolute_get()),
227                                 sp->desc, sp->str_val);
228   }
229   else
230   {
231     GNUNET_break (0);
232     return GNUNET_SYSERR;
233   }
234   return GNUNET_OK;
235 }
236
237 static void
238 update_uptime (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
239 {
240         struct SysmonProperty *sp = cls;
241         static int first_run = GNUNET_YES;
242
243         if (GNUNET_YES == first_run)
244                         first_run = GNUNET_NO;
245         else
246                         sp->num_val += sp->interval.rel_value / 1000;
247
248   put_property (sp);
249 }
250
251 static void
252 exec_cmd_proc (void *cls, const char *line)
253 {
254   struct SysmonProperty *sp = cls;
255   unsigned long long tmp;
256   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Property output: `%s'\n", line);
257   if (NULL == line)
258   {
259       GNUNET_OS_command_stop (sp->cmd_exec_handle);
260       sp->cmd_exec_handle = NULL;
261       return;
262   }
263
264   switch (sp->value_type) {
265     case v_numeric:
266       if (1 != sscanf (line, "%llu", &tmp))
267       {
268         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Command output was not a numerical value: `%s'\n", line);
269         return;
270       }
271       break;
272     case v_string:
273       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "NOT IMPLEMENTED\n");
274       break;
275     default:
276       break;
277
278   }
279   sp->num_val = tmp;
280
281   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Property output: `%s'\n", line);
282   put_property (sp);
283
284
285 }
286
287 static void
288 exec_cmd (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
289 {
290   struct SysmonProperty *sp = cls;
291   GNUNET_assert (NULL != sp->cmd);
292
293   if (NULL != sp->cmd_exec_handle)
294   {
295     GNUNET_OS_command_stop (sp->cmd_exec_handle);
296     sp->cmd_exec_handle = NULL;
297     GNUNET_break (0);
298   }
299   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Property `%s': command `%s' `%s'\n", sp->desc, sp->cmd, sp->cmd_args);
300   if (NULL == (sp->cmd_exec_handle = GNUNET_OS_command_run (&exec_cmd_proc, sp,
301       GNUNET_TIME_UNIT_SECONDS,
302       sp->cmd, sp->cmd,
303       sp->cmd_args,
304       NULL)))
305     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Property `%s': command `%s' failed\n", sp->desc, sp->cmd);
306 }
307
308 static void
309 exec_gtop_proc_mon (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
310 {
311   //struct SysmonGtopProcProperty *sp = cls;
312 }
313
314 static void
315 load_property (void *cls,
316                const char *section)
317 {
318   struct GNUNET_CONFIGURATION_Handle *properties = cls;
319   struct SysmonProperty *sp;
320   char *tmp;
321
322   if (NULL == strstr (section, "sysmon-"))
323     return;
324
325   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loading section `%s'\n", section);
326
327   if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (properties, section, "TYPE"))
328   {
329     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing value %s in section `%s'\n",
330         "TYPE", section);
331     return;
332   }
333   if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (properties, section,"VALUE"))
334   {
335     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing value %s in section `%s'\n",
336         "VALUE", section);
337     return;
338   }
339   if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (properties, section,"DESCRIPTION"))
340   {
341     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing value %s in section `%s'\n",
342         "DESCRIPTION", section);
343     return;
344   }
345   if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (properties, section,"CMD"))
346   {
347     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing value %s in section `%s'\n",
348         "CMD", section);
349     return;
350   }
351   sp = GNUNET_malloc (sizeof (struct SysmonProperty));
352
353   /* description */
354   GNUNET_CONFIGURATION_get_value_string (properties, section, "DESCRIPTION", &sp->desc);
355
356   /* cmd */
357   GNUNET_CONFIGURATION_get_value_string (properties, section, "CMD", &tmp);
358   char *args = "";
359   if (NULL != strchr (tmp, ' '))
360   {
361       args = strchr (tmp, ' ');
362       if (strlen (args) > 1)
363       {
364           args[0] = '\0';
365           args++;
366       }
367   }
368   sp->cmd = GNUNET_strdup (tmp);
369   sp->cmd_args = GNUNET_strdup (args);
370   GNUNET_free (tmp);
371   sp->task = &exec_cmd;
372
373   /* type */
374   GNUNET_CONFIGURATION_get_value_string (properties, section, "TYPE", &tmp);
375   to_lower_str (tmp);
376   if (0 == strcasecmp(tmp, "static"))
377     sp->type = t_static;
378   else if (0 == strcasecmp(tmp, "continous"))
379     sp->type = t_continous;
380   else
381   {
382     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid value %s for %s in section `%s'\n",
383         tmp, "TYPE", section);
384     GNUNET_free (tmp);
385     GNUNET_free (sp);
386     return;
387   }
388   GNUNET_free (tmp);
389
390   /* value */
391   GNUNET_CONFIGURATION_get_value_string (properties, section, "VALUE", &tmp);
392   to_lower_str (tmp);
393   if (0 == strcasecmp(tmp, "numeric"))
394     sp->value_type = v_numeric;
395   else if (0 == strcasecmp(tmp, "string"))
396     sp->value_type = v_string;
397   else
398   {
399     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid value %s for %s in section `%s'\n",
400         tmp, "VALUE", section);
401     GNUNET_free (tmp);
402     GNUNET_free (sp);
403     return;
404   }
405   GNUNET_free (tmp);
406
407   /* interval */
408   if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (properties, section,"INTERVAL"))
409     sp->interval = GNUNET_TIME_UNIT_MINUTES;
410   else
411   {
412     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time (properties, section, "INTERVAL", &sp->interval))
413     {
414         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
415             _("Could not parse execution interval for `%s', set to default 60 sec.\n"), section);
416         sp->interval = GNUNET_TIME_UNIT_MINUTES;
417     }
418   }
419
420   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loaded property `%s': %s, %s, interval %llu\n",
421       (NULL != sp->desc) ? sp->desc: "<undefined>",
422       (t_continous == sp->type) ? "continious" : "static",
423       (v_numeric == sp->value_type) ? "numeric" : "string",
424       sp->interval.rel_value);
425
426   GNUNET_CONTAINER_DLL_insert (sp_head, sp_tail, sp);
427
428 }
429
430 static int
431 load_default_properties (void)
432 {
433   struct SysmonProperty *sp;
434   /* GNUnet version array */
435   unsigned int ver[3];
436
437   /* GNUnet vcs revision */
438   unsigned int revision;
439   /* version */
440 #ifdef VERSION
441   if (3 != sscanf (VERSION, "%u.%u.%u", &ver[0], &ver[1], &ver[2]))
442   {
443     ver[0] = 0;
444     ver[1] = 0;
445     ver[2] = 0;
446     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not parse version string `%s'\n", VERSION);
447   }
448 #else
449   ver[0] = 0;
450   ver[1] = 0;
451   ver[2] = 0;
452   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Version string is undefined \n");
453 #endif
454   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Version: %u.%u.%u\n", ver[0], ver[1], ver[2]);
455
456   sp = GNUNET_malloc (sizeof (struct SysmonProperty));
457   sp->desc = GNUNET_strdup ("GNUnet version");
458   sp->type = t_static;
459   sp->value_type = v_numeric;
460   sp->num_val = 100 * ver[0] + 10  * ver[1] + ver[2];
461   GNUNET_CONTAINER_DLL_insert (sp_head, sp_tail, sp);
462   /* revision */
463 #ifdef VCS_VERSION
464   if (1 != sscanf (VCS_VERSION, "svn-%uM", &revision))
465   {
466     revision = 0;
467     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not parse revision string `%s'\n", VCS_VERSION);
468   }
469 #else
470   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "VCS revision string is undefined \n");
471   revision = 0;
472 #endif
473   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Revision: %u\n", revision);
474   sp = GNUNET_malloc (sizeof (struct SysmonProperty));
475   sp->desc = GNUNET_strdup ("GNUnet vcs revision");
476   sp->type = t_static;
477   sp->value_type = v_numeric;
478   sp->num_val = (uint64_t) revision;
479   GNUNET_CONTAINER_DLL_insert (sp_head, sp_tail, sp);
480
481
482   /* GNUnet startup time  */
483   sp = GNUNET_malloc (sizeof (struct SysmonProperty));
484   sp->desc = GNUNET_strdup ("GNUnet startup time");
485   sp->type = t_static;
486   sp->value_type = v_numeric;
487   sp->num_val = (uint64_t) GNUNET_TIME_absolute_get().abs_value;
488   GNUNET_CONTAINER_DLL_insert (sp_head, sp_tail, sp);
489
490
491   /* GNUnet sysmon daemon uptime in seconds */
492   sp = GNUNET_malloc (sizeof (struct SysmonProperty));
493   sp->desc = GNUNET_strdup ("GNUnet uptime");
494   sp->type = t_continous;
495   sp->value_type = v_numeric;
496   sp->num_val = (uint64_t) 0;
497   sp->interval = GNUNET_TIME_UNIT_MINUTES;
498   sp->task_id = GNUNET_SCHEDULER_NO_TASK;
499   sp->task = update_uptime;
500   GNUNET_CONTAINER_DLL_insert (sp_head, sp_tail, sp);
501   return GNUNET_OK;
502 }
503
504 static int
505 load_gtop_properties (void)
506 {
507         char *services;
508         char *s;
509         char *binary;
510         struct SysmonGtopProcProperty *pp;
511         int c;
512         /* Load network monitoring tasks */
513
514         /* Load service memory monitoring tasks */
515         if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (cfg, "sysmon", "MONITOR_SERVICES"))
516                 return GNUNET_OK;
517
518         if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string(cfg, "sysmon", "MONITOR_SERVICES", &services))
519                 return GNUNET_SYSERR;
520
521         s = strtok (services, " ");
522         c = 0;
523         while (NULL != s)
524         {
525                 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, s, "BINARY", &binary))
526                 {
527                         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Monitoring service `%s' with binary `%s'\n", s, binary);
528                         pp = GNUNET_malloc (sizeof (struct SysmonGtopProcProperty));
529                         pp->binary = binary;
530                         GNUNET_CONTAINER_DLL_insert (pp_head, pp_tail, pp);
531                         c++
532                 }
533                 s = strtok (NULL, " ");
534         }
535         GNUNET_free (services);
536
537         if (c > 0)
538         {
539
540         }
541
542         return GNUNET_OK;
543
544 }
545
546
547 static void
548 run_property (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
549 {
550   struct SysmonProperty *sp = cls;
551   sp->task_id = GNUNET_SCHEDULER_NO_TASK;
552   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Running continous property `%s' \n", sp->desc);
553   sp->task (cls, tc);
554   sp->task_id = GNUNET_SCHEDULER_add_delayed (sp->interval, &run_property, sp);
555 }
556
557
558 static int
559 run_properties (void)
560 {
561   struct SysmonProperty *sp;
562
563   for (sp = sp_head; NULL != sp; sp = sp->next)
564   {
565       if (t_static == sp->type)
566       {
567           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Running static property `%s' \n", sp->desc);
568           put_property (sp);
569       }
570       else
571       {
572           if (NULL == sp->task)
573           {
574             GNUNET_break (0);
575             continue;
576           }
577           sp->task_id = GNUNET_SCHEDULER_add_now (&run_property, sp);
578       }
579   }
580   return GNUNET_OK;
581 }
582
583
584 /**
585  * Task run during shutdown.
586  *
587  * @param cls unused
588  * @param tc unused
589  */
590 static void
591 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
592 {
593         struct SysmonGtopProcProperty *cur;
594         struct SysmonGtopProcProperty *next;
595
596         next = pp_head;
597         while (NULL != (cur = next))
598         {
599                         next = cur->next;
600                         GNUNET_CONTAINER_DLL_remove (pp_head, pp_tail, cur);
601                         GNUNET_free (cur->binary);
602                         GNUNET_free (cur);
603         }
604
605 }
606
607
608 /**
609  * Process template requests.
610  *
611  * @param cls closure
612  * @param server the initialized server
613  * @param mycfg configuration to use
614  */
615 static void
616 run (void *cls, struct GNUNET_SERVER_Handle *server,
617      const struct GNUNET_CONFIGURATION_Handle *mycfg)
618 {
619   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
620     /* FIXME: add handlers here! */
621     {NULL, NULL, 0, 0}
622   };
623   /* FIXME: do setup here */
624   GNUNET_SERVER_add_handlers (server, handlers);
625   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
626                                 NULL);
627
628   struct GNUNET_CONFIGURATION_Handle *properties;
629   char *file;
630
631   end_task = GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, NULL);
632   cfg = mycfg;
633
634   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sysdaemon starting ... \n");
635
636   if (GNUNET_SYSERR ==GNUNET_CONFIGURATION_get_value_filename (mycfg, "sysmon", "CFGFILE", &file))
637   {
638     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Sysmon configuration file not set, exit! \n");
639     shutdown_now();
640     ret = 1;
641     return;
642   }
643
644   properties = GNUNET_CONFIGURATION_create();
645   if (NULL == properties)
646   {
647     GNUNET_break (0);
648     shutdown_now();
649     ret = 1;
650     return;
651   }
652   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_load (properties, file))
653   {
654       GNUNET_break (0);
655       GNUNET_CONFIGURATION_destroy (properties);
656       GNUNET_free (file);
657       ret = 1;
658       shutdown_now();
659       return;
660   }
661   GNUNET_free (file);
662   GNUNET_CONFIGURATION_iterate_sections (properties, &load_property, properties);
663
664   GNUNET_CONFIGURATION_destroy (properties);
665
666   /* Creating statistics */
667   stats = GNUNET_STATISTICS_create ("sysmon", mycfg);
668   if (NULL == stats)
669   {
670     GNUNET_break (0);
671     shutdown_now();
672     ret = 1;
673     return;
674   }
675
676   /* load properties */
677   if (GNUNET_SYSERR == load_default_properties ())
678   {
679     GNUNET_break (0);
680     shutdown_now();
681     ret = 1;
682     return;
683   }
684
685 #if HAVE_LIBGTOP
686   if (GNUNET_SYSERR == load_gtop_properties ())
687   {
688           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to load gtop properties \n");
689   }
690 #endif
691
692   /* run properties */
693   if (GNUNET_SYSERR == run_properties ())
694   {
695     GNUNET_break (0);
696     shutdown_now();
697     ret = 1;
698     return;
699   }
700 }
701
702
703 /**
704  * The main function for the template service.
705  *
706  * @param argc number of arguments from the command line
707  * @param argv command line arguments
708  * @return 0 ok, 1 on error
709  */
710 int
711 main (int argc, char *const *argv)
712 {
713   return (GNUNET_OK ==
714           GNUNET_SERVICE_run (argc, argv, "sysmon",
715                               GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1;
716 }
717
718 /* end of gnunet-service-sysmon.c */