X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-crash%2Flttng-crash.c;h=298ed71a43c2b1b47053156f624aa50b39fd0519;hp=793877d9d846fd06c906488926884971632fe60d;hb=ef6fc488ca5672ccd46efac9de9db0cfd24bc810;hpb=acade31f6b673d845aca862a75b2edb936896e34 diff --git a/src/bin/lttng-crash/lttng-crash.c b/src/bin/lttng-crash/lttng-crash.c index 793877d9d..298ed71a4 100644 --- a/src/bin/lttng-crash/lttng-crash.c +++ b/src/bin/lttng-crash/lttng-crash.c @@ -16,7 +16,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#define _GNU_SOURCE #include #include #include @@ -29,15 +28,16 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include +#include #define DEFAULT_VIEWER "babeltrace" @@ -61,6 +61,14 @@ 0xF1 ^ 0xFF, 0x77 ^ 0xFF, 0xBF ^ 0xFF, 0x17 ^ 0xFF, \ } +static const char *help_msg = +#ifdef LTTNG_EMBED_HELP +#include +#else +NULL +#endif +; + /* * Non-static to ensure the compiler does not optimize away the xor. */ @@ -103,7 +111,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 @@ -183,8 +191,8 @@ struct lttng_crash_layout { /* Variables */ static char *progname, - *opt_viewer_path = DEFAULT_VIEWER, - *opt_output_path; + *opt_viewer_path = NULL, + *opt_output_path = NULL; static char *input_path; @@ -205,27 +213,15 @@ 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-crash [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) @@ -270,18 +266,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 +287,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 +299,18 @@ static int parse_args(int argc, char **argv) ret = 1; goto end; default: - usage(stderr); + ERR("Unknown command-line option"); goto error; } } + if (!opt_viewer_path) { + opt_viewer_path = DEFAULT_VIEWER; + } + /* 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 +325,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 +359,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 @@ -642,7 +663,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); /* @@ -766,7 +787,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); @@ -951,6 +973,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 +982,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 +992,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 +1051,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 +1061,7 @@ int extract_trace_recursive(const char *output_path, has_warning = 1; } } - /* Ignore other files */ - break; - default: + } else { has_warning = 1; goto end; } @@ -1032,49 +1075,102 @@ 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; } @@ -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; - + 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); }