// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) /* Copyright (c) 2015-2017 Daniel Borkmann */ /* Copyright (c) 2018 Netronome Systems, Inc. */ #include <errno.h> #include <limits.h> #include <signal.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <linux/magic.h> #include <fcntl.h> #include <sys/vfs.h> #include "main.h" #ifndef TRACEFS_MAGIC # define TRACEFS_MAGIC 0x74726163 #endif #define _textify(x) #x #define textify(x) _textify(x) FILE *trace_pipe_fd; char *buff; static int validate_tracefs_mnt(const char *mnt, unsigned long magic) { struct statfs st_fs; if (statfs(mnt, &st_fs) < 0) return -ENOENT; if ((unsigned long)st_fs.f_type != magic) return -ENOENT; return 0; } static bool find_tracefs_mnt_single(unsigned long magic, char *mnt, const char *mntpt) { size_t src_len; if (validate_tracefs_mnt(mntpt, magic)) return false; src_len = strlen(mntpt); if (src_len + 1 >= PATH_MAX) { p_err("tracefs mount point name too long"); return false; } strcpy(mnt, mntpt); return true; } static bool get_tracefs_pipe(char *mnt) { static const char * const known_mnts[] = { "/sys/kernel/debug/tracing", "/sys/kernel/tracing", "/tracing", "/trace", }; const char *pipe_name = "/trace_pipe"; const char *fstype = "tracefs"; char type[100], format[32]; const char * const *ptr; bool found = false; FILE *fp; for (ptr = known_mnts; ptr < known_mnts + ARRAY_SIZE(known_mnts); ptr++) if (find_tracefs_mnt_single(TRACEFS_MAGIC, mnt, *ptr)) goto exit_found; fp = fopen("/proc/mounts", "r"); if (!fp) return false; /* Allow room for NULL terminating byte and pipe file name */ snprintf(format, sizeof(format), "%%*s %%%zds %%99s %%*s %%*d %%*d\\n", PATH_MAX - strlen(pipe_name) - 1); while (fscanf(fp, format, mnt, type) == 2) if (strcmp(type, fstype) == 0) { found = true; break; } fclose(fp); /* The string from fscanf() might be truncated, check mnt is valid */ if (found && validate_tracefs_mnt(mnt, TRACEFS_MAGIC)) goto exit_found; if (block_mount) return false; p_info("could not find tracefs, attempting to mount it now"); /* Most of the time, tracefs is automatically mounted by debugfs at * /sys/kernel/debug/tracing when we try to access it. If we could not * find it, it is likely that debugfs is not mounted. Let's give one * attempt at mounting just tracefs at /sys/kernel/tracing. */ strcpy(mnt, known_mnts[1]); if (mount_tracefs(mnt)) return false; exit_found: strcat(mnt, pipe_name); return true; } static void exit_tracelog(int signum) { fclose(trace_pipe_fd); free(buff); if (json_output) { jsonw_end_array(json_wtr); jsonw_destroy(&json_wtr); } exit(0); } int do_tracelog(int argc, char **argv) { const struct sigaction act = { .sa_handler = exit_tracelog }; char trace_pipe[PATH_MAX]; size_t buff_len = 0; if (json_output) jsonw_start_array(json_wtr); if (!get_tracefs_pipe(trace_pipe)) return -1; trace_pipe_fd = fopen(trace_pipe, "r"); if (!trace_pipe_fd) { p_err("could not open trace pipe: %s", strerror(errno)); return -1; } sigaction(SIGHUP, &act, NULL); sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); while (1) { ssize_t ret; ret = getline(&buff, &buff_len, trace_pipe_fd); if (ret <= 0) { p_err("failed to read content from trace pipe: %s", strerror(errno)); break; } if (json_output) jsonw_string(json_wtr, buff); else printf("%s", buff); } fclose(trace_pipe_fd); free(buff); return -1; }