diff options
| author | William Hubbs <w.d.hubbs@gmail.com> | 2022-04-06 10:51:55 -0500 | 
|---|---|---|
| committer | William Hubbs <w.d.hubbs@gmail.com> | 2022-04-06 10:51:55 -0500 | 
| commit | 391d12db48754861b5cecac92ee3321597ee02c1 (patch) | |
| tree | b42fad5a31ca342de7b7ecf1fb78784194c1400c /src/openrc-shutdown | |
| parent | 0efc1b133e4182bd53cde78153bd8b5cc2e99448 (diff) | |
| download | openrc-391d12db48754861b5cecac92ee3321597ee02c1.tar.xz | |
migrate fully to meson build system
- drop old build system
- move shared include and source files to common directory
- drop "rc-" prefix from shared include and source files
- move executable-specific code to individual directories under src
- adjust top-level .gitignore file for new build system
This closes #489.
Diffstat (limited to 'src/openrc-shutdown')
| -rw-r--r-- | src/openrc-shutdown/broadcast.c | 211 | ||||
| -rw-r--r-- | src/openrc-shutdown/broadcast.h | 16 | ||||
| -rw-r--r-- | src/openrc-shutdown/meson.build | 10 | ||||
| -rw-r--r-- | src/openrc-shutdown/openrc-shutdown.c | 358 | ||||
| -rw-r--r-- | src/openrc-shutdown/rc-sysvinit.c | 102 | ||||
| -rw-r--r-- | src/openrc-shutdown/rc-sysvinit.h | 72 | 
6 files changed, 769 insertions, 0 deletions
| diff --git a/src/openrc-shutdown/broadcast.c b/src/openrc-shutdown/broadcast.c new file mode 100644 index 00000000..402a9fb9 --- /dev/null +++ b/src/openrc-shutdown/broadcast.c @@ -0,0 +1,211 @@ +/* + * broadcast.c + * broadcast a message to every logged in user + */ + +/* + * Copyright 2018 Sony Interactive Entertainment Inc. + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + *    except according to the terms contained in the LICENSE file. + */ +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <stdio.h> +#include <utmp.h> +#include <utmpx.h> +#include <pwd.h> +#include <fcntl.h> +#include <signal.h> +#include <setjmp.h> +#include <paths.h> +#include <sys/utsname.h> + +#include "broadcast.h" +#include "helpers.h" + +#ifndef _PATH_DEV +# define _PATH_DEV	"/dev/" +#endif + +static sigjmp_buf jbuf; + +/* + *	Alarm handler + */ +/*ARGSUSED*/ +# ifdef __GNUC__ +static void handler(int arg __attribute__((unused))) +# else +static void handler(int arg) +# endif +{ +	siglongjmp(jbuf, 1); +} + +static void getuidtty(char **userp, char **ttyp) +{ +	struct passwd 		*pwd; +	uid_t			uid; +	char			*tty; +	static char		uidbuf[32]; +	char		*ttynm = NULL; + +	uid = getuid(); +	if ((pwd = getpwuid(uid)) != NULL) { +		uidbuf[0] = 0; +		strncat(uidbuf, pwd->pw_name, sizeof(uidbuf) - 1); +	} else { +		if (uid) +			sprintf(uidbuf, "uid %d", (int) uid); +		else +			sprintf(uidbuf, "root"); +	} + +	if ((tty = ttyname(0)) != NULL) { +		const size_t plen = strlen(_PATH_DEV); +		if (strncmp(tty, _PATH_DEV, plen) == 0) { +			tty += plen; +			if (tty[0] == '/') +				tty++; +		} +		xasprintf(&ttynm, "(%s) ", tty); +	} + +	*userp = uidbuf; +	*ttyp  = ttynm; +} + +/* + *	Check whether the given filename looks like a tty device. + */ +static int file_isatty(const char *fname) +{ +	struct stat		st; +	int			major; + +	if (stat(fname, &st) < 0) +		return 0; + +	if (st.st_nlink != 1 || !S_ISCHR(st.st_mode)) +		return 0; + +	/* +	 *	It would be an impossible task to list all major/minors +	 *	of tty devices here, so we just exclude the obvious +	 *	majors of which just opening has side-effects: +	 *	printers and tapes. +	 */ +	major = major(st.st_dev); +	if (major == 1 || major == 2 || major == 6 || major == 9 || +	    major == 12 || major == 16 || major == 21 || major == 27 || +	    major == 37 || major == 96 || major == 97 || major == 206 || +	    major == 230) +		return 0; +	return 1; +} + +/* + *	broadcast function. + * + *	NB: Not multithread safe. + */ +void broadcast(char *text) +{ +	char *tty; +	char *user; +	struct utsname name; +	time_t t; +	char	*date; +	char *p; +	char *line = NULL; +	struct sigaction sa; +	int	flags; +	char *term = NULL; +	struct utmpx *utmp; +	/* +	 * These are set across the sigsetjmp call, so they can't be stored on +	 * the stack, otherwise they might be clobbered. +	 */ +	static int fd; +	static FILE *tp; + +	getuidtty(&user, &tty); + +	/* +	 * Get and report current hostname, to make it easier to find out +	 * which machine is being shut down. +	 */ +	uname(&name); + +	/* Get the time */ +	time(&t); +	date = ctime(&t); +	p = strchr(date, '\n'); +	if (p) +		*p = 0; + +	xasprintf(&line, "\007\r\nBroadcast message from %s@%s %s(%s):\r\n\r\n", +			user, name.nodename, tty, date); +	free(tty); + +	/* +	 *	Fork to avoid hanging in a write() +	 */ +	if (fork() != 0) +		return; + +	memset(&sa, 0, sizeof(sa)); +	sa.sa_handler = handler; +	sigemptyset(&sa.sa_mask); +	sigaction(SIGALRM, &sa, NULL); + +	setutxent(); + +	while ((utmp = getutxent()) != NULL) { +		if (utmp->ut_type != USER_PROCESS || utmp->ut_user[0] == 0) +			continue; +		if (strncmp(utmp->ut_line, _PATH_DEV, strlen(_PATH_DEV)) == 0) +			xasprintf(&term, "%s", utmp->ut_line); +		else +			xasprintf(&term, "%s%s", _PATH_DEV, utmp->ut_line); +		if (strstr(term, "/../")) { +			free(term); +			continue; +		} + +		/* +		 *	Open it non-delay +		 */ +		if (sigsetjmp(jbuf, 1) == 0) { +			alarm(2); +			flags = O_WRONLY|O_NDELAY|O_NOCTTY; +			if (file_isatty(term) && (fd = open(term, flags)) >= 0) { +				if (isatty(fd) && (tp = fdopen(fd, "w")) != NULL) { +					fputs(line, tp); +					fputs(text, tp); +					fflush(tp); +				} +			} +		} +		alarm(0); +		if (fd >= 0) +			close(fd); +		if (tp != NULL) +			fclose(tp); +		free(term); +	} +	endutxent(); +	free(line); +	exit(0); +} diff --git a/src/openrc-shutdown/broadcast.h b/src/openrc-shutdown/broadcast.h new file mode 100644 index 00000000..2255fe67 --- /dev/null +++ b/src/openrc-shutdown/broadcast.h @@ -0,0 +1,16 @@ +/* + * Copyright 2018 Sony Interactive Entertainment Inc. + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + *    except according to the terms contained in the LICENSE file. + */ + +#ifndef BROADCAST_H +#define BROADCAST_H + +void broadcast(char *text); + +#endif diff --git a/src/openrc-shutdown/meson.build b/src/openrc-shutdown/meson.build new file mode 100644 index 00000000..bdd58338 --- /dev/null +++ b/src/openrc-shutdown/meson.build @@ -0,0 +1,10 @@ +if os == 'Linux' +  executable('openrc-shutdown', +    ['openrc-shutdown.c', 'broadcast.c', 'rc-sysvinit.c', misc_c, +		usage_c, wtmp_c, version_h], +    c_args : cc_branding_flags, +    include_directories: [incdir, einfo_incdir, rc_incdir], +    link_with: [libeinfo, librc], +    install: true, +    install_dir: sbindir) +endif diff --git a/src/openrc-shutdown/openrc-shutdown.c b/src/openrc-shutdown/openrc-shutdown.c new file mode 100644 index 00000000..1234dcfc --- /dev/null +++ b/src/openrc-shutdown/openrc-shutdown.c @@ -0,0 +1,358 @@ +/* + * openrc-shutdown.c + * If you are using OpenRC's provided init, this will shut down or + * reboot your system. + * + * This is based on code written by James Hammons <jlhamm@acm.org>, so + * I would like to publically thank him for his work. + */ + +/* + * Copyright 2017 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + *    except according to the terms contained in the LICENSE file. + */ + +#include <getopt.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/utsname.h> + +#include "broadcast.h" +#include "einfo.h" +#include "rc.h" +#include "helpers.h" +#include "misc.h" +#include "rc-sysvinit.h" +#include "wtmp.h" +#include "_usage.h" + +const char *applet = NULL; +const char *extraopts = NULL; +const char getoptstring[] = "cdDfFHKpRrsw" getoptstring_COMMON; +const struct option longopts[] = { +	{ "cancel",        no_argument, NULL, 'c'}, +	{ "no-write",        no_argument, NULL, 'd'}, +	{ "dry-run",        no_argument, NULL, 'D'}, +	{ "halt",        no_argument, NULL, 'H'}, +	{ "kexec",        no_argument, NULL, 'K'}, +	{ "poweroff",        no_argument, NULL, 'p'}, +	{ "reexec",        no_argument, NULL, 'R'}, +	{ "reboot",        no_argument, NULL, 'r'}, +	{ "single",        no_argument, NULL, 's'}, +	{ "write-only",        no_argument, NULL, 'w'}, +	longopts_COMMON +}; +const char * const longopts_help[] = { +	"cancel a pending shutdown", +	"do not write wtmp record", +	"print actions instead of executing them", +	"halt the system", +	"reboot the system using kexec", +	"power off the system", +	"re-execute init (use after upgrading)", +	"reboot the system", +	"single user mode", +	"write wtmp boot record and exit", +	longopts_help_COMMON +}; +const char *usagestring = "" \ +						   "Usage: openrc-shutdown -c | --cancel\n" \ +						   "   or: openrc-shutdown -R | --reexec\n" \ +						   "   or: openrc-shutdown -w | --write-only\n" \ +						   "   or: openrc-shutdown -H | --halt time\n" \ +						   "   or: openrc-shutdown -K | --kexec time\n" \ +						   "   or: openrc-shutdown -p | --poweroff time\n" \ +						   "   or: openrc-shutdown -r | --reboot time\n" \ +						   "   or: openrc-shutdown -s | --single time"; +const char *exclusive = "Select one of " +	"--cancel, --halt, --kexec, --poweroff, --reexec, --reboot, --single or \n" +	"--write-only"; +const char *nologin_file = RC_SYSCONFDIR"/nologin"; +const char *shutdown_pid = "/run/openrc-shutdown.pid"; + +static bool do_cancel = false; +static bool do_dryrun = false; +static bool do_halt = false; +static bool do_kexec = false; +static bool do_poweroff = false; +static bool do_reboot = false; +static bool do_reexec = false; +static bool do_single = false; +static bool do_wtmp = true; +static bool do_wtmp_only = false; + +static void cancel_shutdown(void) +{ +	pid_t pid; + +	pid = get_pid(applet, shutdown_pid); +	if (pid <= 0) +		eerrorx("%s: Unable to cancel shutdown", applet); + +	if (kill(pid, SIGTERM) != -1) +		einfo("%s: shutdown canceled", applet); +	else +		eerrorx("%s: Unable to cancel shutdown", applet); +} + +/* + *	Create the nologin file. + */ +static void create_nologin(int mins) +{ +	FILE *fp; +	time_t t; + +	time(&t); +	t += 60 * mins; + +	if ((fp = fopen(nologin_file, "w")) != NULL) { +		fprintf(fp, "\rThe system is going down on %s\r\n", ctime(&t)); +		fclose(fp); +	} +} + +/* + * Send a command to our init + */ +static void send_cmd(const char *cmd) +{ +	FILE *fifo; +	size_t ignored; + +	if (do_dryrun) { +		einfo("Would send %s to init", cmd); +		return; +	} +	if (do_wtmp && (do_halt || do_kexec || do_reboot || do_poweroff)) +		log_wtmp("shutdown", "~~", 0, RUN_LVL, "~~"); +	fifo = fopen(RC_INIT_FIFO, "w"); +	if (!fifo) { +		perror("fopen"); +		return; +	} + +	ignored = fwrite(cmd, 1, strlen(cmd), fifo); +	if (ignored != strlen(cmd)) +		printf("Error writing to init fifo\n"); +	fclose(fifo); +} + +/* + * sleep without being interrupted. + * The idea for this code came from sysvinit. + */ +static void sleep_no_interrupt(int seconds) +{ +	struct timespec duration; +	struct timespec remaining; + +	duration.tv_sec = seconds; +	duration.tv_nsec = 0; + +	while (nanosleep(&duration, &remaining) < 0 && errno == EINTR) +		duration = remaining; +} + +static void stop_shutdown(int sig) +{ +	(void) sig; +	unlink(nologin_file); +	unlink(shutdown_pid); +einfo("Shutdown canceled"); +exit(0); +} + +int main(int argc, char **argv) +{ +	char *ch = NULL; +	int opt; +	int cmd_count = 0; +	int hour = 0; +	int min = 0; +	int shutdown_delay = 0; +	struct sigaction sa; +	struct tm *lt; +	time_t tv; +	bool need_warning = false; +	char *msg = NULL; +	char *state = NULL; +	char *time_arg = NULL; +	FILE *fp; + +	applet = basename_c(argv[0]); +	while ((opt = getopt_long(argc, argv, getoptstring, +		    longopts, (int *) 0)) != -1) +	{ +		switch (opt) { +			case 'c': +				do_cancel = true; +			cmd_count++; +				break; +			case 'd': +				do_wtmp = false; +				break; +		case 'D': +			do_dryrun = true; +			break; +		case 'H': +			do_halt = true; +			xasprintf(&state, "%s", "halt"); +			cmd_count++; +			break; +		case 'K': +			do_kexec = true; +			xasprintf(&state, "%s", "reboot"); +			cmd_count++; +			break; +		case 'p': +			do_poweroff = true; +			xasprintf(&state, "%s", "power off"); +			cmd_count++; +			break; +		case 'R': +			do_reexec = true; +			cmd_count++; +			break; +		case 'r': +			do_reboot = true; +			xasprintf(&state, "%s", "reboot"); +			cmd_count++; +			break; +		case 's': +			do_single = true; +			xasprintf(&state, "%s", "go down for maintenance"); +			cmd_count++; +			break; +		case 'w': +			do_wtmp_only = true; +			cmd_count++; +			break; +		case_RC_COMMON_GETOPT +		} +	} +	if (geteuid() != 0) +		eerrorx("%s: you must be root\n", applet); +	if (cmd_count != 1) { +		eerror("%s: %s\n", applet, exclusive); +		usage(EXIT_FAILURE); +	} + +	if (do_cancel) { +		cancel_shutdown(); +		exit(EXIT_SUCCESS); +	} else if (do_reexec) { +		send_cmd("reexec"); +		exit(EXIT_SUCCESS); +	} else if (do_wtmp_only) { +		log_wtmp("shutdown", "~~", 0, RUN_LVL, "~~"); +		exit(EXIT_SUCCESS); +	} + +	if (optind >= argc) { +		eerror("%s: No shutdown time specified", applet); +		usage(EXIT_FAILURE); +	} +	time_arg = argv[optind]; +	if (*time_arg == '+') +		time_arg++; +	if (strcasecmp(time_arg, "now") == 0) +		strcpy(time_arg, "0"); +	for (ch=time_arg; *ch; ch++) +		if ((*ch < '0' || *ch > '9') && *ch != ':') { +			eerror("%s: invalid time %s", applet, time_arg); +			usage(EXIT_FAILURE); +		} +	if (strchr(time_arg, ':')) { +		if ((sscanf(time_arg, "%2d:%2d", &hour, &min) != 2) || +				(hour > 23) || (min > 59)) { +			eerror("%s: invalid time %s", applet, time_arg); +			usage(EXIT_FAILURE); +		} +		time(&tv); +		lt = localtime(&tv); +		shutdown_delay = (hour * 60 + min) - (lt->tm_hour * 60 + lt->tm_min); +		if (shutdown_delay < 0) +			shutdown_delay += 1440; +	} else { +		shutdown_delay = atoi(time_arg); +	} + +	fp = fopen(shutdown_pid, "w"); +	if (!fp) +		eerrorx("%s: fopen `%s': %s", applet, shutdown_pid, strerror(errno)); +	fprintf(fp, "%d\n", getpid()); +	fclose(fp); + +	openlog(applet, LOG_PID, LOG_DAEMON); +	memset(&sa, 0, sizeof(sa)); +	sa.sa_handler = stop_shutdown; +	sigemptyset(&sa.sa_mask); +	sigaction(SIGINT, &sa, NULL); +	sigaction(SIGTERM, &sa, NULL); +	while (shutdown_delay > 0) { +		need_warning = false; +		if (shutdown_delay > 180) +			need_warning = (shutdown_delay % 60 == 0); +		else if (shutdown_delay > 60) +			need_warning = (shutdown_delay % 30 == 0); +		else if	(shutdown_delay > 10) +			need_warning = (shutdown_delay % 15 == 0); +		else +			need_warning = true; +		if (shutdown_delay <= 5) +			create_nologin(shutdown_delay); +		if (need_warning) { +		xasprintf(&msg, "\rThe system will %s in %d minutes\r\n", +		          state, shutdown_delay); +			broadcast(msg); +			free(msg); +		} +		sleep_no_interrupt(60); +		shutdown_delay--; +	} +	xasprintf(&msg, "\rThe system will %s now\r\n", state); +	broadcast(msg); +	syslog(LOG_NOTICE, "The system will %s now", state); +	unlink(nologin_file); +	unlink(shutdown_pid); +	if (do_halt) { +		if (exists("/run/initctl")) { +			sysvinit_setenv("INIT_HALT", "HALT"); +			sysvinit_runlevel('0'); +		} else +			send_cmd("halt"); +	} else if (do_kexec) +		send_cmd("kexec"); +	else if (do_poweroff) { +		if (exists("/run/initctl")) { +			sysvinit_setenv("INIT_HALT", "POWEROFF"); +			sysvinit_runlevel('0'); +		} else +			send_cmd("poweroff"); +	} else if (do_reboot) { +		if (exists("/run/initctl")) +			sysvinit_runlevel('6'); +		else +			send_cmd("reboot"); +	} else if (do_single) { +		if (exists("/run/initctl")) +			sysvinit_runlevel('S'); +		else +			send_cmd("single"); +	} +	return 0; +} diff --git a/src/openrc-shutdown/rc-sysvinit.c b/src/openrc-shutdown/rc-sysvinit.c new file mode 100644 index 00000000..8d258b63 --- /dev/null +++ b/src/openrc-shutdown/rc-sysvinit.c @@ -0,0 +1,102 @@ +/* + * rc-sysvinit.c + * Helper to send a runlevel change to sysvinit + */ + +/* + * Copyright (c) 2019 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + *    except according to the terms contained in the LICENSE file. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "einfo.h" +#include "rc-sysvinit.h" + +static void sysvinit_send_cmd(struct init_request *request) +{ +	int fd; +	char *p; +	size_t bytes; +	ssize_t r; + +	fd = open("/run/initctl", O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); +	if (fd < 0) { +		if (errno != ENOENT) +			eerror("Failed to open initctl fifo: %s", strerror(errno)); +		return; +	} +	p = (char *) request; +	bytes = sizeof(*request); +	do { +		r = write(fd, p, bytes); +		if (r < 0) { +			if ((errno == EAGAIN) || (errno == EINTR)) +				continue; +			eerror("Failed to write to /run/initctl: %s", strerror(errno)); +			return; +		} +		p += r; +		bytes -= r; +	} while (bytes > 0); +} + +void sysvinit_runlevel(char rl) +{ +	struct init_request request; + +	if (!rl) +		return; + +	request = (struct init_request) { +		.magic = INIT_MAGIC, +		.sleeptime = 0, +		.cmd = INIT_CMD_RUNLVL, +		.runlevel = rl, +	}; +	sysvinit_send_cmd(&request); +		return; +} + +/* + *	Set environment variables in the init process. + */ +void sysvinit_setenv(const char *name, const char *value) +{ +	struct init_request	request; +	size_t nl; +	size_t vl; + +	memset(&request, 0, sizeof(request)); +	request.magic = INIT_MAGIC; +	request.cmd = INIT_CMD_SETENV; +	nl = strlen(name); +	if (value) +		vl = strlen(value); +else +		vl = 0; + +	if (nl + vl + 3 >= (int)sizeof(request.i.data)) +		return; + +	memcpy(request.i.data, name, nl); +	if (value) { +		request.i.data[nl] = '='; +		memcpy(request.i.data + nl + 1, value, vl); +	} +	sysvinit_send_cmd(&request); +	return; +} diff --git a/src/openrc-shutdown/rc-sysvinit.h b/src/openrc-shutdown/rc-sysvinit.h new file mode 100644 index 00000000..6142cdd6 --- /dev/null +++ b/src/openrc-shutdown/rc-sysvinit.h @@ -0,0 +1,72 @@ +/* + * rc-sysvinit.h	- Interface to communicate with sysvinit via /run/initctl. + */ + +/* + * Copyright (c) 2019 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + *    except according to the terms contained in the LICENSE file. + */ + +#ifndef _RC_SYSVINIT_H +#define _RC_SYSVINIT_H + +/* + * The #defines and structures below are taken from initreq.h in + * sysvinit and must be used by any program wishing to communicate with + * it. + */ + +#define INIT_MAGIC 0x03091969 +#define INIT_CMD_START		0 +#define INIT_CMD_RUNLVL		1 +#define INIT_CMD_POWERFAIL	2 +#define INIT_CMD_POWERFAILNOW	3 +#define INIT_CMD_POWEROK	4 +#define INIT_CMD_BSD		5 +#define INIT_CMD_SETENV		6 +#define INIT_CMD_UNSETENV	7 + +/* + *	This is what BSD 4.4 uses when talking to init. + *	Linux doesn't use this right now. + */ +struct init_request_bsd { +	char	gen_id[8];		/* Beats me.. telnetd uses "fe" */ +	char	tty_id[16];		/* Tty name minus /dev/tty      */ +	char	host[64];	/* Hostname                     */ +	char	term_type[16];		/* Terminal type                */ +	int	signal;			/* Signal to send               */ +	int	pid;			/* Process to send to           */ +	char	exec_name[128];	        /* Program to execute           */ +	char	reserved[128];		/* For future expansion.        */ +}; + +/* + *	Because of legacy interfaces, "runlevel" and "sleeptime" + *	aren't in a seperate struct in the union. + * + *	The weird sizes are because init expects the whole + *	struct to be 384 bytes. + */ +struct init_request { +	int	magic;			/* Magic number                 */ +	int	cmd;			/* What kind of request         */ +	int	runlevel;		/* Runlevel to change to        */ +	int	sleeptime;		/* Time between TERM and KILL   */ +	union { +		struct init_request_bsd	bsd; +		char			data[368]; +	} i; +}; + +void sysvinit_runlevel(char rl); +void sysvinit_setenv(const char *name, const char *value); + +#endif | 
