1 /* $OpenBSD: csh.c,v 1.51 2024/07/28 15:31:22 deraadt Exp $ */
2 /* $NetBSD: csh.c,v 1.14 1995/04/29 23:21:28 mycroft Exp $ */
3
4 /*-
5 * Copyright (c) 1980, 1991, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <pwd.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <locale.h>
41 #include <unistd.h>
42 #include <limits.h>
43 #include <vis.h>
44 #include <stdarg.h>
45
46 #include "csh.h"
47 #include "proc.h"
48 #include "extern.h"
49 #include "pathnames.h"
50
51 /*
52 * C Shell
53 *
54 * Bill Joy, UC Berkeley, California, USA
55 * October 1978, May 1980
56 *
57 * Jim Kulp, IIASA, Laxenburg, Austria
58 * April 1980
59 *
60 * Christos Zoulas, Cornell University
61 * June, 1991
62 */
63
64 /* Globals from csh.h */
65 struct timespec time0;
66 struct rusage ru0;
67 struct Bin B;
68 struct Ain lineloc;
69 struct whyle *whyles;
70 struct varent shvhed, aliases;
71 struct wordent *alhistp;
72 struct wordent *alhistt;
73 struct wordent paraml;
74 struct Hist Histlist;
75 FILE *cshin, *cshout, *csherr;
76 bool chkstop;
77 bool didfds;
78 bool doneinp;
79 bool exiterr;
80 bool child;
81 bool haderr;
82 bool intty;
83 bool intact;
84 bool justpr;
85 bool loginsh;
86 bool neednote;
87 bool noexec;
88 bool pjobs;
89 bool setintr;
90 bool timflg;
91 bool havhash;
92 bool needprompt;
93 Char *arginp;
94 int onelflg;
95 Char *ffile;
96 Char *doldol;
97 int backpid;
98 uid_t uid, euid;
99 gid_t gid, egid;
100 time_t chktim;
101 pid_t shpgrp;
102 pid_t tpgrp;
103 pid_t opgrp;
104 int SHIN;
105 int SHOUT;
106 int SHERR;
107 int OLDSTD;
108 jmp_buf reslab;
109 int exitset;
110 Char *gointr;
111 sig_t parintr;
112 sig_t parterm;
113 bool cantell;
114 Char *lap;
115 Char **alvec;
116 int gflag;
117 Char *pargs;
118 long pnleft;
119 Char *pargcp;
120 int eventno;
121 int lastev;
122 Char HIST;
123 Char HISTSUB;
124 char *bname;
125 Char *Vsav;
126 Char *Vdp;
127 Char *Vexpath;
128 char **Vt;
129 Char **evalvec;
130 Char *evalp;
131 Char *word_chars;
132 Char *STR_SHELLPATH;
133 Char *STR_BSHELL;
134 Char *STR_WORD_CHARS;
135 Char **STR_environ;
136
137 /* Locals */
138 Char *dumphist[] = {STRhistory, STRmh, 0, 0};
139 Char *loadhist[] = {STRsource, STRmh, STRtildothist, 0};
140
141 int nofile = 0;
142 bool reenter = 0;
143 bool nverbose = 0;
144 bool nexececho = 0;
145 bool quitit = 0;
146 bool fast = 0;
147 bool batch = 0;
148 bool mflag = 0;
149 bool prompt = 1;
150 bool enterhist = 0;
151 bool tellwhat = 0;
152
153 extern char **environ;
154
155 static int readf(void *, char *, int);
156 static off_t seekf(void *, off_t, int);
157 static int writef(void *, const char *, int);
158 static int closef(void *);
159 static int srccat(Char *, Char *);
160 static int srcfile(char *, bool, bool);
161 static void phup(int);
162 static void srcunit(int, bool, bool);
163 static void mailchk(void);
164 static Char **defaultpath(void);
165
166 int
main(int argc,char * argv[])167 main(int argc, char *argv[])
168 {
169 Char *cp;
170 char *tcp;
171 int f;
172 char **tempv;
173 struct sigaction oact;
174 sigset_t sigset;
175
176 cshin = stdin;
177 cshout = stdout;
178 csherr = stderr;
179
180 settimes(); /* Immed. estab. timing base */
181
182 /*
183 * Initialize non constant strings
184 */
185 STR_BSHELL = SAVE(_PATH_BSHELL);
186 STR_SHELLPATH = SAVE(_PATH_CSHELL);
187 STR_environ = blk2short(environ);
188 environ = short2blk(STR_environ); /* So that we can free it */
189 STR_WORD_CHARS = SAVE(WORD_CHARS);
190
191 HIST = '!';
192 HISTSUB = '^';
193 word_chars = STR_WORD_CHARS;
194
195 tempv = argv;
196 if (eq(str2short(tempv[0]), STRaout)) /* A.out's are quittable */
197 quitit = 1;
198 uid = getuid();
199 gid = getgid();
200 euid = geteuid();
201 egid = getegid();
202 /*
203 * We are a login shell if: 1. we were invoked as -<something> and we had
204 * no arguments 2. or we were invoked only with the -l flag
205 */
206 loginsh = (**tempv == '-' && argc == 1) ||
207 (argc == 2 && tempv[1][0] == '-' && tempv[1][1] == 'l' &&
208 tempv[1][2] == '\0');
209
210 if (loginsh && **tempv != '-') {
211 /*
212 * Mangle the argv space
213 */
214 tempv[1][0] = '\0';
215 tempv[1][1] = '\0';
216 tempv[1] = NULL;
217 for (tcp = *tempv; *tcp++;)
218 continue;
219 for (tcp--; tcp >= *tempv; tcp--)
220 tcp[1] = tcp[0];
221 *++tcp = '-';
222 argc--;
223 }
224 if (loginsh)
225 (void) time(&chktim);
226
227 if (pledge("stdio rpath wpath cpath fattr getpw proc exec tty",
228 NULL) == -1) {
229 perror("pledge");
230 exit(1);
231 }
232
233 /*
234 * Move the descriptors to safe places. The variable didfds is 0 while we
235 * have only FSH* to work with. When didfds is true, we have 0,1,2 and
236 * prefer to use these.
237 */
238 initdesc();
239 /*
240 * XXX: This is to keep programs that use stdio happy.
241 * what we really want is freunopen() ....
242 * Closing cshin cshout and csherr (which are really stdin stdout
243 * and stderr at this point and then reopening them in the same order
244 * gives us again stdin == cshin stdout == cshout and stderr == csherr.
245 * If that was not the case builtins like printf that use stdio
246 * would break. But in any case we could fix that with memcpy and
247 * a bit of pointer manipulation...
248 * Fortunately this is not needed under the current implementation
249 * of stdio.
250 */
251 (void) fclose(cshin);
252 (void) fclose(cshout);
253 (void) fclose(csherr);
254 if (!(cshin = funopen((void *) &SHIN, readf, writef, seekf, closef)))
255 exit(1);
256 if (!(cshout = funopen((void *) &SHOUT, readf, writef, seekf, closef)))
257 exit(1);
258 if (!(csherr = funopen((void *) &SHERR, readf, writef, seekf, closef)))
259 exit(1);
260 (void) setvbuf(cshin, NULL, _IOLBF, 0);
261 (void) setvbuf(cshout, NULL, _IOLBF, 0);
262 (void) setvbuf(csherr, NULL, _IOLBF, 0);
263
264 /*
265 * Initialize the shell variables. ARGV and PROMPT are initialized later.
266 * STATUS is also munged in several places. CHILD is munged when
267 * forking/waiting
268 */
269 set(STRstatus, Strsave(STR0));
270
271 if ((tcp = getenv("HOME")) != NULL && strlen(tcp) < PATH_MAX)
272 cp = SAVE(tcp);
273 else
274 cp = NULL;
275
276 if (cp == NULL)
277 fast = 1; /* No home -> can't read scripts */
278 else
279 set(STRhome, cp);
280 dinit(cp); /* dinit thinks that HOME == cwd in a login
281 * shell */
282 /*
283 * Grab other useful things from the environment. Should we grab
284 * everything??
285 */
286 if ((tcp = getenv("LOGNAME")) != NULL ||
287 (tcp = getenv("USER")) != NULL)
288 set(STRuser, quote(SAVE(tcp)));
289 if ((tcp = getenv("TERM")) != NULL)
290 set(STRterm, quote(SAVE(tcp)));
291
292 /*
293 * Re-initialize path if set in environment
294 */
295 if ((tcp = getenv("PATH")) == NULL)
296 setq(STRpath, defaultpath(), &shvhed);
297 else
298 importpath(str2short(tcp));
299
300 set(STRshell, Strsave(STR_SHELLPATH));
301
302 doldol = putn((int) getpid()); /* For $$ */
303
304 /*
305 * Record the interrupt states from the parent process. If the parent is
306 * non-interruptible our hand must be forced or we (and our children) won't
307 * be either. Our children inherit termination from our parent. We catch it
308 * only if we are the login shell.
309 */
310 /* parents interruptibility */
311 (void) sigaction(SIGINT, NULL, &oact);
312 parintr = oact.sa_handler;
313 (void) sigaction(SIGTERM, NULL, &oact);
314 parterm = oact.sa_handler;
315
316 /* catch these all, login shell or not */
317 (void) signal(SIGHUP, phup); /* exit processing on HUP */
318 (void) signal(SIGXCPU, phup); /* ...and on XCPU */
319 (void) signal(SIGXFSZ, phup); /* ...and on XFSZ */
320
321 /*
322 * Process the arguments.
323 *
324 * Note that processing of -v/-x is actually delayed till after script
325 * processing.
326 *
327 * We set the first character of our name to be '-' if we are a shell
328 * running interruptible commands. Many programs which examine ps'es
329 * use this to filter such shells out.
330 */
331 argc--, tempv++;
332 while (argc > 0 && (tcp = tempv[0])[0] == '-' && *++tcp != '\0' && !batch) {
333 do
334 switch (*tcp++) {
335
336 case 0: /* - Interruptible, no prompt */
337 prompt = 0;
338 setintr = 1;
339 nofile = 1;
340 break;
341
342 case 'b': /* -b Next arg is input file */
343 batch = 1;
344 break;
345
346 case 'c': /* -c Command input from arg */
347 if (argc == 1)
348 xexit(0);
349 argc--, tempv++;
350 arginp = SAVE(tempv[0]);
351 prompt = 0;
352 nofile = 1;
353 break;
354
355 case 'e': /* -e Exit on any error */
356 exiterr = 1;
357 break;
358
359 case 'f': /* -f Fast start */
360 fast = 1;
361 break;
362
363 case 'i': /* -i Interactive, even if !intty */
364 intact = 1;
365 nofile = 1;
366 break;
367
368 case 'm': /* -m read .cshrc (from su) */
369 mflag = 1;
370 break;
371
372 case 'n': /* -n Don't execute */
373 noexec = 1;
374 break;
375
376 case 'q': /* -q (Undoc'd) ... die on quit */
377 quitit = 1;
378 break;
379
380 case 's': /* -s Read from std input */
381 nofile = 1;
382 break;
383
384 case 't': /* -t Read one line from input */
385 onelflg = 2;
386 prompt = 0;
387 nofile = 1;
388 break;
389
390 case 'v': /* -v Echo hist expanded input */
391 nverbose = 1; /* ... later */
392 break;
393
394 case 'x': /* -x Echo just before execution */
395 nexececho = 1; /* ... later */
396 break;
397
398 case 'V': /* -V Echo hist expanded input */
399 setNS(STRverbose); /* NOW! */
400 break;
401
402 case 'X': /* -X Echo just before execution */
403 setNS(STRecho); /* NOW! */
404 break;
405
406 } while (*tcp);
407 tempv++, argc--;
408 }
409
410 if (quitit) /* With all due haste, for debugging */
411 (void) signal(SIGQUIT, SIG_DFL);
412
413 /*
414 * Unless prevented by -, -c, -i, -s, or -t, if there are remaining
415 * arguments the first of them is the name of a shell file from which to
416 * read commands.
417 */
418 if (nofile == 0 && argc > 0) {
419 nofile = open(tempv[0], O_RDONLY);
420 if (nofile == -1) {
421 child = 1; /* So this doesn't return */
422 stderror(ERR_SYSTEM, tempv[0], strerror(errno));
423 }
424 ffile = SAVE(tempv[0]);
425 /*
426 * Replace FSHIN. Handle /dev/std{in,out,err} specially
427 * since once they are closed we cannot open them again.
428 * In that case we use our own saved descriptors
429 */
430 if ((SHIN = dmove(nofile, FSHIN)) < 0)
431 switch(nofile) {
432 case 0:
433 SHIN = FSHIN;
434 break;
435 case 1:
436 SHIN = FSHOUT;
437 break;
438 case 2:
439 SHIN = FSHERR;
440 break;
441 default:
442 stderror(ERR_SYSTEM, tempv[0], strerror(errno));
443 break;
444 }
445 (void) fcntl(SHIN, F_SETFD, FD_CLOEXEC);
446 prompt = 0;
447 /* argc not used any more */ tempv++;
448 }
449
450 intty = isatty(SHIN);
451 intty |= intact;
452 if (intty || (intact && isatty(SHOUT))) {
453 if (!batch && (uid != euid || gid != egid)) {
454 errno = EACCES;
455 child = 1; /* So this doesn't return */
456 stderror(ERR_SYSTEM, "csh", strerror(errno));
457 }
458 }
459 /*
460 * Decide whether we should play with signals or not. If we are explicitly
461 * told (via -i, or -) or we are a login shell (arg0 starts with -) or the
462 * input and output are both the ttys("csh", or "csh</dev/ttyx>/dev/ttyx")
463 * Note that in only the login shell is it likely that parent may have set
464 * signals to be ignored
465 */
466 if (loginsh || intact || (intty && isatty(SHOUT)))
467 setintr = 1;
468 settell();
469 /*
470 * Save the remaining arguments in argv.
471 */
472 setq(STRargv, blk2short(tempv), &shvhed);
473
474 /*
475 * Set up the prompt.
476 */
477 if (prompt) {
478 set(STRprompt, Strsave(uid == 0 ? STRpromptroot : STRpromptuser));
479 /* that's a meta-questionmark */
480 set(STRprompt2, Strsave(STRmquestion));
481 }
482
483 /*
484 * If we are an interactive shell, then start fiddling with the signals;
485 * this is a tricky game.
486 */
487 shpgrp = getpgrp();
488 opgrp = tpgrp = -1;
489 if (setintr) {
490 **argv = '-';
491 if (!quitit) /* Wary! */
492 (void) signal(SIGQUIT, SIG_IGN);
493 (void) signal(SIGINT, pintr);
494 sigemptyset(&sigset);
495 sigaddset(&sigset, SIGINT);
496 sigprocmask(SIG_BLOCK, &sigset, NULL);
497 (void) signal(SIGTERM, SIG_IGN);
498 if (quitit == 0 && arginp == 0) {
499 (void) signal(SIGTSTP, SIG_IGN);
500 (void) signal(SIGTTIN, SIG_IGN);
501 (void) signal(SIGTTOU, SIG_IGN);
502 /*
503 * Wait till in foreground, in case someone stupidly runs csh &
504 * dont want to try to grab away the tty.
505 */
506 if (isatty(FSHERR))
507 f = FSHERR;
508 else if (isatty(FSHOUT))
509 f = FSHOUT;
510 else if (isatty(OLDSTD))
511 f = OLDSTD;
512 else
513 f = -1;
514 retry:
515 if ((tpgrp = tcgetpgrp(f)) != -1) {
516 if (tpgrp != shpgrp) {
517 sig_t old = signal(SIGTTIN, SIG_DFL);
518 (void) kill(0, SIGTTIN);
519 (void) signal(SIGTTIN, old);
520 goto retry;
521 }
522 opgrp = shpgrp;
523 shpgrp = getpid();
524 tpgrp = shpgrp;
525 /*
526 * Setpgid will fail if we are a session leader and
527 * mypid == mypgrp (POSIX 4.3.3)
528 */
529 if (opgrp != shpgrp)
530 if (setpgid(0, shpgrp) == -1)
531 goto notty;
532 /*
533 * We do that after we set our process group, to make sure
534 * that the process group belongs to a process in the same
535 * session as the tty (our process and our group) (POSIX 7.2.4)
536 */
537 if (tcsetpgrp(f, shpgrp) == -1)
538 goto notty;
539 (void) fcntl(dcopy(f, FSHTTY), F_SETFD, FD_CLOEXEC);
540 }
541 if (tpgrp == -1) {
542 notty:
543 (void) fprintf(csherr, "Warning: no access to tty (%s).\n",
544 strerror(errno));
545 (void) fprintf(csherr, "Thus no job control in this shell.\n");
546 }
547 }
548 }
549 if ((setintr == 0) && (parintr == SIG_DFL))
550 setintr = 1;
551 (void) signal(SIGCHLD, pchild); /* while signals not ready */
552
553 /*
554 * Set an exit here in case of an interrupt or error reading the shell
555 * start-up scripts.
556 */
557 reenter = setexit(); /* PWP */
558 exitset++;
559 haderr = 0; /* In case second time through */
560 if (!fast && reenter == 0) {
561 /* Will have value(STRhome) here because set fast if don't */
562 {
563 int osetintr = setintr;
564 sig_t oparintr = parintr;
565 sigset_t osigset;
566
567 sigemptyset(&sigset);
568 sigaddset(&sigset, SIGINT);
569 sigprocmask(SIG_BLOCK, &sigset, &osigset);
570
571 setintr = 0;
572 parintr = SIG_IGN; /* Disable onintr */
573 (void) srcfile(_PATH_DOTCSHRC, 0, 0);
574 if (!fast && !arginp && !onelflg)
575 dohash(NULL, NULL);
576 if (loginsh)
577 (void) srcfile(_PATH_DOTLOGIN, 0, 0);
578 sigprocmask(SIG_SETMASK, &osigset, NULL);
579 setintr = osetintr;
580 parintr = oparintr;
581 }
582 (void) srccat(value(STRhome), STRsldotcshrc);
583
584 if (!fast && !arginp && !onelflg && !havhash)
585 dohash(NULL, NULL);
586 /*
587 * Source history before .login so that it is available in .login
588 */
589 if ((cp = value(STRhistfile)) != STRNULL)
590 loadhist[2] = cp;
591 dosource(loadhist, NULL);
592 if (loginsh)
593 (void) srccat(value(STRhome), STRsldotlogin);
594 }
595
596 /*
597 * Now are ready for the -v and -x flags
598 */
599 if (nverbose)
600 setNS(STRverbose);
601 if (nexececho)
602 setNS(STRecho);
603
604 /*
605 * All the rest of the world is inside this call. The argument to process
606 * indicates whether it should catch "error unwinds". Thus if we are a
607 * interactive shell our call here will never return by being blown past on
608 * an error.
609 */
610 process(setintr);
611
612 /*
613 * Mop-up.
614 */
615 if (intty) {
616 if (loginsh) {
617 (void) fprintf(cshout, "logout\n");
618 (void) close(SHIN);
619 child = 1;
620 goodbye();
621 }
622 else {
623 (void) fprintf(cshout, "exit\n");
624 }
625 }
626 rechist();
627 exitstat();
628 return (0);
629 }
630
631 void
untty(void)632 untty(void)
633 {
634 if (tpgrp > 0) {
635 (void) setpgid(0, opgrp);
636 (void) tcsetpgrp(FSHTTY, opgrp);
637 }
638 }
639
640 void
importpath(Char * cp)641 importpath(Char *cp)
642 {
643 int i = 0;
644 Char *dp;
645 Char **pv;
646 int c;
647
648 for (dp = cp; *dp; dp++)
649 if (*dp == ':')
650 i++;
651 /*
652 * i+2 where i is the number of colons in the path. There are i+1
653 * directories in the path plus we need room for a zero terminator.
654 */
655 pv = xcalloc(i + 2, sizeof(*pv));
656 dp = cp;
657 i = 0;
658 if (*dp)
659 for (;;) {
660 if ((c = *dp) == ':' || c == 0) {
661 *dp = 0;
662 pv[i++] = Strsave(*cp ? cp : STRdot);
663 if (c) {
664 cp = dp + 1;
665 *dp = ':';
666 }
667 else
668 break;
669 }
670 dp++;
671 }
672 pv[i] = 0;
673 setq(STRpath, pv, &shvhed);
674 }
675
676 /*
677 * Source to the file which is the catenation of the argument names.
678 */
679 static int
srccat(Char * cp,Char * dp)680 srccat(Char *cp, Char *dp)
681 {
682 Char *ep = Strspl(cp, dp);
683 char *ptr = short2str(ep);
684
685 free(ep);
686 return srcfile(ptr, mflag ? 0 : 1, 0);
687 }
688
689 /*
690 * Source to a file putting the file descriptor in a safe place (> 2).
691 */
692 static int
srcfile(char * f,bool onlyown,bool flag)693 srcfile(char *f, bool onlyown, bool flag)
694 {
695 int unit;
696
697 if ((unit = open(f, O_RDONLY)) == -1)
698 return 0;
699 unit = dmove(unit, -1);
700
701 (void) fcntl(unit, F_SETFD, FD_CLOEXEC);
702 srcunit(unit, onlyown, flag);
703 return 1;
704 }
705
706 /*
707 * Source to a unit. If onlyown it must be our file or our group or
708 * we don't chance it. This occurs on ".cshrc"s and the like.
709 */
710 int insource;
711 static void
srcunit(int unit,bool onlyown,bool hflg)712 srcunit(int unit, bool onlyown, bool hflg)
713 {
714 /* We have to push down a lot of state here */
715 /* All this could go into a structure */
716 int oSHIN = -1, oldintty = intty, oinsource = insource;
717 struct whyle *oldwhyl = whyles;
718 Char *ogointr = gointr, *oarginp = arginp;
719 Char *oevalp = evalp, **oevalvec = evalvec;
720 int oonelflg = onelflg;
721 bool oenterhist = enterhist;
722 char OHIST = HIST;
723 bool otell = cantell;
724
725 struct Bin saveB;
726 sigset_t sigset, osigset;
727 jmp_buf oldexit;
728
729 /* The (few) real local variables */
730 int my_reenter;
731
732 if (unit < 0)
733 return;
734 if (didfds)
735 donefds();
736 if (onlyown) {
737 struct stat stb;
738
739 if (fstat(unit, &stb) == -1) {
740 (void) close(unit);
741 return;
742 }
743 }
744
745 /*
746 * There is a critical section here while we are pushing down the input
747 * stream since we have stuff in different structures. If we weren't
748 * careful an interrupt could corrupt SHIN's Bin structure and kill the
749 * shell.
750 *
751 * We could avoid the critical region by grouping all the stuff in a single
752 * structure and pointing at it to move it all at once. This is less
753 * efficient globally on many variable references however.
754 */
755 insource = 1;
756 getexit(oldexit);
757
758 if (setintr) {
759 sigemptyset(&sigset);
760 sigaddset(&sigset, SIGINT);
761 sigprocmask(SIG_BLOCK, &sigset, &osigset);
762 }
763 /* Setup the new values of the state stuff saved above */
764 memcpy(&saveB, &B, sizeof(B));
765 fbuf = NULL;
766 fseekp = feobp = fblocks = 0;
767 oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0;
768 intty = isatty(SHIN), whyles = 0, gointr = 0;
769 evalvec = 0;
770 evalp = 0;
771 enterhist = hflg;
772 if (enterhist)
773 HIST = '\0';
774
775 /*
776 * Now if we are allowing commands to be interrupted, we let ourselves be
777 * interrupted.
778 */
779 if (setintr)
780 sigprocmask(SIG_SETMASK, &osigset, NULL);
781 settell();
782
783 if ((my_reenter = setexit()) == 0)
784 process(0); /* 0 -> blow away on errors */
785
786 if (setintr)
787 sigprocmask(SIG_SETMASK, &osigset, NULL);
788 if (oSHIN >= 0) {
789 int i;
790
791 /* We made it to the new state... free up its storage */
792 /* This code could get run twice but free doesn't care */
793 for (i = 0; i < fblocks; i++)
794 free(fbuf[i]);
795 free(fbuf);
796
797 /* Reset input arena */
798 memcpy(&B, &saveB, sizeof(B));
799
800 (void) close(SHIN), SHIN = oSHIN;
801 arginp = oarginp, onelflg = oonelflg;
802 evalp = oevalp, evalvec = oevalvec;
803 intty = oldintty, whyles = oldwhyl, gointr = ogointr;
804 if (enterhist)
805 HIST = OHIST;
806 enterhist = oenterhist;
807 cantell = otell;
808 }
809
810 resexit(oldexit);
811 /*
812 * If process reset() (effectively an unwind) then we must also unwind.
813 */
814 if (my_reenter)
815 stderror(ERR_SILENT);
816 insource = oinsource;
817 }
818
819 void
rechist(void)820 rechist(void)
821 {
822 Char buf[BUFSIZ], hbuf[BUFSIZ], *hfile;
823 int fd, ftmp, oldidfds;
824 struct varent *shist;
825
826 if (!fast) {
827 /*
828 * If $savehist is just set, we use the value of $history
829 * else we use the value in $savehist
830 */
831 if ((shist = adrof(STRsavehist)) != NULL) {
832 if (shist->vec[0][0] != '\0')
833 (void) Strlcpy(hbuf, shist->vec[0], sizeof hbuf/sizeof(Char));
834 else if ((shist = adrof(STRhistory)) && shist->vec[0][0] != '\0')
835 (void) Strlcpy(hbuf, shist->vec[0], sizeof hbuf/sizeof(Char));
836 else
837 return;
838 }
839 else
840 return;
841
842 if ((hfile = value(STRhistfile)) == STRNULL) {
843 Strlcpy(buf, value(STRhome), sizeof buf/sizeof(Char));
844 hfile = buf;
845 (void) Strlcat(buf, STRsldthist, sizeof buf/sizeof(Char));
846 }
847
848 if ((fd = open(short2str(hfile), O_WRONLY | O_CREAT | O_TRUNC,
849 0600)) == -1)
850 return;
851
852 oldidfds = didfds;
853 didfds = 0;
854 ftmp = SHOUT;
855 SHOUT = fd;
856 dumphist[2] = hbuf;
857 dohist(dumphist, NULL);
858 SHOUT = ftmp;
859 (void) close(fd);
860 didfds = oldidfds;
861 }
862 }
863
864 void
goodbye(void)865 goodbye(void)
866 {
867 rechist();
868
869 if (loginsh) {
870 (void) signal(SIGQUIT, SIG_IGN);
871 (void) signal(SIGINT, SIG_IGN);
872 (void) signal(SIGTERM, SIG_IGN);
873 setintr = 0; /* No interrupts after "logout" */
874 if (!(adrof(STRlogout)))
875 set(STRlogout, STRnormal);
876 (void) srcfile(_PATH_DOTLOGOUT, 0, 0);
877 if (adrof(STRhome))
878 (void) srccat(value(STRhome), STRsldtlogout);
879 }
880 exitstat();
881 }
882
883 void
exitstat(void)884 exitstat(void)
885 {
886 Char *s;
887 /*
888 * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit
889 * directly because we poke child here. Otherwise we might continue
890 * unwarrantedly (sic).
891 */
892 child = 1;
893 s = value(STRstatus);
894 xexit(s ? getn(s) : 0);
895 }
896
897 /*
898 * in the event of a HUP we want to save the history
899 */
900 static void
phup(int sig)901 phup(int sig)
902 {
903 rechist(); /* XXX big signal race */
904
905 /*
906 * We kill the last foreground process group. It then becomes
907 * responsible to propagate the SIGHUP to its progeny.
908 */
909 {
910 struct process *pp, *np;
911
912 for (pp = proclist.p_next; pp; pp = pp->p_next) {
913 np = pp;
914 /*
915 * Find if this job is in the foreground. It could be that
916 * the process leader has exited and the foreground flag
917 * is cleared for it.
918 */
919 do
920 /*
921 * If a process is in the foreground; we try to kill
922 * its process group. If we succeed, then the
923 * whole job is gone. Otherwise we keep going...
924 * But avoid sending HUP to the shell again.
925 */
926 if ((np->p_flags & PFOREGND) != 0 && np->p_jobid != shpgrp &&
927 kill(-np->p_jobid, SIGHUP) != -1) {
928 /* In case the job was suspended... */
929 (void) kill(-np->p_jobid, SIGCONT);
930 break;
931 }
932 while ((np = np->p_friends) != pp);
933 }
934 }
935 xexit(sig);
936 }
937
938 Char *jobargv[2] = {STRjobs, 0};
939
940 /*
941 * Catch an interrupt, e.g. during lexical input.
942 * If we are an interactive shell, we reset the interrupt catch
943 * immediately. In any case we drain the shell output,
944 * and finally go through the normal error mechanism, which
945 * gets a chance to make the shell go away.
946 */
947 void
pintr(int notused)948 pintr(int notused)
949 {
950 int save_errno = errno;
951
952 pintr1(1);
953 errno = save_errno;
954 }
955
956 void
pintr1(bool wantnl)957 pintr1(bool wantnl)
958 {
959 sigset_t sigset, osigset;
960
961 sigemptyset(&sigset);
962 sigprocmask(SIG_BLOCK, &sigset, &osigset);
963 if (setintr) {
964 sigset = osigset;
965 sigdelset(&sigset, SIGINT);
966 sigprocmask(SIG_SETMASK, &sigset, NULL);
967 if (pjobs) {
968 pjobs = 0;
969 (void) fprintf(cshout, "\n");
970 dojobs(jobargv, NULL);
971 stderror(ERR_NAME | ERR_INTR);
972 }
973 }
974 sigdelset(&osigset, SIGCHLD);
975 sigprocmask(SIG_SETMASK, &osigset, NULL);
976 (void) fpurge(cshout);
977 (void) endpwent();
978
979 /*
980 * If we have an active "onintr" then we search for the label. Note that if
981 * one does "onintr -" then we shan't be interruptible so we needn't worry
982 * about that here.
983 */
984 if (gointr) {
985 gotolab(gointr);
986 timflg = 0;
987 blkfree(pargv);
988 pargv = NULL;
989 blkfree(gargv);
990 gargv = NULL;
991 reset();
992 }
993 else if (intty && wantnl) {
994 (void) fputc('\r', cshout);
995 (void) fputc('\n', cshout);
996 }
997 stderror(ERR_SILENT);
998 }
999
1000 /*
1001 * Process is the main driving routine for the shell.
1002 * It runs all command processing, except for those within { ... }
1003 * in expressions (which is run by a routine evalav in sh.exp.c which
1004 * is a stripped down process), and `...` evaluation which is run
1005 * also by a subset of this code in sh.glob.c in the routine backeval.
1006 *
1007 * The code here is a little strange because part of it is interruptible
1008 * and hence freeing of structures appears to occur when none is necessary
1009 * if this is ignored.
1010 *
1011 * Note that if catch is not set then we will unwind on any error.
1012 * If an end-of-file occurs, we return.
1013 */
1014 static struct command *savet = NULL;
1015 void
process(bool catch)1016 process(bool catch)
1017 {
1018 jmp_buf osetexit;
1019 struct command *t = savet;
1020 sigset_t sigset;
1021
1022 savet = NULL;
1023 getexit(osetexit);
1024 for (;;) {
1025 pendjob();
1026 paraml.next = paraml.prev = ¶ml;
1027 paraml.word = STRNULL;
1028 (void) setexit();
1029 justpr = enterhist; /* execute if not entering history */
1030
1031 /*
1032 * Interruptible during interactive reads
1033 */
1034 if (setintr) {
1035 sigemptyset(&sigset);
1036 sigaddset(&sigset, SIGINT);
1037 sigprocmask(SIG_UNBLOCK, &sigset, NULL);
1038 }
1039
1040 /*
1041 * For the sake of reset()
1042 */
1043 freelex(¶ml);
1044 if (savet)
1045 freesyn(savet), savet = NULL;
1046
1047 if (haderr) {
1048 if (!catch) {
1049 /* unwind */
1050 doneinp = 0;
1051 resexit(osetexit);
1052 savet = t;
1053 reset();
1054 }
1055 haderr = 0;
1056 /*
1057 * Every error is eventually caught here or the shell dies. It is
1058 * at this point that we clean up any left-over open files, by
1059 * closing all but a fixed number of pre-defined files. Thus
1060 * routines don't have to worry about leaving files open due to
1061 * deeper errors... they will get closed here.
1062 */
1063 closem();
1064 continue;
1065 }
1066 if (doneinp) {
1067 doneinp = 0;
1068 break;
1069 }
1070 if (chkstop)
1071 chkstop--;
1072 if (neednote)
1073 pnote();
1074 if (intty && prompt && evalvec == 0) {
1075 mailchk();
1076 /*
1077 * If we are at the end of the input buffer then we are going to
1078 * read fresh stuff. Otherwise, we are rereading input and don't
1079 * need or want to prompt.
1080 */
1081 needprompt = aret == F_SEEK && fseekp == feobp;
1082 if (!filec && needprompt)
1083 printprompt();
1084 (void) fflush(cshout);
1085 }
1086 free(seterr);
1087 seterr = NULL;
1088
1089 /*
1090 * Echo not only on VERBOSE, but also with history expansion. If there
1091 * is a lexical error then we forego history echo.
1092 */
1093 if ((lex(¶ml) && !seterr && intty) || adrof(STRverbose)) {
1094 prlex(csherr, ¶ml);
1095 }
1096
1097 /*
1098 * The parser may lose space if interrupted.
1099 */
1100 if (setintr)
1101 sigprocmask(SIG_BLOCK, &sigset, NULL);
1102
1103 /*
1104 * Save input text on the history list if reading in old history, or it
1105 * is from the terminal at the top level and not in a loop.
1106 *
1107 * PWP: entry of items in the history list while in a while loop is done
1108 * elsewhere...
1109 */
1110 if (enterhist || (catch && intty && !whyles))
1111 savehist(¶ml);
1112
1113 /*
1114 * Print lexical error messages, except when sourcing history lists.
1115 */
1116 if (!enterhist && seterr)
1117 stderror(ERR_OLD);
1118
1119 /*
1120 * If had a history command :p modifier then this is as far as we
1121 * should go
1122 */
1123 if (justpr)
1124 reset();
1125
1126 alias(¶ml);
1127
1128 /*
1129 * Parse the words of the input into a parse tree.
1130 */
1131 savet = syntax(paraml.next, ¶ml, 0);
1132 if (seterr)
1133 stderror(ERR_OLD);
1134
1135 execute(savet, (tpgrp > 0 ? tpgrp : -1), NULL, NULL);
1136
1137 /*
1138 * Made it!
1139 */
1140 freelex(¶ml);
1141 freesyn((struct command *) savet), savet = NULL;
1142 }
1143 resexit(osetexit);
1144 savet = t;
1145 }
1146
1147 void
dosource(Char ** v,struct command * t)1148 dosource(Char **v, struct command *t)
1149 {
1150 Char *f;
1151 bool hflg = 0;
1152 Char buf[BUFSIZ];
1153 char sbuf[BUFSIZ];
1154
1155 v++;
1156 if (*v && eq(*v, STRmh)) {
1157 if (*++v == NULL)
1158 stderror(ERR_NAME | ERR_HFLAG);
1159 hflg++;
1160 }
1161 (void) Strlcpy(buf, *v, sizeof buf/sizeof(Char));
1162 f = globone(buf, G_ERROR);
1163 (void) strlcpy(sbuf, short2str(f), sizeof sbuf);
1164 free(f);
1165 if (!srcfile(sbuf, 0, hflg) && !hflg)
1166 stderror(ERR_SYSTEM, sbuf, strerror(errno));
1167 }
1168
1169 /*
1170 * Check for mail.
1171 * If we are a login shell, then we don't want to tell
1172 * about any mail file unless its been modified
1173 * after the time we started.
1174 * This prevents us from telling the user things he already
1175 * knows, since the login program insists on saying
1176 * "You have mail."
1177 */
1178 static void
mailchk(void)1179 mailchk(void)
1180 {
1181 struct varent *v;
1182 Char **vp;
1183 time_t t;
1184 int intvl, cnt;
1185 struct stat stb;
1186 bool new;
1187
1188 v = adrof(STRmail);
1189 if (v == 0)
1190 return;
1191 (void) time(&t);
1192 vp = v->vec;
1193 cnt = blklen(vp);
1194 intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
1195 if (intvl < 1)
1196 intvl = 1;
1197 if (chktim + intvl > t)
1198 return;
1199 for (; *vp; vp++) {
1200 if (stat(short2str(*vp), &stb) == -1)
1201 continue;
1202 new = stb.st_mtime > time0.tv_sec;
1203 if (stb.st_size == 0 || stb.st_atime > stb.st_mtime ||
1204 (stb.st_atime < chktim && stb.st_mtime < chktim) ||
1205 (loginsh && !new))
1206 continue;
1207 if (cnt == 1)
1208 (void) fprintf(cshout, "You have %smail.\n", new ? "new " : "");
1209 else
1210 (void) fprintf(cshout, "%s in %s.\n", new ? "New mail" : "Mail",
1211 vis_str(*vp));
1212 }
1213 chktim = t;
1214 }
1215
1216 /*
1217 * Extract a home directory from the password file
1218 * The argument points to a buffer where the name of the
1219 * user whose home directory is sought is currently.
1220 * We write the home directory of the user back there.
1221 */
1222 int
gethdir(Char * home,int len)1223 gethdir(Char *home, int len)
1224 {
1225 Char *h;
1226 struct passwd *pw;
1227
1228 /*
1229 * Is it us?
1230 */
1231 if (*home == '\0') {
1232 if ((h = value(STRhome)) != NULL) {
1233 if (Strlcpy(home, h, len) >= len)
1234 return 1;
1235 return 0;
1236 }
1237 else
1238 return 1;
1239 }
1240
1241 if ((pw = getpwnam(short2str(home))) != NULL) {
1242 if (Strlcpy(home, str2short(pw->pw_dir), len) >= len)
1243 return 1;
1244 return 0;
1245 }
1246 else
1247 return 1;
1248 }
1249
1250 /*
1251 * When didfds is set, we do I/O from 0, 1, 2 otherwise from 15, 16, 17
1252 * We also check if the shell has already changed the descriptor to point to
1253 * 0, 1, 2 when didfds is set.
1254 */
1255 #define DESC(a) (*((int *) (a)) - (didfds && *((int *) a) >= FSHIN ? FSHIN : 0))
1256
1257 static int
readf(void * oreo,char * buf,int siz)1258 readf(void *oreo, char *buf, int siz)
1259 {
1260 return read(DESC(oreo), buf, siz);
1261 }
1262
1263
1264 static int
writef(void * oreo,const char * buf,int siz)1265 writef(void *oreo, const char *buf, int siz)
1266 {
1267 return write(DESC(oreo), buf, siz);
1268 }
1269
1270 static off_t
seekf(void * oreo,off_t off,int whence)1271 seekf(void *oreo, off_t off, int whence)
1272 {
1273 return lseek(DESC(oreo), off, whence);
1274 }
1275
1276
1277 static int
closef(void * oreo)1278 closef(void *oreo)
1279 {
1280 return close(DESC(oreo));
1281 }
1282
1283
1284 /*
1285 * Print the visible version of a string.
1286 */
1287 int
vis_fputc(int ch,FILE * fp)1288 vis_fputc(int ch, FILE *fp)
1289 {
1290 char uenc[5]; /* 4 + NUL */
1291
1292 if (ch & QUOTE)
1293 return fputc(ch & TRIM, fp);
1294 (void) vis(uenc, ch & TRIM, VIS_NOSLASH, 0);
1295 return fputs(uenc, fp);
1296 }
1297
1298 /*
1299 * Move the initial descriptors to their eventual
1300 * resting places, closing all other units.
1301 */
1302 void
initdesc(void)1303 initdesc(void)
1304 {
1305
1306 didfds = 0; /* 0, 1, 2 aren't set up */
1307 (void) fcntl(SHIN = dcopy(0, FSHIN), F_SETFD, FD_CLOEXEC);
1308 (void) fcntl(SHOUT = dcopy(1, FSHOUT), F_SETFD, FD_CLOEXEC);
1309 (void) fcntl(SHERR = dcopy(2, FSHERR), F_SETFD, FD_CLOEXEC);
1310 (void) fcntl(OLDSTD = dcopy(SHIN, FOLDSTD), F_SETFD, FD_CLOEXEC);
1311 closem();
1312 }
1313
1314
1315 void
xexit(int i)1316 xexit(int i)
1317 {
1318 untty();
1319 _exit(i);
1320 }
1321
1322 static Char **
defaultpath(void)1323 defaultpath(void)
1324 {
1325 char *ptr;
1326 Char **blk, **blkp;
1327 struct stat stb;
1328
1329 blkp = blk = xreallocarray(NULL, 10, sizeof(Char *));
1330
1331 #define DIRAPPEND(a) \
1332 if (stat(ptr = a, &stb) == 0 && S_ISDIR(stb.st_mode)) \
1333 *blkp++ = SAVE(ptr)
1334
1335 DIRAPPEND(_PATH_BIN);
1336 DIRAPPEND(_PATH_USRBIN);
1337
1338 #undef DIRAPPEND
1339
1340 *blkp = NULL;
1341 return (blk);
1342 }
1343
1344 void
printprompt(void)1345 printprompt(void)
1346 {
1347 Char *cp;
1348
1349 if (!whyles) {
1350 for (cp = value(STRprompt); *cp; cp++)
1351 if (*cp == HIST)
1352 (void) fprintf(cshout, "%d", eventno + 1);
1353 else if (*cp == '%' && *(cp + 1) == 'm') {
1354 char hostname[HOST_NAME_MAX + 1];
1355 char *p;
1356
1357 gethostname(hostname, sizeof hostname);
1358 if ((p = strchr(hostname, '.')) != NULL)
1359 *p = '\0';
1360 fprintf(cshout, "%s", hostname);
1361 cp++;
1362 } else {
1363 if (*cp == '\\' && cp[1] == HIST)
1364 cp++;
1365 (void) vis_fputc(*cp | QUOTE, cshout);
1366 }
1367 }
1368 else
1369 /*
1370 * Prompt for forward reading loop body content.
1371 */
1372 (void) fprintf(cshout, "? ");
1373 (void) fflush(cshout);
1374 }
1375