Soramichi's blog

Some seek complex solutions to simple problems; it is better to find simple solutions to complex problems

'who am i' does not work in recent GNOME-terminal (and MATE-terminal)

The 'who am i' idiom and the problem

The *unix command who is used to "show who is logged on" (c.f. https://linux.die.net/man/1/who ), and who am i, which is equivalent to who -m, is an idiomatic usage of who that only shows the user who calls this command. So it tells who you are. A funny thing is that any string works as the arguments. The command only checks if the number of arguments is 2 and then executes who -m if it is. Thus, as the manual says, who mother loves yields the same result.

The problem is that it does not work in recent GNOME-terminal, MATE-terminal, and any other terminal emulators that rely on libvte. In these terminal emulators, who am i just returns nothing. I think this is a critical issue as who am i is used in many shell scripts to retrieve the current user name.

f:id:sorami_chi:20180217231615p:plain

How it is implemented

Before explaining why it doesn't work, I describe how it is implemented. The who.c file of gnu coreutils relies on the file named /var/run/utmp. This file contains login logs of the system and what who.c does is basically just opens the file, parse it and print the log entries.

In each entry of /var/run/utmp, a username and login information of that user are recorded such as the pid of the login process and the device name of the terminal the user is associated with (see the manual for the details). who am i compares the terminal device name in an entry with a string obtained by ttyname(3), and prints the entry if they match.

  if (my_line_only) // when -m is specified
    {
      // retrieves the device name of stdin
      ttyname_b = ttyname (STDIN_FILENO);
      ...
    }

  while (n--) // for each entry of /var/run/utmp
    {
      // if -m is not specified, or
      // the device name of stdin is equal to the device name of the terminal associated with this user
      if (!my_line_only
          || STREQ_LEN (ttyname_b, utmp_buf->ut_line,
                        sizeof (utmp_buf->ut_line)))
        {
        // parse and print the entry
        ...
        }

Code: A part of who.c of gnu coreutils (comments are added by me)

Why it doesn't work and what to do

The root cause behind this problem is that /var/run/utmp is not magically maintained by the OS and it has to be updated by each process, while recent versions of libvte does not do this. This is not a bug of libvte, but an intentional choice discussed like here. Although in the same thread a concern about who am i was raised, it was just removed to improve the code cleanness (it seems like the function to update /var/run/utmp was in a very nasty file that is almost never used). Because both GNOME-terminal and MATE-terminal use libvte, this problem exist in both of them.

Two easy alternatives are (1) to use xterm, which does not rely on libvte and updates /ver/run/tmp by itself, and (2) to use the whoami command, which uses a different mechanism, instead of who am i.