162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
262306a36Sopenharmony_ci/* Copyright (c) 2015-2017 Daniel Borkmann */
362306a36Sopenharmony_ci/* Copyright (c) 2018 Netronome Systems, Inc. */
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <errno.h>
662306a36Sopenharmony_ci#include <limits.h>
762306a36Sopenharmony_ci#include <signal.h>
862306a36Sopenharmony_ci#include <stdio.h>
962306a36Sopenharmony_ci#include <string.h>
1062306a36Sopenharmony_ci#include <unistd.h>
1162306a36Sopenharmony_ci#include <linux/magic.h>
1262306a36Sopenharmony_ci#include <fcntl.h>
1362306a36Sopenharmony_ci#include <sys/vfs.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "main.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#ifndef TRACEFS_MAGIC
1862306a36Sopenharmony_ci# define TRACEFS_MAGIC	0x74726163
1962306a36Sopenharmony_ci#endif
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define _textify(x)	#x
2262306a36Sopenharmony_ci#define textify(x)	_textify(x)
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ciFILE *trace_pipe_fd;
2562306a36Sopenharmony_cichar *buff;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int validate_tracefs_mnt(const char *mnt, unsigned long magic)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct statfs st_fs;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	if (statfs(mnt, &st_fs) < 0)
3262306a36Sopenharmony_ci		return -ENOENT;
3362306a36Sopenharmony_ci	if ((unsigned long)st_fs.f_type != magic)
3462306a36Sopenharmony_ci		return -ENOENT;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	return 0;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic bool
4062306a36Sopenharmony_cifind_tracefs_mnt_single(unsigned long magic, char *mnt, const char *mntpt)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	size_t src_len;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (validate_tracefs_mnt(mntpt, magic))
4562306a36Sopenharmony_ci		return false;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	src_len = strlen(mntpt);
4862306a36Sopenharmony_ci	if (src_len + 1 >= PATH_MAX) {
4962306a36Sopenharmony_ci		p_err("tracefs mount point name too long");
5062306a36Sopenharmony_ci		return false;
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	strcpy(mnt, mntpt);
5462306a36Sopenharmony_ci	return true;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic bool get_tracefs_pipe(char *mnt)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	static const char * const known_mnts[] = {
6062306a36Sopenharmony_ci		"/sys/kernel/debug/tracing",
6162306a36Sopenharmony_ci		"/sys/kernel/tracing",
6262306a36Sopenharmony_ci		"/tracing",
6362306a36Sopenharmony_ci		"/trace",
6462306a36Sopenharmony_ci	};
6562306a36Sopenharmony_ci	const char *pipe_name = "/trace_pipe";
6662306a36Sopenharmony_ci	const char *fstype = "tracefs";
6762306a36Sopenharmony_ci	char type[100], format[32];
6862306a36Sopenharmony_ci	const char * const *ptr;
6962306a36Sopenharmony_ci	bool found = false;
7062306a36Sopenharmony_ci	FILE *fp;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	for (ptr = known_mnts; ptr < known_mnts + ARRAY_SIZE(known_mnts); ptr++)
7362306a36Sopenharmony_ci		if (find_tracefs_mnt_single(TRACEFS_MAGIC, mnt, *ptr))
7462306a36Sopenharmony_ci			goto exit_found;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	fp = fopen("/proc/mounts", "r");
7762306a36Sopenharmony_ci	if (!fp)
7862306a36Sopenharmony_ci		return false;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* Allow room for NULL terminating byte and pipe file name */
8162306a36Sopenharmony_ci	snprintf(format, sizeof(format), "%%*s %%%zds %%99s %%*s %%*d %%*d\\n",
8262306a36Sopenharmony_ci		 PATH_MAX - strlen(pipe_name) - 1);
8362306a36Sopenharmony_ci	while (fscanf(fp, format, mnt, type) == 2)
8462306a36Sopenharmony_ci		if (strcmp(type, fstype) == 0) {
8562306a36Sopenharmony_ci			found = true;
8662306a36Sopenharmony_ci			break;
8762306a36Sopenharmony_ci		}
8862306a36Sopenharmony_ci	fclose(fp);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	/* The string from fscanf() might be truncated, check mnt is valid */
9162306a36Sopenharmony_ci	if (found && validate_tracefs_mnt(mnt, TRACEFS_MAGIC))
9262306a36Sopenharmony_ci		goto exit_found;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (block_mount)
9562306a36Sopenharmony_ci		return false;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	p_info("could not find tracefs, attempting to mount it now");
9862306a36Sopenharmony_ci	/* Most of the time, tracefs is automatically mounted by debugfs at
9962306a36Sopenharmony_ci	 * /sys/kernel/debug/tracing when we try to access it. If we could not
10062306a36Sopenharmony_ci	 * find it, it is likely that debugfs is not mounted. Let's give one
10162306a36Sopenharmony_ci	 * attempt at mounting just tracefs at /sys/kernel/tracing.
10262306a36Sopenharmony_ci	 */
10362306a36Sopenharmony_ci	strcpy(mnt, known_mnts[1]);
10462306a36Sopenharmony_ci	if (mount_tracefs(mnt))
10562306a36Sopenharmony_ci		return false;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ciexit_found:
10862306a36Sopenharmony_ci	strcat(mnt, pipe_name);
10962306a36Sopenharmony_ci	return true;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic void exit_tracelog(int signum)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	fclose(trace_pipe_fd);
11562306a36Sopenharmony_ci	free(buff);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (json_output) {
11862306a36Sopenharmony_ci		jsonw_end_array(json_wtr);
11962306a36Sopenharmony_ci		jsonw_destroy(&json_wtr);
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	exit(0);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ciint do_tracelog(int argc, char **argv)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	const struct sigaction act = {
12862306a36Sopenharmony_ci		.sa_handler = exit_tracelog
12962306a36Sopenharmony_ci	};
13062306a36Sopenharmony_ci	char trace_pipe[PATH_MAX];
13162306a36Sopenharmony_ci	size_t buff_len = 0;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (json_output)
13462306a36Sopenharmony_ci		jsonw_start_array(json_wtr);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (!get_tracefs_pipe(trace_pipe))
13762306a36Sopenharmony_ci		return -1;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	trace_pipe_fd = fopen(trace_pipe, "r");
14062306a36Sopenharmony_ci	if (!trace_pipe_fd) {
14162306a36Sopenharmony_ci		p_err("could not open trace pipe: %s", strerror(errno));
14262306a36Sopenharmony_ci		return -1;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	sigaction(SIGHUP, &act, NULL);
14662306a36Sopenharmony_ci	sigaction(SIGINT, &act, NULL);
14762306a36Sopenharmony_ci	sigaction(SIGTERM, &act, NULL);
14862306a36Sopenharmony_ci	while (1) {
14962306a36Sopenharmony_ci		ssize_t ret;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		ret = getline(&buff, &buff_len, trace_pipe_fd);
15262306a36Sopenharmony_ci		if (ret <= 0) {
15362306a36Sopenharmony_ci			p_err("failed to read content from trace pipe: %s",
15462306a36Sopenharmony_ci			      strerror(errno));
15562306a36Sopenharmony_ci			break;
15662306a36Sopenharmony_ci		}
15762306a36Sopenharmony_ci		if (json_output)
15862306a36Sopenharmony_ci			jsonw_string(json_wtr, buff);
15962306a36Sopenharmony_ci		else
16062306a36Sopenharmony_ci			printf("%s", buff);
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	fclose(trace_pipe_fd);
16462306a36Sopenharmony_ci	free(buff);
16562306a36Sopenharmony_ci	return -1;
16662306a36Sopenharmony_ci}
167