Line data Source code
1 : /*
2 : * SPDX license identifier: MPL-2.0
3 : *
4 : * Copyright (C) 2015 Advanced Driver Information Technology.
5 : * This code is developed by Advanced Driver Information Technology.
6 : * Copyright of Advanced Driver Information Technology, Bosch and DENSO.
7 : *
8 : * This file is part of COVESA Project DLT - Diagnostic Log and Trace.
9 : *
10 : * This Source Code Form is subject to the terms of the
11 : * Mozilla Public License (MPL), v. 2.0.
12 : * If a copy of the MPL was not distributed with this file,
13 : * You can obtain one at http://mozilla.org/MPL/2.0/.
14 : *
15 : * For further information see http://www.covesa.org/.
16 : */
17 :
18 : /*!
19 : * \author
20 : * Frederic Berat <fberat@de.adit-jv.com>
21 : *
22 : * \copyright Copyright © 2015 Advanced Driver Information Technology. \n
23 : * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/.
24 : *
25 : * \file dlt_daemon_connection.c
26 : */
27 :
28 : #include <errno.h>
29 : #include <stdio.h>
30 : #include <stdlib.h>
31 : #include <string.h>
32 : #include <unistd.h>
33 :
34 : #include <sys/socket.h>
35 : #include <syslog.h>
36 : #include <sys/stat.h>
37 : #include <sys/types.h>
38 :
39 : #include "dlt_daemon_connection_types.h"
40 : #include "dlt_daemon_connection.h"
41 : #include "dlt_daemon_event_handler_types.h"
42 : #include "dlt_daemon_event_handler.h"
43 : #include "dlt-daemon.h"
44 : #include "dlt-daemon_cfg.h"
45 : #include "dlt_daemon_common.h"
46 : #include "dlt_common.h"
47 : #include "dlt_log.h"
48 : #include "dlt_gateway.h"
49 : #include "dlt_daemon_socket.h"
50 :
51 : static DltConnectionId connectionId;
52 : extern char *app_recv_buffer;
53 :
54 : /** @brief Generic sending function.
55 : *
56 : * We manage different type of connection which have similar send/write
57 : * functions. We can then abstract the data transfer using this function,
58 : * moreover as we often transfer data to different kind of connection
59 : * within the same loop.
60 : *
61 : * @param conn The connection structure.
62 : * @param msg The message buffer to be sent
63 : * @param msg_size The length of the message to be sent
64 : *
65 : * @return DLT_DAEMON_ERROR_OK on success, DLT_DAEMON_ERROR_SEND_FAILED
66 : * on send failure, DLT_DAEMON_ERROR_UNKNOWN otherwise.
67 : * errno is appropriately set.
68 : */
69 436 : DLT_STATIC int dlt_connection_send(DltConnection *conn,
70 : const void *msg,
71 : size_t msg_size)
72 : {
73 : DltConnectionType type = DLT_CONNECTION_TYPE_MAX;
74 : int ret = 0;
75 :
76 436 : if ((conn != NULL) && (conn->receiver != NULL))
77 436 : type = conn->type;
78 :
79 436 : switch (type) {
80 1 : case DLT_CONNECTION_CLIENT_MSG_SERIAL:
81 1 : if (msg_size > 0 && msg_size <= SSIZE_MAX) {
82 1 : if (write(conn->receiver->fd, msg, msg_size) > 0)
83 : return DLT_DAEMON_ERROR_OK;
84 : }
85 : return DLT_DAEMON_ERROR_UNKNOWN;
86 :
87 434 : case DLT_CONNECTION_CLIENT_MSG_TCP:
88 434 : if (msg_size > INT_MAX) {
89 : return DLT_DAEMON_ERROR_UNKNOWN;
90 : }
91 434 : ret = dlt_daemon_socket_sendreliable(conn->receiver->fd,
92 : msg,
93 : (int)msg_size);
94 434 : return ret;
95 : default:
96 : return DLT_DAEMON_ERROR_UNKNOWN;
97 : }
98 : }
99 :
100 : /** @brief Send up to two messages through a connection.
101 : *
102 : * We often need to send 2 messages through a specific connection, plus
103 : * the serial header. This function groups these different calls.
104 : *
105 : * @param con The connection to send the messages through.
106 : * @param data1 The first message to be sent.
107 : * @param size1 The size of the first message.
108 : * @param data2 The second message to be send.
109 : * @param size2 The second message size.
110 : * @param sendserialheader Whether we need or not to send the serial header.
111 : *
112 : * @return DLT_DAEMON_ERROR_OK on success, -1 otherwise. errno is properly set.
113 : */
114 219 : int dlt_connection_send_multiple(DltConnection *con,
115 : void *data1,
116 : int size1,
117 : void *data2,
118 : int size2,
119 : int sendserialheader)
120 : {
121 : int ret = 0;
122 :
123 219 : if (con == NULL)
124 : return DLT_DAEMON_ERROR_UNKNOWN;
125 :
126 218 : if (sendserialheader)
127 1 : ret = dlt_connection_send(con,
128 : (const void *)dltSerialHeader,
129 : (size_t)sizeof(dltSerialHeader));
130 :
131 218 : if ((data1 != NULL) && (ret == DLT_RETURN_OK) && size1 > 0) {
132 218 : ret = dlt_connection_send(con, data1, (size_t)size1);
133 : }
134 :
135 218 : if ((data2 != NULL) && (ret == DLT_RETURN_OK) && size2 > 0) {
136 214 : ret = dlt_connection_send(con, data2, (size_t)size2);
137 : }
138 :
139 : return ret;
140 : }
141 :
142 : /** @brief Get the next connection filtered with a type mask.
143 : *
144 : * In some cases we need the next connection available of a specific type or
145 : * specific different types. This function returns the next available connection
146 : * that is of one of the types included in the mask. The current connection can
147 : * be returned.
148 : *
149 : * @param current The current connection pointer.
150 : * @param type_mask A bit mask representing the connection types to be filtered.
151 : *
152 : * @return The next available connection of the considered types or NULL.
153 : */
154 2 : DltConnection *dlt_connection_get_next(DltConnection *current, int type_mask)
155 : {
156 3 : while (current && !((1 << current->type) & type_mask))
157 1 : current = current->next;
158 :
159 2 : return current;
160 : }
161 :
162 49 : DLT_STATIC void dlt_connection_destroy_receiver(DltConnection *con)
163 : {
164 49 : if (!con)
165 : return;
166 :
167 49 : switch (con->type) {
168 : case DLT_CONNECTION_GATEWAY:
169 : /* We rely on the gateway for clean-up */
170 : break;
171 9 : case DLT_CONNECTION_APP_MSG:
172 9 : dlt_receiver_free_global_buffer(con->receiver);
173 9 : free(con->receiver);
174 9 : con->receiver = NULL;
175 9 : break;
176 36 : default:
177 36 : (void)dlt_receiver_free(con->receiver);
178 36 : free(con->receiver);
179 36 : con->receiver = NULL;
180 36 : break;
181 : }
182 : }
183 :
184 : /** @brief Get the receiver structure associated to a connection.
185 : *
186 : * The receiver structure is sometimes needed while handling the event.
187 : * This behavior is mainly due to the fact that it's not intended to modify
188 : * the whole design of the daemon while implementing the new event handling.
189 : * Based on the connection type provided, this function returns the pointer
190 : * to the DltReceiver structure corresponding.
191 : *
192 : * @param daemon_local Structure where to take the DltReceiver pointer from.
193 : * @param type Type of the connection.
194 : * @param fd File descriptor
195 : *
196 : * @return DltReceiver structure or NULL if none corresponds to the type.
197 : */
198 48 : DLT_STATIC DltReceiver *dlt_connection_get_receiver(DltDaemonLocal *daemon_local,
199 : DltConnectionType type,
200 : int fd)
201 : {
202 : DltReceiver *ret = NULL;
203 : DltReceiverType receiver_type = DLT_RECEIVE_FD;
204 : struct stat statbuf;
205 :
206 48 : switch (type) {
207 25 : case DLT_CONNECTION_CONTROL_CONNECT:
208 : /* FALL THROUGH */
209 : case DLT_CONNECTION_CONTROL_MSG:
210 : /* FALL THROUGH */
211 : case DLT_CONNECTION_CLIENT_CONNECT:
212 : /* FALL THROUGH */
213 : case DLT_CONNECTION_CLIENT_MSG_TCP:
214 25 : ret = calloc(1, sizeof(DltReceiver));
215 :
216 25 : if (ret)
217 25 : dlt_receiver_init(ret, fd, DLT_RECEIVE_SOCKET, DLT_DAEMON_RCVBUFSIZESOCK);
218 :
219 : break;
220 1 : case DLT_CONNECTION_CLIENT_MSG_SERIAL:
221 1 : ret = calloc(1, sizeof(DltReceiver));
222 :
223 1 : if (ret)
224 1 : dlt_receiver_init(ret, fd, DLT_RECEIVE_FD, DLT_DAEMON_RCVBUFSIZESERIAL);
225 :
226 : break;
227 9 : case DLT_CONNECTION_APP_MSG:
228 9 : ret = calloc(1, sizeof(DltReceiver));
229 :
230 : receiver_type = DLT_RECEIVE_FD;
231 :
232 9 : if (fstat(fd, &statbuf) == 0) {
233 9 : if (S_ISSOCK(statbuf.st_mode))
234 : receiver_type = DLT_RECEIVE_SOCKET;
235 : } else {
236 0 : dlt_vlog(LOG_WARNING,
237 : "Failed to determine receive type for DLT_CONNECTION_APP_MSG, using \"FD\"\n");
238 : }
239 :
240 9 : if (ret)
241 9 : dlt_receiver_init_global_buffer(ret, fd, receiver_type, &app_recv_buffer);
242 :
243 : break;
244 : #if defined DLT_DAEMON_USE_UNIX_SOCKET_IPC || defined DLT_DAEMON_VSOCK_IPC_ENABLE
245 : case DLT_CONNECTION_APP_CONNECT:
246 : /* FALL THROUGH */
247 : #endif
248 10 : case DLT_CONNECTION_ONE_S_TIMER:
249 : /* FALL THROUGH */
250 : case DLT_CONNECTION_SIXTY_S_TIMER:
251 : #ifdef DLT_SYSTEMD_WATCHDOG_ENABLE
252 : /* FALL THROUGH */
253 : case DLT_CONNECTION_SYSTEMD_TIMER:
254 : #endif
255 : /* FALL THROUGH */
256 : case DLT_CONNECTION_GATEWAY_TIMER:
257 10 : ret = calloc(1, sizeof(DltReceiver));
258 :
259 10 : if (ret)
260 10 : dlt_receiver_init(ret, fd, DLT_RECEIVE_FD, DLT_DAEMON_RCVBUFSIZE);
261 :
262 : break;
263 3 : case DLT_CONNECTION_GATEWAY:
264 : /* We rely on the gateway for init */
265 3 : ret = dlt_gateway_get_connection_receiver(&daemon_local->pGateway, fd);
266 3 : break;
267 : default:
268 : ret = NULL;
269 : }
270 :
271 48 : return ret;
272 : }
273 :
274 : /** @brief Get the callback from a specific connection.
275 : *
276 : * The callback retrieved that way is used to handle event for this connection.
277 : * It as been chosen to proceed that way instead of having the callback directly
278 : * in the structure in order to have some way to check that the structure is
279 : * still valid, or at least gracefully handle errors instead of crashing.
280 : *
281 : * @param con The connection to retrieve the callback from.
282 : *
283 : * @return Function pointer or NULL.
284 : */
285 1285 : void *dlt_connection_get_callback(DltConnection *con)
286 : {
287 : void *ret = NULL;
288 : DltConnectionType type = DLT_CONNECTION_TYPE_MAX;
289 :
290 1285 : if (con)
291 1285 : type = con->type;
292 :
293 1285 : switch (type) {
294 : case DLT_CONNECTION_CLIENT_CONNECT:
295 : ret = (void *)(intptr_t)dlt_daemon_process_client_connect;
296 : break;
297 8 : case DLT_CONNECTION_CLIENT_MSG_TCP:
298 : ret = (void *)(intptr_t)dlt_daemon_process_client_messages;
299 8 : break;
300 0 : case DLT_CONNECTION_CLIENT_MSG_SERIAL:
301 : ret = (void *)(intptr_t)dlt_daemon_process_client_messages_serial;
302 0 : break;
303 : #if defined DLT_DAEMON_USE_UNIX_SOCKET_IPC || defined DLT_DAEMON_VSOCK_IPC_ENABLE
304 : case DLT_CONNECTION_APP_CONNECT:
305 : ret = (void *)(intptr_t)dlt_daemon_process_app_connect;
306 : break;
307 : #endif
308 1246 : case DLT_CONNECTION_APP_MSG:
309 : ret = (void *)(intptr_t)dlt_daemon_process_user_messages;
310 1246 : break;
311 11 : case DLT_CONNECTION_ONE_S_TIMER:
312 : ret = (void *)(intptr_t)dlt_daemon_process_one_s_timer;
313 11 : break;
314 0 : case DLT_CONNECTION_SIXTY_S_TIMER:
315 : ret = (void *)(intptr_t)dlt_daemon_process_sixty_s_timer;
316 0 : break;
317 : #ifdef DLT_SYSTEMD_WATCHDOG_ENABLE
318 : case DLT_CONNECTION_SYSTEMD_TIMER:
319 : ret = (void *)(intptr_t)dlt_daemon_process_systemd_timer;
320 : break;
321 : #endif
322 2 : case DLT_CONNECTION_CONTROL_CONNECT:
323 : ret = (void *)(intptr_t)dlt_daemon_process_control_connect;
324 2 : break;
325 4 : case DLT_CONNECTION_CONTROL_MSG:
326 : ret = (void *)(intptr_t)dlt_daemon_process_control_messages;
327 4 : break;
328 10 : case DLT_CONNECTION_GATEWAY:
329 : ret = (void *)(intptr_t)dlt_gateway_process_passive_node_messages;
330 10 : break;
331 0 : case DLT_CONNECTION_GATEWAY_TIMER:
332 : ret = (void *)(intptr_t)dlt_gateway_process_gateway_timer;
333 0 : break;
334 0 : default:
335 : ret = NULL;
336 : }
337 :
338 1285 : return ret;
339 : }
340 :
341 : /** @brief Destroys a connection.
342 : *
343 : * This function closes and frees the corresponding connection. This is expected
344 : * to be called by the connection owner: the DltEventHandler.
345 : * Ownership of the connection is given during the registration to
346 : * the DltEventHandler.
347 : *
348 : * @param to_destroy Connection to be destroyed.
349 : */
350 48 : void dlt_connection_destroy(DltConnection *to_destroy)
351 : {
352 48 : to_destroy->id = 0;
353 48 : close(to_destroy->receiver->fd);
354 48 : dlt_connection_destroy_receiver(to_destroy);
355 48 : free(to_destroy);
356 48 : }
357 :
358 : /** @brief Creates a connection and registers it to the DltEventHandler.
359 : *
360 : * The function will allocate memory for the connection, and give the pointer
361 : * to the DltEventHandler in order to register it for incoming events.
362 : * The connection is then destroyed later on, once it's not needed anymore or
363 : * it the event handler is destroyed.
364 : *
365 : * @param daemon_local Structure were some needed information is.
366 : * @param evh DltEventHandler to register the connection to.
367 : * @param fd File descriptor of the connection.
368 : * @param mask Event list bit mask.
369 : * @param type Connection type.
370 : *
371 : * @return 0 On success, -1 otherwise.
372 : */
373 47 : int dlt_connection_create(DltDaemonLocal *daemon_local,
374 : DltEventHandler *evh,
375 : int fd,
376 : int mask,
377 : DltConnectionType type)
378 : {
379 : DltConnection *temp = NULL;
380 :
381 47 : if (fd < 0)
382 : /* Nothing to do */
383 : return 0;
384 :
385 47 : if (dlt_event_handler_find_connection(evh, fd) != NULL)
386 : /* No need for the same client to be registered twice
387 : * for the same event.
388 : * TODO: If another mask can be expected,
389 : * we need it to update the poll event here.
390 : */
391 : return 0;
392 :
393 47 : temp = (DltConnection *)malloc(sizeof(DltConnection));
394 :
395 47 : if (temp == NULL) {
396 0 : dlt_log(LOG_CRIT, "Allocation of client handle failed\n");
397 0 : return -1;
398 : }
399 :
400 : memset(temp, 0, sizeof(DltConnection));
401 :
402 47 : temp->receiver = dlt_connection_get_receiver(daemon_local, type, fd);
403 :
404 47 : if (!temp->receiver) {
405 0 : dlt_vlog(LOG_CRIT, "Unable to get receiver from %u connection.\n",
406 : type);
407 0 : free(temp);
408 0 : return -1;
409 : }
410 :
411 : struct timeval timeout;
412 47 : timeout.tv_sec = 5;
413 47 : timeout.tv_usec = 0;
414 : #ifdef DLT_SYSTEMD_WATCHDOG_ENABLE
415 : char *watchdogUSec = getenv("WATCHDOG_USEC");
416 : if (watchdogUSec) {
417 : timeout.tv_sec = atoi(watchdogUSec) / 1000000;
418 : timeout.tv_usec = atoi(watchdogUSec) % 1000000;
419 : }
420 : #endif
421 :
422 47 : if (setsockopt (temp->receiver->fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof timeout) < 0) {
423 20 : dlt_vlog(LOG_WARNING, "Unable to set send timeout %s.\n", strerror(errno));
424 : // as this function is used for non socket connection as well
425 : // we only can return an error here if it is a socket
426 20 : if (errno != ENOTSOCK) {
427 1 : free(temp);
428 1 : return -1;
429 : }
430 : }
431 :
432 : /* We are single threaded no need for protection. */
433 46 : temp->id = connectionId++;
434 :
435 46 : if (!temp->id)
436 : /* Skipping 0 */
437 10 : temp->id = connectionId++;
438 :
439 46 : temp->type = type;
440 46 : temp->status = ACTIVE;
441 :
442 : /* Now give the ownership of the newly created connection
443 : * to the event handler, by registering for events.
444 : */
445 46 : return dlt_event_handler_register_connection(evh, daemon_local, temp, mask);
446 : }
|