167 lines
3.5 KiB
C
167 lines
3.5 KiB
C
|
// 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;
|
||
|
}
|