bootstrap.sh 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. #!/bin/sh
  2. # Copyright (C) 2022 Kovid Goyal <kovid at kovidgoyal.net>
  3. # Distributed under terms of the GPLv3 license.
  4. { \unalias command; \unset -f command; } >/dev/null 2>&1
  5. tdir=""
  6. shell_integration_dir=""
  7. echo_on="ECHO_ON"
  8. cleanup_on_bootstrap_exit() {
  9. [ "$echo_on" = "1" ] && command stty "echo" 2> /dev/null < /dev/tty
  10. echo_on="0"
  11. [ -n "$tdir" ] && command rm -rf "$tdir"
  12. tdir=""
  13. }
  14. die() {
  15. if [ -e /dev/stderr ]; then
  16. printf "\033[31m%s\033[m\n\r" "$*" > /dev/stderr;
  17. elif [ -e /dev/fd/2 ]; then
  18. printf "\033[31m%s\033[m\n\r" "$*" > /dev/fd/2;
  19. else
  20. printf "\033[31m%s\033[m\n\r" "$*";
  21. fi
  22. cleanup_on_bootstrap_exit;
  23. exit 1;
  24. }
  25. python_detected="0"
  26. detect_python() {
  27. if [ python_detected = "1" ]; then
  28. [ -n "$python" ] && return 0
  29. return 1
  30. fi
  31. python_detected="1"
  32. python=$(command -v python3)
  33. [ -z "$python" ] && python=$(command -v python2)
  34. [ -z "$python" ] && python=$(command -v python)
  35. if [ -z "$python" -o ! -x "$python" ]; then python=""; return 1; fi
  36. return 0
  37. }
  38. perl_detected="0"
  39. detect_perl() {
  40. if [ perl_detected = "1" ]; then
  41. [ -n "$perl" ] && return 0
  42. return 1
  43. fi
  44. perl_detected="1"
  45. perl=$(command -v perl)
  46. if [ -z "$perl" -o ! -x "$perl" ]; then perl=""; return 1; fi
  47. return 0
  48. }
  49. if command -v base64 > /dev/null 2> /dev/null; then
  50. base64_encode() { command base64 | command tr -d \\n\\r; }
  51. base64_decode() { command base64 -d; }
  52. elif command -v openssl > /dev/null 2> /dev/null; then
  53. base64_encode() { command openssl enc -A -base64; }
  54. base64_decode() { command openssl enc -A -d -base64; }
  55. elif command -v b64encode > /dev/null 2> /dev/null; then
  56. base64_encode() { command b64encode - | command sed '1d;$d' | command tr -d \\n\\r; }
  57. base64_decode() { command fold -w 76 | command b64decode -r; }
  58. elif detect_python; then
  59. pybase64() { command "$python" -c "import sys, base64; getattr(sys.stdout, 'buffer', sys.stdout).write(base64.standard_b64$1(getattr(sys.stdin, 'buffer', sys.stdin).read()))"; }
  60. base64_encode() { pybase64 "encode"; }
  61. base64_decode() { pybase64 "decode"; }
  62. elif detect_perl; then
  63. base64_encode() { command "$perl" -MMIME::Base64 -0777 -ne 'print encode_base64($_)'; }
  64. base64_decode() { command "$perl" -MMIME::Base64 -ne 'print decode_base64($_)'; }
  65. else
  66. die "base64 executable not present on remote host, ssh kitten cannot function."
  67. fi
  68. dcs_to_kitty() { printf "\033P@kitty-$1|%s\033\134" "$(printf "%s" "$2" | base64_encode)" > /dev/tty; }
  69. debug() { dcs_to_kitty "print" "debug: $1"; }
  70. # If $HOME is configured set it here
  71. EXPORT_HOME_CMD
  72. # ensure $HOME is set
  73. [ -z "$HOME" ] && HOME=~
  74. # ensure $USER is set
  75. [ -z "$USER" ] && USER="$LOGNAME"
  76. [ -z "$USER" ] && USER="$(command whoami 2> /dev/null)"
  77. leading_data=""
  78. login_shell=""
  79. login_cwd=""
  80. request_data="REQUEST_DATA"
  81. trap "cleanup_on_bootstrap_exit" EXIT
  82. [ "$request_data" = "1" ] && {
  83. command stty "-echo" < /dev/tty
  84. dcs_to_kitty "ssh" "id="REQUEST_ID":pwfile="PASSWORD_FILENAME":pw="DATA_PASSWORD""
  85. }
  86. read_base64_from_tty() {
  87. while IFS= read -r line; do
  88. [ "$line" = "KITTY_DATA_END" ] && return 0
  89. printf "%s" "$line"
  90. done
  91. }
  92. untar_and_read_env() {
  93. # extract the tar file atomically, in the sense that any file from the
  94. # tarfile is only put into place after it has been fully written to disk
  95. command -v tar > /dev/null 2> /dev/null || die "tar is not available on this server. The ssh kitten requires tar."
  96. tdir=$(command mktemp -d "$HOME/.kitty-ssh-kitten-untar-XXXXXXXXXXXX")
  97. [ $? = 0 ] || die "Creating temp directory failed"
  98. # suppress STDERR for tar as tar prints various warnings if for instance, timestamps are in the future
  99. old_umask=$(umask)
  100. umask 000
  101. read_base64_from_tty | base64_decode | command tar "xpzf" "-" "-C" "$tdir" 2> /dev/null
  102. umask "$old_umask"
  103. . "$tdir/bootstrap-utils.sh"
  104. . "$tdir/data.sh"
  105. [ -z "$KITTY_SSH_KITTEN_DATA_DIR" ] && die "Failed to read SSH data from tty"
  106. case "$KITTY_SSH_KITTEN_DATA_DIR" in
  107. /*) data_dir="$KITTY_SSH_KITTEN_DATA_DIR" ;;
  108. *) data_dir="$HOME/$KITTY_SSH_KITTEN_DATA_DIR"
  109. esac
  110. shell_integration_dir="$data_dir/shell-integration"
  111. unset KITTY_SSH_KITTEN_DATA_DIR
  112. login_shell="$KITTY_LOGIN_SHELL"
  113. unset KITTY_LOGIN_SHELL
  114. login_cwd="$KITTY_LOGIN_CWD"
  115. unset KITTY_LOGIN_CWD
  116. kitty_remote="$KITTY_REMOTE"
  117. unset KITTY_REMOTE
  118. compile_terminfo "$tdir/home"
  119. mv_files_and_dirs "$tdir/home" "$HOME"
  120. [ -e "$tdir/root" ] && mv_files_and_dirs "$tdir/root" ""
  121. command rm -rf "$tdir"
  122. tdir=""
  123. }
  124. get_data() {
  125. started="n"
  126. while IFS= read -r line; do
  127. if [ "$started" = "y" ]; then
  128. [ "$line" = "OK" ] && break
  129. die "$line"
  130. else
  131. if [ "$line" = "KITTY_DATA_START" ]; then
  132. started="y"
  133. else
  134. leading_data="$leading_data$line"
  135. fi
  136. fi
  137. done
  138. untar_and_read_env
  139. }
  140. # ask for the SSH data
  141. get_data
  142. cleanup_on_bootstrap_exit
  143. prepare_for_exec
  144. # If a command was passed to SSH execute it here
  145. EXEC_CMD
  146. # Used in the tests
  147. TEST_SCRIPT
  148. exec_login_shell