port: fix compat/endian.h on FreeBSD
[lttng-tools.git] / src / bin / lttng-crash / lttng-crash.c
index f50b0f86b26abf709a9e8047133312599084ecf5..7f1f52803f9f5c5102ebcdbc5b8f5cd1281fa3d0 100644 (file)
@@ -2,21 +2,10 @@
  * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
  * Copyright (C) 2014 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2 only,
- * as published by the Free Software Foundation.
+ * SPDX-License-Identifier: GPL-2.0-only
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-#define _GNU_SOURCE
 #include <getopt.h>
 #include <signal.h>
 #include <stdio.h>
 #include <fcntl.h>
 #include <sys/wait.h>
 #include <unistd.h>
-#include <config.h>
 #include <ctype.h>
 #include <dirent.h>
-#include <byteswap.h>
+#include <common/compat/endian.h>
 #include <inttypes.h>
+#include <stdbool.h>
 
 #include <version.h>
 #include <lttng/lttng.h>
 #include <common/common.h>
-
-#define DEFAULT_VIEWER "babeltrace"
+#include <common/spawn-viewer.h>
+#include <common/utils.h>
 
 #define COPY_BUFLEN            4096
 #define RB_CRASH_DUMP_ABI_LEN  32
                0xF1 ^ 0xFF, 0x77 ^ 0xFF, 0xBF ^ 0xFF, 0x17 ^ 0xFF,     \
        }
 
+static const char *help_msg =
+#ifdef LTTNG_EMBED_HELP
+#include <lttng-crash.1.h>
+#else
+NULL
+#endif
+;
+
 /*
  * Non-static to ensure the compiler does not optimize away the xor.
  */
@@ -103,7 +100,7 @@ enum rb_modes {
 
 struct crash_abi_unknown {
        uint8_t magic[RB_CRASH_DUMP_ABI_MAGIC_LEN];
-       uint64_t mmap_length;   /* Overall lenght of crash record */
+       uint64_t mmap_length;   /* Overall length of crash record */
        uint16_t endian;        /*
                                 * { 0x12, 0x34 }: big endian
                                 * { 0x34, 0x12 }: little endian
@@ -182,9 +179,9 @@ struct lttng_crash_layout {
 };
 
 /* Variables */
-static char *progname,
-       *opt_viewer_path = DEFAULT_VIEWER,
-       *opt_output_path;
+static const char *progname;
+static char *opt_viewer_path = NULL;
+static char *opt_output_path = NULL;
 
 static char *input_path;
 
@@ -205,35 +202,24 @@ static struct option long_options[] = {
        { NULL, 0, NULL, 0 },
 };
 
-static void usage(FILE *ofp)
+static void usage(void)
 {
-       fprintf(ofp, "LTTng Crash Trace Viewer " VERSION " - " VERSION_NAME "%s\n\n",
-               GIT_VERSION[0] == '\0' ? "" : " - " GIT_VERSION);
-       fprintf(ofp, "usage: lttng [OPTIONS] FILE\n");
-       fprintf(ofp, "\n");
-       fprintf(ofp, "Options:\n");
-       fprintf(ofp, "  -V, --version              Show version.\n");
-       fprintf(ofp, "  -h, --help                 Show this help.\n");
-       fprintf(ofp, "      --list-options         Simple listing of lttng-crash options.\n");
-       fprintf(ofp, "  -v, --verbose              Increase verbosity.\n");
-       fprintf(ofp, "  -e, --viewer               Specify viewer and/or options to use. This will\n"
-                    "                             completely override the default viewers so please\n"
-                    "                             make sure to specify the full command. The trace\n"
-                    "                             directory paths appended at the end to the\n"
-                    "                             arguments.\n");
-       fprintf(ofp, "  -x, --extract PATH         Extract trace(s) to specified path. Don't view\n"
-                    "                             trace.\n");
-       fprintf(ofp, "\n");
-       fprintf(ofp, "Please see the lttng-crash(1) man page for full documentation.\n");
-       fprintf(ofp, "See http://lttng.org for updates, bug reports and news.\n");
+       int ret = utils_show_help(1, "lttng-crash", help_msg);
+
+       if (ret) {
+               ERR("Cannot show --help for `lttng-crash`");
+               perror("exec");
+               exit(EXIT_FAILURE);
+       }
 }
 
 static void version(FILE *ofp)
 {
        fprintf(ofp, "%s (LTTng Crash Trace Viewer) " VERSION " - " VERSION_NAME
-"%s\n",
+                       "%s%s\n",
                        progname,
-                       GIT_VERSION[0] == '\0' ? "" : " - " GIT_VERSION);
+                       GIT_VERSION[0] == '\0' ? "" : " - " GIT_VERSION,
+                       EXTRA_VERSION_NAME[0] == '\0' ? "" : " - " EXTRA_VERSION_NAME);
 }
 
 /*
@@ -270,18 +256,18 @@ static int parse_args(int argc, char **argv)
        int opt, ret = 0;
 
        if (argc < 2) {
-               usage(stderr);
+               usage();
                exit(EXIT_FAILURE);
        }
 
-       while ((opt = getopt_long(argc, argv, "+Vhvex:", long_options, NULL)) != -1) {
+       while ((opt = getopt_long(argc, argv, "+Vhve:x:", long_options, NULL)) != -1) {
                switch (opt) {
                case 'V':
                        version(stdout);
                        ret = 1;
                        goto end;
                case 'h':
-                       usage(stdout);
+                       usage();
                        ret = 1;
                        goto end;
                case 'v':
@@ -291,9 +277,11 @@ static int parse_args(int argc, char **argv)
                        }
                        break;
                case 'e':
+                       free(opt_viewer_path);
                        opt_viewer_path = strdup(optarg);
                        break;
                case 'x':
+                       free(opt_output_path);
                        opt_output_path = strdup(optarg);
                        break;
                case OPT_DUMP_OPTIONS:
@@ -301,14 +289,14 @@ static int parse_args(int argc, char **argv)
                        ret = 1;
                        goto end;
                default:
-                       usage(stderr);
+                       ERR("Unknown command-line option");
                        goto error;
                }
        }
 
        /* No leftovers, or more than one input path, print usage and quit */
-       if ((argc - optind) == 0 || (argc - optind) > 1) {
-               usage(stderr);
+       if (argc - optind != 1) {
+               ERR("Command-line error: Specify exactly one input path");
                goto error;
        }
 
@@ -323,29 +311,33 @@ error:
 static
 int copy_file(const char *file_dest, const char *file_src)
 {
-       int fd_src, fd_dest;
+       int fd_src = -1, fd_dest = -1;
        ssize_t readlen, writelen;
        char buf[COPY_BUFLEN];
+       int ret;
 
        DBG("Copy metadata file '%s' into '%s'", file_src, file_dest);
 
        fd_src = open(file_src, O_RDONLY);
        if (fd_src < 0) {
                PERROR("Error opening %s for reading", file_src);
-               return fd_src;
+               ret = -errno;
+               goto error;
        }
        fd_dest = open(file_dest, O_RDWR | O_CREAT | O_EXCL,
                        S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
        if (fd_dest < 0) {
                PERROR("Error opening %s for writing", file_dest);
-               return fd_dest;
+               ret = -errno;
+               goto error;
        }
 
        for (;;) {
                readlen = lttng_read(fd_src, buf, COPY_BUFLEN);
                if (readlen < 0) {
                        PERROR("Error reading input file");
-                       return -1;
+                       ret = -1;
+                       goto error;
                }
                if (!readlen) {
                        break;
@@ -353,10 +345,25 @@ int copy_file(const char *file_dest, const char *file_src)
                writelen = lttng_write(fd_dest, buf, readlen);
                if (writelen < readlen) {
                        PERROR("Error writing to output file");
-                       return -1;
+                       ret = -1;
+                       goto error;
                }
        }
-       return 0;
+
+       ret = 0;
+error:
+       if (fd_src >= 0) {
+               if (close(fd_src) < 0) {
+                       PERROR("Error closing %s", file_src);
+               }
+       }
+
+       if (fd_dest >= 0) {
+               if (close(fd_dest) < 0) {
+                       PERROR("Error closing %s", file_dest);
+               }
+       }
+       return ret;
 }
 
 static
@@ -366,19 +373,19 @@ uint64_t _crash_get_field(const struct lttng_crash_layout *layout,
        switch (size) {
        case 1: return *(uint8_t *) ptr;
        case 2: if (layout->reverse_byte_order) {
-                       return __bswap_16(*(uint16_t *) ptr);
+                       return bswap_16(*(uint16_t *) ptr);
                } else {
                        return *(uint16_t *) ptr;
 
                }
        case 4: if (layout->reverse_byte_order) {
-                       return __bswap_32(*(uint32_t *) ptr);
+                       return bswap_32(*(uint32_t *) ptr);
                } else {
                        return *(uint32_t *) ptr;
 
                }
        case 8: if (layout->reverse_byte_order) {
-                       return __bswap_64(*(uint64_t *) ptr);
+                       return bswap_64(*(uint64_t *) ptr);
                } else {
                        return *(uint64_t *) ptr;
                }
@@ -477,7 +484,8 @@ int check_magic(const uint8_t *magic)
 }
 
 static
-int get_crash_layout(struct lttng_crash_layout *layout, int fd)
+int get_crash_layout(struct lttng_crash_layout *layout, int fd,
+               const char *input_file)
 {
        char *map;
        int ret = 0, unmapret;
@@ -488,7 +496,21 @@ int get_crash_layout(struct lttng_crash_layout *layout, int fd)
        const struct crash_abi_unknown *abi;
        uint16_t endian;
        enum lttng_crash_type layout_type;
+       struct stat stat;
 
+       ret = fstat(fd, &stat);
+       if (ret < 0) {
+               PERROR("Failed to fstat '%s'", input_file);
+               return -1;
+       }
+       if (stat.st_size < RB_CRASH_DUMP_ABI_LEN) {
+               ERR("File '%s' truncated: file length of %" PRIi64
+                               " bytes does not meet the minimal expected "
+                               "length of %d bytes",
+                               input_file, (int64_t) stat.st_size,
+                               RB_CRASH_DUMP_ABI_LEN);
+               return -1;
+       }
        map = mmap(NULL, RB_CRASH_DUMP_ABI_LEN, PROT_READ, MAP_PRIVATE,
                fd, 0);
        if (map == MAP_FAILED) {
@@ -642,7 +664,7 @@ int copy_crash_subbuf(const struct lttng_crash_layout *layout,
                return -EINVAL;
        }
 
-       DBG("Copy crash subbuffer at offset %lu", offset);
+       DBG("Copy crash subbuffer at offset %" PRIu64, offset);
        sbidx = subbuf_index(offset, buf_size, subbuf_size);
 
        /*
@@ -698,7 +720,7 @@ int copy_crash_subbuf(const struct lttng_crash_layout *layout,
                                subbuf_ptr + layout->offset.packet_size,
                                layout->length.packet_size);
                        if (layout->reverse_byte_order) {
-                               packet_size = __bswap_64(packet_size);
+                               packet_size = bswap_64(packet_size);
                        }
                        packet_size /= CHAR_BIT;
                } else {
@@ -714,7 +736,7 @@ int copy_crash_subbuf(const struct lttng_crash_layout *layout,
                 */
                patch_size = committed * CHAR_BIT;
                if (layout->reverse_byte_order) {
-                       patch_size = __bswap_64(patch_size);
+                       patch_size = bswap_64(patch_size);
                }
                if (layout->length.content_size) {
                        memcpy(subbuf_ptr + layout->offset.content_size,
@@ -766,7 +788,8 @@ int copy_crash_data(const struct lttng_crash_layout *layout, int fd_dest,
        readlen = lttng_read(fd_src, buf, src_file_len);
        if (readlen < 0) {
                PERROR("Error reading input file");
-               return -1;
+               ret = -1;
+               goto end;
        }
 
        prod_offset = crash_get_field(layout, buf, prod_offset);
@@ -816,7 +839,7 @@ int extract_file(int output_dir_fd, const char *output_file,
        }
 
        /* Query the crash ABI layout */
-       ret = get_crash_layout(&layout, fd_src);
+       ret = get_crash_layout(&layout, fd_src, input_file);
        if (ret) {
                goto close_src;
        }
@@ -951,6 +974,7 @@ int extract_trace_recursive(const char *output_path,
        DIR *dir;
        int dir_fd, ret = 0, closeret;
        struct dirent *entry;
+       size_t path_len;
        int has_warning = 0;
 
        /* Open directory */
@@ -959,6 +983,9 @@ int extract_trace_recursive(const char *output_path,
                PERROR("Cannot open '%s' path", input_path);
                return -1;
        }
+
+       path_len = strlen(input_path);
+
        dir_fd = dirfd(dir);
        if (dir_fd < 0) {
                PERROR("dirfd");
@@ -966,13 +993,34 @@ int extract_trace_recursive(const char *output_path,
        }
 
        while ((entry = readdir(dir))) {
+               struct stat st;
+               size_t name_len;
+               char filename[PATH_MAX];
+
                if (!strcmp(entry->d_name, ".")
                                || !strcmp(entry->d_name, "..")) {
                        continue;
                }
-               switch (entry->d_type) {
-               case DT_DIR:
-               {
+
+               name_len = strlen(entry->d_name);
+               if (path_len + name_len + 2 > sizeof(filename)) {
+                       ERR("Failed to remove file: path name too long (%s/%s)",
+                               input_path, entry->d_name);
+                       continue;
+               }
+
+               if (snprintf(filename, sizeof(filename), "%s/%s",
+                               input_path, entry->d_name) < 0) {
+                       ERR("Failed to format path.");
+                       continue;
+               }
+
+               if (stat(filename, &st)) {
+                       PERROR("stat");
+                       continue;
+               }
+
+               if (S_ISDIR(st.st_mode)) {
                        char output_subpath[PATH_MAX];
                        char input_subpath[PATH_MAX];
 
@@ -1004,9 +1052,7 @@ int extract_trace_recursive(const char *output_path,
                        if (ret) {
                                has_warning = 1;
                        }
-                       break;
-               }
-               case DT_REG:
+               } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
                        if (!strcmp(entry->d_name, "metadata")) {
                                ret = extract_one_trace(output_path,
                                        input_path);
@@ -1016,9 +1062,7 @@ int extract_trace_recursive(const char *output_path,
                                        has_warning = 1;
                                }
                        }
-                       /* Ignore other files */
-                       break;
-               default:
+               } else {
                        has_warning = 1;
                        goto end;
                }
@@ -1032,54 +1076,107 @@ end:
 }
 
 static
-int delete_trace(const char *trace_path)
+int delete_dir_recursive(const char *path)
 {
-       DIR *trace_dir;
-       int trace_dir_fd, ret = 0, closeret;
+       DIR *dir;
+       int dir_fd, ret = 0, closeret;
+       size_t path_len;
        struct dirent *entry;
 
        /* Open trace directory */
-       trace_dir = opendir(trace_path);
-       if (!trace_dir) {
-               PERROR("Cannot open '%s' path", trace_path);
-               return -1;
+       dir = opendir(path);
+       if (!dir) {
+               PERROR("Cannot open '%s' path", path);
+               ret = -errno;
+               goto end_no_closedir;
        }
-       trace_dir_fd = dirfd(trace_dir);
-       if (trace_dir_fd < 0) {
+
+       path_len = strlen(path);
+
+       dir_fd = dirfd(dir);
+       if (dir_fd < 0) {
                PERROR("dirfd");
-               return -1;
+               ret = -errno;
+               goto end;
        }
 
-       while ((entry = readdir(trace_dir))) {
+       while ((entry = readdir(dir))) {
+               struct stat st;
+               size_t name_len;
+               char filename[PATH_MAX];
+
                if (!strcmp(entry->d_name, ".")
                                || !strcmp(entry->d_name, "..")) {
                        continue;
                }
-               switch (entry->d_type) {
-               case DT_DIR:
-                       unlinkat(trace_dir_fd, entry->d_name, AT_REMOVEDIR);
-                       break;
-               case DT_REG:
-                       unlinkat(trace_dir_fd, entry->d_name, 0);
-                       break;
-               default:
+
+               name_len = strlen(entry->d_name);
+               if (path_len + name_len + 2 > sizeof(filename)) {
+                       ERR("Failed to remove file: path name too long (%s/%s)",
+                               path, entry->d_name);
+                       continue;
+               }
+
+               if (snprintf(filename, sizeof(filename), "%s/%s",
+                               path, entry->d_name) < 0) {
+                       ERR("Failed to format path.");
+                       continue;
+               }
+
+               if (stat(filename, &st)) {
+                       PERROR("stat");
+                       continue;
+               }
+
+               if (S_ISDIR(st.st_mode)) {
+                       char *subpath = zmalloc(PATH_MAX);
+
+                       if (!subpath) {
+                               PERROR("zmalloc path");
+                               ret = -1;
+                               goto end;
+                       }
+                       strncpy(subpath, path, PATH_MAX);
+                       subpath[PATH_MAX - 1] = '\0';
+                       strncat(subpath, "/",
+                                       PATH_MAX - strlen(subpath) - 1);
+                       strncat(subpath, entry->d_name,
+                                       PATH_MAX - strlen(subpath) - 1);
+
+                       ret = delete_dir_recursive(subpath);
+                       free(subpath);
+                       if (ret) {
+                               /* Error occurred, abort traversal. */
+                               goto end;
+                       }
+               } else if (S_ISREG(st.st_mode)) {
+                       ret = unlinkat(dir_fd, entry->d_name, 0);
+                       if (ret) {
+                               PERROR("Unlinking '%s'", entry->d_name);
+                               goto end;
+                       }
+               } else {
                        ret = -EINVAL;
                        goto end;
                }
        }
 end:
-       closeret = closedir(trace_dir);
+       if (!ret) {
+               ret = rmdir(path);
+               if (ret) {
+                       PERROR("rmdir '%s'", path);
+               }
+       }
+       closeret = closedir(dir);
        if (closeret) {
                PERROR("closedir");
        }
-       if (!ret) {
-               ret = rmdir(trace_path);
-       }
+end_no_closedir:
        return ret;
 }
 
 static
-int view_trace(const char *viewer_path, const char *trace_path)
+int view_trace(const char *trace_path, char *viewer_path)
 {
        pid_t pid;
 
@@ -1100,13 +1197,12 @@ int view_trace(const char *viewer_path, const char *trace_path)
                /* Child */
                int ret;
 
-               ret = execlp(viewer_path, viewer_path,
-                       trace_path, (char *) NULL);
+               ret = spawn_viewer(trace_path, viewer_path, false);
                if (ret) {
-                       PERROR("execlp");
                        exit(EXIT_FAILURE);
                }
-               exit(EXIT_SUCCESS);     /* Never reached */
+               /* Never reached */
+               exit(EXIT_SUCCESS);
        }
        return 0;
 }
@@ -1116,7 +1212,8 @@ int view_trace(const char *viewer_path, const char *trace_path)
  */
 int main(int argc, char *argv[])
 {
-       int ret, has_warning = 0;
+       int ret;
+       bool has_warning = false;
        const char *output_path = NULL;
        char tmppath[] = "/tmp/lttng-crash-XXXXXX";
 
@@ -1124,9 +1221,10 @@ int main(int argc, char *argv[])
 
        ret = parse_args(argc, argv);
        if (ret > 0) {
-               exit(EXIT_SUCCESS);
+               goto end;
        } else if (ret < 0) {
-               exit(EXIT_FAILURE);
+               has_warning = true;
+               goto end;
        }
 
        if (opt_output_path) {
@@ -1134,34 +1232,38 @@ int main(int argc, char *argv[])
                ret = mkdir(output_path, S_IRWXU | S_IRWXG);
                if (ret) {
                        PERROR("mkdir");
-                       exit(EXIT_FAILURE);
+                       has_warning = true;
+                       goto end;
                }
        } else {
                output_path = mkdtemp(tmppath);
                if (!output_path) {
                        PERROR("mkdtemp");
-                       exit(EXIT_FAILURE);
+                       has_warning = true;
+                       goto end;
                }
        }
 
        ret = extract_trace_recursive(output_path, input_path);
        if (ret < 0) {
-               exit(EXIT_FAILURE);
+               has_warning = true;
+               goto end;
        } else if (ret > 0) {
-               has_warning = 1;
+               /* extract_trace_recursive reported a warning. */
+               has_warning = true;
        }
        if (!opt_output_path) {
                /* View trace */
-               ret = view_trace(opt_viewer_path, output_path);
-               if (ret)
-                       has_warning = 1;
-
+               ret = view_trace(output_path, opt_viewer_path);
+               if (ret) {
+                       has_warning = true;
+               }
                /* unlink temporary trace */
-               ret = delete_trace(output_path);
-               if (ret)
-                       has_warning = 1;
+               ret = delete_dir_recursive(output_path);
+               if (ret) {
+                       has_warning = true;
+               }
        }
-       if (has_warning)
-               exit(EXIT_FAILURE);
-       exit(EXIT_SUCCESS);
+end:
+       exit(has_warning ? EXIT_FAILURE : EXIT_SUCCESS);
 }
This page took 0.031793 seconds and 4 git commands to generate.