#!/usr/bin/env bpftrace
// Syd: rock-solid application kernel
// data/syd.bt: Defines tracepoints to trace a syd process using bpftrace
//
// Usage:
// 1. Run systemd-run --slice=syd.slice --scope command args..
// 2. Run bpftrace sydsnoop.bt
//
// Copyright (c) 2023 Ali Polatel <alip@chesswob.org>
// SPDX-License-Identifier: GPL-3.0

#include <linux/openat2.h>
#include <linux/seccomp.h>
#include <linux/signal.h>
#include <linux/in.h>
#include <linux/socket.h>

struct sockaddr_in6 {
	uint16_t sin6_family;		// AF_INET6
	uint16_t sin6_port;			// Port number
	uint32_t sin6_flowinfo; // IPv6 flow information
	uint8_t sin6_addr[16];	// IPv6 address
	uint32_t sin6_scope_id; // Scope ID
};

struct sockaddr_un {
		uint16_t sun_family;		// AF_UNIX
		char sun_path[108];			// Pathname
};

tracepoint:sched:sched_process_exit
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope")/
{
	printf("%s[%d]: exit\n", args->comm, args->pid);
}

tracepoint:signal:signal_deliver
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope")/
{
	printf("%s[%d-%d]: signal = %d\n",
			comm, pid, tid,
			args->sig);
}

tracepoint:syscalls:sys_enter_epoll_wait
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm == "syd::p☮ll"/
{
	@fd = args->epfd;
	@events = args->events;
	printf("syd::p☮ll[%d-%d]: epoll_wait fd=%d ...\n",
			pid, tid,
			args->epfd);
}

tracepoint:syscalls:sys_exit_epoll_wait
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm == "syd::p☮ll"/
{
	$events = *( ( (uint32*)@events)+0 );
	printf("syd::p☮ll[%d-%d]: epoll_wait fd=%d, events=0x%x, return=%d\n",
			pid, tid,
			@fd, $events, args->ret);
	delete(@fd);
	delete(@events);
}

tracepoint:syscalls:sys_enter_epoll_pwait
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm == "syd::p☮ll"/
{
	@fd_p = args->epfd;
	@events_p = args->events;
	printf("syd::p☮ll[%d-%d]: epoll_pwait fd=%d ...\n",
			pid, tid,
			args->epfd);
}

tracepoint:syscalls:sys_exit_epoll_pwait
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm == "syd::p☮ll"/
{
	$events = *( ( (uint32*)@events_p)+0 );
	printf("syd::p☮ll[%d-%d]: epoll_pwait fd=%d, events=0x%x, return=%d\n",
			pid, tid,
			@fd_p, $events, args->ret);
	delete(@fd_p);
	delete(@events_p);
}

tracepoint:syscalls:sys_enter_ioctl
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm == "syd::p☮ll"/
{
	@poll_cmd = args->cmd;
	@poll_arg = args->arg;
	@poll_cmd_i = (int64)args->cmd;
	if (@poll_cmd == 0xc0502100) {
		printf("syd::p☮ll[%d-%d]: seccomp_notify_recv ...\n",
				pid, tid);
	} else if (@poll_cmd_i == 0xffffffffc0502100) {
		printf("syd::p☮ll[%d-%d]: seccomp_notify_recv ...\n",
				pid, tid);
	}
}

tracepoint:syscalls:sys_exit_ioctl
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm == "syd::p☮ll"/
{
	if (@poll_cmd == 0xc0502100) {
		$req = (struct seccomp_notif *)@poll_arg;
		printf("syd::p☮ll[%d-%d]: seccomp_notify_recv id=0x%x, pid=%d, nr=%d, arch=0x%x, ip=0x%x, args=[0x%x,0x%x,0x%x,0x%x,0x%x,0x%x] return=%d\n",
				pid, tid,
				$req->id, $req->pid,
				$req->data.nr, $req->data.arch,
				$req->data.instruction_pointer,
				$req->data.args[0],
				$req->data.args[1],
				$req->data.args[2],
				$req->data.args[3],
				$req->data.args[4],
				$req->data.args[5],
				args->ret);
	} else if (@poll_cmd_i == 0xffffffffc0502100) {
		$req = (struct seccomp_notif *)@poll_arg;
		printf("syd::p☮ll[%d-%d]: seccomp_notify_recv id=0x%x, pid=%d, nr=%d, arch=0x%x, ip=0x%x, args=[0x%x,0x%x,0x%x,0x%x,0x%x,0x%x] return=%d\n",
				pid, tid,
				$req->id, $req->pid,
				$req->data.nr, $req->data.arch,
				$req->data.instruction_pointer,
				$req->data.args[0],
				$req->data.args[1],
				$req->data.args[2],
				$req->data.args[3],
				$req->data.args[4],
				$req->data.args[5],
				args->ret);
	}
	delete(@poll_cmd);
	delete(@poll_cmd_i);
	delete(@poll_arg);
}

tracepoint:syscalls:sys_enter_ioctl
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm == "syd::h☮☮k"/
{
	@hook_cmd = args->cmd;
	@hook_cmd_i = (int64)args->cmd;
	@hook_arg = args->arg;
}

tracepoint:syscalls:sys_exit_ioctl
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm == "syd::h☮☮k"/
{
	if (@hook_cmd == 0xc0182101) {
		$resp = (struct seccomp_notif_resp *)@hook_arg;
		printf("syd::h☮☮k[%d-%d]: seccomp_notify_send id=0x%x, val=%d, error=%d, flags=%d, return=%d\n",
				pid, tid,
				$resp->id,
				$resp->val,
				$resp->error,
				$resp->flags,
				args->ret);
	} else if (@hook_cmd_i == 0xffffffffc0182101) {
		$resp = (struct seccomp_notif_resp *)@hook_arg;
		printf("syd::h☮☮k[%d-%d]: seccomp_notify_send id=0x%x, val=%d, error=%d, flags=%d, return=%d\n",
				pid, tid,
				$resp->id,
				$resp->val,
				$resp->error,
				$resp->flags,
				args->ret);
	} else if (@hook_cmd == 0x40182103) {
		$addfd = (struct seccomp_notif_addfd *)@hook_arg;
		printf("syd::h☮☮k[%d-%d]: seccomp_notify_addfd id=0x%x, flags=0x%x, srcfd=%d, newfd=%d, newfd_flags:0x%x return=%d\n",
				pid, tid,
				$addfd->id,
				$addfd->flags,
				$addfd->srcfd,
				$addfd->newfd,
				$addfd->newfd_flags,
				args->ret);
	} else if (@hook_cmd_i == 0xffffffff40182103) {
		$addfd = (struct seccomp_notif_addfd *)@hook_arg;
		printf("syd::h☮☮k[%d-%d]: seccomp_notify_addfd id=0x%x, flags=0x%x, srcfd=%d, newfd=%d, newfd_flags:0x%x return=%d\n",
				pid, tid,
				$addfd->id,
				$addfd->flags,
				$addfd->srcfd,
				$addfd->newfd,
				$addfd->newfd_flags,
				args->ret);
	}
	delete(@hook_cmd);
	delete(@hook_cmd_i);
	delete(@hook_arg);
}

tracepoint:syscalls:sys_enter_pidfd_getfd
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm == "syd::h☮☮k"/
{
	@pidfd_fd = args->fd;
	@pidfd_flags = args->flags;
	printf("syd::h☮☮k[%d-%d]: pidfd_getfd fd=%d, flags=0x%x ...\n",
			pid, tid,
			args->fd,
			args->flags);
}

tracepoint:syscalls:sys_exit_pidfd_getfd
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm == "syd::h☮☮k"/
{
	printf("syd::h☮☮k[%d-%d]: pidfd_getfd fd=%d, flags=0x%x, return=%d\n",
			pid, tid,
			@pidfd_fd,
			@pidfd_flags,
			args->ret);
	delete(@pidfd_fd);
	delete(@pidfd_flags);
}

uprobe:libc:openat2
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope")/
{
	$how = (struct open_how *)arg2;

	printf("%s[%d-%d]: libc_openat2(%d, %s, {flags=0x%x, mode=0x%x, resolve=0x%x})\n",
			comm, pid, tid,
			arg0, str(arg1),
			$how->flags,
			$how->mode,
			$how->resolve);
}

uretprobe:libc:openat2
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope")/
{
	printf("%s[%d-%d]: libc_openat2 = %d\n",
			comm, pid, tid,
			retval);
}

tracepoint:syscalls:sys_enter_newfstatat
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm == "syd::h☮☮k"/
{
	@newfstatat_dfd = args->dfd;
	@newfstatat_filename = str(args->filename);
	@newfstatat_flag = args->flag;
}

tracepoint:syscalls:sys_exit_newfstatat
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm == "syd::h☮☮k"/
{
	// bpftrace.git is required for the string comparison,
	// otherwise it gives an Addrspace mismatch warning.
	// Next version released after bpftrace-0.19.1 will fix this.
	// See: https://github.com/iovisor/bpftrace/issues/2480
	if (@newfstatat_filename != "") {
		printf("%s[%d-%d]: newfstatat(%d, %s, %d) = %d\n",
				comm, pid, tid,
				@newfstatat_dfd,
				@newfstatat_filename,
				@newfstatat_flag,
				args->ret);
	}
	delete(@newfstatat_dfd);
	delete(@newfstatat_filename);
	delete(@newfstatat_flag);
}

tracepoint:syscalls:sys_enter_openat2
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm == "syd::h☮☮k"/
{
	@openat2_dfd = args->dfd;
	@openat2_filename = str(args->filename);
	@openat2_how = args->how;
}

tracepoint:syscalls:sys_exit_openat2
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm == "syd::h☮☮k"/
{
	// See the note in exit_newfstatat about the string compare.
	if (args->ret >= 0) {
		if (@openat2_filename != "") {
			printf("%s[%d-%d]: openat2(%d, %s, {flags=0x%x, mode=0x%x, resolve=0x%x}) = %d\n",
					comm, pid, tid,
					@openat2_dfd,
					@openat2_filename,
					@openat2_how->flags,
					@openat2_how->mode,
					@openat2_how->resolve,
					args->ret);
		}
	}
	delete(@openat2_dfd);
	delete(@openat2_filename);
	delete(@openat2_how);
}

tracepoint:syscalls:sys_enter_close
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm == "syd::h☮☮k"/
{
	@close_fd = args->fd;
	printf("%s[%d-%d] close fd=%d ...\n",
			comm, pid, tid,
			args->fd);
}

tracepoint:syscalls:sys_exit_close
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm == "syd::h☮☮k"/
{
	printf("%s[%d-%d] close fd=%d, return=%d\n",
			comm, pid, tid,
			@close_fd,
			args->ret);
	delete(@close_fd);
}

tracepoint:syscalls:sys_enter_bind
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope")/
{
	$family = args->umyaddr->sa_family;

	if ($family == AF_INET) {
		$v4addr = (struct sockaddr_in *)args->umyaddr;
		$v4port = (($v4addr->sin_port & 0xff00) >> 8) | (($v4addr->sin_port & 0x00ff) << 8);
		printf("%s[%d-%d]: bind %s:%d\n",
				comm, pid, tid,
				ntop(AF_INET, $v4addr->sin_addr.s_addr),
				$v4port);
	} else if ($family == AF_INET6) {
		$v6addr = (struct sockaddr_in6 *)args->umyaddr;
		$v6port = (($v6addr->sin6_port & 0xff00) >> 8) | (($v6addr->sin6_port & 0x00ff) << 8);
		printf("%s[%d-%d]: bind %s:%d\n",
				comm, pid, tid,
				ntop(AF_INET6, $v6addr->sin6_addr),
				$v6port);
	} else if ($family == AF_UNIX) {
		$unaddr = (struct sockaddr_un *)args->umyaddr;
		$unpath = $unaddr + offsetof(struct sockaddr_un, sun_path);
		if (strncmp("", str($unpath, 1), 1) == 0) {
			printf("%s[%d-%d]: connect %s\n",
					comm, pid, tid,
					str($unpath+1, 108));
		} else {
			printf("%s[%d-%d]: connect %s\n",
					comm, pid, tid,
					str($unpath, 108));
		}
	} else {
		printf("%s[%d-%d]: bind AF_%d\n",
				comm, pid, tid,
				$family);
	}
}

tracepoint:syscalls:sys_enter_connect
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope")/
{
	$family = args->uservaddr->sa_family;

	if ($family == AF_INET) {
		$v4addr = (struct sockaddr_in *)args->uservaddr;
		$v4port = (($v4addr->sin_port & 0xff00) >> 8) | (($v4addr->sin_port & 0x00ff) << 8);
		printf("%s[%d-%d]: connect %s:%d\n",
				comm, pid, tid,
				ntop(AF_INET, $v4addr->sin_addr.s_addr),
				$v4port);
	} else if ($family == AF_INET6) {
		$v6addr = (struct sockaddr_in6 *)args->uservaddr;
		$v6port = (($v6addr->sin6_port & 0xff00) >> 8) | (($v6addr->sin6_port & 0x00ff) << 8);
		printf("%s[%d-%d]: connect %s:%d\n",
				comm, pid, tid,
				ntop(AF_INET6, $v6addr->sin6_addr),
				$v6port);
	} else if ($family == AF_UNIX) {
		$unaddr = (struct sockaddr_un *)args->uservaddr;
		$unpath = $unaddr + offsetof(struct sockaddr_un, sun_path);
		if (strncmp("", str($unpath, 1), 1) == 0) {
			printf("%s[%d-%d]: connect %s\n",
					comm, pid, tid,
					str($unpath+1, 108));
		} else {
			printf("%s[%d-%d]: connect %s\n",
					comm, pid, tid,
					str($unpath, 108));
		}
	} else {
		printf("%s[%d-%d]: connect AF_%d\n",
				comm, pid, tid,
				$family);
	}
}

tracepoint:syscalls:sys_enter_socket
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope")/
{
	@sock_family = args->family;
	@sock_type = args->type;
	@sock_prot = args->protocol;

	if (args->family == AF_INET) {
		printf("%s[%d-%d] socket AF_INET, type=%d, protocol=%d ...\n",
				comm, pid, tid,
				args->type,
				args->protocol);
	} else if (args->family == AF_INET6) {
		printf("%s[%d-%d] socket AF_INET6, type=%d, protocol=%d ...\n",
				comm, pid, tid,
				args->type,
				args->protocol);
	} else if (args->family == AF_UNIX) {
		printf("%s[%d-%d] socket AF_UNIX, type=%d, protocol=%d ...\n",
				comm, pid, tid,
				args->type,
				args->protocol);
	} else {
		printf("%s[%d-%d] socket AF_%d, type=%d, protocol=%d ...\n",
				comm, pid, tid,
				args->family,
				args->type,
				args->protocol);
	}
}

tracepoint:syscalls:sys_exit_socket
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope")/
{
	if (@sock_family == AF_INET) {
		printf("%s[%d-%d] socket AF_INET, type=%d, protocol=%d, return=%d\n",
				comm, pid, tid,
				@sock_type,
				@sock_prot,
				args->ret);
	} else if (@sock_family == AF_INET6) {
		printf("%s[%d-%d] socket AF_INET6, type=%d, protocol=%d, return=%d\n",
				comm, pid, tid,
				@sock_type,
				@sock_prot,
				args->ret);
	} else if (@sock_family == AF_UNIX) {
		printf("%s[%d-%d] socket AF_UNIX, type=%d, protocol=%d, return=%d\n",
				comm, pid, tid,
				@sock_type,
				@sock_prot,
				args->ret);
	} else {
		printf("%s[%d-%d] socket AF_%d, type=%d, protocol=%d, return=%d\n",
				comm, pid, tid,
				@sock_family,
				@sock_type,
				@sock_prot,
				args->ret);
	}

	delete(@sock_family);
	delete(@sock_type);
	delete(@sock_prot);
}

tracepoint:syscalls:sys_enter_exec*
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope")/
{
	printf("%s[%d-%d]: exec ",
			comm, pid, tid);
	join(args.argv);
}

uprobe:libc:open
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm != "syd::h☮☮k"/
{
	printf("%s[%d-%d]: libc_open(%s, 0x%x, %d)\n",
			comm, pid, tid,
			str(arg0),
			arg1, arg2);
}

uretprobe:libc:open
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm != "syd::h☮☮k"/
{
	printf("%s[%d-%d]: libc_open = %d\n",
			comm, pid, tid,
			retval);
}

uprobe:libc:openat
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope")/
{
	printf("%s[%d-%d]: libc_openat(%d, %s, 0x%x, %d)\n",
			comm, pid, tid,
			arg0, str(arg1),
			arg2, arg3);
}

uretprobe:libc:openat
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope")/
{
	printf("%s[%d-%d]: libc_openat = %d\n",
			comm, pid, tid,
			retval);
}

uprobe:libc:unlink
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm != "syd::h☮☮k"/
{
	printf("%s[%d-%d]: libc_unlink(%s, 0x%x, %d)\n",
			comm, pid, tid,
			str(arg0),
			arg1, arg2);
}

uretprobe:libc:unlink
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm != "syd::h☮☮k"/
{
	printf("%s[%d-%d]: libc_unlink = %d\n",
			comm, pid, tid,
			retval);
}

uprobe:libc:unlinkat
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm != "syd::h☮☮k"/
{
	printf("%s[%d-%d]: libc_unlinkat(%d, %s, 0x%x)\n",
			comm, pid, tid,
			arg0, str(arg1), arg2);
}

uretprobe:libc:unlinkat
		/cgroup == cgroupid("/sys/fs/cgroup/syd.slice/syd.scope") && comm != "syd::h☮☮k"/
{
	printf("%s[%d-%d]: libc_unlinkat = %d\n",
			comm, pid, tid,
			retval);
}
