Line data Source code
1 : /**
2 : * Copyright (C) 2013 - 2015 Advanced Driver Information Technology.
3 : * This code is developed by Advanced Driver Information Technology.
4 : * Copyright of Advanced Driver Information Technology, Bosch and DENSO. *
5 : * This file is part of COVESA Project Dlt - Diagnostic Log and Trace console apps.
6 : *
7 : *
8 : * \copyright
9 : * This Source Code Form is subject to the terms of the
10 : * Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with
11 : * this file, You can obtain one at http://mozilla.org/MPL/2.0/.
12 : *
13 : *
14 : * \author Syed Hameed <shameed@jp.adit-jv.com> ADIT 2013 - 2015
15 : * \author Christoph Lipka <clipka@jp.adit-jv.com> ADIT 2015
16 : * \author Frederic Berat <fberat@de.adit-jv.com> ADIT 2015
17 : *
18 : * \file dlt-logstorage-ctrl.c
19 : * For further information see http://www.covesa.org/.
20 : */
21 : /*******************************************************************************
22 : ** **
23 : ** SRC-MODULE: dlt-logstorage-ctrl.c **
24 : ** **
25 : ** TARGET : linux **
26 : ** **
27 : ** PROJECT : DLT **
28 : ** **
29 : ** AUTHOR : Syed Hameed shameed@jp.adit-jv.com **
30 : ** Christoph Lipka clipka@jp.adit-jv.com **
31 : ** AnithaAmmaji.baggam@in.bosch.com **
32 : ** Frederic Berat fberat@de.adit-jv.com **
33 : ** PURPOSE : **
34 : ** **
35 : ** REMARKS : **
36 : ** **
37 : ** PLATFORM DEPENDANT [yes/no]: yes **
38 : ** **
39 : ** TO BE CHANGED BY USER [yes/no]: no **
40 : ** **
41 : *******************************************************************************/
42 :
43 : /*******************************************************************************
44 : ** Author Identity **
45 : ********************************************************************************
46 : ** Initials Name Company **
47 : ** -------- ------------------------- ---------------------------------- **
48 : ** sh Syed Hameed ADIT **
49 : ** cl Christoph Lipka ADIT **
50 : ** BA Anitha BA ADIT **
51 : ** fb Frederic Berat ADIT **
52 : *******************************************************************************/
53 :
54 : #define pr_fmt(fmt) "Logstorage control: " fmt
55 :
56 : #include <ctype.h>
57 : #include <errno.h>
58 : #include <stdio.h>
59 : #include <stdlib.h>
60 : #include <signal.h>
61 : #include <string.h>
62 : #include <getopt.h>
63 :
64 : #include <poll.h>
65 :
66 : #if defined(__linux__)
67 : #include "sd-daemon.h"
68 : #endif
69 :
70 : #include "dlt_protocol.h"
71 : #include "dlt_client.h"
72 : #include "dlt-control-common.h"
73 : #include "dlt-logstorage-common.h"
74 : #include "dlt-logstorage-ctrl.h"
75 :
76 : #define POLL_TIME_OUT 500
77 : #define EV_MASK_REJECTED (POLLERR | POLLHUP | POLLNVAL)
78 :
79 : #define DLT_LOGSTORAGE_CTRL_EXIT 1
80 : static int must_exit;
81 : struct dlt_event {
82 : struct pollfd pfd;
83 : void* func;
84 : };
85 :
86 : /** @brief Triggers the application exit
87 : *
88 : * The application will exit on next poll timeout.
89 : */
90 0 : void dlt_logstorage_exit(void)
91 : {
92 0 : must_exit = DLT_LOGSTORAGE_CTRL_EXIT;
93 0 : }
94 :
95 : /** @brief Check if the application must exit
96 : *
97 : * The application will exit on next poll timeout.
98 : */
99 0 : int dlt_logstorage_must_exit(void)
100 : {
101 0 : return must_exit;
102 : }
103 :
104 : /** @brief Signal handler.
105 : *
106 : * Triggers the exit of the application in case of specific signals
107 : *
108 : * @param signo The value of the signal received.
109 : */
110 0 : static void catch_signal(int signo)
111 : {
112 0 : if (signo) {
113 0 : pr_error("Signal %d received, exiting.", signo);
114 : dlt_logstorage_exit();
115 : }
116 0 : }
117 :
118 : /** @brief Install a handler for some signals
119 : *
120 : * Handler are installed on exit related signals. That allows one to exit from
121 : * the main loop gracefully.
122 : */
123 0 : static void install_signal_handler(void)
124 : {
125 0 : int signals[] = {SIGINT, SIGQUIT, SIGTERM, 0};
126 : unsigned int i;
127 : struct sigaction sa;
128 :
129 0 : pr_verbose("Installing signal handler.\n");
130 :
131 : /* install a signal handler for the above listed signals */
132 0 : for (i = 0; signals[i]; i++) {
133 : memset(&sa, 0, sizeof(sa));
134 0 : sa.sa_handler = catch_signal;
135 :
136 0 : if (sigaction(signals[i], &sa, NULL) < 0)
137 0 : pr_error("Failed to install signal %u handler. Error: %s\n", signals[i], strerror(errno));
138 : }
139 0 : }
140 :
141 : #define MAX_RESPONSE_LENGTH 32
142 : /** @brief Analyze the daemon answer to a request
143 : *
144 : * This function checks whether if the daemon answered positively to
145 : * the request or not.
146 : *
147 : * @param data The textual answer
148 : * @param payload The answer payload
149 : * @param len The answer payload length
150 : * @return 0 on success, -1 otherwise.
151 : */
152 1 : static int analyze_response(char* data, void* payload, int len)
153 : {
154 : int ret = -1;
155 1 : char resp_ok[MAX_RESPONSE_LENGTH] = {0};
156 1 : char resp_warning[MAX_RESPONSE_LENGTH] = {0};
157 1 : char resp_perm_denied[MAX_RESPONSE_LENGTH] = {0};
158 :
159 1 : if ((data == NULL) || (payload == NULL))
160 : return -1;
161 :
162 : /* satisfy compiler */
163 : (void)payload;
164 : (void)len;
165 :
166 : snprintf(resp_ok, MAX_RESPONSE_LENGTH, "service(%d), ok", DLT_SERVICE_ID_OFFLINE_LOGSTORAGE);
167 :
168 : snprintf(resp_warning, MAX_RESPONSE_LENGTH, "service(%d), warning", DLT_SERVICE_ID_OFFLINE_LOGSTORAGE);
169 :
170 : snprintf(resp_perm_denied, MAX_RESPONSE_LENGTH, "service(%d), perm_denied", DLT_SERVICE_ID_OFFLINE_LOGSTORAGE);
171 :
172 1 : if (strncmp(data, resp_ok, strlen(resp_ok)) == 0)
173 : ret = 0;
174 :
175 1 : if (strncmp(data, resp_warning, strlen(resp_warning)) == 0) {
176 0 : pr_error("Warning:Some filter configurations are ignored due to configuration issues \n");
177 : ret = 0;
178 : }
179 :
180 1 : if (strncmp(data, resp_perm_denied, strlen(resp_perm_denied)) == 0) {
181 0 : pr_error("Warning: Permission denied.\n");
182 : ret = 0;
183 : }
184 :
185 1 : pr_verbose("Response received: '%s'\n", data);
186 1 : pr_verbose("Response expected: '%s'\n", resp_ok);
187 :
188 : return ret;
189 : }
190 :
191 : /** @brief Add a new event to watch
192 : *
193 : * This function could be exported to be used by udev/prop so that they can
194 : * register several events.
195 : *
196 : * @param ev_hdl The structure containing the file descriptors
197 : * @param fd The file descriptor to watch
198 : * @param cb The callback to be called on event.
199 : *
200 : * @return 0 on success, -1 if the parameters are invalid.
201 : */
202 0 : static int dlt_logstorage_ctrl_add_event(struct dlt_event* ev_hdl, int fd, void* cb)
203 : {
204 0 : if ((fd < 0) || !cb || !ev_hdl) {
205 0 : pr_error("Wrong parameter to add event (%d %p)\n", fd, cb);
206 0 : return -1;
207 : }
208 :
209 0 : pr_verbose("Setting up the event handler with (%d, %p).\n", fd, cb);
210 :
211 0 : ev_hdl->func = cb;
212 0 : ev_hdl->pfd.fd = fd;
213 :
214 0 : return 0;
215 : }
216 :
217 : /** @brief Main execution loop
218 : *
219 : * Waits on events, and executes the callbacks retrieved
220 : * back from the event structure.
221 : *
222 : * @return 0 on success, -1 otherwise.
223 : */
224 0 : static int dlt_logstorage_ctrl_execute_event_loop(struct dlt_event* ev)
225 : {
226 : int ret = 0;
227 : union {
228 : void* ptr;
229 : int (*callback)(void);
230 : } callback_converter;
231 : int (*callback)(void);
232 :
233 0 : callback_converter.ptr = ev->func;
234 0 : callback = callback_converter.callback;
235 :
236 0 : ret = poll(&ev->pfd, 1, POLL_TIME_OUT);
237 :
238 0 : if (ret <= 0) {
239 0 : if (errno == EINTR)
240 : ret = 0;
241 :
242 0 : if (ret < 0)
243 0 : pr_error("poll error: %s\n", strerror(errno));
244 :
245 0 : return ret;
246 : }
247 :
248 0 : if (ev->pfd.revents == 0)
249 : return 0;
250 :
251 0 : if (ev->pfd.events & EV_MASK_REJECTED) {
252 0 : pr_error("Error while polling. Event received: 0x%x\n", ev->pfd.events);
253 : /* We only support one event producer.
254 : * Error means that this producer died.
255 : */
256 0 : pr_error("Now closing fd and exiting.\n");
257 0 : close(ev->pfd.fd);
258 0 : ev->pfd.fd = -1;
259 : dlt_logstorage_exit();
260 0 : return -1;
261 : }
262 :
263 0 : if (!callback) {
264 0 : pr_error("Callback not found, exiting.\n");
265 : dlt_logstorage_exit();
266 0 : return -1;
267 : }
268 :
269 : callback_converter.callback = callback;
270 0 : pr_verbose("Got new event, calling %p.\n", callback_converter.ptr);
271 :
272 0 : if (callback() < 0) {
273 0 : pr_error("Error while calling the callback, exiting.\n");
274 : dlt_logstorage_exit();
275 0 : return -1;
276 : }
277 :
278 : return 0;
279 : }
280 :
281 : /** @brief Start event loop and receive messages from DLT.
282 : *
283 : * The function will first install the signal handler,
284 : * then create the poll instance, initialize the communication controller,
285 : * initialize the event handler and finally start the polling.
286 : *
287 : * @return 0 on success, -1 on error
288 : */
289 0 : static int dlt_logstorage_ctrl_setup_event_loop(void)
290 : {
291 : int ret = 0;
292 0 : struct dlt_event ev_hdl = {.pfd = {.fd = -1, .events = POLLIN}};
293 :
294 0 : install_signal_handler();
295 :
296 0 : pr_verbose("Creating poll instance.\n");
297 :
298 : /* Initializing the communication with the daemon */
299 0 : while (dlt_control_init(analyze_response, get_ecuid(), get_verbosity()) && !dlt_logstorage_must_exit()) {
300 0 : pr_error("Failed to initialize connection with the daemon.\n");
301 0 : pr_error("Retrying to connect in %ds.\n", get_timeout());
302 0 : sleep((unsigned int)get_timeout());
303 : }
304 :
305 0 : if (dlt_logstorage_must_exit()) {
306 0 : pr_verbose("Exiting.\n");
307 0 : return 0;
308 : }
309 :
310 0 : pr_verbose("Initializing event generator.\n");
311 :
312 0 : if (dlt_logstorage_init_handler() < 0) {
313 0 : pr_error("Failed to initialize handler.\n");
314 0 : dlt_control_deinit();
315 0 : return -1;
316 : }
317 :
318 0 : if (dlt_logstorage_ctrl_add_event(&ev_hdl, dlt_logstorage_get_handler_fd(), dlt_logstorage_get_handler_cb()) < 0) {
319 0 : pr_error("add_event error: %s\n", strerror(errno));
320 : dlt_logstorage_exit();
321 : }
322 :
323 0 : while (!dlt_logstorage_must_exit() && (ret == 0))
324 0 : ret = dlt_logstorage_ctrl_execute_event_loop(&ev_hdl);
325 :
326 : /* Clean up */
327 0 : dlt_logstorage_deinit_handler();
328 0 : dlt_control_deinit();
329 :
330 0 : return ret;
331 : }
332 :
333 : /** @brief Send a single command to DLT daemon and wait for response
334 : *
335 : * @return 0 on success, -1 otherwise.
336 : */
337 1 : static int dlt_logstorage_ctrl_single_request()
338 : {
339 : int ret = 0;
340 :
341 : /* in case sync all caches, an empty path is given */
342 1 : if (get_default_event_type() != EVENT_SYNC_CACHE) {
343 : /* Check if a 'CONF_NAME' file is present at the given path */
344 0 : if (!dlt_logstorage_check_config_file(get_default_path())) {
345 0 : pr_error("No '%s' file available at: %s\n", CONF_NAME, get_default_path());
346 0 : return -1;
347 : }
348 :
349 0 : if (!dlt_logstorage_check_directory_permission(get_default_path())) {
350 0 : pr_error("'%s' is not writable\n", get_default_path());
351 0 : return -1;
352 : }
353 : }
354 :
355 : /* Initializing the communication with the daemon */
356 1 : while (dlt_control_init(analyze_response, get_ecuid(), get_verbosity()) && !dlt_logstorage_must_exit()) {
357 0 : pr_error("Failed to initialize connection with the daemon.\n");
358 0 : pr_error("Retrying to connect in %ds.\n", get_timeout());
359 0 : sleep((unsigned int)get_timeout());
360 : }
361 :
362 1 : pr_verbose("event type is [%d]\t device path is [%s]\n", get_default_event_type(), get_default_path());
363 :
364 1 : ret = dlt_logstorage_send_event(get_default_event_type(), get_default_path());
365 :
366 1 : dlt_control_deinit();
367 :
368 1 : return ret;
369 : }
370 :
371 : /** @brief Print out the application help
372 : */
373 0 : static void usage(void)
374 : {
375 : printf("Usage: dlt-logstorage-ctrl [options]\n");
376 : printf(
377 : "Send a trigger to DLT daemon to connect/disconnect"
378 : "a certain logstorage device\n");
379 : printf("\n");
380 : printf("Options:\n");
381 : printf(" -c --command Connection type: connect = 1, disconnect = 0\n");
382 : printf(" -d[prop] --daemonize=prop Run as daemon: prop = use proprietary handler\n");
383 : printf(" 'prop' may be replaced by any meaningful word\n");
384 : printf(" If prop is not specified, udev will be used\n");
385 : printf(" -e --ecuid Set ECU ID (Default: %s)\n", DLT_CTRL_DEFAULT_ECUID);
386 : printf(" -h --help Usage\n");
387 : printf(" -p --path Mount point path\n");
388 : printf(" -s[path] --snapshot=path Sync Logstorage cache\n");
389 : printf(" Don't use -s together with -d and -c\n");
390 : printf(" -t Specify connection timeout (Default: %ds)\n", DLT_CTRL_TIMEOUT);
391 : printf(" -S --send-header Send message with serial header (Default: Without serial header)\n");
392 : printf(" -R --resync-header Enable resync serial header\n");
393 0 : printf(" -v --verbose Set verbose flag (Default:%d)\n", get_verbosity());
394 : printf(" -C filename DLT daemon configuration file (Default: " CONFIGURATION_FILES_DIR
395 : "/dlt.conf)\n");
396 0 : }
397 :
398 : static struct option long_options[] = {
399 : {"command", required_argument, 0, 'c'},
400 : {"daemonize", optional_argument, 0, 'd'},
401 : {"ecuid", required_argument, 0, 'e'},
402 : {"help", no_argument, 0, 'h'},
403 : {"path", required_argument, 0, 'p'},
404 : {"snapshot", optional_argument, 0, 's'},
405 : {"timeout", required_argument, 0, 't'},
406 : {"send-header", no_argument, 0, 'S'},
407 : {"resync-header", no_argument, 0, 'R'},
408 : {"verbose", no_argument, 0, 'v'},
409 : {0, 0, 0, 0}};
410 :
411 : /** @brief Parses the application arguments
412 : *
413 : * The arguments are parsed and saved in static structure for future use.
414 : *
415 : * @param argc The amount of arguments
416 : * @param argv The table of arguments
417 : *
418 : * @return 0 on success, -1 otherwise
419 : */
420 1 : static int parse_args(int argc, char* argv[])
421 : {
422 : int c = -1;
423 1 : int long_index = 0;
424 :
425 3 : while ((c = getopt_long(argc, argv, ":s::t:hSRe:p:d::c:vC:", long_options, &long_index)) != -1)
426 2 : switch (c) {
427 1 : case 's':
428 1 : set_default_event_type(EVENT_SYNC_CACHE);
429 :
430 1 : if ((optarg != NULL) && (strlen(optarg) >= DLT_MOUNT_PATH_MAX)) {
431 0 : pr_error("Mount path '%s' too long\n", optarg);
432 0 : return -1;
433 : }
434 :
435 1 : set_default_path(optarg);
436 1 : break;
437 0 : case 't':
438 0 : if (optarg != NULL)
439 0 : set_timeout((int)strtol(optarg, NULL, 10));
440 : break;
441 0 : case 'S': {
442 0 : set_send_serial_header(1);
443 0 : break;
444 : }
445 0 : case 'R': {
446 0 : set_resync_serial_header(1);
447 0 : break;
448 : }
449 0 : case 'h':
450 0 : usage();
451 0 : return -1;
452 0 : case 'e':
453 0 : set_ecuid(optarg);
454 0 : break;
455 0 : case 'd':
456 0 : pr_verbose("Choosing handler.\n");
457 0 : set_handler_type(optarg);
458 0 : pr_verbose("Handler chosen: %d.\n", get_handler_type());
459 : break;
460 0 : case 'p':
461 :
462 0 : if ((optarg != NULL) && (strlen(optarg) >= DLT_MOUNT_PATH_MAX)) {
463 0 : pr_error("Mount path '%s' too long\n", optarg);
464 0 : return -1;
465 : }
466 :
467 0 : set_default_path(optarg);
468 0 : break;
469 0 : case 'c':
470 0 : if (optarg != NULL)
471 0 : set_default_event_type(strtol(optarg, NULL, 10));
472 : break;
473 0 : case 'v':
474 0 : set_verbosity(1);
475 0 : pr_verbose("Now in verbose mode.\n");
476 : break;
477 1 : case 'C':
478 1 : set_conf(optarg);
479 1 : pr_verbose("Set %s to read options\n", optarg);
480 : break;
481 0 : case ':':
482 0 : pr_error("Option -%c requires an argument.\n", optopt);
483 0 : usage();
484 0 : return -1;
485 0 : case '?':
486 :
487 0 : if (isprint(optopt))
488 0 : pr_error("Unknown option -%c.\n", optopt);
489 : else
490 0 : pr_error("Unknown option character \\x%x.\n", optopt);
491 :
492 0 : usage();
493 0 : return -1;
494 0 : default:
495 0 : pr_error("Try %s -h for more information.\n", argv[0]);
496 0 : return -1;
497 : }
498 :
499 :
500 :
501 1 : if ((get_default_event_type() == EVENT_SYNC_CACHE) && (get_handler_type() != CTRL_NOHANDLER)) {
502 0 : pr_error("Sync caches not available in daemon mode\n");
503 0 : return -1;
504 : }
505 :
506 : /* Retrieve ECUID from dlt.conf */
507 1 : if (get_ecuid() == NULL)
508 0 : set_ecuid(NULL);
509 :
510 : return 0;
511 : }
512 :
513 : #if !defined(DLT_SYSTEMD_ENABLE)
514 0 : int sd_notify(int unset_environment, char* state)
515 : {
516 : /* Satisfy Compiler for warnings */
517 : (void)unset_environment;
518 : (void)state;
519 0 : return 0;
520 : }
521 : #endif
522 :
523 : /** @brief Entry point
524 : *
525 : * Execute the argument parser and call the main feature accordingly.
526 : *
527 : * @param argc The amount of arguments
528 : * @param argv The table of arguments
529 : *
530 : * @return 0 on success, -1 otherwise
531 : */
532 1 : int main(int argc, char* argv[])
533 : {
534 : int ret = 0;
535 :
536 1 : set_timeout(DLT_CTRL_TIMEOUT);
537 1 : set_send_serial_header(0);
538 1 : set_resync_serial_header(0);
539 :
540 : /* Get command line arguments */
541 1 : if (parse_args(argc, argv) != 0)
542 : return -1;
543 :
544 : /* all parameter valid, start communication with daemon or setup
545 : * communication with control daemon */
546 1 : if (get_handler_type() == CTRL_NOHANDLER) {
547 1 : pr_verbose("One shot.\n");
548 :
549 1 : ret = dlt_logstorage_ctrl_single_request();
550 :
551 1 : if (ret < 0)
552 0 : pr_error("Message failed to be send. Please check DLT config.\n");
553 : } else {
554 0 : pr_verbose("Entering in daemon mode.\n");
555 :
556 : /* Let's daemonize */
557 : if (sd_notify(0, "READY=1") <= 0) {
558 0 : pr_verbose("SD notify failed, manually daemonizing.\n");
559 :
560 : /* No message can be sent or Systemd is not available.
561 : * Daemonizing manually.
562 : */
563 :
564 : // This does the same thing as daemon(1, 1) but is not deprecated
565 0 : pid_t pid = fork();
566 0 : if (pid < 0) {
567 0 : pr_error("Failed to fork: %s\n", strerror(errno));
568 0 : return EXIT_FAILURE;
569 : }
570 0 : if (pid > 0) {
571 : /* Parent exits */
572 0 : _exit(EXIT_SUCCESS);
573 : }
574 : /* Child continues, create new session */
575 0 : if (setsid() < 0) {
576 0 : pr_error("Failed to create new session: %s\n", strerror(errno));
577 0 : return EXIT_FAILURE;
578 : }
579 : }
580 :
581 0 : pr_verbose("Executing the event loop\n");
582 0 : ret = dlt_logstorage_ctrl_setup_event_loop();
583 : }
584 :
585 1 : pr_verbose("Exiting.\n");
586 : return ret;
587 : }
|