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