/* vi: set sw=4 ts=4: */
/*
* ash shell port for busybox
*
* This code is derived from software contributed to Berkeley by
* Kenneth Almquist.
*
* Original BSD copyright notice is retained at the end of this file.
*
* Copyright (c) 1989, 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
* was re-ported from NetBSD and debianized.
*
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*/
/*
* The following should be set to reflect the type of system you have:
* JOBS -> 1 if you have Berkeley job control, 0 otherwise.
* define SYSV if you are running under System V.
* define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
* define DEBUG=2 to compile in and turn on debugging.
*
* When debugging is on, debugging info will be written to ./trace and
* a quit signal will generate a core dump.
*/
#define DEBUG 0
/* Tweak debug output verbosity here */
#define DEBUG_TIME 0
#define DEBUG_PID 1
#define DEBUG_SIG 1
#define PROFILE 0
#define JOBS ENABLE_ASH_JOB_CONTROL
#if DEBUG
# ifndef _GNU_SOURCE
# define _GNU_SOURCE
# endif
#endif
#include "busybox.h" /* for applet_names */
#include <paths.h>
#include <setjmp.h>
#include <fnmatch.h>
#include "shell_common.h"
#include "builtin_read.h"
#include "math.h"
#if ENABLE_ASH_RANDOM_SUPPORT
# include "random.h"
#else
# define CLEAR_RANDOM_T(rnd) ((void)0)
#endif
#define SKIP_definitions 1
#include "applet_tables.h"
#undef SKIP_definitions
#if NUM_APPLETS == 1
/* STANDALONE does not make sense, and won't compile */
# undef CONFIG_FEATURE_SH_STANDALONE
# undef ENABLE_FEATURE_SH_STANDALONE
# undef IF_FEATURE_SH_STANDALONE
# undef IF_NOT_FEATURE_SH_STANDALONE
# define ENABLE_FEATURE_SH_STANDALONE 0
# define IF_FEATURE_SH_STANDALONE(...)
# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
#endif
#ifndef PIPE_BUF
# define PIPE_BUF 4096 /* amount of buffering in a pipe */
#endif
#if defined(__uClinux__)
# error "Do not even bother, ash will not run on NOMMU machine"
#endif
/* ============ Hash table sizes. Configurable. */
#define VTABSIZE 39
#define ATABSIZE 39
#define CMDTABLESIZE 31 /* should be prime */
/* ============ Shell options */
static const char *const optletters_optnames[] = {
"e" "errexit",
"f" "noglob",
"I" "ignoreeof",
"i" "interactive",
"m" "monitor",
"n" "noexec",
"s" "stdin",
"x" "xtrace",
"v" "verbose",
"C" "noclobber",
"a" "allexport",
"b" "notify",
"u" "nounset",
"\0" "vi"
#if ENABLE_ASH_BASH_COMPAT
,"\0" "pipefail"
#endif
#if DEBUG
,"\0" "nolog"
,"\0" "debug"
#endif
};
#define optletters(n) optletters_optnames[n][0]
#define optnames(n) (optletters_optnames[n] + 1)
enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
/* ============ Misc data */
static const char homestr[] ALIGN1 = "HOME";
static const char snlfmt[] ALIGN1 = "%s\n";
static const char msg_illnum[] ALIGN1 = "Illegal number: %s";
/*
* We enclose jmp_buf in a structure so that we can declare pointers to
* jump locations. The global variable handler contains the location to
* jump to when an exception occurs, and the global variable exception_type
* contains a code identifying the exception. To implement nested
* exception handlers, the user should save the value of handler on entry
* to an inner scope, set handler to point to a jmploc structure for the
* inner scope, and restore handler on exit from the scope.
*/
struct jmploc {
jmp_buf loc;
};
struct globals_misc {
/* pid of main shell */
int rootpid;
/* shell level: 0 for the main shell, 1 for its children, and so on */
int shlvl;
#define rootshell (!shlvl)
char *minusc; /* argument to -c option */
char *curdir; // = nullstr; /* current working directory */
char *physdir; // = nullstr; /* physical working directory */
char *arg0; /* value of $0 */
struct jmploc *exception_handler;
volatile int suppress_int; /* counter */
volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
/* last pending signal */
volatile /*sig_atomic_t*/ smallint pending_sig;
smallint exception_type; /* kind of exception (0..5) */
/* exceptions */
#define EXINT 0 /* SIGINT received */
#define EXERROR 1 /* a generic error */
#define EXSHELLPROC 2 /* execute a shell procedure */
#define EXEXEC 3 /* command execution failed */
#define EXEXIT 4 /* exit the shell */
#define EXSIG 5 /* trapped signal in wait(1) */
smallint isloginsh;
char nullstr[1]; /* zero length string */
char optlist[NOPTS];
#define eflag optlist[0]
#define fflag optlist[1]
#define Iflag optlist[2]
#define iflag optlist[3]
#define mflag optlist[4]
#define nflag optlist[5]
#define sflag optlist[6]
#define xflag optlist[7]
#define vflag optlist[8]
#define Cflag optlist[9]
#define aflag optlist[10]
#define bflag optlist[11]
#define uflag optlist[12]
#define viflag optlist[13]
#if ENABLE_ASH_BASH_COMPAT
# define pipefail optlist[14]
#else
# define pipefail 0
#endif
#if DEBUG
# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT]
# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT]
#endif
/* trap handler commands */
/*
* Sigmode records the current value of the signal handlers for the various
* modes. A value of zero means that the current handler is not known.
* S_HARD_IGN indicates that the signal was ignored on entry to the shell.
*/
char sigmode[NSIG - 1];
#define S_DFL 1 /* default signal handling (SIG_DFL) */
#define S_CATCH 2 /* signal is caught */
#define S_IGN 3 /* signal is ignored (SIG_IGN) */
#define S_HARD_IGN 4 /* signal is ignored permenantly */
/* indicates specified signal received */
uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
char *trap[NSIG];
char **trap_ptr; /* used only by "trap hack" */
/* Rarely referenced stuff */
#if ENABLE_ASH_RANDOM_SUPPORT
random_t random_gen;
#endif
pid_t backgndpid; /* pid of last background process */
smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
};
extern struct globals_misc *const ash_ptr_to_globals_misc;
#define G_misc (*ash_ptr_to_globals_misc)
#define rootpid (G_misc.rootpid )
#define shlvl (G_misc.shlvl )
#define minusc (G_misc.minusc )
#define curdir (G_misc.curdir )
#define physdir (G_misc.physdir )
#define arg0 (G_misc.arg0 )
#define exception_handler (G_misc.exception_handler)
#define exception_type (G_misc.exception_type )
#define suppress_int (G_misc.suppress_int )
#define pending_int (G_misc.pending_int )
#define pending_sig (G_misc.pending_sig )
#define isloginsh (G_misc.isloginsh )
#define nullstr (G_misc.nullstr )
#define optlist (G_misc.optlist )
#define sigmode (G_misc.sigmode )
#define gotsig (G_misc.gotsig )
#define trap (G_misc.trap )
#define trap_ptr (G_misc.trap_ptr )
#define random_gen (G_misc.random_gen )
#define backgndpid (G_misc.backgndpid )
#define job_warning (G_misc.job_warning)
#define INIT_G_misc() do { \
(*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
barrier(); \
curdir = nullstr; \
physdir = nullstr; \
trap_ptr = trap; \
} while (0)
/* ============ DEBUG */
#if DEBUG
static void trace_printf(const char *fmt, ...);
static void trace_vprintf(const char *fmt, va_list va);
# define TRACE(param) trace_printf param
# define TRACEV(param) trace_vprintf param
# define close(fd) do { \
int dfd = (fd); \
if (close(dfd) < 0) \
bb_error_msg("bug on %d: closing %d(0x%x)", \
__LINE__, dfd, dfd); \
} while (0)
#else
# define TRACE(param)
# define TRACEV(param)
#endif
/* ============ Utility functions */
#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
static int isdigit_str9(const char *str)
{
int maxlen = 9 + 1; /* max 9 digits: 999999999 */
while (--maxlen && isdigit(*str))
str++;
return (*str == '\0');
}
/* ============ Interrupts / exceptions */
/*
* These macros allow the user to suspend the handling of interrupt signals
* over a period of time. This is similar to SIGHOLD or to sigblock, but
* much more efficient and portable. (But hacking the kernel is so much
* more fun than worrying about efficiency and portability. :-))
*/
#define INT_OFF do { \
suppress_int++; \
xbarrier(); \
} while (0)
/*
* Called to raise an exception. Since C doesn't include exceptions, we
* just do a longjmp to the exception handler. The type of exception is
* stored in the global variable "exception_type".
*/
static void raise_exception(int) NORETURN;
static void
raise_exception(int e)
{
#if DEBUG
if (exception_handler == NULL)
abort();
#endif
INT_OFF;
exception_type = e;
longjmp(exception_handler->loc, 1);
}
#if DEBUG
#define raise_exception(e) do { \
TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
raise_exception(e); \
} while (0)
#endif
/*
* Called from trap.c when a SIGINT is received. (If the user specifies
* that SIGINT is to be trapped or ignored using the trap builtin, then
* this routine is not called.) Suppressint is nonzero when interrupts
* are held using the INT_OFF macro. (The test for iflag is just
* defensive programming.)
*/
static void raise_interrupt(void) NORETURN;
static void
raise_interrupt(void)
{
int ex_type;
pending_int = 0;
/* Signal is not automatically unmasked after it is raised,
* do it ourself - unmask all signals */
sigprocmask_allsigs(SIG_UNBLOCK);
/* pending_sig = 0; - now done in onsig() */
ex_type = EXSIG;
if (gotsig[SIGINT - 1] && !