?? lash.c
字號:
/* vi: set sw=4 ts=4: *//* * lash -- the BusyBox Lame-Ass SHell * * Copyright (C) 1999,2000 by Lineo, inc. and Erik Andersen * Copyright (C) 1999-2002 Erik Andersen <andersee@debian.org> * * Based in part on ladsh.c by Michael K. Johnson and Erik W. Troan, which is * under the following liberal license: "We have placed this source code in the * public domain. Use it in any project, free or commercial." * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * *//* This shell's parsing engine is officially at a dead-end. * Future work shell work should be done using hush.c *///For debugging/development on the shell only...//#define DEBUG_SHELL#include <stdio.h>#include <stdlib.h>#include <ctype.h>#include <errno.h>#include <fcntl.h>#include <signal.h>#include <string.h>#include <sys/ioctl.h>#include <sys/wait.h>#include <unistd.h>#include <getopt.h>#include <termios.h>#include "busybox.h"#include "cmdedit.h"#ifdef BB_LOCALE_SUPPORT#include <locale.h>#endif#include <glob.h>#define expand_t glob_tstatic const int MAX_READ = 128; /* size of input buffer for `read' builtin */#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"enum redir_type { REDIRECT_INPUT, REDIRECT_OVERWRITE, REDIRECT_APPEND};static const unsigned int DEFAULT_CONTEXT=0x1;static const unsigned int IF_TRUE_CONTEXT=0x2;static const unsigned int IF_FALSE_CONTEXT=0x4;static const unsigned int THEN_EXP_CONTEXT=0x8;static const unsigned int ELSE_EXP_CONTEXT=0x10;struct jobset { struct job *head; /* head of list of running jobs */ struct job *fg; /* current foreground job */};struct redir_struct { enum redir_type type; /* type of redirection */ int fd; /* file descriptor being redirected */ char *filename; /* file to redirect fd to */};struct child_prog { pid_t pid; /* 0 if exited */ char **argv; /* program name and arguments */ int num_redirects; /* elements in redirection array */ struct redir_struct *redirects; /* I/O redirects */ int is_stopped; /* is the program currently running? */ struct job *family; /* pointer back to the child's parent job */};struct job { int jobid; /* job number */ int num_progs; /* total number of programs in job */ int running_progs; /* number of programs running */ char *text; /* name of job */ char *cmdbuf; /* buffer various argv's point into */ pid_t pgrp; /* process group ID for the job */ struct child_prog *progs; /* array of programs in job */ struct job *next; /* to track background commands */ int stopped_progs; /* number of programs alive, but stopped */ unsigned int job_context; /* bitmask defining current context */ struct jobset *job_list;};struct built_in_command { char *cmd; /* name */ char *descr; /* description */ int (*function) (struct child_prog *); /* function ptr */};struct close_me { int fd; struct close_me *next;};/* function prototypes for builtins */static int builtin_cd(struct child_prog *cmd);static int builtin_exec(struct child_prog *cmd);static int builtin_exit(struct child_prog *cmd);static int builtin_fg_bg(struct child_prog *cmd);static int builtin_help(struct child_prog *cmd);static int builtin_jobs(struct child_prog *dummy);static int builtin_pwd(struct child_prog *dummy);static int builtin_export(struct child_prog *cmd);static int builtin_source(struct child_prog *cmd);static int builtin_unset(struct child_prog *cmd);static int builtin_read(struct child_prog *cmd);/* function prototypes for shell stuff */static void mark_open(int fd);static void mark_closed(int fd);static void close_all(void);static void checkjobs(struct jobset *job_list);static void remove_job(struct jobset *j_list, struct job *job);static int get_command(FILE * source, char *command);static int parse_command(char **command_ptr, struct job *job, int *inbg);static int run_command(struct job *newjob, int inbg, int outpipe[2]);static int pseudo_exec(struct child_prog *cmd) __attribute__ ((noreturn));static int busy_loop(FILE * input);/* Table of built-in functions (these are non-forking builtins, meaning they * can change global variables in the parent shell process but they will not * work with pipes and redirects; 'unset foo | whatever' will not work) */static struct built_in_command bltins[] = { {"bg", "Resume a job in the background", builtin_fg_bg}, {"cd", "Change working directory", builtin_cd}, {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec}, {"exit", "Exit from shell()", builtin_exit}, {"fg", "Bring job into the foreground", builtin_fg_bg}, {"jobs", "Lists the active jobs", builtin_jobs}, {"export", "Set environment variable", builtin_export}, {"unset", "Unset environment variable", builtin_unset}, {"read", "Input environment variable", builtin_read}, {".", "Source-in and run commands in a file", builtin_source}, /* to do: add ulimit */ {NULL, NULL, NULL}};/* Table of forking built-in functions (things that fork cannot change global * variables in the parent process, such as the current working directory) */static struct built_in_command bltins_forking[] = { {"pwd", "Print current directory", builtin_pwd}, {"help", "List shell built-in commands", builtin_help}, {NULL, NULL, NULL}};static int shell_context; /* Type prompt trigger (PS1 or PS2) *//* Globals that are static to this file */static const char *cwd;static char *local_pending_command = NULL;static struct jobset job_list = { NULL, NULL };static int argc;static char **argv;static struct close_me *close_me_head;static int last_return_code;static int last_bg_pid;static unsigned int last_jobid;static int shell_terminal;static pid_t shell_pgrp;static char *PS1;static char *PS2 = "> ";#ifdef DEBUG_SHELLstatic inline void debug_printf(const char *format, ...){ va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args);}#elsestatic inline void debug_printf(const char *format, ...) { }#endif/* Most builtins need access to the struct child_prog that has their arguments, previously coded as cmd->progs[0]. That coding can exhibit a bug, if the builtin is not the first command in a pipeline: "echo foo | exec sort" will attempt to exec foo.builtin previous use notes------ ----------------- ---------cd cmd->progs[0]exec cmd->progs[0] squashed bug: didn't look for applets or forking builtinsexit cmd->progs[0]fg_bg cmd->progs[0], job_list->head, job_list->fghelp 0jobs job_list->headpwd 0export cmd->progs[0]source cmd->progs[0]unset cmd->progs[0]read cmd->progs[0]I added "struct job *family;" to struct child_prog,and switched API to builtin_foo(struct child_prog *child);So cmd->text becomes child->family->text cmd->job_context becomes child->family->job_context cmd->progs[0] becomes *child job_list becomes child->family->job_list *//* built-in 'cd <path>' handler */static int builtin_cd(struct child_prog *child){ char *newdir; if (child->argv[1] == NULL) newdir = getenv("HOME"); else newdir = child->argv[1]; if (chdir(newdir)) { printf("cd: %s: %m\n", newdir); return EXIT_FAILURE; } cwd = xgetcwd((char *)cwd); if (!cwd) cwd = unknown; return EXIT_SUCCESS;}/* built-in 'exec' handler */static int builtin_exec(struct child_prog *child){ if (child->argv[1] == NULL) return EXIT_SUCCESS; /* Really? */ child->argv++; close_all(); pseudo_exec(child); /* never returns */}/* built-in 'exit' handler */static int builtin_exit(struct child_prog *child){ if (child->argv[1] == NULL) exit(EXIT_SUCCESS); exit (atoi(child->argv[1]));}/* built-in 'fg' and 'bg' handler */static int builtin_fg_bg(struct child_prog *child){ int i, jobnum; struct job *job=NULL; /* If they gave us no args, assume they want the last backgrounded task */ if (!child->argv[1]) { for (job = child->family->job_list->head; job; job = job->next) { if (job->jobid == last_jobid) { break; } } if (!job) { error_msg("%s: no current job", child->argv[0]); return EXIT_FAILURE; } } else { if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) { error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]); return EXIT_FAILURE; } for (job = child->family->job_list->head; job; job = job->next) { if (job->jobid == jobnum) { break; } } if (!job) { error_msg("%s: %d: no such job", child->argv[0], jobnum); return EXIT_FAILURE; } } if (*child->argv[0] == 'f') { /* Put the job into the foreground. */ tcsetpgrp(shell_terminal, job->pgrp); child->family->job_list->fg = job; } /* Restart the processes in the job */ for (i = 0; i < job->num_progs; i++) job->progs[i].is_stopped = 0; job->stopped_progs = 0; if ( (i=kill(- job->pgrp, SIGCONT)) < 0) { if (i == ESRCH) { remove_job(&job_list, job); } else { perror_msg("kill (SIGCONT)"); } } return EXIT_SUCCESS;}/* built-in 'help' handler */static int builtin_help(struct child_prog *dummy){ struct built_in_command *x; printf("\nBuilt-in commands:\n"); printf("-------------------\n"); for (x = bltins; x->cmd; x++) { if (x->descr==NULL) continue; printf("%s\t%s\n", x->cmd, x->descr); } for (x = bltins_forking; x->cmd; x++) { if (x->descr==NULL) continue; printf("%s\t%s\n", x->cmd, x->descr); } printf("\n\n"); return EXIT_SUCCESS;}/* built-in 'jobs' handler */static int builtin_jobs(struct child_prog *child){ struct job *job; char *status_string; for (job = child->family->job_list->head; job; job = job->next) { if (job->running_progs == job->stopped_progs) status_string = "Stopped"; else status_string = "Running"; printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->text); } return EXIT_SUCCESS;}/* built-in 'pwd' handler */static int builtin_pwd(struct child_prog *dummy){ cwd = xgetcwd((char *)cwd); if (!cwd) cwd = unknown; puts(cwd); return EXIT_SUCCESS;}/* built-in 'export VAR=value' handler */static int builtin_export(struct child_prog *child){ int res; char *v = child->argv[1]; if (v == NULL) { char **e; for (e = environ; *e; e++) { puts(*e); } return 0; } res = putenv(v); if (res) fprintf(stderr, "export: %m\n");#ifdef BB_FEATURE_SH_FANCY_PROMPT if (strncmp(v, "PS1=", 4)==0) PS1 = getenv("PS1");#endif#ifdef BB_LOCALE_SUPPORT if(strncmp(v, "LC_ALL=", 7)==0) setlocale(LC_ALL, getenv("LC_ALL")); if(strncmp(v, "LC_CTYPE=", 9)==0) setlocale(LC_CTYPE, getenv("LC_CTYPE"));#endif return (res);}/* built-in 'read VAR' handler */static int builtin_read(struct child_prog *child){ int res = 0, len, newlen; char *s; char string[MAX_READ]; if (child->argv[1]) { /* argument (VAR) given: put "VAR=" into buffer */ snprintf(string, sizeof(string)-1, "%s=", child->argv[1]); len = strlen(string); fgets(&string[len], sizeof(string) - len, stdin); /* read string */ newlen = strlen(string); if(newlen > len) string[--newlen] = '\0'; /* chomp trailing newline */ /* ** string should now contain "VAR=<value>" ** copy it (putenv() won't do that, so we must make sure ** the string resides in a static buffer!) */ res = -1; if((s = strdup(string))) res = putenv(s); if (res) fprintf(stderr, "read: %m\n"); } else fgets(string, sizeof(string), stdin); return (res);}/* Built-in '.' handler (read-in and execute commands from file) */static int builtin_source(struct child_prog *child){ FILE *input; int status; int fd; if (child->argv[1] == NULL) return EXIT_FAILURE; input = fopen(child->argv[1], "r"); if (!input) { printf( "Couldn't open file '%s'\n", child->argv[1]); return EXIT_FAILURE; } fd=fileno(input); mark_open(fd); /* Now run the file */ status = busy_loop(input); fclose(input); mark_closed(fd); return (status);}/* built-in 'unset VAR' handler */static int builtin_unset(struct child_prog *child){ if (child->argv[1] == NULL) { printf( "unset: parameter required.\n"); return EXIT_FAILURE; } unsetenv(child->argv[1]); return EXIT_SUCCESS;}static void mark_open(int fd){ struct close_me *new = xmalloc(sizeof(struct close_me)); new->fd = fd; new->next = close_me_head; close_me_head = new;}static void mark_closed(int fd){ struct close_me *tmp; if (close_me_head == NULL || close_me_head->fd != fd) error_msg_and_die("corrupt close_me"); tmp = close_me_head; close_me_head = close_me_head->next; free(tmp);}static void close_all(){ struct close_me *c, *tmp; for (c=close_me_head; c; c=tmp) { close(c->fd); tmp=c->next; free(c); } close_me_head = NULL;}/* free up all memory from a job */static void free_job(struct job *cmd){ int i; struct jobset *keep; for (i = 0; i < cmd->num_progs; i++) { free(cmd->progs[i].argv); if (cmd->progs[i].redirects) free(cmd->progs[i].redirects); } if (cmd->progs) free(cmd->progs); if (cmd->text) free(cmd->text); if (cmd->cmdbuf) free(cmd->cmdbuf); keep = cmd->job_list; memset(cmd, 0, sizeof(struct job)); cmd->job_list = keep;}/* remove a job from a jobset */static void remove_job(struct jobset *j_list, struct job *job){ struct job *prevjob; free_job(job); if (job == j_list->head) { j_list->head = job->next; } else { prevjob = j_list->head; while (prevjob->next != job) prevjob = prevjob->next; prevjob->next = job->next; } if (j_list->head) last_jobid = j_list->head->jobid; else last_jobid = 0; free(job);}/* Checks to see if any background processes have exited -- if they
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -