main.c
1/* $OpenBSD: main.c,v 1.99 2023/02/08 17:22:10 kn Exp $ */
2
3/*
4 * startup, main loop, environments and error handling
5 */
6
7#include <sys/stat.h>
8
9#include <errno.h>
10#include <fcntl.h>
11#include <paths.h>
12#include <pwd.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <unistd.h>
17
18#include "sh.h"
19
20extern char **environ;
21
22/*
23 * global data
24 */
25
26static void reclaim(void);
27static void remove_temps(struct temp *tp);
28static int is_restricted(char *name);
29static void init_username(void);
30
31const char *kshname;
32pid_t kshpid;
33pid_t procpid;
34uid_t ksheuid;
35int exstat;
36int subst_exstat;
37const char *safe_prompt;
38int disable_subst;
39
40Area aperm;
41
42struct env *genv;
43
44char shell_flags[FNFLAGS];
45
46char null[] = "";
47
48int shl_stdout_ok;
49
50unsigned int ksh_tmout;
51enum tmout_enum ksh_tmout_state = TMOUT_EXECUTING;
52
53int really_exit;
54
55int ifs0 = ' ';
56
57volatile sig_atomic_t trap;
58volatile sig_atomic_t intrsig;
59volatile sig_atomic_t fatal_trap;
60
61Getopt builtin_opt;
62Getopt user_opt;
63
64struct coproc coproc;
65sigset_t sm_default, sm_sigchld;
66
67char *builtin_argv0;
68int builtin_flag;
69
70char *current_wd;
71int current_wd_size;
72
73int x_cols = 80;
74
75/*
76 * shell initialization
77 */
78
79static const char initifs[] = "IFS= \t\n";
80
81static const char initsubs[] = "${PS2=> } ${PS3=#? } ${PS4=+ }";
82
83static const char *initcoms [] = {
84#ifndef SMALL
85 "typeset", "-r", "KSH_VERSION", NULL,
86#endif /* SMALL */
87 "typeset", "-x", "SHELL", "PATH", "HOME", "PWD", "OLDPWD", NULL,
88 "typeset", "-ir", "PPID", NULL,
89 "typeset", "-i", "OPTIND=1", NULL,
90 "eval", "typeset -i RANDOM MAILCHECK=\"${MAILCHECK-600}\" SECONDS=\"${SECONDS-0}\" TMOUT=\"${TMOUT-0}\"", NULL,
91 "alias",
92 /* Standard ksh aliases */
93 "hash=alias -t", /* not "alias -t --": hash -r needs to work */
94 "stop=kill -STOP",
95 "autoload=typeset -fu",
96 "functions=typeset -f",
97 "history=fc -l",
98 "integer=typeset -i",
99 "nohup=nohup ",
100 "local=typeset",
101 "r=fc -s",
102 /* Aliases that are builtin commands in at&t */
103 "login=exec login",
104 NULL,
105 /* this is what at&t ksh seems to track, with the addition of emacs */
106 "alias", "-tU",
107 "cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
108 "mail", "make", "mv", "pr", "rm", "sed", "sh", "vi", "who",
109 NULL,
110 NULL
111};
112
113char username[_PW_NAME_LEN + 1];
114
115#ifndef SMALL
116#define version_param (initcoms[2])
117#endif /* SMALL */
118
119/* The shell uses its own variation on argv, to build variables like
120 * $0 and $@.
121 * Allocate a new array since modifying the original argv will modify
122 * ps output.
123 */
124static char **
125make_argv(int argc, char *argv[])
126{
127 int i;
128 char **nargv;
129
130 nargv = areallocarray(NULL, argc + 1, sizeof(char *), &aperm);
131 nargv[0] = (char *) kshname;
132 for (i = 1; i < argc; i++)
133 nargv[i] = argv[i];
134 nargv[i] = NULL;
135
136 return nargv;
137}
138
139int
140main(int argc, char *argv[])
141{
142 int i;
143 int argi;
144 Source *s;
145 struct block *l;
146 int restricted, errexit;
147 char **wp;
148 struct env env;
149 pid_t ppid;
150
151 kshname = argv[0];
152
153 if (issetugid()) { /* could later drop privileges */
154 if (pledge("stdio rpath wpath cpath fattr flock getpw proc "
155 "exec tty id", NULL) == -1) {
156 perror("pledge");
157 exit(1);
158 }
159 } else {
160 if (pledge("stdio rpath wpath cpath fattr flock getpw proc "
161 "exec tty", NULL) == -1) {
162 perror("pledge");
163 exit(1);
164 }
165 }
166
167 ainit(&aperm); /* initialize permanent Area */
168
169 /* set up base environment */
170 memset(&env, 0, sizeof(env));
171 env.type = E_NONE;
172 ainit(&env.area);
173 genv = &env;
174 newblock(); /* set up global l->vars and l->funs */
175
176 /* Do this first so output routines (eg, errorf, shellf) can work */
177 initio();
178
179 initvar();
180
181 initctypes();
182
183 inittraps();
184
185 coproc_init();
186
187 /* set up variable and command dictionaries */
188 ktinit(&taliases, APERM, 0);
189 ktinit(&aliases, APERM, 0);
190 ktinit(&homedirs, APERM, 0);
191
192 /* define shell keywords */
193 initkeywords();
194
195 /* define built-in commands */
196 ktinit(&builtins, APERM, 64); /* must be 2^n (currently 40 builtins) */
197 for (i = 0; shbuiltins[i].name != NULL; i++)
198 builtin(shbuiltins[i].name, shbuiltins[i].func);
199 for (i = 0; kshbuiltins[i].name != NULL; i++)
200 builtin(kshbuiltins[i].name, kshbuiltins[i].func);
201
202 init_histvec();
203
204 def_path = _PATH_DEFPATH;
205 {
206 size_t len = confstr(_CS_PATH, NULL, 0);
207 char *new;
208
209 if (len > 0) {
210 confstr(_CS_PATH, new = alloc(len + 1, APERM), len + 1);
211 def_path = new;
212 }
213 }
214
215 /* Set PATH to def_path (will set the path global variable).
216 * (import of environment below will probably change this setting).
217 */
218 {
219 struct tbl *vp = global("PATH");
220 /* setstr can't fail here */
221 setstr(vp, def_path, KSH_RETURN_ERROR);
222 }
223
224
225 /* Turn on nohup by default for now - will change to off
226 * by default once people are aware of its existence
227 * (at&t ksh does not have a nohup option - it always sends
228 * the hup).
229 */
230 Flag(FNOHUP) = 1;
231
232 /* Turn on brace expansion by default. At&t ksh's that have
233 * alternation always have it on. BUT, posix doesn't have
234 * brace expansion, so set this before setting up FPOSIX
235 * (change_flag() clears FBRACEEXPAND when FPOSIX is set).
236 */
237 Flag(FBRACEEXPAND) = 1;
238
239 /* set posix flag just before environment so that it will have
240 * exactly the same effect as the POSIXLY_CORRECT environment
241 * variable. If this needs to be done sooner to ensure correct posix
242 * operation, an initial scan of the environment will also have
243 * done sooner.
244 */
245#ifdef POSIXLY_CORRECT
246 change_flag(FPOSIX, OF_SPECIAL, 1);
247#endif /* POSIXLY_CORRECT */
248
249 /* Check to see if we're /bin/sh. */
250 if (!strcmp(kshname, "sh") || !strcmp(kshname, "-sh") ||
251 (strlen(kshname) >= 3 &&
252 !strcmp(&kshname[strlen(kshname) - 3], "/sh"))) {
253 Flag(FSH) = 1;
254#ifndef SMALL
255 version_param = "SH_VERSION";
256#endif /* SMALL */
257 }
258
259 /* Set edit mode to emacs by default, may be overridden
260 * by the environment or the user. Also, we want tab completion
261 * on in vi by default. */
262#if defined(EMACS)
263 change_flag(FEMACS, OF_SPECIAL, 1);
264#endif /* EMACS */
265#if defined(VI)
266 Flag(FVITABCOMPLETE) = 1;
267#endif /* VI */
268
269 /* import environment */
270 if (environ != NULL)
271 for (wp = environ; *wp != NULL; wp++)
272 typeset(*wp, IMPORT|EXPORT, 0, 0, 0);
273
274 kshpid = procpid = getpid();
275 typeset(initifs, 0, 0, 0, 0); /* for security */
276
277 /* assign default shell variable values */
278 substitute(initsubs, 0);
279
280 /* Figure out the current working directory and set $PWD */
281 {
282 struct stat s_pwd, s_dot;
283 struct tbl *pwd_v = global("PWD");
284 char *pwd = str_val(pwd_v);
285 char *pwdx = pwd;
286
287 /* Try to use existing $PWD if it is valid */
288 if (pwd[0] != '/' ||
289 stat(pwd, &s_pwd) == -1 || stat(".", &s_dot) == -1 ||
290 s_pwd.st_dev != s_dot.st_dev ||
291 s_pwd.st_ino != s_dot.st_ino)
292 pwdx = NULL;
293 set_current_wd(pwdx);
294 if (current_wd[0])
295 simplify_path(current_wd);
296 /* Only set pwd if we know where we are or if it had a
297 * bogus value
298 */
299 if (current_wd[0] || pwd != null)
300 /* setstr can't fail here */
301 setstr(pwd_v, current_wd, KSH_RETURN_ERROR);
302 }
303 ppid = getppid();
304 setint(global("PPID"), (int64_t) ppid);
305#ifndef SMALL
306 /* setstr can't fail here */
307 setstr(global(version_param), ksh_version, KSH_RETURN_ERROR);
308#endif /* SMALL */
309
310 /* execute initialization statements */
311 for (wp = (char**) initcoms; *wp != NULL; wp++) {
312 shcomexec(wp);
313 for (; *wp != NULL; wp++)
314 ;
315 }
316
317
318 ksheuid = geteuid();
319 init_username();
320 safe_prompt = ksheuid ? "$ " : "# ";
321 {
322 struct tbl *vp = global("PS1");
323
324 /* Set PS1 if it isn't set */
325 if (!(vp->flag & ISSET)) {
326 /* setstr can't fail here */
327 setstr(vp, "\\h\\$ ", KSH_RETURN_ERROR);
328 }
329 }
330
331 /* Set this before parsing arguments */
332 Flag(FPRIVILEGED) = getuid() != ksheuid || getgid() != getegid();
333
334 /* this to note if monitor is set on command line (see below) */
335 Flag(FMONITOR) = 127;
336 argi = parse_args(argv, OF_CMDLINE, NULL);
337 if (argi < 0)
338 exit(1);
339
340 if (Flag(FCOMMAND)) {
341 s = pushs(SSTRING, ATEMP);
342 if (!(s->start = s->str = argv[argi++]))
343 errorf("-c requires an argument");
344 if (argv[argi])
345 kshname = argv[argi++];
346 } else if (argi < argc && !Flag(FSTDIN)) {
347 s = pushs(SFILE, ATEMP);
348 s->file = argv[argi++];
349 s->u.shf = shf_open(s->file, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC);
350 if (s->u.shf == NULL) {
351 exstat = 127; /* POSIX */
352 errorf("%s: %s", s->file, strerror(errno));
353 }
354 kshname = s->file;
355 } else {
356 Flag(FSTDIN) = 1;
357 s = pushs(SSTDIN, ATEMP);
358 s->file = "<stdin>";
359 s->u.shf = shf_fdopen(0, SHF_RD | can_seek(0), NULL);
360 if (isatty(0) && isatty(2)) {
361 Flag(FTALKING) = Flag(FTALKING_I) = 1;
362 /* The following only if isatty(0) */
363 s->flags |= SF_TTY;
364 s->u.shf->flags |= SHF_INTERRUPT;
365 s->file = NULL;
366 }
367 }
368
369 /* This bizarreness is mandated by POSIX */
370 {
371 struct stat s_stdin;
372
373 if (fstat(0, &s_stdin) >= 0 && S_ISCHR(s_stdin.st_mode) &&
374 Flag(FTALKING))
375 reset_nonblock(0);
376 }
377
378 /* initialize job control */
379 i = Flag(FMONITOR) != 127;
380 Flag(FMONITOR) = 0;
381 j_init(i);
382 /* Do this after j_init(), as tty_fd is not initialized 'til then */
383 if (Flag(FTALKING))
384 x_init();
385
386 l = genv->loc;
387 l->argv = make_argv(argc - (argi - 1), &argv[argi - 1]);
388 l->argc = argc - argi;
389 getopts_reset(1);
390
391 /* Disable during .profile/ENV reading */
392 restricted = Flag(FRESTRICTED);
393 Flag(FRESTRICTED) = 0;
394 errexit = Flag(FERREXIT);
395 Flag(FERREXIT) = 0;
396
397 /* Do this before profile/$ENV so that if it causes problems in them,
398 * user will know why things broke.
399 */
400 if (!current_wd[0] && Flag(FTALKING))
401 warningf(false, "Cannot determine current working directory");
402
403 if (Flag(FLOGIN)) {
404 include(KSH_SYSTEM_PROFILE, 0, NULL, 1);
405 if (!Flag(FPRIVILEGED))
406 include(substitute("$HOME/.profile", 0), 0, NULL, 1);
407 }
408
409 if (Flag(FPRIVILEGED))
410 include("/etc/suid_profile", 0, NULL, 1);
411 else if (Flag(FTALKING)) {
412 char *env_file;
413
414 /* include $ENV */
415 env_file = str_val(global("ENV"));
416
417#ifdef DEFAULT_ENV
418 /* If env isn't set, include default environment */
419 if (env_file == null)
420 env_file = DEFAULT_ENV;
421#endif /* DEFAULT_ENV */
422 env_file = substitute(env_file, DOTILDE);
423 if (*env_file != '\0')
424 include(env_file, 0, NULL, 1);
425 }
426
427 if (is_restricted(argv[0]) || is_restricted(str_val(global("SHELL"))))
428 restricted = 1;
429 if (restricted) {
430 static const char *const restr_com[] = {
431 "typeset", "-r", "PATH",
432 "ENV", "SHELL",
433 NULL
434 };
435 shcomexec((char **) restr_com);
436 /* After typeset command... */
437 Flag(FRESTRICTED) = 1;
438 }
439 if (errexit)
440 Flag(FERREXIT) = 1;
441
442 if (Flag(FTALKING)) {
443 hist_init(s);
444 alarm_init();
445 } else
446 Flag(FTRACKALL) = 1; /* set after ENV */
447
448 shell(s, true); /* doesn't return */
449 return 0;
450}
451
452static void
453init_username(void)
454{
455 char *p;
456 struct tbl *vp = global("USER");
457
458 if (vp->flag & ISSET)
459 p = ksheuid == 0 ? "root" : str_val(vp);
460 else
461 p = getlogin();
462
463 strlcpy(username, p != NULL ? p : "?", sizeof username);
464}
465
466int
467include(const char *name, int argc, char **argv, int intr_ok)
468{
469 Source *volatile s = NULL;
470 struct shf *shf;
471 char **volatile old_argv;
472 volatile int old_argc;
473 int i;
474
475 shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC);
476 if (shf == NULL)
477 return -1;
478
479 if (argv) {
480 old_argv = genv->loc->argv;
481 old_argc = genv->loc->argc;
482 } else {
483 old_argv = NULL;
484 old_argc = 0;
485 }
486 newenv(E_INCL);
487 i = sigsetjmp(genv->jbuf, 0);
488 if (i) {
489 quitenv(s ? s->u.shf : NULL);
490 if (old_argv) {
491 genv->loc->argv = old_argv;
492 genv->loc->argc = old_argc;
493 }
494 switch (i) {
495 case LRETURN:
496 case LERROR:
497 return exstat & 0xff; /* see below */
498 case LINTR:
499 /* intr_ok is set if we are including .profile or $ENV.
500 * If user ^C's out, we don't want to kill the shell...
501 */
502 if (intr_ok && (exstat - 128) != SIGTERM)
503 return 1;
504 /* FALLTHROUGH */
505 case LEXIT:
506 case LLEAVE:
507 case LSHELL:
508 unwind(i);
509 /* NOTREACHED */
510 default:
511 internal_errorf("%s: %d", __func__, i);
512 /* NOTREACHED */
513 }
514 }
515 if (argv) {
516 genv->loc->argv = argv;
517 genv->loc->argc = argc;
518 }
519 s = pushs(SFILE, ATEMP);
520 s->u.shf = shf;
521 s->file = str_save(name, ATEMP);
522 i = shell(s, false);
523 quitenv(s->u.shf);
524 if (old_argv) {
525 genv->loc->argv = old_argv;
526 genv->loc->argc = old_argc;
527 }
528 return i & 0xff; /* & 0xff to ensure value not -1 */
529}
530
531/*
532 * spawn a command into a shell optionally keeping track of line
533 * number.
534 */
535int
536command(const char *comm, int line)
537{
538 Source *s;
539
540 s = pushs(SSTRING, ATEMP);
541 s->start = s->str = comm;
542 s->line = line;
543 return shell(s, false);
544}
545
546/*
547 * run the commands from the input source, returning status.
548 */
549int
550shell(Source *volatile s, volatile int toplevel)
551{
552 struct op *t;
553 volatile int wastty = s->flags & SF_TTY;
554 volatile int attempts = 13;
555 volatile int interactive = Flag(FTALKING) && toplevel;
556 Source *volatile old_source = source;
557 int i;
558
559 newenv(E_PARSE);
560 if (interactive)
561 really_exit = 0;
562 i = sigsetjmp(genv->jbuf, 0);
563 if (i) {
564 switch (i) {
565 case LINTR: /* we get here if SIGINT not caught or ignored */
566 case LERROR:
567 case LSHELL:
568 if (interactive) {
569 c_fc_reset();
570 if (i == LINTR)
571 shellf("\n");
572 /* Reset any eof that was read as part of a
573 * multiline command.
574 */
575 if (Flag(FIGNOREEOF) && s->type == SEOF &&
576 wastty)
577 s->type = SSTDIN;
578 /* Used by exit command to get back to
579 * top level shell. Kind of strange since
580 * interactive is set if we are reading from
581 * a tty, but to have stopped jobs, one only
582 * needs FMONITOR set (not FTALKING/SF_TTY)...
583 */
584 /* toss any input we have so far */
585 s->start = s->str = null;
586 break;
587 }
588 /* FALLTHROUGH */
589 case LEXIT:
590 case LLEAVE:
591 case LRETURN:
592 source = old_source;
593 quitenv(NULL);
594 unwind(i); /* keep on going */
595 /* NOTREACHED */
596 default:
597 source = old_source;
598 quitenv(NULL);
599 internal_errorf("%s: %d", __func__, i);
600 /* NOTREACHED */
601 }
602 }
603
604 while (1) {
605 if (trap)
606 runtraps(0);
607
608 if (s->next == NULL) {
609 if (Flag(FVERBOSE))
610 s->flags |= SF_ECHO;
611 else
612 s->flags &= ~SF_ECHO;
613 }
614
615 if (interactive) {
616 got_sigwinch = 1;
617 j_notify();
618 mcheck();
619 set_prompt(PS1);
620 /* Reset term title */
621 char *d = str_val(global("DISPLAY"));
622 if(d[0] > 0) {
623 shellnof("%c]0;ksh (%s)%c",
624 '\033', current_wd, '\007');
625 }
626 }
627
628 t = compile(s);
629 if (t != NULL && t->type == TEOF) {
630 if (wastty && Flag(FIGNOREEOF) && --attempts > 0) {
631 shellf("Use `exit' to leave ksh\n");
632 s->type = SSTDIN;
633 } else if (wastty && !really_exit &&
634 j_stopped_running()) {
635 really_exit = 1;
636 s->type = SSTDIN;
637 } else {
638 /* this for POSIX, which says EXIT traps
639 * shall be taken in the environment
640 * immediately after the last command
641 * executed.
642 */
643 if (toplevel)
644 unwind(LEXIT);
645 break;
646 }
647 }
648
649 if (t && (!Flag(FNOEXEC) || (s->flags & SF_TTY)))
650 exstat = execute(t, 0, NULL);
651
652 if (t != NULL && t->type != TEOF && interactive && really_exit)
653 really_exit = 0;
654
655 reclaim();
656 }
657 quitenv(NULL);
658 source = old_source;
659 return exstat;
660}
661
662/* return to closest error handler or shell(), exit if none found */
663void
664unwind(int i)
665{
666 /* ordering for EXIT vs ERR is a bit odd (this is what at&t ksh does) */
667 if (i == LEXIT || (Flag(FERREXIT) && (i == LERROR || i == LINTR) &&
668 sigtraps[SIGEXIT_].trap)) {
669 if (trap)
670 runtraps(0);
671 runtrap(&sigtraps[SIGEXIT_]);
672 i = LLEAVE;
673 } else if (Flag(FERREXIT) && (i == LERROR || i == LINTR)) {
674 if (trap)
675 runtraps(0);
676 runtrap(&sigtraps[SIGERR_]);
677 i = LLEAVE;
678 }
679 while (1) {
680 switch (genv->type) {
681 case E_PARSE:
682 case E_FUNC:
683 case E_INCL:
684 case E_LOOP:
685 case E_ERRH:
686 siglongjmp(genv->jbuf, i);
687 /* NOTREACHED */
688
689 case E_NONE:
690 if (i == LINTR)
691 genv->flags |= EF_FAKE_SIGDIE;
692 /* FALLTHROUGH */
693
694 default:
695 quitenv(NULL);
696 /*
697 * quitenv() may have reclaimed the memory
698 * used by source which will end badly when
699 * we jump to a function that expects it to
700 * be valid
701 */
702 source = NULL;
703 }
704 }
705}
706
707void
708newenv(int type)
709{
710 struct env *ep;
711
712 ep = alloc(sizeof(*ep), ATEMP);
713 ep->type = type;
714 ep->flags = 0;
715 ainit(&ep->area);
716 ep->loc = genv->loc;
717 ep->savefd = NULL;
718 ep->oenv = genv;
719 ep->temps = NULL;
720 genv = ep;
721}
722
723void
724quitenv(struct shf *shf)
725{
726 struct env *ep = genv;
727 int fd;
728
729 if (ep->oenv && ep->oenv->loc != ep->loc)
730 popblock();
731 if (ep->savefd != NULL) {
732 for (fd = 0; fd < NUFILE; fd++)
733 /* if ep->savefd[fd] < 0, means fd was closed */
734 if (ep->savefd[fd])
735 restfd(fd, ep->savefd[fd]);
736 if (ep->savefd[2]) /* Clear any write errors */
737 shf_reopen(2, SHF_WR, shl_out);
738 }
739
740 /* Bottom of the stack.
741 * Either main shell is exiting or cleanup_parents_env() was called.
742 */
743 if (ep->oenv == NULL) {
744 if (ep->type == E_NONE) { /* Main shell exiting? */
745 if (Flag(FTALKING))
746 hist_finish();
747 j_exit();
748 if (ep->flags & EF_FAKE_SIGDIE) {
749 int sig = exstat - 128;
750
751 /* ham up our death a bit (at&t ksh
752 * only seems to do this for SIGTERM)
753 * Don't do it for SIGQUIT, since we'd
754 * dump a core..
755 */
756 if ((sig == SIGINT || sig == SIGTERM) &&
757 getpgrp() == kshpid) {
758 setsig(&sigtraps[sig], SIG_DFL,
759 SS_RESTORE_CURR|SS_FORCE);
760 kill(0, sig);
761 }
762 }
763 }
764 if (shf)
765 shf_close(shf);
766 reclaim();
767 exit(exstat);
768 }
769 if (shf)
770 shf_close(shf);
771 reclaim();
772
773 genv = genv->oenv;
774 afree(ep, ATEMP);
775}
776
777/* Called after a fork to cleanup stuff left over from parents environment */
778void
779cleanup_parents_env(void)
780{
781 struct env *ep;
782 int fd;
783
784 /* Don't clean up temporary files - parent will probably need them.
785 * Also, can't easily reclaim memory since variables, etc. could be
786 * anywhere.
787 */
788
789 /* close all file descriptors hiding in savefd */
790 for (ep = genv; ep; ep = ep->oenv) {
791 if (ep->savefd) {
792 for (fd = 0; fd < NUFILE; fd++)
793 if (ep->savefd[fd] > 0)
794 close(ep->savefd[fd]);
795 afree(ep->savefd, &ep->area);
796 ep->savefd = NULL;
797 }
798 }
799 genv->oenv = NULL;
800}
801
802/* Called just before an execve cleanup stuff temporary files */
803void
804cleanup_proc_env(void)
805{
806 struct env *ep;
807
808 for (ep = genv; ep; ep = ep->oenv)
809 remove_temps(ep->temps);
810}
811
812/* remove temp files and free ATEMP Area */
813static void
814reclaim(void)
815{
816 remove_temps(genv->temps);
817 genv->temps = NULL;
818 afreeall(&genv->area);
819}
820
821static void
822remove_temps(struct temp *tp)
823{
824
825 for (; tp != NULL; tp = tp->next)
826 if (tp->pid == procpid) {
827 unlink(tp->name);
828 }
829}
830
831/* Returns true if name refers to a restricted shell */
832static int
833is_restricted(char *name)
834{
835 char *p;
836
837 if ((p = strrchr(name, '/')))
838 name = p + 1;
839 /* accepts rsh, rksh, rpdksh, pdrksh */
840 if (strcmp(name, "rsh") && \
841 strcmp(name, "rksh") && \
842 strcmp(name, "rpdksh") && \
843 strcmp(name, "pdrksh"))
844 return(0);
845 else
846 return(1);
847
848}