Skip to content

Commit

Permalink
Add -u option to show users
Browse files Browse the repository at this point in the history
Co-Authored-By: Martin Pitt <[email protected]>
  • Loading branch information
gabrielkulp and martinpitt committed May 30, 2023
1 parent 43327b1 commit 052e524
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 10 deletions.
4 changes: 4 additions & 0 deletions fatrace.8
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ Add timestamp to events. When this option is given once, the format will be a
human readable hour:minute:second.microsecond; when given twice, the timestamp
is printed as seconds/microseconds since the epoch.

.TP
.B \-u\fR, \fB\-\-user
Add process user information to events, formatted as "[uid:gid]".

.TP
.B \-p \fIPID\fR, \fB\-\-ignore\-pid=\fIPID
Ignore events for this process ID. Can be specified multiple times.
Expand Down
42 changes: 33 additions & 9 deletions fatrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ static long option_filter_mask = 0xffffffff;
static long option_timeout = -1;
static int option_current_mount = 0;
static int option_timestamp = 0;
static int option_user = 0;
static pid_t ignored_pids[1024];
static unsigned int ignored_pids_len = 0;
static char* option_comm = NULL;
Expand Down Expand Up @@ -226,26 +227,26 @@ static void
print_event (const struct fanotify_event_metadata *data,
const struct timeval *event_time)
{
int proc_fd;
int event_fd = data->fd;
static char printbuf[100];
static char procname[100];
static int procname_pid = -1;
static char pathname[PATH_MAX];
bool got_procname = false;
struct stat st;
struct stat proc_fd_stat = { .st_uid = -1 };

if ((data->mask & option_filter_mask) == 0 || !show_pid (data->pid)) {
if (event_fd >= 0)
close (event_fd);
return;
}

/* read process name */
snprintf (printbuf, sizeof (printbuf), "/proc/%i/comm", data->pid);
proc_fd = open (printbuf, O_RDONLY);
snprintf (printbuf, sizeof (printbuf), "/proc/%i", data->pid);
int proc_fd = open (printbuf, O_RDONLY | O_DIRECTORY);
if (proc_fd >= 0) {
ssize_t len = read (proc_fd, procname, sizeof (procname));
/* read process name */
int procname_fd = openat (proc_fd, "comm", O_RDONLY);
ssize_t len = read (procname_fd, procname, sizeof (procname));
if (len >= 0) {
while (len > 0 && procname[len-1] == '\n')
len--;
Expand All @@ -256,9 +257,18 @@ print_event (const struct fanotify_event_metadata *data,
debug ("failed to read /proc/%i/comm", data->pid);
}

close (procname_fd);

/* get user and group */
if (option_user) {
if (fstat (proc_fd, &proc_fd_stat) < 0)
debug ("failed to stat /proc/%i: %m", data->pid);
}

close (proc_fd);
} else {
debug ("failed to open /proc/%i/comm: %m", data->pid);
debug ("failed to open /proc/%i: %m", data->pid);
procname[0] = '\0';
}

/* /proc/pid/comm often goes away before processing the event; reuse previously cached value if pid still matches */
Expand Down Expand Up @@ -289,6 +299,7 @@ print_event (const struct fanotify_event_metadata *data,
ssize_t len = readlink (printbuf, pathname, sizeof (pathname));
if (len < 0) {
/* fall back to the device/inode */
struct stat st;
if (fstat (event_fd, &st) < 0)
err (EXIT_FAILURE, "stat");
snprintf (pathname, sizeof (pathname), "device %i:%i inode %ld\n", major (st.st_dev), minor (st.st_dev), st.st_ino);
Expand All @@ -308,7 +319,14 @@ print_event (const struct fanotify_event_metadata *data,
} else if (option_timestamp == 2) {
printf ("%li.%06li ", event_time->tv_sec, event_time->tv_usec);
}
printf ("%s(%i): %-3s %s\n", procname[0] == '\0' ? "unknown" : procname, data->pid, mask2str (data->mask), pathname);

/* print user and group */
if (option_user && proc_fd_stat.st_uid != (uid_t)-1)
snprintf(printbuf, sizeof printbuf, " [%u:%u]", proc_fd_stat.st_uid, proc_fd_stat.st_gid);
else
printbuf[0] = '\0';

printf ("%s(%i)%s: %-3s %s\n", procname[0] == '\0' ? "unknown" : procname, data->pid, printbuf, mask2str (data->mask), pathname);
}

static void
Expand Down Expand Up @@ -412,6 +430,7 @@ help (void)
" -o FILE, --output=FILE\tWrite events to a file instead of standard output.\n"
" -s SECONDS, --seconds=SECONDS\tStop after the given number of seconds.\n"
" -t, --timestamp\t\tAdd timestamp to events. Give twice for seconds since the epoch.\n"
" -u, --user\t\t\tAdd user ID and group ID to events.\n"
" -p PID, --ignore-pid PID\tIgnore events for this process ID. Can be specified multiple times.\n"
" -f TYPES, --filter=TYPES\tShow only the given event types; choose from C, R, O, or W, e. g. --filter=OC.\n"
" -C COMM, --command=COMM\tShow only events for this command.\n"
Expand All @@ -436,6 +455,7 @@ parse_args (int argc, char** argv)
{"output", required_argument, 0, 'o'},
{"seconds", required_argument, 0, 's'},
{"timestamp", no_argument, 0, 't'},
{"user", no_argument, 0, 'u'},
{"ignore-pid", required_argument, 0, 'p'},
{"filter", required_argument, 0, 'f'},
{"command", required_argument, 0, 'C'},
Expand All @@ -444,7 +464,7 @@ parse_args (int argc, char** argv)
};

while (1) {
c = getopt_long (argc, argv, "C:co:s:tp:f:h", long_options, NULL);
c = getopt_long (argc, argv, "C:co:s:tup:f:h", long_options, NULL);

if (c == -1)
break;
Expand All @@ -462,6 +482,10 @@ parse_args (int argc, char** argv)
option_output = strdup (optarg);
break;

case 'u':
option_user = 1;
break;

case 'f':
j = 0;
option_filter_mask = 0;
Expand Down
64 changes: 64 additions & 0 deletions tests/fatrace-user
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/bin/sh
set -euC

USER=nobody
ROOT_LOG="\\[0:0\\]"
USER_LOG="\\[$(id -u $USER):$(id -g $USER)\\]"

mkdir -m 777 tmp
trap "rm -rf tmp" EXIT INT QUIT PIPE

LOG="$AUTOPKGTEST_TMP/fatrace.log"
echo "starting fatrace..."
fatrace --current-mount --user -s 2 -o $LOG &
sleep 1

echo "read a file as root ..."
head NEWS > /dev/null

echo "read a file as user ..."
runuser -u $USER tail NEWS > /dev/null

echo "create/remove a file as root..."
TEST_FILE_ROOT=testroot.txt
touch "$TEST_FILE_ROOT"
rm "$TEST_FILE_ROOT"

echo "create/remove a file as usr..."
TEST_FILE_USER=tmp/test$USER.txt
runuser -u $USER touch "$TEST_FILE_USER"
runuser -u $USER rm "$TEST_FILE_USER"

echo "waiting for fatrace..."
wait

echo "checking log..."
RC=0
check_log() {
if ! grep -q "$1" $LOG; then
echo "$1 not found in log" >&2
((RC=RC+1))
fi
}

# accessing the NEWS file as root
check_log "^head([0-9]*) $ROOT_LOG: RC\?O\? \+$(pwd)/NEWS$"

# accessing the NEWS file as user
check_log "^tail([0-9]*) $USER_LOG: RC\?O\? \+$(pwd)/NEWS$"

# file creation as root
TEST_FILE_ROOT=$(realpath "$TEST_FILE_ROOT")
check_log "^touch([0-9]*) $ROOT_LOG: C\?W\?O \+$TEST_FILE_ROOT"

TEST_FILE_USER=$(realpath "$TEST_FILE_USER")
check_log "^touch([0-9]*) $USER_LOG: C\?W\?O \+$TEST_FILE_USER"

if [ $RC -ne 0 ]; then
echo "$RC checks failed -- log:" >&2
echo "===================" >&2
cat $LOG >&2
echo "===================" >&2
fi

exit $RC
2 changes: 1 addition & 1 deletion tests/run
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set -eu
MYDIR=$(dirname $(readlink -f "$0"))
export PATH=$(pwd):$PATH

for t in fatrace fatrace-currentmount fatrace-btrfs; do
for t in fatrace fatrace-currentmount fatrace-btrfs fatrace-user; do
export AUTOPKGTEST_TMP=$(mktemp -d)
echo "===== $t ===="
"$MYDIR"/$t
Expand Down

0 comments on commit 052e524

Please sign in to comment.