Viewing: monitor_lustrefs.c

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>
#include <errno.h>
#include <string.h>
#include <libgen.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/fanotify.h>


void usage(const char *progname)
{
	const char *base, *msg;

	base = strrchr(progname, '/');
	if (base == NULL)
		base = progname;
	else
		base++;

	fprintf(stderr, "Usage:\n");
	fprintf(stderr, "  %s LUSTRE_MOUNT_DIR\n", base);
	fprintf(stderr, "\n");

	msg =
"Description:\n"
"  Monitor some file operations on a lustre fs. Report the events as below:\n"
"    <events>:<lustre_file>:<pid>:[<command>]\n"
"\n"
"  <events>      is 1 event or multiple events separated by '&'. For example,\n"
"                'open', 'write&close'. Currently only these events are\n"
"                monitored:\n"
"                  - open\n"
"                  - close\n"
"                  - read\n"
"                  - write\n"
"  <lustre_file> is the file to be operated.\n"
"  <pid>         is the process id who operates the file.\n"
"  <command>     is the command who operates the file. It is reported only if\n"
"                the process is still running so it can be found from pid.\n"
"\n";
	fprintf(stderr, "%s", msg);
}

void print_event(struct fanotify_event_metadata *metadata)
{
	bool first = true;
	char procfd_path[PATH_MAX], path[PATH_MAX], cmd_file[PATH_MAX];
	int path_len, cmd_fd, cmd_len;

	// print event type
	if (metadata->mask & FAN_OPEN) {
		printf("open");
		first = false;
	}
	if (metadata->mask & FAN_ACCESS) {
		if (!first)
			printf("&");
		printf("read");
		first = false;
	}
	if (metadata->mask & FAN_MODIFY) {
		if (!first)
			printf("&");
		printf("write");
		first = false;
	}
	if (metadata->mask & FAN_CLOSE) {
		if (!first)
			printf("&");
		printf("close");
		first = false;
	}
	printf(":");

	// print the name of the file
	snprintf(procfd_path, sizeof(procfd_path), "/proc/self/fd/%d",
		 metadata->fd);
	path_len = readlink(procfd_path, path, sizeof(path) - 1);
	if (path_len == -1) {
		fprintf(stderr, "failed to read link target of %s. %d:%s\n",
			procfd_path, errno, strerror(errno));
		exit(EXIT_FAILURE);
	}
	path[path_len] = '\0';
	printf("%s:", path);
	close(metadata->fd);

	// print the pid
	printf("%d:", metadata->pid);

	// try to print the cmdline of process
	snprintf(cmd_file, sizeof(cmd_file), "/proc/%d/cmdline", metadata->pid);
	cmd_fd = open(cmd_file, O_RDONLY);
	if (cmd_fd >= 0) {
		// reuse cmd_file as buffer
		cmd_len = read(cmd_fd, cmd_file, sizeof(cmd_file) - 1);
		if (cmd_len > 0) {
			cmd_file[cmd_len] = '\0';
			printf("%s", cmd_file);
		}
		close(cmd_fd);
	}
	printf("\n");
	fflush(stdout);
}

int main(int argc, char *argv[])
{
	int fd, rc;
	uint32_t mask;
	struct fanotify_event_metadata buf[256], *metadata;
	int len;

	if (argc != 2) {
		usage(argv[0]);
		exit(EXIT_FAILURE);
	}

	fd = fanotify_init(FAN_CLASS_CONTENT, O_RDONLY | O_LARGEFILE);
	if (fd < 0) {
		fprintf(stderr, "failed to init fanotify. %d:%s\n", errno,
			strerror(errno));
		exit(EXIT_FAILURE);
	}

	mask = FAN_OPEN | FAN_ACCESS | FAN_MODIFY | FAN_CLOSE;
	rc = fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT, mask, AT_FDCWD,
			   argv[1]);
	if (rc < 0) {
		fprintf(stderr,
			"failed to open watch descriptor on %s. %d:%s\n",
			argv[1], errno, strerror(errno));
		exit(EXIT_FAILURE);
	}

	printf("Started...\n");
	fflush(stdout);

	while (1) {
		len = read(fd, (void *)&buf[0], sizeof(buf));
		if (len < 0 && errno != EAGAIN) {
			fprintf(stderr,
				"failed to read from fanotify file descriptor. %d:%s\n",
				errno, strerror(errno));
			exit(EXIT_FAILURE);
		}
		if (len < 0)
			break;

		metadata = &buf[0];
		while (FAN_EVENT_OK(metadata, len)) {
			/* Check run-time and compile-time structures match */
			if (metadata->vers != FANOTIFY_METADATA_VERSION) {
				fprintf(stderr, "Mismatch of fanotify metadata version.\n");
				exit(EXIT_FAILURE);
			}

			if (metadata->fd >= 0) {
				print_event(metadata);
				close(metadata->fd);
			}
			metadata = FAN_EVENT_NEXT(metadata, len);
		}
	}

	close(fd);
	return EXIT_SUCCESS;
}