/*
 * sysv-wrapper — translate traditional sysv-init commands to zygctl calls.
 *
 * Installed as multiple symlinks pointing at the same binary; we dispatch
 * on argv[0] (basename) so each link behaves like the legacy tool.
 *
 *   /sbin/init <runlevel>     map runlevel char to a zygctl verb
 *   /sbin/telinit <runlevel>  same; telinit is the historical client name
 *   /sbin/halt                zygctl halt
 *   /sbin/reboot              zygctl reboot
 *   /sbin/poweroff            zygctl poweroff
 *
 * The wrapper itself does not connect to zyginit's socket — it just exec's
 * /sbin/zygctl with the right verb. zygctl handles the socket protocol.
 *
 * Note: when /sbin/init is run by the kernel as PID 1, it is the zyginit
 * daemon binary, NOT this wrapper. This wrapper lives at a different path
 * (or replaces /sbin/init only on systems where zyginit is at /sbin/zyginit).
 * On the current Hammerhead deploy /sbin/init IS zyginit; the wrapper
 * is therefore meant for /sbin/halt, /sbin/reboot, /sbin/poweroff,
 * /sbin/telinit. If you DO want /sbin/init to be this wrapper, install
 * zyginit at /sbin/zyginit and point the kernel boot path there.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>
#include <errno.h>
#include "version.h"

#define ZYGCTL "/sbin/zygctl"

static int
run_zygctl(const char *verb)
{
	execl(ZYGCTL, "zygctl", verb, (char *)NULL);
	/* execl returns only on failure */
	(void) fprintf(stderr, "%s: cannot exec %s: %s\n",
	    "sysv-wrapper", ZYGCTL, strerror(errno));
	return (1);
}

static int
init_dispatch(int argc, char **argv)
{
	if (argc < 2) {
		(void) fprintf(stderr, "usage: %s <runlevel>\n", argv[0]);
		return (1);
	}

	switch (argv[1][0]) {
	case '0':
		return (run_zygctl("halt"));
	case '5':
		return (run_zygctl("poweroff"));
	case '6':
		return (run_zygctl("reboot"));
	case '1':
	case 's':
	case 'S':
		return (run_zygctl("single"));
	case '2':
	case '3':
	case '4':
		return (run_zygctl("multi"));
	case 'q':
	case 'Q':
		return (run_zygctl("reload"));
	default:
		(void) fprintf(stderr,
		    "%s: unknown runlevel '%s'\n", argv[0], argv[1]);
		return (1);
	}
}

int
main(int argc, char **argv)
{
	const char *me;
	char *argv0_copy;

	/* basename(3) on illumos modifies its argument, so duplicate first */
	argv0_copy = strdup(argv[0]);
	if (argv0_copy == NULL) {
		(void) fprintf(stderr, "out of memory\n");
		return (1);
	}
	me = basename(argv0_copy);

	if (argc >= 2 &&
	    (strcmp(argv[1], "--version") == 0 ||
	     strcmp(argv[1], "-V") == 0)) {
		(void) printf("%s %s\n", me, ZYGINIT_VERSION);
		free(argv0_copy);
		return (0);
	}

	if (strcmp(me, "init") == 0 || strcmp(me, "telinit") == 0)
		return (init_dispatch(argc, argv));
	if (strcmp(me, "halt") == 0)
		return (run_zygctl("halt"));
	if (strcmp(me, "reboot") == 0)
		return (run_zygctl("reboot"));
	if (strcmp(me, "poweroff") == 0)
		return (run_zygctl("poweroff"));

	(void) fprintf(stderr, "sysv-wrapper: unknown invocation '%s'\n", me);
	return (1);
}
