Reading Bash [DRAFT]
Created at 2017-04-04T10:18:47+09:00

Build from source

out-of-tree build and out-of-tree install.

$ mkdir -p out/Default/_install
$ cd out/Default
$ ../../configure --prefix=$PWD/_install CC=clang
$ make -j 4 install
$ lldb ./_install/bin/bash

For debugging, I added these lines on top of main:

int
main (argc, argv, env)
     int argc;
     char **argv, **env;
{
  printf("getpid: %d\n", getpid());
  sleep (10);
...

bash will be started like this:

$ out/Default/_install/bin/bash
getpid: 21502

From another shell, you can attach debugger:

$ lldb -p 21502

I don't know why but child gets segmentation fault if I step-execute from parent before execve.

Source Reading

I'll quote standard library functions (e.g. `setsid`).

Here, I assume bash is started as pid 21502 and tty 34832.

[Main path]
- main =>
  - check_dev_tty => `open` '/dev/tty' and `close`
  - uidget =>
    - `getuid`, `getgid`, `geteuid`, and `getegid`
    - update current_user global
  - if `isatty(0)` and `isatty(2)` then init_interactive
  - shell_initialize =>
    - sh_setlinebuf stderr, stdout => `setvbuf`
    - initialize_shell_builtins => (shell_builtins, check mkbuiltins.c ?)
    - get_current_user_info => `getpwid`
    - initialize_job_control =>
      - `isatty` ?
      - `getpgid(0)` (assigned to shell_pgrp)
      - `dup(2)` and get 3
      - move_to_high_fd(3) => `dup2(3, 255)` (assign 255 to shell_tty)
      - `tcgetpgrp` (assign 21502 to terminal_pgrp)
      - `getpid` (assigned to shell_pgrp)
      - SET_CLOSE_ON_EXEC(shell_tty) (which is 255) => `fcntl(255, F_SETFD, FD_CLOEXEC)`
      - get_tty_state =>
        - `tcgetattr(255)`
      - getmaxchild => `sysconf(_SC_CHILD_MAX)`
    - initialize_bash_input (parse.y thing ?)
  - run_startup_files =>
    - set_job_control
    - maybe_execute_file("~/.bashrc") =>
      - _evalfile => parse_and_execute
    - set_job_control
  - set_bash_input =>
    - with_input_from_stdin (parse.y thing ?)
  - bind_args
  - bash_initialize_history
  - load_history
  - get_tty_state
  - reader_loop =>
    - read_command (we'll have "struct command" in global_command)
      - => parse_command => yyparse, yylex ..., yy_readline_get, readline, readlin_internal ...
    - execute_command => execute_command_internal (branches depending on enum command_type)

[Spawning process (e.g. /bin/ls)]
- execute_command_internal =>
  - execute_simple_command =>
    - (after going through a lot of condition (e.g. builtin command, fg, bg, etc...))
    - execute_disk_command =>
      - make_child("/bin/ls") =>
        - making_children => start_pipeline => `pipe` (got [3, 4] and assigned to pgrp_pipe)
        - pid = `fork`
          - (child path) (when lldb-ing it's already zonbie though...)
            - `getpid` (assigned to mypid)
            - pipeline_pgrp = my_pid (only for first child in pipeline)
            - default_tty_job_signals => ?
            - `setpgid(mypid, pipeline_pgrp)`
            - give_terminal_to(pipeline_pgrp, 0) => `tcsetpgrp(pipeline_pgrp)`
            - pipe_read() => `read` (first child stops continuing until parent does sh_closepipe below ?)
          - (parent path)
            - pipeline_pdrp = pid (does same on parent too)
            - `setpgid(pid, pipeline_pgrp)` (does same on parent as well)
            - add_process (book keeping the list of child process)
      - (child path)
        - do_piping => ?
        - do_redirections (if it's necessary) => ?
        - shell_execve => `execve`
      - (parent path)
        - close_pipes (it's not "pipe" for pgrp_pipe pipeline. I think this is about top level redirection ?)
  - stop_pipeline => ?
    - sh_closepipe (pgrp_pipe) (this starts blocked child at 'pipe_read' ?)
    - maybe_give_terminal_to => give_terminal_to (child)
  - wait_for =>
    - waitchld => ??
    - give_terminal_to(shell_pgrp) (make tty back to parent i.e. shell itself)
    - set_tty_state => `tcsetattr`


[Foreground/background job control]
- read_command =>
  - ?
    - tcsetpgrp ?

[Ctrl-Z and fg or bg]

[Pipe and redirection]
- fork => close => dup => exec ?

[Builtin command]
- read_command =>
  - help

Architecture

  • tty handling
  • process handling
  • job handling
  • posix conformance
  • tab completion
  • unicode encoding