1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29 /*
30 * UNIX shell
31 */
32
33 #include "defs.h"
34 #include "sym.h"
35 #include "timeout.h"
36 #include <stdio.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/wait.h>
40 #include "dup.h"
41
42 #ifdef RES
43 #include <sgtty.h>
44 #endif
45
46 pid_t mypid, mypgid, mysid;
47
48 static BOOL beenhere = FALSE;
49 unsigned char tmpout[TMPOUTSZ];
50 struct fileblk stdfile;
51 struct fileblk *standin = &stdfile;
52 int mailchk = 0;
53
54 static unsigned char *mailp;
55 static long *mod_time = 0;
56 static BOOL login_shell = FALSE;
57
58 #if vax
59 char **execargs = (char **)(0x7ffffffc);
60 #endif
61
62 #if pdp11
63 char **execargs = (char **)(-2);
64 #endif
65
66
67 static void exfile();
68 extern unsigned char *simple();
69 static void Ldup(int, int);
70 void settmp(void);
71 void chkmail(void);
72 void setmail(unsigned char *);
73
74 int
main(int c,char * v[],char * e[])75 main(int c, char *v[], char *e[])
76 {
77 int rflag = ttyflg;
78 int rsflag = 1; /* local restricted flag */
79 unsigned char *flagc = flagadr;
80 struct namnod *n;
81
82 mypid = getpid();
83 mypgid = getpgid(mypid);
84 mysid = getsid(mypid);
85
86 /*
87 * Do locale processing only if /usr is mounted.
88 */
89 localedir_exists = (access(localedir, F_OK) == 0);
90
91 /*
92 * initialize storage allocation
93 */
94
95 if (stakbot == 0) {
96 addblok((unsigned)0);
97 }
98
99 /*
100 * If the first character of the last path element of v[0] is "-"
101 * (ex. -sh, or /bin/-sh), this is a login shell
102 */
103 if (*simple(v[0]) == '-') {
104 signal(SIGXCPU, SIG_DFL);
105 signal(SIGXFSZ, SIG_DFL);
106
107 /*
108 * As the previous comment states, this is a login shell.
109 * Therefore, we set the login_shell flag to explicitly
110 * indicate this condition.
111 */
112 login_shell = TRUE;
113 }
114
115 stdsigs();
116
117 /*
118 * set names from userenv
119 */
120
121 setup_env();
122
123 /*
124 * LC_MESSAGES is set here so that early error messages will
125 * come out in the right style.
126 * Note that LC_CTYPE is done later on and is *not*
127 * taken from the previous environ
128 */
129
130 /*
131 * Do locale processing only if /usr is mounted.
132 */
133 if (localedir_exists)
134 (void) setlocale(LC_ALL, "");
135 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
136 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
137 #endif
138 (void) textdomain(TEXT_DOMAIN);
139
140 /*
141 * 'rsflag' is zero if SHELL variable is
142 * set in environment and
143 * the simple file part of the value.
144 * is rsh
145 */
146 if (n = findnam("SHELL")) {
147 if (eq("rsh", simple(n->namval)))
148 rsflag = 0;
149 }
150
151 /*
152 * a shell is also restricted if the simple name of argv(0) is
153 * rsh or -rsh in its simple name
154 */
155
156 #ifndef RES
157
158 if (c > 0 && (eq("rsh", simple(*v)) || eq("-rsh", simple(*v))))
159 rflag = 0;
160
161 #endif
162
163 if (eq("jsh", simple(*v)) || eq("-jsh", simple(*v)))
164 flags |= monitorflg;
165
166 hcreate();
167 set_dotpath();
168
169
170 /*
171 * look for options
172 * dolc is $#
173 */
174 dolc = options(c, v);
175
176 if (dolc < 2) {
177 flags |= stdflg;
178 {
179
180 while (*flagc)
181 flagc++;
182 *flagc++ = STDFLG;
183 *flagc = 0;
184 }
185 }
186 if ((flags & stdflg) == 0)
187 dolc--;
188
189 if ((flags & privflg) == 0) {
190 uid_t euid;
191 gid_t egid;
192 uid_t ruid;
193 gid_t rgid;
194
195 /*
196 * Determine all of the user's id #'s for this process and
197 * then decide if this shell is being entered as a result
198 * of a fork/exec.
199 * If the effective uid/gid do NOT match and the euid/egid
200 * is < 100 and the egid is NOT 1, reset the uid and gid to
201 * the user originally calling this process.
202 */
203 euid = geteuid();
204 ruid = getuid();
205 egid = getegid();
206 rgid = getgid();
207 if ((euid != ruid) && (euid < 100))
208 setuid(ruid); /* reset the uid to the orig user */
209 if ((egid != rgid) && ((egid < 100) && (egid != 1)))
210 setgid(rgid); /* reset the gid to the orig user */
211 }
212
213 dolv = (unsigned char **)v + c - dolc;
214 dolc--;
215
216 /*
217 * return here for shell file execution
218 * but not for parenthesis subshells
219 */
220 if (setjmp(subshell)) {
221 freejobs();
222 flags |= subsh;
223 }
224
225 /*
226 * number of positional parameters
227 */
228 replace(&cmdadr, dolv[0]); /* cmdadr is $0 */
229
230 /*
231 * set pidname '$$'
232 */
233 assnum(&pidadr, (long)mypid);
234
235 /*
236 * set up temp file names
237 */
238 settmp();
239
240 /*
241 * default internal field separators
242 * Do not allow importing of IFS from parent shell.
243 * setup_env() may have set anything from parent shell to IFS.
244 * Always set the default ifs to IFS.
245 */
246 assign(&ifsnod, (unsigned char *)sptbnl);
247
248 dfault(&mchknod, MAILCHECK);
249 mailchk = stoi(mchknod.namval);
250
251 /* initialize OPTIND for getopt */
252
253 n = lookup("OPTIND");
254 assign(n, (unsigned char *)"1");
255 /*
256 * make sure that option parsing starts
257 * at first character
258 */
259 _sp = 1;
260
261 if ((beenhere++) == FALSE) /* ? profile */
262 {
263 if ((login_shell == TRUE) && (flags & privflg) == 0) {
264
265 /* system profile */
266
267 #ifndef RES
268
269 if ((input = pathopen(nullstr, sysprofile)) >= 0)
270 exfile(rflag); /* file exists */
271
272 #endif
273 /* user profile */
274
275 if ((input = pathopen(homenod.namval, profile)) >= 0) {
276 exfile(rflag);
277 flags &= ~ttyflg;
278 }
279 }
280 if (rsflag == 0 || rflag == 0) {
281 if ((flags & rshflg) == 0) {
282 while (*flagc)
283 flagc++;
284 *flagc++ = 'r';
285 *flagc = '\0';
286 }
287 flags |= rshflg;
288 }
289
290 /*
291 * open input file if specified
292 */
293 if (comdiv) {
294 estabf(comdiv);
295 input = -1;
296 }
297 else
298 {
299 if (flags & stdflg) {
300 input = 0;
301 } else {
302 /*
303 * If the command file specified by 'cmdadr'
304 * doesn't exist, chkopen() will fail calling
305 * exitsh(). If this is a login shell and
306 * the $HOME/.profile file does not exist, the
307 * above statement "flags &= ~ttyflg" does not
308 * get executed and this makes exitsh() call
309 * longjmp() instead of exiting. longjmp() will
310 * return to the location specified by the last
311 * active jmpbuffer, which is the one set up in
312 * the function exfile() called after the system
313 * profile file is executed (see lines above).
314 * This would cause an infinite loop, because
315 * chkopen() will continue to fail and exitsh()
316 * to call longjmp(). To make exitsh() exit instead
317 * of calling longjmp(), we then set the flag forcexit
318 * at this stage.
319 */
320
321 flags |= forcexit;
322 input = chkopen(cmdadr, 0);
323 flags &= ~forcexit;
324 }
325
326 #ifdef ACCT
327 if (input != 0)
328 preacct(cmdadr);
329 #endif
330 comdiv--;
331 }
332 }
333 #ifdef pdp11
334 else
335 *execargs = (char *)dolv; /* for `ps' cmd */
336 #endif
337
338
339 exfile(0);
340 done(0);
341 }
342
343 static void
exfile(int prof)344 exfile(int prof)
345 {
346 time_t mailtime = 0; /* Must not be a register variable */
347 time_t curtime = 0;
348
349 /*
350 * move input
351 */
352 if (input > 0) {
353 Ldup(input, INIO);
354 input = INIO;
355 }
356
357
358 setmode(prof);
359
360 if (setjmp(errshell) && prof) {
361 close(input);
362 (void) endjobs(0);
363 return;
364 }
365 /*
366 * error return here
367 */
368
369 loopcnt = peekc = peekn = 0;
370 fndef = 0;
371 nohash = 0;
372 iopend = 0;
373
374 if (input >= 0)
375 initf(input);
376 /*
377 * command loop
378 */
379 for (;;) {
380 tdystak(0);
381 stakchk(); /* may reduce sbrk */
382 exitset();
383
384 if ((flags & prompt) && standin->fstak == 0 && !eof) {
385
386 if (mailp) {
387 time(&curtime);
388
389 if ((curtime - mailtime) >= mailchk) {
390 chkmail();
391 mailtime = curtime;
392 }
393 }
394
395 /* necessary to print jobs in a timely manner */
396 if (trapnote & TRAPSET)
397 chktrap();
398
399 prs(ps1nod.namval);
400
401 #ifdef TIME_OUT
402 alarm(TIMEOUT);
403 #endif
404
405 }
406
407 trapnote = 0;
408 peekc = readwc();
409 if (eof) {
410 if (endjobs(JOB_STOPPED))
411 return;
412 eof = 0;
413 }
414
415 #ifdef TIME_OUT
416 alarm(0);
417 #endif
418
419 {
420 struct trenod *t;
421 t = cmd(NL, MTFLG);
422 if (t == NULL && flags & ttyflg)
423 freejobs();
424 else
425 execute(t, 0, eflag);
426 }
427
428 eof |= (flags & oneflg);
429
430 }
431 }
432
433 void
chkpr(void)434 chkpr(void)
435 {
436 if ((flags & prompt) && standin->fstak == 0)
437 prs(ps2nod.namval);
438 }
439
440 void
settmp(void)441 settmp(void)
442 {
443 int len;
444 serial = 0;
445 if ((len = snprintf((char *)tmpout, TMPOUTSZ, "/tmp/sh%u", mypid)) >=
446 TMPOUTSZ) {
447 /*
448 * TMPOUTSZ should be big enough, but if it isn't,
449 * we'll at least try to create tmp files with
450 * a truncated tmpfile name at tmpout.
451 */
452 tmpout_offset = TMPOUTSZ - 1;
453 } else {
454 tmpout_offset = len;
455 }
456 }
457
458 static void
Ldup(int fa,int fb)459 Ldup(int fa, int fb)
460 {
461 #ifdef RES
462
463 dup(fa | DUPFLG, fb);
464 close(fa);
465 ioctl(fb, FIOCLEX, 0);
466
467 #else
468
469 if (fa >= 0) {
470 if (fa != fb) {
471 close(fb);
472 fcntl(fa, 0, fb); /* normal dup */
473 close(fa);
474 }
475 fcntl(fb, 2, 1); /* autoclose for fb */
476 }
477
478 #endif
479 }
480
481 void
chkmail(void)482 chkmail(void)
483 {
484 unsigned char *s = mailp;
485 unsigned char *save;
486
487 long *ptr = mod_time;
488 unsigned char *start;
489 BOOL flg;
490 struct stat statb;
491
492 while (*s) {
493 start = s;
494 save = 0;
495 flg = 0;
496
497 while (*s) {
498 if (*s != COLON) {
499 if (*s == '%' && save == 0)
500 save = s;
501
502 s++;
503 } else {
504 flg = 1;
505 *s = 0;
506 }
507 }
508
509 if (save)
510 *save = 0;
511
512 if (*start && stat((const char *)start, &statb) >= 0) {
513 if (statb.st_size && *ptr &&
514 statb.st_mtime != *ptr) {
515 if (save) {
516 prs(save+1);
517 newline();
518 }
519 else
520 prs(_gettext(mailmsg));
521 }
522 *ptr = statb.st_mtime;
523 } else if (*ptr == 0)
524 *ptr = 1;
525
526 if (save)
527 *save = '%';
528
529 if (flg)
530 *s++ = COLON;
531
532 ptr++;
533 }
534 }
535
536 void
setmail(unsigned char * mailpath)537 setmail(unsigned char *mailpath)
538 {
539 unsigned char *s = mailpath;
540 int cnt = 1;
541
542 long *ptr;
543
544 free(mod_time);
545 if (mailp = mailpath) {
546 while (*s) {
547 if (*s == COLON)
548 cnt += 1;
549
550 s++;
551 }
552
553 ptr = mod_time = (long *)alloc(sizeof (long) * cnt);
554
555 while (cnt) {
556 *ptr = 0;
557 ptr++;
558 cnt--;
559 }
560 }
561 }
562
563 void
setmode(int prof)564 setmode(int prof)
565 {
566 /*
567 * decide whether interactive
568 */
569
570 if ((flags & intflg) ||
571 ((flags&oneflg) == 0 &&
572 isatty(output) &&
573 isatty(input)))
574
575 {
576 dfault(&ps1nod, (geteuid() ? stdprompt : supprompt));
577 dfault(&ps2nod, readmsg);
578 flags |= ttyflg | prompt;
579 if (mailpnod.namflg != N_DEFAULT)
580 setmail(mailpnod.namval);
581 else
582 setmail(mailnod.namval);
583 startjobs();
584 }
585 else
586 {
587 flags |= prof;
588 flags &= ~prompt;
589 }
590 }
591