1/*
2 * System V init functionality
3 *
4 * Copyright (C) 2013, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * $Id: init.c 241182 2011-02-17 21:50:03Z $
19 */
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <errno.h>
24#include <paths.h>
25#include <signal.h>
26#include <stdarg.h>
27#include <string.h>
28#include <termios.h>
29#include <unistd.h>
30#include <limits.h>
31#include <sys/fcntl.h>
32#include <sys/ioctl.h>
33#include <sys/mount.h>
34#include <sys/reboot.h>
35#include <sys/types.h>
36#include <sys/wait.h>
37#include <sys/time.h>
38#include <sys/poll.h>
39
40#include <shutils.h>
41
42#include <rc.h>
43#include <bcmparams.h>
44#include <wlutils.h>
45#include <pmon.h>
46
47#define loop_forever() do { sleep(1); } while (1)
48#define SHELL "/bin/sh"
49
50/* Set terminal settings to reasonable defaults */
51static void set_term(int fd)
52{
53	struct termios tty;
54
55	tcgetattr(fd, &tty);
56
57	/* set control chars */
58	tty.c_cc[VINTR]  = 3;	/* C-c */
59	tty.c_cc[VQUIT]  = 28;	/* C-\ */
60	tty.c_cc[VERASE] = 127; /* C-? */
61	tty.c_cc[VKILL]  = 21;	/* C-u */
62	tty.c_cc[VEOF]   = 4;	/* C-d */
63	tty.c_cc[VSTART] = 17;	/* C-q */
64	tty.c_cc[VSTOP]  = 19;	/* C-s */
65	tty.c_cc[VSUSP]  = 26;	/* C-z */
66
67	/* use line dicipline 0 */
68	tty.c_line = 0;
69
70	/* Make it be sane */
71	tty.c_cflag &= CBAUD|CBAUDEX|CSIZE|CSTOPB|PARENB|PARODD;
72	tty.c_cflag |= CREAD|HUPCL|CLOCAL;
73
74
75	/* input modes */
76	tty.c_iflag = ICRNL | IXON | IXOFF;
77
78	/* output modes */
79	tty.c_oflag = OPOST | ONLCR;
80
81	/* local modes */
82	tty.c_lflag =
83		ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
84
85	tcsetattr(fd, TCSANOW, &tty);
86}
87
88int
89console_init()
90{
91	int fd;
92
93	/* Clean up */
94	ioctl(0, TIOCNOTTY, 0);
95	close(0);
96	close(1);
97	close(2);
98	setsid();
99
100	/* Reopen console */
101	if ((fd = open(_PATH_CONSOLE, O_RDWR)) < 0) {
102		perror(_PATH_CONSOLE);
103		return errno;
104	}
105	dup2(fd, 0);
106	dup2(fd, 1);
107	dup2(fd, 2);
108
109	ioctl(0, TIOCSCTTY, 1);
110	tcsetpgrp(0, getpgrp());
111	set_term(0);
112
113	return 0;
114}
115
116#if defined(BB_SH)
117#undef CONFIG_FEATURE_SH_IS_NONE
118#endif
119
120#if !defined(__CONFIG_BUSYBOX__) || !defined(CONFIG_FEATURE_SH_IS_NONE)
121pid_t
122run_shell(int timeout, int nowait)
123{
124	pid_t pid;
125	char tz[1000];
126	char *envp[] = {
127		"TERM=vt100",
128		"HOME=/",
129		"PATH=/usr/bin:/bin:/usr/sbin:/sbin",
130		"SHELL=" SHELL,
131		"USER=root",
132		tz,
133		NULL
134	};
135	int sig;
136
137	/* Wait for user input */
138	cprintf("Hit enter to continue...");
139	if (waitfor(STDIN_FILENO, timeout) <= 0)
140		return 0;
141
142	switch ((pid = fork())) {
143	case -1:
144		perror("fork");
145		return 0;
146	case 0:
147		/* Reset signal handlers set for parent process */
148		for (sig = 0; sig < (_NSIG-1); sig++)
149			signal(sig, SIG_DFL);
150
151		/* Reopen console */
152		console_init();
153
154		/* Pass on TZ */
155		snprintf(tz, sizeof(tz), "TZ=%s", getenv("TZ"));
156
157		/* Now run it.  The new program will take over this PID,
158		 * so nothing further in init.c should be run.
159		 */
160		execve(SHELL, (char *[]) { "/bin/sh", NULL }, envp);
161
162		/* We're still here?  Some error happened. */
163		perror(SHELL);
164		exit(errno);
165	default:
166		if (nowait)
167			return pid;
168		else {
169			waitpid(pid, NULL, 0);
170			return 0;
171		}
172	}
173}
174#else /* Busybox configured w/o a shell */
175pid_t
176run_shell(int timeout, int nowait)
177{
178	/* Wait for usr input to say we're ignoring it */
179	if (waitfor(STDIN_FILENO, timeout) > 0) {
180		char c;
181		struct pollfd pfd = { STDIN_FILENO, POLLIN, 0 };
182
183		/* Read while available (flush input) */
184		while ((poll(&pfd, 1, 0) > 0) && (pfd.revents & POLLIN))
185			read(STDIN_FILENO, &c, 1);
186
187		cprintf("Busybox configured w/o a shell\n");
188	}
189	return 0;
190}
191#endif /* !__CONFIG_BUSYBOX__ || !CONFIG_FEATURE_SH_IS_NONE */
192
193static void
194shutdown_system(void)
195{
196	int sig;
197
198#ifdef __CONFIG_USBAP__
199	{
200		int ret = 0, i = 0;
201		char	ifname[16] = {0};
202
203		for (i = 1; i <= DEV_NUMIFS; i++) {
204			sprintf(ifname, "eth%d", i);
205			if (wl_probe(ifname))
206				break;
207		}
208
209		sprintf(ifname, "eth%d", i - 1);
210		if ((ret = wl_ioctl(ifname, WLC_UP, NULL, 0)))
211			fprintf(stderr, "%s:%d:(%s): cannot bringup, err = %d\n",
212				__FUNCTION__, __LINE__, ifname, ret);
213
214		if ((ret = wl_iovar_setint(ifname, "dngl_wd", 1)))
215			fprintf(stderr, "%s:%d:(%s): setting iovar \"%s\" to 0x%x failed,"
216				" err = %d\n", __FUNCTION__, __LINE__, ifname, "dngl_wd",
217				(unsigned int)1, ret);
218		sleep(1);
219	}
220#endif /* __CONFIG_USBAP__ */
221
222	/* Disable signal handlers */
223	for (sig = 0; sig < (_NSIG-1); sig++)
224		signal(sig, SIG_DFL);
225
226	cprintf("Sending SIGTERM to all processes\n");
227	kill(-1, SIGTERM);
228	sleep(1);
229
230	cprintf("Sending SIGKILL to all processes\n");
231	kill(-1, SIGKILL);
232	sleep(1);
233
234	sync();
235}
236
237static int fatal_signals[] = {
238	SIGQUIT,
239	SIGILL,
240	SIGABRT,
241	SIGFPE,
242	SIGPIPE,
243	SIGBUS,
244	SIGSEGV,
245	SIGSYS,
246	SIGTRAP,
247	SIGPWR,
248	SIGTERM,	/* reboot */
249	SIGUSR1,	/* halt */
250};
251
252void
253fatal_signal(int sig)
254{
255	char *message = NULL;
256
257	switch (sig) {
258	case SIGQUIT: message = "Quit"; break;
259	case SIGILL: message = "Illegal instruction"; break;
260	case SIGABRT: message = "Abort"; break;
261	case SIGFPE: message = "Floating exception"; break;
262	case SIGPIPE: message = "Broken pipe"; break;
263	case SIGBUS: message = "Bus error"; break;
264	case SIGSEGV: message  = "Segmentation fault"; break;
265	case SIGSYS: message = "Bad system call"; break;
266	case SIGTRAP: message = "Trace trap"; break;
267	case SIGPWR: message = "Power failure"; break;
268	case SIGTERM: message = "Terminated"; break;
269	case SIGUSR1: message = "User-defined signal 1"; break;
270	}
271
272	if (message)
273		cprintf("%s\n", message);
274	else
275		cprintf("Caught signal %d\n", sig);
276
277	shutdown_system();
278	sleep(2);
279
280    /* wklin modified start, 01/17/2007 */
281#if 1
282    /* NEVER HALT */
283    reboot(RB_AUTOBOOT);
284#else
285	/* Halt on SIGUSR1 */
286	reboot(sig == SIGUSR1 ? RB_HALT_SYSTEM : RB_AUTOBOOT);
287#endif
288    /* wklin modified end, 01/17/2007 */
289	loop_forever();
290}
291
292static void
293reap(int sig)
294{
295	pid_t pid;
296	pmon_func_t func;
297
298	while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) {
299		dprintf("Reaped %d\n", pid);
300		if ((func = pmon_get_func(pid)) != NULL) {
301			pmon_unregister(pid);
302			(*func)();
303		}
304	}
305}
306
307
308void
309signal_init(void)
310{
311	int i;
312
313	for (i = 0; i < sizeof(fatal_signals)/sizeof(fatal_signals[0]); i++)
314		signal(fatal_signals[i], fatal_signal);
315
316	signal(SIGCHLD, reap);
317}
318