1 /* $OpenBSD: main.c,v 1.92 2018/05/18 13:25:20 benno Exp $ */
4 * startup, main loop, environments and error handling
20 extern char **environ
;
26 static void reclaim(void);
27 static void remove_temps(struct temp
*tp
);
28 static int is_restricted(char *name
);
29 static void init_username(void);
37 const char *safe_prompt
;
43 char shell_flags
[FNFLAGS
];
49 unsigned int ksh_tmout
;
50 enum tmout_enum ksh_tmout_state
= TMOUT_EXECUTING
;
56 volatile sig_atomic_t trap
;
57 volatile sig_atomic_t intrsig
;
58 volatile sig_atomic_t fatal_trap
;
64 sigset_t sm_default
, sm_sigchld
;
75 * shell initialization
78 static const char initifs
[] = "IFS= \t\n";
80 static const char initsubs
[] = "${PS2=> } ${PS3=#? } ${PS4=+ }";
82 static const char *initcoms
[] = {
83 "typeset", "-r", "KSH_VERSION", NULL
,
84 "typeset", "-x", "SHELL", "PATH", "HOME", NULL
,
85 "typeset", "-ir", "PPID", NULL
,
86 "typeset", "-i", "OPTIND=1", NULL
,
87 "eval", "typeset -i RANDOM MAILCHECK=\"${MAILCHECK-600}\" SECONDS=\"${SECONDS-0}\" TMOUT=\"${TMOUT-0}\"", NULL
,
89 /* Standard ksh aliases */
90 "hash=alias -t", /* not "alias -t --": hash -r needs to work */
92 "autoload=typeset -fu",
93 "functions=typeset -f",
99 /* Aliases that are builtin commands in at&t */
102 /* this is what at&t ksh seems to track, with the addition of emacs */
104 "cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
105 "mail", "make", "mv", "pr", "rm", "sed", "sh", "vi", "who",
110 char username
[_PW_NAME_LEN
+ 1];
112 #define version_param (initcoms[2])
114 /* The shell uses its own variation on argv, to build variables like
116 * Allocate a new array since modifying the original argv will modify
120 make_argv(int argc
, char *argv
[])
125 nargv
= areallocarray(NULL
, argc
+ 1, sizeof(char *), &aperm
);
126 nargv
[0] = (char *) kshname
;
127 for (i
= 1; i
< argc
; i
++)
135 main(int argc
, char *argv
[])
141 int restricted
, errexit
;
148 if (pledge("stdio rpath wpath cpath fattr flock getpw proc exec tty",
154 ainit(&aperm
); /* initialize permanent Area */
156 /* set up base environment */
157 memset(&env
, 0, sizeof(env
));
161 newblock(); /* set up global l->vars and l->funs */
163 /* Do this first so output routines (eg, errorf, shellf) can work */
174 /* set up variable and command dictionaries */
175 ktinit(&taliases
, APERM
, 0);
176 ktinit(&aliases
, APERM
, 0);
177 ktinit(&homedirs
, APERM
, 0);
179 /* define shell keywords */
182 /* define built-in commands */
183 ktinit(&builtins
, APERM
, 64); /* must be 2^n (currently 40 builtins) */
184 for (i
= 0; shbuiltins
[i
].name
!= NULL
; i
++)
185 builtin(shbuiltins
[i
].name
, shbuiltins
[i
].func
);
186 for (i
= 0; kshbuiltins
[i
].name
!= NULL
; i
++)
187 builtin(kshbuiltins
[i
].name
, kshbuiltins
[i
].func
);
191 def_path
= _PATH_DEFPATH
;
193 size_t len
= confstr(_CS_PATH
, NULL
, 0);
197 confstr(_CS_PATH
, new = alloc(len
+ 1, APERM
), len
+ 1);
202 /* Set PATH to def_path (will set the path global variable).
203 * (import of environment below will probably change this setting).
206 struct tbl
*vp
= global("PATH");
207 /* setstr can't fail here */
208 setstr(vp
, def_path
, KSH_RETURN_ERROR
);
212 /* Turn on nohup by default for now - will change to off
213 * by default once people are aware of its existence
214 * (at&t ksh does not have a nohup option - it always sends
219 /* Turn on brace expansion by default. At&t ksh's that have
220 * alternation always have it on. BUT, posix doesn't have
221 * brace expansion, so set this before setting up FPOSIX
222 * (change_flag() clears FBRACEEXPAND when FPOSIX is set).
224 Flag(FBRACEEXPAND
) = 1;
226 /* set posix flag just before environment so that it will have
227 * exactly the same effect as the POSIXLY_CORRECT environment
228 * variable. If this needs to be done sooner to ensure correct posix
229 * operation, an initial scan of the environment will also have
232 #ifdef POSIXLY_CORRECT
233 change_flag(FPOSIX
, OF_SPECIAL
, 1);
234 #endif /* POSIXLY_CORRECT */
236 /* Check to see if we're /bin/sh. */
237 if (!strcmp(kshname
, "sh") || !strcmp(kshname
, "-sh") ||
238 (strlen(kshname
) >= 3 &&
239 !strcmp(&kshname
[strlen(kshname
) - 3], "/sh"))) {
241 version_param
= "SH_VERSION";
244 /* Set edit mode to emacs by default, may be overridden
245 * by the environment or the user. Also, we want tab completion
246 * on in vi by default. */
248 change_flag(FEMACS
, OF_SPECIAL
, 1);
251 Flag(FVITABCOMPLETE
) = 1;
254 /* import environment */
256 for (wp
= environ
; *wp
!= NULL
; wp
++)
257 typeset(*wp
, IMPORT
|EXPORT
, 0, 0, 0);
259 kshpid
= procpid
= getpid();
260 typeset(initifs
, 0, 0, 0, 0); /* for security */
262 /* assign default shell variable values */
263 substitute(initsubs
, 0);
265 /* Figure out the current working directory and set $PWD */
267 struct stat s_pwd
, s_dot
;
268 struct tbl
*pwd_v
= global("PWD");
269 char *pwd
= str_val(pwd_v
);
272 /* Try to use existing $PWD if it is valid */
274 stat(pwd
, &s_pwd
) < 0 || stat(".", &s_dot
) < 0 ||
275 s_pwd
.st_dev
!= s_dot
.st_dev
||
276 s_pwd
.st_ino
!= s_dot
.st_ino
)
278 set_current_wd(pwdx
);
280 simplify_path(current_wd
);
281 /* Only set pwd if we know where we are or if it had a
284 if (current_wd
[0] || pwd
!= null
)
285 /* setstr can't fail here */
286 setstr(pwd_v
, current_wd
, KSH_RETURN_ERROR
);
289 setint(global("PPID"), (int64_t) ppid
);
290 /* setstr can't fail here */
291 setstr(global(version_param
), ksh_version
, KSH_RETURN_ERROR
);
293 /* execute initialization statements */
294 for (wp
= (char**) initcoms
; *wp
!= NULL
; wp
++) {
296 for (; *wp
!= NULL
; wp
++)
303 safe_prompt
= ksheuid
? "$ " : "# ";
305 struct tbl
*vp
= global("PS1");
307 /* Set PS1 if it isn't set */
308 if (!(vp
->flag
& ISSET
)) {
309 /* setstr can't fail here */
310 setstr(vp
, "\\h\\$ ", KSH_RETURN_ERROR
);
314 /* Set this before parsing arguments */
315 Flag(FPRIVILEGED
) = getuid() != ksheuid
|| getgid() != getegid();
317 /* this to note if monitor is set on command line (see below) */
318 Flag(FMONITOR
) = 127;
319 argi
= parse_args(argv
, OF_CMDLINE
, NULL
);
323 if (Flag(FCOMMAND
)) {
324 s
= pushs(SSTRING
, ATEMP
);
325 if (!(s
->start
= s
->str
= argv
[argi
++]))
326 errorf("-c requires an argument");
328 kshname
= argv
[argi
++];
329 } else if (argi
< argc
&& !Flag(FSTDIN
)) {
330 s
= pushs(SFILE
, ATEMP
);
331 s
->file
= argv
[argi
++];
332 s
->u
.shf
= shf_open(s
->file
, O_RDONLY
, 0, SHF_MAPHI
|SHF_CLEXEC
);
333 if (s
->u
.shf
== NULL
) {
334 exstat
= 127; /* POSIX */
335 errorf("%s: %s", s
->file
, strerror(errno
));
340 s
= pushs(SSTDIN
, ATEMP
);
342 s
->u
.shf
= shf_fdopen(0, SHF_RD
| can_seek(0), NULL
);
343 if (isatty(0) && isatty(2)) {
344 Flag(FTALKING
) = Flag(FTALKING_I
) = 1;
345 /* The following only if isatty(0) */
347 s
->u
.shf
->flags
|= SHF_INTERRUPT
;
352 /* This bizarreness is mandated by POSIX */
356 if (fstat(0, &s_stdin
) >= 0 && S_ISCHR(s_stdin
.st_mode
) &&
361 /* initialize job control */
362 i
= Flag(FMONITOR
) != 127;
365 /* Do this after j_init(), as tty_fd is not initialized 'til then */
370 l
->argv
= make_argv(argc
- (argi
- 1), &argv
[argi
- 1]);
371 l
->argc
= argc
- argi
;
374 /* Disable during .profile/ENV reading */
375 restricted
= Flag(FRESTRICTED
);
376 Flag(FRESTRICTED
) = 0;
377 errexit
= Flag(FERREXIT
);
380 /* Do this before profile/$ENV so that if it causes problems in them,
381 * user will know why things broke.
383 if (!current_wd
[0] && Flag(FTALKING
))
384 warningf(false, "Cannot determine current working directory");
387 include(KSH_SYSTEM_PROFILE
, 0, NULL
, 1);
388 if (!Flag(FPRIVILEGED
))
389 include(substitute("$HOME/.profile", 0), 0, NULL
, 1);
392 if (Flag(FPRIVILEGED
))
393 include("/etc/suid_profile", 0, NULL
, 1);
394 else if (Flag(FTALKING
)) {
398 env_file
= str_val(global("ENV"));
401 /* If env isn't set, include default environment */
402 if (env_file
== null
)
403 env_file
= DEFAULT_ENV
;
404 #endif /* DEFAULT_ENV */
405 env_file
= substitute(env_file
, DOTILDE
);
406 if (*env_file
!= '\0')
407 include(env_file
, 0, NULL
, 1);
410 if (is_restricted(argv
[0]) || is_restricted(str_val(global("SHELL"))))
413 static const char *const restr_com
[] = {
414 "typeset", "-r", "PATH",
418 shcomexec((char **) restr_com
);
419 /* After typeset command... */
420 Flag(FRESTRICTED
) = 1;
425 if (Flag(FTALKING
)) {
429 Flag(FTRACKALL
) = 1; /* set after ENV */
431 shell(s
, true); /* doesn't return */
439 struct tbl
*vp
= global("USER");
441 if (vp
->flag
& ISSET
)
442 p
= ksheuid
== 0 ? "root" : str_val(vp
);
446 strlcpy(username
, p
!= NULL
? p
: "?", sizeof username
);
450 include(const char *name
, int argc
, char **argv
, int intr_ok
)
452 Source
*volatile s
= NULL
;
454 char **volatile old_argv
;
455 volatile int old_argc
;
458 shf
= shf_open(name
, O_RDONLY
, 0, SHF_MAPHI
|SHF_CLEXEC
);
463 old_argv
= genv
->loc
->argv
;
464 old_argc
= genv
->loc
->argc
;
470 i
= sigsetjmp(genv
->jbuf
, 0);
472 quitenv(s
? s
->u
.shf
: NULL
);
474 genv
->loc
->argv
= old_argv
;
475 genv
->loc
->argc
= old_argc
;
480 return exstat
& 0xff; /* see below */
482 /* intr_ok is set if we are including .profile or $ENV.
483 * If user ^C's out, we don't want to kill the shell...
485 if (intr_ok
&& (exstat
- 128) != SIGTERM
)
494 internal_errorf("%s: %d", __func__
, i
);
499 genv
->loc
->argv
= argv
;
500 genv
->loc
->argc
= argc
;
502 s
= pushs(SFILE
, ATEMP
);
504 s
->file
= str_save(name
, ATEMP
);
508 genv
->loc
->argv
= old_argv
;
509 genv
->loc
->argc
= old_argc
;
511 return i
& 0xff; /* & 0xff to ensure value not -1 */
515 * spawn a command into a shell optionally keeping track of line
519 command(const char *comm
, int line
)
523 s
= pushs(SSTRING
, ATEMP
);
524 s
->start
= s
->str
= comm
;
526 return shell(s
, false);
530 * run the commands from the input source, returning status.
533 shell(Source
*volatile s
, volatile int toplevel
)
536 volatile int wastty
= s
->flags
& SF_TTY
;
537 volatile int attempts
= 13;
538 volatile int interactive
= Flag(FTALKING
) && toplevel
;
539 Source
*volatile old_source
= source
;
545 i
= sigsetjmp(genv
->jbuf
, 0);
548 case LINTR
: /* we get here if SIGINT not caught or ignored */
554 /* Reset any eof that was read as part of a
557 if (Flag(FIGNOREEOF
) && s
->type
== SEOF
&&
560 /* Used by exit command to get back to
561 * top level shell. Kind of strange since
562 * interactive is set if we are reading from
563 * a tty, but to have stopped jobs, one only
564 * needs FMONITOR set (not FTALKING/SF_TTY)...
566 /* toss any input we have so far */
567 s
->start
= s
->str
= null
;
576 unwind(i
); /* keep on going */
581 internal_errorf("%s: %d", __func__
, i
);
590 if (s
->next
== NULL
) {
594 s
->flags
&= ~SF_ECHO
;
605 if (t
!= NULL
&& t
->type
== TEOF
) {
606 if (wastty
&& Flag(FIGNOREEOF
) && --attempts
> 0) {
607 shellf("Use `exit' to leave ksh\n");
609 } else if (wastty
&& !really_exit
&&
610 j_stopped_running()) {
614 /* this for POSIX, which says EXIT traps
615 * shall be taken in the environment
616 * immediately after the last command
625 if (t
&& (!Flag(FNOEXEC
) || (s
->flags
& SF_TTY
)))
626 exstat
= execute(t
, 0, NULL
);
628 if (t
!= NULL
&& t
->type
!= TEOF
&& interactive
&& really_exit
)
638 /* return to closest error handler or shell(), exit if none found */
642 /* ordering for EXIT vs ERR is a bit odd (this is what at&t ksh does) */
643 if (i
== LEXIT
|| (Flag(FERREXIT
) && (i
== LERROR
|| i
== LINTR
) &&
644 sigtraps
[SIGEXIT_
].trap
)) {
647 runtrap(&sigtraps
[SIGEXIT_
]);
649 } else if (Flag(FERREXIT
) && (i
== LERROR
|| i
== LINTR
)) {
652 runtrap(&sigtraps
[SIGERR_
]);
656 switch (genv
->type
) {
662 siglongjmp(genv
->jbuf
, i
);
667 genv
->flags
|= EF_FAKE_SIGDIE
;
673 * quitenv() may have reclaimed the memory
674 * used by source which will end badly when
675 * we jump to a function that expects it to
688 ep
= alloc(sizeof(*ep
), ATEMP
);
700 quitenv(struct shf
*shf
)
702 struct env
*ep
= genv
;
705 if (ep
->oenv
&& ep
->oenv
->loc
!= ep
->loc
)
707 if (ep
->savefd
!= NULL
) {
708 for (fd
= 0; fd
< NUFILE
; fd
++)
709 /* if ep->savefd[fd] < 0, means fd was closed */
711 restfd(fd
, ep
->savefd
[fd
]);
712 if (ep
->savefd
[2]) /* Clear any write errors */
713 shf_reopen(2, SHF_WR
, shl_out
);
716 /* Bottom of the stack.
717 * Either main shell is exiting or cleanup_parents_env() was called.
719 if (ep
->oenv
== NULL
) {
720 if (ep
->type
== E_NONE
) { /* Main shell exiting? */
724 if (ep
->flags
& EF_FAKE_SIGDIE
) {
725 int sig
= exstat
- 128;
727 /* ham up our death a bit (at&t ksh
728 * only seems to do this for SIGTERM)
729 * Don't do it for SIGQUIT, since we'd
732 if ((sig
== SIGINT
|| sig
== SIGTERM
) &&
733 getpgrp() == kshpid
) {
734 setsig(&sigtraps
[sig
], SIG_DFL
,
735 SS_RESTORE_CURR
|SS_FORCE
);
753 /* Called after a fork to cleanup stuff left over from parents environment */
755 cleanup_parents_env(void)
760 /* Don't clean up temporary files - parent will probably need them.
761 * Also, can't easily reclaim memory since variables, etc. could be
765 /* close all file descriptors hiding in savefd */
766 for (ep
= genv
; ep
; ep
= ep
->oenv
) {
768 for (fd
= 0; fd
< NUFILE
; fd
++)
769 if (ep
->savefd
[fd
] > 0)
770 close(ep
->savefd
[fd
]);
771 afree(ep
->savefd
, &ep
->area
);
778 /* Called just before an execve cleanup stuff temporary files */
780 cleanup_proc_env(void)
784 for (ep
= genv
; ep
; ep
= ep
->oenv
)
785 remove_temps(ep
->temps
);
788 /* remove temp files and free ATEMP Area */
792 remove_temps(genv
->temps
);
794 afreeall(&genv
->area
);
798 remove_temps(struct temp
*tp
)
801 for (; tp
!= NULL
; tp
= tp
->next
)
802 if (tp
->pid
== procpid
) {
807 /* Returns true if name refers to a restricted shell */
809 is_restricted(char *name
)
813 if ((p
= strrchr(name
, '/')))
815 /* accepts rsh, rksh, rpdksh, pdrksh */
816 if (strcmp(name
, "rsh") && \
817 strcmp(name
, "rksh") && \
818 strcmp(name
, "rpdksh") && \
819 strcmp(name
, "pdrksh"))