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 }
621
622 t = compile(s);
623 if (t != NULL && t->type == TEOF) {
624 if (wastty && Flag(FIGNOREEOF) && --attempts > 0) {
625 shellf("Use `exit' to leave ksh\n");
626 s->type = SSTDIN;
627 } else if (wastty && !really_exit &&
628 j_stopped_running()) {
629 really_exit = 1;
630 s->type = SSTDIN;
631 } else {
632 /* this for POSIX, which says EXIT traps
633 * shall be taken in the environment
634 * immediately after the last command
635 * executed.
636 */
637 if (toplevel)
638 unwind(LEXIT);
639 break;
640 }
641 }
642
643 if (t && (!Flag(FNOEXEC) || (s->flags & SF_TTY)))
644 exstat = execute(t, 0, NULL);
645
646 if (t != NULL && t->type != TEOF && interactive && really_exit)
647 really_exit = 0;
648
649 reclaim();
650 }
651 quitenv(NULL);
652 source = old_source;
653 return exstat;
654}
655
656/* return to closest error handler or shell(), exit if none found */
657void
658unwind(int i)
659{
660 /* ordering for EXIT vs ERR is a bit odd (this is what at&t ksh does) */
661 if (i == LEXIT || (Flag(FERREXIT) && (i == LERROR || i == LINTR) &&
662 sigtraps[SIGEXIT_].trap)) {
663 if (trap)
664 runtraps(0);
665 runtrap(&sigtraps[SIGEXIT_]);
666 i = LLEAVE;
667 } else if (Flag(FERREXIT) && (i == LERROR || i == LINTR)) {
668 if (trap)
669 runtraps(0);
670 runtrap(&sigtraps[SIGERR_]);
671 i = LLEAVE;
672 }
673 while (1) {
674 switch (genv->type) {
675 case E_PARSE:
676 case E_FUNC:
677 case E_INCL:
678 case E_LOOP:
679 case E_ERRH:
680 siglongjmp(genv->jbuf, i);
681 /* NOTREACHED */
682
683 case E_NONE:
684 if (i == LINTR)
685 genv->flags |= EF_FAKE_SIGDIE;
686 /* FALLTHROUGH */
687
688 default:
689 quitenv(NULL);
690 /*
691 * quitenv() may have reclaimed the memory
692 * used by source which will end badly when
693 * we jump to a function that expects it to
694 * be valid
695 */
696 source = NULL;
697 }
698 }
699}
700
701void
702newenv(int type)
703{
704 struct env *ep;
705
706 ep = alloc(sizeof(*ep), ATEMP);
707 ep->type = type;
708 ep->flags = 0;
709 ainit(&ep->area);
710 ep->loc = genv->loc;
711 ep->savefd = NULL;
712 ep->oenv = genv;
713 ep->temps = NULL;
714 genv = ep;
715}
716
717void
718quitenv(struct shf *shf)
719{
720 struct env *ep = genv;
721 int fd;
722
723 if (ep->oenv && ep->oenv->loc != ep->loc)
724 popblock();
725 if (ep->savefd != NULL) {
726 for (fd = 0; fd < NUFILE; fd++)
727 /* if ep->savefd[fd] < 0, means fd was closed */
728 if (ep->savefd[fd])
729 restfd(fd, ep->savefd[fd]);
730 if (ep->savefd[2]) /* Clear any write errors */
731 shf_reopen(2, SHF_WR, shl_out);
732 }
733
734 /* Bottom of the stack.
735 * Either main shell is exiting or cleanup_parents_env() was called.
736 */
737 if (ep->oenv == NULL) {
738 if (ep->type == E_NONE) { /* Main shell exiting? */
739 if (Flag(FTALKING))
740 hist_finish();
741 j_exit();
742 if (ep->flags & EF_FAKE_SIGDIE) {
743 int sig = exstat - 128;
744
745 /* ham up our death a bit (at&t ksh
746 * only seems to do this for SIGTERM)
747 * Don't do it for SIGQUIT, since we'd
748 * dump a core..
749 */
750 if ((sig == SIGINT || sig == SIGTERM) &&
751 getpgrp() == kshpid) {
752 setsig(&sigtraps[sig], SIG_DFL,
753 SS_RESTORE_CURR|SS_FORCE);
754 kill(0, sig);
755 }
756 }
757 }
758 if (shf)
759 shf_close(shf);
760 reclaim();
761 exit(exstat);
762 }
763 if (shf)
764 shf_close(shf);
765 reclaim();
766
767 genv = genv->oenv;
768 afree(ep, ATEMP);
769}
770
771/* Called after a fork to cleanup stuff left over from parents environment */
772void
773cleanup_parents_env(void)
774{
775 struct env *ep;
776 int fd;
777
778 /* Don't clean up temporary files - parent will probably need them.
779 * Also, can't easily reclaim memory since variables, etc. could be
780 * anywhere.
781 */
782
783 /* close all file descriptors hiding in savefd */
784 for (ep = genv; ep; ep = ep->oenv) {
785 if (ep->savefd) {
786 for (fd = 0; fd < NUFILE; fd++)
787 if (ep->savefd[fd] > 0)
788 close(ep->savefd[fd]);
789 afree(ep->savefd, &ep->area);
790 ep->savefd = NULL;
791 }
792 }
793 genv->oenv = NULL;
794}
795
796/* Called just before an execve cleanup stuff temporary files */
797void
798cleanup_proc_env(void)
799{
800 struct env *ep;
801
802 for (ep = genv; ep; ep = ep->oenv)
803 remove_temps(ep->temps);
804}
805
806/* remove temp files and free ATEMP Area */
807static void
808reclaim(void)
809{
810 remove_temps(genv->temps);
811 genv->temps = NULL;
812 afreeall(&genv->area);
813}
814
815static void
816remove_temps(struct temp *tp)
817{
818
819 for (; tp != NULL; tp = tp->next)
820 if (tp->pid == procpid) {
821 unlink(tp->name);
822 }
823}
824
825/* Returns true if name refers to a restricted shell */
826static int
827is_restricted(char *name)
828{
829 char *p;
830
831 if ((p = strrchr(name, '/')))
832 name = p + 1;
833 /* accepts rsh, rksh, rpdksh, pdrksh */
834 if (strcmp(name, "rsh") && \
835 strcmp(name, "rksh") && \
836 strcmp(name, "rpdksh") && \
837 strcmp(name, "pdrksh"))
838 return(0);
839 else
840 return(1);
841
842}