-avoid calling memcpy() with NULL argument, even if len is 0
[oweals/gnunet.git] / src / testbed-logger / gnunet-service-testbed-logger.c
1 /*
2   This file is part of GNUnet.
3   Copyright (C) 2008--2013 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18   Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file testbed-logger/gnunet-service-testbed-logger.c
23  * @brief service for collecting messages and writing to a file
24  * @author Sree Harsha Totakura
25  */
26
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29
30 /**
31  * Generic logging shorthand
32  */
33 #define LOG(type, ...)                         \
34   GNUNET_log (type, __VA_ARGS__)
35
36 /**
37  * Debug logging shorthand
38  */
39 #define LOG_DEBUG(...)                          \
40   LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
41
42 /**
43  * The message queue for sending messages to clients
44  */
45 struct MessageQueue
46 {
47   /**
48    * The message to be sent
49    */
50   struct GNUNET_MessageHeader *msg;
51
52   /**
53    * The client to send the message to
54    */
55   struct GNUNET_SERVER_Client *client;
56
57   /**
58    * next pointer for DLL
59    */
60   struct MessageQueue *next;
61
62   /**
63    * prev pointer for DLL
64    */
65   struct MessageQueue *prev;
66 };
67
68 /**
69  * The message queue head
70  */
71 static struct MessageQueue *mq_head;
72
73 /**
74  * The message queue tail
75  */
76 static struct MessageQueue *mq_tail;
77
78 /**
79  * Handle for buffered writing.
80  */
81 struct GNUNET_BIO_WriteHandle *bio;
82
83 /**
84  * The number of connections we have
85  */
86 static unsigned int nconn;
87
88 /**
89  * Are we shutting down?
90  */
91 static int in_shutdown;
92
93
94 /**
95  * Message handler for #GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
96  *
97  * @param cls NULL
98  * @param client identification of the client
99  * @param msg the actual message
100  */
101 static void
102 handle_log_msg (void *cls,
103                 struct GNUNET_SERVER_Client *client,
104                 const struct GNUNET_MessageHeader *msg)
105 {
106   uint16_t ms;
107
108   ms = ntohs (msg->size);
109   ms -= sizeof (struct GNUNET_MessageHeader);
110   GNUNET_BIO_write (bio, &msg[1], ms);
111   GNUNET_SERVER_receive_done (client, GNUNET_OK);
112 }
113
114
115 /**
116  * Task to clean up and shutdown nicely
117  *
118  * @param cls NULL
119  */
120 static void
121 shutdown_task (void *cls)
122 {
123   struct MessageQueue *mq_entry;
124
125   in_shutdown = GNUNET_YES;
126   if (0 != nconn)
127   {
128     /* Delay shutdown if there are active connections */
129     GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
130     return;
131   }
132   while (NULL != (mq_entry = mq_head))
133   {
134     GNUNET_free (mq_entry->msg);
135     GNUNET_SERVER_client_drop (mq_entry->client);
136     GNUNET_CONTAINER_DLL_remove (mq_head,
137                                  mq_tail,
138                                  mq_entry);
139     GNUNET_free (mq_entry);
140   }
141   GNUNET_break (GNUNET_OK == GNUNET_BIO_write_close (bio));
142 }
143
144
145 /**
146 x * Functions with this signature are called whenever a client
147  * is disconnected on the network level.
148  *
149  * @param cls closure
150  * @param client identification of the client; NULL
151  *        for the last call when the server is destroyed
152  */
153 static void
154 client_disconnected (void *cls,
155                      struct GNUNET_SERVER_Client *client)
156 {
157   if (NULL == client)
158   {
159     GNUNET_break (0 == nconn);
160     return;
161   }
162   nconn--;
163   if (GNUNET_YES == in_shutdown)
164     GNUNET_SCHEDULER_shutdown ();
165 }
166
167
168 /**
169  * Functions with this signature are called whenever a client
170  * is connected on the network level.
171  *
172  * @param cls closure
173  * @param client identification of the client
174  */
175 static void
176 client_connected (void *cls,
177                   struct GNUNET_SERVER_Client *client)
178 {
179   if (NULL == client)
180   {
181     GNUNET_break (0 == nconn);
182     return;
183   }
184   GNUNET_SERVER_client_persist_ (client);
185   nconn++;
186 }
187
188
189 /**
190  * Testbed setup
191  *
192  * @param cls closure
193  * @param server the initialized server
194  * @param cfg configuration to use
195  */
196 static void
197 logger_run (void *cls,
198             struct GNUNET_SERVER_Handle *server,
199             const struct GNUNET_CONFIGURATION_Handle *cfg)
200 {
201   static const struct GNUNET_SERVER_MessageHandler message_handlers[] = {
202     {&handle_log_msg, NULL, GNUNET_MESSAGE_TYPE_TESTBED_LOGGER_MSG, 0},
203     {NULL, NULL, 0, 0}
204   };
205   char *dir;
206   char *fn;
207   char *hname;
208   size_t hname_len;
209   pid_t pid;
210
211   if (GNUNET_OK !=
212       GNUNET_CONFIGURATION_get_value_filename (cfg,
213                                                "TESTBED-LOGGER",
214                                                "DIR",
215                                                &dir))
216   {
217     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
218                                "TESTBED-LOGGER",
219                                "DIR");
220     GNUNET_SCHEDULER_shutdown ();
221     return;
222   }
223   pid = getpid ();
224   hname_len = GNUNET_OS_get_hostname_max_length ();
225   hname = GNUNET_malloc (hname_len);
226   if (0 != gethostname (hname, hname_len))
227   {
228     LOG (GNUNET_ERROR_TYPE_ERROR,
229          "Cannot get hostname.  Exiting\n");
230     GNUNET_free (hname);
231     GNUNET_free (dir);
232     GNUNET_SCHEDULER_shutdown ();
233     return;
234   }
235   GNUNET_asprintf (&fn,
236                    "%s/%.*s_%jd.dat",
237                    dir,
238                    hname_len,
239                    hname,
240                    (intmax_t) pid);
241   GNUNET_free (hname);
242   GNUNET_free (dir);
243   if (NULL == (bio = GNUNET_BIO_write_open (fn)))
244   {
245     GNUNET_free (fn);
246     GNUNET_SCHEDULER_shutdown ();
247     return;
248   }
249   GNUNET_free (fn);
250   GNUNET_SERVER_add_handlers (server, message_handlers);
251   GNUNET_SERVER_connect_notify (server, &client_connected, NULL);
252   GNUNET_SERVER_disconnect_notify (server, &client_disconnected, NULL);
253   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
254   LOG_DEBUG ("TESTBED-LOGGER startup complete\n");
255 }
256
257
258 /**
259  * The starting point of execution
260  */
261 int
262 main (int argc, char *const *argv)
263 {
264   return (GNUNET_OK ==
265           GNUNET_SERVICE_run (argc, argv, "testbed-logger",
266                               GNUNET_SERVICE_OPTION_NONE,
267                               &logger_run, NULL)) ? 0 : 1;
268 }
269
270 /* end of gnunet-service-testbed-logger.c */