init: remove superfluous forks and messing up with argv[0]
cttyhack: add stealing of ctty
This commit is contained in:
parent
191836845e
commit
2afabe8b83
@ -19,6 +19,7 @@ HOSTCFLAGS_usage.o = -I$(srctree)/include
|
||||
applets/applets.o: include/usage_compressed.h include/applet_tables.h
|
||||
|
||||
applets/usage: .config $(srctree)/applets/usage_compressed
|
||||
applets/applet_tables: .config
|
||||
|
||||
quiet_cmd_gen_usage_compressed = GEN include/usage_compressed.h
|
||||
cmd_gen_usage_compressed = $(srctree)/applets/usage_compressed include/usage_compressed.h applets
|
||||
|
@ -365,7 +365,7 @@ this is a great mystery.
|
||||
becomes its controlling tty.
|
||||
</p><p>The BSD approach is that one has to explicitly call
|
||||
</p><blockquote>
|
||||
<pre>ioctl(fd, TIOCSCTTY, ...);
|
||||
<pre>ioctl(fd, TIOCSCTTY, 0/1);
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
@ -378,6 +378,8 @@ and (ii) it does not yet have a controlling tty, and
|
||||
(iii) maybe the tty should not already control some other session;
|
||||
if it does it is an error if we aren't root, or we steal the tty
|
||||
if we are all-powerful.
|
||||
[vda: correction: third parameter controls this: if 1, we steal tty from
|
||||
any such session, if 0, we don't steal]
|
||||
</p><p>Opening some terminal will give us a controlling tty,
|
||||
provided that (i) the current process is a session leader, and
|
||||
(ii) it does not yet have a controlling tty, and
|
||||
|
@ -37,6 +37,7 @@ config FEATURE_INIT_SCTTY
|
||||
controlling tty (TIOCSCTTY). This is not the traditional init
|
||||
behavour, but is often what you want in an embedded system where
|
||||
the console is only accessed during development or for maintenance.
|
||||
NB: using cttyhack applet may work better.
|
||||
|
||||
config FEATURE_INIT_SYSLOG
|
||||
bool "Enable init to write to syslog"
|
||||
|
244
init/init.c
244
init/init.c
@ -47,31 +47,22 @@
|
||||
#define SHUTDOWN 0x040
|
||||
#define RESTART 0x080
|
||||
|
||||
/* A mapping between "inittab" action name strings and action type codes. */
|
||||
struct init_action_type {
|
||||
const char *name;
|
||||
int action;
|
||||
};
|
||||
|
||||
static const struct init_action_type actions[] = {
|
||||
{"sysinit", SYSINIT},
|
||||
{"respawn", RESPAWN},
|
||||
{"askfirst", ASKFIRST},
|
||||
{"wait", WAIT},
|
||||
{"once", ONCE},
|
||||
{"ctrlaltdel", CTRLALTDEL},
|
||||
{"shutdown", SHUTDOWN},
|
||||
{"restart", RESTART},
|
||||
{0, 0}
|
||||
};
|
||||
#define STR_SYSINIT "\x01"
|
||||
#define STR_RESPAWN "\x02"
|
||||
#define STR_ASKFIRST "\x04"
|
||||
#define STR_WAIT "\x08"
|
||||
#define STR_ONCE "\x10"
|
||||
#define STR_CTRLALTDEL "\x20"
|
||||
#define STR_SHUTDOWN "\x40"
|
||||
#define STR_RESTART "\x80"
|
||||
|
||||
/* Set up a linked list of init_actions, to be read from inittab */
|
||||
struct init_action {
|
||||
struct init_action *next;
|
||||
int action;
|
||||
pid_t pid;
|
||||
char command[INIT_BUFFS_SIZE];
|
||||
uint8_t action;
|
||||
char terminal[CONSOLE_NAME_SIZE];
|
||||
char command[INIT_BUFFS_SIZE];
|
||||
};
|
||||
|
||||
/* Static variables */
|
||||
@ -113,7 +104,7 @@ static const char *const environment[] = {
|
||||
|
||||
/* Function prototypes */
|
||||
static void delete_init_action(struct init_action *a);
|
||||
static int waitfor(const struct init_action *a, pid_t pid);
|
||||
static int waitfor(pid_t pid);
|
||||
#if !ENABLE_DEBUG_INIT
|
||||
static void shutdown_signal(int sig);
|
||||
#endif
|
||||
@ -193,43 +184,6 @@ static void message(int device, const char *fmt, ...)
|
||||
}
|
||||
}
|
||||
|
||||
/* Set terminal settings to reasonable defaults */
|
||||
static void set_sane_term(void)
|
||||
{
|
||||
struct termios tty;
|
||||
|
||||
tcgetattr(STDIN_FILENO, &tty);
|
||||
|
||||
/* set control chars */
|
||||
tty.c_cc[VINTR] = 3; /* C-c */
|
||||
tty.c_cc[VQUIT] = 28; /* C-\ */
|
||||
tty.c_cc[VERASE] = 127; /* C-? */
|
||||
tty.c_cc[VKILL] = 21; /* C-u */
|
||||
tty.c_cc[VEOF] = 4; /* C-d */
|
||||
tty.c_cc[VSTART] = 17; /* C-q */
|
||||
tty.c_cc[VSTOP] = 19; /* C-s */
|
||||
tty.c_cc[VSUSP] = 26; /* C-z */
|
||||
|
||||
/* use line dicipline 0 */
|
||||
tty.c_line = 0;
|
||||
|
||||
/* Make it be sane */
|
||||
tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD;
|
||||
tty.c_cflag |= CREAD | HUPCL | CLOCAL;
|
||||
|
||||
/* input modes */
|
||||
tty.c_iflag = ICRNL | IXON | IXOFF;
|
||||
|
||||
/* output modes */
|
||||
tty.c_oflag = OPOST | ONLCR;
|
||||
|
||||
/* local modes */
|
||||
tty.c_lflag =
|
||||
ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &tty);
|
||||
}
|
||||
|
||||
/* From <linux/serial.h> */
|
||||
struct serial_struct {
|
||||
int type;
|
||||
@ -277,7 +231,7 @@ static void console_init(void)
|
||||
|
||||
s = getenv("TERM");
|
||||
if (ioctl(0, TIOCGSERIAL, &sr) == 0) {
|
||||
/* Force the TERM setting to vt102 for serial console --
|
||||
/* Force the TERM setting to vt102 for serial console
|
||||
* if TERM is set to linux (the default) */
|
||||
if (!s || strcmp(s, "linux") == 0)
|
||||
putenv((char*)"TERM=vt102");
|
||||
@ -288,14 +242,41 @@ static void console_init(void)
|
||||
putenv((char*)"TERM=linux");
|
||||
}
|
||||
|
||||
static void fixup_argv(char **argv)
|
||||
/* Set terminal settings to reasonable defaults */
|
||||
static void set_sane_term(void)
|
||||
{
|
||||
/* Fix up argv[0] to be certain we claim to be init */
|
||||
strncpy(argv[0], "init", strlen(argv[0]));
|
||||
struct termios tty;
|
||||
|
||||
/* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
|
||||
while (*++argv)
|
||||
memset(*argv, 0, strlen(*argv));
|
||||
tcgetattr(STDIN_FILENO, &tty);
|
||||
|
||||
/* set control chars */
|
||||
tty.c_cc[VINTR] = 3; /* C-c */
|
||||
tty.c_cc[VQUIT] = 28; /* C-\ */
|
||||
tty.c_cc[VERASE] = 127; /* C-? */
|
||||
tty.c_cc[VKILL] = 21; /* C-u */
|
||||
tty.c_cc[VEOF] = 4; /* C-d */
|
||||
tty.c_cc[VSTART] = 17; /* C-q */
|
||||
tty.c_cc[VSTOP] = 19; /* C-s */
|
||||
tty.c_cc[VSUSP] = 26; /* C-z */
|
||||
|
||||
/* use line dicipline 0 */
|
||||
tty.c_line = 0;
|
||||
|
||||
/* Make it be sane */
|
||||
tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD;
|
||||
tty.c_cflag |= CREAD | HUPCL | CLOCAL;
|
||||
|
||||
/* input modes */
|
||||
tty.c_iflag = ICRNL | IXON | IXOFF;
|
||||
|
||||
/* output modes */
|
||||
tty.c_oflag = OPOST | ONLCR;
|
||||
|
||||
/* local modes */
|
||||
tty.c_lflag =
|
||||
ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &tty);
|
||||
}
|
||||
|
||||
/* Open the new terminal device */
|
||||
@ -324,6 +305,7 @@ static void open_stdio_to_tty(const char* tty_name, int fail)
|
||||
set_sane_term();
|
||||
}
|
||||
|
||||
/* Used only by run_actions */
|
||||
static pid_t run(const struct init_action *a)
|
||||
{
|
||||
int i;
|
||||
@ -333,16 +315,20 @@ static pid_t run(const struct init_action *a)
|
||||
char buf[INIT_BUFFS_SIZE + 6]; /* INIT_BUFFS_SIZE+strlen("exec ")+1 */
|
||||
sigset_t nmask, omask;
|
||||
|
||||
/* Block sigchild while forking. */
|
||||
/* Block sigchild while forking (why?) */
|
||||
sigemptyset(&nmask);
|
||||
sigaddset(&nmask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
||||
pid = fork();
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
|
||||
if (pid < 0)
|
||||
message(L_LOG | L_CONSOLE, "Can't fork");
|
||||
if (pid)
|
||||
return pid;
|
||||
|
||||
/* Child */
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
signal(SIGUSR1, SIG_DFL);
|
||||
signal(SIGUSR2, SIG_DFL);
|
||||
@ -359,8 +345,9 @@ static pid_t run(const struct init_action *a)
|
||||
setsid();
|
||||
|
||||
/* Open the new terminal device */
|
||||
open_stdio_to_tty(a->terminal, 1);
|
||||
open_stdio_to_tty(a->terminal, 1 /* - exit if open fails*/);
|
||||
|
||||
#ifdef BUT_RUN_ACTIONS_ALREADY_DOES_WAITING
|
||||
/* If the init Action requires us to wait, then force the
|
||||
* supplied terminal to be the controlling tty. */
|
||||
if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
|
||||
@ -373,13 +360,13 @@ static pid_t run(const struct init_action *a)
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
/* We are the parent -- wait till the child is done */
|
||||
/* Parent - wait till the child is done */
|
||||
signal(SIGINT, SIG_IGN);
|
||||
signal(SIGTSTP, SIG_IGN);
|
||||
signal(SIGQUIT, SIG_IGN);
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
|
||||
waitfor(NULL, pid);
|
||||
waitfor(pid);
|
||||
/* See if stealing the controlling tty back is necessary */
|
||||
if (tcgetpgrp(0) != getpid())
|
||||
_exit(0);
|
||||
@ -395,12 +382,13 @@ static pid_t run(const struct init_action *a)
|
||||
ioctl(0, TIOCSCTTY, 1);
|
||||
_exit(0);
|
||||
}
|
||||
waitfor(NULL, pid);
|
||||
waitfor(pid);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
/* Now fall though to actually execute things */
|
||||
/* Child - fall though to actually execute things */
|
||||
}
|
||||
#endif
|
||||
|
||||
/* See if any special /bin/sh requiring characters are present */
|
||||
if (strpbrk(a->command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) {
|
||||
@ -433,9 +421,9 @@ static pid_t run(const struct init_action *a)
|
||||
/* skip over the dash */
|
||||
++cmdpath;
|
||||
|
||||
#ifdef WHY_WE_DO_THIS_SHELL_MUST_HANDLE_THIS_ITSELF
|
||||
/* find the last component in the command pathname */
|
||||
s = bb_get_last_path_component_nostrip(cmdpath);
|
||||
|
||||
/* make a new argv[0] */
|
||||
cmd[0] = malloc(strlen(s) + 2);
|
||||
if (cmd[0] == NULL) {
|
||||
@ -445,16 +433,16 @@ static pid_t run(const struct init_action *a)
|
||||
cmd[0][0] = '-';
|
||||
strcpy(cmd[0] + 1, s);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_FEATURE_INIT_SCTTY
|
||||
/* Establish this process as session leader and
|
||||
* (attempt) to make the tty (if any) a controlling tty.
|
||||
* _attempt_ to make stdin a controlling tty.
|
||||
*/
|
||||
setsid();
|
||||
ioctl(0, TIOCSCTTY, 0 /*don't steal it*/);
|
||||
ioctl(0, TIOCSCTTY, 0 /*only try, don't steal*/);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
|
||||
if (a->action & ASKFIRST) {
|
||||
static const char press_enter[] ALIGN1 =
|
||||
#ifdef CUSTOMIZED_BANNER
|
||||
@ -474,10 +462,10 @@ static pid_t run(const struct init_action *a)
|
||||
"(pid %d, tty '%s')\n",
|
||||
cmdpath, getpid(), a->terminal);
|
||||
full_write(1, press_enter, sizeof(press_enter) - 1);
|
||||
while (read(0, &c, 1) == 1 && c != '\n')
|
||||
;
|
||||
while (safe_read(0, &c, 1) == 1 && c != '\n')
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Log the process name and args */
|
||||
message(L_LOG, "starting pid %d, tty '%s': '%s'",
|
||||
getpid(), a->terminal, cmdpath);
|
||||
@ -504,22 +492,15 @@ static pid_t run(const struct init_action *a)
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
static int waitfor(const struct init_action *a, pid_t pid)
|
||||
static int waitfor(pid_t runpid)
|
||||
{
|
||||
int runpid;
|
||||
int status, wpid;
|
||||
|
||||
runpid = (NULL == a)? pid : run(a);
|
||||
while (1) {
|
||||
wpid = waitpid(runpid, &status, 0);
|
||||
if (wpid == runpid)
|
||||
if (wpid == -1 && errno == EINTR)
|
||||
continue;
|
||||
break;
|
||||
if (wpid == -1 && errno == ECHILD) {
|
||||
/* we missed its termination */
|
||||
break;
|
||||
}
|
||||
/* FIXME other errors should maybe trigger an error, but allow
|
||||
* the program to continue */
|
||||
}
|
||||
return wpid;
|
||||
}
|
||||
@ -534,9 +515,10 @@ static void run_actions(int action)
|
||||
if (a->action == action) {
|
||||
/* a->terminal of "" means "init's console" */
|
||||
if (a->terminal[0] && access(a->terminal, R_OK | W_OK)) {
|
||||
//message(L_LOG | L_CONSOLE, "Device %s cannot be opened in RW mode", a->terminal /*, strerror(errno)*/);
|
||||
delete_init_action(a);
|
||||
} else if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
|
||||
waitfor(a, 0);
|
||||
waitfor(run(a));
|
||||
delete_init_action(a);
|
||||
} else if (a->action & ONCE) {
|
||||
run(a);
|
||||
@ -607,6 +589,29 @@ static void shutdown_system(void)
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
static void shutdown_signal(int sig)
|
||||
{
|
||||
const char *m;
|
||||
int rb;
|
||||
|
||||
shutdown_system();
|
||||
|
||||
m = "halt";
|
||||
rb = RB_HALT_SYSTEM;
|
||||
if (sig == SIGTERM) {
|
||||
m = "reboot";
|
||||
rb = RB_AUTOBOOT;
|
||||
} else if (sig == SIGUSR2) {
|
||||
m = "poweroff";
|
||||
rb = RB_POWER_OFF;
|
||||
}
|
||||
message(L_CONSOLE | L_LOG, "Requesting system %s", m);
|
||||
/* allow time for last message to reach serial console */
|
||||
sleep(2);
|
||||
init_reboot(rb);
|
||||
loop_forever();
|
||||
}
|
||||
|
||||
static void exec_signal(int sig ATTRIBUTE_UNUSED)
|
||||
{
|
||||
struct init_action *a, *tmp;
|
||||
@ -632,7 +637,7 @@ static void exec_signal(int sig ATTRIBUTE_UNUSED)
|
||||
sigprocmask(SIG_UNBLOCK, &unblock_signals, NULL);
|
||||
|
||||
/* Open the new terminal device */
|
||||
open_stdio_to_tty(a->terminal, 0);
|
||||
open_stdio_to_tty(a->terminal, 0 /* - shutdown_signal(SIGUSR1) [halt] if open fails */);
|
||||
|
||||
messageD(L_CONSOLE | L_LOG, "Trying to re-exec %s", a->command);
|
||||
BB_EXECLP(a->command, a->command, NULL);
|
||||
@ -646,29 +651,6 @@ static void exec_signal(int sig ATTRIBUTE_UNUSED)
|
||||
}
|
||||
}
|
||||
|
||||
static void shutdown_signal(int sig)
|
||||
{
|
||||
const char *m;
|
||||
int rb;
|
||||
|
||||
shutdown_system();
|
||||
|
||||
m = "halt";
|
||||
rb = RB_HALT_SYSTEM;
|
||||
if (sig == SIGTERM) {
|
||||
m = "reboot";
|
||||
rb = RB_AUTOBOOT;
|
||||
} else if (sig == SIGUSR2) {
|
||||
m = "poweroff";
|
||||
rb = RB_POWER_OFF;
|
||||
}
|
||||
message(L_CONSOLE | L_LOG, "Requesting system %s", m);
|
||||
/* allow time for last message to reach serial console */
|
||||
sleep(2);
|
||||
init_reboot(rb);
|
||||
loop_forever();
|
||||
}
|
||||
|
||||
static void ctrlaltdel_signal(int sig ATTRIBUTE_UNUSED)
|
||||
{
|
||||
run_actions(CTRLALTDEL);
|
||||
@ -682,7 +664,7 @@ static void stop_handler(int sig ATTRIBUTE_UNUSED)
|
||||
got_cont = 0;
|
||||
while (!got_cont)
|
||||
pause();
|
||||
got_cont = 0;
|
||||
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
@ -694,7 +676,7 @@ static void cont_handler(int sig ATTRIBUTE_UNUSED)
|
||||
|
||||
#endif /* !ENABLE_DEBUG_INIT */
|
||||
|
||||
static void new_init_action(int action, const char *command, const char *cons)
|
||||
static void new_init_action(uint8_t action, const char *command, const char *cons)
|
||||
{
|
||||
struct init_action *new_action, *a, *last;
|
||||
|
||||
@ -754,11 +736,21 @@ static void delete_init_action(struct init_action *action)
|
||||
static void parse_inittab(void)
|
||||
{
|
||||
#if ENABLE_FEATURE_USE_INITTAB
|
||||
static const char actions[] =
|
||||
STR_SYSINIT "sysinit\0"
|
||||
STR_RESPAWN "respawn\0"
|
||||
STR_ASKFIRST "askfirst\0"
|
||||
STR_WAIT "wait\0"
|
||||
STR_ONCE "once\0"
|
||||
STR_CTRLALTDEL "ctrlaltdel\0"
|
||||
STR_SHUTDOWN "shutdown\0"
|
||||
STR_RESTART "restart\0"
|
||||
;
|
||||
|
||||
FILE *file;
|
||||
char buf[INIT_BUFFS_SIZE], lineAsRead[INIT_BUFFS_SIZE];
|
||||
char tmpConsole[CONSOLE_NAME_SIZE];
|
||||
char *id, *runlev, *action, *command, *eol;
|
||||
const struct init_action_type *a = actions;
|
||||
|
||||
file = fopen(INITTAB, "r");
|
||||
if (file == NULL) {
|
||||
@ -769,7 +761,8 @@ static void parse_inittab(void)
|
||||
/* Umount all filesystems on halt/reboot */
|
||||
new_init_action(SHUTDOWN, "umount -a -r", "");
|
||||
/* Swapoff on halt/reboot */
|
||||
if (ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "swapoff -a", "");
|
||||
if (ENABLE_SWAPONOFF)
|
||||
new_init_action(SHUTDOWN, "swapoff -a", "");
|
||||
/* Prepare to restart init when a HUP is received */
|
||||
new_init_action(RESTART, "init", "");
|
||||
/* Askfirst shell on tty1-4 */
|
||||
@ -785,6 +778,8 @@ static void parse_inittab(void)
|
||||
}
|
||||
|
||||
while (fgets(buf, INIT_BUFFS_SIZE, file) != NULL) {
|
||||
const char *a;
|
||||
|
||||
/* Skip leading spaces */
|
||||
for (id = buf; *id == ' ' || *id == '\t'; id++);
|
||||
|
||||
@ -832,8 +827,8 @@ static void parse_inittab(void)
|
||||
}
|
||||
|
||||
/* Ok, now process it */
|
||||
for (a = actions; a->name != 0; a++) {
|
||||
if (strcmp(a->name, action) == 0) {
|
||||
for (a = actions; a[0]; a += strlen(a) + 1) {
|
||||
if (strcmp(a + 1, action) == 0) {
|
||||
if (*id != '\0') {
|
||||
if (strncmp(id, "/dev/", 5) == 0)
|
||||
id += 5;
|
||||
@ -842,11 +837,11 @@ static void parse_inittab(void)
|
||||
sizeof(tmpConsole) - 5);
|
||||
id = tmpConsole;
|
||||
}
|
||||
new_init_action(a->action, command, id);
|
||||
new_init_action((uint8_t)a[0], command, id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (a->name == 0) {
|
||||
if (!a[0]) {
|
||||
/* Choke on an unknown action */
|
||||
message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
|
||||
}
|
||||
@ -981,8 +976,11 @@ int init_main(int argc, char **argv)
|
||||
}
|
||||
#endif /* CONFIG_SELINUX */
|
||||
|
||||
/* Make the command line just say "init" -- thats all, nothing else */
|
||||
fixup_argv(argv);
|
||||
/* Make the command line just say "init" - thats all, nothing else */
|
||||
strncpy(argv[0], "init", strlen(argv[0]));
|
||||
/* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
|
||||
while (*++argv)
|
||||
memset(*argv, 0, strlen(*argv));
|
||||
|
||||
/* Now run everything that needs to be run */
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
/* This code is adapted from busybox project
|
||||
*
|
||||
/* vi: set sw=4 ts=4: */
|
||||
/*
|
||||
* Licensed under GPLv2
|
||||
*
|
||||
* Copyright (c) 2007 Denys Vlasenko <vda.linux@googlemail.com>
|
||||
*/
|
||||
#include "libbb.h"
|
||||
|
||||
@ -66,8 +68,10 @@ int cttyhack_main(int argc, char **argv)
|
||||
dup2(fd, 1);
|
||||
dup2(fd, 2);
|
||||
while (fd > 2) close(fd--);
|
||||
/* Some other session may have it as ctty. Steal it from them */
|
||||
ioctl(0, TIOCSCTTY, 1)
|
||||
}
|
||||
|
||||
execvp(argv[0], argv);
|
||||
BB_EXECVP(argv[0], argv);
|
||||
bb_perror_msg_and_die("cannot exec '%s'", argv[0]);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user