Zuletzt aktiv 5 months ago

Änderung 5186c22629de5dff3bf5b5a68981796ab8137c2a

install-client.sh Originalformat
1#!/usr/bin/env bash
2#
3# install_server.sh - hysteria client install script
4# Try `install_client.sh --help` for usage.
5#
6# SPDX-License-Identifier: MIT
7# Copyright (c) 2023 Aperture Internet Laboratory
8#
9
10set -e
11
12
13###
14# SCRIPT CONFIGURATION
15###
16
17# Basename of this script
18SCRIPT_NAME="$(basename "$0")"
19
20# Command line arguments of this script
21SCRIPT_ARGS=("$@")
22
23# Path for installing executable
24EXECUTABLE_INSTALL_PATH="/usr/local/bin/hysteria"
25
26# Paths to install systemd files
27SYSTEMD_SERVICES_DIR="/etc/systemd/system"
28
29# Directory to store hysteria config file
30CONFIG_DIR="/etc/hysteria"
31
32# URLs of GitHub
33REPO_URL="https://github.com/apernet/hysteria"
34
35# URL of Hysteria 2 API
36HY2_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
41CURL_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
49PACKAGE_MANAGEMENT_INSTALL="${PACKAGE_MANAGEMENT_INSTALL:-}"
50
51# Operating System of current machine, supported: linux
52OPERATING_SYSTEM="${OPERATING_SYSTEM:-}"
53
54# Architecture of current machine, supported: 386, amd64, arm, arm64, mipsle, s390x
55ARCHITECTURE="${ARCHITECTURE:-}"
56
57# User for running hysteria
58HYSTERIA_USER="${HYSTERIA_USER:-}"
59
60# Directory for ACME certificates storage
61HYSTERIA_HOME_DIR="${HYSTERIA_HOME_DIR:-}"
62
63# SELinux context of systemd unit files
64SECONTEXT_SYSTEMD_UNIT="${SECONTEXT_SYSTEMD_UNIT:-}"
65
66
67###
68# ARGUMENTS
69###
70
71# Supported operation: install, remove, check_update
72OPERATION=
73
74# User specified version to install
75VERSION=
76
77# Force install even if installed
78FORCE=
79
80# User specified binary to install
81LOCAL_FILE=
82
83
84###
85# COMMAND REPLACEMENT & UTILITIES
86###
87
88has_command() {
89 local _command=$1
90
91 type -P "$_command" > /dev/null 2>&1
92}
93
94curl() {
95 command curl "${CURL_FLAGS[@]}" "$@"
96}
97
98mktemp() {
99 command mktemp "$@" "/tmp/hyservinst.XXXXXXXXXX"
100}
101
102tput() {
103 if has_command tput; then
104 command tput "$@"
105 fi
106}
107
108tred() {
109 tput setaf 1
110}
111
112tgreen() {
113 tput setaf 2
114}
115
116tyellow() {
117 tput setaf 3
118}
119
120tblue() {
121 tput setaf 4
122}
123
124taoi() {
125 tput setaf 6
126}
127
128tbold() {
129 tput bold
130}
131
132treset() {
133 tput sgr0
134}
135
136note() {
137 local _msg="$1"
138
139 echo -e "$SCRIPT_NAME: $(tbold)note: $_msg$(treset)"
140}
141
142warning() {
143 local _msg="$1"
144
145 echo -e "$SCRIPT_NAME: $(tyellow)warning: $_msg$(treset)"
146}
147
148error() {
149 local _msg="$1"
150
151 echo -e "$SCRIPT_NAME: $(tred)error: $_msg$(treset)"
152}
153
154has_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
169generate_random_password() {
170 dd if=/dev/random bs=18 count=1 status=none | base64
171}
172
173systemctl() {
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
182chcon() {
183 if ! has_command chcon || [[ "x$FORCE_NO_SELINUX" == "x1" ]]; then
184 return
185 fi
186
187 command chcon "$@"
188}
189
190get_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
198systemd_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
212get_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
235show_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
243install_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
262remove_file() {
263 local _target="$1"
264
265 echo -ne "Remove $_target ... "
266 if rm "$_target"; then
267 echo -e "ok"
268 fi
269}
270
271exec_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
290detect_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
324install_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
344is_user_exists() {
345 local _user="$1"
346
347 id "$_user" > /dev/null 2>&1
348}
349
350rerun_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
378check_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
398check_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
414check_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
450check_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
470check_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
494check_environment_curl() {
495 if has_command curl; then
496 return
497 fi
498
499 install_software curl
500}
501
502check_environment_grep() {
503 if has_command grep; then
504 return
505 fi
506
507 install_software grep
508}
509
510check_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
519vercmp_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
579vercmp() {
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
617check_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
636check_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
656show_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
682parse_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
755tpl_hysteria_client_service_base() {
756 local _config_name="$1"
757
758 cat << EOF
759[Unit]
760Description=Hysteria Server Service (${_config_name}.yaml)
761After=network.target
762
763[Service]
764Type=simple
765ExecStart=$EXECUTABLE_INSTALL_PATH -c ${CONFIG_DIR}/${_config_name}.yaml
766WorkingDirectory=$(systemd_unit_working_directory)
767User=$HYSTERIA_USER
768Group=$HYSTERIA_USER
769Environment=HYSTERIA_LOG_LEVEL=info
770NoNewPrivileges=true
771
772[Install]
773WantedBy=multi-user.target
774EOF
775}
776
777# /etc/systemd/system/hysteria-client.service
778tpl_hysteria_client_service() {
779 tpl_hysteria_client_service_base 'config'
780}
781
782# /etc/systemd/system/hysteria-client@.service
783tpl_hysteria_client_x_service() {
784 tpl_hysteria_client_service_base '%i'
785}
786
787# /etc/hysteria/config.yaml
788tpl_etc_hysteria_config_yaml() {
789 cat << EOF
790server: your.domain.net:port
791
792auth: $(generate_random_password)
793
794socks5:
795 listen: 127.0.0.1:2080
796
797fastOpen: true
798lazy: false
799
800bandwidth:
801 up: 1 gbps
802 down: 1 gbps
803
804EOF
805}
806
807
808###
809# SYSTEMD
810###
811
812get_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
821restart_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
835stop_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
854is_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
865get_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
876get_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
899download_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
912check_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
948perform_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
981perform_remove_hysteria_binary() {
982 remove_file "$EXECUTABLE_INSTALL_PATH"
983}
984
985perform_install_hysteria_example_config() {
986 install_content -Dm644 "$(tpl_etc_hysteria_config_yaml)" "$CONFIG_DIR/config.yaml" ""
987}
988
989perform_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
1004perform_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
1011perform_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
1019perform_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
1072perform_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
1097perform_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
1111main() {
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
1135main "$@"
1136