Ostatnio aktywny 5 months ago

zyppe's Avatar zyppe zrewidował ten Gist 5 months ago. Przejdź do rewizji

1 file changed, 1 insertion, 1 deletion

install-client.sh

@@ -1,7 +1,7 @@
1 1 #!/usr/bin/env bash
2 2 #
3 3 # install_server.sh - hysteria client install script
4 - # Try `install_server.sh --help` for usage.
4 + # Try `install_client.sh --help` for usage.
5 5 #
6 6 # SPDX-License-Identifier: MIT
7 7 # Copyright (c) 2023 Aperture Internet Laboratory

zyppe's Avatar zyppe zrewidował ten Gist 5 months ago. Przejdź do rewizji

1 file changed, 1135 insertions

install-client.sh(stworzono plik)

@@ -0,0 +1,1135 @@
1 + #!/usr/bin/env bash
2 + #
3 + # install_server.sh - hysteria client install script
4 + # Try `install_server.sh --help` for usage.
5 + #
6 + # SPDX-License-Identifier: MIT
7 + # Copyright (c) 2023 Aperture Internet Laboratory
8 + #
9 +
10 + set -e
11 +
12 +
13 + ###
14 + # SCRIPT CONFIGURATION
15 + ###
16 +
17 + # Basename of this script
18 + SCRIPT_NAME="$(basename "$0")"
19 +
20 + # Command line arguments of this script
21 + SCRIPT_ARGS=("$@")
22 +
23 + # Path for installing executable
24 + EXECUTABLE_INSTALL_PATH="/usr/local/bin/hysteria"
25 +
26 + # Paths to install systemd files
27 + SYSTEMD_SERVICES_DIR="/etc/systemd/system"
28 +
29 + # Directory to store hysteria config file
30 + CONFIG_DIR="/etc/hysteria"
31 +
32 + # URLs of GitHub
33 + REPO_URL="https://github.com/apernet/hysteria"
34 +
35 + # URL of Hysteria 2 API
36 + HY2_API_BASE_URL="https://api.hy2.io/v1"
37 +
38 + # curl command line flags.
39 + # To using a proxy, please specify ALL_PROXY in the environ variable, such like:
40 + # export ALL_PROXY=socks5h://192.0.2.1:1080
41 + CURL_FLAGS=(-L -f -q --retry 5 --retry-delay 10 --retry-max-time 60)
42 +
43 +
44 + ###
45 + # AUTO DETECTED GLOBAL VARIABLE
46 + ###
47 +
48 + # Package manager
49 + PACKAGE_MANAGEMENT_INSTALL="${PACKAGE_MANAGEMENT_INSTALL:-}"
50 +
51 + # Operating System of current machine, supported: linux
52 + OPERATING_SYSTEM="${OPERATING_SYSTEM:-}"
53 +
54 + # Architecture of current machine, supported: 386, amd64, arm, arm64, mipsle, s390x
55 + ARCHITECTURE="${ARCHITECTURE:-}"
56 +
57 + # User for running hysteria
58 + HYSTERIA_USER="${HYSTERIA_USER:-}"
59 +
60 + # Directory for ACME certificates storage
61 + HYSTERIA_HOME_DIR="${HYSTERIA_HOME_DIR:-}"
62 +
63 + # SELinux context of systemd unit files
64 + SECONTEXT_SYSTEMD_UNIT="${SECONTEXT_SYSTEMD_UNIT:-}"
65 +
66 +
67 + ###
68 + # ARGUMENTS
69 + ###
70 +
71 + # Supported operation: install, remove, check_update
72 + OPERATION=
73 +
74 + # User specified version to install
75 + VERSION=
76 +
77 + # Force install even if installed
78 + FORCE=
79 +
80 + # User specified binary to install
81 + LOCAL_FILE=
82 +
83 +
84 + ###
85 + # COMMAND REPLACEMENT & UTILITIES
86 + ###
87 +
88 + has_command() {
89 + local _command=$1
90 +
91 + type -P "$_command" > /dev/null 2>&1
92 + }
93 +
94 + curl() {
95 + command curl "${CURL_FLAGS[@]}" "$@"
96 + }
97 +
98 + mktemp() {
99 + command mktemp "$@" "/tmp/hyservinst.XXXXXXXXXX"
100 + }
101 +
102 + tput() {
103 + if has_command tput; then
104 + command tput "$@"
105 + fi
106 + }
107 +
108 + tred() {
109 + tput setaf 1
110 + }
111 +
112 + tgreen() {
113 + tput setaf 2
114 + }
115 +
116 + tyellow() {
117 + tput setaf 3
118 + }
119 +
120 + tblue() {
121 + tput setaf 4
122 + }
123 +
124 + taoi() {
125 + tput setaf 6
126 + }
127 +
128 + tbold() {
129 + tput bold
130 + }
131 +
132 + treset() {
133 + tput sgr0
134 + }
135 +
136 + note() {
137 + local _msg="$1"
138 +
139 + echo -e "$SCRIPT_NAME: $(tbold)note: $_msg$(treset)"
140 + }
141 +
142 + warning() {
143 + local _msg="$1"
144 +
145 + echo -e "$SCRIPT_NAME: $(tyellow)warning: $_msg$(treset)"
146 + }
147 +
148 + error() {
149 + local _msg="$1"
150 +
151 + echo -e "$SCRIPT_NAME: $(tred)error: $_msg$(treset)"
152 + }
153 +
154 + has_prefix() {
155 + local _s="$1"
156 + local _prefix="$2"
157 +
158 + if [[ -z "$_prefix" ]]; then
159 + return 0
160 + fi
161 +
162 + if [[ -z "$_s" ]]; then
163 + return 1
164 + fi
165 +
166 + [[ "x$_s" != "x${_s#"$_prefix"}" ]]
167 + }
168 +
169 + generate_random_password() {
170 + dd if=/dev/random bs=18 count=1 status=none | base64
171 + }
172 +
173 + systemctl() {
174 + if [[ "x$FORCE_NO_SYSTEMD" == "x2" ]] || ! has_command systemctl; then
175 + warning "Ignored systemd command: systemctl $@"
176 + return
177 + fi
178 +
179 + command systemctl "$@"
180 + }
181 +
182 + chcon() {
183 + if ! has_command chcon || [[ "x$FORCE_NO_SELINUX" == "x1" ]]; then
184 + return
185 + fi
186 +
187 + command chcon "$@"
188 + }
189 +
190 + get_systemd_version() {
191 + if ! has_command systemctl; then
192 + return
193 + fi
194 +
195 + command systemctl --version | head -1 | cut -d ' ' -f 2
196 + }
197 +
198 + systemd_unit_working_directory() {
199 + local _systemd_version="$(get_systemd_version || true)"
200 +
201 + # WorkingDirectory=~ requires systemd v227 or later.
202 + # (released on Oct 2015, only CentOS 7 use an earlier version)
203 + # ref: systemd/systemd@5f5d8eab1f2f5f5e088bc301533b3e4636de96c7
204 + if [[ -n "$_systemd_version" && "$_systemd_version" -lt "227" ]]; then
205 + echo "$HYSTERIA_HOME_DIR"
206 + return
207 + fi
208 +
209 + echo "~"
210 + }
211 +
212 + get_selinux_context() {
213 + local _file="$1"
214 +
215 + local _lsres="$(ls -dZ "$_file" | head -1)"
216 + local _sectx=''
217 + case "$(echo "$_lsres" | wc -w)" in
218 + 2)
219 + _sectx="$(echo "$_lsres" | cut -d ' ' -f 1)"
220 + ;;
221 + 5)
222 + _sectx="$(echo "$_lsres" | cut -d ' ' -f 4)"
223 + ;;
224 + *)
225 + ;;
226 + esac
227 +
228 + if [[ "x$_sectx" == "x?" ]]; then
229 + _sectx=""
230 + fi
231 +
232 + echo "$_sectx"
233 + }
234 +
235 + show_argument_error_and_exit() {
236 + local _error_msg="$1"
237 +
238 + error "$_error_msg"
239 + echo "Try \"$0 --help\" for usage." >&2
240 + exit 22
241 + }
242 +
243 + install_content() {
244 + local _install_flags="$1"
245 + local _content="$2"
246 + local _destination="$3"
247 + local _overwrite="$4"
248 +
249 + local _tmpfile="$(mktemp)"
250 +
251 + echo -ne "Install $_destination ... "
252 + echo "$_content" > "$_tmpfile"
253 + if [[ -z "$_overwrite" && -e "$_destination" ]]; then
254 + echo -e "exists"
255 + elif install "$_install_flags" "$_tmpfile" "$_destination"; then
256 + echo -e "ok"
257 + fi
258 +
259 + rm -f "$_tmpfile"
260 + }
261 +
262 + remove_file() {
263 + local _target="$1"
264 +
265 + echo -ne "Remove $_target ... "
266 + if rm "$_target"; then
267 + echo -e "ok"
268 + fi
269 + }
270 +
271 + exec_sudo() {
272 + # exec sudo with configurable environ preserved.
273 + local _saved_ifs="$IFS"
274 + IFS=$'\n'
275 + local _preserved_env=(
276 + $(env | grep "^PACKAGE_MANAGEMENT_INSTALL=" || true)
277 + $(env | grep "^OPERATING_SYSTEM=" || true)
278 + $(env | grep "^ARCHITECTURE=" || true)
279 + $(env | grep "^HYSTERIA_\w*=" || true)
280 + $(env | grep "^SECONTEXT_SYSTEMD_UNIT=" || true)
281 + $(env | grep "^FORCE_\w*=" || true)
282 + )
283 + IFS="$_saved_ifs"
284 +
285 + exec sudo env \
286 + "${_preserved_env[@]}" \
287 + "$@"
288 + }
289 +
290 + detect_package_manager() {
291 + if [[ -n "$PACKAGE_MANAGEMENT_INSTALL" ]]; then
292 + return 0
293 + fi
294 +
295 + if has_command apt; then
296 + apt update
297 + PACKAGE_MANAGEMENT_INSTALL='apt -y --no-install-recommends install'
298 + return 0
299 + fi
300 +
301 + if has_command dnf; then
302 + PACKAGE_MANAGEMENT_INSTALL='dnf -y install'
303 + return 0
304 + fi
305 +
306 + if has_command yum; then
307 + PACKAGE_MANAGEMENT_INSTALL='yum -y install'
308 + return 0
309 + fi
310 +
311 + if has_command zypper; then
312 + PACKAGE_MANAGEMENT_INSTALL='zypper install -y --no-recommends'
313 + return 0
314 + fi
315 +
316 + if has_command pacman; then
317 + PACKAGE_MANAGEMENT_INSTALL='pacman -Syu --noconfirm'
318 + return 0
319 + fi
320 +
321 + return 1
322 + }
323 +
324 + install_software() {
325 + local _package_name="$1"
326 +
327 + if ! detect_package_manager; then
328 + error "Supported package manager is not detected, please install the following package manually:"
329 + echo
330 + echo -e "\t* $_package_name"
331 + echo
332 + exit 65
333 + fi
334 +
335 + echo "Installing missing dependence '$_package_name' with '$PACKAGE_MANAGEMENT_INSTALL' ... "
336 + if $PACKAGE_MANAGEMENT_INSTALL "$_package_name"; then
337 + echo "ok"
338 + else
339 + error "Cannot install '$_package_name' with detected package manager, please install it manually."
340 + exit 65
341 + fi
342 + }
343 +
344 + is_user_exists() {
345 + local _user="$1"
346 +
347 + id "$_user" > /dev/null 2>&1
348 + }
349 +
350 + rerun_with_sudo() {
351 + if ! has_command sudo; then
352 + return 13
353 + fi
354 +
355 + local _target_script
356 +
357 + if has_prefix "$0" "/dev/" || has_prefix "$0" "/proc/"; then
358 + local _tmp_script="$(mktemp)"
359 + chmod +x "$_tmp_script"
360 +
361 + if has_command curl; then
362 + curl -o "$_tmp_script" 'https://get.hy2.sh/'
363 + elif has_command wget; then
364 + wget -O "$_tmp_script" 'https://get.hy2.sh'
365 + else
366 + return 127
367 + fi
368 +
369 + _target_script="$_tmp_script"
370 + else
371 + _target_script="$0"
372 + fi
373 +
374 + note "Re-running this script with sudo. You can also specify FORCE_NO_ROOT=1 to force this script to run as the current user."
375 + exec_sudo "$_target_script" "${SCRIPT_ARGS[@]}"
376 + }
377 +
378 + check_permission() {
379 + if [[ "$UID" -eq '0' ]]; then
380 + return
381 + fi
382 +
383 + note "The user running this script is not root."
384 +
385 + case "$FORCE_NO_ROOT" in
386 + '1')
387 + warning "FORCE_NO_ROOT=1 detected, we will proceed without root, but you may get insufficient privileges errors."
388 + ;;
389 + *)
390 + if ! rerun_with_sudo; then
391 + error "Please run this script with root or specify FORCE_NO_ROOT=1 to force this script to run as the current user."
392 + exit 13
393 + fi
394 + ;;
395 + esac
396 + }
397 +
398 + check_environment_operating_system() {
399 + if [[ -n "$OPERATING_SYSTEM" ]]; then
400 + warning "OPERATING_SYSTEM=$OPERATING_SYSTEM detected, operating system detection will not be performed."
401 + return
402 + fi
403 +
404 + if [[ "x$(uname)" == "xLinux" ]]; then
405 + OPERATING_SYSTEM=linux
406 + return
407 + fi
408 +
409 + error "This script only supports Linux."
410 + note "Specify OPERATING_SYSTEM=[linux|darwin|freebsd|windows] to bypass this check and force this script to run on this $(uname)."
411 + exit 95
412 + }
413 +
414 + check_environment_architecture() {
415 + if [[ -n "$ARCHITECTURE" ]]; then
416 + warning "ARCHITECTURE=$ARCHITECTURE detected, architecture detection will not be performed."
417 + return
418 + fi
419 +
420 + case "$(uname -m)" in
421 + 'i386' | 'i686')
422 + ARCHITECTURE='386'
423 + ;;
424 + 'amd64' | 'x86_64')
425 + ARCHITECTURE='amd64'
426 + ;;
427 + 'armv5tel' | 'armv6l' | 'armv7' | 'armv7l')
428 + ARCHITECTURE='arm'
429 + ;;
430 + 'armv8' | 'aarch64')
431 + ARCHITECTURE='arm64'
432 + ;;
433 + 'mips' | 'mipsle' | 'mips64' | 'mips64le')
434 + ARCHITECTURE='mipsle'
435 + ;;
436 + 's390x')
437 + ARCHITECTURE='s390x'
438 + ;;
439 + 'loongarch64')
440 + ARCHITECTURE='loong64'
441 + ;;
442 + *)
443 + error "The architecture '$(uname -a)' is not supported."
444 + note "Specify ARCHITECTURE=<architecture> to bypass this check and force this script to run on this $(uname -m)."
445 + exit 8
446 + ;;
447 + esac
448 + }
449 +
450 + check_environment_systemd() {
451 + if [[ -d "/run/systemd/system" ]] || grep -q systemd <(ls -l /sbin/init); then
452 + return
453 + fi
454 +
455 + case "$FORCE_NO_SYSTEMD" in
456 + '1')
457 + warning "FORCE_NO_SYSTEMD=1, we will proceed as normal even if systemd is not detected."
458 + ;;
459 + '2')
460 + warning "FORCE_NO_SYSTEMD=2, we will proceed but skip all systemd related commands."
461 + ;;
462 + *)
463 + error "This script only supports Linux distributions with systemd."
464 + note "Specify FORCE_NO_SYSTEMD=1 to disable this check and force this script to run as if systemd exists."
465 + note "Specify FORCE_NO_SYSTEMD=2 to disable this check and skip all systemd related commands."
466 + ;;
467 + esac
468 + }
469 +
470 + check_environment_selinux() {
471 + if ! has_command getenforce; then
472 + return
473 + fi
474 +
475 + note "SELinux is detected"
476 +
477 + if [[ "x$FORCE_NO_SELINUX" == "x1" ]]; then
478 + warning "FORCE_NO_SELINUX=1, we will skip all SELinux related commands."
479 + return
480 + fi
481 +
482 + if [[ -z "$SECONTEXT_SYSTEMD_UNIT" ]]; then
483 + if [[ -z "$FORCE_NO_SYSTEMD" ]] && [[ -e "$SYSTEMD_SERVICES_DIR" ]]; then
484 + local _sectx="$(get_selinux_context "$SYSTEMD_SERVICES_DIR")"
485 + if [[ -z "$_sectx" ]]; then
486 + warning "Failed to obtain SEContext of $SYSTEMD_SERVICES_DIR"
487 + else
488 + SECONTEXT_SYSTEMD_UNIT="$_sectx"
489 + fi
490 + fi
491 + fi
492 + }
493 +
494 + check_environment_curl() {
495 + if has_command curl; then
496 + return
497 + fi
498 +
499 + install_software curl
500 + }
501 +
502 + check_environment_grep() {
503 + if has_command grep; then
504 + return
505 + fi
506 +
507 + install_software grep
508 + }
509 +
510 + check_environment() {
511 + check_environment_operating_system
512 + check_environment_architecture
513 + check_environment_systemd
514 + check_environment_selinux
515 + check_environment_curl
516 + check_environment_grep
517 + }
518 +
519 + vercmp_segment() {
520 + local _lhs="$1"
521 + local _rhs="$2"
522 +
523 + if [[ "x$_lhs" == "x$_rhs" ]]; then
524 + echo 0
525 + return
526 + fi
527 + if [[ -z "$_lhs" ]]; then
528 + echo -1
529 + return
530 + fi
531 + if [[ -z "$_rhs" ]]; then
532 + echo 1
533 + return
534 + fi
535 +
536 + local _lhs_num="${_lhs//[A-Za-z]*/}"
537 + local _rhs_num="${_rhs//[A-Za-z]*/}"
538 +
539 + if [[ "x$_lhs_num" == "x$_rhs_num" ]]; then
540 + echo 0
541 + return
542 + fi
543 + if [[ -z "$_lhs_num" ]]; then
544 + echo -1
545 + return
546 + fi
547 + if [[ -z "$_rhs_num" ]]; then
548 + echo 1
549 + return
550 + fi
551 + local _numcmp=$(($_lhs_num - $_rhs_num))
552 + if [[ "$_numcmp" -ne 0 ]]; then
553 + echo "$_numcmp"
554 + return
555 + fi
556 +
557 + local _lhs_suffix="${_lhs#"$_lhs_num"}"
558 + local _rhs_suffix="${_rhs#"$_rhs_num"}"
559 +
560 + if [[ "x$_lhs_suffix" == "x$_rhs_suffix" ]]; then
561 + echo 0
562 + return
563 + fi
564 + if [[ -z "$_lhs_suffix" ]]; then
565 + echo 1
566 + return
567 + fi
568 + if [[ -z "$_rhs_suffix" ]]; then
569 + echo -1
570 + return
571 + fi
572 + if [[ "$_lhs_suffix" < "$_rhs_suffix" ]]; then
573 + echo -1
574 + return
575 + fi
576 + echo 1
577 + }
578 +
579 + vercmp() {
580 + local _lhs=${1#v}
581 + local _rhs=${2#v}
582 +
583 + while [[ -n "$_lhs" && -n "$_rhs" ]]; do
584 + local _clhs="${_lhs/.*/}"
585 + local _crhs="${_rhs/.*/}"
586 +
587 + local _segcmp="$(vercmp_segment "$_clhs" "$_crhs")"
588 + if [[ "$_segcmp" -ne 0 ]]; then
589 + echo "$_segcmp"
590 + return
591 + fi
592 +
593 + _lhs="${_lhs#"$_clhs"}"
594 + _lhs="${_lhs#.}"
595 + _rhs="${_rhs#"$_crhs"}"
596 + _rhs="${_rhs#.}"
597 + done
598 +
599 + if [[ "x$_lhs" == "x$_rhs" ]]; then
600 + echo 0
601 + return
602 + fi
603 +
604 + if [[ -z "$_lhs" ]]; then
605 + echo -1
606 + return
607 + fi
608 +
609 + if [[ -z "$_rhs" ]]; then
610 + echo 1
611 + return
612 + fi
613 +
614 + return
615 + }
616 +
617 + check_hysteria_user() {
618 + local _default_hysteria_user="$1"
619 +
620 + if [[ -n "$HYSTERIA_USER" ]]; then
621 + return
622 + fi
623 +
624 + if [[ ! -e "$SYSTEMD_SERVICES_DIR/hysteria-client.service" ]]; then
625 + HYSTERIA_USER="$_default_hysteria_user"
626 + return
627 + fi
628 +
629 + HYSTERIA_USER="$(grep -o '^User=\w*' "$SYSTEMD_SERVICES_DIR/hysteria-client.service" | tail -1 | cut -d '=' -f 2 || true)"
630 +
631 + if [[ -z "$HYSTERIA_USER" ]]; then
632 + HYSTERIA_USER="$_default_hysteria_user"
633 + fi
634 + }
635 +
636 + check_hysteria_homedir() {
637 + local _default_hysteria_homedir="$1"
638 +
639 + if [[ -n "$HYSTERIA_HOME_DIR" ]]; then
640 + return
641 + fi
642 +
643 + if ! is_user_exists "$HYSTERIA_USER"; then
644 + HYSTERIA_HOME_DIR="$_default_hysteria_homedir"
645 + return
646 + fi
647 +
648 + HYSTERIA_HOME_DIR="$(eval echo ~"$HYSTERIA_USER")"
649 + }
650 +
651 +
652 + ###
653 + # ARGUMENTS PARSER
654 + ###
655 +
656 + show_usage_and_exit() {
657 + echo
658 + echo -e "\t$(tbold)$SCRIPT_NAME$(treset) - hysteria client install script"
659 + echo
660 + echo -e "Usage:"
661 + echo
662 + echo -e "$(tbold)Install hysteria$(treset)"
663 + echo -e "\t$0 [ -f | -l <file> | --version <version> ]"
664 + echo -e "Flags:"
665 + echo -e "\t-f, --force\tForce re-install latest or specified version even if it has been installed."
666 + echo -e "\t-l, --local <file>\tInstall specified hysteria binary instead of download it."
667 + echo -e "\t--version <version>\tInstall specified version instead of the latest."
668 + echo
669 + echo -e "$(tbold)Remove hysteria$(treset)"
670 + echo -e "\t$0 --remove"
671 + echo
672 + echo -e "$(tbold)Check for the update$(treset)"
673 + echo -e "\t$0 -c"
674 + echo -e "\t$0 --check"
675 + echo
676 + echo -e "$(tbold)Show this help$(treset)"
677 + echo -e "\t$0 -h"
678 + echo -e "\t$0 --help"
679 + exit 0
680 + }
681 +
682 + parse_arguments() {
683 + while [[ "$#" -gt '0' ]]; do
684 + case "$1" in
685 + '--remove')
686 + if [[ -n "$OPERATION" && "$OPERATION" != 'remove' ]]; then
687 + show_argument_error_and_exit "Option '--remove' is in conflict with other options."
688 + fi
689 + OPERATION='remove'
690 + ;;
691 + '--version')
692 + VERSION="$2"
693 + if [[ -z "$VERSION" ]]; then
694 + show_argument_error_and_exit "Please specify the version for option '--version'."
695 + fi
696 + shift
697 + if ! has_prefix "$VERSION" 'v'; then
698 + show_argument_error_and_exit "Version numbers should begin with 'v' (such as 'v2.0.0'), got '$VERSION'"
699 + fi
700 + ;;
701 + '-c' | '--check')
702 + if [[ -n "$OPERATION" && "$OPERATION" != 'check' ]]; then
703 + show_argument_error_and_exit "Option '-c' or '--check' is in conflict with other options."
704 + fi
705 + OPERATION='check_update'
706 + ;;
707 + '-f' | '--force')
708 + FORCE='1'
709 + ;;
710 + '-h' | '--help')
711 + show_usage_and_exit
712 + ;;
713 + '-l' | '--local')
714 + LOCAL_FILE="$2"
715 + if [[ -z "$LOCAL_FILE" ]]; then
716 + show_argument_error_and_exit "Please specify the local binary to install for option '-l' or '--local'."
717 + fi
718 + break
719 + ;;
720 + *)
721 + show_argument_error_and_exit "Unknown option '$1'"
722 + ;;
723 + esac
724 + shift
725 + done
726 +
727 + if [[ -z "$OPERATION" ]]; then
728 + OPERATION='install'
729 + fi
730 +
731 + # validate arguments
732 + case "$OPERATION" in
733 + 'install')
734 + if [[ -n "$VERSION" && -n "$LOCAL_FILE" ]]; then
735 + show_argument_error_and_exit '--version and --local cannot be used together.'
736 + fi
737 + ;;
738 + *)
739 + if [[ -n "$VERSION" ]]; then
740 + show_argument_error_and_exit "--version is only valid for install operation."
741 + fi
742 + if [[ -n "$LOCAL_FILE" ]]; then
743 + show_argument_error_and_exit "--local is only valid for install operation."
744 + fi
745 + ;;
746 + esac
747 + }
748 +
749 +
750 + ###
751 + # FILE TEMPLATES
752 + ###
753 +
754 + # /etc/systemd/system/hysteria-client.service
755 + tpl_hysteria_client_service_base() {
756 + local _config_name="$1"
757 +
758 + cat << EOF
759 + [Unit]
760 + Description=Hysteria Server Service (${_config_name}.yaml)
761 + After=network.target
762 +
763 + [Service]
764 + Type=simple
765 + ExecStart=$EXECUTABLE_INSTALL_PATH -c ${CONFIG_DIR}/${_config_name}.yaml
766 + WorkingDirectory=$(systemd_unit_working_directory)
767 + User=$HYSTERIA_USER
768 + Group=$HYSTERIA_USER
769 + Environment=HYSTERIA_LOG_LEVEL=info
770 + NoNewPrivileges=true
771 +
772 + [Install]
773 + WantedBy=multi-user.target
774 + EOF
775 + }
776 +
777 + # /etc/systemd/system/hysteria-client.service
778 + tpl_hysteria_client_service() {
779 + tpl_hysteria_client_service_base 'config'
780 + }
781 +
782 + # /etc/systemd/system/hysteria-client@.service
783 + tpl_hysteria_client_x_service() {
784 + tpl_hysteria_client_service_base '%i'
785 + }
786 +
787 + # /etc/hysteria/config.yaml
788 + tpl_etc_hysteria_config_yaml() {
789 + cat << EOF
790 + server: your.domain.net:port
791 +
792 + auth: $(generate_random_password)
793 +
794 + socks5:
795 + listen: 127.0.0.1:2080
796 +
797 + fastOpen: true
798 + lazy: false
799 +
800 + bandwidth:
801 + up: 1 gbps
802 + down: 1 gbps
803 +
804 + EOF
805 + }
806 +
807 +
808 + ###
809 + # SYSTEMD
810 + ###
811 +
812 + get_running_services() {
813 + if [[ "x$FORCE_NO_SYSTEMD" == "x2" ]]; then
814 + return
815 + fi
816 +
817 + systemctl list-units --state=active --plain --no-legend \
818 + | grep -o "hysteria-client@*[^\s]*.service" || true
819 + }
820 +
821 + restart_running_services() {
822 + if [[ "x$FORCE_NO_SYSTEMD" == "x2" ]]; then
823 + return
824 + fi
825 +
826 + echo "Restarting running service ... "
827 +
828 + for service in $(get_running_services); do
829 + echo -ne "Restarting $service ... "
830 + systemctl restart "$service"
831 + echo "done"
832 + done
833 + }
834 +
835 + stop_running_services() {
836 + if [[ "x$FORCE_NO_SYSTEMD" == "x2" ]]; then
837 + return
838 + fi
839 +
840 + echo "Stopping running service ... "
841 +
842 + for service in $(get_running_services); do
843 + echo -ne "Stopping $service ... "
844 + systemctl stop "$service"
845 + echo "done"
846 + done
847 + }
848 +
849 +
850 + ###
851 + # HYSTERIA & GITHUB API
852 + ###
853 +
854 + is_hysteria_installed() {
855 + # RETURN VALUE
856 + # 0: hysteria is installed
857 + # 1: hysteria is not installed
858 +
859 + if [[ -f "$EXECUTABLE_INSTALL_PATH" || -h "$EXECUTABLE_INSTALL_PATH" ]]; then
860 + return 0
861 + fi
862 + return 1
863 + }
864 +
865 + get_installed_version() {
866 + if is_hysteria_installed; then
867 + if "$EXECUTABLE_INSTALL_PATH" version > /dev/null 2>&1; then
868 + "$EXECUTABLE_INSTALL_PATH" version | grep '^Version' | grep -o 'v[.0-9]*'
869 + elif "$EXECUTABLE_INSTALL_PATH" -v > /dev/null 2>&1; then
870 + # hysteria 1
871 + "$EXECUTABLE_INSTALL_PATH" -v | cut -d ' ' -f 3
872 + fi
873 + fi
874 + }
875 +
876 + get_latest_version() {
877 + if [[ -n "$VERSION" ]]; then
878 + echo "$VERSION"
879 + return
880 + fi
881 +
882 + local _tmpfile=$(mktemp)
883 + if ! curl -sS "$HY2_API_BASE_URL/update?cver=installscript&plat=${OPERATING_SYSTEM}&arch=${ARCHITECTURE}&chan=release&side=server" -o "$_tmpfile"; then
884 + error "Failed to get the latest version from Hysteria 2 API, please check your network and try again."
885 + exit 11
886 + fi
887 +
888 + local _latest_version=$(grep -oP '"lver":\s*\K"v.*?"' "$_tmpfile" | head -1)
889 + _latest_version=${_latest_version#'"'}
890 + _latest_version=${_latest_version%'"'}
891 +
892 + if [[ -n "$_latest_version" ]]; then
893 + echo "$_latest_version"
894 + fi
895 +
896 + rm -f "$_tmpfile"
897 + }
898 +
899 + download_hysteria() {
900 + local _version="$1"
901 + local _destination="$2"
902 +
903 + local _download_url="$REPO_URL/releases/download/app/$_version/hysteria-$OPERATING_SYSTEM-$ARCHITECTURE"
904 + echo "Downloading hysteria binary: $_download_url ..."
905 + if ! curl -R -H 'Cache-Control: no-cache' "$_download_url" -o "$_destination"; then
906 + error "Download failed, please check your network and try again."
907 + return 11
908 + fi
909 + return 0
910 + }
911 +
912 + check_update() {
913 + # RETURN VALUE
914 + # 0: update available
915 + # 1: installed version is latest
916 +
917 + echo -ne "Checking for installed version ... "
918 + local _installed_version="$(get_installed_version)"
919 + if [[ -n "$_installed_version" ]]; then
920 + echo "$_installed_version"
921 + else
922 + echo "not installed"
923 + fi
924 +
925 + echo -ne "Checking for latest version ... "
926 + local _latest_version="$(get_latest_version)"
927 + if [[ -n "$_latest_version" ]]; then
928 + echo "$_latest_version"
929 + VERSION="$_latest_version"
930 + else
931 + echo "failed"
932 + return 1
933 + fi
934 +
935 + local _vercmp="$(vercmp "$_installed_version" "$_latest_version")"
936 + if [[ "$_vercmp" -lt 0 ]]; then
937 + return 0
938 + fi
939 +
940 + return 1
941 + }
942 +
943 +
944 + ###
945 + # ENTRY
946 + ###
947 +
948 + perform_install_hysteria_binary() {
949 + if [[ -n "$LOCAL_FILE" ]]; then
950 + note "Performing local install: $LOCAL_FILE"
951 +
952 + echo -ne "Installing hysteria executable ... "
953 +
954 + if install -Dm755 "$LOCAL_FILE" "$EXECUTABLE_INSTALL_PATH"; then
955 + echo "ok"
956 + else
957 + exit 2
958 + fi
959 +
960 + return
961 + fi
962 +
963 + local _tmpfile=$(mktemp)
964 +
965 + if ! download_hysteria "$VERSION" "$_tmpfile"; then
966 + rm -f "$_tmpfile"
967 + exit 11
968 + fi
969 +
970 + echo -ne "Installing hysteria executable ... "
971 +
972 + if install -Dm755 "$_tmpfile" "$EXECUTABLE_INSTALL_PATH"; then
973 + echo "ok"
974 + else
975 + exit 13
976 + fi
977 +
978 + rm -f "$_tmpfile"
979 + }
980 +
981 + perform_remove_hysteria_binary() {
982 + remove_file "$EXECUTABLE_INSTALL_PATH"
983 + }
984 +
985 + perform_install_hysteria_example_config() {
986 + install_content -Dm644 "$(tpl_etc_hysteria_config_yaml)" "$CONFIG_DIR/config.yaml" ""
987 + }
988 +
989 + perform_install_hysteria_systemd() {
990 + if [[ "x$FORCE_NO_SYSTEMD" == "x2" ]]; then
991 + return
992 + fi
993 +
994 + install_content -Dm644 "$(tpl_hysteria_client_service)" "$SYSTEMD_SERVICES_DIR/hysteria-client.service" "1"
995 + install_content -Dm644 "$(tpl_hysteria_client_x_service)" "$SYSTEMD_SERVICES_DIR/hysteria-client@.service" "1"
996 + if [[ -n "$SECONTEXT_SYSTEMD_UNIT" ]]; then
997 + chcon "$SECONTEXT_SYSTEMD_UNIT" "$SYSTEMD_SERVICES_DIR/hysteria-client.service"
998 + chcon "$SECONTEXT_SYSTEMD_UNIT" "$SYSTEMD_SERVICES_DIR/hysteria-client@.service"
999 + fi
1000 +
1001 + systemctl daemon-reload
1002 + }
1003 +
1004 + perform_remove_hysteria_systemd() {
1005 + remove_file "$SYSTEMD_SERVICES_DIR/hysteria-client.service"
1006 + remove_file "$SYSTEMD_SERVICES_DIR/hysteria-client@.service"
1007 +
1008 + systemctl daemon-reload
1009 + }
1010 +
1011 + perform_install_hysteria_home_legacy() {
1012 + if ! is_user_exists "$HYSTERIA_USER"; then
1013 + echo -ne "Creating user $HYSTERIA_USER ... "
1014 + useradd -r -d "$HYSTERIA_HOME_DIR" -m "$HYSTERIA_USER"
1015 + echo "ok"
1016 + fi
1017 + }
1018 +
1019 + perform_install() {
1020 + local _is_frash_install
1021 +
1022 + local _is_update_required
1023 +
1024 + if [[ -n "$LOCAL_FILE" ]] || [[ -n "$VERSION" ]] || check_update; then
1025 + _is_update_required=1
1026 + fi
1027 +
1028 + if [[ "x$FORCE" == "x1" ]]; then
1029 + if [[ -z "$_is_update_required" ]]; then
1030 + note "Option '--force' detected, re-install even if installed version is the latest."
1031 + fi
1032 + _is_update_required=1
1033 + fi
1034 +
1035 + if [[ -n "$_is_update_required" ]]; then
1036 + perform_install_hysteria_binary
1037 + fi
1038 +
1039 + # Always install additional files, regardless of $_is_update_required.
1040 + # This allows changes to be made with environment variables (e.g. change HYSTERIA_USER without --force).
1041 + perform_install_hysteria_example_config
1042 + perform_install_hysteria_home_legacy
1043 + perform_install_hysteria_systemd
1044 +
1045 + if [[ -z "$_is_update_required" ]]; then
1046 + echo
1047 + echo "$(tgreen)Installed version is up-to-date, there is nothing to do.$(treset)"
1048 + echo
1049 + elif [[ -n "$_is_frash_install" ]]; then
1050 + echo
1051 + echo -e "$(tbold)Congratulation! Hysteria 2 has been successfully installed on your server.$(treset)"
1052 + echo
1053 + echo -e "What's next?"
1054 + echo
1055 + echo -e "\t+ Take a look at the differences between Hysteria 2 and Hysteria 1 at https://hysteria.network/docs/misc/2-vs-1/"
1056 + echo -e "\t+ Check out the quick server config guide at $(tblue)https://hysteria.network/docs/getting-started/Server/$(treset)"
1057 + echo -e "\t+ Edit server config file at $(tred)$CONFIG_DIR/config.yaml$(treset)"
1058 + echo -e "\t+ Start your hysteria server with $(tred)systemctl start hysteria-client.service$(treset)"
1059 + echo -e "\t+ Configure hysteria start on system boot with $(tred)systemctl enable hysteria-client.service$(treset)"
1060 + echo
1061 + else
1062 + restart_running_services
1063 +
1064 + echo
1065 + echo -e "$(tbold)Hysteria has been successfully update to $VERSION.$(treset)"
1066 + echo
1067 + echo -e "Check out the latest changelog $(tblue)https://github.com/apernet/hysteria/blob/master/CHANGELOG.md$(treset)"
1068 + echo
1069 + fi
1070 + }
1071 +
1072 + perform_remove() {
1073 + perform_remove_hysteria_binary
1074 + stop_running_services
1075 + perform_remove_hysteria_systemd
1076 +
1077 + echo
1078 + echo -e "$(tbold)Congratulation! Hysteria has been successfully removed from your linux.$(treset)"
1079 + echo
1080 + echo -e "You still need to remove configuration files and ACME certificates manually with the following commands:"
1081 + echo
1082 + echo -e "\t$(tred)rm -rf "$CONFIG_DIR"$(treset)"
1083 + if [[ "x$HYSTERIA_USER" != "xroot" ]]; then
1084 + echo -e "\t$(tred)userdel -r "$HYSTERIA_USER"$(treset)"
1085 + fi
1086 + if [[ "x$FORCE_NO_SYSTEMD" != "x2" ]]; then
1087 + echo
1088 + echo -e "You still might need to disable all related systemd services with the following commands:"
1089 + echo
1090 + echo -e "\t$(tred)rm -f /etc/systemd/system/multi-user.target.wants/hysteria-client.service$(treset)"
1091 + echo -e "\t$(tred)rm -f /etc/systemd/system/multi-user.target.wants/hysteria-client@*.service$(treset)"
1092 + echo -e "\t$(tred)systemctl daemon-reload$(treset)"
1093 + fi
1094 + echo
1095 + }
1096 +
1097 + perform_check_update() {
1098 + if check_update; then
1099 + echo
1100 + echo -e "$(tbold)Update available: $VERSION$(treset)"
1101 + echo
1102 + echo -e "$(tgreen)You can download and install the latest version by execute this script without any arguments.$(treset)"
1103 + echo
1104 + else
1105 + echo
1106 + echo "$(tgreen)Installed version is up-to-date.$(treset)"
1107 + echo
1108 + fi
1109 + }
1110 +
1111 + main() {
1112 + parse_arguments "$@"
1113 +
1114 + check_permission
1115 + check_environment
1116 + check_hysteria_user "hysteria"
1117 + check_hysteria_homedir "/var/lib/$HYSTERIA_USER"
1118 +
1119 + case "$OPERATION" in
1120 + "install")
1121 + perform_install
1122 + ;;
1123 + "remove")
1124 + perform_remove
1125 + ;;
1126 + "check_update")
1127 + perform_check_update
1128 + ;;
1129 + *)
1130 + error "Unknown operation '$OPERATION'."
1131 + ;;
1132 + esac
1133 + }
1134 +
1135 + main "$@"
Nowsze Starsze