master xplshn/aruu / cmd / posix / sh / main.c
  1/*-
  2 * SPDX-License-Identifier: BSD-3-Clause
  3 *
  4 * Copyright (c) 1991, 1993
  5 *	The Regents of the University of California.  All rights reserved.
  6 *
  7 * This code is derived from software contributed to Berkeley by
  8 * Kenneth Almquist.
  9 *
 10 * Redistribution and use in source and binary forms, with or without
 11 * modification, are permitted provided that the following conditions
 12 * are met:
 13 * 1. Redistributions of source code must retain the above copyright
 14 *    notice, this list of conditions and the following disclaimer.
 15 * 2. Redistributions in binary form must reproduce the above copyright
 16 *    notice, this list of conditions and the following disclaimer in the
 17 *    documentation and/or other materials provided with the distribution.
 18 * 3. Neither the name of the University nor the names of its contributors
 19 *    may be used to endorse or promote products derived from this software
 20 *    without specific prior written permission.
 21 *
 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 32 * SUCH DAMAGE.
 33 */
 34
 35#include <errno.h>
 36#include <fcntl.h>
 37#include <locale.h>
 38#include <signal.h>
 39#include <stdio.h>
 40#include <sys/stat.h>
 41#include <termios.h>
 42#include <unistd.h>
 43
 44#include "../../../shared/paths.h"
 45#include "../../../shared/wexec.h"
 46#include "builtins.h"
 47#include "cd.h"
 48#include "error.h"
 49#include "eval.h"
 50#include "exec.h"
 51#include "expand.h"
 52#include "input.h"
 53#include "jobs.h"
 54#include "mail.h"
 55#include "main.h"
 56#include "memalloc.h"
 57#include "mystring.h"
 58#include "nodes.h"
 59#include "options.h"
 60#include "output.h"
 61#include "parser.h"
 62#include "redir.h"
 63#include "shell.h"
 64#include "show.h"
 65#include "trap.h"
 66#include "var.h"
 67#ifndef NO_HISTORY
 68#include "lineedit.h"
 69#endif
 70
 71int           rootpid;
 72int           rootshell;
 73struct jmploc main_handler;
 74int           localeisutf8, initial_localeisutf8;
 75
 76static void  reset(void);
 77static void  cmdloop(int);
 78static void  read_profile(const char *);
 79static char *find_dot_file(char *);
 80
 81/*
 82 * Main routine.  We initialize things, parse the arguments, execute
 83 * profiles if we're a login shell, and then call cmdloop to execute
 84 * commands.  The setjmp call sets up the location to jump to when an
 85 * exception occurs.  When an exception occurs the variable "state"
 86 * is used to figure out how far we had gotten.
 87 */
 88
 89int
 90main(int argc, char *argv[])
 91{
 92  /*
 93   * As smark is accessed after a longjmp, it cannot be a local in main().
 94   * The C standard specifies that the values of non-volatile local
 95   * variables are unspecified after a jump if modified between the
 96   * setjmp and longjmp.
 97   */
 98  static struct stackmark smark, smark2;
 99  volatile int            state;
100  char                   *shinit;
101  int                     login;
102
103  (void)setlocale(LC_ALL, "");
104  wexec_register_env_lookup(lookupvar);
105  initcharset();
106  state = 0;
107  if (setjmp(main_handler.loc)) {
108    if (state == 0 || iflag == 0 || !rootshell || exception == EXEXIT)
109      exitshell(exitstatus);
110    reset();
111    if (exception == EXINT)
112      out2fmt_flush("\n");
113    popstackmark(&smark);
114    FORCEINTON; /* enable interrupts */
115    if (state == 1)
116      goto state1;
117    else if (state == 2)
118      goto state2;
119    else if (state == 3)
120      goto state3;
121    else
122      goto state4;
123  }
124  handler = &main_handler;
125#ifdef DEBUG
126  opentrace();
127  trputs("Shell args:  ");
128  trargs(argv);
129#endif
130  rootpid = getpid();
131  if (rootpid == 1) {
132    /*
133     * Make sh usable for invocation as interactive init
134     * substitute with init_path=/bin/sh, by opening
135     * file descriptors 0, 1, and 2 on /dev/console.
136     */
137    if (fcntl(STDIN_FILENO, F_GETFL, NULL) == -1 && errno == EBADF) {
138      (void)open(ARUU_PATH_DEVCONSOLE, O_RDWR);
139      (void)setsid();
140      (void)tcsetsid(STDIN_FILENO, rootpid);
141    }
142    if (fcntl(STDOUT_FILENO, F_GETFL, NULL) == -1 && errno == EBADF)
143      (void)dup2(STDIN_FILENO, STDOUT_FILENO);
144    if (fcntl(STDERR_FILENO, F_GETFL, NULL) == -1 && errno == EBADF)
145      (void)dup2(STDIN_FILENO, STDERR_FILENO);
146  }
147  rootshell = 1;
148  INTOFF;
149  initvar();
150  setstackmark(&smark);
151  setstackmark(&smark2);
152  login = procargs(argc, argv);
153  trap_init();
154  pwd_init(iflag);
155  INTON;
156  if (iflag)
157    chkmail(1);
158  if (login) {
159    state = 1;
160    read_profile("/etc/profile");
161  state1:
162    state = 2;
163    if (privileged == 0)
164      read_profile("${HOME-}/.profile");
165    else
166      read_profile("/etc/suid_profile");
167  }
168state2:
169  state = 3;
170  if (!privileged && iflag) {
171    if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
172      state = 3;
173      read_profile(shinit);
174    }
175  }
176#ifndef NO_HISTORY
177  if (iflag)
178    histload();
179#endif
180state3:
181  state = 4;
182  popstackmark(&smark2);
183  if (minusc) {
184    evalstring(minusc, sflag ? 0 : EV_EXIT);
185  }
186state4:
187  if (sflag || minusc == NULL) {
188    cmdloop(1);
189  }
190  exitshell(exitstatus);
191  /*NOTREACHED*/
192  return 0;
193}
194
195static void
196reset(void)
197{
198  reseteval();
199  resetinput();
200}
201
202/*
203 * Read and execute commands.  "Top" is nonzero for the top level command
204 * loop; it turns on prompting if the shell is interactive.
205 */
206
207static void
208cmdloop(int top)
209{
210  union node      *n;
211  struct stackmark smark;
212  int              inter;
213  int              numeof = 0;
214
215  TRACE(("cmdloop(%d) called\n", top));
216  setstackmark(&smark);
217  for (;;) {
218    if (pendingsig)
219      dotrap();
220    inter = 0;
221    if (iflag && top) {
222      inter++;
223      showjobs(1, SHOWJOBS_DEFAULT);
224      chkmail(0);
225      flushout(&output);
226    }
227    n = parsecmd(inter);
228    /* showtree(n); DEBUG */
229    if (n == NEOF) {
230      if (!top || numeof >= 50)
231        break;
232      if (!stoppedjobs()) {
233        if (!Iflag)
234          break;
235        out2fmt_flush("\nUse \"exit\" to leave shell.\n");
236      }
237      numeof++;
238    } else if (n != NULL && nflag == 0) {
239      job_warning = (job_warning == 2) ? 1 : 0;
240      numeof      = 0;
241      evaltree(n, 0);
242    }
243    popstackmark(&smark);
244    setstackmark(&smark);
245    if (evalskip != 0) {
246      if (evalskip == SKIPRETURN)
247        evalskip = 0;
248      break;
249    }
250  }
251  popstackmark(&smark);
252  if (top && iflag) {
253    out2c('\n');
254    flushout(out2);
255  }
256}
257
258/*
259 * Read /etc/profile or .profile.  Return on error.
260 */
261
262static void
263read_profile(const char *name)
264{
265  int         fd;
266  const char *expandedname;
267  int         oflags = O_RDONLY | O_CLOEXEC;
268
269  if (verifyflag)
270    oflags |= O_VERIFY;
271
272  expandedname = expandstr(name);
273  if (expandedname == NULL)
274    return;
275  INTOFF;
276  if ((fd = open(expandedname, oflags)) >= 0)
277    setinputfd(fd, 1);
278  INTON;
279  if (fd < 0)
280    return;
281  cmdloop(0);
282  popfile();
283}
284
285/*
286 * Read a file containing shell functions.
287 */
288
289void
290readcmdfile(const char *name, int verify)
291{
292  setinputfile(name, 1, verify);
293  cmdloop(0);
294  popfile();
295}
296
297/*
298 * Take commands from a file.  To be compatible we should do a path
299 * search for the file, which is necessary to find sub-commands.
300 */
301
302static char *
303find_dot_file(char *basename)
304{
305  char       *fullname;
306  const char *opt;
307  const char *path = pathval();
308  struct stat statb;
309
310  /* don't try this for absolute or relative paths */
311  if (strchr(basename, '/'))
312    return basename;
313
314  while ((fullname = padvance(&path, &opt, basename)) != NULL) {
315    if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
316      /*
317       * Don't bother freeing here, since it will
318       * be freed by the caller.
319       */
320      return fullname;
321    }
322    stunalloc(fullname);
323  }
324  return basename;
325}
326
327int
328dotcmd(int argc, char **argv)
329{
330  char *filename, *fullname;
331
332  if (argc < 2)
333    error("missing filename");
334
335  exitstatus = 0;
336
337  /*
338   * Because we have historically not supported any options,
339   * only treat "--" specially.
340   */
341  filename = argc > 2 && strcmp(argv[1], "--") == 0 ? argv[2] : argv[1];
342
343  fullname = find_dot_file(filename);
344  setinputfile(fullname, 1, -1 /* verify */);
345  commandname = fullname;
346  cmdloop(0);
347  popfile();
348  return exitstatus;
349}
350
351int
352exitcmd(int argc, char **argv)
353{
354  if (stoppedjobs())
355    return 0;
356  if (argc > 1)
357    exitshell(number(argv[1]));
358  else
359    exitshell_savedstatus();
360}