// SPDX-License-Identifier: GPL-2.0

#define _GNU_SOURCE
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#ifndef CLONE_PIDFD
#define CLONE_PIDFD 0x00001000
#endif

#ifndef __NR_pidfd_send_signal
#define __NR_pidfd_send_signal -1
#endif

static int do_child(void *args)
{
	printf("%d\n", getpid());
	_exit(EXIT_SUCCESS);
}

static pid_t pidfd_clone(int flags, int *pidfd)
{
	size_t stack_size = 1024;
	char *stack[1024] = { 0 };

#ifdef __ia64__
	return __clone2(do_child, stack, stack_size, flags | SIGCHLD, NULL, pidfd);
#else
	return clone(do_child, stack + stack_size, flags | SIGCHLD, NULL, pidfd);
#endif
}

static inline int sys_pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
					unsigned int flags)
{
	return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
}

static int pidfd_metadata_fd(pid_t pid, int pidfd)
{
	int procfd, ret;
	char path[100];

	snprintf(path, sizeof(path), "/proc/%d", pid);
	procfd = open(path, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
	if (procfd < 0) {
		warn("Failed to open %s\n", path);
		return -1;
	}

	/*
	 * Verify that the pid has not been recycled and our /proc/<pid> handle
	 * is still valid.
	 */
	ret = sys_pidfd_send_signal(pidfd, 0, NULL, 0);
	if (ret < 0) {
		switch (errno) {
		case EPERM:
			/* Process exists, just not allowed to signal it. */
			break;
		default:
			warn("Failed to signal process\n");
			close(procfd);
			procfd = -1;
		}
	}

	return procfd;
}

int main(int argc, char *argv[])
{
	int pidfd = -1, ret = EXIT_FAILURE;
	char buf[4096] = { 0 };
	pid_t pid;
	int procfd, statusfd;
	ssize_t bytes;

	pid = pidfd_clone(CLONE_PIDFD, &pidfd);
	if (pid < 0)
		err(ret, "CLONE_PIDFD");
	if (pidfd == -1) {
		warnx("CLONE_PIDFD is not supported by the kernel");
		goto out;
	}

	procfd = pidfd_metadata_fd(pid, pidfd);
	close(pidfd);
	if (procfd < 0)
		goto out;

	statusfd = openat(procfd, "status", O_RDONLY | O_CLOEXEC);
	close(procfd);
	if (statusfd < 0)
		goto out;

	bytes = read(statusfd, buf, sizeof(buf));
	if (bytes > 0)
		bytes = write(STDOUT_FILENO, buf, bytes);
	close(statusfd);
	ret = EXIT_SUCCESS;

out:
	(void)wait(NULL);

	exit(ret);
}
