1 0 stevel /* 2 1301 jonb * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 3 0 stevel * Use is subject to license terms. 4 0 stevel */ 5 0 stevel 6 0 stevel /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 7 0 stevel /* All Rights Reserved */ 8 0 stevel 9 0 stevel /* 10 0 stevel * Copyright (c) 1980 Regents of the University of California. 11 0 stevel * All rights reserved. The Berkeley Software License Agreement 12 0 stevel * specifies the terms and conditions for redistribution. 13 0 stevel */ 14 0 stevel 15 0 stevel #pragma ident "%Z%%M% %I% %E% SMI" 16 0 stevel 17 0 stevel #include <locale.h> 18 0 stevel #include "sh.h" 19 0 stevel /* #include <sys/ioctl.h> */ 20 0 stevel #include <fcntl.h> 21 0 stevel #include <sys/filio.h> 22 0 stevel #include "sh.tconst.h" 23 0 stevel #include <pwd.h> 24 0 stevel #include <stdlib.h> 25 0 stevel #include "sh_policy.h" /* for pfcsh */ 26 400 muffin #ifdef TRACE 27 400 muffin #include <stdio.h> 28 400 muffin #endif 29 0 stevel 30 0 stevel /* 31 0 stevel * We use these csh(1) private versions of the select macros, (see select(3C)) 32 0 stevel * so as not to be limited by the size of struct fd_set (ie 1024). 33 0 stevel */ 34 559 nakanon #define CSH_FD_SET(n, p) ((*((p) + ((n)/NFDBITS))) |= (1 << ((n) % NFDBITS))) 35 559 nakanon #define CSH_FD_CLR(n, p) ((*((p) + ((n)/NFDBITS))) &= ~(1 << ((n) % NFDBITS))) 36 559 nakanon #define CSH_FD_ISSET(n, p) ((*((p) + ((n)/NFDBITS))) & (1 << ((n) % NFDBITS))) 37 559 nakanon #define CSH_FD_ZERO(p, n) memset((void *)(p), 0, (n)) 38 0 stevel 39 559 nakanon tchar *pathlist[] = { S_usrbin /* "/usr/bin" */, S_DOT /* "." */, 0 }; 40 559 nakanon tchar *dumphist[] = { S_history /* "history" */, S_h /* "-h" */, 0, 0 }; 41 559 nakanon tchar *loadhist[] = { S_source /* "source" */, S_h /* "-h" */, S_NDOThistory /* "~/.history" */, 0 }; 42 0 stevel tchar HIST = '!'; 43 0 stevel tchar HISTSUB = '^'; 44 0 stevel int nofile; 45 0 stevel bool reenter; 46 0 stevel bool nverbose; 47 0 stevel bool nexececho; 48 0 stevel bool quitit; 49 0 stevel bool fast; 50 0 stevel bool batch; 51 0 stevel bool prompt = 1; 52 0 stevel bool enterhist = 0; 53 0 stevel 54 0 stevel extern gid_t getegid(), getgid(); 55 0 stevel extern uid_t geteuid(), getuid(); 56 0 stevel extern tchar **strblktotsblk(/* char **, int */); 57 2200 mg147109 58 2200 mg147109 extern void hupforegnd(void); 59 2200 mg147109 void interactive_hup(void); 60 2200 mg147109 void interactive_login_hup(void); 61 0 stevel 62 356 muffin void importpath(tchar *); 63 356 muffin void srccat(tchar *, tchar *); 64 356 muffin void srccat_inlogin(tchar *, tchar *); 65 356 muffin void srcunit(int, bool, bool); 66 356 muffin void rechist(void); 67 356 muffin void goodbye(void); 68 356 muffin void pintr1(bool); 69 356 muffin void process(bool); 70 356 muffin void dosource(tchar **); 71 356 muffin void mailchk(void); 72 356 muffin void printprompt(void); 73 356 muffin void sigwaiting(void); 74 356 muffin void siglwp(void); 75 356 muffin void initdesc(int, char *[]); 76 356 muffin void initdesc_x(int, char *[], int); 77 356 muffin void closem(void); 78 356 muffin void unsetfd(int); 79 356 muffin void secpolicy_print(int, const char *); 80 356 muffin void phup(void); 81 0 stevel 82 400 muffin #ifdef TRACE 83 400 muffin FILE *trace; 84 400 muffin /* 85 400 muffin * Trace routines 86 400 muffin */ 87 400 muffin #define TRACEFILE "/tmp/trace.XXXXXX" 88 400 muffin 89 400 muffin /* 90 400 muffin * Initialize trace file. 91 400 muffin * Called from main. 92 400 muffin */ 93 400 muffin void 94 400 muffin trace_init(void) 95 400 muffin { 96 400 muffin char name[128]; 97 400 muffin char *p; 98 400 muffin 99 400 muffin strcpy(name, TRACEFILE); 100 400 muffin p = mktemp(name); 101 400 muffin trace = fopen(p, "w"); 102 400 muffin } 103 400 muffin 104 400 muffin /* 105 400 muffin * write message to trace file 106 400 muffin */ 107 400 muffin /*VARARGS1*/ 108 400 muffin void 109 400 muffin tprintf(fmt, a, b, c, d, e, f, g, h, i, j) 110 400 muffin char *fmt; 111 400 muffin { 112 400 muffin if (trace) { 113 400 muffin fprintf(trace, fmt, a, b, c, d, e, f, g, h, i, j); 114 400 muffin fflush(trace); 115 400 muffin } 116 400 muffin } 117 400 muffin #endif 118 356 muffin 119 356 muffin int 120 356 muffin main(int c, char **av) 121 0 stevel { 122 356 muffin tchar **v, *cp, *p, *q, *r; 123 356 muffin int f; 124 0 stevel struct sigvec osv; 125 0 stevel struct sigaction sa; 126 0 stevel tchar s_prompt[MAXHOSTNAMELEN+3]; 127 1301 jonb char *c_max_var_len; 128 1301 jonb int c_max_var_len_size; 129 0 stevel 130 0 stevel pfcshflag = 0; 131 0 stevel 132 0 stevel /* 133 0 stevel * set up the error exit, if there is an error before 134 0 stevel * this is done, it will core dump, and we don't 135 0 stevel * tolerate core dumps 136 0 stevel */ 137 0 stevel haderr = 0; 138 0 stevel setexit(); 139 559 nakanon if (haderr) { 140 0 stevel /* 141 0 stevel * if were here, there was an error in the csh 142 0 stevel * startup so just punt 143 0 stevel */ 144 0 stevel printf("csh startup error, csh exiting...\n"); 145 0 stevel flush(); 146 0 stevel exitstat(); 147 0 stevel } 148 0 stevel 149 0 stevel 150 559 nakanon (void) setlocale(LC_ALL, ""); 151 0 stevel #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 152 559 nakanon #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 153 0 stevel #endif 154 0 stevel (void) textdomain(TEXT_DOMAIN); 155 0 stevel 156 0 stevel /* 157 0 stevel * This is a profile shell if the simple name of argv[0] is 158 0 stevel * pfcsh or -pfcsh 159 0 stevel */ 160 0 stevel p = strtots(NOSTR, "pfcsh"); 161 0 stevel r = strtots(NOSTR, "-pfcsh"); 162 0 stevel if ((p != NOSTR) && (r != NOSTR) && 163 0 stevel ((q = strtots(NOSTR, *av)) != NOSTR)) { 164 0 stevel if (c > 0 && (eq(p, simple(q)) || eq(r, simple(q)))) { 165 0 stevel pfcshflag = 1; 166 0 stevel } 167 559 nakanon xfree(q); 168 0 stevel } 169 0 stevel 170 0 stevel if (p != NOSTR) 171 559 nakanon xfree(p); 172 0 stevel if (r != NOSTR) 173 559 nakanon xfree(r); 174 0 stevel 175 0 stevel if (pfcshflag == 1) { 176 0 stevel secpolicy_init(); 177 0 stevel } 178 0 stevel 179 0 stevel /* Copy arguments */ 180 0 stevel v = strblktotsblk(av, c); 181 0 stevel 182 0 stevel /* 183 0 stevel * Initialize paraml list 184 0 stevel */ 185 0 stevel paraml.next = paraml.prev = ¶ml; 186 0 stevel 187 0 stevel settimes(); /* Immed. estab. timing base */ 188 559 nakanon 189 559 nakanon if (eq(v[0], S_aout /* "a.out" */)) /* A.out's are quittable */ 190 0 stevel quitit = 1; 191 0 stevel uid = getuid(); 192 0 stevel loginsh = **v == '-'; 193 0 stevel if (loginsh) 194 0 stevel (void) time(&chktim); 195 0 stevel 196 0 stevel /* 197 0 stevel * Move the descriptors to safe places. 198 0 stevel * The variable didfds is 0 while we have only FSH* to work with. 199 0 stevel * When didfds is true, we have 0,1,2 and prefer to use these. 200 0 stevel * 201 0 stevel * Also, setup data for csh internal file descriptor book keeping. 202 0 stevel */ 203 0 stevel initdesc(c, av); 204 0 stevel 205 0 stevel /* 206 0 stevel * Initialize the shell variables. 207 0 stevel * ARGV and PROMPT are initialized later. 208 0 stevel * STATUS is also munged in several places. 209 0 stevel * CHILD is munged when forking/waiting 210 0 stevel */ 211 1301 jonb 212 1301 jonb c_max_var_len_size = snprintf(NULL, 0, "%ld", MAX_VAR_LEN); 213 1301 jonb c_max_var_len = (char *)xalloc(c_max_var_len_size + 1); 214 1301 jonb (void) snprintf(c_max_var_len, (c_max_var_len_size + 1), 215 1301 jonb "%ld", MAX_VAR_LEN); 216 1301 jonb set(S_SUNW_VARLEN, strtots(NOSTR, c_max_var_len)); 217 1301 jonb xfree(c_max_var_len); 218 0 stevel 219 0 stevel /* don't do globbing here, just set exact copies */ 220 0 stevel setNS(S_noglob); 221 0 stevel 222 0 stevel set(S_status /* "status" */, S_0 /* "0" */); 223 0 stevel dinit(cp = getenvs_("HOME")); /* dinit thinks that HOME==cwd in a */ 224 0 stevel /* login shell */ 225 0 stevel if (cp == NOSTR) 226 0 stevel fast++; /* No home -> can't read scripts */ 227 0 stevel else { 228 0 stevel if (strlen_(cp) >= BUFSIZ - 10) { 229 0 stevel cp = NOSTR; 230 0 stevel fast++; 231 0 stevel printf("%s\n", gettext("Pathname too long")); 232 0 stevel set(S_home /* "home" */, savestr(cp)); 233 0 stevel local_setenv(S_HOME, savestr(cp)); 234 0 stevel } 235 0 stevel set(S_home /* "home" */, savestr(cp)); 236 0 stevel } 237 0 stevel /* 238 0 stevel * Grab other useful things from the environment. 239 0 stevel * Should we grab everything?? 240 0 stevel */ 241 0 stevel if ((cp = getenvs_("USER")) != NOSTR) 242 559 nakanon set(S_user /* "user" */, savestr(cp)); 243 0 stevel else { 244 0 stevel /* 245 0 stevel * If USER is not defined, set it here. 246 0 stevel */ 247 0 stevel struct passwd *pw; 248 0 stevel pw = getpwuid(getuid()); 249 0 stevel 250 0 stevel if (pw != NULL) { 251 559 nakanon set(S_user, strtots((tchar *)0, pw->pw_name)); 252 0 stevel local_setenv(S_USER, strtots((tchar *)0, pw->pw_name)); 253 0 stevel } 254 0 stevel else if (loginsh) { /* Give up setting USER variable. */ 255 559 nakanon printf("Warning: USER environment variable could not be set.\n"); 256 0 stevel } 257 0 stevel } 258 0 stevel if ((cp = getenvs_("TERM")) != NOSTR) 259 559 nakanon set(S_term /* "term" */, savestr(cp)); 260 0 stevel /* 261 0 stevel * Re-initialize path if set in environment 262 0 stevel */ 263 0 stevel if ((cp = getenvs_("PATH")) == NOSTR) 264 559 nakanon set1(S_path /* "path" */, saveblk(pathlist), &shvhed); 265 0 stevel else 266 0 stevel importpath(cp); 267 559 nakanon set(S_shell /* "shell" */, S_SHELLPATH); 268 0 stevel 269 0 stevel doldol = putn(getpid()); /* For $$ */ 270 0 stevel 271 0 stevel /* restore globbing until the user says otherwise */ 272 0 stevel unsetv(S_noglob); 273 0 stevel 274 0 stevel /* 275 0 stevel * Record the interrupt states from the parent process. 276 0 stevel * If the parent is non-interruptible our hand must be forced 277 0 stevel * or we (and our children) won't be either. 278 0 stevel * Our children inherit termination from our parent. 279 0 stevel * We catch it only if we are the login shell. 280 0 stevel */ 281 0 stevel /* parents interruptibility */ 282 0 stevel (void) sigvec(SIGINT, (struct sigvec *)0, &osv); 283 0 stevel parintr = osv.sv_handler; 284 0 stevel /* parents terminability */ 285 0 stevel (void) sigvec(SIGTERM, (struct sigvec *)0, &osv); 286 0 stevel parterm = osv.sv_handler; 287 0 stevel 288 0 stevel _signal(SIGLWP, siglwp); 289 0 stevel _signal(SIGWAITING, sigwaiting); 290 0 stevel if (loginsh) { 291 0 stevel (void) signal(SIGHUP, phup); /* exit processing on HUP */ 292 0 stevel (void) signal(SIGXCPU, phup); /* ...and on XCPU */ 293 0 stevel (void) signal(SIGXFSZ, phup); /* ...and on XFSZ */ 294 0 stevel } 295 0 stevel 296 0 stevel /* 297 0 stevel * Process the arguments. 298 0 stevel * 299 0 stevel * Note that processing of -v/-x is actually delayed till after 300 0 stevel * script processing. 301 0 stevel */ 302 0 stevel c--, v++; 303 0 stevel while (c > 0 && (cp = v[0])[0] == '-' && *++cp != '\0' && !batch) { 304 0 stevel do switch (*cp++) { 305 0 stevel 306 0 stevel case 'b': /* -b Next arg is input file */ 307 0 stevel batch++; 308 0 stevel break; 309 0 stevel 310 0 stevel case 'c': /* -c Command input from arg */ 311 0 stevel if (c == 1) 312 0 stevel exit(0); 313 0 stevel c--, v++; 314 0 stevel arginp = v[0]; 315 0 stevel prompt = 0; 316 0 stevel nofile++; 317 0 stevel cflg++; 318 0 stevel break; 319 0 stevel 320 0 stevel case 'e': /* -e Exit on any error */ 321 0 stevel exiterr++; 322 0 stevel break; 323 0 stevel 324 0 stevel case 'f': /* -f Fast start */ 325 0 stevel fast++; 326 0 stevel break; 327 0 stevel 328 0 stevel case 'i': /* -i Interactive, even if !intty */ 329 0 stevel intact++; 330 0 stevel nofile++; 331 0 stevel break; 332 0 stevel 333 0 stevel case 'n': /* -n Don't execute */ 334 0 stevel noexec++; 335 0 stevel break; 336 0 stevel 337 0 stevel case 'q': /* -q (Undoc'd) ... die on quit */ 338 0 stevel quitit = 1; 339 0 stevel break; 340 0 stevel 341 0 stevel case 's': /* -s Read from std input */ 342 0 stevel nofile++; 343 0 stevel break; 344 0 stevel 345 0 stevel case 't': /* -t Read one line from input */ 346 0 stevel onelflg = 2; 347 0 stevel prompt = 0; 348 0 stevel nofile++; 349 0 stevel break; 350 0 stevel #ifdef TRACE 351 0 stevel case 'T': /* -T trace switch on */ 352 0 stevel trace_init(); 353 0 stevel break; 354 0 stevel #endif 355 0 stevel 356 0 stevel case 'v': /* -v Echo hist expanded input */ 357 0 stevel nverbose = 1; /* ... later */ 358 0 stevel break; 359 0 stevel 360 0 stevel case 'x': /* -x Echo just before execution */ 361 0 stevel nexececho = 1; /* ... later */ 362 0 stevel break; 363 0 stevel 364 0 stevel case 'V': /* -V Echo hist expanded input */ 365 559 nakanon setNS(S_verbose /* "verbose" */); /* NOW! */ 366 0 stevel break; 367 0 stevel 368 0 stevel case 'X': /* -X Echo just before execution */ 369 559 nakanon setNS(S_echo /* "echo" */); /* NOW! */ 370 0 stevel break; 371 0 stevel 372 0 stevel } while (*cp); 373 0 stevel v++, c--; 374 0 stevel } 375 0 stevel 376 0 stevel if (quitit) /* With all due haste, for debugging */ 377 0 stevel (void) signal(SIGQUIT, SIG_DFL); 378 0 stevel 379 0 stevel /* 380 0 stevel * Unless prevented by -c, -i, -s, or -t, if there 381 0 stevel * are remaining arguments the first of them is the name 382 0 stevel * of a shell file from which to read commands. 383 0 stevel */ 384 0 stevel if (!batch && (uid != geteuid() || getgid() != getegid())) { 385 0 stevel errno = EACCES; 386 0 stevel child++; /* So this ... */ 387 559 nakanon Perror(S_csh /* "csh" */); /* ... doesn't return */ 388 0 stevel } 389 0 stevel 390 0 stevel if (nofile == 0 && c > 0) { 391 0 stevel nofile = open_(v[0], 0); 392 0 stevel if (nofile < 0) { 393 0 stevel child++; /* So this ... */ 394 0 stevel Perror(v[0]); /* ... doesn't return */ 395 0 stevel } 396 0 stevel file = v[0]; 397 0 stevel SHIN = dmove(nofile, FSHIN); /* Replace FSHIN */ 398 0 stevel (void) fcntl(SHIN, F_SETFD, 1); 399 0 stevel prompt = 0; 400 0 stevel c--, v++; 401 0 stevel } 402 0 stevel 403 0 stevel /* 404 0 stevel * Consider input a tty if it really is or we are interactive. 405 0 stevel */ 406 0 stevel intty = intact || isatty(SHIN); 407 0 stevel 408 0 stevel /* 409 0 stevel * Decide whether we should play with signals or not. 410 0 stevel * If we are explicitly told (via -i, or -) or we are a login 411 0 stevel * shell (arg0 starts with -) or the input and output are both 412 0 stevel * the ttys("csh", or "csh</dev/ttyx>/dev/ttyx") 413 0 stevel * Note that in only the login shell is it likely that parent 414 0 stevel * may have set signals to be ignored 415 0 stevel */ 416 0 stevel if (loginsh || intact || intty && isatty(SHOUT)) 417 0 stevel setintr = 1; 418 0 stevel #ifdef TELL 419 0 stevel settell(); 420 0 stevel #endif 421 0 stevel /* 422 0 stevel * Save the remaining arguments in argv. 423 0 stevel */ 424 754 nakanon setq(S_argv /* "argv" */, copyblk(v), &shvhed); 425 0 stevel 426 0 stevel /* 427 0 stevel * Set up the prompt. 428 0 stevel */ 429 0 stevel if (prompt) { 430 0 stevel gethostname_(s_prompt, MAXHOSTNAMELEN); 431 559 nakanon strcat_(s_prompt, uid == 0 ? S_SHARPSP /* "# " */ : S_PERSENTSP /* "% " */); 432 559 nakanon set(S_prompt /* "prompt" */, s_prompt); 433 0 stevel } 434 0 stevel 435 0 stevel /* 436 0 stevel * If we are an interactive shell, then start fiddling 437 0 stevel * with the signals; this is a tricky game. 438 0 stevel */ 439 0 stevel shpgrp = getpgid(0); 440 0 stevel opgrp = tpgrp = -1; 441 0 stevel if (setintr) { 442 0 stevel **av = '-'; 443 0 stevel if (!quitit) /* Wary! */ 444 0 stevel (void) signal(SIGQUIT, SIG_IGN); 445 0 stevel (void) signal(SIGINT, pintr); 446 0 stevel (void) sigblock(sigmask(SIGINT)); 447 0 stevel (void) signal(SIGTERM, SIG_IGN); 448 2200 mg147109 449 2200 mg147109 /* 450 2200 mg147109 * Explicitly terminate foreground jobs and exit if we are 451 2200 mg147109 * interactive shell 452 2200 mg147109 */ 453 2200 mg147109 if (loginsh) { 454 2200 mg147109 (void) signal(SIGHUP, interactive_login_hup); 455 2200 mg147109 } else { 456 2200 mg147109 (void) signal(SIGHUP, interactive_hup); 457 2200 mg147109 } 458 2200 mg147109 459 0 stevel if (quitit == 0 && arginp == 0) { 460 0 stevel (void) signal(SIGTSTP, SIG_IGN); 461 0 stevel (void) signal(SIGTTIN, SIG_IGN); 462 0 stevel (void) signal(SIGTTOU, SIG_IGN); 463 0 stevel /* 464 0 stevel * Wait till in foreground, in case someone 465 0 stevel * stupidly runs 466 0 stevel * csh & 467 0 stevel * dont want to try to grab away the tty. 468 0 stevel */ 469 0 stevel if (isatty(FSHDIAG)) 470 0 stevel f = FSHDIAG; 471 0 stevel else if (isatty(FSHOUT)) 472 0 stevel f = FSHOUT; 473 0 stevel else if (isatty(OLDSTD)) 474 0 stevel f = OLDSTD; 475 0 stevel else 476 0 stevel f = -1; 477 0 stevel retry: 478 0 stevel if (ioctl(f, TIOCGPGRP, (char *)&tpgrp) == 0 && 479 0 stevel tpgrp != -1) { 480 0 stevel if (tpgrp != shpgrp) { 481 0 stevel void (*old)() = (void (*)())signal(SIGTTIN, SIG_DFL); 482 0 stevel (void) kill(0, SIGTTIN); 483 0 stevel (void) signal(SIGTTIN, old); 484 0 stevel goto retry; 485 0 stevel } 486 0 stevel opgrp = shpgrp; 487 0 stevel shpgrp = getpid(); 488 0 stevel tpgrp = shpgrp; 489 0 stevel (void) setpgid(0, shpgrp); 490 0 stevel (void) ioctl(f, TIOCSPGRP, (char *)&shpgrp); 491 0 stevel (void) fcntl(dcopy(f, FSHTTY), F_SETFD, 1); 492 0 stevel } else { 493 0 stevel notty: 494 559 nakanon printf("Warning: no access to tty; thus no job control in this shell...\n"); 495 0 stevel tpgrp = -1; 496 0 stevel } 497 0 stevel } 498 0 stevel } 499 0 stevel if (setintr == 0 && parintr == SIG_DFL) 500 0 stevel setintr++; 501 0 stevel 502 0 stevel /* 503 0 stevel * Set SIGCHLD handler, making sure that reads restart after it runs. 504 0 stevel */ 505 0 stevel sigemptyset(&sa.sa_mask); 506 0 stevel sa.sa_handler = pchild; 507 0 stevel sa.sa_flags = SA_RESTART; 508 559 nakanon (void) sigaction(SIGCHLD, &sa, (struct sigaction *)NULL); 509 0 stevel 510 0 stevel /* 511 0 stevel * Set an exit here in case of an interrupt or error reading 512 0 stevel * the shell start-up scripts. 513 0 stevel */ 514 0 stevel setexit(); 515 0 stevel haderr = 0; /* In case second time through */ 516 0 stevel if (!fast && reenter == 0) { 517 0 stevel reenter++; 518 0 stevel 519 559 nakanon /* 520 559 nakanon * If this is a login csh, and /etc/.login exists, 521 559 nakanon * source /etc/.login first. 522 559 nakanon */ 523 559 nakanon if (loginsh) { 524 559 nakanon tchar tmp_etc[4+1]; /* strlen("/etc")+1 */ 525 559 nakanon tchar tmp_login[7+1]; /* strlen("/.login")+1 */ 526 0 stevel 527 0 stevel strtots(tmp_etc, "/etc"); 528 0 stevel strtots(tmp_login, "/.login"); 529 559 nakanon srccat_inlogin(tmp_etc, tmp_login); 530 559 nakanon } 531 0 stevel 532 0 stevel /* Will have value("home") here because set fast if don't */ 533 559 nakanon srccat(value(S_home /* "home" */), S_SLADOTcshrc /* "/.cshrc" */); 534 0 stevel 535 559 nakanon /* Hash path */ 536 0 stevel if (!fast && !arginp && !onelflg && !havhash) 537 0 stevel dohash(xhash); 538 559 nakanon 539 0 stevel 540 0 stevel /* 541 0 stevel * Reconstruct the history list now, so that it's 542 0 stevel * available from within .login. 543 0 stevel */ 544 0 stevel dosource(loadhist); 545 0 stevel if (loginsh) { 546 559 nakanon srccat_inlogin(value(S_home /* "home" */), S_SLADOTlogin /* "/.login" */); 547 0 stevel } 548 0 stevel 549 0 stevel /* 550 0 stevel * To get cdpath hashing $cdpath must have a 551 0 stevel * value, not $CDPATH. So if after reading 552 0 stevel * the startup files ( .cshrc ), and 553 0 stevel * user has specified a value for cdpath, then 554 0 stevel * cache $cdpath paths. xhash2 is global array 555 0 stevel * for $cdpath caching. 556 0 stevel */ 557 559 nakanon if (!fast && !arginp && !onelflg && !havhash2) 558 0 stevel dohash(xhash2); 559 0 stevel } 560 0 stevel 561 0 stevel /* 562 0 stevel * Now are ready for the -v and -x flags 563 0 stevel */ 564 0 stevel if (nverbose) 565 559 nakanon setNS(S_verbose /* "verbose" */); 566 0 stevel if (nexececho) 567 559 nakanon setNS(S_echo /* "echo" */); 568 0 stevel 569 0 stevel /* 570 0 stevel * All the rest of the world is inside this call. 571 0 stevel * The argument to process indicates whether it should 572 0 stevel * catch "error unwinds". Thus if we are a interactive shell 573 0 stevel * our call here will never return by being blown past on an error. 574 0 stevel */ 575 0 stevel process(setintr); 576 0 stevel 577 0 stevel /* 578 0 stevel * Mop-up. 579 0 stevel */ 580 0 stevel if (loginsh) { 581 0 stevel printf("logout\n"); 582 0 stevel (void) close(SHIN); /* No need for unsetfd(). */ 583 0 stevel child++; 584 0 stevel goodbye(); 585 0 stevel } 586 0 stevel rechist(); 587 0 stevel exitstat(); 588 0 stevel } 589 0 stevel 590 356 muffin void 591 356 muffin untty(void) 592 0 stevel { 593 0 stevel 594 0 stevel if (tpgrp > 0) { 595 0 stevel (void) setpgid(0, opgrp); 596 0 stevel (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&opgrp); 597 0 stevel } 598 0 stevel } 599 0 stevel 600 356 muffin void 601 356 muffin importpath(tchar *cp) 602 0 stevel { 603 356 muffin int i = 0; 604 356 muffin tchar *dp; 605 356 muffin tchar **pv; 606 0 stevel int c; 607 0 stevel static tchar dot[2] = {'.', 0}; 608 0 stevel 609 0 stevel for (dp = cp; *dp; dp++) 610 0 stevel if (*dp == ':') 611 0 stevel i++; 612 0 stevel /* 613 0 stevel * i+2 where i is the number of colons in the path. 614 0 stevel * There are i+1 directories in the path plus we need 615 0 stevel * room for a zero terminator. 616 0 stevel */ 617 559 nakanon pv = (tchar **)xcalloc((unsigned)(i + 2), sizeof (tchar **)); 618 0 stevel dp = cp; 619 0 stevel i = 0; 620 0 stevel if (*dp) 621 0 stevel for (;;) { 622 0 stevel if ((c = *dp) == ':' || c == 0) { 623 0 stevel *dp = 0; 624 0 stevel pv[i++] = savestr(*cp ? cp : dot); 625 0 stevel if (c) { 626 0 stevel cp = dp + 1; 627 0 stevel *dp = ':'; 628 0 stevel } else 629 0 stevel break; 630 0 stevel } 631 0 stevel dp++; 632 0 stevel } 633 0 stevel pv[i] = 0; 634 559 nakanon set1(S_path /* "path" */, pv, &shvhed); 635 0 stevel } 636 0 stevel 637 0 stevel /* 638 0 stevel * Source to the file which is the catenation of the argument names. 639 0 stevel */ 640 356 muffin void 641 356 muffin srccat(tchar *cp, tchar *dp) 642 0 stevel { 643 356 muffin tchar *ep = strspl(cp, dp); 644 356 muffin int unit = dmove(open_(ep, 0), -1); 645 0 stevel 646 0 stevel (void) fcntl(unit, F_SETFD, 1); 647 0 stevel xfree(ep); 648 0 stevel #ifdef INGRES 649 0 stevel srcunit(unit, 0, 0); 650 0 stevel #else 651 0 stevel srcunit(unit, 1, 0); 652 0 stevel #endif 653 0 stevel } 654 0 stevel 655 0 stevel /* 656 0 stevel * Source to the file which is the catenation of the argument names. 657 0 stevel * This one does not check the ownership. 658 0 stevel */ 659 356 muffin void 660 356 muffin srccat_inlogin(tchar *cp, tchar *dp) 661 0 stevel { 662 356 muffin tchar *ep = strspl(cp, dp); 663 356 muffin int unit = dmove(open_(ep, 0), -1); 664 0 stevel 665 0 stevel (void) fcntl(unit, F_SETFD, 1); 666 0 stevel xfree(ep); 667 0 stevel srcunit(unit, 0, 0); 668 0 stevel } 669 0 stevel 670 0 stevel /* 671 0 stevel * Source to a unit. If onlyown it must be our file or our group or 672 0 stevel * we don't chance it. This occurs on ".cshrc"s and the like. 673 0 stevel */ 674 356 muffin void 675 356 muffin srcunit(int unit, bool onlyown, bool hflg) 676 0 stevel { 677 0 stevel /* We have to push down a lot of state here */ 678 0 stevel /* All this could go into a structure */ 679 0 stevel int oSHIN = -1, oldintty = intty; 680 0 stevel struct whyle *oldwhyl = whyles; 681 0 stevel tchar *ogointr = gointr, *oarginp = arginp; 682 0 stevel tchar *oevalp = evalp, **oevalvec = evalvec; 683 0 stevel int oonelflg = onelflg; 684 0 stevel bool oenterhist = enterhist; 685 0 stevel tchar OHIST = HIST; 686 0 stevel #ifdef TELL 687 0 stevel bool otell = cantell; 688 0 stevel #endif 689 0 stevel struct Bin saveB; 690 0 stevel 691 0 stevel /* The (few) real local variables */ 692 0 stevel jmp_buf oldexit; 693 0 stevel int reenter, omask; 694 0 stevel 695 0 stevel if (unit < 0) 696 0 stevel return; 697 0 stevel if (didfds) 698 0 stevel donefds(); 699 0 stevel if (onlyown) { 700 0 stevel struct stat stb; 701 0 stevel 702 0 stevel if (fstat(unit, &stb) < 0 || 703 0 stevel (stb.st_uid != uid && stb.st_gid != getgid())) { 704 0 stevel (void) close(unit); 705 0 stevel unsetfd(unit); 706 0 stevel return; 707 0 stevel } 708 0 stevel } 709 0 stevel 710 0 stevel /* 711 0 stevel * There is a critical section here while we are pushing down the 712 0 stevel * input stream since we have stuff in different structures. 713 0 stevel * If we weren't careful an interrupt could corrupt SHIN's Bin 714 0 stevel * structure and kill the shell. 715 0 stevel * 716 0 stevel * We could avoid the critical region by grouping all the stuff 717 0 stevel * in a single structure and pointing at it to move it all at 718 0 stevel * once. This is less efficient globally on many variable references 719 0 stevel * however. 720 0 stevel */ 721 0 stevel getexit(oldexit); 722 0 stevel reenter = 0; 723 0 stevel if (setintr) 724 0 stevel omask = sigblock(sigmask(SIGINT)); 725 0 stevel setexit(); 726 0 stevel reenter++; 727 0 stevel if (reenter == 1) { 728 0 stevel /* Setup the new values of the state stuff saved above */ 729 559 nakanon copy((char *)&saveB, (char *)&B, sizeof saveB); 730 0 stevel fbuf = (tchar **) 0; 731 0 stevel fseekp = feobp = fblocks = 0; 732 0 stevel oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0; 733 0 stevel intty = isatty(SHIN), whyles = 0, gointr = 0; 734 0 stevel evalvec = 0; evalp = 0; 735 0 stevel enterhist = hflg; 736 0 stevel if (enterhist) 737 0 stevel HIST = '\0'; 738 0 stevel /* 739 0 stevel * Now if we are allowing commands to be interrupted, 740 0 stevel * we let ourselves be interrupted. 741 0 stevel */ 742 0 stevel if (setintr) 743 0 stevel (void) sigsetmask(omask); 744 0 stevel #ifdef TELL 745 0 stevel settell(); 746 0 stevel #endif 747 0 stevel process(0); /* 0 -> blow away on errors */ 748 0 stevel } 749 0 stevel if (setintr) 750 0 stevel (void) sigsetmask(omask); 751 0 stevel if (oSHIN >= 0) { 752 356 muffin int i; 753 0 stevel 754 0 stevel /* We made it to the new state... free up its storage */ 755 0 stevel /* This code could get run twice but xfree doesn't care */ 756 0 stevel for (i = 0; i < fblocks; i++) 757 0 stevel xfree(fbuf[i]); 758 559 nakanon xfree((char *)fbuf); 759 0 stevel 760 0 stevel /* Reset input arena */ 761 559 nakanon copy((char *)&B, (char *)&saveB, sizeof B); 762 0 stevel 763 0 stevel (void) close(SHIN), SHIN = oSHIN; 764 0 stevel unsetfd(SHIN); 765 0 stevel arginp = oarginp, onelflg = oonelflg; 766 0 stevel evalp = oevalp, evalvec = oevalvec; 767 0 stevel intty = oldintty, whyles = oldwhyl, gointr = ogointr; 768 0 stevel if (enterhist) 769 0 stevel HIST = OHIST; 770 0 stevel enterhist = oenterhist; 771 0 stevel #ifdef TELL 772 0 stevel cantell = otell; 773 0 stevel #endif 774 0 stevel } 775 0 stevel 776 0 stevel resexit(oldexit); 777 0 stevel /* 778 0 stevel * If process reset() (effectively an unwind) then 779 0 stevel * we must also unwind. 780 0 stevel */ 781 0 stevel if (reenter >= 2) 782 0 stevel error(NULL); 783 0 stevel } 784 0 stevel 785 356 muffin void 786 356 muffin rechist(void) 787 0 stevel { 788 0 stevel tchar buf[BUFSIZ]; 789 0 stevel int fp, ftmp, oldidfds; 790 0 stevel 791 0 stevel if (!fast) { 792 559 nakanon if (value(S_savehist /* "savehist" */)[0] == '\0') 793 0 stevel return; 794 559 nakanon (void) strcpy_(buf, value(S_home /* "home" */)); 795 559 nakanon (void) strcat_(buf, S_SLADOThistory /* "/.history" */); 796 0 stevel fp = creat_(buf, 0666); 797 0 stevel if (fp == -1) 798 0 stevel return; 799 0 stevel oldidfds = didfds; 800 0 stevel didfds = 0; 801 0 stevel ftmp = SHOUT; 802 0 stevel SHOUT = fp; 803 559 nakanon (void) strcpy_(buf, value(S_savehist /* "savehist" */)); 804 0 stevel dumphist[2] = buf; 805 0 stevel dohist(dumphist); 806 0 stevel (void) close(fp); 807 0 stevel unsetfd(fp); 808 0 stevel SHOUT = ftmp; 809 0 stevel didfds = oldidfds; 810 0 stevel } 811 0 stevel } 812 0 stevel 813 356 muffin void 814 356 muffin goodbye(void) 815 0 stevel { 816 0 stevel if (loginsh) { 817 0 stevel (void) signal(SIGQUIT, SIG_IGN); 818 0 stevel (void) signal(SIGINT, SIG_IGN); 819 0 stevel (void) signal(SIGTERM, SIG_IGN); 820 0 stevel setintr = 0; /* No interrupts after "logout" */ 821 559 nakanon if (adrof(S_home /* "home" */)) 822 559 nakanon srccat(value(S_home /* "home" */), S_SLADOTlogout /* "/.logout" */); 823 0 stevel } 824 0 stevel rechist(); 825 0 stevel exitstat(); 826 0 stevel } 827 0 stevel 828 356 muffin void 829 356 muffin exitstat(void) 830 0 stevel { 831 0 stevel 832 0 stevel #ifdef PROF 833 0 stevel monitor(0); 834 0 stevel #endif 835 0 stevel /* 836 0 stevel * Note that if STATUS is corrupted (i.e. getn bombs) 837 0 stevel * then error will exit directly because we poke child here. 838 0 stevel * Otherwise we might continue unwarrantedly (sic). 839 0 stevel */ 840 0 stevel child++; 841 0 stevel untty(); 842 559 nakanon exit(getn(value(S_status /* "status" */))); 843 0 stevel } 844 0 stevel 845 0 stevel /* 846 0 stevel * in the event of a HUP we want to save the history 847 0 stevel */ 848 0 stevel void 849 356 muffin phup(void) 850 0 stevel { 851 0 stevel rechist(); 852 0 stevel exit(1); 853 0 stevel } 854 0 stevel 855 2200 mg147109 void 856 2200 mg147109 interactive_hup(void) 857 2200 mg147109 { 858 2200 mg147109 hupforegnd(); 859 2200 mg147109 exit(1); 860 2200 mg147109 } 861 2200 mg147109 862 2200 mg147109 void 863 2200 mg147109 interactive_login_hup(void) 864 2200 mg147109 { 865 2200 mg147109 rechist(); 866 2200 mg147109 hupforegnd(); 867 2200 mg147109 exit(1); 868 2200 mg147109 } 869 2200 mg147109 870 559 nakanon tchar *jobargv[2] = { S_jobs /* "jobs" */, 0 }; 871 0 stevel /* 872 0 stevel * Catch an interrupt, e.g. during lexical input. 873 0 stevel * If we are an interactive shell, we reset the interrupt catch 874 0 stevel * immediately. In any case we drain the shell output, 875 0 stevel * and finally go through the normal error mechanism, which 876 0 stevel * gets a chance to make the shell go away. 877 0 stevel */ 878 0 stevel void 879 356 muffin pintr(void) 880 0 stevel { 881 0 stevel pintr1(1); 882 0 stevel } 883 0 stevel 884 356 muffin void 885 356 muffin pintr1(bool wantnl) 886 0 stevel { 887 356 muffin tchar **v; 888 0 stevel int omask; 889 0 stevel 890 0 stevel omask = sigblock(0); 891 0 stevel if (setintr) { 892 0 stevel (void) sigsetmask(omask & ~sigmask(SIGINT)); 893 0 stevel if (pjobs) { 894 0 stevel pjobs = 0; 895 0 stevel printf("\n"); 896 0 stevel dojobs(jobargv); 897 0 stevel bferr("Interrupted"); 898 0 stevel } 899 0 stevel } 900 0 stevel (void) sigsetmask(omask & ~sigmask(SIGCHLD)); 901 0 stevel draino(); 902 0 stevel 903 0 stevel /* 904 0 stevel * If we have an active "onintr" then we search for the label. 905 0 stevel * Note that if one does "onintr -" then we shan't be interruptible 906 0 stevel * so we needn't worry about that here. 907 0 stevel */ 908 0 stevel if (gointr) { 909 0 stevel search(ZGOTO, 0, gointr); 910 0 stevel timflg = 0; 911 0 stevel if (v = pargv) 912 0 stevel pargv = 0, blkfree(v); 913 0 stevel if (v = gargv) 914 0 stevel gargv = 0, blkfree(v); 915 0 stevel reset(); 916 0 stevel } else if (intty && wantnl) 917 0 stevel printf("\n"); /* Some like this, others don't */ 918 0 stevel error(NULL); 919 0 stevel } 920 0 stevel 921 0 stevel /* 922 0 stevel * Process is the main driving routine for the shell. 923 0 stevel * It runs all command processing, except for those within { ... } 924 0 stevel * in expressions (which is run by a routine evalav in sh.exp.c which 925 0 stevel * is a stripped down process), and `...` evaluation which is run 926 0 stevel * also by a subset of this code in sh.glob.c in the routine backeval. 927 0 stevel * 928 0 stevel * The code here is a little strange because part of it is interruptible 929 0 stevel * and hence freeing of structures appears to occur when none is necessary 930 0 stevel * if this is ignored. 931 0 stevel * 932 0 stevel * Note that if catch is not set then we will unwind on any error. 933 0 stevel * If an end-of-file occurs, we return. 934 0 stevel */ 935 356 muffin void 936 356 muffin process(bool catch) 937 0 stevel { 938 0 stevel jmp_buf osetexit; 939 356 muffin struct command *t; 940 0 stevel 941 0 stevel getexit(osetexit); 942 0 stevel for (;;) { 943 0 stevel pendjob(); 944 559 nakanon freelex(¶ml); 945 0 stevel paraml.next = paraml.prev = ¶ml; 946 559 nakanon paraml.word = S_ /* "" */; 947 0 stevel t = 0; 948 0 stevel setexit(); 949 0 stevel justpr = enterhist; /* execute if not entering history */ 950 0 stevel 951 0 stevel /* 952 0 stevel * Interruptible during interactive reads 953 0 stevel */ 954 0 stevel if (setintr) 955 0 stevel (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT)); 956 0 stevel 957 0 stevel /* 958 0 stevel * For the sake of reset() 959 0 stevel */ 960 0 stevel freelex(¶ml), freesyn(t), t = 0; 961 0 stevel 962 0 stevel if (haderr) { 963 0 stevel if (!catch) { 964 0 stevel /* unwind */ 965 0 stevel doneinp = 0; 966 0 stevel resexit(osetexit); 967 0 stevel reset(); 968 0 stevel } 969 0 stevel haderr = 0; 970 0 stevel /* 971 0 stevel * Every error is eventually caught here or 972 0 stevel * the shell dies. It is at this 973 0 stevel * point that we clean up any left-over open 974 0 stevel * files, by closing all but a fixed number 975 0 stevel * of pre-defined files. Thus routines don't 976 0 stevel * have to worry about leaving files open due 977 0 stevel * to deeper errors... they will get closed here. 978 0 stevel */ 979 0 stevel closem(); 980 0 stevel continue; 981 0 stevel } 982 0 stevel if (doneinp) { 983 0 stevel doneinp = 0; 984 0 stevel break; 985 0 stevel } 986 0 stevel if (chkstop) 987 0 stevel chkstop--; 988 0 stevel if (neednote) 989 0 stevel pnote(); 990 0 stevel if (intty && prompt && evalvec == 0) { 991 0 stevel mailchk(); 992 0 stevel /* 993 0 stevel * If we are at the end of the input buffer 994 0 stevel * then we are going to read fresh stuff. 995 0 stevel * Otherwise, we are rereading input and don't 996 0 stevel * need or want to prompt. 997 0 stevel */ 998 0 stevel if (fseekp == feobp) 999 0 stevel printprompt(); 1000 0 stevel } 1001 0 stevel err = 0; 1002 0 stevel 1003 0 stevel /* 1004 0 stevel * Echo not only on VERBOSE, but also with history expansion. 1005 0 stevel */ 1006 0 stevel if (lex(¶ml) && intty || 1007 559 nakanon adrof(S_verbose /* "verbose" */)) { 1008 0 stevel haderr = 1; 1009 0 stevel prlex(¶ml); 1010 0 stevel haderr = 0; 1011 0 stevel } 1012 0 stevel 1013 0 stevel /* 1014 0 stevel * The parser may lose space if interrupted. 1015 0 stevel */ 1016 0 stevel if (setintr) 1017 0 stevel (void) sigblock(sigmask(SIGINT)); 1018 0 stevel 1019 0 stevel /* 1020 559 nakanon * Save input text on the history list if 1021 0 stevel * reading in old history, or it 1022 0 stevel * is from the terminal at the top level and not 1023 0 stevel * in a loop. 1024 0 stevel */ 1025 0 stevel if (enterhist || catch && intty && !whyles) 1026 0 stevel savehist(¶ml); 1027 0 stevel 1028 0 stevel /* 1029 0 stevel * Print lexical error messages, except when sourcing 1030 0 stevel * history lists. 1031 0 stevel */ 1032 0 stevel if (!enterhist && err) 1033 0 stevel error("%s", gettext(err)); 1034 0 stevel 1035 0 stevel /* 1036 0 stevel * If had a history command :p modifier then 1037 0 stevel * this is as far as we should go 1038 0 stevel */ 1039 0 stevel if (justpr) 1040 0 stevel reset(); 1041 0 stevel 1042 0 stevel alias(¶ml); 1043 0 stevel 1044 0 stevel /* 1045 0 stevel * Parse the words of the input into a parse tree. 1046 0 stevel */ 1047 0 stevel t = syntax(paraml.next, ¶ml, 0); 1048 0 stevel if (err) 1049 0 stevel error("%s", gettext(err)); 1050 0 stevel 1051 0 stevel /* 1052 0 stevel * Execute the parse tree 1053 0 stevel */ 1054 0 stevel { 1055 0 stevel /* 1056 0 stevel * POSIX requires SIGCHLD to be held 1057 0 stevel * until all processes have joined the 1058 0 stevel * process group in order to avoid race 1059 0 stevel * condition. 1060 0 stevel */ 1061 0 stevel int omask; 1062 0 stevel 1063 0 stevel omask = sigblock(sigmask(SIGCHLD)); 1064 0 stevel execute(t, tpgrp); 1065 559 nakanon (void) sigsetmask(omask &~ sigmask(SIGCHLD)); 1066 0 stevel } 1067 0 stevel 1068 0 stevel if (err) 1069 0 stevel error("%s", gettext(err)); 1070 0 stevel /* 1071 0 stevel * Made it! 1072 0 stevel */ 1073 0 stevel freelex(¶ml), freesyn(t); 1074 0 stevel } 1075 0 stevel resexit(osetexit); 1076 0 stevel } 1077 0 stevel 1078 356 muffin void 1079 356 muffin dosource(tchar **t) 1080 0 stevel { 1081 356 muffin tchar *f; 1082 356 muffin int u; 1083 0 stevel bool hflg = 0; 1084 0 stevel tchar buf[BUFSIZ]; 1085 0 stevel 1086 0 stevel t++; 1087 559 nakanon if (*t && eq(*t, S_h /* "-h" */)) { 1088 0 stevel if (*++t == NOSTR) 1089 0 stevel bferr("Too few arguments."); 1090 0 stevel hflg++; 1091 0 stevel } 1092 0 stevel (void) strcpy_(buf, *t); 1093 0 stevel f = globone(buf); 1094 0 stevel u = dmove(open_(f, 0), -1); 1095 0 stevel xfree(f); 1096 0 stevel freelex(¶ml); 1097 0 stevel if (u < 0 && !hflg) 1098 0 stevel Perror(f); 1099 0 stevel (void) fcntl(u, F_SETFD, 1); 1100 0 stevel srcunit(u, 0, hflg); 1101 0 stevel } 1102 0 stevel 1103 0 stevel /* 1104 0 stevel * Check for mail. 1105 0 stevel * If we are a login shell, then we don't want to tell 1106 0 stevel * about any mail file unless its been modified 1107 0 stevel * after the time we started. 1108 0 stevel * This prevents us from telling the user things he already 1109 0 stevel * knows, since the login program insists on saying 1110 0 stevel * "You have mail." 1111 0 stevel */ 1112 356 muffin void 1113 356 muffin mailchk(void) 1114 0 stevel { 1115 356 muffin struct varent *v; 1116 356 muffin tchar **vp; 1117 0 stevel time_t t; 1118 0 stevel int intvl, cnt; 1119 0 stevel struct stat stb; 1120 0 stevel bool new; 1121 0 stevel 1122 559 nakanon v = adrof(S_mail /* "mail" */); 1123 0 stevel if (v == 0) 1124 0 stevel return; 1125 0 stevel (void) time(&t); 1126 0 stevel vp = v->vec; 1127 0 stevel cnt = blklen(vp); 1128 0 stevel intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; 1129 0 stevel if (intvl < 1) 1130 0 stevel intvl = 1; 1131 0 stevel if (chktim + intvl > t) 1132 0 stevel return; 1133 0 stevel for (; *vp; vp++) { 1134 0 stevel if (stat_(*vp, &stb) < 0) 1135 0 stevel continue; 1136 0 stevel new = stb.st_mtime > time0.tv_sec; 1137 0 stevel if (stb.st_size == 0 || stb.st_atime >= stb.st_mtime || 1138 0 stevel (stb.st_atime <= chktim && stb.st_mtime <= chktim) || 1139 0 stevel loginsh && !new) 1140 0 stevel continue; 1141 0 stevel if (cnt == 1) 1142 0 stevel printf("You have %smail.\n", new ? "new " : ""); 1143 0 stevel else 1144 0 stevel printf("%s in %t.\n", new ? "New mail" : "Mail", *vp); 1145 0 stevel } 1146 0 stevel chktim = t; 1147 0 stevel } 1148 0 stevel 1149 0 stevel /* 1150 0 stevel * Extract a home directory from the password file 1151 0 stevel * The argument points to a buffer where the name of the 1152 0 stevel * user whose home directory is sought is currently. 1153 0 stevel * We write the home directory of the user back there. 1154 0 stevel */ 1155 356 muffin int 1156 356 muffin gethdir(tchar *home) 1157 0 stevel { 1158 0 stevel /* getpwname will not be modified, so we need temp. buffer */ 1159 0 stevel char home_str[BUFSIZ]; 1160 0 stevel tchar home_ts[BUFSIZ]; 1161 559 nakanon struct passwd *pp /* = getpwnam(home) */; 1162 0 stevel 1163 0 stevel pp = getpwnam(tstostr(home_str, home)); 1164 0 stevel if (pp == 0) 1165 0 stevel return (1); 1166 0 stevel (void) strcpy_(home, strtots(home_ts, pp->pw_dir)); 1167 0 stevel return (0); 1168 0 stevel } 1169 0 stevel 1170 0 stevel 1171 559 nakanon #if 0 1172 356 muffin void 1173 0 stevel #ifdef PROF 1174 356 muffin done(int i) 1175 0 stevel #else 1176 356 muffin exit(int i) 1177 0 stevel #endif 1178 0 stevel { 1179 0 stevel 1180 0 stevel untty(); 1181 0 stevel _exit(i); 1182 0 stevel } 1183 559 nakanon #endif 1184 0 stevel 1185 356 muffin void 1186 356 muffin printprompt(void) 1187 0 stevel { 1188 356 muffin tchar *cp; 1189 0 stevel 1190 0 stevel if (!whyles) { 1191 0 stevel /* 1192 0 stevel * Print the prompt string 1193 0 stevel */ 1194 559 nakanon for (cp = value(S_prompt /* "prompt" */); *cp; cp++) 1195 0 stevel if (*cp == HIST) 1196 0 stevel printf("%d", eventno + 1); 1197 0 stevel else { 1198 0 stevel if (*cp == '\\' && cp[1] == HIST) 1199 0 stevel cp++; 1200 0 stevel Putchar(*cp | QUOTE); 1201 0 stevel } 1202 0 stevel } else 1203 559 nakanon /* 1204 0 stevel * Prompt for forward reading loop 1205 0 stevel * body content. 1206 0 stevel */ 1207 0 stevel printf("? "); 1208 0 stevel flush(); 1209 0 stevel } 1210 0 stevel 1211 0 stevel /* 1212 0 stevel * Save char * block. 1213 0 stevel */ 1214 0 stevel tchar ** 1215 356 muffin strblktotsblk(char **v, int num) 1216 0 stevel { 1217 356 muffin tchar **newv = 1218 559 nakanon (tchar **)xcalloc((unsigned)(num+ 1), sizeof (tchar **)); 1219 0 stevel tchar **onewv = newv; 1220 0 stevel 1221 0 stevel while (*v && num--) 1222 559 nakanon *newv++ = strtots(NOSTR, *v++); 1223 0 stevel *newv = 0; 1224 0 stevel return (onewv); 1225 0 stevel } 1226 0 stevel 1227 356 muffin void 1228 356 muffin sigwaiting(void) 1229 0 stevel { 1230 0 stevel _signal(SIGWAITING, sigwaiting); 1231 0 stevel } 1232 0 stevel 1233 356 muffin void 1234 356 muffin siglwp(void) 1235 0 stevel { 1236 559 nakanon _signal(SIGLWP, siglwp); 1237 0 stevel } 1238 0 stevel 1239 0 stevel 1240 0 stevel /* 1241 0 stevel * Following functions and data are used for csh to do its 1242 0 stevel * file descriptors book keeping. 1243 0 stevel */ 1244 0 stevel 1245 0 stevel static int *fdinuse = NULL; /* The list of files opened by csh */ 1246 0 stevel static int nbytesused = 0; /* no of bytes allocated to fdinuse */ 1247 0 stevel static int max_fd = 0; /* The maximum descriptor in fdinuse */ 1248 0 stevel static int my_pid; /* The process id set in initdesc() */ 1249 0 stevel static int NoFile = NOFILE; /* The number of files I can use. */ 1250 0 stevel 1251 0 stevel /* 1252 0 stevel * Get the number of files this csh can use. 1253 0 stevel * 1254 0 stevel * Move the initial descriptors to their eventual 1255 0 stevel * resting places, closing all other units. 1256 0 stevel * 1257 0 stevel * Also, reserve 0/1/2, so NIS+ routines do not get 1258 0 stevel * hold of them. And initialize fdinuse list and set 1259 0 stevel * the current process id. 1260 0 stevel * 1261 559 nakanon * If this csh was invoked from setuid'ed script file, 1262 559 nakanon * do not close the third argument passed. The file 1263 0 stevel * must be one of /dev/fd/0,1,2,,, 1264 0 stevel * (execv() always passes three arguments when it execs a script 1265 0 stevel * file in a form of #! /bin/csh -b.) 1266 0 stevel * 1267 0 stevel * If is_reinit is set in initdesc_x(), then we only close the file 1268 0 stevel * descriptors that we actually opened (as recorded in fdinuse). 1269 0 stevel */ 1270 356 muffin void 1271 356 muffin initdesc(int argc, char *argv[]) 1272 0 stevel { 1273 0 stevel initdesc_x(argc, argv, 0); 1274 0 stevel } 1275 0 stevel 1276 356 muffin void 1277 356 muffin reinitdesc(int argc, char *argv[]) 1278 0 stevel { 1279 0 stevel initdesc_x(argc, argv, 1); 1280 0 stevel } 1281 0 stevel 1282 0 stevel /* 1283 0 stevel * Callback functions for closing all file descriptors. 1284 0 stevel */ 1285 0 stevel static int 1286 0 stevel close_except(void *cd, int fd) 1287 0 stevel { 1288 0 stevel int script_fd = *(int *)cd; 1289 0 stevel 1290 0 stevel if (fd >= 3 && fd < NoFile && fd != script_fd) 1291 0 stevel (void) close(fd); 1292 0 stevel return (0); 1293 0 stevel } 1294 0 stevel 1295 0 stevel static int 1296 0 stevel close_inuse(void *cd, int fd) 1297 0 stevel { 1298 0 stevel int script_fd = *(int *)cd; 1299 0 stevel 1300 0 stevel if (fd >= 3 && fd < NoFile && fd != script_fd && 1301 0 stevel CSH_FD_ISSET(fd, fdinuse)) { 1302 0 stevel (void) close(fd); 1303 0 stevel unsetfd(fd); 1304 0 stevel } 1305 0 stevel return (0); 1306 0 stevel } 1307 0 stevel 1308 356 muffin void 1309 356 muffin initdesc_x(int argc, char *argv[], int is_reinit) 1310 0 stevel { 1311 0 stevel 1312 0 stevel int script_fd = -1; 1313 0 stevel struct stat buf; 1314 0 stevel struct rlimit rlp; 1315 0 stevel 1316 559 nakanon /* 1317 559 nakanon * Get pid of this shell 1318 559 nakanon */ 1319 0 stevel my_pid = getpid(); 1320 0 stevel 1321 0 stevel /* 1322 0 stevel * Get the hard limit numbers of descriptors 1323 0 stevel * this csh can use. 1324 0 stevel */ 1325 559 nakanon if (getrlimit(RLIMIT_NOFILE, &rlp) == 0) 1326 0 stevel NoFile = rlp.rlim_cur; 1327 559 nakanon 1328 0 stevel /* 1329 559 nakanon * If this csh was invoked for executing setuid script file, 1330 0 stevel * the third argument passed is the special file name 1331 0 stevel * which should not be closed. This special file name is 1332 0 stevel * in the form /dev/fd/X. 1333 0 stevel */ 1334 0 stevel if (argc >= 3) 1335 0 stevel if (sscanf(argv[2], "/dev/fd/%d", &script_fd) != 1) 1336 0 stevel script_fd = -1; 1337 0 stevel else 1338 0 stevel fcntl(script_fd, F_SETFD, 1); /* Make sure to close 1339 0 stevel * this file on exec. 1340 0 stevel */ 1341 0 stevel 1342 0 stevel if (fdinuse == NULL) { 1343 559 nakanon nbytesused = sizeof (int) * howmany(NoFile, sizeof (int) * NBBY); 1344 559 nakanon fdinuse = (int *)xalloc(nbytesused); 1345 0 stevel } 1346 0 stevel 1347 0 stevel /* 1348 0 stevel * Close all files except 0/1/2 to get a clean 1349 0 stevel * file descritor space. 1350 0 stevel */ 1351 0 stevel if (!is_reinit) 1352 0 stevel (void) fdwalk(close_except, &script_fd); 1353 0 stevel else 1354 0 stevel (void) fdwalk(close_inuse, &script_fd); 1355 0 stevel 1356 0 stevel didfds = 0; /* 0, 1, 2 aren't set up */ 1357 559 nakanon 1358 0 stevel if (fstat(0, &buf) < 0) 1359 0 stevel open("/dev/null", 0); 1360 0 stevel 1361 0 stevel (void) fcntl(SHIN = dcopy(0, FSHIN), F_SETFD, 1); 1362 0 stevel (void) fcntl(SHOUT = dcopy(1, FSHOUT), F_SETFD, 1); 1363 0 stevel (void) fcntl(SHDIAG = dcopy(2, FSHDIAG), F_SETFD, 1); 1364 0 stevel (void) fcntl(OLDSTD = dcopy(SHIN, FOLDSTD), F_SETFD, 1); 1365 0 stevel 1366 559 nakanon /* 1367 0 stevel * Open 0/1/2 to avoid Nis+ functions to pick them up. 1368 0 stevel * Now, 0/1/2 are saved, close them and open them. 1369 0 stevel */ 1370 0 stevel close(0); close(1); close(2); 1371 0 stevel open("/dev/null", 0); 1372 0 stevel dup(0); 1373 0 stevel dup(0); 1374 0 stevel 1375 0 stevel /* 1376 0 stevel * Clear fd_set mask 1377 0 stevel */ 1378 559 nakanon if (!is_reinit) 1379 0 stevel CSH_FD_ZERO(fdinuse, nbytesused); 1380 0 stevel } 1381 0 stevel 1382 0 stevel /* 1383 0 stevel * This routine is called after an error to close up 1384 0 stevel * any units which may have been left open accidentally. 1385 0 stevel * 1386 0 stevel * You only need to remove files in fdinuse list. 1387 0 stevel * After you have removed the files, you can clear the 1388 0 stevel * list and max_fd. 1389 0 stevel */ 1390 356 muffin void 1391 356 muffin closem(void) 1392 0 stevel { 1393 356 muffin int f; 1394 0 stevel 1395 0 stevel for (f = 3; f <= max_fd; f++) { 1396 0 stevel if (CSH_FD_ISSET(f, fdinuse) && 1397 559 nakanon f != SHIN && f != SHOUT && f != SHDIAG && 1398 0 stevel f != OLDSTD && f != FSHTTY) 1399 0 stevel close(f); 1400 0 stevel } 1401 0 stevel CSH_FD_ZERO(fdinuse, nbytesused); 1402 0 stevel max_fd = 0; 1403 0 stevel } 1404 0 stevel 1405 0 stevel /* 1406 0 stevel * Reset my_pid when a new process is created. Only call this 1407 0 stevel * if you want the process to affect fdinuse (e.g., fork, but 1408 0 stevel * not vfork). 1409 0 stevel */ 1410 356 muffin void 1411 356 muffin new_process(void) 1412 0 stevel { 1413 0 stevel my_pid = getpid(); 1414 0 stevel } 1415 0 stevel 1416 0 stevel 1417 0 stevel /* 1418 0 stevel * Whenever Csh open/create/dup/pipe a file or files, 1419 559 nakanon * Csh keeps track of its open files. The open files 1420 0 stevel * are kept in "fdinuse, Fd In Use" list. 1421 0 stevel * 1422 0 stevel * When a file descriptor is newly allocated, setfd() is 1423 0 stevel * used to mark the fact in "fdinuse" list. 1424 559 nakanon * For example, 1425 0 stevel * fd = open("newfile", 0); 1426 0 stevel * setfd(fd); 1427 0 stevel * 1428 0 stevel * When a file is freed by close() function, unsetfd() is 1429 0 stevel * used to remove the fd from "fdinuse" list. 1430 559 nakanon * For example, 1431 0 stevel * close(fd); 1432 0 stevel * unsetfd(fd); 1433 0 stevel */ 1434 356 muffin void 1435 356 muffin setfd(int fd) 1436 0 stevel { 1437 0 stevel /* 1438 0 stevel * Because you want to avoid 1439 0 stevel * conflict due to vfork(). 1440 0 stevel */ 1441 0 stevel if (my_pid != getpid()) 1442 0 stevel return; 1443 0 stevel 1444 0 stevel if (fd >= NoFile || fd < 0) 1445 0 stevel return; 1446 0 stevel 1447 0 stevel if (fd > max_fd) 1448 0 stevel max_fd = fd; 1449 0 stevel CSH_FD_SET(fd, fdinuse); 1450 0 stevel } 1451 0 stevel 1452 356 muffin void 1453 356 muffin unsetfd(int fd) 1454 0 stevel { 1455 356 muffin int i; 1456 0 stevel 1457 0 stevel /* 1458 0 stevel * Because you want to avoid 1459 0 stevel * conflict due to vfork(). 1460 0 stevel */ 1461 0 stevel if (my_pid != getpid()) 1462 0 stevel return; 1463 0 stevel 1464 0 stevel if (fd >= NoFile || fd < 0) 1465 0 stevel return; 1466 0 stevel 1467 0 stevel CSH_FD_CLR(fd, fdinuse); 1468 0 stevel if (fd == max_fd) { 1469 559 nakanon for (i = max_fd-1; i >= 3; i--) 1470 0 stevel if (CSH_FD_ISSET(i, fdinuse)) { 1471 0 stevel max_fd = i; 1472 0 stevel return; 1473 0 stevel } 1474 0 stevel max_fd = 0; 1475 0 stevel } 1476 0 stevel } 1477 0 stevel 1478 0 stevel /* 1479 0 stevel * A generic call back routine to output error messages from the 1480 0 stevel * policy backing functions called by pfcsh. 1481 0 stevel */ 1482 0 stevel void 1483 0 stevel secpolicy_print(int level, const char *msg) 1484 0 stevel { 1485 0 stevel switch (level) { 1486 0 stevel case SECPOLICY_WARN: 1487 0 stevel default: 1488 0 stevel haderr = 1; 1489 0 stevel printf("%s: ", msg); /* printf() does gettext() */ 1490 0 stevel break; 1491 0 stevel case SECPOLICY_ERROR: 1492 356 muffin bferr((char *)msg); /* bferr() does gettext() */ 1493 0 stevel break; 1494 0 stevel } 1495 0 stevel } 1496