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;
65 * Sensor will not run again unless
66 * explicitly enabled or reloaded
68 * @param sensor sensor information
70 static void set_sensor_enabled(struct SensorInfo *sensor, int state)
72 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
73 "Sensor `%s': Setting enabled to %d.\n",
75 sensor->enabled = GNUNET_NO;
76 GNUNET_assert(NULL != sensor->cfg);
77 GNUNET_CONFIGURATION_set_value_string(sensor->cfg, sensor->name, "ENABLED",
78 (GNUNET_YES == state)?"YES":"NO");
79 GNUNET_CONFIGURATION_write(sensor->cfg, sensor->def_file);
83 * Task run during shutdown.
89 shutdown_task (void *cls,
90 const struct GNUNET_SCHEDULER_TaskContext *tc)
92 SENSOR_reporting_stop();
93 SENSOR_analysis_stop();
94 GNUNET_SENSOR_destroy_sensors (sensors);
95 if(NULL != statistics)
97 GNUNET_STATISTICS_destroy(statistics, GNUNET_YES);
100 if(NULL != peerstore)
102 GNUNET_PEERSTORE_disconnect(peerstore);
105 GNUNET_SCHEDULER_shutdown();
110 * A client disconnected. Remove all of its data structure entries.
112 * @param cls closure, NULL
113 * @param client identification of the client
116 handle_client_disconnect (void *cls,
117 struct GNUNET_SERVER_Client
123 * Creates a structure with basic sensor info to be sent to a client
125 * @param sensor sensor information
126 * @return message ready to be sent to client
128 static struct SensorInfoMessage *
129 create_sensor_info_msg(struct SensorInfo *sensor)
131 struct SensorInfoMessage *msg;
137 name_len = strlen(sensor->name);
138 if(NULL == sensor->description)
141 desc_len = strlen(sensor->description) + 1;
143 len += sizeof(struct SensorInfoMessage);
146 msg = GNUNET_malloc(len);
147 msg->header.size = htons(len);
148 msg->header.type = htons(GNUNET_MESSAGE_TYPE_SENSOR_INFO);
149 msg->name_len = htons(name_len);
150 msg->description_len = htons(desc_len);
151 msg->version_major = htons(sensor->version_major);
152 msg->version_minor = htons(sensor->version_minor);
153 str_ptr = (char*) &msg[1];
154 memcpy(str_ptr, sensor->name, name_len);
155 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending sensor name (%d): %.*s\n",
156 name_len, name_len, str_ptr);
158 memcpy(str_ptr, sensor->description, desc_len);
159 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending sensor description (%d): %.*s\n",
160 desc_len, desc_len, str_ptr);
166 * Handle GET SENSOR message.
169 * @param client identification of the client
170 * @param message the actual message
173 handle_get_sensor (void *cls, struct GNUNET_SERVER_Client *client,
174 const struct GNUNET_MessageHeader *message)
176 struct GNUNET_SERVER_TransmitContext *tc;
178 size_t sensorname_len;
179 struct GNUNET_HashCode key;
180 struct SensorInfo *sensorinfo;
181 struct SensorInfoMessage *msg;
183 sensorname = (char *)&message[1];
184 sensorname_len = ntohs(message->size) - sizeof(struct GNUNET_MessageHeader);
185 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received for sensor (%d) `%.*s'\n",
186 "GET SENSOR", sensorname_len, sensorname_len, sensorname);
187 tc = GNUNET_SERVER_transmit_context_create (client);
188 GNUNET_CRYPTO_hash(sensorname, sensorname_len, &key);
189 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created key hash for requested sensor\n");
190 sensorinfo = (struct SensorInfo *)GNUNET_CONTAINER_multihashmap_get(sensors, &key);
191 if(NULL != sensorinfo)
193 msg = create_sensor_info_msg(sensorinfo);
194 GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
198 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Requested sensor `%.*s' was not found\n",
199 sensorname_len, sensorname);
200 GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
201 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
205 * Iterator for sensors and adds them to transmit context
207 * @param cls a 'struct GNUNET_SERVER_TransmitContext *'
208 * @param key hash of sensor name, key to hashmap
209 * @param value a 'struct SensorInfo *'
211 int add_sensor_to_tc(void *cls,
212 const struct GNUNET_HashCode *key, void *value)
214 struct GNUNET_SERVER_TransmitContext *tc = cls;
215 struct SensorInfo *sensorinfo = value;
216 struct SensorInfoMessage *msg;
218 msg = create_sensor_info_msg(sensorinfo);
219 GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
227 * Handle GET ALL SENSORS message.
230 * @param client identification of the client
231 * @param message the actual message
234 handle_get_all_sensors (void *cls, struct GNUNET_SERVER_Client *client,
235 const struct GNUNET_MessageHeader *message)
237 struct GNUNET_SERVER_TransmitContext *tc;
239 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received.\n",
241 tc = GNUNET_SERVER_transmit_context_create (client);
242 GNUNET_CONTAINER_multihashmap_iterate(sensors, &add_sensor_to_tc, tc);
243 GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
244 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
248 * Do a series of checks to determine if sensor should execute
250 * @return #GNUNET_YES / #GNUNET_NO
253 should_run_sensor(struct SensorInfo *sensorinfo)
255 struct GNUNET_TIME_Absolute now;
257 if(GNUNET_NO == sensorinfo->enabled)
259 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Sensor `%s' is disabled, will not run\n", sensorinfo->name);
262 now = GNUNET_TIME_absolute_get();
263 if(NULL != sensorinfo->start_time
264 && now.abs_value_us < sensorinfo->start_time->abs_value_us)
266 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Start time for sensor `%s' not reached yet, will not run\n", sensorinfo->name);
269 if(NULL != sensorinfo->end_time
270 && now.abs_value_us >= sensorinfo->end_time->abs_value_us)
272 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Sensor `%s' expired, disabling.\n", sensorinfo->name);
273 set_sensor_enabled(sensorinfo, GNUNET_NO);
280 * Callback function to process statistic values
282 * @param cls 'struct SensorInfo *'
283 * @param subsystem name of subsystem that created the statistic
284 * @param name the name of the datum
285 * @param value the current value
286 * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
287 * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
289 int sensor_statistics_iterator (void *cls,
295 struct SensorInfo *sensorinfo = cls;
296 double dvalue = (double)value;
297 struct GNUNET_TIME_Absolute expiry;
299 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Received a value for sensor `%s': %" PRIu64 "\n", sensorinfo->name, value);
300 expiry = GNUNET_TIME_relative_to_absolute(sensorinfo->lifetime);
301 GNUNET_PEERSTORE_store(peerstore,
308 GNUNET_PEERSTORE_STOREOPTION_MULTIPLE,
311 return GNUNET_SYSERR; /* We only want one value */
315 * Continuation called after sensor gets all gnunet statistics values
317 * @param cls 'struct SensorInfo *'
318 * @param success #GNUNET_OK if statistics were
319 * successfully obtained, #GNUNET_SYSERR if not.
321 void end_sensor_run_stat (void *cls, int success)
323 struct SensorInfo *sensorinfo = cls;
325 sensorinfo->gnunet_stat_get_handle = NULL;
326 sensorinfo->running = GNUNET_NO;
330 * Tries to parse a received sensor value to its
333 * @param value the string value received, should be null terminated
334 * @param sensor sensor information struct
335 * @param ret pointer to parsed value
336 * @return size of new parsed value, 0 for error
339 parse_sensor_value (const char *value, struct SensorInfo* sensor, void **ret)
347 if(0 == strcmp("numeric", sensor->expected_datatype))
349 dval = GNUNET_new(double);
350 *dval = strtod(value, &endptr);
354 return sizeof(double);
356 if(0 == strcmp("string", sensor->expected_datatype))
358 *ret = GNUNET_strdup(value);
359 return strlen(value) + 1;
361 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
362 _("Unknown value type expected by sensor, this should not happen.\n"));
367 * Callback for output of executed sensor process
369 * @param cls 'struct SensorInfo *'
370 * @param line line of output from a command, NULL for the end
372 void sensor_process_callback (void *cls, const char *line)
374 struct SensorInfo *sensorinfo = cls;
377 struct GNUNET_TIME_Absolute expiry;
381 GNUNET_OS_command_stop(sensorinfo->ext_cmd);
382 sensorinfo->ext_cmd = NULL;
383 sensorinfo->running = GNUNET_NO;
384 sensorinfo->ext_cmd_value_received = GNUNET_NO;
387 if(GNUNET_YES == sensorinfo->ext_cmd_value_received)
388 return; /* We only want one *valid* value */
389 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Received a value for sensor `%s': %s\n", sensorinfo->name, line);
390 valsize = parse_sensor_value(line, sensorinfo, &value);
391 if (valsize == 0) /* invalid value, FIXME: should we disable the sensor now? */
393 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
394 _("Received an invalid value for sensor `%s': %s\n"),
395 sensorinfo->name, line);
399 sensorinfo->ext_cmd_value_received = GNUNET_YES;
400 expiry = GNUNET_TIME_relative_to_absolute(sensorinfo->lifetime);
401 GNUNET_PEERSTORE_store(peerstore,
408 GNUNET_PEERSTORE_STOREOPTION_MULTIPLE,
416 * Checks if the given file is a path
418 * @return #GNUNET_YES / #GNUNET_NO
421 is_path(char *filename)
426 filename_len = strlen(filename);
427 for(i = 0; i < filename_len; i++)
429 if(DIR_SEPARATOR == filename[i])
436 * Actual execution of a sensor
438 * @param cls 'struct SensorInfo'
442 sensor_run (void *cls,
443 const struct GNUNET_SCHEDULER_TaskContext * tc)
445 struct SensorInfo *sensorinfo = cls;
450 sensorinfo->execution_task = GNUNET_SCHEDULER_add_delayed(sensorinfo->interval, &sensor_run, sensorinfo);
451 if(GNUNET_YES == sensorinfo->running) //FIXME: should we try to kill?
453 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Sensor `%s' running for too long, will try again next interval\n", sensorinfo->name);
456 if(GNUNET_NO == should_run_sensor(sensorinfo))
458 sensorinfo->running = GNUNET_YES;
459 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Starting the execution of sensor `%s'\n", sensorinfo->name);
460 if(0 == strcmp ("gnunet-statistics", sensorinfo->source))
462 sensorinfo->gnunet_stat_get_handle = GNUNET_STATISTICS_get(statistics,
463 sensorinfo->gnunet_stat_service,
464 sensorinfo->gnunet_stat_name,
465 sensorinfo->interval, //try to get values only for the interval of the sensor
466 &end_sensor_run_stat,
467 &sensor_statistics_iterator,
470 else if(0 == strcmp ("process", sensorinfo->source))
472 if(GNUNET_YES == is_path(sensorinfo->ext_process))
474 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
475 _("Sensor `%s': External process should not be a path, disabling sensor.\n"),
477 set_sensor_enabled(sensorinfo, GNUNET_NO);
480 //check if the process exists in $PATH
481 process_path = GNUNET_strdup(sensorinfo->ext_process);
483 GNUNET_OS_check_helper_binary(process_path, GNUNET_NO, NULL);
484 if(GNUNET_SYSERR == check_result)
486 //search in sensor directory
487 sensors_dir = GNUNET_SENSOR_get_sensor_dir ();
488 GNUNET_free(process_path);
489 GNUNET_asprintf(&process_path, "%s%s-files%s%s",
493 sensorinfo->ext_process);
494 GNUNET_free(sensors_dir);
496 GNUNET_OS_check_helper_binary(process_path, GNUNET_NO, NULL);
498 if(GNUNET_SYSERR == check_result)
500 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
501 _("Sensor `%s' process `%s' problem: binary doesn't exist or not executable\n"),
503 sensorinfo->ext_process);
504 set_sensor_enabled(sensorinfo, GNUNET_NO);
505 sensorinfo->running = GNUNET_NO;
506 GNUNET_free(process_path);
509 sensorinfo->ext_cmd_value_received = GNUNET_NO;
510 sensorinfo->ext_cmd = GNUNET_OS_command_run(&sensor_process_callback,
512 GNUNET_TIME_UNIT_FOREVER_REL,
514 sensorinfo->ext_process,
515 sensorinfo->ext_args,
517 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Process started for sensor `%s'\n", sensorinfo->name);
518 GNUNET_free(process_path);
522 sensorinfo->running = GNUNET_NO;
523 GNUNET_break(0); //shouldn't happen
528 * Starts the execution of a sensor
531 * @param key hash of sensor name, key to hashmap (unused)
532 * @param value a 'struct SensorInfo *'
533 * @return #GNUNET_YES if we should continue to
537 int schedule_sensor(void *cls,
538 const struct GNUNET_HashCode *key, void *value)
540 struct SensorInfo *sensorinfo = value;
542 if(GNUNET_NO == should_run_sensor(sensorinfo))
544 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Scheduling sensor `%s' to run after %" PRIu64 " microseconds\n",
545 sensorinfo->name, sensorinfo->interval.rel_value_us);
546 if(GNUNET_SCHEDULER_NO_TASK != sensorinfo->execution_task)
548 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
549 _("Sensor `%s' execution task already set, this should not happen\n"), sensorinfo->name);
552 sensorinfo->execution_task = GNUNET_SCHEDULER_add_delayed(sensorinfo->interval, &sensor_run, sensorinfo);
557 * Starts the execution of all enabled sensors
561 schedule_all_sensors()
563 GNUNET_CONTAINER_multihashmap_iterate(sensors, &schedule_sensor, NULL);
567 * Process statistics requests.
570 * @param server the initialized server
571 * @param c configuration to use
575 struct GNUNET_SERVER_Handle *server,
576 const struct GNUNET_CONFIGURATION_Handle *c)
578 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
579 {&handle_get_sensor, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GET,
581 {&handle_get_all_sensors, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GETALL,
582 sizeof (struct GNUNET_MessageHeader)},
587 sensors = GNUNET_SENSOR_load_all_sensors ();
588 schedule_all_sensors();
589 SENSOR_analysis_start(c, sensors);
590 SENSOR_reporting_start(c, sensors);
591 statistics = GNUNET_STATISTICS_create("sensor", cfg);
592 GNUNET_CRYPTO_get_peer_identity(cfg, &peerid);
593 peerstore = GNUNET_PEERSTORE_connect(cfg);
594 GNUNET_SERVER_add_handlers (server, handlers);
595 GNUNET_SERVER_disconnect_notify (server,
596 &handle_client_disconnect,
598 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
605 * The main function for the sensor service.
607 * @param argc number of arguments from the command line
608 * @param argv command line arguments
609 * @return 0 ok, 1 on error
612 main (int argc, char *const *argv)
615 GNUNET_SERVICE_run (argc,
618 GNUNET_SERVICE_OPTION_NONE,
619 &run, NULL)) ? 0 : 1;
622 /* end of gnunet-service-sensor.c */