- more changes
[oweals/gnunet.git] / src / sysmon / gnunet-daemon-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-daemon-sysmon.c
23  * @brief system monitoring daemon
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   else if (v_string ==sp->value_type)
204   {
205       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "NOT IMPLEMENTED\n");
206   }
207   else
208   {
209     GNUNET_break (0);
210     return GNUNET_SYSERR;
211   }
212   return GNUNET_OK;
213 }
214
215 static void
216 update_uptime (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
217 {
218   struct SysmonProperty *sp = cls;
219   sp->num_val ++;
220   put_property (sp);
221 }
222
223 static void
224 exec_cmd_proc (void *cls, const char *line)
225 {
226   struct SysmonProperty *sp = cls;
227   if (NULL == line)
228   {
229       GNUNET_OS_command_stop (sp->cmd_exec_handle);
230       sp->cmd_exec_handle = NULL;
231       return;
232   }
233
234   switch (sp->value_type) {
235     case v_numeric:
236       if (1 != sscanf (line, "%lu", &sp->num_val))
237       {
238         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Command output was not a numerical value: `%s'\n", line);
239         return;
240       }
241       break;
242     case v_string:
243       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "NOT IMPLEMENTED\n");
244       break;
245     default:
246       break;
247   }
248
249   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Property output: `%s'\n", line);
250   put_property (sp);
251
252
253 }
254
255 static void
256 exec_cmd (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
257 {
258   struct SysmonProperty *sp = cls;
259   GNUNET_assert (NULL != sp->cmd);
260
261   if (NULL != sp->cmd_exec_handle)
262   {
263     GNUNET_OS_command_stop (sp->cmd_exec_handle);
264     sp->cmd_exec_handle = NULL;
265     GNUNET_break (0);
266   }
267   if (NULL == (sp->cmd_exec_handle = GNUNET_OS_command_run (&exec_cmd_proc, sp,
268       GNUNET_TIME_UNIT_SECONDS,
269       sp->cmd, sp->cmd,
270       sp->cmd_args,
271       NULL)))
272     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Property `%s': command `%s' failed\n", sp->desc, sp->cmd);
273 }
274
275 static void
276 load_property (void *cls,
277                const char *section)
278 {
279   struct GNUNET_CONFIGURATION_Handle *properties = cls;
280   struct SysmonProperty *sp;
281   char *tmp;
282
283   if (NULL == strstr (section, "sysmon-"))
284     return;
285
286   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loading section `%s'\n", section);
287
288   if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (properties, section, "TYPE"))
289   {
290     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing value %s in section `%s'\n",
291         "TYPE", section);
292     return;
293   }
294   if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (properties, section,"VALUE"))
295   {
296     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing value %s in section `%s'\n",
297         "VALUE", section);
298     return;
299   }
300   if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (properties, section,"DESCRIPTION"))
301   {
302     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing value %s in section `%s'\n",
303         "DESCRIPTION", section);
304     return;
305   }
306   if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (properties, section,"CMD"))
307   {
308     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing value %s in section `%s'\n",
309         "CMD", section);
310     return;
311   }
312   sp = GNUNET_malloc (sizeof (struct SysmonProperty));
313
314   /* description */
315   GNUNET_CONFIGURATION_get_value_string (properties, section, "DESCRIPTION", &sp->desc);
316
317   /* cmd */
318   GNUNET_CONFIGURATION_get_value_string (properties, section, "CMD", &tmp);
319   char *args = "";
320   if (NULL != strchr (tmp, ' '))
321   {
322       args = strchr (tmp, ' ');
323       if (strlen (args) > 1)
324       {
325           args[0] = '\0';
326           args++;
327       }
328   }
329   sp->cmd = GNUNET_strdup (tmp);
330   sp->cmd_args = GNUNET_strdup (args);
331   GNUNET_free (tmp);
332   sp->task = &exec_cmd;
333
334   /* type */
335   GNUNET_CONFIGURATION_get_value_string (properties, section, "TYPE", &tmp);
336   to_lower_str (tmp);
337   if (0 == strcasecmp(tmp, "static"))
338     sp->type = t_static;
339   else if (0 == strcasecmp(tmp, "continous"))
340     sp->type = t_continous;
341   else
342   {
343     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid value %s for %s in section `%s'\n",
344         tmp, "TYPE", section);
345     GNUNET_free (tmp);
346     GNUNET_free (sp);
347     return;
348   }
349   GNUNET_free (tmp);
350
351   /* value */
352   GNUNET_CONFIGURATION_get_value_string (properties, section, "VALUE", &tmp);
353   to_lower_str (tmp);
354   if (0 == strcasecmp(tmp, "numeric"))
355     sp->value_type = v_numeric;
356   else if (0 == strcasecmp(tmp, "string"))
357     sp->value_type = v_string;
358   else
359   {
360     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid value %s for %s in section `%s'\n",
361         tmp, "VALUE", section);
362     GNUNET_free (tmp);
363     GNUNET_free (sp);
364     return;
365   }
366   GNUNET_free (tmp);
367
368   /* interval */
369   if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (properties, section,"INTERVAL"))
370     sp->interval = GNUNET_TIME_UNIT_MINUTES;
371   else
372   {
373     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time (properties, section, "INTERVAL", &sp->interval))
374     {
375         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
376             _("Could not parse execution interval for `%s', set to default 60 sec.\n"), section);
377         sp->interval = GNUNET_TIME_UNIT_MINUTES;
378     }
379   }
380
381   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loaded property `%s': %s, %s, interval %llu\n",
382       (NULL != sp->desc) ? sp->desc: "<undefined>",
383       (t_continous == sp->type) ? "continious" : "static",
384       (v_numeric == sp->value_type) ? "numeric" : "string",
385       sp->interval.rel_value);
386
387   GNUNET_CONTAINER_DLL_insert (sp_head, sp_tail, sp);
388
389 }
390
391 static int
392 load_default_properties (void)
393 {
394   struct SysmonProperty *sp;
395   /* GNUnet version array */
396   unsigned int ver[3];
397
398   /* GNUnet vcs revision */
399   unsigned int revision;
400 return GNUNET_OK;
401   /* version */
402 #ifdef VERSION
403   if (3 != sscanf (VERSION, "%u.%u.%u", &ver[0], &ver[1], &ver[2]))
404   {
405     ver[0] = 0;
406     ver[1] = 0;
407     ver[2] = 0;
408     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not parse version string `%s'\n", VERSION);
409   }
410 #else
411   ver[0] = 0;
412   ver[1] = 0;
413   ver[2] = 0;
414   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Version string is undefined \n");
415 #endif
416   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Version: %u.%u.%u\n", ver[0], ver[1], ver[2]);
417
418   sp = GNUNET_malloc (sizeof (struct SysmonProperty));
419   sp->desc = GNUNET_strdup ("GNUnet version");
420   sp->type = t_static;
421   sp->value_type = v_numeric;
422   sp->num_val = 100 * ver[0] + 10  * ver[1] + ver[2];
423   GNUNET_CONTAINER_DLL_insert (sp_head, sp_tail, sp);
424
425   /* revision */
426 #ifdef VCS_VERSION
427   if (1 != sscanf (VCS_VERSION, "svn-%uM", &revision))
428   {
429     revision = 0;
430     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not parse revision string `%s'\n", VCS_VERSION);
431   }
432 #else
433   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "VCS revision string is undefined \n");
434   revision = 0;
435 #endif
436   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Revision: %u\n", revision);
437
438   sp = GNUNET_malloc (sizeof (struct SysmonProperty));
439   sp->desc = GNUNET_strdup ("GNUnet vcs revision");
440   sp->type = t_static;
441   sp->value_type = v_numeric;
442   sp->num_val = (uint64_t) revision;
443   GNUNET_CONTAINER_DLL_insert (sp_head, sp_tail, sp);
444
445
446   /* GNUnet startup time  */
447   sp = GNUNET_malloc (sizeof (struct SysmonProperty));
448   sp->desc = GNUNET_strdup ("GNUnet startup time");
449   sp->type = t_static;
450   sp->value_type = v_numeric;
451   sp->num_val = (uint64_t) GNUNET_TIME_absolute_get().abs_value;
452   GNUNET_CONTAINER_DLL_insert (sp_head, sp_tail, sp);
453
454
455   /* GNUnet sysmon daemon uptime */
456   sp = GNUNET_malloc (sizeof (struct SysmonProperty));
457   sp->desc = GNUNET_strdup ("GNUnet uptime");
458   sp->type = t_continous;
459   sp->value_type = v_numeric;
460   sp->num_val = (uint64_t) 0;
461   sp->interval = GNUNET_TIME_UNIT_SECONDS;
462   sp->task_id = GNUNET_SCHEDULER_NO_TASK;
463   sp->task = update_uptime;
464   GNUNET_CONTAINER_DLL_insert (sp_head, sp_tail, sp);
465
466   return GNUNET_OK;
467 }
468
469
470 static void
471 run_property (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
472 {
473   struct SysmonProperty *sp = cls;
474   sp->task_id = GNUNET_SCHEDULER_NO_TASK;
475   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Running continous property `%s' \n", sp->desc);
476   sp->task (cls, tc);
477   sp->task_id = GNUNET_SCHEDULER_add_delayed (sp->interval, &run_property, sp);
478 }
479
480
481 static int
482 run_properties (void)
483 {
484   struct SysmonProperty *sp;
485
486   for (sp = sp_head; NULL != sp; sp = sp->next)
487   {
488       if (t_static == sp->type)
489       {
490           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Running static property `%s' \n", sp->desc);
491           put_property (sp);
492       }
493       else
494       {
495           if (NULL == sp->task)
496           {
497             GNUNET_break (0);
498             continue;
499           }
500           sp->task_id = GNUNET_SCHEDULER_add_now (&run_property, sp);
501       }
502   }
503   return GNUNET_OK;
504 }
505
506 /**
507  * Main function that will be run by the scheduler.
508  *
509  * @param cls closure
510  * @param args remaining command-line arguments
511  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
512  * @param cfg configuration
513  */
514 static void
515 run (void *cls, char *const *args, const char *cfgfile,
516      const struct GNUNET_CONFIGURATION_Handle *mycfg)
517 {
518   struct GNUNET_CONFIGURATION_Handle *properties;
519   char *file;
520
521   end_task = GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, NULL);
522   cfg = mycfg;
523
524   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sysdaemon starting ... \n");
525
526   if (GNUNET_SYSERR ==GNUNET_CONFIGURATION_get_value_filename (mycfg, "sysmon", "CFGFILE", &file))
527   {
528     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Sysmon configuration file not set, exit! \n");
529     shutdown_now();
530     ret = 1;
531     return;
532   }
533
534   properties = GNUNET_CONFIGURATION_create();
535   if (NULL == properties)
536   {
537     GNUNET_break (0);
538     shutdown_now();
539     ret = 1;
540     return;
541   }
542   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_load (properties, file))
543   {
544       GNUNET_break (0);
545       GNUNET_CONFIGURATION_destroy (properties);
546       GNUNET_free (file);
547       ret = 1;
548       shutdown_now();
549       return;
550   }
551   GNUNET_free (file);
552   GNUNET_CONFIGURATION_iterate_sections (properties, &load_property, properties);
553
554   GNUNET_CONFIGURATION_destroy (properties);
555
556   /* Creating statistics */
557   stats = GNUNET_STATISTICS_create ("sysmon", mycfg);
558   if (NULL == stats)
559   {
560     GNUNET_break (0);
561     shutdown_now();
562     ret = 1;
563     return;
564   }
565
566   /* load properties */
567   if (GNUNET_SYSERR == load_default_properties ())
568   {
569     GNUNET_break (0);
570     shutdown_now();
571     ret = 1;
572     return;
573   }
574
575   /* run properties */
576   if (GNUNET_SYSERR == run_properties ())
577   {
578     GNUNET_break (0);
579     shutdown_now();
580     ret = 1;
581     return;
582   }
583
584 }
585
586
587 /**
588  * The main function.
589  *
590  * @param argc number of arguments from the command line
591  * @param argv command line arguments
592  * @return 0 ok, 1 on error
593  */
594 int
595 main (int argc, char *const *argv)
596 {
597   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
598     GNUNET_GETOPT_OPTION_END
599   };
600   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
601     return 2;
602
603   ret = (GNUNET_OK ==
604          GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-sysmon",
605                              gettext_noop ("GNUnet system monitoring and information daemon"), options, &run,
606                              NULL)) ? ret : 1;
607   GNUNET_free ((void*) argv);
608   return ret;
609 }
610
611 /* end of gnunet-daemon-sysmon.c */