2 This file is part of GNUnet.
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.
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.
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.
22 * @file sensor/gnunet-service-sensor.c
23 * @brief sensor service implementation
24 * @author Omar Tarabai
28 #include "gnunet_util_lib.h"
30 #include "gnunet_statistics_service.h"
31 #include "gnunet_peerstore_service.h"
36 static const struct GNUNET_CONFIGURATION_Handle *cfg;
39 * Hashmap of loaded sensor definitions
41 static struct GNUNET_CONTAINER_MultiHashMap *sensors;
44 * Handle to statistics service
46 struct GNUNET_STATISTICS_Handle *statistics;
49 * Handle to peerstore service
51 struct GNUNET_PEERSTORE_Handle *peerstore;
56 char *subsystem = "sensor";
61 struct GNUNET_PeerIdentity peerid;
64 * Remove sensor execution from scheduler
67 * @param key hash of sensor name, key to hashmap
68 * @param value a 'struct SensorInfo *'
69 * @return #GNUNET_YES if we should continue to
73 static int destroy_sensor(void *cls,
74 const struct GNUNET_HashCode *key, void *value)
76 struct SensorInfo *sensorinfo = value;
78 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Destroying sensor `%s'\n", sensorinfo->name);
79 if(GNUNET_SCHEDULER_NO_TASK != sensorinfo->execution_task)
81 GNUNET_SCHEDULER_cancel(sensorinfo->execution_task);
82 sensorinfo->execution_task = GNUNET_SCHEDULER_NO_TASK;
84 if(NULL != sensorinfo->gnunet_stat_get_handle)
86 GNUNET_STATISTICS_get_cancel(sensorinfo->gnunet_stat_get_handle);
87 sensorinfo->gnunet_stat_get_handle = NULL;
89 if(NULL != sensorinfo->ext_cmd)
91 GNUNET_OS_command_stop(sensorinfo->ext_cmd);
92 sensorinfo->ext_cmd = NULL;
94 if(NULL != sensorinfo->cfg)
95 GNUNET_CONFIGURATION_destroy(sensorinfo->cfg);
96 if(NULL != sensorinfo->name)
97 GNUNET_free(sensorinfo->name);
98 if(NULL != sensorinfo->def_file)
99 GNUNET_free(sensorinfo->def_file);
100 if(NULL != sensorinfo->description)
101 GNUNET_free(sensorinfo->description);
102 if(NULL != sensorinfo->category)
103 GNUNET_free(sensorinfo->category);
104 if(NULL != sensorinfo->capabilities)
105 GNUNET_free(sensorinfo->capabilities);
106 if(NULL != sensorinfo->gnunet_stat_service)
107 GNUNET_free(sensorinfo->gnunet_stat_service);
108 if(NULL != sensorinfo->gnunet_stat_name)
109 GNUNET_free(sensorinfo->gnunet_stat_name);
110 if(NULL != sensorinfo->ext_process)
111 GNUNET_free(sensorinfo->ext_process);
112 if(NULL != sensorinfo->ext_args)
113 GNUNET_free(sensorinfo->ext_args);
114 GNUNET_free(sensorinfo);
120 * Sensor will not run again unless
121 * explicitly enabled or reloaded
123 * @param sensor sensor information
125 static void set_sensor_enabled(struct SensorInfo *sensor, int state)
127 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
128 "Sensor `%s': Setting enabled to %d.\n",
129 sensor->name, state);
130 sensor->enabled = GNUNET_NO;
131 GNUNET_assert(NULL != sensor->cfg);
132 GNUNET_CONFIGURATION_set_value_string(sensor->cfg, sensor->name, "ENABLED",
133 (GNUNET_YES == state)?"YES":"NO");
134 GNUNET_CONFIGURATION_write(sensor->cfg, sensor->def_file);
138 * Task run during shutdown.
144 shutdown_task (void *cls,
145 const struct GNUNET_SCHEDULER_TaskContext *tc)
147 SENSOR_reporting_stop();
148 SENSOR_analysis_stop();
149 GNUNET_CONTAINER_multihashmap_iterate(sensors, &destroy_sensor, NULL);
150 GNUNET_CONTAINER_multihashmap_destroy(sensors);
151 if(NULL != statistics)
153 GNUNET_STATISTICS_destroy(statistics, GNUNET_YES);
156 if(NULL != peerstore)
158 GNUNET_PEERSTORE_disconnect(peerstore);
161 GNUNET_SCHEDULER_shutdown();
166 * A client disconnected. Remove all of its data structure entries.
168 * @param cls closure, NULL
169 * @param client identification of the client
172 handle_client_disconnect (void *cls,
173 struct GNUNET_SERVER_Client
179 * Creates a structure with basic sensor info to be sent to a client
181 * @param sensor sensor information
182 * @return message ready to be sent to client
184 static struct SensorInfoMessage *
185 create_sensor_info_msg(struct SensorInfo *sensor)
187 struct SensorInfoMessage *msg;
193 name_len = strlen(sensor->name);
194 if(NULL == sensor->description)
197 desc_len = strlen(sensor->description) + 1;
199 len += sizeof(struct SensorInfoMessage);
202 msg = GNUNET_malloc(len);
203 msg->header.size = htons(len);
204 msg->header.type = htons(GNUNET_MESSAGE_TYPE_SENSOR_INFO);
205 msg->name_len = htons(name_len);
206 msg->description_len = htons(desc_len);
207 msg->version_major = htons(sensor->version_major);
208 msg->version_minor = htons(sensor->version_minor);
209 str_ptr = (char*) &msg[1];
210 memcpy(str_ptr, sensor->name, name_len);
211 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending sensor name (%d): %.*s\n",
212 name_len, name_len, str_ptr);
214 memcpy(str_ptr, sensor->description, desc_len);
215 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending sensor description (%d): %.*s\n",
216 desc_len, desc_len, str_ptr);
222 * Handle GET SENSOR message.
225 * @param client identification of the client
226 * @param message the actual message
229 handle_get_sensor (void *cls, struct GNUNET_SERVER_Client *client,
230 const struct GNUNET_MessageHeader *message)
232 struct GNUNET_SERVER_TransmitContext *tc;
234 size_t sensorname_len;
235 struct GNUNET_HashCode key;
236 struct SensorInfo *sensorinfo;
237 struct SensorInfoMessage *msg;
239 sensorname = (char *)&message[1];
240 sensorname_len = ntohs(message->size) - sizeof(struct GNUNET_MessageHeader);
241 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received for sensor (%d) `%.*s'\n",
242 "GET SENSOR", sensorname_len, sensorname_len, sensorname);
243 tc = GNUNET_SERVER_transmit_context_create (client);
244 GNUNET_CRYPTO_hash(sensorname, sensorname_len, &key);
245 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created key hash for requested sensor\n");
246 sensorinfo = (struct SensorInfo *)GNUNET_CONTAINER_multihashmap_get(sensors, &key);
247 if(NULL != sensorinfo)
249 msg = create_sensor_info_msg(sensorinfo);
250 GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
254 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Requested sensor `%.*s' was not found\n",
255 sensorname_len, sensorname);
256 GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
257 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
261 * Iterator for sensors and adds them to transmit context
263 * @param cls a 'struct GNUNET_SERVER_TransmitContext *'
264 * @param key hash of sensor name, key to hashmap
265 * @param value a 'struct SensorInfo *'
267 int add_sensor_to_tc(void *cls,
268 const struct GNUNET_HashCode *key, void *value)
270 struct GNUNET_SERVER_TransmitContext *tc = cls;
271 struct SensorInfo *sensorinfo = value;
272 struct SensorInfoMessage *msg;
274 msg = create_sensor_info_msg(sensorinfo);
275 GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
283 * Handle GET ALL SENSORS message.
286 * @param client identification of the client
287 * @param message the actual message
290 handle_get_all_sensors (void *cls, struct GNUNET_SERVER_Client *client,
291 const struct GNUNET_MessageHeader *message)
293 struct GNUNET_SERVER_TransmitContext *tc;
295 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received.\n",
297 tc = GNUNET_SERVER_transmit_context_create (client);
298 GNUNET_CONTAINER_multihashmap_iterate(sensors, &add_sensor_to_tc, tc);
299 GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
300 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
304 * Do a series of checks to determine if sensor should execute
306 * @return #GNUNET_YES / #GNUNET_NO
309 should_run_sensor(struct SensorInfo *sensorinfo)
311 struct GNUNET_TIME_Absolute now;
313 if(GNUNET_NO == sensorinfo->enabled)
315 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Sensor `%s' is disabled, will not run\n", sensorinfo->name);
318 now = GNUNET_TIME_absolute_get();
319 if(NULL != sensorinfo->start_time
320 && now.abs_value_us < sensorinfo->start_time->abs_value_us)
322 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Start time for sensor `%s' not reached yet, will not run\n", sensorinfo->name);
325 if(NULL != sensorinfo->end_time
326 && now.abs_value_us >= sensorinfo->end_time->abs_value_us)
328 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Sensor `%s' expired, disabling.\n", sensorinfo->name);
329 set_sensor_enabled(sensorinfo, GNUNET_NO);
336 * Callback function to process statistic values
338 * @param cls 'struct SensorInfo *'
339 * @param subsystem name of subsystem that created the statistic
340 * @param name the name of the datum
341 * @param value the current value
342 * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
343 * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
345 int sensor_statistics_iterator (void *cls,
351 struct SensorInfo *sensorinfo = cls;
352 double dvalue = (double)value;
353 struct GNUNET_TIME_Absolute expiry;
355 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Received a value for sensor `%s': %" PRIu64 "\n", sensorinfo->name, value);
356 expiry = GNUNET_TIME_relative_to_absolute(sensorinfo->lifetime);
357 GNUNET_PEERSTORE_store(peerstore,
364 GNUNET_PEERSTORE_STOREOPTION_MULTIPLE,
367 return GNUNET_SYSERR; /* We only want one value */
371 * Continuation called after sensor gets all gnunet statistics values
373 * @param cls 'struct SensorInfo *'
374 * @param success #GNUNET_OK if statistics were
375 * successfully obtained, #GNUNET_SYSERR if not.
377 void end_sensor_run_stat (void *cls, int success)
379 struct SensorInfo *sensorinfo = cls;
381 sensorinfo->gnunet_stat_get_handle = NULL;
382 sensorinfo->running = GNUNET_NO;
386 * Tries to parse a received sensor value to its
389 * @param value the string value received, should be null terminated
390 * @param sensor sensor information struct
391 * @param ret pointer to parsed value
392 * @return size of new parsed value, 0 for error
395 parse_sensor_value (const char *value, struct SensorInfo* sensor, void **ret)
403 if(0 == strcmp("numeric", sensor->expected_datatype))
405 dval = GNUNET_new(double);
406 *dval = strtod(value, &endptr);
410 return sizeof(double);
412 if(0 == strcmp("string", sensor->expected_datatype))
414 *ret = GNUNET_strdup(value);
415 return strlen(value) + 1;
417 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
418 _("Unknown value type expected by sensor, this should not happen.\n"));
423 * Callback for output of executed sensor process
425 * @param cls 'struct SensorInfo *'
426 * @param line line of output from a command, NULL for the end
428 void sensor_process_callback (void *cls, const char *line)
430 struct SensorInfo *sensorinfo = cls;
433 struct GNUNET_TIME_Absolute expiry;
437 GNUNET_OS_command_stop(sensorinfo->ext_cmd);
438 sensorinfo->ext_cmd = NULL;
439 sensorinfo->running = GNUNET_NO;
440 sensorinfo->ext_cmd_value_received = GNUNET_NO;
443 if(GNUNET_YES == sensorinfo->ext_cmd_value_received)
444 return; /* We only want one *valid* value */
445 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Received a value for sensor `%s': %s\n", sensorinfo->name, line);
446 valsize = parse_sensor_value(line, sensorinfo, &value);
447 if (valsize == 0) /* invalid value, FIXME: should we disable the sensor now? */
449 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
450 _("Received an invalid value for sensor `%s': %s\n"),
451 sensorinfo->name, line);
455 sensorinfo->ext_cmd_value_received = GNUNET_YES;
456 expiry = GNUNET_TIME_relative_to_absolute(sensorinfo->lifetime);
457 GNUNET_PEERSTORE_store(peerstore,
464 GNUNET_PEERSTORE_STOREOPTION_MULTIPLE,
471 * Checks if the given file is a path
473 * @return #GNUNET_YES / #GNUNET_NO
476 is_path(char *filename)
481 filename_len = strlen(filename);
482 for(i = 0; i < filename_len; i++)
484 if(DIR_SEPARATOR == filename[i])
491 * Actual execution of a sensor
493 * @param cls 'struct SensorInfo'
497 sensor_run (void *cls,
498 const struct GNUNET_SCHEDULER_TaskContext * tc)
500 struct SensorInfo *sensorinfo = cls;
505 sensorinfo->execution_task = GNUNET_SCHEDULER_add_delayed(sensorinfo->interval, &sensor_run, sensorinfo);
506 if(GNUNET_YES == sensorinfo->running) //FIXME: should we try to kill?
508 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Sensor `%s' running for too long, will try again next interval\n", sensorinfo->name);
511 if(GNUNET_NO == should_run_sensor(sensorinfo))
513 sensorinfo->running = GNUNET_YES;
514 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Starting the execution of sensor `%s'\n", sensorinfo->name);
515 if(0 == strcmp ("gnunet-statistics", sensorinfo->source))
517 sensorinfo->gnunet_stat_get_handle = GNUNET_STATISTICS_get(statistics,
518 sensorinfo->gnunet_stat_service,
519 sensorinfo->gnunet_stat_name,
520 sensorinfo->interval, //try to get values only for the interval of the sensor
521 &end_sensor_run_stat,
522 &sensor_statistics_iterator,
525 else if(0 == strcmp ("process", sensorinfo->source))
527 if(GNUNET_YES == is_path(sensorinfo->ext_process))
529 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
530 _("Sensor `%s': External process should not be a path, disabling sensor.\n"),
532 set_sensor_enabled(sensorinfo, GNUNET_NO);
535 //check if the process exists in $PATH
536 process_path = GNUNET_strdup(sensorinfo->ext_process);
538 GNUNET_OS_check_helper_binary(process_path, GNUNET_NO, NULL);
539 if(GNUNET_SYSERR == check_result)
541 //search in sensor directory
542 sensors_dir = GNUNET_SENSOR_get_sensor_dir ();
543 GNUNET_free(process_path);
544 GNUNET_asprintf(&process_path, "%s%s-files%s%s",
548 sensorinfo->ext_process);
549 GNUNET_free(sensors_dir);
551 GNUNET_OS_check_helper_binary(process_path, GNUNET_NO, NULL);
553 if(GNUNET_SYSERR == check_result)
555 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
556 _("Sensor `%s' process `%s' problem: binary doesn't exist or not executable\n"),
558 sensorinfo->ext_process);
559 set_sensor_enabled(sensorinfo, GNUNET_NO);
560 sensorinfo->running = GNUNET_NO;
561 GNUNET_free(process_path);
564 sensorinfo->ext_cmd_value_received = GNUNET_NO;
565 sensorinfo->ext_cmd = GNUNET_OS_command_run(&sensor_process_callback,
567 GNUNET_TIME_UNIT_FOREVER_REL,
569 sensorinfo->ext_process,
570 sensorinfo->ext_args,
572 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Process started for sensor `%s'\n", sensorinfo->name);
573 GNUNET_free(process_path);
577 sensorinfo->running = GNUNET_NO;
578 GNUNET_break(0); //shouldn't happen
583 * Starts the execution of a sensor
586 * @param key hash of sensor name, key to hashmap (unused)
587 * @param value a 'struct SensorInfo *'
588 * @return #GNUNET_YES if we should continue to
592 int schedule_sensor(void *cls,
593 const struct GNUNET_HashCode *key, void *value)
595 struct SensorInfo *sensorinfo = value;
597 if(GNUNET_NO == should_run_sensor(sensorinfo))
599 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Scheduling sensor `%s' to run after %" PRIu64 " microseconds\n",
600 sensorinfo->name, sensorinfo->interval.rel_value_us);
601 if(GNUNET_SCHEDULER_NO_TASK != sensorinfo->execution_task)
603 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
604 _("Sensor `%s' execution task already set, this should not happen\n"), sensorinfo->name);
607 sensorinfo->execution_task = GNUNET_SCHEDULER_add_delayed(sensorinfo->interval, &sensor_run, sensorinfo);
612 * Starts the execution of all enabled sensors
616 schedule_all_sensors()
618 GNUNET_CONTAINER_multihashmap_iterate(sensors, &schedule_sensor, NULL);
622 * Process statistics requests.
625 * @param server the initialized server
626 * @param c configuration to use
630 struct GNUNET_SERVER_Handle *server,
631 const struct GNUNET_CONFIGURATION_Handle *c)
633 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
634 {&handle_get_sensor, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GET,
636 {&handle_get_all_sensors, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GETALL,
637 sizeof (struct GNUNET_MessageHeader)},
642 sensors = GNUNET_SENSOR_load_all_sensors ();
643 schedule_all_sensors();
644 SENSOR_analysis_start(c, sensors);
645 SENSOR_reporting_start(c, sensors);
646 statistics = GNUNET_STATISTICS_create("sensor", cfg);
647 GNUNET_CRYPTO_get_peer_identity(cfg, &peerid);
648 peerstore = GNUNET_PEERSTORE_connect(cfg);
649 GNUNET_SERVER_add_handlers (server, handlers);
650 GNUNET_SERVER_disconnect_notify (server,
651 &handle_client_disconnect,
653 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
660 * The main function for the sensor service.
662 * @param argc number of arguments from the command line
663 * @param argv command line arguments
664 * @return 0 ok, 1 on error
667 main (int argc, char *const *argv)
670 GNUNET_SERVICE_run (argc,
673 GNUNET_SERVICE_OPTION_NONE,
674 &run, NULL)) ? 0 : 1;
677 /* end of gnunet-service-sensor.c */