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