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