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