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_command
34 };
35
36
37 enum type
38 {
39   t_static,
40   t_continous
41 };
42
43 enum value
44 {
45   v_numeric,
46   v_string
47 };
48
49 /**
50  * A system property to monitor
51  */
52 struct SysmonProperty
53 {
54   /**
55    * Next element in in the DLL
56    */
57   struct SysmonProperty *next;
58
59   /**
60    * Previous element in in the DLL
61    */
62   struct SysmonProperty *prev;
63
64   /**
65    * Description used for statistics valuesd
66    */
67   char * desc;
68
69   /**
70    * Type
71    */
72   int type;
73
74   /**
75    * Value type
76    */
77   int value_type;
78
79   /**
80    * Execution interval
81    */
82   struct GNUNET_TIME_Relative interval;
83
84   /**
85    * Command
86    */
87   char * cmd;
88
89   /**
90    * Command arguments
91    */
92   char * cmd_args;
93
94   /**
95    * Command execution handle
96    */
97   void * cmd_exec_handle;
98
99   /**
100    * Numerical value
101    */
102   uint64_t num_val;
103
104   /**
105    * String value
106    */
107   char * str_val;
108
109   /**
110    * Task id
111    */
112   GNUNET_SCHEDULER_TaskIdentifier task_id;
113
114   /**
115    * Task handle
116    */
117   GNUNET_SCHEDULER_Task task;
118
119 };
120
121 /**
122  * Final status code.
123  */
124 static int ret;
125
126 /**
127  * Configuration handle
128  */
129 const struct GNUNET_CONFIGURATION_Handle *cfg;
130
131
132 /**
133  * Statistics handle
134  */
135 struct GNUNET_STATISTICS_Handle *stats;
136
137 /**
138  * Shutdown task
139  */
140 GNUNET_SCHEDULER_TaskIdentifier end_task;
141
142 struct SysmonProperty *sp_head;
143 struct SysmonProperty *sp_tail;
144
145 static void
146 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
147 {
148   struct SysmonProperty *sp;
149   struct SysmonProperty *next;
150
151   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sysdaemon stopping ... \n");
152
153   end_task = GNUNET_SCHEDULER_NO_TASK;
154
155   if (NULL != stats)
156   {
157     GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
158     stats = NULL;
159   }
160
161   next = sp_head;
162   while (NULL != (sp = next))
163   {
164       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stopping `%s' \n", sp->desc);
165       GNUNET_CONTAINER_DLL_remove (sp_head, sp_tail, sp);
166       next = sp->next;
167       if (GNUNET_SCHEDULER_NO_TASK != sp->task_id)
168       {
169         GNUNET_SCHEDULER_cancel (sp->task_id);
170         sp->task_id = GNUNET_SCHEDULER_NO_TASK;
171       }
172       GNUNET_free_non_null (sp->cmd);
173       GNUNET_free_non_null (sp->cmd_args);
174       GNUNET_free (sp->desc);
175       GNUNET_free (sp);
176   }
177
178 }
179
180 static void
181 shutdown_now (void)
182 {
183   if (GNUNET_SCHEDULER_NO_TASK != end_task)
184     GNUNET_SCHEDULER_cancel (end_task);
185   GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
186 }
187
188 static void
189 to_lower_str (char * str)
190 {
191   int c;
192   for (c = 0; c <= strlen (str); c++)
193     str[c] = tolower(str[c]);
194 }
195
196 static int
197 put_property (struct SysmonProperty *sp)
198 {
199   if (v_numeric ==sp->value_type)
200   {
201       /* GNUNET_STATISTICS_set (stats, sp->desc, sp->num_val, GNUNET_NO); */
202
203                 fprintf (stderr, "%s : %s : %llu\n",
204                                 GNUNET_STRINGS_absolute_time_to_string(GNUNET_TIME_absolute_get()),
205                                 sp->desc, (unsigned long long) sp->num_val);
206   }
207   else if (v_string ==sp->value_type)
208   {
209       /* GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "NOT IMPLEMENTED\n"); */
210                 fprintf (stderr, "SYSMON STRING\n");
211   }
212   else
213   {
214     GNUNET_break (0);
215     return GNUNET_SYSERR;
216   }
217   return GNUNET_OK;
218 }
219
220 static void
221 update_uptime (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
222 {
223         struct SysmonProperty *sp = cls;
224         static int first_run = GNUNET_YES;
225
226         if (GNUNET_YES == first_run)
227                         first_run = GNUNET_NO;
228         else
229                         sp->num_val += sp->interval.rel_value / 1000;
230
231   put_property (sp);
232 }
233
234 static void
235 exec_cmd_proc (void *cls, const char *line)
236 {
237   struct SysmonProperty *sp = cls;
238   unsigned long long tmp;
239   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Property output: `%s'\n", line);
240   if (NULL == line)
241   {
242       GNUNET_OS_command_stop (sp->cmd_exec_handle);
243       sp->cmd_exec_handle = NULL;
244       return;
245   }
246
247   switch (sp->value_type) {
248     case v_numeric:
249       if (1 != sscanf (line, "%llu", &tmp))
250       {
251         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Command output was not a numerical value: `%s'\n", line);
252         return;
253       }
254       break;
255     case v_string:
256       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "NOT IMPLEMENTED\n");
257       break;
258     default:
259       break;
260
261   }
262   sp->num_val = tmp;
263
264   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Property output: `%s'\n", line);
265   put_property (sp);
266
267
268 }
269
270 static void
271 exec_cmd (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
272 {
273   struct SysmonProperty *sp = cls;
274   GNUNET_assert (NULL != sp->cmd);
275
276   if (NULL != sp->cmd_exec_handle)
277   {
278     GNUNET_OS_command_stop (sp->cmd_exec_handle);
279     sp->cmd_exec_handle = NULL;
280     GNUNET_break (0);
281   }
282   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Property `%s': command `%s' `%s'\n", sp->desc, sp->cmd, sp->cmd_args);
283   if (NULL == (sp->cmd_exec_handle = GNUNET_OS_command_run (&exec_cmd_proc, sp,
284       GNUNET_TIME_UNIT_SECONDS,
285       sp->cmd, sp->cmd,
286       sp->cmd_args,
287       NULL)))
288     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Property `%s': command `%s' failed\n", sp->desc, sp->cmd);
289 }
290
291 static void
292 load_property (void *cls,
293                const char *section)
294 {
295   struct GNUNET_CONFIGURATION_Handle *properties = cls;
296   struct SysmonProperty *sp;
297   char *tmp;
298
299   if (NULL == strstr (section, "sysmon-"))
300     return;
301
302   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loading section `%s'\n", section);
303
304   if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (properties, section, "TYPE"))
305   {
306     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing value %s in section `%s'\n",
307         "TYPE", section);
308     return;
309   }
310   if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (properties, section,"VALUE"))
311   {
312     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing value %s in section `%s'\n",
313         "VALUE", section);
314     return;
315   }
316   if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (properties, section,"DESCRIPTION"))
317   {
318     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing value %s in section `%s'\n",
319         "DESCRIPTION", section);
320     return;
321   }
322   if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (properties, section,"CMD"))
323   {
324     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing value %s in section `%s'\n",
325         "CMD", section);
326     return;
327   }
328   sp = GNUNET_malloc (sizeof (struct SysmonProperty));
329
330   /* description */
331   GNUNET_CONFIGURATION_get_value_string (properties, section, "DESCRIPTION", &sp->desc);
332
333   /* cmd */
334   GNUNET_CONFIGURATION_get_value_string (properties, section, "CMD", &tmp);
335   char *args = "";
336   if (NULL != strchr (tmp, ' '))
337   {
338       args = strchr (tmp, ' ');
339       if (strlen (args) > 1)
340       {
341           args[0] = '\0';
342           args++;
343       }
344   }
345   sp->cmd = GNUNET_strdup (tmp);
346   sp->cmd_args = GNUNET_strdup (args);
347   GNUNET_free (tmp);
348   sp->task = &exec_cmd;
349
350   /* type */
351   GNUNET_CONFIGURATION_get_value_string (properties, section, "TYPE", &tmp);
352   to_lower_str (tmp);
353   if (0 == strcasecmp(tmp, "static"))
354     sp->type = t_static;
355   else if (0 == strcasecmp(tmp, "continous"))
356     sp->type = t_continous;
357   else
358   {
359     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid value %s for %s in section `%s'\n",
360         tmp, "TYPE", section);
361     GNUNET_free (tmp);
362     GNUNET_free (sp);
363     return;
364   }
365   GNUNET_free (tmp);
366
367   /* value */
368   GNUNET_CONFIGURATION_get_value_string (properties, section, "VALUE", &tmp);
369   to_lower_str (tmp);
370   if (0 == strcasecmp(tmp, "numeric"))
371     sp->value_type = v_numeric;
372   else if (0 == strcasecmp(tmp, "string"))
373     sp->value_type = v_string;
374   else
375   {
376     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid value %s for %s in section `%s'\n",
377         tmp, "VALUE", section);
378     GNUNET_free (tmp);
379     GNUNET_free (sp);
380     return;
381   }
382   GNUNET_free (tmp);
383
384   /* interval */
385   if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (properties, section,"INTERVAL"))
386     sp->interval = GNUNET_TIME_UNIT_MINUTES;
387   else
388   {
389     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time (properties, section, "INTERVAL", &sp->interval))
390     {
391         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
392             _("Could not parse execution interval for `%s', set to default 60 sec.\n"), section);
393         sp->interval = GNUNET_TIME_UNIT_MINUTES;
394     }
395   }
396
397   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loaded property `%s': %s, %s, interval %llu\n",
398       (NULL != sp->desc) ? sp->desc: "<undefined>",
399       (t_continous == sp->type) ? "continious" : "static",
400       (v_numeric == sp->value_type) ? "numeric" : "string",
401       sp->interval.rel_value);
402
403   GNUNET_CONTAINER_DLL_insert (sp_head, sp_tail, sp);
404
405 }
406
407 static int
408 load_default_properties (void)
409 {
410   struct SysmonProperty *sp;
411   /* GNUnet version array */
412   unsigned int ver[3];
413
414   /* GNUnet vcs revision */
415   unsigned int revision;
416   /* version */
417 #ifdef VERSION
418   if (3 != sscanf (VERSION, "%u.%u.%u", &ver[0], &ver[1], &ver[2]))
419   {
420     ver[0] = 0;
421     ver[1] = 0;
422     ver[2] = 0;
423     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not parse version string `%s'\n", VERSION);
424   }
425 #else
426   ver[0] = 0;
427   ver[1] = 0;
428   ver[2] = 0;
429   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Version string is undefined \n");
430 #endif
431   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Version: %u.%u.%u\n", ver[0], ver[1], ver[2]);
432
433   sp = GNUNET_malloc (sizeof (struct SysmonProperty));
434   sp->desc = GNUNET_strdup ("GNUnet version");
435   sp->type = t_static;
436   sp->value_type = v_numeric;
437   sp->num_val = 100 * ver[0] + 10  * ver[1] + ver[2];
438   GNUNET_CONTAINER_DLL_insert (sp_head, sp_tail, sp);
439   /* revision */
440 #ifdef VCS_VERSION
441   if (1 != sscanf (VCS_VERSION, "svn-%uM", &revision))
442   {
443     revision = 0;
444     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not parse revision string `%s'\n", VCS_VERSION);
445   }
446 #else
447   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "VCS revision string is undefined \n");
448   revision = 0;
449 #endif
450   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Revision: %u\n", revision);
451   sp = GNUNET_malloc (sizeof (struct SysmonProperty));
452   sp->desc = GNUNET_strdup ("GNUnet vcs revision");
453   sp->type = t_static;
454   sp->value_type = v_numeric;
455   sp->num_val = (uint64_t) revision;
456   GNUNET_CONTAINER_DLL_insert (sp_head, sp_tail, sp);
457
458
459   /* GNUnet startup time  */
460   sp = GNUNET_malloc (sizeof (struct SysmonProperty));
461   sp->desc = GNUNET_strdup ("GNUnet startup time");
462   sp->type = t_static;
463   sp->value_type = v_numeric;
464   sp->num_val = (uint64_t) GNUNET_TIME_absolute_get().abs_value;
465   GNUNET_CONTAINER_DLL_insert (sp_head, sp_tail, sp);
466
467
468   /* GNUnet sysmon daemon uptime in seconds */
469   sp = GNUNET_malloc (sizeof (struct SysmonProperty));
470   sp->desc = GNUNET_strdup ("GNUnet uptime");
471   sp->type = t_continous;
472   sp->value_type = v_numeric;
473   sp->num_val = (uint64_t) 0;
474   sp->interval = GNUNET_TIME_UNIT_MINUTES;
475   sp->task_id = GNUNET_SCHEDULER_NO_TASK;
476   sp->task = update_uptime;
477   GNUNET_CONTAINER_DLL_insert (sp_head, sp_tail, sp);
478   return GNUNET_OK;
479 }
480
481
482 static void
483 run_property (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
484 {
485   struct SysmonProperty *sp = cls;
486   sp->task_id = GNUNET_SCHEDULER_NO_TASK;
487   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Running continous property `%s' \n", sp->desc);
488   sp->task (cls, tc);
489   sp->task_id = GNUNET_SCHEDULER_add_delayed (sp->interval, &run_property, sp);
490 }
491
492
493 static int
494 run_properties (void)
495 {
496   struct SysmonProperty *sp;
497
498   for (sp = sp_head; NULL != sp; sp = sp->next)
499   {
500       if (t_static == sp->type)
501       {
502           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Running static property `%s' \n", sp->desc);
503           put_property (sp);
504       }
505       else
506       {
507           if (NULL == sp->task)
508           {
509             GNUNET_break (0);
510             continue;
511           }
512           sp->task_id = GNUNET_SCHEDULER_add_now (&run_property, sp);
513       }
514   }
515   return GNUNET_OK;
516 }
517
518
519 /**
520  * Task run during shutdown.
521  *
522  * @param cls unused
523  * @param tc unused
524  */
525 static void
526 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
527 {
528   /* FIXME: do clean up here */
529 }
530
531
532 /**
533  * Process template requests.
534  *
535  * @param cls closure
536  * @param server the initialized server
537  * @param mycfg configuration to use
538  */
539 static void
540 run (void *cls, struct GNUNET_SERVER_Handle *server,
541      const struct GNUNET_CONFIGURATION_Handle *mycfg)
542 {
543   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
544     /* FIXME: add handlers here! */
545     {NULL, NULL, 0, 0}
546   };
547   /* FIXME: do setup here */
548   GNUNET_SERVER_add_handlers (server, handlers);
549   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
550                                 NULL);
551
552   struct GNUNET_CONFIGURATION_Handle *properties;
553   char *file;
554
555   end_task = GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, NULL);
556   cfg = mycfg;
557
558   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sysdaemon starting ... \n");
559
560   if (GNUNET_SYSERR ==GNUNET_CONFIGURATION_get_value_filename (mycfg, "sysmon", "CFGFILE", &file))
561   {
562     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Sysmon configuration file not set, exit! \n");
563     shutdown_now();
564     ret = 1;
565     return;
566   }
567
568   properties = GNUNET_CONFIGURATION_create();
569   if (NULL == properties)
570   {
571     GNUNET_break (0);
572     shutdown_now();
573     ret = 1;
574     return;
575   }
576   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_load (properties, file))
577   {
578       GNUNET_break (0);
579       GNUNET_CONFIGURATION_destroy (properties);
580       GNUNET_free (file);
581       ret = 1;
582       shutdown_now();
583       return;
584   }
585   GNUNET_free (file);
586   GNUNET_CONFIGURATION_iterate_sections (properties, &load_property, properties);
587
588   GNUNET_CONFIGURATION_destroy (properties);
589
590   /* Creating statistics */
591   stats = GNUNET_STATISTICS_create ("sysmon", mycfg);
592   if (NULL == stats)
593   {
594     GNUNET_break (0);
595     shutdown_now();
596     ret = 1;
597     return;
598   }
599
600   /* load properties */
601   if (GNUNET_SYSERR == load_default_properties ())
602   {
603     GNUNET_break (0);
604     shutdown_now();
605     ret = 1;
606     return;
607   }
608
609   /* run properties */
610   if (GNUNET_SYSERR == run_properties ())
611   {
612     GNUNET_break (0);
613     shutdown_now();
614     ret = 1;
615     return;
616   }
617 }
618
619
620 /**
621  * The main function for the template service.
622  *
623  * @param argc number of arguments from the command line
624  * @param argv command line arguments
625  * @return 0 ok, 1 on error
626  */
627 int
628 main (int argc, char *const *argv)
629 {
630   return (GNUNET_OK ==
631           GNUNET_SERVICE_run (argc, argv, "sysmon",
632                               GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1;
633 }
634
635 /* end of gnunet-service-sysmon.c */