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