2 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
3 * Copyright (c) 2016 by Delphix. All rights reserved.
6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
10 * Copyright (c) 1980 Regents of the University of California.
11 * All rights reserved. The Berkeley Software License Agreement
12 * specifies the terms and conditions for redistribution.
17 /* #include <sys/ioctl.h> */
19 #include <sys/filio.h>
20 #include "sh.tconst.h"
28 * We use these csh(1) private versions of the select macros, (see select(3C))
29 * so as not to be limited by the size of struct fd_set (ie 1024).
31 #define CSH_FD_SET(n, p) ((*((p) + ((n)/NFDBITS))) |= (1 << ((n) % NFDBITS)))
32 #define CSH_FD_CLR(n, p) ((*((p) + ((n)/NFDBITS))) &= ~(1 << ((n) % NFDBITS)))
33 #define CSH_FD_ISSET(n, p) ((*((p) + ((n)/NFDBITS))) & (1 << ((n) % NFDBITS)))
34 #define CSH_FD_ZERO(p, n) memset((void *)(p), 0, (n))
36 tchar
*pathlist
[] = { S_usrbin
/* "/usr/bin" */, S_DOT
/* "." */, 0 };
37 tchar
*dumphist
[] = { S_history
/* "history" */, S_h
/* "-h" */, 0, 0 };
38 tchar
*loadhist
[] = { S_source
/* "source" */, S_h
/* "-h" */,
39 S_NDOThistory
/* "~/.history" */, 0 };
52 extern gid_t
getegid(), getgid();
53 extern uid_t
geteuid(), getuid();
54 extern tchar
**strblktotsblk(/* char **, int */);
56 extern void hupforegnd(void);
57 void interactive_hup(void);
58 void interactive_login_hup(void);
60 void importpath(tchar
*);
61 void srccat(tchar
*, tchar
*);
62 void srccat_inlogin(tchar
*, tchar
*);
63 void srcunit(int, bool, bool);
68 void dosource(tchar
**);
70 void printprompt(void);
71 void sigwaiting(void);
73 void initdesc(int, char *[]);
74 void initdesc_x(int, char *[], int);
84 #define TRACEFILE "/tmp/trace.XXXXXX"
87 * Initialize trace file.
96 strcpy(name
, TRACEFILE
);
98 trace
= fopen(p
, "w");
102 * write message to trace file
106 tprintf(fmt
, a
, b
, c
, d
, e
, f
, g
, h
, i
, j
)
110 fprintf(trace
, fmt
, a
, b
, c
, d
, e
, f
, g
, h
, i
, j
);
117 main(int c
, char **av
)
123 tchar s_prompt
[MAXHOSTNAMELEN
+3];
125 int c_max_var_len_size
;
128 * set up the error exit, if there is an error before
129 * this is done, it will core dump, and we don't
130 * tolerate core dumps
136 * if were here, there was an error in the csh
137 * startup so just punt
139 printf("csh startup error, csh exiting...\n");
145 (void) setlocale(LC_ALL
, "");
146 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
147 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
149 (void) textdomain(TEXT_DOMAIN
);
152 v
= strblktotsblk(av
, c
);
155 * Initialize paraml list
157 paraml
.next
= paraml
.prev
= ¶ml
;
159 settimes(); /* Immed. estab. timing base */
161 if (eq(v
[0], S_aout
/* "a.out" */)) /* A.out's are quittable */
164 loginsh
= **v
== '-';
166 (void) time(&chktim
);
169 * Move the descriptors to safe places.
170 * The variable didfds is 0 while we have only FSH* to work with.
171 * When didfds is true, we have 0,1,2 and prefer to use these.
173 * Also, setup data for csh internal file descriptor book keeping.
178 * Initialize the shell variables.
179 * ARGV and PROMPT are initialized later.
180 * STATUS is also munged in several places.
181 * CHILD is munged when forking/waiting
184 c_max_var_len_size
= snprintf(NULL
, 0, "%ld", MAX_VAR_LEN
);
185 c_max_var_len
= (char *)xalloc(c_max_var_len_size
+ 1);
186 (void) snprintf(c_max_var_len
, (c_max_var_len_size
+ 1),
188 set(S_SUNW_VARLEN
, strtots(NOSTR
, c_max_var_len
));
189 xfree(c_max_var_len
);
191 /* don't do globbing here, just set exact copies */
194 set(S_status
/* "status" */, S_0
/* "0" */);
195 dinit(cp
= getenvs_("HOME")); /* dinit thinks that HOME==cwd in a */
198 fast
++; /* No home -> can't read scripts */
200 if (strlen_(cp
) >= BUFSIZ
- 10) {
203 printf("%s\n", gettext("Pathname too long"));
204 set(S_home
/* "home" */, savestr(cp
));
205 local_setenv(S_HOME
, savestr(cp
));
207 set(S_home
/* "home" */, savestr(cp
));
210 * Grab other useful things from the environment.
211 * Should we grab everything??
213 if ((cp
= getenvs_("USER")) != NOSTR
)
214 set(S_user
/* "user" */, savestr(cp
));
217 * If USER is not defined, set it here.
220 pw
= getpwuid(getuid());
223 set(S_user
, strtots((tchar
*)0, pw
->pw_name
));
224 local_setenv(S_USER
, strtots((tchar
*)0, pw
->pw_name
));
225 } else if (loginsh
) { /* Give up setting USER variable. */
226 printf("Warning: USER environment variable could not be set.\n");
229 if ((cp
= getenvs_("TERM")) != NOSTR
)
230 set(S_term
/* "term" */, savestr(cp
));
232 * Re-initialize path if set in environment
234 if ((cp
= getenvs_("PATH")) == NOSTR
)
235 set1(S_path
/* "path" */, saveblk(pathlist
), &shvhed
);
238 set(S_shell
/* "shell" */, S_SHELLPATH
);
240 doldol
= putn(getpid()); /* For $$ */
242 /* restore globbing until the user says otherwise */
246 * Record the interrupt states from the parent process.
247 * If the parent is non-interruptible our hand must be forced
248 * or we (and our children) won't be either.
249 * Our children inherit termination from our parent.
250 * We catch it only if we are the login shell.
252 /* parents interruptibility */
253 (void) sigvec(SIGINT
, NULL
, &osv
);
254 parintr
= osv
.sv_handler
;
255 /* parents terminability */
256 (void) sigvec(SIGTERM
, NULL
, &osv
);
257 parterm
= osv
.sv_handler
;
259 _signal(SIGLWP
, siglwp
);
260 _signal(SIGWAITING
, sigwaiting
);
262 (void) signal(SIGHUP
, phup
); /* exit processing on HUP */
263 (void) signal(SIGXCPU
, phup
); /* ...and on XCPU */
264 (void) signal(SIGXFSZ
, phup
); /* ...and on XFSZ */
268 * Process the arguments.
270 * Note that processing of -v/-x is actually delayed till after
274 while (c
> 0 && (cp
= v
[0])[0] == '-' && *++cp
!= '\0' && !batch
) {
277 case 'b': /* -b Next arg is input file */
281 case 'c': /* -c Command input from arg */
291 case 'e': /* -e Exit on any error */
295 case 'f': /* -f Fast start */
299 case 'i': /* -i Interactive, even if !intty */
304 case 'n': /* -n Don't execute */
308 case 'q': /* -q (Undoc'd) ... die on quit */
312 case 's': /* -s Read from std input */
316 case 't': /* -t Read one line from input */
322 case 'T': /* -T trace switch on */
327 case 'v': /* -v Echo hist expanded input */
328 nverbose
= 1; /* ... later */
331 case 'x': /* -x Echo just before execution */
332 nexececho
= 1; /* ... later */
335 case 'V': /* -V Echo hist expanded input */
336 setNS(S_verbose
/* "verbose" */); /* NOW! */
339 case 'X': /* -X Echo just before execution */
340 setNS(S_echo
/* "echo" */); /* NOW! */
347 if (quitit
) /* With all due haste, for debugging */
348 (void) signal(SIGQUIT
, SIG_DFL
);
351 * Unless prevented by -c, -i, -s, or -t, if there
352 * are remaining arguments the first of them is the name
353 * of a shell file from which to read commands.
355 if (!batch
&& (uid
!= geteuid() || getgid() != getegid())) {
357 child
++; /* So this ... */
358 Perror(S_csh
/* "csh" */); /* ... doesn't return */
361 if (nofile
== 0 && c
> 0) {
362 nofile
= open_(v
[0], 0);
364 child
++; /* So this ... */
365 Perror(v
[0]); /* ... doesn't return */
368 SHIN
= dmove(nofile
, FSHIN
); /* Replace FSHIN */
369 (void) fcntl(SHIN
, F_SETFD
, 1);
375 * Consider input a tty if it really is or we are interactive.
377 intty
= intact
|| isatty(SHIN
);
380 * Decide whether we should play with signals or not.
381 * If we are explicitly told (via -i, or -) or we are a login
382 * shell (arg0 starts with -) or the input and output are both
383 * the ttys("csh", or "csh</dev/ttyx>/dev/ttyx")
384 * Note that in only the login shell is it likely that parent
385 * may have set signals to be ignored
387 if (loginsh
|| intact
|| intty
&& isatty(SHOUT
))
393 * Save the remaining arguments in argv.
395 setq(S_argv
/* "argv" */, copyblk(v
), &shvhed
);
401 gethostname_(s_prompt
, MAXHOSTNAMELEN
);
403 uid
== 0 ? S_SHARPSP
/* "# " */ : S_PERSENTSP
/* "% " */);
404 set(S_prompt
/* "prompt" */, s_prompt
);
408 * If we are an interactive shell, then start fiddling
409 * with the signals; this is a tricky game.
415 if (!quitit
) /* Wary! */
416 (void) signal(SIGQUIT
, SIG_IGN
);
417 (void) signal(SIGINT
, pintr
);
418 (void) sigblock(sigmask(SIGINT
));
419 (void) signal(SIGTERM
, SIG_IGN
);
422 * Explicitly terminate foreground jobs and exit if we are
426 (void) signal(SIGHUP
, interactive_login_hup
);
428 (void) signal(SIGHUP
, interactive_hup
);
431 if (quitit
== 0 && arginp
== 0) {
432 (void) signal(SIGTSTP
, SIG_IGN
);
433 (void) signal(SIGTTIN
, SIG_IGN
);
434 (void) signal(SIGTTOU
, SIG_IGN
);
436 * Wait till in foreground, in case someone
439 * dont want to try to grab away the tty.
443 else if (isatty(FSHOUT
))
445 else if (isatty(OLDSTD
))
450 if (ioctl(f
, TIOCGPGRP
, (char *)&tpgrp
) == 0 &&
452 if (tpgrp
!= shpgrp
) {
453 void (*old
)() = (void (*)())
454 signal(SIGTTIN
, SIG_DFL
);
455 (void) kill(0, SIGTTIN
);
456 (void) signal(SIGTTIN
, old
);
462 (void) setpgid(0, shpgrp
);
463 (void) ioctl(f
, TIOCSPGRP
, (char *)&shpgrp
);
464 (void) fcntl(dcopy(f
, FSHTTY
), F_SETFD
, 1);
467 printf("Warning: no access to tty; thus no job control in this shell...\n");
472 if (setintr
== 0 && parintr
== SIG_DFL
)
476 * Set SIGCHLD handler, making sure that reads restart after it runs.
478 sigemptyset(&sa
.sa_mask
);
479 sa
.sa_handler
= pchild
;
480 sa
.sa_flags
= SA_RESTART
;
481 (void) sigaction(SIGCHLD
, &sa
, NULL
);
484 * Set an exit here in case of an interrupt or error reading
485 * the shell start-up scripts.
488 haderr
= 0; /* In case second time through */
489 if (!fast
&& reenter
== 0) {
493 * If this is a login csh, and /etc/.login exists,
494 * source /etc/.login first.
497 tchar tmp_etc
[4+1]; /* strlen("/etc")+1 */
498 tchar tmp_login
[7+1]; /* strlen("/.login")+1 */
500 strtots(tmp_etc
, "/etc");
501 strtots(tmp_login
, "/.login");
502 srccat_inlogin(tmp_etc
, tmp_login
);
505 /* Will have value("home") here because set fast if don't */
506 srccat(value(S_home
/* "home" */),
507 S_SLADOTcshrc
/* "/.cshrc" */);
510 if (!fast
&& !arginp
&& !onelflg
&& !havhash
)
515 * Reconstruct the history list now, so that it's
516 * available from within .login.
520 srccat_inlogin(value(S_home
/* "home" */),
521 S_SLADOTlogin
/* "/.login" */);
525 * To get cdpath hashing $cdpath must have a
526 * value, not $CDPATH. So if after reading
527 * the startup files ( .cshrc ), and
528 * user has specified a value for cdpath, then
529 * cache $cdpath paths. xhash2 is global array
530 * for $cdpath caching.
532 if (!fast
&& !arginp
&& !onelflg
&& !havhash2
)
537 * Now are ready for the -v and -x flags
540 setNS(S_verbose
/* "verbose" */);
542 setNS(S_echo
/* "echo" */);
545 * All the rest of the world is inside this call.
546 * The argument to process indicates whether it should
547 * catch "error unwinds". Thus if we are a interactive shell
548 * our call here will never return by being blown past on an error.
557 (void) close(SHIN
); /* No need for unsetfd(). */
570 (void) setpgid(0, opgrp
);
571 (void) ioctl(FSHTTY
, TIOCSPGRP
, (char *)&opgrp
);
576 importpath(tchar
*cp
)
582 static tchar dot
[2] = {'.', 0};
584 for (dp
= cp
; *dp
; dp
++)
588 * i+2 where i is the number of colons in the path.
589 * There are i+1 directories in the path plus we need
590 * room for a zero terminator.
592 pv
= (tchar
**)xcalloc((unsigned)(i
+ 2), sizeof (tchar
**));
597 if ((c
= *dp
) == ':' || c
== 0) {
599 pv
[i
++] = savestr(*cp
? cp
: dot
);
609 set1(S_path
/* "path" */, pv
, &shvhed
);
613 * Source to the file which is the catenation of the argument names.
616 srccat(tchar
*cp
, tchar
*dp
)
618 tchar
*ep
= strspl(cp
, dp
);
619 int unit
= dmove(open_(ep
, 0), -1);
621 (void) fcntl(unit
, F_SETFD
, 1);
631 * Source to the file which is the catenation of the argument names.
632 * This one does not check the ownership.
635 srccat_inlogin(tchar
*cp
, tchar
*dp
)
637 tchar
*ep
= strspl(cp
, dp
);
638 int unit
= dmove(open_(ep
, 0), -1);
640 (void) fcntl(unit
, F_SETFD
, 1);
646 * Source to a unit. If onlyown it must be our file or our group or
647 * we don't chance it. This occurs on ".cshrc"s and the like.
650 srcunit(int unit
, bool onlyown
, bool hflg
)
652 /* We have to push down a lot of state here */
653 /* All this could go into a structure */
654 int oSHIN
= -1, oldintty
= intty
;
655 struct whyle
*oldwhyl
= whyles
;
656 tchar
*ogointr
= gointr
, *oarginp
= arginp
;
657 tchar
*oevalp
= evalp
, **oevalvec
= evalvec
;
658 int oonelflg
= onelflg
;
659 bool oenterhist
= enterhist
;
662 bool otell
= cantell
;
666 /* The (few) real local variables */
677 if (fstat(unit
, &stb
) < 0 ||
678 (stb
.st_uid
!= uid
&& stb
.st_gid
!= getgid())) {
686 * There is a critical section here while we are pushing down the
687 * input stream since we have stuff in different structures.
688 * If we weren't careful an interrupt could corrupt SHIN's Bin
689 * structure and kill the shell.
691 * We could avoid the critical region by grouping all the stuff
692 * in a single structure and pointing at it to move it all at
693 * once. This is less efficient globally on many variable references
699 omask
= sigblock(sigmask(SIGINT
));
703 /* Setup the new values of the state stuff saved above */
704 copy((char *)&saveB
, (char *)&B
, sizeof (saveB
));
706 fseekp
= feobp
= fblocks
= 0;
707 oSHIN
= SHIN
, SHIN
= unit
, arginp
= 0, onelflg
= 0;
708 intty
= isatty(SHIN
), whyles
= 0, gointr
= 0;
709 evalvec
= 0; evalp
= 0;
714 * Now if we are allowing commands to be interrupted,
715 * we let ourselves be interrupted.
718 (void) sigsetmask(omask
);
722 process(0); /* 0 -> blow away on errors */
725 (void) sigsetmask(omask
);
729 /* We made it to the new state... free up its storage */
730 /* This code could get run twice but xfree doesn't care */
731 for (i
= 0; i
< fblocks
; i
++)
735 /* Reset input arena */
736 copy((char *)&B
, (char *)&saveB
, sizeof (B
));
738 (void) close(SHIN
), SHIN
= oSHIN
;
740 arginp
= oarginp
, onelflg
= oonelflg
;
741 evalp
= oevalp
, evalvec
= oevalvec
;
742 intty
= oldintty
, whyles
= oldwhyl
, gointr
= ogointr
;
745 enterhist
= oenterhist
;
753 * If process reset() (effectively an unwind) then
754 * we must also unwind.
764 int fp
, ftmp
, oldidfds
;
767 if (value(S_savehist
/* "savehist" */)[0] == '\0')
769 (void) strcpy_(buf
, value(S_home
/* "home" */));
770 (void) strcat_(buf
, S_SLADOThistory
/* "/.history" */);
771 fp
= creat_(buf
, 0666);
778 (void) strcpy_(buf
, value(S_savehist
/* "savehist" */));
792 (void) signal(SIGQUIT
, SIG_IGN
);
793 (void) signal(SIGINT
, SIG_IGN
);
794 (void) signal(SIGTERM
, SIG_IGN
);
795 setintr
= 0; /* No interrupts after "logout" */
796 if (adrof(S_home
/* "home" */))
797 srccat(value(S_home
/* "home" */),
798 S_SLADOTlogout
/* "/.logout" */);
812 * Note that if STATUS is corrupted (i.e. getn bombs)
813 * then error will exit directly because we poke child here.
814 * Otherwise we might continue unwarrantedly (sic).
818 exit(getn(value(S_status
/* "status" */)));
822 * in the event of a HUP we want to save the history
832 interactive_hup(void)
839 interactive_login_hup(void)
846 tchar
*jobargv
[2] = { S_jobs
/* "jobs" */, 0 };
848 * Catch an interrupt, e.g. during lexical input.
849 * If we are an interactive shell, we reset the interrupt catch
850 * immediately. In any case we drain the shell output,
851 * and finally go through the normal error mechanism, which
852 * gets a chance to make the shell go away.
868 (void) sigsetmask(omask
& ~sigmask(SIGINT
));
873 bferr("Interrupted");
876 (void) sigsetmask(omask
& ~sigmask(SIGCHLD
));
880 * If we have an active "onintr" then we search for the label.
881 * Note that if one does "onintr -" then we shan't be interruptible
882 * so we needn't worry about that here.
885 search(ZGOTO
, 0, gointr
);
888 pargv
= 0, blkfree(v
);
890 gargv
= 0, blkfree(v
);
892 } else if (intty
&& wantnl
)
893 printf("\n"); /* Some like this, others don't */
898 * Process is the main driving routine for the shell.
899 * It runs all command processing, except for those within { ... }
900 * in expressions (which is run by a routine evalav in sh.exp.c which
901 * is a stripped down process), and `...` evaluation which is run
902 * also by a subset of this code in sh.glob.c in the routine backeval.
904 * The code here is a little strange because part of it is interruptible
905 * and hence freeing of structures appears to occur when none is necessary
906 * if this is ignored.
908 * Note that if catch is not set then we will unwind on any error.
909 * If an end-of-file occurs, we return.
921 paraml
.next
= paraml
.prev
= ¶ml
;
922 paraml
.word
= S_
/* "" */;
925 justpr
= enterhist
; /* execute if not entering history */
928 * Interruptible during interactive reads
931 (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT
));
934 * For the sake of reset()
936 freelex(¶ml
), freesyn(t
), t
= 0;
947 * Every error is eventually caught here or
948 * the shell dies. It is at this
949 * point that we clean up any left-over open
950 * files, by closing all but a fixed number
951 * of pre-defined files. Thus routines don't
952 * have to worry about leaving files open due
953 * to deeper errors... they will get closed here.
966 if (intty
&& prompt
&& evalvec
== 0) {
969 * If we are at the end of the input buffer
970 * then we are going to read fresh stuff.
971 * Otherwise, we are rereading input and don't
972 * need or want to prompt.
980 * Echo not only on VERBOSE, but also with history expansion.
982 if (lex(¶ml
) && intty
||
983 adrof(S_verbose
/* "verbose" */)) {
990 * The parser may lose space if interrupted.
993 (void) sigblock(sigmask(SIGINT
));
996 * Save input text on the history list if
997 * reading in old history, or it
998 * is from the terminal at the top level and not
1001 if (enterhist
|| catch && intty
&& !whyles
)
1005 * Print lexical error messages, except when sourcing
1008 if (!enterhist
&& err
)
1009 error("%s", gettext(err
));
1012 * If had a history command :p modifier then
1013 * this is as far as we should go
1021 * Parse the words of the input into a parse tree.
1023 t
= syntax(paraml
.next
, ¶ml
, 0);
1025 error("%s", gettext(err
));
1028 * Execute the parse tree
1032 * POSIX requires SIGCHLD to be held
1033 * until all processes have joined the
1034 * process group in order to avoid race
1039 omask
= sigblock(sigmask(SIGCHLD
));
1041 (void) sigsetmask(omask
&~ sigmask(SIGCHLD
));
1045 error("%s", gettext(err
));
1049 freelex(¶ml
), freesyn(t
);
1063 if (*t
&& eq(*t
, S_h
/* "-h" */)) {
1065 bferr("Too few arguments.");
1068 (void) strcpy_(buf
, *t
);
1070 u
= dmove(open_(f
, 0), -1);
1075 (void) fcntl(u
, F_SETFD
, 1);
1076 srcunit(u
, 0, hflg
);
1081 * If we are a login shell, then we don't want to tell
1082 * about any mail file unless its been modified
1083 * after the time we started.
1084 * This prevents us from telling the user things they already
1085 * know, since the login program insists on saying
1098 v
= adrof(S_mail
/* "mail" */);
1104 intvl
= (cnt
&& number(*vp
)) ? (--cnt
, getn(*vp
++)) : MAILINTVL
;
1107 if (chktim
+ intvl
> t
)
1110 if (stat_(*vp
, &stb
) < 0)
1112 new = stb
.st_mtime
> time0
.tv_sec
;
1113 if (stb
.st_size
== 0 || stb
.st_atime
>= stb
.st_mtime
||
1114 (stb
.st_atime
<= chktim
&& stb
.st_mtime
<= chktim
) ||
1118 printf("You have %smail.\n", new ? "new " : "");
1120 printf("%s in %t.\n", new ? "New mail" : "Mail", *vp
);
1126 * Extract a home directory from the password file
1127 * The argument points to a buffer where the name of the
1128 * user whose home directory is sought is currently.
1129 * We write the home directory of the user back there.
1132 gethdir(tchar
*home
)
1134 /* getpwname will not be modified, so we need temp. buffer */
1135 char home_str
[BUFSIZ
];
1136 tchar home_ts
[BUFSIZ
];
1137 struct passwd
*pp
/* = getpwnam(home) */;
1139 pp
= getpwnam(tstostr(home_str
, home
));
1142 (void) strcpy_(home
, strtots(home_ts
, pp
->pw_dir
));
1168 * Print the prompt string
1170 for (cp
= value(S_prompt
/* "prompt" */); *cp
; cp
++)
1172 printf("%d", eventno
+ 1);
1174 if (*cp
== '\\' && cp
[1] == HIST
)
1176 Putchar(*cp
| QUOTE
);
1180 * Prompt for forward reading loop
1188 * Save char * block.
1191 strblktotsblk(char **v
, int num
)
1194 (tchar
**)xcalloc((unsigned)(num
+ 1), sizeof (tchar
**));
1195 tchar
**onewv
= newv
;
1198 *newv
++ = strtots(NOSTR
, *v
++);
1206 _signal(SIGWAITING
, sigwaiting
);
1212 _signal(SIGLWP
, siglwp
);
1217 * Following functions and data are used for csh to do its
1218 * file descriptors book keeping.
1221 static int *fdinuse
= NULL
; /* The list of files opened by csh */
1222 static int nbytesused
= 0; /* no of bytes allocated to fdinuse */
1223 static int max_fd
= 0; /* The maximum descriptor in fdinuse */
1224 static int my_pid
; /* The process id set in initdesc() */
1225 static int NoFile
= NOFILE
; /* The number of files I can use. */
1228 * Get the number of files this csh can use.
1230 * Move the initial descriptors to their eventual
1231 * resting places, closing all other units.
1233 * Also, reserve 0/1/2, so NIS+ routines do not get
1234 * hold of them. And initialize fdinuse list and set
1235 * the current process id.
1237 * If this csh was invoked from setuid'ed script file,
1238 * do not close the third argument passed. The file
1239 * must be one of /dev/fd/0,1,2,,,
1240 * (execv() always passes three arguments when it execs a script
1241 * file in a form of #! /bin/csh -b.)
1243 * If is_reinit is set in initdesc_x(), then we only close the file
1244 * descriptors that we actually opened (as recorded in fdinuse).
1247 initdesc(int argc
, char *argv
[])
1249 initdesc_x(argc
, argv
, 0);
1253 reinitdesc(int argc
, char *argv
[])
1255 initdesc_x(argc
, argv
, 1);
1259 * Callback functions for closing all file descriptors.
1262 close_except(void *cd
, int fd
)
1264 int script_fd
= *(int *)cd
;
1266 if (fd
>= 3 && fd
< NoFile
&& fd
!= script_fd
)
1272 close_inuse(void *cd
, int fd
)
1274 int script_fd
= *(int *)cd
;
1276 if (fd
>= 3 && fd
< NoFile
&& fd
!= script_fd
&&
1277 CSH_FD_ISSET(fd
, fdinuse
)) {
1285 initdesc_x(int argc
, char *argv
[], int is_reinit
)
1293 * Get pid of this shell
1298 * Get the hard limit numbers of descriptors
1301 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == 0)
1302 NoFile
= rlp
.rlim_cur
;
1305 * If this csh was invoked for executing setuid script file,
1306 * the third argument passed is the special file name
1307 * which should not be closed. This special file name is
1308 * in the form /dev/fd/X.
1311 if (sscanf(argv
[2], "/dev/fd/%d", &script_fd
) != 1)
1314 /* Make sure to close this file on exec. */
1315 fcntl(script_fd
, F_SETFD
, 1);
1317 if (fdinuse
== NULL
) {
1318 nbytesused
= sizeof (int) *
1319 howmany(NoFile
, sizeof (int) * NBBY
);
1320 fdinuse
= (int *)xalloc(nbytesused
);
1324 * Close all files except 0/1/2 to get a clean
1325 * file descritor space.
1328 (void) fdwalk(close_except
, &script_fd
);
1330 (void) fdwalk(close_inuse
, &script_fd
);
1332 didfds
= 0; /* 0, 1, 2 aren't set up */
1334 if (fstat(0, &buf
) < 0)
1335 open("/dev/null", O_RDONLY
);
1337 (void) fcntl(SHIN
= dcopy(0, FSHIN
), F_SETFD
, 1);
1338 (void) fcntl(SHOUT
= dcopy(1, FSHOUT
), F_SETFD
, 1);
1339 (void) fcntl(SHDIAG
= dcopy(2, FSHDIAG
), F_SETFD
, 1);
1340 (void) fcntl(OLDSTD
= dcopy(SHIN
, FOLDSTD
), F_SETFD
, 1);
1343 * Open 0/1/2 to avoid Nis+ functions to pick them up.
1344 * Now, 0/1/2 are saved, close them and open them.
1346 close(0); close(1); close(2);
1347 open("/dev/null", O_RDONLY
);
1355 CSH_FD_ZERO(fdinuse
, nbytesused
);
1359 * This routine is called after an error to close up
1360 * any units which may have been left open accidentally.
1362 * You only need to remove files in fdinuse list.
1363 * After you have removed the files, you can clear the
1371 for (f
= 3; f
<= max_fd
; f
++) {
1372 if (CSH_FD_ISSET(f
, fdinuse
) &&
1373 f
!= SHIN
&& f
!= SHOUT
&& f
!= SHDIAG
&&
1374 f
!= OLDSTD
&& f
!= FSHTTY
)
1377 CSH_FD_ZERO(fdinuse
, nbytesused
);
1382 * Reset my_pid when a new process is created. Only call this
1383 * if you want the process to affect fdinuse (e.g., fork, but
1394 * Whenever Csh open/create/dup/pipe a file or files,
1395 * Csh keeps track of its open files. The open files
1396 * are kept in "fdinuse, Fd In Use" list.
1398 * When a file descriptor is newly allocated, setfd() is
1399 * used to mark the fact in "fdinuse" list.
1401 * fd = open("newfile", O_RDONLY);
1404 * When a file is freed by close() function, unsetfd() is
1405 * used to remove the fd from "fdinuse" list.
1414 * Because you want to avoid
1415 * conflict due to vfork().
1417 if (my_pid
!= getpid())
1420 if (fd
>= NoFile
|| fd
< 0)
1425 CSH_FD_SET(fd
, fdinuse
);
1434 * Because you want to avoid
1435 * conflict due to vfork().
1437 if (my_pid
!= getpid())
1440 if (fd
>= NoFile
|| fd
< 0)
1443 CSH_FD_CLR(fd
, fdinuse
);
1445 for (i
= max_fd
-1; i
>= 3; i
--)
1446 if (CSH_FD_ISSET(i
, fdinuse
)) {