-/* $OpenBSD: main.c,v 1.47 2011/09/07 11:33:25 otto Exp $ */
+/* $OpenBSD: main.c,v 1.54 2013/11/28 10:33:37 sobrado Exp $ */
/* $OpenBSD: tty.c,v 1.9 2006/03/14 22:08:01 deraadt Exp $ */
-/* $OpenBSD: io.c,v 1.22 2006/03/17 16:30:13 millert Exp $ */
-/* $OpenBSD: table.c,v 1.13 2009/01/17 22:06:44 millert Exp $ */
+/* $OpenBSD: io.c,v 1.23 2013/12/17 16:37:06 deraadt Exp $ */
+/* $OpenBSD: table.c,v 1.15 2012/02/19 07:52:30 otto Exp $ */
/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ * 2011, 2012, 2013, 2014
* Thorsten Glaser <tg@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
#include <locale.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/main.c,v 1.200 2011/10/07 19:51:28 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/main.c,v 1.280 2014/06/09 12:28:17 tg Exp $");
extern char **environ;
#define MKSH_DEFAULT_TMPDIR "/tmp"
#endif
+static uint8_t isuc(const char *);
+static int main_init(int, const char *[], Source **, struct block **);
void chvt_reinit(void);
static void reclaim(void);
static void remove_temps(struct temp *);
static const char initifs[] = "IFS= \t\n";
static const char initsubs[] =
- "${PS2=> } ${PS3=#? } ${PS4=+ } ${SECONDS=0} ${TMOUT=0}";
+ "${PS2=> } ${PS3=#? } ${PS4=+ } ${SECONDS=0} ${TMOUT=0} ${EPOCHREALTIME=}";
static const char *initcoms[] = {
Ttypeset, "-r", initvsn, NULL,
Ttypeset, "-x", "HOME", "PATH", "RANDOM", "SHELL", NULL,
- Ttypeset, "-i10", "SECONDS", "TMOUT", NULL,
+ Ttypeset, "-i10", "COLUMNS", "LINES", "SECONDS", "TMOUT", NULL,
Talias,
"integer=typeset -i",
Tlocal_typeset,
/* not in Android for political reasons */
/* not in ARGE mksh due to no job control */
"stop=kill -STOP",
- "suspend=kill -STOP $$",
#endif
"autoload=typeset -fu",
"functions=typeset -f",
Ttypeset, "-r", "PATH", "ENV", "SHELL", NULL
};
-static int initio_done;
+static bool initio_done;
/* top-level parsing and execution environment */
static struct env env;
struct {
ALLOC_ITEM alloc_INT;
void *dataptr, *stkptr, *mallocptr;
+#if defined(__GLIBC__) && (__GLIBC__ >= 2)
sigjmp_buf jbuf;
+#endif
struct timeval tv;
- struct timezone tz;
} *bufptr;
char *cp;
bufptr->stkptr = &bufptr;
/* randomised malloc in BSD (and possibly others) */
bufptr->mallocptr = bufptr;
+#if defined(__GLIBC__) && (__GLIBC__ >= 2)
/* glibc pointer guard */
sigsetjmp(bufptr->jbuf, 1);
- /* introduce variation */
- gettimeofday(&bufptr->tv, &bufptr->tz);
+#endif
+ /* introduce variation (and yes, second arg MBZ for portability) */
+ mksh_TIME(bufptr->tv);
- NZATInit(h);
- /* variation through pid, ppid, and the works */
- NZATUpdateMem(h, &rndsetupstate, sizeof(rndsetupstate));
- /* some variation, some possibly entropy, depending on OE */
- NZATUpdateMem(h, bufptr, sizeof(*bufptr));
- NZAATFinish(h);
+ h = chvt_rndsetup(bufptr, sizeof(*bufptr));
afree(cp, APERM);
return ((mksh_uari_t)h);
"mksh", NULL
};
-int
-main(int argc, const char *argv[])
+static uint8_t
+isuc(const char *cx) {
+ char *cp, *x;
+ uint8_t rv = 0;
+
+ if (!cx || !*cx)
+ return (0);
+
+ /* uppercase a string duplicate */
+ strdupx(x, cx, ATEMP);
+ cp = x;
+ while ((*cp = ksh_toupper(*cp)))
+ ++cp;
+
+ /* check for UTF-8 */
+ if (strstr(x, "UTF-8") || strstr(x, "UTF8"))
+ rv = 1;
+
+ /* free copy and out */
+ afree(x, ATEMP);
+ return (rv);
+}
+
+static int
+main_init(int argc, const char *argv[], Source **sp, struct block **lp)
{
int argi, i;
Source *s = NULL;
if (!*ccp)
ccp = empty_argv[0];
+ /*
+ * Turn on nohup by default. (AT&T ksh does not have a nohup
+ * option - it always sends the hup).
+ */
+ Flag(FNOHUP) = 1;
+
+ /*
+ * Turn on brace expansion by default. AT&T kshs that have
+ * alternation always have it on.
+ */
+ Flag(FBRACEEXPAND) = 1;
+
+ /*
+ * Turn on "set -x" inheritance by default.
+ */
+ Flag(FXTRACEREC) = 1;
+
/* define built-in commands and see if we were called as one */
ktinit(APERM, &builtins,
- /* currently 50 builtins -> 80% of 64 (2^6) */
- 6);
+ /* currently up to 51 builtins: 75% of 128 = 2^7 */
+ 7);
for (i = 0; mkshbuiltins[i].name != NULL; i++)
if (!strcmp(ccp, builtin(mkshbuiltins[i].name,
mkshbuiltins[i].func)))
if (argi < 0)
return (1);
+#if defined(MKSH_BINSHPOSIX) || defined(MKSH_BINSHREDUCED)
+ /* are we called as -sh or /bin/sh or so? */
+ if (!strcmp(ccp, "sh")) {
+ /* either also turns off braceexpand */
+#ifdef MKSH_BINSHPOSIX
+ /* enable better POSIX conformance */
+ change_flag(FPOSIX, OF_FIRSTTIME, true);
+#endif
#ifdef MKSH_BINSHREDUCED
- /* set FSH if we're called as -sh or /bin/sh or so */
- if (!strcmp(ccp, "sh"))
- change_flag(FSH, OF_FIRSTTIME, 1);
+ /* enable kludge/compat mode */
+ change_flag(FSH, OF_FIRSTTIME, true);
+#endif
+ }
#endif
}
init_histvec();
+ /* initialise tty size before importing environment */
+ change_winsz();
+
#ifdef _PATH_DEFPATH
def_path = _PATH_DEFPATH;
#else
/* setstr can't fail here */
setstr(vp, def_path, KSH_RETURN_ERROR);
- /*
- * Turn on nohup by default for now - will change to off
- * by default once people are aware of its existence
- * (AT&T ksh does not have a nohup option - it always sends
- * the hup).
- */
- Flag(FNOHUP) = 1;
-
- /*
- * Turn on brace expansion by default. AT&T kshs that have
- * alternation always have it on.
- */
- Flag(FBRACEEXPAND) = 1;
-
+#ifndef MKSH_NO_CMDLINE_EDITING
/*
* Set edit mode to emacs by default, may be overridden
* by the environment or the user. Also, we want tab completion
* on in vi by default.
*/
- change_flag(FEMACS, OF_SPECIAL, 1);
+ change_flag(FEMACS, OF_SPECIAL, true);
#if !MKSH_S_NOVI
Flag(FVITABCOMPLETE) = 1;
#endif
+#endif
/* import environment */
- if (environ != NULL)
- for (wp = (const char **)environ; *wp != NULL; wp++)
+ if (environ != NULL) {
+ wp = (const char **)environ;
+ while (*wp != NULL) {
+ rndpush(*wp);
typeset(*wp, IMPORT | EXPORT, 0, 0, 0);
+ ++wp;
+ }
+ }
/* for security */
typeset(initifs, 0, 0, 0, 0);
while (*wp != NULL)
wp++;
}
- setint_n(global("COLUMNS"), 0);
- setint_n(global("LINES"), 0);
- setint_n(global("OPTIND"), 1);
+ setint_n(global("OPTIND"), 1, 10);
kshuid = getuid();
kshgid = getgid();
(!ksheuid && !strchr(str_val(vp), '#')))
/* setstr can't fail here */
setstr(vp, safe_prompt, KSH_RETURN_ERROR);
- setint_n((vp = global("PGRP")), (mksh_uari_t)kshpgrp);
+ setint_n((vp = global("BASHPID")), 0, 10);
+ vp->flag |= INT_U;
+ setint_n((vp = global("PGRP")), (mksh_uari_t)kshpgrp, 10);
vp->flag |= INT_U;
- setint_n((vp = global("PPID")), (mksh_uari_t)kshppid);
+ setint_n((vp = global("PPID")), (mksh_uari_t)kshppid, 10);
vp->flag |= INT_U;
- setint_n((vp = global("USER_ID")), (mksh_uari_t)ksheuid);
+ setint_n((vp = global("USER_ID")), (mksh_uari_t)ksheuid, 10);
vp->flag |= INT_U;
- setint_n((vp = global("KSHUID")), (mksh_uari_t)kshuid);
+ setint_n((vp = global("KSHUID")), (mksh_uari_t)kshuid, 10);
vp->flag |= INT_U;
- setint_n((vp = global("KSHEGID")), (mksh_uari_t)kshegid);
+ setint_n((vp = global("KSHEGID")), (mksh_uari_t)kshegid, 10);
vp->flag |= INT_U;
- setint_n((vp = global("KSHGID")), (mksh_uari_t)kshgid);
+ setint_n((vp = global("KSHGID")), (mksh_uari_t)kshgid, 10);
vp->flag |= INT_U;
- setint_n((vp = global("RANDOM")), rndsetup());
+ setint_n((vp = global("RANDOM")), rndsetup(), 10);
vp->flag |= INT_U;
- setint_n((vp_pipest = global("PIPESTATUS")), 0);
+ setint_n((vp_pipest = global("PIPESTATUS")), 0, 10);
/* Set this before parsing arguments */
- Flag(FPRIVILEGED) = kshuid != ksheuid || kshgid != kshegid;
+ Flag(FPRIVILEGED) = (kshuid != ksheuid || kshgid != kshegid) ? 2 : 0;
/* this to note if monitor is set on command line (see below) */
#ifndef MKSH_UNEMPLOYED
return (1);
}
-#ifdef DEBUG
- /* test wraparound of arithmetic types */
- {
- volatile long xl;
- volatile unsigned long xul;
- volatile int xi;
- volatile unsigned int xui;
- volatile mksh_ari_t xa;
- volatile mksh_uari_t xua, xua2;
- volatile uint8_t xc;
-
- xa = 2147483647;
- xua = 2147483647;
- ++xa;
- ++xua;
- xua2 = xa;
- xl = xa;
- xul = xua;
- xa = 0;
- xua = 0;
- --xa;
- --xua;
- xi = xa;
- xui = xua;
- xa = -1;
- xua = xa;
- ++xa;
- ++xua;
- xc = 0;
- --xc;
- if ((xua2 != 2147483648UL) ||
- (xl != -2147483648L) || (xul != 2147483648UL) ||
- (xi != -1) || (xui != 4294967295U) ||
- (xa != 0) || (xua != 0) || (xc != 255))
- errorf("integer wraparound test failed");
- }
-#endif
-
/* process this later only, default to off (hysterical raisins) */
utf_flag = UTFMODE;
UTFMODE = 0;
/* auto-detect from environment variables, always */
utf_flag = 3;
} else if (Flag(FCOMMAND)) {
- s = pushs(SSTRING, ATEMP);
+ s = pushs(SSTRINGCMDLINE, ATEMP);
if (!(s->start = s->str = argv[argi++]))
errorf("%s %s", "-c", "requires an argument");
+ while (*s->str) {
+ if (*s->str != ' ' && ctype(*s->str, C_QUOTE))
+ break;
+ s->str++;
+ }
+ if (!*s->str)
+ s->flags |= SF_MAYEXEC;
+ s->str = s->start;
#ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
/* compatibility to MidnightBSD 0.1 /bin/sh (kludge) */
if (Flag(FSH) && argv[argi] && !strcmp(argv[argi], "--"))
SHF_MAPHI | SHF_CLEXEC);
if (s->u.shf == NULL) {
shl_stdout_ok = false;
- warningf(true, "%s: %s", s->file, strerror(errno));
+ warningf(true, "%s: %s", s->file, cstrerror(errno));
/* mandated by SUSv4 */
exstat = 127;
unwind(LERROR);
/* initialise job control */
j_init();
- /* Do this after j_init(), as tty_fd is not initialised until then */
+ /* do this after j_init() which calls tty_init_state() */
if (Flag(FTALKING)) {
if (utf_flag == 2) {
#ifndef MKSH_ASSUME_UTF8
/* auto-detect from locale or environment */
utf_flag = 4;
-#elif MKSH_ASSUME_UTF8
+#else /* this may not be an #elif */
+#if MKSH_ASSUME_UTF8
utf_flag = 1;
#else
/* always disable UTF-8 (for interactive) */
utf_flag = 0;
#endif
+#endif
}
+#ifndef MKSH_NO_CMDLINE_EDITING
x_init();
+#endif
}
#ifdef SIGWINCH
l->argv[0] = ccp;
} else {
l->argc = argc - argi;
- l->argv = &argv[argi - 1];
+ /*
+ * allocate a new array because otherwise, when we modify
+ * it in-place, ps(1) output changes; the meaning of argc
+ * here is slightly different as it excludes kshname, and
+ * we add a trailing NULL sentinel as well
+ */
+ l->argv = alloc2(l->argc + 2, sizeof(void *), APERM);
l->argv[0] = kshname;
+ memcpy(&l->argv[1], &argv[argi], l->argc * sizeof(void *));
+ l->argv[l->argc + 1] = NULL;
getopts_reset(1);
}
/* divine the initial state of the utf8-mode Flag */
-#define isuc(x) (((x) != NULL) && \
- (stristr((x), "UTF-8") || stristr((x), "utf8")))
ccp = null;
switch (utf_flag) {
UTFMODE = utf_flag;
break;
}
-#undef isuc
/* Disable during .profile/ENV reading */
restricted = Flag(FRESTRICTED);
if (!current_wd[0] && Flag(FTALKING))
warningf(false, "can't determine current directory");
- if (Flag(FLOGIN)) {
- include(MKSH_SYSTEM_PROFILE, 0, NULL, 1);
- if (!Flag(FPRIVILEGED))
- include(substitute("$HOME/.profile", 0), 0,
- NULL, 1);
- }
- if (Flag(FPRIVILEGED))
- include(MKSH_SUID_PROFILE, 0, NULL, 1);
- else if (Flag(FTALKING)) {
- char *env_file;
-
- /* include $ENV */
- env_file = substitute(substitute("${ENV:-" MKSHRC_PATH "}", 0),
- DOTILDE);
- if (*env_file != '\0')
- include(env_file, 0, NULL, 1);
+ if (Flag(FLOGIN))
+ include(MKSH_SYSTEM_PROFILE, 0, NULL, true);
+ if (!Flag(FPRIVILEGED)) {
+ if (Flag(FLOGIN))
+ include(substitute("$HOME/.profile", 0), 0, NULL, true);
+ if (Flag(FTALKING)) {
+ cp = substitute(substitute("${ENV:-" MKSHRC_PATH "}",
+ 0), DOTILDE);
+ if (cp[0] != '\0')
+ include(cp, 0, NULL, true);
+ }
+ } else {
+ include(MKSH_SUID_PROFILE, 0, NULL, true);
+ /* turn off -p if not set explicitly */
+ if (Flag(FPRIVILEGED) != 1)
+ change_flag(FPRIVILEGED, OF_INTERNAL, false);
}
if (restricted) {
}
Flag(FERREXIT) = errexit;
- if (Flag(FTALKING))
+ if (Flag(FTALKING) && s)
hist_init(s);
else
/* set after ENV */
alarm_init();
- if (Flag(FAS_BUILTIN))
- return (shcomexec(l->argv));
-
- /* doesn't return */
- shell(s, true);
- /* NOTREACHED */
+ *sp = s;
+ *lp = l;
return (0);
}
+/* this indirection barrier reduces stack usage during normal operation */
+
+int
+main(int argc, const char *argv[])
+{
+ int rv;
+ Source *s;
+ struct block *l;
+
+ if ((rv = main_init(argc, argv, &s, &l)) == 0) {
+ if (Flag(FAS_BUILTIN)) {
+ rv = shcomexec(l->argv);
+ } else {
+ shell(s, true);
+ /* NOTREACHED */
+ }
+ }
+ return (rv);
+}
+
int
-include(const char *name, int argc, const char **argv, int intr_ok)
+include(const char *name, int argc, const char **argv, bool intr_ok)
{
Source *volatile s = NULL;
struct shf *shf;
old_argc = 0;
}
newenv(E_INCL);
- i = sigsetjmp(e->jbuf, 0);
- if (i) {
+ if ((i = kshsetjmp(e->jbuf))) {
quitenv(s ? s->u.shf : NULL);
if (old_argv) {
e->loc->argv = old_argv;
* intr_ok is set if we are including .profile or $ENV.
* If user ^Cs out, we don't want to kill the shell...
*/
- if (intr_ok && (exstat - 128) != SIGTERM)
+ if (intr_ok && ((exstat & 0xFF) - 128) != SIGTERM)
return (1);
/* FALLTHROUGH */
case LEXIT:
* run the commands from the input source, returning status.
*/
int
-shell(Source * volatile s, volatile int toplevel)
+shell(Source * volatile s, volatile bool toplevel)
{
struct op *t;
- volatile int wastty = s->flags & SF_TTY;
- volatile int attempts = 13;
- volatile int interactive = Flag(FTALKING) && toplevel;
+ volatile bool wastty = tobool(s->flags & SF_TTY);
+ volatile uint8_t attempts = 13;
+ volatile bool interactive = Flag(FTALKING) && toplevel;
volatile bool sfirst = true;
Source *volatile old_source = source;
int i;
newenv(E_PARSE);
if (interactive)
- really_exit = 0;
- i = sigsetjmp(e->jbuf, 0);
- if (i) {
- switch (i) {
- case LINTR:
- /* we get here if SIGINT not caught or ignored */
- case LERROR:
- case LSHELL:
- if (interactive) {
- if (i == LINTR)
- shellf("\n");
- /*
- * Reset any eof that was read as part of a
- * multiline command.
- */
- if (Flag(FIGNOREEOF) && s->type == SEOF &&
- wastty)
- s->type = SSTDIN;
- /*
- * Used by exit command to get back to
- * top level shell. Kind of strange since
- * interactive is set if we are reading from
- * a tty, but to have stopped jobs, one only
- * needs FMONITOR set (not FTALKING/SF_TTY)...
- */
- /* toss any input we have so far */
- s->start = s->str = null;
- break;
- }
- /* FALLTHROUGH */
- case LEXIT:
- case LLEAVE:
- case LRETURN:
- source = old_source;
- quitenv(NULL);
- /* keep on going */
- unwind(i);
- /* NOTREACHED */
- default:
- source = old_source;
- quitenv(NULL);
- internal_errorf("%s %d", "shell", i);
- /* NOTREACHED */
+ really_exit = false;
+ switch ((i = kshsetjmp(e->jbuf))) {
+ case 0:
+ break;
+ case LINTR:
+ /* we get here if SIGINT not caught or ignored */
+ case LERROR:
+ case LSHELL:
+ if (interactive) {
+ if (i == LINTR)
+ shellf("\n");
+ /*
+ * Reset any eof that was read as part of a
+ * multiline command.
+ */
+ if (Flag(FIGNOREEOF) && s->type == SEOF && wastty)
+ s->type = SSTDIN;
+ /*
+ * Used by exit command to get back to
+ * top level shell. Kind of strange since
+ * interactive is set if we are reading from
+ * a tty, but to have stopped jobs, one only
+ * needs FMONITOR set (not FTALKING/SF_TTY)...
+ */
+ /* toss any input we have so far */
+ yyrecursive_pop(true);
+ s->start = s->str = null;
+ retrace_info = NULL;
+ herep = heres;
+ break;
}
+ /* FALLTHROUGH */
+ case LEXIT:
+ case LLEAVE:
+ case LRETURN:
+ source = old_source;
+ quitenv(NULL);
+ /* keep on going */
+ unwind(i);
+ /* NOTREACHED */
+ default:
+ source = old_source;
+ quitenv(NULL);
+ internal_errorf("%s %d", "shell", i);
+ /* NOTREACHED */
}
while (/* CONSTCOND */ 1) {
if (trap)
}
t = compile(s, sfirst);
sfirst = false;
- if (t != NULL && t->type == TEOF) {
+ if (!t)
+ goto source_no_tree;
+ if (t->type == TEOF) {
if (wastty && Flag(FIGNOREEOF) && --attempts > 0) {
shellf("Use 'exit' to leave mksh\n");
s->type = SSTDIN;
} else if (wastty && !really_exit &&
j_stopped_running()) {
- really_exit = 1;
+ really_exit = true;
s->type = SSTDIN;
} else {
/*
unwind(LEXIT);
break;
}
- }
- if (t && (!Flag(FNOEXEC) || (s->flags & SF_TTY)))
- exstat = execute(t, 0, NULL);
+ } else if ((s->flags & SF_MAYEXEC) && t->type == TCOM)
+ t->u.evalflags |= DOTCOMEXEC;
+ if (!Flag(FNOEXEC) || (s->flags & SF_TTY))
+ exstat = execute(t, 0, NULL) & 0xFF;
- if (t != NULL && t->type != TEOF && interactive && really_exit)
- really_exit = 0;
+ if (t->type != TEOF && interactive && really_exit)
+ really_exit = false;
+ source_no_tree:
reclaim();
}
quitenv(NULL);
source = old_source;
- return (exstat);
+ return (exstat & 0xFF);
}
/* return to closest error handler or shell(), exit if none found */
+/* note: i MUST NOT be 0 */
void
unwind(int i)
{
+ /*
+ * This is a kludge. We need to restore everything that was
+ * changed in the new environment, see cid 1005090337C7A669439
+ * and 10050903386452ACBF1, but fail to even save things most of
+ * the time. funcs.c:c_eval() changes FERREXIT temporarily to 0,
+ * which needs to be restored thus (related to Debian #696823).
+ * We did not save the shell flags, so we use a special or'd
+ * value here... this is mostly to clean up behind *other*
+ * callers of unwind(LERROR) here; exec.c has the regular case.
+ */
+ if (Flag(FERREXIT) & 0x80) {
+ /* GNU bash does not run this trapsig */
+ trapsig(ksh_SIGERR);
+ Flag(FERREXIT) &= ~0x80;
+ }
+
/* ordering for EXIT vs ERR is a bit odd (this is what AT&T ksh does) */
- if (i == LEXIT || (Flag(FERREXIT) && (i == LERROR || i == LINTR) &&
- sigtraps[ksh_SIGEXIT].trap)) {
+ if (i == LEXIT || ((i == LERROR || i == LINTR) &&
+ sigtraps[ksh_SIGEXIT].trap &&
+ (!Flag(FTALKING) || Flag(FERREXIT)))) {
++trap_nested;
runtrap(&sigtraps[ksh_SIGEXIT], trap_nested == 1);
--trap_nested;
i = LLEAVE;
- } else if (Flag(FERREXIT) && (i == LERROR || i == LINTR)) {
+ } else if (Flag(FERREXIT) == 1 && (i == LERROR || i == LINTR)) {
++trap_nested;
runtrap(&sigtraps[ksh_SIGERR], trap_nested == 1);
--trap_nested;
i = LLEAVE;
}
+
while (/* CONSTCOND */ 1) {
switch (e->type) {
case E_PARSE:
case E_INCL:
case E_LOOP:
case E_ERRH:
- siglongjmp(e->jbuf, i);
+ kshlongjmp(e->jbuf, i);
/* NOTREACHED */
case E_NONE:
if (i == LINTR)
ep->loc = e->loc;
ep->savefd = NULL;
ep->temps = NULL;
+ ep->yyrecursive_statep = NULL;
ep->type = type;
ep->flags = 0;
/* jump buffer is invalid because flags == 0 */
char *cp;
int fd;
- if (ep->oenv && ep->oenv->loc != ep->loc)
+ yyrecursive_pop(true);
+ while (ep->oenv && ep->oenv->loc != ep->loc)
popblock();
if (ep->savefd != NULL) {
for (fd = 0; fd < NUFILE; fd++)
* Either main shell is exiting or cleanup_parents_env() was called.
*/
if (ep->oenv == NULL) {
+#ifdef DEBUG_LEAKS
+ int i;
+#endif
+
if (ep->type == E_NONE) {
/* Main shell exiting? */
#if HAVE_PERSISTENT_HISTORY
#endif
j_exit();
if (ep->flags & EF_FAKE_SIGDIE) {
- int sig = exstat - 128;
+ int sig = (exstat & 0xFF) - 128;
/*
* ham up our death a bit (AT&T ksh
if (shf)
shf_close(shf);
reclaim();
- exit(exstat);
+#ifdef DEBUG_LEAKS
+#ifndef MKSH_NO_CMDLINE_EDITING
+ x_done();
+#endif
+#ifndef MKSH_NOPROSPECTOFWORK
+ /* block at least SIGCHLD during/after afreeall */
+ sigprocmask(SIG_BLOCK, &sm_sigchld, NULL);
+#endif
+ afreeall(APERM);
+ for (fd = 3; fd < NUFILE; fd++)
+ if ((i = fcntl(fd, F_GETFD, 0)) != -1 &&
+ (i & FD_CLOEXEC))
+ close(fd);
+ close(2);
+ close(1);
+ close(0);
+#endif
+ exit(exstat & 0xFF);
}
if (shf)
shf_close(shf);
afree(ep->savefd, &ep->area);
ep->savefd = NULL;
}
+#ifdef DEBUG_LEAKS
+ if (ep->type != E_NONE)
+ ep->type = E_GONE;
+#endif
}
+#ifndef DEBUG_LEAKS
e->oenv = NULL;
+#endif
}
/* Called just before an execve cleanup stuff temporary files */
static void
reclaim(void)
{
+ struct block *l;
+
+ while ((l = e->loc) && (!e->oenv || e->oenv->loc != l)) {
+ e->loc = l->next;
+ afreeall(&l->area);
+ }
+
remove_temps(e->temps);
e->temps = NULL;
afreeall(&e->area);
{
for (; tp != NULL; tp = tp->next)
if (tp->pid == procpid)
- unlink(tp->name);
+ unlink(tp->tffn);
}
/*
- * Initialise tty_fd. Used for saving/reseting tty modes upon
- * foreground job completion and for setting up tty process group.
+ * Initialise tty_fd. Used for tracking the size of the terminal,
+ * saving/resetting tty modes upon forground job completion, and
+ * for setting up the tty process group. Return values:
+ * 0 = got controlling tty
+ * 1 = got terminal but no controlling tty
+ * 2 = cannot find a terminal
+ * 3 = cannot dup fd
+ * 4 = cannot make fd close-on-exec
+ * An existing tty_fd is cached if no "better" one could be found,
+ * i.e. if tty_devtty was already set or the new would not set it.
*/
-void
-tty_init(bool init_ttystate, bool need_tty)
+int
+tty_init_fd(void)
{
- bool do_close = true;
- int tfd;
+ int fd, rv, eno = 0;
+ bool do_close = false, is_devtty = true;
- if (tty_fd >= 0) {
- close(tty_fd);
- tty_fd = -1;
+ if (tty_devtty) {
+ /* already got a tty which is /dev/tty */
+ return (0);
}
- tty_devtty = true;
#ifdef _UWIN
/*XXX imake style */
if (isatty(3)) {
/* fd 3 on UWIN _is_ /dev/tty (or our controlling tty) */
- tfd = 3;
- do_close = false;
- } else
-#endif
- if ((tfd = open("/dev/tty", O_RDWR, 0)) < 0) {
- tty_devtty = false;
- if (need_tty)
- warningf(false, "%s: %s %s: %s",
- "No controlling tty", "open", "/dev/tty",
- strerror(errno));
+ fd = 3;
+ goto got_fd;
}
- if (tfd < 0) {
- do_close = false;
- if (isatty(0))
- tfd = 0;
- else if (isatty(2))
- tfd = 2;
- else {
- if (need_tty)
- warningf(false, "can't find tty fd");
- return;
- }
+#endif
+ if ((fd = open("/dev/tty", O_RDWR, 0)) >= 0) {
+ do_close = true;
+ goto got_fd;
}
- if ((tty_fd = fcntl(tfd, F_DUPFD, FDBASE)) < 0) {
- if (need_tty)
- warningf(false, "%s: %s %s: %s", "j_ttyinit",
- "dup of tty fd", "failed", strerror(errno));
- } else if (fcntl(tty_fd, F_SETFD, FD_CLOEXEC) < 0) {
- if (need_tty)
- warningf(false, "%s: %s: %s", "j_ttyinit",
- "can't set close-on-exec flag", strerror(errno));
- close(tty_fd);
- tty_fd = -1;
- } else if (init_ttystate)
- tcgetattr(tty_fd, &tty_state);
- if (do_close)
- close(tfd);
-}
+ eno = errno;
-void
-tty_close(void)
-{
if (tty_fd >= 0) {
- close(tty_fd);
- tty_fd = -1;
+ /* already got a non-devtty one */
+ rv = 1;
+ goto out;
}
+ is_devtty = false;
+
+ if (isatty((fd = 0)) || isatty((fd = 2)))
+ goto got_fd;
+ /* cannot find one */
+ rv = 2;
+ /* assert: do_close == false */
+ goto out;
+
+ got_fd:
+ if ((rv = fcntl(fd, F_DUPFD, FDBASE)) < 0) {
+ eno = errno;
+ rv = 3;
+ goto out;
+ }
+ if (fcntl(rv, F_SETFD, FD_CLOEXEC) < 0) {
+ eno = errno;
+ close(rv);
+ rv = 4;
+ goto out;
+ }
+ tty_fd = rv;
+ tty_devtty = is_devtty;
+ rv = eno = 0;
+ out:
+ if (do_close)
+ close(fd);
+ errno = eno;
+ return (rv);
}
/* A shell error occurred (eg, syntax error, etc.) */
static void
vwarningf(unsigned int flags, const char *fmt, va_list ap)
{
- if (*fmt != 1) {
+ if (fmt) {
if (flags & VWARNINGF_INTERNAL)
shf_fprintf(shl_out, "internal error: ");
if (flags & VWARNINGF_ERRORPREFIX)
SHF_UNBUF : 0);
}
-struct shf shf_iob[3];
+#ifdef DF
+int shl_dbg_fd;
+#define NSHF_IOB 4
+#else
+#define NSHF_IOB 3
+#endif
+struct shf shf_iob[NSHF_IOB];
void
initio(void)
{
+#ifdef DF
+ const char *lfp;
+#endif
+
/* force buffer allocation */
shf_fdopen(1, SHF_WR, shl_stdout);
shf_fdopen(2, SHF_WR, shl_out);
- /* force buffer allocation */
- shf_fdopen(2, SHF_WR, shl_spare);
- initio_done = 1;
+ shf_fdopen(2, SHF_WR, shl_xtrace);
+#ifdef DF
+ if ((lfp = getenv("SDMKSH_PATH")) == NULL) {
+ if ((lfp = getenv("HOME")) == NULL || *lfp != '/')
+ errorf("cannot get home directory");
+ lfp = shf_smprintf("%s/mksh-dbg.txt", lfp);
+ }
+
+ if ((shl_dbg_fd = open(lfp, O_WRONLY | O_APPEND | O_CREAT, 0600)) < 0)
+ errorf("cannot open debug output file %s", lfp);
+ if (shl_dbg_fd < FDBASE) {
+ int nfd;
+
+ nfd = fcntl(shl_dbg_fd, F_DUPFD, FDBASE);
+ close(shl_dbg_fd);
+ if ((shl_dbg_fd = nfd) == -1)
+ errorf("cannot dup debug output file");
+ }
+ fcntl(shl_dbg_fd, F_SETFD, FD_CLOEXEC);
+ shf_fdopen(shl_dbg_fd, SHF_WR, shl_dbg);
+ DF("=== open ===");
+#endif
+ initio_done = true;
}
/* A dup2() with error checking */
restfd(int fd, int ofd)
{
if (fd == 2)
- shf_flush(&shf_iob[fd]);
+ shf_flush(&shf_iob[/* fd */ 2]);
if (ofd < 0)
/* original fd closed */
close(fd);
struct temp *
maketemp(Area *ap, Temp_type type, struct temp **tlist)
{
- struct temp *tp;
+ char *cp;
size_t len;
- int fd;
- char *pathname;
+ int i, j;
+ struct temp *tp;
const char *dir;
+ struct stat sb;
dir = tmpdir ? tmpdir : MKSH_DEFAULT_TMPDIR;
-#if HAVE_MKSTEMP
- len = strlen(dir) + 6 + 10 + 1;
-#else
- pathname = tempnam(dir, "mksh.");
- len = ((pathname == NULL) ? 0 : strlen(pathname)) + 1;
-#endif
- /* reasonably sure that this will not overflow */
- tp = alloc(sizeof(struct temp) + len, ap);
- tp->name = (char *)&tp[1];
-#if !HAVE_MKSTEMP
- if (pathname == NULL)
- tp->name[0] = '\0';
- else {
- memcpy(tp->name, pathname, len);
- free_ostempnam(pathname);
- }
-#endif
- pathname = tp->name;
+ /* add "/shXXXXXX.tmp" plus NUL */
+ len = strlen(dir);
+ checkoktoadd(len, offsetof(struct temp, tffn[0]) + 14);
+ tp = alloc(offsetof(struct temp, tffn[0]) + 14 + len, ap);
+
tp->shf = NULL;
- tp->type = type;
-#if HAVE_MKSTEMP
- shf_snprintf(pathname, len, "%s%s", dir, "/mksh.XXXXXXXXXX");
- if ((fd = mkstemp(pathname)) >= 0)
-#else
- if (tp->name[0] && (fd = open(tp->name, O_CREAT | O_RDWR, 0600)) >= 0)
-#endif
- tp->shf = shf_fdopen(fd, SHF_WR, NULL);
tp->pid = procpid;
+ tp->type = type;
+
+ if (stat(dir, &sb) || !S_ISDIR(sb.st_mode)) {
+ tp->tffn[0] = '\0';
+ goto maketemp_out;
+ }
+
+ cp = (void *)tp;
+ cp += offsetof(struct temp, tffn[0]);
+ memcpy(cp, dir, len);
+ cp += len;
+ memcpy(cp, "/shXXXXXX.tmp", 14);
+ /* point to the first of six Xes */
+ cp += 3;
+ /* generate random part of filename */
+ len = -1;
+ do {
+ i = rndget() % 36;
+ cp[++len] = i < 26 ? 'a' + i : '0' + i - 26;
+ } while (len < 5);
+
+ /* cyclically attempt to open a temporary file */
+ while ((i = open(tp->tffn, O_CREAT | O_EXCL | O_RDWR | O_BINARY,
+ 0600)) < 0) {
+ if (errno != EEXIST)
+ goto maketemp_out;
+ /* count down from z to a then from 9 to 0 */
+ while (cp[len] == '0')
+ if (!len--)
+ goto maketemp_out;
+ if (cp[len] == 'a')
+ cp[len] = '9';
+ else
+ --cp[len];
+ /* do another cycle */
+ }
+
+ if (type == TT_FUNSUB) {
+ /* map us high and mark as close-on-exec */
+ if ((j = savefd(i)) != i) {
+ close(i);
+ i = j;
+ }
+
+ /* operation mode for the shf */
+ j = SHF_RD;
+ } else
+ j = SHF_WR;
+
+ /* shf_fdopen cannot fail, so no fd leak */
+ tp->shf = shf_fdopen(i, j, NULL);
+ maketemp_out:
tp->next = *tlist;
*tlist = tp;
return (tp);
/* multiplication cannot overflow: alloc2 checked that */
memset(ntblp, 0, i * sizeof(struct tbl *));
- /* table can get 80% full except when reaching its limit */
- tp->nfree = (tp->tshift == 30) ? 0x3FFF0000UL : ((i * 4) / 5);
+ /* table can get very full when reaching its size limit */
+ tp->nfree = (tp->tshift == 30) ? 0x3FFF0000UL :
+ /* but otherwise, only 75% */
+ ((i * 3) / 4);
tp->tbls = ntblp;
if (otblp == NULL)
return;
if ((tblp = otblp[i]) != NULL) {
if ((tblp->flag & DEFINED)) {
/* search for free hash table slot */
- j = (perturb = tblp->ua.hval) & mask;
+ j = perturb = tblp->ua.hval;
goto find_first_empty_slot;
find_next_empty_slot:
j = (j << 2) + j + perturb + 1;
mask = ((size_t)1 << (tp->tshift)) - 1;
/* search for hash table slot matching name */
- j = (perturb = h) & mask;
+ j = perturb = h;
goto find_first_slot;
find_next_slot:
j = (j << 2) + j + perturb + 1;
got_winch = 1;
}
#endif
+
+#ifdef DF
+void
+DF(const char *fmt, ...)
+{
+ va_list args;
+ struct timeval tv;
+ mirtime_mjd mjd;
+
+ mksh_lockfd(shl_dbg_fd);
+ mksh_TIME(tv);
+ timet2mjd(&mjd, tv.tv_sec);
+ shf_fprintf(shl_dbg, "[%02u:%02u:%02u (%u) %u.%06u] ",
+ (unsigned)mjd.sec / 3600, ((unsigned)mjd.sec / 60) % 60,
+ (unsigned)mjd.sec % 60, (unsigned)getpid(),
+ (unsigned)tv.tv_sec, (unsigned)tv.tv_usec);
+ va_start(args, fmt);
+ shf_vfprintf(shl_dbg, fmt, args);
+ va_end(args);
+ shf_putc('\n', shl_dbg);
+ shf_flush(shl_dbg);
+ mksh_unlkfd(shl_dbg_fd);
+}
+#endif
+
+void
+x_mkraw(int fd, mksh_ttyst *ocb, bool forread)
+{
+ mksh_ttyst cb;
+
+ if (ocb)
+ mksh_tcget(fd, ocb);
+ else
+ ocb = &tty_state;
+
+ cb = *ocb;
+ if (forread) {
+ cb.c_iflag &= ~(ISTRIP);
+ cb.c_lflag &= ~(ICANON) | ECHO;
+ } else {
+ cb.c_iflag &= ~(INLCR | ICRNL | ISTRIP);
+ cb.c_lflag &= ~(ISIG | ICANON | ECHO);
+ }
+#if defined(VLNEXT) && defined(_POSIX_VDISABLE)
+ /* OSF/1 processes lnext when ~icanon */
+ cb.c_cc[VLNEXT] = _POSIX_VDISABLE;
+#endif
+ /* SunOS 4.1.x & OSF/1 processes discard(flush) when ~icanon */
+#if defined(VDISCARD) && defined(_POSIX_VDISABLE)
+ cb.c_cc[VDISCARD] = _POSIX_VDISABLE;
+#endif
+ cb.c_cc[VTIME] = 0;
+ cb.c_cc[VMIN] = 1;
+
+ mksh_tcset(fd, &cb);
+}