📄 lash.c
字号:
return 1; } prog->redirects[i].filename = buf; while (*chptr && !isspace(*chptr)) *buf++ = *chptr++; src = chptr - 1; /* we src++ later */ prog->argv[argc_l] = ++buf; break; case '|': /* pipe */ /* finish this command */ if (*prog->argv[argc_l] || saw_quote) argc_l++; if (!argc_l) { bb_error_msg("empty command in pipe"); free_job(job); job->num_progs=0; return 1; } prog->argv[argc_l] = NULL; /* and start the next */ job->num_progs++; job->progs = xrealloc(job->progs, sizeof(*job->progs) * job->num_progs); prog = job->progs + (job->num_progs - 1); prog->num_redirects = 0; prog->redirects = NULL; prog->is_stopped = 0; prog->family = job; argc_l = 0; argv_alloced = 5; prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced); prog->argv[0] = ++buf; src++; while (*src && isspace(*src)) src++; if (!*src) { bb_error_msg("empty command in pipe"); free_job(job); job->num_progs=0; return 1; } src--; /* we'll ++ it at the end of the loop */ break;#endif#ifdef CONFIG_LASH_JOB_CONTROL case '&': /* background */ *inbg = 1;#endif case ';': /* multiple commands */ done = 1; return_command = *command_ptr + (src - *command_ptr) + 1; break; case '\\': src++; if (!*src) { bb_error_msg("character expected after \\"); free_job(job); return 1; } if (*src == '*' || *src == '[' || *src == ']' || *src == '?') *buf++ = '\\'; /* fallthrough */ default: *buf++ = *src; } src++; } if (*prog->argv[argc_l] || saw_quote) { argc_l++; } if (!argc_l) { free_job(job); return 0; } prog->argv[argc_l] = NULL; if (!return_command) { job->text = xmalloc(strlen(*command_ptr) + 1); strcpy(job->text, *command_ptr); } else { /* This leaves any trailing spaces, which is a bit sloppy */ count = return_command - *command_ptr; job->text = xmalloc(count + 1); strncpy(job->text, *command_ptr, count); job->text[count] = '\0'; } *command_ptr = return_command; return 0;}/* Run the child_prog, no matter what kind of command it uses. */static int pseudo_exec(struct child_prog *child){ struct built_in_command *x;#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL char *name;#endif /* Check if the command matches any of the non-forking builtins. * Depending on context, this might be redundant. But it's * easier to waste a few CPU cycles than it is to figure out * if this is one of those cases. */ for (x = bltins; x->cmd; x++) { if (strcmp(child->argv[0], x->cmd) == 0 ) { _exit(x->function(child)); } } /* Check if the command matches any of the forking builtins. */ for (x = bltins_forking; x->cmd; x++) { if (strcmp(child->argv[0], x->cmd) == 0) { bb_applet_name=x->cmd; _exit (x->function(child)); } }#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL /* Check if the command matches any busybox internal * commands ("applets") here. Following discussions from * November 2000 on busybox@busybox.net, don't use * bb_get_last_path_component(). This way explicit (with * slashes) filenames will never be interpreted as an * applet, just like with builtins. This way the user can * override an applet with an explicit filename reference. * The only downside to this change is that an explicit * /bin/foo invocation will fork and exec /bin/foo, even if * /bin/foo is a symlink to busybox. */ name = child->argv[0]; { char** argv_l=child->argv; int argc_l; for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++); optind = 1; run_applet_by_name(name, argc_l, child->argv); }#endif execvp(child->argv[0], child->argv); /* Do not use bb_perror_msg_and_die() here, since we must not * call exit() but should call _exit() instead */ fprintf(stderr, "%s: %m\n", child->argv[0]); _exit(EXIT_FAILURE);}static void insert_job(struct job *newjob, int inbg){ struct job *thejob; struct jobset *j_list=newjob->job_list; /* find the ID for thejob to use */ newjob->jobid = 1; for (thejob = j_list->head; thejob; thejob = thejob->next) if (thejob->jobid >= newjob->jobid) newjob->jobid = thejob->jobid + 1; /* add thejob to the list of running jobs */ if (!j_list->head) { thejob = j_list->head = xmalloc(sizeof(*thejob)); } else { for (thejob = j_list->head; thejob->next; thejob = thejob->next) /* nothing */; thejob->next = xmalloc(sizeof(*thejob)); thejob = thejob->next; } *thejob = *newjob; /* physically copy the struct job */ thejob->next = NULL; thejob->running_progs = thejob->num_progs; thejob->stopped_progs = 0;#ifdef CONFIG_LASH_JOB_CONTROL if (inbg) { /* we don't wait for background thejobs to return -- append it to the list of backgrounded thejobs and leave it alone */ printf("[%d] %d\n", thejob->jobid, newjob->progs[newjob->num_progs - 1].pid); last_jobid = newjob->jobid; last_bg_pid=newjob->progs[newjob->num_progs - 1].pid; } else { newjob->job_list->fg = thejob; /* move the new process group into the foreground */ /* suppress messages when run from /linuxrc mag@sysgo.de */ if (tcsetpgrp(shell_terminal, newjob->pgrp) && errno != ENOTTY) bb_perror_msg("tcsetpgrp"); }#endif}static int run_command(struct job *newjob, int inbg, int outpipe[2]){ /* struct job *thejob; */ int i; int nextin, nextout; int pipefds[2]; /* pipefd[0] is for reading */ struct built_in_command *x; struct child_prog *child; nextin = 0, nextout = 1; for (i = 0; i < newjob->num_progs; i++) { child = & (newjob->progs[i]); if ((i + 1) < newjob->num_progs) { if (pipe(pipefds)<0) bb_perror_msg_and_die("pipe"); nextout = pipefds[1]; } else { if (outpipe[1]!=-1) { nextout = outpipe[1]; } else { nextout = 1; } } /* Check if the command matches any non-forking builtins, * but only if this is a simple command. * Non-forking builtins within pipes have to fork anyway, * and are handled in pseudo_exec. "echo foo | read bar" * is doomed to failure, and doesn't work on bash, either. */ if (newjob->num_progs == 1) { for (x = bltins; x->cmd; x++) { if (strcmp(child->argv[0], x->cmd) == 0 ) { int rcode; int squirrel[] = {-1, -1, -1}; setup_redirects(child, squirrel); rcode = x->function(child); restore_redirects(squirrel); return rcode; } } }#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__) if (!(child->pid = fork()))#else if (!(child->pid = vfork()))#endif { /* Set the handling for job control signals back to the default. */ signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGTSTP, SIG_DFL); signal(SIGTTIN, SIG_DFL); signal(SIGTTOU, SIG_DFL); signal(SIGCHLD, SIG_DFL); close_all(); if (outpipe[1]!=-1) { close(outpipe[0]); } if (nextin != 0) { dup2(nextin, 0); close(nextin); } if (nextout != 1) { dup2(nextout, 1); dup2(nextout, 2); /* Really? */ close(nextout); close(pipefds[0]); } /* explicit redirects override pipes */ setup_redirects(child,NULL); pseudo_exec(child); } if (outpipe[1]!=-1) { close(outpipe[1]); } /* put our child in the process group whose leader is the first process in this pipe */ setpgid(child->pid, newjob->progs[0].pid); if (nextin != 0) close(nextin); if (nextout != 1) close(nextout); /* If there isn't another process, nextin is garbage but it doesn't matter */ nextin = pipefds[0]; } newjob->pgrp = newjob->progs[0].pid; insert_job(newjob, inbg); return 0;}static int busy_loop(FILE * input){ char *command; char *next_command = NULL; struct job newjob; int i; int inbg; int status;#ifdef CONFIG_LASH_JOB_CONTROL pid_t parent_pgrp; /* save current owner of TTY so we can restore it on exit */ parent_pgrp = tcgetpgrp(shell_terminal);#endif newjob.job_list = &job_list; newjob.job_context = DEFAULT_CONTEXT; command = (char *) xcalloc(BUFSIZ, sizeof(char)); while (1) { if (!job_list.fg) { /* no job is in the foreground */ /* see if any background processes have exited */ checkjobs(&job_list); if (!next_command) { if (get_command(input, command)) break; next_command = command; } if (! expand_arguments(next_command)) { free(command); command = (char *) xcalloc(BUFSIZ, sizeof(char)); next_command = NULL; continue; } if (!parse_command(&next_command, &newjob, &inbg) && newjob.num_progs) { int pipefds[2] = {-1,-1}; debug_printf( "job=%p fed to run_command by busy_loop()'\n", &newjob); run_command(&newjob, inbg, pipefds); } else { free(command); command = (char *) xcalloc(BUFSIZ, sizeof(char)); next_command = NULL; } } else { /* a job is running in the foreground; wait for it */ i = 0; while (!job_list.fg->progs[i].pid || job_list.fg->progs[i].is_stopped == 1) i++; if (waitpid(job_list.fg->progs[i].pid, &status, WUNTRACED)<0) { if (errno != ECHILD) { bb_perror_msg_and_die("waitpid(%d)",job_list.fg->progs[i].pid); } } if (WIFEXITED(status) || WIFSIGNALED(status)) { /* the child exited */ job_list.fg->running_progs--; job_list.fg->progs[i].pid = 0; last_return_code=WEXITSTATUS(status); if (!job_list.fg->running_progs) { /* child exited */ remove_job(&job_list, job_list.fg); job_list.fg = NULL; } }#ifdef CONFIG_LASH_JOB_CONTROL else { /* the child was stopped */ job_list.fg->stopped_progs++; job_list.fg->progs[i].is_stopped = 1; if (job_list.fg->stopped_progs == job_list.fg->running_progs) { printf("\n" JOB_STATUS_FORMAT, job_list.fg->jobid, "Stopped", job_list.fg->text); job_list.fg = NULL; } } if (!job_list.fg) { /* move the shell to the foreground */ /* suppress messages when run from /linuxrc mag@sysgo.de */ if (tcsetpgrp(shell_terminal, getpgrp()) && errno != ENOTTY) bb_perror_msg("tcsetpgrp"); }#endif } } free(command);#ifdef CONFIG_LASH_JOB_CONTROL /* return controlling TTY back to parent process group before exiting */ if (tcsetpgrp(shell_terminal, parent_pgrp) && errno != ENOTTY) bb_perror_msg("tcsetpgrp");#endif /* return exit status if called with "-c" */ if (input == NULL && WIFEXITED(status)) return WEXITSTATUS(status); return 0;}#ifdef CONFIG_FEATURE_CLEAN_UPvoid free_memory(void){ if (cwd && cwd!=bb_msg_unknown) { free((char*)cwd); } if (local_pending_command) free(local_pending_command); if (job_list.fg && !job_list.fg->running_progs) { remove_job(&job_list, job_list.fg); }}#endif#ifdef CONFIG_LASH_JOB_CONTROL/* Make sure we have a controlling tty. If we get started under a job * aware app (like bash for example), make sure we are now in charge so * we don't fight over who gets the foreground */static void setup_job_control(void){ int status; pid_t shell_pgrp; /* Loop until we are in the foreground. */ while ((status = tcgetpgrp (shell_terminal)) >= 0) { if (status == (shell_pgrp = getpgrp ())) { break; } kill (- shell_pgrp, SIGTTIN); } /* Ignore interactive and job-control signals. */ signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGTSTP, SIG_IGN); signal(SIGTTIN, SIG_IGN); signal(SIGTTOU, SIG_IGN); signal(SIGCHLD, SIG_IGN); /* Put ourselves in our own process group. */ setsid(); shell_pgrp = getpid (); setpgid (shell_pgrp, shell_pgrp); /* Grab control of the terminal. */ tcsetpgrp(shell_terminal, shell_pgrp);}#elsestatic inline void setup_job_control(void){}#endifint lash_main(int argc_l, char **argv_l){ int opt, interactive=FALSE; FILE *input = stdin; argc = argc_l; argv = argv_l; /* These variables need re-initializing when recursing */ last_jobid = 0; local_pending_command = NULL; close_me_head = NULL; job_list.head = NULL; job_list.fg = NULL; last_return_code=1; if (argv[0] && argv[0][0] == '-') { FILE *prof_input; prof_input = fopen("/etc/profile", "r"); if (prof_input) { int tmp_fd = fileno(prof_input); mark_open(tmp_fd); /* Now run the file */ busy_loop(prof_input); fclose(prof_input); mark_closed(tmp_fd); } } while ((opt = getopt(argc_l, argv_l, "cxi")) > 0) { switch (opt) { case 'c': input = NULL; if (local_pending_command != 0) bb_error_msg_and_die("multiple -c arguments"); local_pending_command = bb_xstrdup(argv[optind]); optind++; argv = argv+optind; break; case 'i': interactive = TRUE; break; default: bb_show_usage(); } } /* A shell is interactive if the `-i' flag was given, or if all of * the following conditions are met: * no -c command * no arguments remaining or the -s flag given * standard input is a terminal * standard output is a terminal * Refer to Posix.2, the description of the `sh' utility. */ if (argv[optind]==NULL && input==stdin && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { interactive=TRUE; } setup_job_control(); if (interactive==TRUE) { //printf( "optind=%d argv[optind]='%s'\n", optind, argv[optind]); /* Looks like they want an interactive shell */#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET printf( "\n\n" BB_BANNER " Built-in shell (lash)\n"); printf( "Enter 'help' for a list of built-in commands.\n\n");#endif } else if (local_pending_command==NULL) { //printf( "optind=%d argv[optind]='%s'\n", optind, argv[optind]); input = bb_xfopen(argv[optind], "r"); mark_open(fileno(input)); /* be lazy, never mark this closed */ } /* initialize the cwd -- this is never freed...*/ cwd = xgetcwd(0); if (!cwd) cwd = bb_msg_unknown;#ifdef CONFIG_FEATURE_CLEAN_UP atexit(free_memory);#endif#ifdef CONFIG_FEATURE_COMMAND_EDITING cmdedit_set_initial_prompt();#else PS1 = NULL;#endif return (busy_loop(input));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -