kovidgoyal / kitty

Cross-platform, fast, feature-rich, GPU based terminal
https://sw.kovidgoyal.net/kitty/
GNU General Public License v3.0
24.12k stars 971 forks source link

macOS Ventura: getlogin() syscall returns 'root' in kitty vs username #6511

Closed sfsam closed 1 year ago

sfsam commented 1 year ago

I must be doing something obviously wrong, but I don't know what...

Describe the bug Calling the getlogin() system call from a simple C program returns root when run in kitty. Running the same program in Terminal.app returns the correct username.

To Reproduce Run the following program in kitty and Terminal.app and compare output. Both kitty and Terminal.app are launched the standard way by double-clicking the app icon in the /Applications folder.

#include <stdio.h>
#include <unistd.h>
int
main(void) {
    printf("Welcome to CS631 Advanced Programming in the UNIX Environment, %s!\n", getlogin());
}

Screenshots kitty:

Screenshot 2023-07-30 at 11 03 30 AM

Terminal.app:

Screenshot 2023-07-30 at 11 04 15 AM

Environment details

kitty 0.29.1 (93ae6e962f) created by Kovid Goyal
Darwin Sanjays-MacBook-Pro-23.local 22.5.0 Darwin Kernel Version 22.5.0: Thu Jun  8 22:22:23 PDT 2023; root:xnu-8796.121.3~7/RELEASE_ARM64_T6020 arm64
ProductName:        macOS ProductVersion:       13.4.1 ProductVersionExtra: (c) BuildVersion:       22F770820d
Frozen: True
Paths:
  kitty: /Applications/kitty.app/Contents/MacOS/kitty
  base dir: /Applications/kitty.app/Contents/Resources/kitty
  extensions dir: /Applications/kitty.app/Contents/Resources/Python/lib/kitty-extensions
  system shell: /bin/zsh
Loaded config files:
  /Users/sanjay/.config/kitty/kitty.conf

Config options different from defaults:
allow_remote_control               yes
bold_font                          Source Code Pro Bold
bold_italic_font                   Source Code Pro Bold Italic
confirm_os_window_close            0
cursor_beam_thickness              2.0
font_family                        Source Code Pro Medium
font_size                          14.0
initial_window_height              (30, 'cells')
initial_window_width               (95, 'cells')
italic_font                        Source Code Pro Italic
listen_on                          unix:/tmp/mykitty
macos_option_as_alt                3
macos_quit_when_last_window_closed True
macos_show_window_title_in         window
macos_titlebar_color               -2
mouse_hide_wait                    -1.0
remember_window_size               False
resize_in_steps                    True
tab_bar_style                      powerline
tab_powerline_style                slanted
window_border_width                (4.0, 'px')
window_resize_step_cells           1
window_resize_step_lines           1
Added shortcuts:
    cmd+[ →  previous_window
    cmd+] →  next_window
Colors:
    active_border_color                #3e953a   
    background                         #161616   
    color1                             #d8633c   
    color10                            #76b768   
    color11                            #eed64a   
    color12                            #387bd2   
    color13                            #957bbd   
    color14                            #3d96e2   
    color15                            #f3f3f3   
    color2                             #68c156   
    color3                             #f1d32b   
    color4                             #0c88d8   
    color5                             #8e69c8   
    color6                             #1c98e8   
    color7                             #f3f3f3   
    color8                             #000000   
    color9                             #df5a4f   
    cursor                             #ea4ca2   
    cursor_text_color                  #ffffff   
    foreground                         #a6a6a6   
    inactive_border_color              #ffffff   
    selection_background               #f1d32b   
    selection_foreground               #212324   

Important environment variables seen by the kitty process:
    PATH                                /Applications/kitty.app/Contents/MacOS:/usr/bin:/bin:/usr/sbin:/sbin
    LANG                                en_US.UTF-8
    SHELL                               /bin/zsh
    USER                                sanjay

On older versions of kitty, run kitty --debug-config instead

Additional context kitty --config NONE reproduces the same output: root instead of username.

kovidgoyal commented 1 year ago

Works for me with

kitty --config=NONE

and inside kitty run

kitty +runpy "import os; print(os.getlogin())"

sfsam commented 1 year ago

I still get root with kitty --config=NONE and kitty +runpy "import os; print(os.getlogin())".

Both Terminal.app and Alacritty report the correct username.

Why might kitty report root, but the other terminal emulators report the username?

FWIW, I installed kitty with the command on https://sw.kovidgoyal.net/kitty/binary/

kovidgoyal commented 1 year ago

kitty isnt reporting anything. getlogin() does not interact with the controlling terminal in any way. IIRC it uses a bunch of heuristics like looking at the file owners of controlling tty devices, env vars, etc. Since apple is closed source no way to know exactly what getlogin() does.

andrelaszlo commented 1 year ago

From macos man 2 getlogin:

The getlogin() routine returns the login name of the user associated with the current session, as previously set by setlogin(). The name is normally associated with a login shell at the time a session is created, and is inherited by all processes descended from the login shell. (This is true even if some of those processes assume another user ID, for example when su(1) is used.)

I think the setlogin part is the key here. It's not that Kitty is doing something wrong, it's just that the other macos terminals are starting login shells. You can replicate the default Kitty behavior in iTerm, for example, by changing the "Command" setting from "Login Shell" to "Command", and passing "/bin/zsh". If you then run python3 -c 'import os; print(os.getlogin())' or something else that calls the getlogin routine, it'll report root as well.

After some experiments (just switched to macos from Arch...) it seems like what happens is that the init process in macos (launchd) is the parent of any program that you start as a logged in user. The launchd process itself has uid=0, or root. It doesn't appear to call setlogin each time a program is started, but just setuid (or something similar). I think that in Linux the parent process is usually a login shell or maybe calls setlogin(haven't checked).

What iTerm, terminal.app, and others seem to be doing is to call login when launching your shell. Here's the htop output in tree mode:

image

The top -zsh process was started with the default iTerm setting ("Login Shell"), and the second one is using the custom "Command" set to "/bin/zsh".

I've browsed the issues in this repo and it seems like @kovidgoyal understands this much better than I ever will. Here's my attempts at fixing this issue by updating the shell in my kitty config:

shell .
# Kitty spawns `-zsh`, getlogin returns `root`
shell /bin/zsh --login --interactive
# Kitty spawns -zsh --login --interactive, getlogin returns `root
shell /usr/bin/login -f andrelaszlo /bin/zsh
# Kitty spawns `login`, which spawns `-zsh`, getlogin returns `andrelaszlo`

The last one matches the behavior of other terminal apps on macos, both in terms of what they spawn and what getlogin returns. The problem is that for some reason the TERMINFO environment variable isn't set properly now, which of course causes problems. Another issue is that my username is hard coded in the config, maybe there's a workaround? iTerm has simple variable substitutions, like $$USERNAME$$.

Maybe this is not the biggest issue, but I wanted to add the info I found in case it helps someone else. I haven't figured out how the TERMINFO environment variable (and the other Kitty env vars of course) is set, but if that worked I would have had an okay workaround at least! :)

andrelaszlo commented 1 year ago

Small update, this works perfectly:

shell /usr/bin/login -fpq andrelaszlo /bin/zsh

But the kitty variable substitution doesn't seem to work the way I expected it to:

shell /usr/bin/login -fpq ${USER} /bin/zsh

Gives ´login incorrect`. Tried finding a debug flag that would log what it's expanded to, but didn't find anything.

Instead, I tried this - to see if it expands the variable at all:

shell "/opt/homebrew/bin/bash" "-c" "echo user: ${USER}; sleep 10"
image

I tried various ways of quoting the shell /usr/bin/login -fpq ${USER} /bin/zsh line, but always ended up with login incorrect, despite ${USER} expanding correctly to andrelaszlo in the bash echo experiment.

This monstrosity does what I want, except it also starts a bash process of course 😆

shell "/opt/homebrew/bin/bash" "-c" "/usr/bin/login -fpq ${USER} /bin/zsh"

Next step would be to read the Kitty code to understand the variable expansion better, but it's getting late! 😴 Would be super interesting to know what's going on though.

kovidgoyal commented 1 year ago

kitty does start shells in login mode by default on macOS. You dont need to set shell at all, see line 277 onwards in child.py

andrelaszlo commented 1 year ago

kitty does start shells in login mode by default on macOS.

@kovidgoyal True! I should have written a better summary:

I was confused about env var expansion yesterday, my bad! It seems like it's only working for include* lines, and that the expansion I managed to get working was actually done by bash 🤦

kovidgoyal commented 1 year ago

On Sat, Sep 23, 2023 at 09:24:44AM -0700, André Laszlo wrote:

kitty does start shells in login mode by default on macOS.

@kovidgoyal True! I should have written a better summary:

  • There seems to be a bug in getlogin on macos (at least my version, 13.5 "Ventura") since it returns root for processes even though they're running under a login shell with a different uid. This kind of contradicts the macos getlogin man page:

    The getlogin() routine returns the login name of the user associated with the current session, as previously set by setlogin(). The name is normally associated with a login shell at the time a session is created, and is inherited by all processes descended from the login shell

  • Kitty starts login shells just fine, but apparently that's not enough. Macos doesn't seem to run the user session inside a login shell, so setlogin is never called (and .profile never loaded). I learned a little bit about this in this SO answer.

No, starting a shell in login mode is enough to load ~/.bash_profile. See man bash or man zsh. As long as the shell is started in login mode it will read /etc/profile and ~/.profile and kitty does this.

  • The way lesser other terminals work on macos is that they start shells using the login (as in /usr/bin/login), which will not only launch the shell in login mode automatically, but also call setlogin - which makes the issue of getlogin returning root go away - (as well as solve mask the issues other Kitty users might have had with profiles not being loaded etc).

The only issue here is getlogin(). Reading the macOS login manpage it messes with a bunch of env vars including TERM, so it seems to me the cure is worse than the disease. The way to do this would probably be to use the run-shell kitten. Something like

/usr/bin/login -fpl $USER kitten run-shell --shell= --shell-integration= --env TERM= --env PATH=<value of PATH from kitty instance> --env SHELL=

  • In order to launch a shell with login, you need to pass the current username, so environment variable expansion for shell config lines might be useful.

I am OK with implementing env var expansion for this setting.

kovidgoyal commented 1 year ago

https://github.com/kovidgoyal/kitty/commit/76c6f9168508aa322f2f6e847e9d0eeb9c713b59

andrelaszlo commented 1 year ago

@kovidgoyal Amazing, thanks for taking the time!