2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
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 sensordashboard/gnunet-service-sensordashboard.c
23 * @brief Service collecting sensor readings from peers
24 * @author Omar Tarabai
28 #include "gnunet_util_lib.h"
29 #include "gnunet_applications.h"
30 #include "sensordashboard.h"
31 #include "gnunet_cadet_service.h"
32 #include "gnunet_sensor_util_lib.h"
33 #include "gnunet_peerstore_service.h"
37 * Context of a connected client peer
39 struct ClientPeerContext
45 struct ClientPeerContext *prev;
50 struct ClientPeerContext *next;
53 * GNUnet Peer identity
55 struct GNUNET_PeerIdentity peerid;
58 * Handle to the cadet channel
60 struct GNUNET_CADET_Channel *ch;
63 * CADET transmit handle if we requested a transmission
65 struct GNUNET_CADET_TransmitHandle *th;
68 * Head of DLL of pending messages to be sent to client
70 struct PendingMessage *pm_head;
73 * Tail of DLL of pending messages to be sent to client
75 struct PendingMessage *pm_tail;
78 * Are we in the process of destroying this context?
85 * Message queued to be sent to a client stored in a DLL
93 struct PendingMessage *prev;
98 struct PendingMessage *next;
101 * Actual queued message
103 struct GNUNET_MessageHeader *msg;
108 * Carries a single reading from a sensor
110 struct ClientSensorReading
114 * Sensor this reading is related to
116 struct GNUNET_SENSOR_SensorInfo *sensor;
119 * Timestamp of taking the reading
121 struct GNUNET_TIME_Absolute timestamp;
137 * Path to sensor definition directory
139 static char *sensor_dir;
142 * Global hashmap of defined sensors
144 static struct GNUNET_CONTAINER_MultiHashMap *sensors;
147 * Handle to CADET service
149 static struct GNUNET_CADET_Handle *cadet;
152 * Handle to the peerstore service connection
154 static struct GNUNET_PEERSTORE_Handle *peerstore;
157 * Name of the subsystem used to store sensor values received from remote peers
160 static char *values_subsystem = "sensordashboard-values";
163 * Name of the subsystem used to store anomaly reports received from remote
166 static char *anomalies_subsystem = "sensordashboard-anomalies";
169 * Head of a DLL of all connected client peers
171 static struct ClientPeerContext *cp_head;
174 * Tail of a DLL of all connected client peers
176 static struct ClientPeerContext *cp_tail;
180 * Trigger sending next pending message to the given client peer if any.
182 * @param cp client peer context struct
185 trigger_send_next_msg (struct ClientPeerContext *cp);
189 * Destroy a given client peer context
191 * @param cp client peer context
194 destroy_clientpeer (struct ClientPeerContext *cp)
196 struct PendingMessage *pm;
198 cp->destroying = GNUNET_YES;
201 GNUNET_CADET_notify_transmit_ready_cancel (cp->th);
207 GNUNET_CONTAINER_DLL_remove (cp->pm_head, cp->pm_tail, pm);
208 GNUNET_free (pm->msg);
214 GNUNET_CADET_channel_destroy (cp->ch);
222 * Task run during shutdown.
228 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
230 struct ClientPeerContext *cp;
235 GNUNET_CONTAINER_DLL_remove (cp_head, cp_tail, cp);
236 destroy_clientpeer (cp);
241 GNUNET_CADET_disconnect (cadet);
244 if (NULL != peerstore)
246 GNUNET_PEERSTORE_disconnect (peerstore, GNUNET_YES);
249 GNUNET_SENSOR_destroy_sensors (sensors);
250 if (NULL != sensor_dir)
252 GNUNET_free (sensor_dir);
255 GNUNET_SCHEDULER_shutdown ();
260 * Function called whenever a channel is destroyed. Should clean up
261 * any associated state.
263 * It must NOT call #GNUNET_CADET_channel_destroy on the channel.
265 * @param cls closure (set from #GNUNET_CADET_connect)
266 * @param channel connection to the other end (henceforth invalid)
267 * @param channel_ctx place where local state associated
268 * with the channel is stored
271 cadet_channel_destroyed (void *cls, const struct GNUNET_CADET_Channel *channel,
274 struct ClientPeerContext *cp = channel_ctx;
276 if (GNUNET_YES == cp->destroying)
279 GNUNET_CONTAINER_DLL_remove (cp_head, cp_tail, cp);
280 destroy_clientpeer (cp);
285 * Method called whenever another peer has added us to a channel
286 * the other peer initiated.
287 * Only called (once) upon reception of data with a message type which was
288 * subscribed to in #GNUNET_CADET_connect.
290 * A call to #GNUNET_CADET_channel_destroy causes the channel to be ignored. In
291 * this case the handler MUST return NULL.
294 * @param channel new handle to the channel
295 * @param initiator peer that started the channel
296 * @param port Port this channel is for.
297 * @param options CadetOption flag field, with all active option bits set to 1.
299 * @return initial channel context for the channel
300 * (can be NULL -- that's not an error)
303 cadet_channel_created (void *cls, struct GNUNET_CADET_Channel *channel,
304 const struct GNUNET_PeerIdentity *initiator,
305 uint32_t port, enum GNUNET_CADET_ChannelOption options)
307 struct ClientPeerContext *cp;
309 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
310 "Received a channel connection from peer `%s'.\n",
311 GNUNET_i2s (initiator));
312 cp = GNUNET_new (struct ClientPeerContext);
314 cp->peerid = *initiator;
316 cp->destroying = GNUNET_NO;
317 GNUNET_CONTAINER_DLL_insert (cp_head, cp_tail, cp);
323 * Function called to notify a client about the connection begin ready
324 * to queue more data. @a buf will be NULL and @a size zero if the
325 * connection was closed for writing in the meantime.
327 * Perform the actual sending of the message to client peer.
329 * @param cls closure, a `struct ClientPeerContext *`
330 * @param size number of bytes available in @a buf
331 * @param buf where the callee should write the message
332 * @return number of bytes written to @a buf
335 do_send_msg (void *cls, size_t size, void *buf)
337 struct ClientPeerContext *cp = cls;
338 struct PendingMessage *pm;
343 msg_size = ntohs (pm->msg->size);
344 GNUNET_CONTAINER_DLL_remove (cp->pm_head, cp->pm_tail, pm);
345 if (NULL == buf || size < msg_size)
347 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
348 _("Error trying to send a message to peer `%s'.\n"),
349 GNUNET_i2s (&cp->peerid));
352 memcpy (buf, pm->msg, msg_size);
353 GNUNET_free (pm->msg);
355 trigger_send_next_msg (cp);
361 * Trigger sending next pending message to the given client peer if any.
363 * @param cp client peer context struct
366 trigger_send_next_msg (struct ClientPeerContext *cp)
368 struct PendingMessage *pm;
370 if (NULL == cp->pm_head)
376 GNUNET_CADET_notify_transmit_ready (cp->ch, GNUNET_YES,
377 GNUNET_TIME_UNIT_FOREVER_REL,
378 ntohs (pm->msg->size), &do_send_msg,
384 * Add a new message to the queue to be sent to the given client peer.
386 * @param msg Message to be queued
387 * @param cp Client peer context
390 queue_msg (struct GNUNET_MessageHeader *msg, struct ClientPeerContext *cp)
392 struct PendingMessage *pm;
394 pm = GNUNET_new (struct PendingMessage);
397 GNUNET_CONTAINER_DLL_insert_tail (cp->pm_head, cp->pm_tail, pm);
398 trigger_send_next_msg (cp);
403 * Called with any anomaly report received from a peer.
405 * Each time the function must call #GNUNET_CADET_receive_done on the channel
406 * in order to receive the next message. This doesn't need to be immediate:
407 * can be delayed if some processing is done on the message.
409 * @param cls Closure (set from #GNUNET_CADET_connect).
410 * @param channel Connection to the other end.
411 * @param channel_ctx Place to store local state associated with the channel.
412 * @param message The actual message.
413 * @return #GNUNET_OK to keep the channel open,
414 * #GNUNET_SYSERR to close it (signal serious error).
417 handle_anomaly_report (void *cls, struct GNUNET_CADET_Channel *channel,
419 const struct GNUNET_MessageHeader *message)
421 struct ClientPeerContext *cp = *channel_ctx;
422 struct GNUNET_SENSOR_AnomalyReportMessage *anomaly_msg;
423 struct GNUNET_SENSOR_SensorInfo *sensor;
424 struct GNUNET_SENSOR_DashboardAnomalyEntry *anomaly_entry;
425 struct GNUNET_TIME_Absolute expiry;
427 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
428 "Received an anomaly report message from peer `%s'.\n",
429 GNUNET_i2s (&cp->peerid));
430 anomaly_msg = (struct GNUNET_SENSOR_AnomalyReportMessage *) message;
432 GNUNET_CONTAINER_multihashmap_get (sensors,
433 &anomaly_msg->sensorname_hash);
437 return GNUNET_SYSERR;
439 anomaly_entry = GNUNET_new (struct GNUNET_SENSOR_DashboardAnomalyEntry);
440 anomaly_entry->anomalous = ntohs (anomaly_msg->anomalous);
441 anomaly_entry->anomalous_neighbors = anomaly_msg->anomalous_neighbors;
444 anomaly_entry->anomalous) ? GNUNET_TIME_UNIT_FOREVER_ABS :
445 GNUNET_TIME_absolute_get ();
446 GNUNET_PEERSTORE_store (peerstore, anomalies_subsystem, &cp->peerid,
447 sensor->name, anomaly_entry,
448 sizeof (struct GNUNET_SENSOR_DashboardAnomalyEntry),
449 expiry, GNUNET_PEERSTORE_STOREOPTION_REPLACE, NULL,
451 GNUNET_free (anomaly_entry);
452 GNUNET_CADET_receive_done (channel);
458 * Iterate over defined sensors, creates and sends brief sensor information to
459 * given client peer over CADET.
461 * @param cls closure, the client peer
462 * @param key sensor key
463 * @param value sensor value
464 * @return #GNUNET_YES to continue iteration
467 send_sensor_brief (void *cls, const struct GNUNET_HashCode *key, void *value)
469 struct ClientPeerContext *cp = cls;
470 struct GNUNET_SENSOR_SensorInfo *sensor = value;
471 struct GNUNET_SENSOR_SensorBriefMessage *msg;
472 uint16_t sensorname_size;
475 /* Create message struct */
476 sensorname_size = strlen (sensor->name) + 1;
478 sizeof (struct GNUNET_SENSOR_SensorBriefMessage) + sensorname_size;
479 msg = GNUNET_malloc (total_size);
480 msg->header.size = htons (total_size);
481 msg->header.type = htons (GNUNET_MESSAGE_TYPE_SENSOR_BRIEF);
482 msg->name_size = htons (sensorname_size);
483 msg->version_major = htons (sensor->version_major);
484 msg->version_minor = htons (sensor->version_minor);
485 memcpy (&msg[1], sensor->name, sensorname_size);
487 queue_msg ((struct GNUNET_MessageHeader *) msg, cp);
493 * Called with any sensor list request received.
495 * Each time the function must call #GNUNET_CADET_receive_done on the channel
496 * in order to receive the next message. This doesn't need to be immediate:
497 * can be delayed if some processing is done on the message.
499 * @param cls Closure (set from #GNUNET_CADET_connect).
500 * @param channel Connection to the other end.
501 * @param channel_ctx Place to store local state associated with the channel.
502 * @param message The actual message.
503 * @return #GNUNET_OK to keep the channel open,
504 * #GNUNET_SYSERR to close it (signal serious error).
507 handle_sensor_list_req (void *cls, struct GNUNET_CADET_Channel *channel,
509 const struct GNUNET_MessageHeader *message)
511 struct ClientPeerContext *cp = *channel_ctx;
512 struct GNUNET_MessageHeader *end_msg;
514 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
515 "Received a sensor list request from peer `%s'.\n",
516 GNUNET_i2s (&cp->peerid));
517 GNUNET_CONTAINER_multihashmap_iterate (sensors, &send_sensor_brief, cp);
518 end_msg = GNUNET_new (struct GNUNET_MessageHeader);
520 end_msg->size = htons (sizeof (struct GNUNET_MessageHeader));
521 end_msg->type = htons (GNUNET_MESSAGE_TYPE_SENSOR_END);
522 queue_msg (end_msg, cp);
523 GNUNET_CADET_receive_done (channel);
529 * Parses a sensor reading message struct
531 * @param msg message header received
532 * @param sensors multihashmap of loaded sensors
533 * @return sensor reading struct or NULL if error
535 static struct ClientSensorReading *
536 parse_reading_message (const struct GNUNET_MessageHeader *msg,
537 struct GNUNET_CONTAINER_MultiHashMap *sensors)
541 struct GNUNET_SENSOR_ValueMessage *vm;
542 struct GNUNET_SENSOR_SensorInfo *sensor;
543 struct ClientSensorReading *reading;
545 msg_size = ntohs (msg->size);
546 if (msg_size < sizeof (struct GNUNET_SENSOR_ValueMessage))
551 vm = (struct GNUNET_SENSOR_ValueMessage *) msg;
552 value_size = ntohs (vm->value_size);
553 if ((sizeof (struct GNUNET_SENSOR_ValueMessage) + value_size) != msg_size)
558 sensor = GNUNET_CONTAINER_multihashmap_get (sensors, &vm->sensorname_hash);
561 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
562 "Unknown sensor name in reading message.\n");
565 if ((sensor->version_minor != ntohs (vm->sensorversion_minor)) ||
566 (sensor->version_major != ntohs (vm->sensorversion_major)))
568 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
569 "Sensor version mismatch in reading message.\n");
572 if (0 == strcmp (sensor->expected_datatype, "numeric") &&
573 sizeof (double) != value_size)
575 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
576 "Invalid value size for a numerical sensor.\n");
579 reading = GNUNET_new (struct ClientSensorReading);
580 reading->sensor = sensor;
581 reading->timestamp = vm->timestamp;
582 reading->value_size = value_size;
583 reading->value = GNUNET_memdup (&vm[1], value_size);
589 * Called with any sensor reading messages received from CADET.
591 * Each time the function must call #GNUNET_CADET_receive_done on the channel
592 * in order to receive the next message. This doesn't need to be immediate:
593 * can be delayed if some processing is done on the message.
595 * @param cls Closure (set from #GNUNET_CADET_connect).
596 * @param channel Connection to the other end.
597 * @param channel_ctx Place to store local state associated with the channel.
598 * @param message The actual message.
599 * @return #GNUNET_OK to keep the channel open,
600 * #GNUNET_SYSERR to close it (signal serious error).
603 handle_sensor_reading (void *cls, struct GNUNET_CADET_Channel *channel,
605 const struct GNUNET_MessageHeader *message)
607 struct ClientPeerContext *cp = *channel_ctx;
608 struct ClientSensorReading *reading;
610 reading = parse_reading_message (message, sensors);
613 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
614 "Received an invalid sensor reading from peer `%s'.\n",
615 GNUNET_i2s (&cp->peerid));
616 return GNUNET_SYSERR;
618 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
619 "Received a sensor reading from peer `%s':\n"
620 "# Sensor name: `%s'\n" "# Timestamp: %" PRIu64 "\n"
621 "# Value size: %" PRIu64 ".\n", GNUNET_i2s (&cp->peerid),
622 reading->sensor->name, reading->timestamp, reading->value_size);
623 GNUNET_PEERSTORE_store (peerstore, values_subsystem, &cp->peerid,
624 reading->sensor->name, reading->value,
625 reading->value_size, GNUNET_TIME_UNIT_FOREVER_ABS,
626 GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, NULL, NULL);
627 GNUNET_free (reading->value);
628 GNUNET_free (reading);
629 GNUNET_CADET_receive_done (channel);
635 * Create a message with full information about sensor
637 * @param sensorname Name of sensor requested
638 * @return Message ready to be sent to client or NULL on error
640 static struct GNUNET_SENSOR_SensorFullMessage *
641 create_full_sensor_msg (char *sensorname)
643 struct GNUNET_HashCode key;
644 struct GNUNET_SENSOR_SensorInfo *sensor;
645 struct GNUNET_SENSOR_SensorFullMessage *msg;
647 char *sensorscript_path;
648 uint64_t sensorname_size;
649 uint64_t sensorfile_size;
650 uint64_t sensorscriptname_size;
651 uint64_t sensorscript_size;
655 GNUNET_CRYPTO_hash (sensorname, strlen (sensorname) + 1, &key);
656 sensor = GNUNET_CONTAINER_multihashmap_get (sensors, &key);
659 GNUNET_asprintf (&sensor_path, "%s%s", sensor_dir, sensorname);
661 GNUNET_DISK_file_size (sensor_path, &sensorfile_size, GNUNET_NO,
664 GNUNET_free (sensor_dir);
665 GNUNET_free (sensor_path);
668 sensorname_size = strlen (sensorname) + 1;
669 sensorscript_size = 0;
670 sensorscriptname_size = 0;
671 /* Test if there is an associated script */
672 if (NULL != sensor->ext_process)
674 GNUNET_asprintf (&sensorscript_path, "%s%s-files%s%s", sensor_dir,
675 sensor->name, DIR_SEPARATOR_STR, sensor->ext_process);
677 GNUNET_DISK_file_size (sensorscript_path, &sensorscript_size, GNUNET_NO,
680 sensorscriptname_size = strlen (sensor->ext_process) + 1;
683 /* Construct the msg */
685 sizeof (struct GNUNET_SENSOR_SensorFullMessage) + sensorname_size +
686 sensorfile_size + sensorscriptname_size + sensorscript_size;
687 msg = GNUNET_malloc (total_size);
688 msg->header.size = htons (total_size);
689 msg->header.type = htons (GNUNET_MESSAGE_TYPE_SENSOR_FULL);
690 msg->sensorname_size = htons (sensorname_size);
691 msg->sensorfile_size = htons (sensorfile_size);
692 msg->scriptname_size = htons (sensorscriptname_size);
693 msg->scriptfile_size = htons (sensorscript_size);
695 memcpy (dummy, sensorname, sensorname_size);
696 dummy += sensorname_size;
697 GNUNET_DISK_fn_read (sensor_path, dummy, sensorfile_size);
698 dummy += sensorfile_size;
699 if (sensorscriptname_size > 0)
701 memcpy (dummy, sensor->ext_process, sensorscriptname_size);
702 dummy += sensorscriptname_size;
703 GNUNET_DISK_fn_read (sensorscript_path, dummy, sensorscript_size);
704 GNUNET_free (sensorscript_path);
706 GNUNET_free (sensor_path);
712 * Called with any request for full sensor information.
714 * Each time the function must call #GNUNET_CADET_receive_done on the channel
715 * in order to receive the next message. This doesn't need to be immediate:
716 * can be delayed if some processing is done on the message.
718 * @param cls Closure (set from #GNUNET_CADET_connect).
719 * @param channel Connection to the other end.
720 * @param channel_ctx Place to store local state associated with the channel.
721 * @param message The actual message.
722 * @return #GNUNET_OK to keep the channel open,
723 * #GNUNET_SYSERR to close it (signal serious error).
726 handle_sensor_full_req (void *cls, struct GNUNET_CADET_Channel *channel,
728 const struct GNUNET_MessageHeader *message)
730 struct ClientPeerContext *cp = *channel_ctx;
731 struct GNUNET_SENSOR_SensorBriefMessage *sbm = NULL;
732 struct GNUNET_SENSOR_SensorFullMessage *sfm;
734 uint16_t sensorname_size;
736 msg_size = ntohs (message->size);
737 /* parse & error check */
738 if (msg_size > sizeof (struct GNUNET_SENSOR_SensorBriefMessage))
740 sbm = (struct GNUNET_SENSOR_SensorBriefMessage *) message;
741 sensorname_size = ntohs (sbm->name_size);
743 sizeof (struct GNUNET_SENSOR_SensorBriefMessage) + sensorname_size)
748 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
749 "Received an invalid full sensor request from peer `%s'.\n",
750 GNUNET_i2s (&cp->peerid));
751 return GNUNET_SYSERR;
753 /* Create and send msg with full sensor info */
754 sfm = create_full_sensor_msg ((char *) &sbm[1]);
757 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
758 "Error creating full sensor info msg for sensor `%s'.\n",
760 return GNUNET_SYSERR;
762 queue_msg ((struct GNUNET_MessageHeader *) sfm, cp);
763 GNUNET_CADET_receive_done (channel);
769 * Process sensordashboard requests.
772 * @param server the initialized server
773 * @param cfg configuration to use
776 run (void *cls, struct GNUNET_SERVER_Handle *server,
777 const struct GNUNET_CONFIGURATION_Handle *cfg)
779 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
782 static struct GNUNET_CADET_MessageHandler cadet_handlers[] = {
783 {&handle_sensor_reading,
784 GNUNET_MESSAGE_TYPE_SENSOR_READING, 0},
785 {&handle_sensor_list_req,
786 GNUNET_MESSAGE_TYPE_SENSOR_LIST_REQ,
787 sizeof (struct GNUNET_MessageHeader)},
788 {&handle_sensor_full_req,
789 GNUNET_MESSAGE_TYPE_SENSOR_FULL_REQ,
790 sizeof (struct GNUNET_MessageHeader)},
791 {&handle_anomaly_report,
792 GNUNET_MESSAGE_TYPE_SENSOR_ANOMALY_REPORT,
793 sizeof (struct GNUNET_SENSOR_AnomalyReportMessage)},
796 static uint32_t cadet_ports[] = {
797 GNUNET_APPLICATION_TYPE_SENSORDASHBOARD,
798 GNUNET_APPLICATION_TYPE_SENSORUPDATE,
799 GNUNET_APPLICATION_TYPE_END
803 GNUNET_CONFIGURATION_get_value_filename (cfg, "SENSOR", "SENSOR_DIR",
805 sensor_dir = GNUNET_SENSOR_get_default_sensor_dir ();
806 sensors = GNUNET_SENSOR_load_all_sensors (sensor_dir);
807 GNUNET_assert (NULL != sensors);
809 GNUNET_CADET_connect (cfg, NULL, &cadet_channel_created,
810 &cadet_channel_destroyed, cadet_handlers,
814 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
815 _("Failed to connect to `%s' service.\n"), "CADET");
816 GNUNET_SCHEDULER_add_now (&cleanup_task, NULL);
819 peerstore = GNUNET_PEERSTORE_connect (cfg);
820 if (NULL == peerstore)
822 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
823 _("Failed to connect to `%s' service.\n"), "PEERSTORE");
824 GNUNET_SCHEDULER_add_now (&cleanup_task, NULL);
827 GNUNET_SERVER_add_handlers (server, handlers);
828 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
834 * The main function for the sensordashboard service.
836 * @param argc number of arguments from the command line
837 * @param argv command line arguments
838 * @return 0 ok, 1 on error
841 main (int argc, char *const *argv)
844 GNUNET_SERVICE_run (argc, argv, "sensordashboard",
845 GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1;
848 /* end of gnunet-service-sensordashboard.c */