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