-
Notifications
You must be signed in to change notification settings - Fork 0
/
run_keybase
executable file
·277 lines (233 loc) · 10.6 KB
/
run_keybase
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
#!/usr/bin/env bash
set -e -u -o pipefail
# This is the script responsible for launching keybase on boot on Linux. A
# .desktop file will be created by the service on first launch in
# ~/.config/autostart/ to invoke this script.
systemd_stop_if_active() {
service="$1";
if command -v systemctl &> /dev/null && systemctl --user is-active -q "$service"; then
systemctl --user stop "$service"
fi
}
# This works no matter how the services were started, because our
# Restart=on-failure systemd unit configuration won't restart after SIGTERM.
kill_all() {
# systemd will restart services if they failed to stop cleanly, so do this explicitly
# first to make the rest of a function a no-op.
if [ "$KEYBASE_KILL" = "1" ]; then
systemd_stop_if_active "keybase"
systemd_stop_if_active "kbfs"
systemd_stop_if_active "keybase.gui"
systemd_stop_if_active "keybase-redirector"
fi
# Only stop main Electron process; others have additional flags after the process name
# Child Electron processes will be stopped by main process' handler
if main_electron_pids="$(pgrep 'Keybase$')"; then
# intentionally splitting pids
# shellcheck disable=SC2046
kill $(echo "$main_electron_pids" | xargs) &> /dev/null && echo Shutting down Keybase GUI...
fi
# mountdir may be empty on the initial install, so don't try to unmount in that case.
if mountdir="$(keybase config get --direct --bare mountdir 2> /dev/null)" && [ -n "$mountdir" ]; then
# Redundant in newer kbfsfuses, which catches SIGTERM and unmounts before exiting.
fusermount -uz "$mountdir" &> /dev/null && echo Unmounting "$mountdir"...
fi
killall kbfsfuse &> /dev/null && echo Unmounting and shutting down kbfsfuse...
killall keybase &> /dev/null && echo Shutting down keybase service...
# Only shut down redirector when stopping, but not for restarts.
if [ "$KEYBASE_KILL" = "1" ]; then
pkill -f keybase-redirector &> /dev/null && echo Shutting down keybase redirector...
fi
}
start_systemd() {
echo Starting via systemd...
# Reload possibly-updated unit files.
# This occurs in post-install, but only if it's actually packaged
# and the user was already running Keybase.
systemd_errmsg="Failed to load systemd units. If systemd is not supported, please 'export KEYBASE_SYSTEMD=0' before running this command."
systemctl --user daemon-reload || echo "$systemd_errmsg"
# We don't want to persist this, so don't store it in the env file.
# This is unset right after start in the unit file so subsequent direct
# systemctl calls don't have it set.
systemctl --user set-environment "KEYBASE_AUTOSTART=$KEYBASE_AUTOSTART"
# The keybase.gui.service unit has keybase.service as dependencies, so we
# don't have to list them here. But including them lets us report an error if
# they fail to start. Also prefer `restart` to `start` so that we don't race
# against the service shutting down. kbfs.service will attempt to start the
# redirector, but it isn't an error if it fails to start, which happens if it
# is disabled.
systemctl --user restart keybase.service
[ "$KEYBASE_NO_KBFS" == "1" ] || systemctl --user restart kbfs.service
gui_fail_help="Failed to launch GUI. Pass -g to prevent startup if on a machine without a graphical display."
[ "$KEYBASE_NO_GUI" == "1" ] || systemctl --user restart keybase.gui.service || echo "$gui_fail_help"
}
run_redirector_in_background() {
if ! keybase --use-root-config-file config get --direct --assert-false --assert-ok-on-nil disable-root-redirector &> /dev/null; then
return 0
fi
redirector_log="$logdir/keybase.redirector.log"
# An older version of post_install.sh could have made a redirector log
# here that's owned by root. If we can't write to it, then just nuke it
# and overwrite.
if [ -e "$redirector_log" ] && [ ! -w "$redirector_log" ]; then
rm -f "$redirector_log"
fi
echo Starting the redirector...
# We need nohup so the redirector doesn't terminate on shell exit,
# but it isn't necessary for keybase/kbfs/gui which autofork.
nohup keybase-redirector /keybase >> "$redirector_log" 2>&1 &
}
start_background() {
echo Launching keybase service...
# We set the --auto-forked flag here so that updated clients that try to
# restart this service will know to re-fork it themselves. That's all it does.
keybase --debug --use-default-log-file service --auto-forked &>> "$logdir/keybase.start.log" &
if [ "$KEYBASE_NO_KBFS" != "1" ]; then
run_redirector_in_background
echo Starting KBFS...
# The only time kbfsfuse -log-to-file prints to stdout is if the mount
# fails. So, if it does fail, because the directory does not exist, or
# has invalid permissions, the user will be notified on the command line
# after running `run_keybase`, but otherwise stdout will not be cluttered
# with other various log messages.
( kbfsfuse -debug -log-to-file | tee "$logdir/keybase.start.log" ) &
fi
if [ "$KEYBASE_NO_GUI" != "1" ]; then
# For system tray icon due to an upstream Electron issue:
# https://github.com/electron/electron/issues/10887.
# Also exported in systemd keybase.gui unit explicitly.
export XDG_CURRENT_DESKTOP=Unity
export KEYBASE_AUTOSTART="$KEYBASE_AUTOSTART"
echo Launching Keybase GUI...
gui_log="$logdir/Keybase.app.log"
"$KEYBASE" &>> "$gui_log" &
fi
}
# Warn if the keybase binary path is unexpected, possibly due to a conflicting
# binary from the Node client. Can silence with KEYBASE_PATH_WARNING=0.
warn_if_weird_path() {
if [ "${KEYBASE_PATH_WARNING:-}" = "0" ] ; then
return
fi
if [ "$(command -v keybase)" != "/usr/bin/keybase" ] ; then
echo "WARNING: Expected the keybase executable to be /usr/bin/keybase, but it's"
echo " $(command -v keybase) instead. Do you have multiple versions installed?"
echo " Export KEYBASE_PATH_WARNING=0 to silence this warning."
fi
}
show_cryptosquirrel() {
if n_colors="$(tput colors 2> /dev/null)" && [ "$n_colors" -gt 2 ]; then
[ "${KEYBASE_NO_SQUIRREL:-}" != "1" ] && cat /opt/keybase/crypto_squirrel.txt
fi
}
warn_if_exists_and_unwritable() {
if ! [ -e "$1" ] || [ -w "$1" ]; then
return
fi
echo "WARNING: Cannot write to $1. Did you previously run 'run_keybase' with sudo?"
echo " Keybase does not need root privileges to run."
echo " Permissions can be restored by running 'sudo chown -R $(whoami):$(whoami) $1',"
echo " after which 'run_keybase' can be run again."
}
init() {
logdir="${XDG_CACHE_HOME:-$HOME/.cache}/keybase"
runtime_dir="${XDG_RUNTIME_DIR:-$HOME/.config}/keybase"
warn_if_exists_and_unwritable "$logdir"
warn_if_exists_and_unwritable "$runtime_dir"
warn_if_exists_and_unwritable "$HOME/.config"
warn_if_exists_and_unwritable "$HOME/.local/share/keybase/keybase.leveldb"
warn_if_exists_and_unwritable "$HOME/.config/keybase"
warn_if_exists_and_unwritable "$HOME/.config/keybase/gui_config.json"
warn_if_exists_and_unwritable "$HOME/.cache/keybase"
# Cannot do in go due to background processes being piped to log in bash
mkdir -p "$logdir"
# Cannot do in go due to flock using a file in this directory
mkdir -p "$runtime_dir"
keybase ctl init
# Remove legacy envfiles; now stored in config directory by ctl init
rm -f "$runtime_dir/keybase.env" "$runtime_dir/keybase.kbfs.env" "$runtime_dir/keybase.gui.env"
# Allow distributions to change the location of the gui as long as it's in PATH.
if command -v Keybase &> /dev/null; then
KEYBASE=Keybase
else
KEYBASE=/opt/keybase/Keybase
fi
}
check_for_url_scheme_or_saltpack_file_launch() {
# We set a URL scheme handler on our .desktop file, and since that file
# points to this script, that's what gets called with URL links. We can
# differentiate between a normal run_keybase invocation and a URL launch
# by checking argv.
#
# We also have a .saltpack MIME type registration, so double clicking on
# a saltpack file will also cause run_keybase to receive a $1 argument
# of a filename to open.
# If Keybase is already running, then pass it the link and exit.
if [[ $# -eq 1 && "$1" =~ ^(/|web\+|keybase).* ]]; then
if pgrep -u "$USER" -f 'Keybase$' &> /dev/null; then
"$KEYBASE" "$1" &
exit
fi
fi
# Since we didn't exit, fall through to a normal startup.
}
startup_all() {
# There is a race condition where if we try to start the keybase service before
# the previous process has died, we might fail to lock the pid file and error
# out. Avoid this by waiting for the lock file to be free, on systems with flock
# installed.
lockfile="$runtime_dir/keybased.pid"
if command -v flock &> /dev/null && [ -e "$lockfile" ] ; then
flock "$lockfile" true
fi
warn_if_weird_path
if keybase ctl wants-systemd &> /dev/null; then
start_systemd
else
start_background
fi
echo 'run_keybase: Success!'
show_cryptosquirrel
}
usage() {
echo "Usage: run_keybase [-afghk]"
echo "Starts the Keybase service, KBFS, and the GUI."
echo "If services are already running, they will be restarted."
echo ""
echo "Options can also be controlled by setting related environment variables to 1"
echo " -a keep the GUI minimized in system tray after startup (env KEYBASE_AUTOSTART=1)"
echo " -f do not start KBFS (env KEYBASE_NO_KBFS=1)"
echo " -g do not start the gui (env KEYBASE_NO_GUI=1)"
echo " -h print this help text"
echo " -k shut down all Keybase services (env KEYBASE_KILL=1)"
}
KEYBASE_NO_GUI="${KEYBASE_NO_GUI:-0}"
KEYBASE_NO_KBFS="${KEYBASE_NO_KBFS:-0}"
KEYBASE_AUTOSTART="${KEYBASE_AUTOSTART:-0}"
KEYBASE_KILL="${KEYBASE_KILL:-0}"
# NOTE: Make sure to update the Linux User Guide doc if you change this!
# http://keybase.io/docs/linux-user-guide
while getopts "afghk" flag; do
case $flag in
a) KEYBASE_AUTOSTART=1;;
f) KEYBASE_NO_KBFS=1;;
g) KEYBASE_NO_GUI=1;;
h) usage; exit 0;;
k) KEYBASE_KILL=1;;
?) usage; exit 1;;
esac
done
init
# Exit early if run caused by a URL scheme invocation and Keybase is already
# running; otherwise fall through to start Keybase.
check_for_url_scheme_or_saltpack_file_launch "$@"
# Always stop any running services. With systemd, we could've decided to just
# `start` services and no-op if they're already running, however:
# 1) We still need to handle the case where services started outside systemd
# are currently running, and making that totally reliable is tricky.
# 2) Users have come to expect that run_keybase will restart everything, and
# we tell them to do it after updates.
kill_all
if [ "$KEYBASE_KILL" = "0" ]; then
startup_all
fi