985dde992b1e5a7ef3edc090bd762121641044bc
[lttng-tools.git] / src / bin / lttng / commands / create.cpp
1 /*
2 * Copyright (C) 2011 EfficiOS Inc.
3 * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-only
6 *
7 */
8
9 #define _LGPL_SOURCE
10 #include "../command.hpp"
11 #include "../utils.hpp"
12
13 #include <common/compat/time.hpp>
14 #include <common/defaults.hpp>
15 #include <common/mi-lttng.hpp>
16 #include <common/path.hpp>
17 #include <common/sessiond-comm/sessiond-comm.hpp>
18 #include <common/uri.hpp>
19 #include <common/utils.hpp>
20
21 #include <lttng/lttng.h>
22
23 #include <ctype.h>
24 #include <popt.h>
25 #include <signal.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include <unistd.h>
33
34 static char *opt_output_path;
35 static char *opt_url;
36 static char *opt_ctrl_url;
37 static char *opt_data_url;
38 static char *opt_shm_path;
39 static int opt_no_consumer;
40 static int opt_no_output;
41 static int opt_snapshot;
42 static uint32_t opt_live_timer;
43
44 #ifdef LTTNG_EMBED_HELP
45 static const char help_msg[] =
46 #include <lttng-create.1.h>
47 ;
48 #endif
49
50 enum {
51 OPT_HELP = 1,
52 OPT_LIST_OPTIONS,
53 OPT_LIVE_TIMER,
54 };
55
56 enum output_type {
57 OUTPUT_NONE,
58 OUTPUT_LOCAL,
59 OUTPUT_NETWORK,
60 OUTPUT_UNSPECIFIED,
61 };
62
63 static struct mi_writer *writer;
64 static struct poptOption long_options[] = {
65 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
66 { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
67 { "output", 'o', POPT_ARG_STRING, &opt_output_path, 0, NULL, NULL },
68 { "list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL },
69 { "set-url", 'U', POPT_ARG_STRING, &opt_url, 0, 0, 0 },
70 { "ctrl-url", 'C', POPT_ARG_STRING, &opt_ctrl_url, 0, 0, 0 },
71 { "data-url", 'D', POPT_ARG_STRING, &opt_data_url, 0, 0, 0 },
72 { "no-output", 0, POPT_ARG_VAL, &opt_no_output, 1, 0, 0 },
73 { "no-consumer", 0, POPT_ARG_VAL, &opt_no_consumer, 1, 0, 0 },
74 { "snapshot", 0, POPT_ARG_VAL, &opt_snapshot, 1, 0, 0 },
75 { "live", 0, POPT_ARG_INT | POPT_ARGFLAG_OPTIONAL, 0, OPT_LIVE_TIMER, 0, 0 },
76 { "shm-path", 0, POPT_ARG_STRING, &opt_shm_path, 0, 0, 0 },
77 { 0, 0, 0, 0, 0, 0, 0 }
78 };
79
80 /*
81 * Retrieve the created session and mi output it based on provided argument
82 * This is currently a summary of what was pretty printed and is subject to
83 * enhancements.
84 */
85 static int mi_created_session(const char *session_name)
86 {
87 int ret, i, count, found;
88 struct lttng_session *sessions;
89
90 /* session_name should not be null */
91 LTTNG_ASSERT(session_name);
92 LTTNG_ASSERT(writer);
93
94 count = lttng_list_sessions(&sessions);
95 if (count < 0) {
96 ret = count;
97 ERR("%s", lttng_strerror(ret));
98 goto error;
99 }
100
101 if (count == 0) {
102 ERR("Error session creation failed: session %s not found", session_name);
103 ret = -LTTNG_ERR_SESS_NOT_FOUND;
104 goto end;
105 }
106
107 found = 0;
108 for (i = 0; i < count; i++) {
109 if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) {
110 found = 1;
111 ret = mi_lttng_session(writer, &sessions[i], 0);
112 if (ret) {
113 goto error;
114 }
115 break;
116 }
117 }
118
119 if (!found) {
120 ret = -LTTNG_ERR_SESS_NOT_FOUND;
121 } else {
122 ret = CMD_SUCCESS;
123 }
124
125 error:
126 free(sessions);
127 end:
128 return ret;
129 }
130
131 static struct lttng_session_descriptor *create_session_descriptor(const char *session_name)
132 {
133 ssize_t uri_count;
134 enum output_type output_type;
135 struct lttng_uri *uris = NULL;
136 struct lttng_session_descriptor *descriptor = NULL;
137 const char *uri_str1 = NULL, *uri_str2 = NULL;
138 char local_output_path[LTTNG_PATH_MAX] = {};
139
140 if (opt_no_output) {
141 output_type = OUTPUT_NONE;
142 } else if (opt_output_path) {
143 char *expanded_output_path;
144 int ret;
145
146 output_type = OUTPUT_LOCAL;
147 expanded_output_path = utils_expand_path(opt_output_path);
148 if (!expanded_output_path) {
149 ERR("Failed to expand output path.");
150 goto end;
151 }
152 ret = lttng_strncpy(
153 local_output_path, expanded_output_path, sizeof(local_output_path));
154 free(expanded_output_path);
155 if (ret) {
156 ERR("Output path exceeds the maximal supported length (%zu bytes)",
157 sizeof(local_output_path));
158 goto end;
159 }
160 } else if (opt_url || opt_ctrl_url) {
161 int ret;
162
163 uri_str1 = opt_ctrl_url ? opt_ctrl_url : opt_url;
164 uri_str2 = opt_data_url;
165
166 uri_count = uri_parse_str_urls(uri_str1, uri_str2, &uris);
167 if (uri_count != 1 && uri_count != 2) {
168 ERR("Unrecognized URL format.");
169 goto end;
170 }
171
172 switch (uri_count) {
173 case 1:
174 output_type = OUTPUT_LOCAL;
175 if (uris[0].dtype != LTTNG_DST_PATH) {
176 ERR("Unrecognized URL format.");
177 goto end;
178 }
179 ret = lttng_strncpy(
180 local_output_path, uris[0].dst.path, sizeof(local_output_path));
181 if (ret) {
182 ERR("Output path exceeds the maximal supported length (%zu bytes)",
183 sizeof(local_output_path));
184 }
185 break;
186 case 2:
187 output_type = OUTPUT_NETWORK;
188 break;
189 default:
190 /* Already checked. */
191 abort();
192 }
193 } else {
194 output_type = OUTPUT_UNSPECIFIED;
195 }
196
197 if (opt_snapshot) {
198 /* Snapshot session. */
199 switch (output_type) {
200 case OUTPUT_UNSPECIFIED:
201 case OUTPUT_LOCAL:
202 descriptor = lttng_session_descriptor_snapshot_local_create(
203 session_name,
204 output_type == OUTPUT_LOCAL ? local_output_path : NULL);
205 break;
206 case OUTPUT_NONE:
207 descriptor = lttng_session_descriptor_snapshot_create(session_name);
208 break;
209 case OUTPUT_NETWORK:
210 descriptor = lttng_session_descriptor_snapshot_network_create(
211 session_name, uri_str1, uri_str2);
212 break;
213 default:
214 abort();
215 }
216 } else if (opt_live_timer) {
217 /* Live session. */
218 if (output_type != OUTPUT_UNSPECIFIED && output_type != OUTPUT_NETWORK) {
219 ERR("Unsupported output type specified for live session.");
220 goto end;
221 }
222 descriptor = lttng_session_descriptor_live_network_create(
223 session_name, uri_str1, uri_str2, opt_live_timer);
224 } else {
225 /* Regular session. */
226 switch (output_type) {
227 case OUTPUT_UNSPECIFIED:
228 case OUTPUT_LOCAL:
229 descriptor = lttng_session_descriptor_local_create(
230 session_name,
231 output_type == OUTPUT_LOCAL ? local_output_path : NULL);
232 break;
233 case OUTPUT_NONE:
234 descriptor = lttng_session_descriptor_create(session_name);
235 break;
236 case OUTPUT_NETWORK:
237 descriptor = lttng_session_descriptor_network_create(
238 session_name, uri_str1, uri_str2);
239 break;
240 default:
241 abort();
242 }
243 }
244 if (!descriptor) {
245 ERR("Failed to initialize session creation command.");
246 } else {
247 /*
248 * Auto-launch the relay daemon when a live session
249 * is created using default URLs.
250 */
251 if (!opt_url && !opt_ctrl_url && !opt_data_url && opt_live_timer &&
252 !check_relayd()) {
253 int ret;
254 const char *pathname = opt_relayd_path ?: INSTALL_BIN_PATH "/lttng-relayd";
255
256 ret = spawn_relayd(pathname, 0);
257 if (ret < 0) {
258 lttng_session_descriptor_destroy(descriptor);
259 descriptor = NULL;
260 }
261 }
262 }
263 end:
264 free(uris);
265 return descriptor;
266 }
267
268 /*
269 * Create a tracing session.
270 * If no name is specified, a default name is generated.
271 *
272 * Returns one of the CMD_* result constants.
273 */
274 static int create_session(const char *session_name)
275 {
276 int ret, i;
277 char shm_path[LTTNG_PATH_MAX] = {};
278 struct lttng_session_descriptor *session_descriptor = NULL;
279 enum lttng_session_descriptor_status descriptor_status;
280 enum lttng_error_code ret_code;
281 struct lttng_session *sessions = NULL;
282 const struct lttng_session *created_session = NULL;
283 const char *created_session_name;
284
285 /* Validate options. */
286 if (session_name) {
287 if (strlen(session_name) > NAME_MAX) {
288 ERR("Session name too long. Length must be lower or equal to %d", NAME_MAX);
289 ret = CMD_ERROR;
290 goto error;
291 }
292 /*
293 * Check if the session name begins with "auto-" or is exactly "auto".
294 * Both are reserved for the default session name. See bug #449 to
295 * understand why we need to check both here.
296 */
297 if ((strncmp(session_name,
298 DEFAULT_SESSION_NAME "-",
299 strlen(DEFAULT_SESSION_NAME) + 1) == 0) ||
300 (strncmp(session_name, DEFAULT_SESSION_NAME, strlen(DEFAULT_SESSION_NAME)) ==
301 0 &&
302 strlen(session_name) == strlen(DEFAULT_SESSION_NAME))) {
303 ERR("%s is a reserved keyword for default session(s)",
304 DEFAULT_SESSION_NAME);
305 ret = CMD_ERROR;
306 goto error;
307 }
308 }
309
310 if (opt_snapshot && opt_live_timer) {
311 ERR("Snapshot and live modes are mutually exclusive.");
312 ret = CMD_ERROR;
313 goto error;
314 }
315
316 if ((!opt_ctrl_url && opt_data_url) || (opt_ctrl_url && !opt_data_url)) {
317 ERR("Both control and data URLs must be specified.");
318 ret = CMD_ERROR;
319 goto error;
320 }
321
322 session_descriptor = create_session_descriptor(session_name);
323 if (!session_descriptor) {
324 ret = CMD_ERROR;
325 goto error;
326 }
327 ret_code = lttng_create_session_ext(session_descriptor);
328 if (ret_code != LTTNG_OK) {
329 ERR("%s", lttng_strerror(-ret_code));
330 ret = CMD_ERROR;
331 goto error;
332 }
333
334 descriptor_status = lttng_session_descriptor_get_session_name(session_descriptor,
335 &created_session_name);
336 if (descriptor_status != LTTNG_SESSION_DESCRIPTOR_STATUS_OK) {
337 ERR("Failed to obtain created session name");
338 ret = CMD_ERROR;
339 goto error;
340 }
341
342 ret = lttng_list_sessions(&sessions);
343 if (ret < 0) {
344 ERR("Failed to fetch properties of created session: %s", lttng_strerror(ret));
345 ret = CMD_ERROR;
346 goto error;
347 }
348 for (i = 0; i < ret; i++) {
349 if (!strcmp(created_session_name, sessions[i].name)) {
350 created_session = &sessions[i];
351 break;
352 }
353 }
354 if (!created_session) {
355 ERR("Failed to fetch properties of created session");
356 ret = CMD_ERROR;
357 goto error;
358 }
359
360 if (opt_shm_path) {
361 char datetime_suffix[17] = {};
362
363 /*
364 * An auto-generated session name already includes the creation
365 * timestamp.
366 */
367 if (session_name) {
368 uint64_t creation_time;
369 struct tm *timeinfo;
370 time_t creation_time_t;
371 size_t strftime_ret;
372
373 ret_code = lttng_session_get_creation_time(created_session, &creation_time);
374 if (ret_code != LTTNG_OK) {
375 ERR("%s", lttng_strerror(-ret_code));
376 ret = CMD_ERROR;
377 goto error;
378 }
379 creation_time_t = (time_t) creation_time;
380 timeinfo = localtime(&creation_time_t);
381 if (!timeinfo) {
382 PERROR("Failed to interpret session creation time");
383 ret = CMD_ERROR;
384 goto error;
385 }
386 strftime_ret = strftime(datetime_suffix,
387 sizeof(datetime_suffix),
388 "-%Y%m%d-%H%M%S",
389 timeinfo);
390 if (strftime_ret == 0) {
391 ERR("Failed to format session creation time.");
392 ret = CMD_ERROR;
393 goto error;
394 }
395 }
396
397 ret = snprintf(shm_path,
398 sizeof(shm_path),
399 "%s/%s%s",
400 opt_shm_path,
401 created_session_name,
402 datetime_suffix);
403 if (ret < 0 || ret >= sizeof(shm_path)) {
404 ERR("Failed to format the shared memory path.");
405 ret = CMD_ERROR;
406 goto error;
407 }
408 ret = lttng_set_session_shm_path(created_session_name, shm_path);
409 if (ret < 0) {
410 lttng_destroy_session(created_session_name);
411 ret = CMD_ERROR;
412 goto error;
413 }
414 }
415
416 if (opt_snapshot) {
417 MSG("Snapshot session %s created.", created_session_name);
418 } else if (opt_live_timer) {
419 MSG("Live session %s created.", created_session_name);
420 } else {
421 MSG("Session %s created.", created_session_name);
422 }
423
424 if (*created_session->path && !opt_snapshot) {
425 MSG("Traces will be output to %s", created_session->path);
426
427 if (opt_live_timer) {
428 MSG("Live timer interval set to %u %s", opt_live_timer, USEC_UNIT);
429 }
430 } else if (opt_snapshot) {
431 struct lttng_snapshot_output_list *list;
432 struct lttng_snapshot_output *iter;
433 char snapshot_url[LTTNG_PATH_MAX] = {};
434
435 ret = lttng_snapshot_list_output(created_session_name, &list);
436 if (ret < 0) {
437 ERR("Failed to list snapshot outputs.");
438 ret = CMD_ERROR;
439 goto error;
440 }
441
442 while ((iter = lttng_snapshot_output_list_get_next(list))) {
443 const char *url = NULL;
444
445 url = lttng_snapshot_output_get_ctrl_url(iter);
446 ret = lttng_strncpy(snapshot_url, url, sizeof(snapshot_url));
447 if (ret) {
448 snapshot_url[0] = '\0';
449 ERR("Failed to retrieve snapshot output destination");
450 }
451 break;
452 }
453 lttng_snapshot_output_list_destroy(list);
454
455 if (*snapshot_url) {
456 MSG("Default snapshot output set to %s", snapshot_url);
457 }
458 MSG("Every channel enabled for this session will be set to mmap output and default to overwrite mode.");
459 }
460 if (opt_shm_path) {
461 MSG("Shared memory path set to %s", shm_path);
462 }
463
464 /* Mi output */
465 if (lttng_opt_mi) {
466 ret = mi_created_session(created_session_name);
467 if (ret) {
468 ret = CMD_ERROR;
469 goto error;
470 }
471 }
472
473 /* Init lttng session config */
474 ret = config_init(created_session_name);
475 if (ret < 0) {
476 ret = CMD_ERROR;
477 goto error;
478 }
479
480 ret = CMD_SUCCESS;
481 error:
482 lttng_session_descriptor_destroy(session_descriptor);
483 free(sessions);
484 return ret;
485 }
486
487 /*
488 * spawn_sessiond
489 *
490 * Spawn a session daemon by forking and execv.
491 */
492 static int spawn_sessiond(const char *pathname)
493 {
494 int ret = 0;
495 pid_t pid;
496
497 MSG("Spawning a session daemon");
498 pid = fork();
499 if (pid == 0) {
500 /*
501 * Spawn session daemon in daemon mode.
502 */
503 execlp(pathname, "lttng-sessiond", "--daemonize", NULL);
504 /* execlp only returns if error happened */
505 if (errno == ENOENT) {
506 ERR("No session daemon found. Use --sessiond-path.");
507 } else {
508 PERROR("execlp");
509 }
510 kill(getppid(), SIGTERM); /* wake parent */
511 exit(EXIT_FAILURE);
512 } else if (pid > 0) {
513 /*
514 * In daemon mode (--daemonize), sessiond only exits when
515 * it's ready to accept commands.
516 */
517 for (;;) {
518 int status;
519 pid_t wait_pid_ret = waitpid(pid, &status, 0);
520
521 if (wait_pid_ret < 0) {
522 if (errno == EINTR) {
523 continue;
524 }
525 PERROR("waitpid");
526 ret = -errno;
527 goto end;
528 }
529
530 if (WIFSIGNALED(status)) {
531 ERR("Session daemon was killed by signal %d", WTERMSIG(status));
532 ret = -1;
533 goto end;
534 } else if (WIFEXITED(status)) {
535 DBG("Session daemon terminated normally (exit status: %d)",
536 WEXITSTATUS(status));
537
538 if (WEXITSTATUS(status) != 0) {
539 ERR("Session daemon terminated with an error (exit status: %d)",
540 WEXITSTATUS(status));
541 ret = -1;
542 goto end;
543 }
544 break;
545 }
546 }
547
548 goto end;
549 } else {
550 PERROR("fork");
551 ret = -1;
552 goto end;
553 }
554
555 end:
556 return ret;
557 }
558
559 /*
560 * launch_sessiond
561 *
562 * Check if the session daemon is available using
563 * the liblttngctl API for the check. If not, try to
564 * spawn a daemon.
565 */
566 static int launch_sessiond(void)
567 {
568 int ret;
569 const char *pathname = NULL;
570
571 ret = lttng_session_daemon_alive();
572 if (ret) {
573 /* Sessiond is alive, not an error */
574 ret = 0;
575 goto end;
576 }
577
578 /* Try command line option path */
579 pathname = opt_sessiond_path;
580
581 /* Try LTTNG_SESSIOND_PATH env variable */
582 if (pathname == NULL) {
583 pathname = getenv(DEFAULT_SESSIOND_PATH_ENV);
584 }
585
586 /* Try with configured path */
587 if (pathname == NULL) {
588 if (CONFIG_SESSIOND_BIN[0] != '\0') {
589 pathname = CONFIG_SESSIOND_BIN;
590 }
591 }
592
593 /* Try the default path */
594 if (pathname == NULL) {
595 pathname = INSTALL_BIN_PATH "/lttng-sessiond";
596 }
597
598 DBG("Session daemon binary path: %s", pathname);
599
600 /* Check existence and permissions */
601 ret = access(pathname, F_OK | X_OK);
602 if (ret < 0) {
603 ERR("No such file or access denied: %s", pathname);
604 goto end;
605 }
606
607 ret = spawn_sessiond(pathname);
608 end:
609 if (ret) {
610 ERR("Problem occurred while launching session daemon (%s)", pathname);
611 }
612 return ret;
613 }
614
615 static int validate_url_option_combination(void)
616 {
617 int ret = 0;
618 int used_count = 0;
619
620 used_count += !!opt_url;
621 used_count += !!opt_output_path;
622 used_count += (opt_data_url || opt_ctrl_url);
623 if (used_count > 1) {
624 ERR("Only one of the --set-url, --ctrl-url/data-url, or --output options may be used at once.");
625 ret = -1;
626 }
627
628 return ret;
629 }
630
631 /*
632 * The 'create <options>' first level command
633 *
634 * Returns one of the CMD_* result constants.
635 */
636 int cmd_create(int argc, const char **argv)
637 {
638 int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1;
639 char *opt_arg = NULL;
640 const char *arg_session_name = NULL;
641 const char *leftover = NULL;
642 static poptContext pc;
643
644 pc = poptGetContext(NULL, argc, argv, long_options, 0);
645 poptReadDefaultConfig(pc, 0);
646
647 while ((opt = poptGetNextOpt(pc)) != -1) {
648 switch (opt) {
649 case OPT_HELP:
650 SHOW_HELP();
651 goto end;
652 case OPT_LIST_OPTIONS:
653 list_cmd_options(stdout, long_options);
654 goto end;
655 case OPT_LIVE_TIMER:
656 {
657 uint64_t v;
658
659 errno = 0;
660 if (opt_arg) {
661 free(opt_arg);
662 opt_arg = nullptr;
663 }
664
665 opt_arg = poptGetOptArg(pc);
666 if (!opt_arg) {
667 /* Set up default values. */
668 opt_live_timer = (uint32_t) DEFAULT_LTTNG_LIVE_TIMER;
669 DBG("Session live timer interval set to default value %d",
670 opt_live_timer);
671 break;
672 }
673
674 if (utils_parse_time_suffix(opt_arg, &v) < 0) {
675 ERR("Wrong value for --live parameter: %s", opt_arg);
676 ret = CMD_ERROR;
677 goto end;
678 }
679
680 if (v != (uint32_t) v) {
681 ERR("32-bit overflow in --live parameter: %s", opt_arg);
682 ret = CMD_ERROR;
683 goto end;
684 }
685
686 if (v == 0) {
687 ERR("Live timer interval must be greater than zero");
688 ret = CMD_ERROR;
689 goto end;
690 }
691
692 opt_live_timer = (uint32_t) v;
693 DBG("Session live timer interval set to %d", opt_live_timer);
694 break;
695 }
696 default:
697 ret = CMD_UNDEFINED;
698 goto end;
699 }
700 }
701
702 if (opt_no_consumer) {
703 MSG("The option --no-consumer is obsolete. Use --no-output now.");
704 ret = CMD_WARNING;
705 goto end;
706 }
707
708 ret = validate_url_option_combination();
709 if (ret) {
710 ret = CMD_ERROR;
711 goto end;
712 }
713
714 /* Spawn a session daemon if needed */
715 if (!opt_no_sessiond) {
716 ret = launch_sessiond();
717 if (ret) {
718 ret = CMD_ERROR;
719 goto end;
720 }
721 }
722
723 /* MI initialization */
724 if (lttng_opt_mi) {
725 writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi);
726 if (!writer) {
727 ret = -LTTNG_ERR_NOMEM;
728 goto end;
729 }
730
731 /* Open command element */
732 ret = mi_lttng_writer_command_open(writer, mi_lttng_element_command_create);
733 if (ret) {
734 ret = CMD_ERROR;
735 goto end;
736 }
737
738 /* Open output element */
739 ret = mi_lttng_writer_open_element(writer, mi_lttng_element_command_output);
740 if (ret) {
741 ret = CMD_ERROR;
742 goto end;
743 }
744 }
745
746 /* Get the optional session name argument. */
747 arg_session_name = poptGetArg(pc);
748
749 leftover = poptGetArg(pc);
750 if (leftover) {
751 ERR("Unknown argument: %s", leftover);
752 ret = CMD_ERROR;
753 goto end;
754 }
755
756 command_ret = create_session(arg_session_name);
757 if (command_ret) {
758 success = 0;
759 }
760
761 if (lttng_opt_mi) {
762 /* Close output element */
763 ret = mi_lttng_writer_close_element(writer);
764 if (ret) {
765 ret = CMD_ERROR;
766 goto end;
767 }
768
769 /* Success ? */
770 ret = mi_lttng_writer_write_element_bool(
771 writer, mi_lttng_element_command_success, success);
772 if (ret) {
773 ret = CMD_ERROR;
774 goto end;
775 }
776
777 /* Command element close */
778 ret = mi_lttng_writer_command_close(writer);
779 if (ret) {
780 ret = CMD_ERROR;
781 goto end;
782 }
783 }
784
785 end:
786 /* Mi clean-up */
787 if (writer && mi_lttng_writer_destroy(writer)) {
788 /* Preserve original error code */
789 ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL;
790 }
791
792 /* Overwrite ret if an error occurred in create_session() */
793 ret = command_ret ? command_ret : ret;
794
795 free(opt_arg);
796 poptFreeContext(pc);
797 return ret;
798 }
This page took 0.045756 seconds and 4 git commands to generate.