Jack Li's Blog

Implement isatty on xv6

Problem #

This bug happens when we try to run shell commands from a file:

// xargstest.sh
mkdir a
echo hello > a/b
mkdir c
echo hello > c/b
echo hello > b
find . b | xargs grep hello

It will print out $ for all commands:

$ sh < xargstest.sh
$ $ $ $ $ $ hello
hello
hello
$ $

The problem is that we need to determine where the input comes from. If it comes from terminal, the program should print out $. If it comes from file, the program should not print out $.

isatty #

isatty is a system call that telling us whether the file descriptor comes from terminal or not.

int main()
{
  if(isatty(0)){
    printf("0 is device\n");
  } else {
    printf("0 is file\n");
  }
  exit(0);
}

After writing some tests, we can see that when isatty is false when we send commands to stdin by file redirection.

$ ./main
0 is device
$ ./main < input.txt
0 is file

Therefore, we need to implement isatty on xv6 to support this feature. The logic here is simple and not be tested for every scenario.

// kernel/sysfile.c
uint64
sys_isatty(void)
{
  int fd;
  struct file *f;

  if(argfd(0, &fd, &f) < 0){
    return -1;
  }

  return f->type == FD_DEVICE;
}

Finally, we modify user/sh.c and only print out $ when commands come from terminal.

// user/sh.c
int
getcmd(char *buf, int nbuf)
{
  if(isatty(0)){
    fprintf(2, "$ ");
  }
  memset(buf, 0, nbuf);
  gets(buf, nbuf);
  if(buf[0] == 0) // EOF
    return -1;
  return 0;
}

Test #

$ sh < xargstest.sh
hello
hello
hello