mirror of
https://github.com/bolucat/Archive.git
synced 2025-09-26 20:21:35 +08:00
Update On Fri Apr 25 14:33:06 CEST 2025
This commit is contained in:
@@ -32,7 +32,7 @@ PROJECT_NAME=$(shell basename "${ROOT}")
|
||||
# - pkg/version/current.go
|
||||
#
|
||||
# Use `tools/bump_version.sh` script to change all those files at one shot.
|
||||
VERSION="3.13.1"
|
||||
VERSION="3.14.1"
|
||||
|
||||
# Build binaries and installation packages.
|
||||
.PHONY: build
|
||||
|
@@ -1,5 +1,5 @@
|
||||
Package: mieru
|
||||
Version: 3.13.1
|
||||
Version: 3.14.1
|
||||
Section: net
|
||||
Priority: optional
|
||||
Architecture: amd64
|
||||
|
@@ -1,5 +1,5 @@
|
||||
Name: mieru
|
||||
Version: 3.13.1
|
||||
Version: 3.14.1
|
||||
Release: 1%{?dist}
|
||||
Summary: Mieru proxy client
|
||||
License: GPLv3+
|
||||
|
@@ -1,5 +1,5 @@
|
||||
Package: mieru
|
||||
Version: 3.13.1
|
||||
Version: 3.14.1
|
||||
Section: net
|
||||
Priority: optional
|
||||
Architecture: arm64
|
||||
|
@@ -1,5 +1,5 @@
|
||||
Name: mieru
|
||||
Version: 3.13.1
|
||||
Version: 3.14.1
|
||||
Release: 1%{?dist}
|
||||
Summary: Mieru proxy client
|
||||
License: GPLv3+
|
||||
|
@@ -1,5 +1,5 @@
|
||||
Package: mita
|
||||
Version: 3.13.1
|
||||
Version: 3.14.1
|
||||
Section: net
|
||||
Priority: optional
|
||||
Architecture: amd64
|
||||
|
@@ -1,5 +1,5 @@
|
||||
Name: mita
|
||||
Version: 3.13.1
|
||||
Version: 3.14.1
|
||||
Release: 1%{?dist}
|
||||
Summary: Mieru proxy server
|
||||
License: GPLv3+
|
||||
|
@@ -1,5 +1,5 @@
|
||||
Package: mita
|
||||
Version: 3.13.1
|
||||
Version: 3.14.1
|
||||
Section: net
|
||||
Priority: optional
|
||||
Architecture: arm64
|
||||
|
@@ -1,5 +1,5 @@
|
||||
Name: mita
|
||||
Version: 3.13.1
|
||||
Version: 3.14.1
|
||||
Release: 1%{?dist}
|
||||
Summary: Mieru proxy server
|
||||
License: GPLv3+
|
||||
|
@@ -182,8 +182,6 @@ The format of the simple sharing link is as follows:
|
||||
|
||||
A simple sharing link starts with `mierus://`, where `s` stands for `simple`.
|
||||
|
||||
Some special characters are not allowed in username or password. In case a character is not allowed, the simple sharing link cannot be generated.
|
||||
|
||||
There is only one server address in a simple sharing link. If the client's configuration contains multiple servers, multiple links will be generated.
|
||||
|
||||
The supported parameters are:
|
||||
|
@@ -182,8 +182,6 @@ mierus://baozi:manlianpenfen@1.2.3.4?mtu=1400&multiplexing=MULTIPLEXING_HIGH&por
|
||||
|
||||
简单分享链接以 `mierus://` 开始,其中 `s` 表示 `simple`。
|
||||
|
||||
用户名和密码不允许使用某些特殊字符。如果使用了不允许的字符,则无法生成简单分享链接。
|
||||
|
||||
简单分享链接中只有一个服务器地址。如果客户端的设置含有多台服务器,则会生成多个链接。
|
||||
|
||||
链接中支持的参数列表如下:
|
||||
|
@@ -8,32 +8,32 @@ Before installation and configuration, connect to the server via SSH and then ex
|
||||
|
||||
```sh
|
||||
# Debian / Ubuntu - X86_64
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.13.1/mita_3.13.1_amd64.deb
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.14.1/mita_3.14.1_amd64.deb
|
||||
|
||||
# Debian / Ubuntu - ARM 64
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.13.1/mita_3.13.1_arm64.deb
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.14.1/mita_3.14.1_arm64.deb
|
||||
|
||||
# RedHat / CentOS / Rocky Linux - X86_64
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.13.1/mita-3.13.1-1.x86_64.rpm
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.14.1/mita-3.14.1-1.x86_64.rpm
|
||||
|
||||
# RedHat / CentOS / Rocky Linux - ARM 64
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.13.1/mita-3.13.1-1.aarch64.rpm
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.14.1/mita-3.14.1-1.aarch64.rpm
|
||||
```
|
||||
|
||||
## Install mita package
|
||||
|
||||
```sh
|
||||
# Debian / Ubuntu - X86_64
|
||||
sudo dpkg -i mita_3.13.1_amd64.deb
|
||||
sudo dpkg -i mita_3.14.1_amd64.deb
|
||||
|
||||
# Debian / Ubuntu - ARM 64
|
||||
sudo dpkg -i mita_3.13.1_arm64.deb
|
||||
sudo dpkg -i mita_3.14.1_arm64.deb
|
||||
|
||||
# RedHat / CentOS / Rocky Linux - X86_64
|
||||
sudo rpm -Uvh --force mita-3.13.1-1.x86_64.rpm
|
||||
sudo rpm -Uvh --force mita-3.14.1-1.x86_64.rpm
|
||||
|
||||
# RedHat / CentOS / Rocky Linux - ARM 64
|
||||
sudo rpm -Uvh --force mita-3.13.1-1.aarch64.rpm
|
||||
sudo rpm -Uvh --force mita-3.14.1-1.aarch64.rpm
|
||||
```
|
||||
|
||||
Those instructions can also be used to upgrade the version of mita software package.
|
||||
|
@@ -8,32 +8,32 @@
|
||||
|
||||
```sh
|
||||
# Debian / Ubuntu - X86_64
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.13.1/mita_3.13.1_amd64.deb
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.14.1/mita_3.14.1_amd64.deb
|
||||
|
||||
# Debian / Ubuntu - ARM 64
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.13.1/mita_3.13.1_arm64.deb
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.14.1/mita_3.14.1_arm64.deb
|
||||
|
||||
# RedHat / CentOS / Rocky Linux - X86_64
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.13.1/mita-3.13.1-1.x86_64.rpm
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.14.1/mita-3.14.1-1.x86_64.rpm
|
||||
|
||||
# RedHat / CentOS / Rocky Linux - ARM 64
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.13.1/mita-3.13.1-1.aarch64.rpm
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.14.1/mita-3.14.1-1.aarch64.rpm
|
||||
```
|
||||
|
||||
## 安装 mita 软件包
|
||||
|
||||
```sh
|
||||
# Debian / Ubuntu - X86_64
|
||||
sudo dpkg -i mita_3.13.1_amd64.deb
|
||||
sudo dpkg -i mita_3.14.1_amd64.deb
|
||||
|
||||
# Debian / Ubuntu - ARM 64
|
||||
sudo dpkg -i mita_3.13.1_arm64.deb
|
||||
sudo dpkg -i mita_3.14.1_arm64.deb
|
||||
|
||||
# RedHat / CentOS / Rocky Linux - X86_64
|
||||
sudo rpm -Uvh --force mita-3.13.1-1.x86_64.rpm
|
||||
sudo rpm -Uvh --force mita-3.14.1-1.x86_64.rpm
|
||||
|
||||
# RedHat / CentOS / Rocky Linux - ARM 64
|
||||
sudo rpm -Uvh --force mita-3.13.1-1.aarch64.rpm
|
||||
sudo rpm -Uvh --force mita-3.14.1-1.aarch64.rpm
|
||||
```
|
||||
|
||||
上述指令也可以用来升级 mita 软件包的版本。
|
||||
|
@@ -353,6 +353,7 @@ func GetServerStatusWithRPC(ctx context.Context) (*pb.AppStatusMsg, error) {
|
||||
}
|
||||
|
||||
// IsServerDaemonRunning returns nil if app status shows server daemon is running.
|
||||
// It returns nil even if the proxy function is not running.
|
||||
func IsServerDaemonRunning(appStatus *pb.AppStatusMsg) error {
|
||||
if appStatus == nil {
|
||||
return fmt.Errorf("AppStatusMsg is nil")
|
||||
|
@@ -20,7 +20,6 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -30,14 +29,6 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
safeURLPattern = `^[0-9A-Za-z_!\$&'\(\)\*\+,;=\.\~-]+$`
|
||||
)
|
||||
|
||||
var (
|
||||
safeURLRegExp = regexp.MustCompile(safeURLPattern)
|
||||
)
|
||||
|
||||
// ClientConfigToURL creates a URL to share the client configuration.
|
||||
func ClientConfigToURL(config *pb.ClientConfig) (string, error) {
|
||||
if config == nil {
|
||||
@@ -66,15 +57,9 @@ func ClientProfileToMultiURLs(profile *pb.ClientProfile) (urls []string, err err
|
||||
if userName == "" {
|
||||
return nil, fmt.Errorf("user name in profile %s is empty", profileName)
|
||||
}
|
||||
if !isSafeURLString(userName) {
|
||||
return nil, fmt.Errorf(`user name %q in profile %s can't be safely encoded to URL. Allowed pattern: %s`, userName, profileName, safeURLPattern)
|
||||
}
|
||||
if password == "" {
|
||||
return nil, fmt.Errorf("password in profile %s is empty", profileName)
|
||||
}
|
||||
if !isSafeURLString(password) {
|
||||
return nil, fmt.Errorf(`password %q in profile %s can't be safely encoded to URL. Allowed pattern: %s`, password, profileName, safeURLPattern)
|
||||
}
|
||||
if len(servers) == 0 {
|
||||
return nil, fmt.Errorf("profile %s has no server", profileName)
|
||||
}
|
||||
@@ -243,7 +228,3 @@ func URLToClientProfile(s string) (*pb.ClientProfile, error) {
|
||||
p.Servers = append(p.Servers, server)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func isSafeURLString(input string) bool {
|
||||
return safeURLRegExp.MatchString(input)
|
||||
}
|
||||
|
@@ -28,8 +28,8 @@ func TestClientConfigWithURL(t *testing.T) {
|
||||
{
|
||||
ProfileName: proto.String("default"),
|
||||
User: &pb.User{
|
||||
Name: proto.String("abcABC123_!$&'()*+,;=.~-"),
|
||||
Password: proto.String("defDEF456_!$&'()*+,;=.~-"),
|
||||
Name: proto.String("abcABC123:<>[]{}|_ !$&'()*+,;=.~-"),
|
||||
Password: proto.String("defDEF456:<>[]{}|_ !$&'()*+,;=.~-"),
|
||||
},
|
||||
Servers: []*pb.ServerEndpoint{
|
||||
{
|
||||
@@ -71,8 +71,8 @@ func TestClientProfileWithMultiURLs(t *testing.T) {
|
||||
p := &pb.ClientProfile{
|
||||
ProfileName: proto.String("default"),
|
||||
User: &pb.User{
|
||||
Name: proto.String("abcABC123_!$&'()*+,;=.~-"),
|
||||
Password: proto.String("defDEF456_!$&'()*+,;=.~-"),
|
||||
Name: proto.String("abcABC123:<>[]{}|_ !$&'()*+,;=.~-"),
|
||||
Password: proto.String("defDEF456:<>[]{}|_ !$&'()*+,;=.~-"),
|
||||
},
|
||||
Servers: []*pb.ServerEndpoint{
|
||||
{
|
||||
@@ -107,8 +107,8 @@ func TestClientProfileWithMultiURLs(t *testing.T) {
|
||||
p0 := &pb.ClientProfile{
|
||||
ProfileName: proto.String("default"),
|
||||
User: &pb.User{
|
||||
Name: proto.String("abcABC123_!$&'()*+,;=.~-"),
|
||||
Password: proto.String("defDEF456_!$&'()*+,;=.~-"),
|
||||
Name: proto.String("abcABC123:<>[]{}|_ !$&'()*+,;=.~-"),
|
||||
Password: proto.String("defDEF456:<>[]{}|_ !$&'()*+,;=.~-"),
|
||||
},
|
||||
Servers: []*pb.ServerEndpoint{
|
||||
{
|
||||
@@ -134,8 +134,8 @@ func TestClientProfileWithMultiURLs(t *testing.T) {
|
||||
p1 := &pb.ClientProfile{
|
||||
ProfileName: proto.String("default"),
|
||||
User: &pb.User{
|
||||
Name: proto.String("abcABC123_!$&'()*+,;=.~-"),
|
||||
Password: proto.String("defDEF456_!$&'()*+,;=.~-"),
|
||||
Name: proto.String("abcABC123:<>[]{}|_ !$&'()*+,;=.~-"),
|
||||
Password: proto.String("defDEF456:<>[]{}|_ !$&'()*+,;=.~-"),
|
||||
},
|
||||
Servers: []*pb.ServerEndpoint{
|
||||
{
|
||||
@@ -177,56 +177,3 @@ func TestClientProfileWithMultiURLs(t *testing.T) {
|
||||
t.Errorf("profile is not equal after generating and loading URL %q", urls[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSafeURLString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
isSafe bool
|
||||
}{
|
||||
{input: "abc", isSafe: true},
|
||||
{input: "ABC", isSafe: true},
|
||||
{input: "123", isSafe: true},
|
||||
{input: "_", isSafe: true},
|
||||
{input: "!", isSafe: true},
|
||||
{input: "$", isSafe: true},
|
||||
{input: "&", isSafe: true},
|
||||
{input: "'", isSafe: true},
|
||||
{input: "(", isSafe: true},
|
||||
{input: ")", isSafe: true},
|
||||
{input: "*", isSafe: true},
|
||||
{input: "+", isSafe: true},
|
||||
{input: ",", isSafe: true},
|
||||
{input: ";", isSafe: true},
|
||||
{input: "=", isSafe: true},
|
||||
{input: ".", isSafe: true},
|
||||
{input: "~", isSafe: true},
|
||||
{input: "-", isSafe: true},
|
||||
{input: "abcABC123_!$&'()*+,;=.~-", isSafe: true},
|
||||
{input: " ", isSafe: false},
|
||||
{input: "\"", isSafe: false},
|
||||
{input: "#", isSafe: false},
|
||||
{input: "%", isSafe: false},
|
||||
{input: "/", isSafe: false},
|
||||
{input: "\\", isSafe: false},
|
||||
{input: ":", isSafe: false},
|
||||
{input: "<", isSafe: false},
|
||||
{input: ">", isSafe: false},
|
||||
{input: "?", isSafe: false},
|
||||
{input: "@", isSafe: false},
|
||||
{input: "[", isSafe: false},
|
||||
{input: "]", isSafe: false},
|
||||
{input: "^", isSafe: false},
|
||||
{input: "`", isSafe: false},
|
||||
{input: "{", isSafe: false},
|
||||
{input: "|", isSafe: false},
|
||||
{input: "}", isSafe: false},
|
||||
{input: "abc 123", isSafe: false},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
actual := isSafeURLString(tc.input)
|
||||
if actual != tc.isSafe {
|
||||
t.Errorf("isSafeURLString(%q) = %v, want %v", tc.input, actual, tc.isSafe)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -154,6 +154,20 @@ func RegisterServerCommands() {
|
||||
},
|
||||
serverGetConnectionsFunc,
|
||||
)
|
||||
RegisterCallback(
|
||||
[]string{"", "get", "users"},
|
||||
func(s []string) error {
|
||||
return unexpectedArgsError(s, 3)
|
||||
},
|
||||
serverGetUsersFunc,
|
||||
)
|
||||
RegisterCallback(
|
||||
[]string{"", "get", "quotas"},
|
||||
func(s []string) error {
|
||||
return unexpectedArgsError(s, 3)
|
||||
},
|
||||
serverGetQuotasFunc,
|
||||
)
|
||||
RegisterCallback(
|
||||
[]string{"", "get", "thread-dump"},
|
||||
func(s []string) error {
|
||||
@@ -248,6 +262,14 @@ var serverHelpFunc = func(s []string) error {
|
||||
cmd: "get connections",
|
||||
help: []string{"Get mita server connections."},
|
||||
},
|
||||
{
|
||||
cmd: "get users",
|
||||
help: []string{"Get mita server registered users."},
|
||||
},
|
||||
{
|
||||
cmd: "get quotas",
|
||||
help: []string{"Get mita server user quotas."},
|
||||
},
|
||||
{
|
||||
cmd: "version",
|
||||
help: []string{"Show mita server version."},
|
||||
@@ -744,6 +766,162 @@ var serverGetConnectionsFunc = func(s []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var serverGetUsersFunc = func(_ []string) error {
|
||||
appStatus, err := appctl.GetServerStatusWithRPC(context.Background())
|
||||
if err != nil {
|
||||
if stderror.IsConnRefused(err) {
|
||||
return fmt.Errorf(stderror.ServerNotRunningWithCommand)
|
||||
}
|
||||
return fmt.Errorf(stderror.GetServerStatusFailedErr, err)
|
||||
}
|
||||
if err := appctl.IsServerDaemonRunning(appStatus); err != nil {
|
||||
return fmt.Errorf(stderror.ServerNotRunningErr, err)
|
||||
}
|
||||
|
||||
client, err := appctl.NewServerManagementRPCClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stderror.CreateServerManagementRPCClientFailedErr, err)
|
||||
}
|
||||
timedctx, cancelFunc := context.WithTimeout(context.Background(), appctl.RPCTimeout)
|
||||
defer cancelFunc()
|
||||
userWithMetricsList, err := client.GetUsers(timedctx, &appctlpb.Empty{})
|
||||
if err != nil {
|
||||
return fmt.Errorf(stderror.GetUsersFailedErr, err)
|
||||
}
|
||||
|
||||
header := []string{
|
||||
"User",
|
||||
"LastActive",
|
||||
"1DayDownload",
|
||||
"1DayUpload",
|
||||
"30DaysDownload",
|
||||
"30DaysUpload",
|
||||
}
|
||||
table := make([][]string, 0)
|
||||
table = append(table, header)
|
||||
for _, userWithMetrics := range userWithMetricsList.GetItems() {
|
||||
row := make([]string, 6)
|
||||
row[0] = userWithMetrics.GetUser().GetName()
|
||||
|
||||
// Collect download and upload metrics of this user.
|
||||
var down, up *metrics.Counter
|
||||
var err error
|
||||
for _, metric := range userWithMetrics.GetMetrics() {
|
||||
switch metric.GetName() {
|
||||
case metrics.UserMetricDownloadBytes:
|
||||
down, err = metrics.NewCounterFromMetricPB(metric)
|
||||
if err != nil {
|
||||
return fmt.Errorf("metrics.NewCounterFromMetricPB() failed: %w", err)
|
||||
}
|
||||
case metrics.UserMetricUploadBytes:
|
||||
up, err = metrics.NewCounterFromMetricPB(metric)
|
||||
if err != nil {
|
||||
return fmt.Errorf("metrics.NewCounterFromMetricPB() failed: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lastDownloadTime, lastUploadTime time.Time
|
||||
if down != nil {
|
||||
lastDownloadTime = down.LastUpdateTime()
|
||||
row[2] = common.ByteCountIEC(down.DeltaBetween(time.Now().Add(-24*time.Hour), time.Now()))
|
||||
row[4] = common.ByteCountIEC(down.DeltaBetween(time.Now().Add(-720*time.Hour), time.Now()))
|
||||
} else {
|
||||
row[2] = "-"
|
||||
row[4] = "-"
|
||||
}
|
||||
if up != nil {
|
||||
lastUploadTime = up.LastUpdateTime()
|
||||
row[3] = common.ByteCountIEC(up.DeltaBetween(time.Now().Add(-24*time.Hour), time.Now()))
|
||||
row[5] = common.ByteCountIEC(up.DeltaBetween(time.Now().Add(-720*time.Hour), time.Now()))
|
||||
} else {
|
||||
row[3] = "-"
|
||||
row[5] = "-"
|
||||
}
|
||||
if lastDownloadTime.IsZero() && lastUploadTime.IsZero() {
|
||||
row[1] = "-"
|
||||
} else if lastDownloadTime.After(lastUploadTime) {
|
||||
row[1] = lastDownloadTime.Format(time.RFC3339)
|
||||
} else {
|
||||
row[1] = lastUploadTime.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
table = append(table, row)
|
||||
}
|
||||
printTable(table, " ")
|
||||
return nil
|
||||
}
|
||||
|
||||
var serverGetQuotasFunc = func(_ []string) error {
|
||||
appStatus, err := appctl.GetServerStatusWithRPC(context.Background())
|
||||
if err != nil {
|
||||
if stderror.IsConnRefused(err) {
|
||||
return fmt.Errorf(stderror.ServerNotRunningWithCommand)
|
||||
}
|
||||
return fmt.Errorf(stderror.GetServerStatusFailedErr, err)
|
||||
}
|
||||
if err := appctl.IsServerDaemonRunning(appStatus); err != nil {
|
||||
return fmt.Errorf(stderror.ServerNotRunningErr, err)
|
||||
}
|
||||
|
||||
client, err := appctl.NewServerManagementRPCClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stderror.CreateServerManagementRPCClientFailedErr, err)
|
||||
}
|
||||
timedctx, cancelFunc := context.WithTimeout(context.Background(), appctl.RPCTimeout)
|
||||
defer cancelFunc()
|
||||
userWithMetricsList, err := client.GetUsers(timedctx, &appctlpb.Empty{})
|
||||
if err != nil {
|
||||
return fmt.Errorf(stderror.GetUsersFailedErr, err)
|
||||
}
|
||||
|
||||
header := []string{
|
||||
"User",
|
||||
"Days",
|
||||
"Limit",
|
||||
"Usage",
|
||||
}
|
||||
table := make([][]string, 0)
|
||||
table = append(table, header)
|
||||
for _, userWithMetrics := range userWithMetricsList.GetItems() {
|
||||
if len(userWithMetrics.GetUser().GetQuotas()) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Collect download and upload metrics of this user.
|
||||
var down, up *metrics.Counter
|
||||
var err error
|
||||
for _, metric := range userWithMetrics.GetMetrics() {
|
||||
switch metric.GetName() {
|
||||
case metrics.UserMetricDownloadBytes:
|
||||
down, err = metrics.NewCounterFromMetricPB(metric)
|
||||
if err != nil {
|
||||
return fmt.Errorf("metrics.NewCounterFromMetricPB() failed: %w", err)
|
||||
}
|
||||
case metrics.UserMetricUploadBytes:
|
||||
up, err = metrics.NewCounterFromMetricPB(metric)
|
||||
if err != nil {
|
||||
return fmt.Errorf("metrics.NewCounterFromMetricPB() failed: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if down == nil || up == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, quota := range userWithMetrics.GetUser().GetQuotas() {
|
||||
row := make([]string, 4)
|
||||
row[0] = userWithMetrics.GetUser().GetName()
|
||||
row[1] = strconv.Itoa(int(quota.GetDays()))
|
||||
row[2] = common.ByteCountIEC(int64(quota.GetMegabytes()) * 1048576)
|
||||
row[3] = common.ByteCountIEC(down.DeltaBetween(time.Now().Add(-24*time.Duration(quota.GetDays())*time.Hour), time.Now()) + up.DeltaBetween(time.Now().Add(-24*time.Duration(quota.GetDays())*time.Hour), time.Now()))
|
||||
table = append(table, row)
|
||||
}
|
||||
}
|
||||
printTable(table, " ")
|
||||
return nil
|
||||
}
|
||||
|
||||
var serverGetThreadDumpFunc = func(s []string) error {
|
||||
appStatus, err := appctl.GetServerStatusWithRPC(context.Background())
|
||||
if err != nil {
|
||||
|
@@ -53,15 +53,6 @@ func printSessionInfoList(info *appctlpb.SessionInfoList) {
|
||||
"LastRecv",
|
||||
"LastSend",
|
||||
}
|
||||
idLen := len(header[0])
|
||||
protocolLen := len(header[1])
|
||||
localAddrLen := len(header[2])
|
||||
remoteAddrLen := len(header[3])
|
||||
stateLen := len(header[4])
|
||||
recvQBufLen := len(header[5])
|
||||
sendQBufLen := len(header[6])
|
||||
lastRecvLen := len(header[7])
|
||||
lastSendLen := len(header[8])
|
||||
|
||||
// Map the SessionInfo object to fields, and record the length of the fields.
|
||||
table := make([][]string, 0)
|
||||
@@ -70,20 +61,12 @@ func printSessionInfoList(info *appctlpb.SessionInfoList) {
|
||||
row := make([]string, 9)
|
||||
|
||||
row[0] = si.GetId()
|
||||
idLen = mathext.Max(idLen, len(row[0]))
|
||||
row[1] = si.GetProtocol()
|
||||
protocolLen = mathext.Max(protocolLen, len(row[1]))
|
||||
row[2] = si.GetLocalAddr()
|
||||
localAddrLen = mathext.Max(localAddrLen, len(row[2]))
|
||||
row[3] = si.GetRemoteAddr()
|
||||
remoteAddrLen = mathext.Max(remoteAddrLen, len(row[3]))
|
||||
row[4] = si.GetState()
|
||||
stateLen = mathext.Max(stateLen, len(row[4]))
|
||||
|
||||
row[5] = fmt.Sprintf("%d+%d", si.GetRecvQ(), si.GetRecvBuf())
|
||||
recvQBufLen = mathext.Max(recvQBufLen, len(row[5]))
|
||||
row[6] = fmt.Sprintf("%d+%d", si.GetSendQ(), si.GetSendBuf())
|
||||
sendQBufLen = mathext.Max(sendQBufLen, len(row[6]))
|
||||
|
||||
lastRecvTime := time.Unix(si.LastRecvTime.GetSeconds(), int64(si.LastRecvTime.GetNanos()))
|
||||
if si.GetProtocol() == "TCP" {
|
||||
@@ -91,27 +74,46 @@ func printSessionInfoList(info *appctlpb.SessionInfoList) {
|
||||
} else {
|
||||
row[7] = fmt.Sprintf("%v (%d)", time.Since(lastRecvTime).Truncate(time.Second), si.GetLastRecvSeq())
|
||||
}
|
||||
lastRecvLen = mathext.Max(lastRecvLen, len(row[7]))
|
||||
lastSendTime := time.Unix(si.LastSendTime.GetSeconds(), int64(si.LastSendTime.GetNanos()))
|
||||
row[8] = fmt.Sprintf("%v (%d)", time.Since(lastSendTime).Truncate(time.Second), si.GetLastSendSeq())
|
||||
lastSendLen = mathext.Max(lastSendLen, len(row[8]))
|
||||
|
||||
table = append(table, row)
|
||||
}
|
||||
|
||||
// Pad the length of each row and print.
|
||||
delim := " "
|
||||
printTable(table, " ")
|
||||
}
|
||||
|
||||
func printTable(table [][]string, delim string) {
|
||||
nRow := len(table)
|
||||
if nRow == 0 {
|
||||
return
|
||||
}
|
||||
nCol := len(table[0])
|
||||
if nCol == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Verify each row has the same number of columns.
|
||||
for _, row := range table {
|
||||
rowWithPadding := make([]string, 9)
|
||||
rowWithPadding[0] = fmt.Sprintf("%-"+fmt.Sprintf("%d", idLen)+"s", row[0])
|
||||
rowWithPadding[1] = fmt.Sprintf("%-"+fmt.Sprintf("%d", protocolLen)+"s", row[1])
|
||||
rowWithPadding[2] = fmt.Sprintf("%-"+fmt.Sprintf("%d", localAddrLen)+"s", row[2])
|
||||
rowWithPadding[3] = fmt.Sprintf("%-"+fmt.Sprintf("%d", remoteAddrLen)+"s", row[3])
|
||||
rowWithPadding[4] = fmt.Sprintf("%-"+fmt.Sprintf("%d", stateLen)+"s", row[4])
|
||||
rowWithPadding[5] = fmt.Sprintf("%-"+fmt.Sprintf("%d", recvQBufLen)+"s", row[5])
|
||||
rowWithPadding[6] = fmt.Sprintf("%-"+fmt.Sprintf("%d", sendQBufLen)+"s", row[6])
|
||||
rowWithPadding[7] = fmt.Sprintf("%-"+fmt.Sprintf("%d", lastRecvLen)+"s", row[7])
|
||||
rowWithPadding[8] = fmt.Sprintf("%-"+fmt.Sprintf("%d", lastSendLen)+"s", row[8])
|
||||
if len(row) != nCol {
|
||||
panic(fmt.Sprintf("when print table, row %v has %d columns, expect %d", row, len(row), nCol))
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the length that should occupy by each column.
|
||||
lens := make([]int, nCol)
|
||||
for _, row := range table {
|
||||
for j, field := range row {
|
||||
lens[j] = mathext.Max(lens[j], len(field))
|
||||
}
|
||||
}
|
||||
|
||||
// Print the table with padding.
|
||||
for _, row := range table {
|
||||
rowWithPadding := make([]string, nCol)
|
||||
for j := range row {
|
||||
rowWithPadding[j] = fmt.Sprintf("%-"+fmt.Sprintf("%d", lens[j])+"s", row[j])
|
||||
}
|
||||
log.Infof("%s", strings.Join(rowWithPadding, delim))
|
||||
}
|
||||
}
|
||||
|
32
mieru/pkg/common/format.go
Normal file
32
mieru/pkg/common/format.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2025 mieru authors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package common
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ByteCountIEC formats bytes as a human-readable string (IEC units).
|
||||
func ByteCountIEC(b int64) string {
|
||||
const unit = 1024
|
||||
if b < unit {
|
||||
return fmt.Sprintf("%dB", b)
|
||||
}
|
||||
div, exp := int64(unit), 0
|
||||
for n := b / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
return fmt.Sprintf("%.1f%ciB", float64(b)/float64(div), "KMGTP"[exp])
|
||||
}
|
@@ -100,7 +100,7 @@ func (f *DaemonFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
var value string
|
||||
switch {
|
||||
case key == FieldKeyTime:
|
||||
value = entry.Time.Format(time.RFC3339)
|
||||
value = entry.Time.Format(defaultTimestampFormat)
|
||||
case key == FieldKeyLevel:
|
||||
value = strings.ToUpper(entry.Level.String())
|
||||
case key == FieldKeyMsg:
|
||||
|
@@ -110,6 +110,24 @@ func (c *Counter) DeltaBetween(t1, t2 time.Time) int64 {
|
||||
return sum
|
||||
}
|
||||
|
||||
// LastUpdateTime returns the last time when the history is updated.
|
||||
func (c *Counter) LastUpdateTime() time.Time {
|
||||
if !c.timeSeries {
|
||||
panic(fmt.Sprintf("%s is not a time series Counter", c.name))
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.op++
|
||||
|
||||
if len(c.history) == 0 {
|
||||
return time.Time{}
|
||||
}
|
||||
if c.history[len(c.history)-1].TimeUnixMilli == nil {
|
||||
return time.Time{}
|
||||
}
|
||||
return time.UnixMilli(*c.history[len(c.history)-1].TimeUnixMilli)
|
||||
}
|
||||
|
||||
func (c *Counter) addWithTime(delta int64, time time.Time) int64 {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
@@ -118,6 +136,7 @@ func (c *Counter) addWithTime(delta int64, time time.Time) int64 {
|
||||
if delta == 0 {
|
||||
return c.value
|
||||
}
|
||||
|
||||
c.value += delta
|
||||
if c.timeSeries {
|
||||
if c.history == nil {
|
||||
|
@@ -69,6 +69,10 @@ func TestCounter(t *testing.T) {
|
||||
t.Errorf("DeltaBetween() = %v, want %v", value, tc.value)
|
||||
}
|
||||
}
|
||||
lastUpdateTime := time.Date(2022, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
if c.LastUpdateTime().Unix() != lastUpdateTime.Unix() {
|
||||
t.Errorf("LastUpdateTime() = %v, want %v", c.LastUpdateTime(), lastUpdateTime)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRollUp(t *testing.T) {
|
||||
|
@@ -130,7 +130,7 @@ func LoadMetricsFromDump() error {
|
||||
metricName := pbMetric.GetName()
|
||||
if metric, ok := group.GetMetric(metricName); ok {
|
||||
if counter, ok := metric.(*Counter); ok {
|
||||
fromMetricPB(counter, pbMetric)
|
||||
loadCounterFromMetricPB(counter, pbMetric)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,6 +218,22 @@ func ToMetricPB(src Metric) *pb.Metric {
|
||||
return dst
|
||||
}
|
||||
|
||||
// NewCounterFromMetricPB creates a counter metric from the protobuf.
|
||||
func NewCounterFromMetricPB(src *pb.Metric) (*Counter, error) {
|
||||
if src.GetType() != pb.MetricType_COUNTER && src.GetType() != pb.MetricType_COUNTER_TIME_SERIES {
|
||||
return nil, fmt.Errorf("type %v can't be converted to Counter", src.GetType().String())
|
||||
}
|
||||
|
||||
c := &Counter{
|
||||
name: src.GetName(),
|
||||
}
|
||||
if src.GetType() == pb.MetricType_COUNTER_TIME_SERIES {
|
||||
c.timeSeries = true
|
||||
}
|
||||
loadCounterFromMetricPB(c, src)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// LogMetricsNow writes the current metrics to log.
|
||||
// This function can be called when (periodic) logging is disabled.
|
||||
func LogMetricsNow() {
|
||||
@@ -252,7 +268,7 @@ func logMetricsLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
func fromMetricPB(dst *Counter, src *pb.Metric) {
|
||||
func loadCounterFromMetricPB(dst *Counter, src *pb.Metric) {
|
||||
// Verify the name and type matches.
|
||||
if src.GetName() != dst.Name() {
|
||||
return
|
||||
|
@@ -21,6 +21,9 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
pb "github.com/enfein/mieru/v3/pkg/metrics/metricspb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestEnableAndDisableLogging(t *testing.T) {
|
||||
@@ -58,3 +61,25 @@ func TestMetricsDump(t *testing.T) {
|
||||
t.Fatalf("LoadMetricsFromDump(): %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetricPBConvertion(t *testing.T) {
|
||||
p := &pb.Metric{
|
||||
Name: proto.String("counter"),
|
||||
Type: pb.MetricType_COUNTER_TIME_SERIES.Enum(),
|
||||
Value: proto.Int64(100),
|
||||
History: []*pb.History{
|
||||
{
|
||||
TimeUnixMilli: proto.Int64(time.Now().UnixMilli()),
|
||||
Delta: proto.Int64(100),
|
||||
},
|
||||
},
|
||||
}
|
||||
c, err := NewCounterFromMetricPB(p)
|
||||
if err != nil {
|
||||
t.Fatalf("NewCounterFromMetricPB() failed: %v", err)
|
||||
}
|
||||
p2 := ToMetricPB(c)
|
||||
if !proto.Equal(p, p2) {
|
||||
t.Errorf("metric protobuf doesn't match")
|
||||
}
|
||||
}
|
||||
|
@@ -22,8 +22,8 @@ const (
|
||||
const (
|
||||
UserMetricGroupFormat = userMetricGroupPrefix + "%s"
|
||||
|
||||
UserMetricUploadBytes = "UploadBytes"
|
||||
UserMetricDownloadBytes = "DownloadBytes"
|
||||
UserMetricUploadBytes = "UploadBytes"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -39,12 +39,12 @@ var (
|
||||
// Current number of established connections.
|
||||
CurrEstablished = RegisterMetric("connections", "CurrEstablished", GAUGE)
|
||||
|
||||
// Number of bytes from client to server.
|
||||
UploadBytes = RegisterMetric("traffic", "UploadBytes", COUNTER)
|
||||
|
||||
// Number of bytes from server to client.
|
||||
DownloadBytes = RegisterMetric("traffic", "DownloadBytes", COUNTER)
|
||||
|
||||
// Number of bytes from client to server.
|
||||
UploadBytes = RegisterMetric("traffic", "UploadBytes", COUNTER)
|
||||
|
||||
// Number of padding bytes send to proxy connections.
|
||||
OutputPaddingBytes = RegisterMetric("traffic", "OutputPaddingBytes", COUNTER)
|
||||
)
|
||||
|
@@ -35,6 +35,7 @@ const (
|
||||
GetServerConfigFailedErr = "get mita server config failed: %w"
|
||||
GetServerStatusFailedErr = "get mita server status failed: %w"
|
||||
GetThreadDumpFailedErr = "get thread dump failed: %w"
|
||||
GetUsersFailedErr = "get users failed: %w"
|
||||
InvalidPortBindingsErr = "invalid port bindings: %w"
|
||||
InvalidTransportProtocol = "invalid transport protocol"
|
||||
IPAddressNotFound = "IP address not found from domain name %q"
|
||||
|
@@ -16,5 +16,5 @@
|
||||
package version
|
||||
|
||||
const (
|
||||
AppVersion = "3.13.1"
|
||||
AppVersion = "3.14.1"
|
||||
)
|
||||
|
@@ -112,6 +112,8 @@ fi
|
||||
./mita get heap-profile /test/mita.tcp.heap.gz
|
||||
|
||||
# Print metrics and memory statistics.
|
||||
./mita get users
|
||||
sleep 1
|
||||
print_mieru_client_metrics
|
||||
sleep 1
|
||||
print_mieru_server_metrics
|
||||
|
@@ -116,6 +116,8 @@ fi
|
||||
./mita get heap-profile /test/mita.udp.heap.gz
|
||||
|
||||
# Print metrics and memory statistics.
|
||||
./mita get users
|
||||
sleep 1
|
||||
print_mieru_client_metrics
|
||||
sleep 1
|
||||
print_mieru_server_metrics
|
||||
|
@@ -17,72 +17,218 @@
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
from typing import Any, List
|
||||
|
||||
|
||||
def main() -> None:
|
||||
check_platform()
|
||||
check_version()
|
||||
check_permission()
|
||||
print(detect_package_system())
|
||||
print(detect_cpu_arch())
|
||||
sys_info = SysInfo()
|
||||
installer = Installer()
|
||||
print(installer.download_mita(sys_info))
|
||||
|
||||
|
||||
def detect_package_system() -> str:
|
||||
if is_deb():
|
||||
return 'deb'
|
||||
if is_rpm():
|
||||
return 'rpm'
|
||||
else:
|
||||
class SysInfo:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.check_python_version()
|
||||
self.check_platform()
|
||||
self.check_permission()
|
||||
|
||||
self.package_manager = self.detect_package_manager()
|
||||
if self.package_manager == '':
|
||||
print_exit('Failed to detect system package manager. Supported: deb, rpm.')
|
||||
self.cpu_arch = self.detect_cpu_arch()
|
||||
if self.cpu_arch == '':
|
||||
print_exit('Failed to detect CPU architecture. Supported: amd64, arm64.')
|
||||
|
||||
self.is_mita_installed = self.detect_mita_installed()
|
||||
self.installed_mita_version = None
|
||||
self.is_mita_config_applied = False
|
||||
if self.is_mita_installed:
|
||||
version_out = run_command(['mita', 'version'], check=True)
|
||||
version = Version()
|
||||
if version.parse(version_out.stdout.strip()):
|
||||
self.installed_mita_version = version
|
||||
if os.path.exists('/etc/mita/server.conf.pb') and \
|
||||
os.stat('/etc/mita/server.conf.pb').st_size > 0:
|
||||
self.is_mita_config_applied = True
|
||||
self.latest_mita_version = None
|
||||
latest_mita_version_str = self.query_latest_mita_version()
|
||||
version2 = Version()
|
||||
if version2.parse(latest_mita_version_str):
|
||||
self.latest_mita_version = version2
|
||||
|
||||
|
||||
def check_python_version(self) -> None:
|
||||
if sys.version_info < (3, 8, 0):
|
||||
print_exit('Python version must be 3.8.0 or higher.')
|
||||
|
||||
|
||||
def check_platform(self) -> None:
|
||||
if not sys.platform.startswith('linux'):
|
||||
print_exit('You can only run this program on Linux.')
|
||||
|
||||
|
||||
def check_permission(self) -> None:
|
||||
uid = os.getuid()
|
||||
if uid != 0:
|
||||
print_exit('Only root user can run this program.')
|
||||
|
||||
|
||||
def detect_package_manager(self) -> str:
|
||||
if self.is_deb():
|
||||
return 'deb'
|
||||
elif self.is_rpm():
|
||||
return 'rpm'
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
def is_deb(self) -> bool:
|
||||
'''
|
||||
Return true if system uses deb package.
|
||||
'''
|
||||
result = run_command(['dpkg', '-l'])
|
||||
return result.returncode == 0 and len(result.stdout.splitlines()) > 1
|
||||
|
||||
|
||||
def is_rpm(self) -> bool:
|
||||
'''
|
||||
Return true if system uses rpm package.
|
||||
'''
|
||||
result = run_command(['rpm', '-qa'])
|
||||
return result.returncode == 0 and len(result.stdout.splitlines()) > 1
|
||||
|
||||
|
||||
def detect_mita_installed(self) -> bool:
|
||||
'''
|
||||
Return true if mita deb or rpm package is installed.
|
||||
'''
|
||||
if self.is_deb():
|
||||
result = run_command(['dpkg', '-l'])
|
||||
for l in result.stdout.splitlines():
|
||||
if "mita" in l:
|
||||
return True
|
||||
elif self.is_rpm():
|
||||
result = run_command(['rpm', '-qa'])
|
||||
for l in result.stdout.splitlines():
|
||||
if "mita" in l:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def detect_cpu_arch(self) -> str:
|
||||
machine = platform.machine()
|
||||
if machine == 'x86_64' or machine == 'AMD64':
|
||||
return 'amd64'
|
||||
elif machine == 'aarch64' or machine == 'arm64':
|
||||
return 'arm64'
|
||||
return ''
|
||||
|
||||
|
||||
def is_deb() -> bool:
|
||||
'''
|
||||
Return true if system uses deb package.
|
||||
'''
|
||||
result = run_command(['dpkg', '-l'], timeout=15)
|
||||
return result.returncode == 0 and len(result.stdout.splitlines()) > 1
|
||||
def query_latest_mita_version(self) -> str:
|
||||
'''
|
||||
Use GitHub API to fetch the latest mita version.
|
||||
'''
|
||||
try:
|
||||
resp = urllib.request.urlopen("https://api.github.com/repos/enfein/mieru/releases/latest")
|
||||
body = resp.read()
|
||||
j = json.loads(body.decode('utf-8'))
|
||||
return j["tag_name"].strip('v')
|
||||
except Exception as e:
|
||||
print_exit(f"Failed to query latest mita version: {e}")
|
||||
|
||||
|
||||
def is_rpm() -> bool:
|
||||
'''
|
||||
Return true if system uses rpm package.
|
||||
'''
|
||||
result = run_command(['rpm', '-qa'], timeout=15)
|
||||
return result.returncode == 0 and len(result.stdout.splitlines()) > 1
|
||||
class Version:
|
||||
|
||||
def __init__(self, major=None, minor=None, patch=None):
|
||||
self.major = major
|
||||
self.minor = minor
|
||||
self.patch = patch
|
||||
|
||||
|
||||
def detect_cpu_arch() -> str:
|
||||
if is_amd64():
|
||||
return 'amd64'
|
||||
if is_arm64():
|
||||
return 'arm64'
|
||||
return ''
|
||||
def __str__(self):
|
||||
return f'{self.major}.{self.minor}.{self.patch}'
|
||||
|
||||
|
||||
def is_amd64() -> bool:
|
||||
'''
|
||||
Return true if the CPU architecture is amd64.
|
||||
'''
|
||||
machine = platform.machine()
|
||||
return machine == 'x86_64' or machine == 'AMD64'
|
||||
def parse(self, v: str) -> bool:
|
||||
match = re.match(r'(\d+)\.(\d+)\.(\d+)', v)
|
||||
if match:
|
||||
self.major = int(match.group(1))
|
||||
self.minor = int(match.group(2))
|
||||
self.patch = int(match.group(3))
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def is_arm64() -> bool:
|
||||
'''
|
||||
Return true if the CPU architecture is arm64.
|
||||
'''
|
||||
machine = platform.machine()
|
||||
return machine == 'aarch64' or machine == 'arm64'
|
||||
def is_less_than(self, another) -> bool:
|
||||
'''
|
||||
Return true if this version is less than another version.
|
||||
'''
|
||||
if self.major is None or self.minor is None or self.patch is None or \
|
||||
another.major is None or another.minor is None or another.patch is None:
|
||||
return False # Handle uninitialized versions
|
||||
|
||||
if self.major < another.major:
|
||||
return True
|
||||
elif self.major > another.major:
|
||||
return False
|
||||
if self.minor < another.minor:
|
||||
return True
|
||||
elif self.minor > another.minor:
|
||||
return False
|
||||
if self.patch < another.patch:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def run_command(args: List[str], input=None, timeout=None, check=False):
|
||||
class Installer:
|
||||
|
||||
def download_mita(self, sys_info: SysInfo) -> str:
|
||||
'''
|
||||
Download mita deb or rpm installation package.
|
||||
Return the path of downloaded file.
|
||||
'''
|
||||
if sys_info.latest_mita_version == None:
|
||||
print_exit('Latest mita version is unknown')
|
||||
ver = sys_info.latest_mita_version
|
||||
download_url = ''
|
||||
if sys_info.package_manager == 'deb' and sys_info.cpu_arch == 'amd64':
|
||||
download_url = f'https://github.com/enfein/mieru/releases/download/v{ver}/mita_{ver}_amd64.deb'
|
||||
elif sys_info.package_manager == 'deb' and sys_info.cpu_arch == 'arm64':
|
||||
download_url = f'https://github.com/enfein/mieru/releases/download/v{ver}/mita_{ver}_arm64.deb'
|
||||
elif sys_info.package_manager == 'rpm' and sys_info.cpu_arch == 'amd64':
|
||||
download_url = f'https://github.com/enfein/mieru/releases/download/v{ver}/mita-{ver}-1.x86_64.rpm'
|
||||
elif sys_info.package_manager == 'rpm' and sys_info.cpu_arch == 'arm64':
|
||||
download_url = f'https://github.com/enfein/mieru/releases/download/v{ver}/mita-{ver}-1.aarch64.rpm'
|
||||
else:
|
||||
print_exit(f'Failed to determine download URL based on package manager {sys_info.package_manager} and CPU architecture {sys_info.cpu_arch}')
|
||||
filename = os.path.join('/tmp', download_url.split('/')[-1])
|
||||
try:
|
||||
urllib.request.urlretrieve(download_url, filename)
|
||||
except urllib.error.URLError as e:
|
||||
print_exit(f'Failed to download {download_url}: {e}')
|
||||
return filename
|
||||
|
||||
|
||||
class Configurer:
|
||||
pass
|
||||
|
||||
|
||||
class Uninstaller:
|
||||
pass
|
||||
|
||||
|
||||
def run_command(args: List[str], input=None, timeout=10, check=False):
|
||||
'''
|
||||
Run the command and return a subprocess.CompletedProcess instance.
|
||||
'''
|
||||
@@ -101,22 +247,6 @@ def run_command(args: List[str], input=None, timeout=None, check=False):
|
||||
return result
|
||||
|
||||
|
||||
def check_platform() -> None:
|
||||
if not sys.platform.startswith('linux'):
|
||||
print_exit('You can only run this program on Linux.')
|
||||
|
||||
|
||||
def check_version() -> None:
|
||||
if sys.version_info < (3, 8, 0):
|
||||
print_exit('Python version must be 3.8.0 or higher.')
|
||||
|
||||
|
||||
def check_permission() -> None:
|
||||
uid = os.getuid()
|
||||
if uid != 0:
|
||||
print_exit('Only root user can run this program.')
|
||||
|
||||
|
||||
def print_exit(*values: Any) -> None:
|
||||
print(*values)
|
||||
sys.exit(1)
|
||||
|
@@ -91,7 +91,8 @@ def deb_uninstall() -> None:
|
||||
run_command('[uninstall deb package]', ['dpkg', '-P', 'mita'])
|
||||
run_command('[remove mita configuration]', ['rm', '-rf', '/etc/mita'])
|
||||
run_command('[remove mita metrics]', ['rm', '-rf', '/var/lib/mita'])
|
||||
run_command('[remove mita runtime]', ['rm', '-rf', '/var/run/mita'])
|
||||
run_command('[remove mita runtime old]', ['rm', '-rf', '/var/run/mita.sock'])
|
||||
run_command('[remove mita runtime new]', ['rm', '-rf', '/var/run/mita'])
|
||||
run_command('[remove mita systemd unit]', ['rm', '-f', '/lib/systemd/system/mita.service'])
|
||||
run_command('[remove TCP BBR sysctl patch]', ['rm', '-f', '/etc/sysctl.d/mieru_tcp_bbr.conf'])
|
||||
run_command('[reload systemd]', ['systemctl', 'daemon-reload'])
|
||||
@@ -105,7 +106,8 @@ def rpm_uninstall() -> None:
|
||||
run_command('[remove mita configuration]', ['rm', '-rf', '/etc/mita'])
|
||||
run_command('[remove mita mailbox]', ['rm', '-rf', '/var/spool/mail/mita'])
|
||||
run_command('[remove mita metrics]', ['rm', '-rf', '/var/lib/mita'])
|
||||
run_command('[remove mita runtime]', ['rm', '-rf', '/var/run/mita'])
|
||||
run_command('[remove mita runtime old]', ['rm', '-rf', '/var/run/mita.sock'])
|
||||
run_command('[remove mita runtime new]', ['rm', '-rf', '/var/run/mita'])
|
||||
run_command('[remove mita systemd unit]', ['rm', '-f', '/lib/systemd/system/mita.service'])
|
||||
run_command('[remove TCP BBR sysctl patch]', ['rm', '-f', '/etc/sysctl.d/mieru_tcp_bbr.conf'])
|
||||
run_command('[reload systemd]', ['systemctl', 'daemon-reload'])
|
||||
|
Reference in New Issue
Block a user