mirror of
https://github.com/bolucat/Archive.git
synced 2026-04-22 16:07:49 +08:00
1208 lines
36 KiB
Go
1208 lines
36 KiB
Go
// Copyright (C) 2021 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 cli
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/user"
|
|
"runtime/pprof"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/enfein/mieru/v3/apis/trafficpattern"
|
|
"github.com/enfein/mieru/v3/pkg/appctl"
|
|
"github.com/enfein/mieru/v3/pkg/appctl/appctlcommon"
|
|
"github.com/enfein/mieru/v3/pkg/appctl/appctlgrpc"
|
|
"github.com/enfein/mieru/v3/pkg/appctl/appctlpb"
|
|
"github.com/enfein/mieru/v3/pkg/cipher"
|
|
"github.com/enfein/mieru/v3/pkg/common"
|
|
"github.com/enfein/mieru/v3/pkg/log"
|
|
"github.com/enfein/mieru/v3/pkg/metrics"
|
|
"github.com/enfein/mieru/v3/pkg/protocol"
|
|
"github.com/enfein/mieru/v3/pkg/socks5"
|
|
"github.com/enfein/mieru/v3/pkg/stderror"
|
|
"github.com/enfein/mieru/v3/pkg/version/updater"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/reflection"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/types/known/emptypb"
|
|
)
|
|
|
|
// RegisterServerCommands registers all the server side CLI commands.
|
|
func RegisterServerCommands() {
|
|
binaryName = "mita"
|
|
RegisterCallback(
|
|
[]string{"", "help"},
|
|
func(s []string) error {
|
|
return unexpectedArgsError(s, 2)
|
|
},
|
|
serverHelpFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "start"},
|
|
func(s []string) error {
|
|
return unexpectedArgsError(s, 2)
|
|
},
|
|
serverStartFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "run"},
|
|
func(s []string) error {
|
|
return unexpectedArgsError(s, 2)
|
|
},
|
|
serverRunFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "stop"},
|
|
func(s []string) error {
|
|
return unexpectedArgsError(s, 2)
|
|
},
|
|
serverStopFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "reload"},
|
|
func(s []string) error {
|
|
return unexpectedArgsError(s, 2)
|
|
},
|
|
serverReloadFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "status"},
|
|
func(s []string) error {
|
|
return unexpectedArgsError(s, 2)
|
|
},
|
|
serverStatusFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "apply", "config"},
|
|
func(s []string) error {
|
|
if len(s) < 4 {
|
|
return fmt.Errorf("usage: mita apply config <FILE>. no config file is provided")
|
|
} else if len(s) > 4 {
|
|
return fmt.Errorf("usage: mita apply config <FILE>. more than 1 config file is provided")
|
|
}
|
|
return nil
|
|
},
|
|
serverApplyConfigFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "describe", "config"},
|
|
func(s []string) error {
|
|
return unexpectedArgsError(s, 3)
|
|
},
|
|
serverDescribeConfigFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "describe", "effective-traffic-pattern"},
|
|
func(s []string) error {
|
|
return unexpectedArgsError(s, 3)
|
|
},
|
|
serverDescribeEffectiveTrafficPatternFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "export", "traffic-pattern"},
|
|
func(s []string) error {
|
|
return unexpectedArgsError(s, 3)
|
|
},
|
|
serverExportTrafficPatternFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "explain", "traffic-pattern"},
|
|
func(s []string) error {
|
|
if len(s) < 4 {
|
|
return fmt.Errorf("usage: mita explain traffic-pattern <STRING>. No string is provided")
|
|
} else if len(s) > 4 {
|
|
return fmt.Errorf("usage: mita explain traffic-pattern <STRING>. More than 1 string is provided")
|
|
}
|
|
return nil
|
|
},
|
|
explainTrafficPatternFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "delete", "user"},
|
|
func(s []string) error {
|
|
if len(s) < 4 {
|
|
return fmt.Errorf("usage: mita delete user <USER_NAME>. no user is provided")
|
|
}
|
|
return nil
|
|
},
|
|
serverDeleteUserFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "version"},
|
|
func(s []string) error {
|
|
return unexpectedArgsError(s, 2)
|
|
},
|
|
versionFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "describe", "build"},
|
|
func(s []string) error {
|
|
return unexpectedArgsError(s, 3)
|
|
},
|
|
describeBuildFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "check", "update"},
|
|
func(s []string) error {
|
|
return unexpectedArgsError(s, 3)
|
|
},
|
|
serverCheckUpdateFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "get", "metrics"},
|
|
func(s []string) error {
|
|
return unexpectedArgsError(s, 3)
|
|
},
|
|
serverGetMetricsFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "get", "connections"},
|
|
func(s []string) error {
|
|
return unexpectedArgsError(s, 3)
|
|
},
|
|
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 {
|
|
return unexpectedArgsError(s, 3)
|
|
},
|
|
serverGetThreadDumpFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "get", "heap-profile"},
|
|
func(s []string) error {
|
|
if len(s) < 4 {
|
|
return fmt.Errorf("usage: mita get heap-profile <FILE>. no file save path is provided")
|
|
} else if len(s) > 4 {
|
|
return fmt.Errorf("usage: mita get heap-profile <FILE>. more than 1 file save path is provided")
|
|
}
|
|
return nil
|
|
},
|
|
serverGetHeapProfileFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "get", "memory-statistics"},
|
|
func(s []string) error {
|
|
return unexpectedArgsError(s, 3)
|
|
},
|
|
serverGetMemoryStatisticsFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "profile", "cpu", "start"},
|
|
func(s []string) error {
|
|
if len(s) < 5 {
|
|
return fmt.Errorf("usage: mita profile cpu start <FILE>. no file save path is provided")
|
|
} else if len(s) > 5 {
|
|
return fmt.Errorf("usage: mita profile cpu start <FILE>. more than 1 file save path is provided")
|
|
}
|
|
return nil
|
|
},
|
|
serverStartCPUProfileFunc,
|
|
)
|
|
RegisterCallback(
|
|
[]string{"", "profile", "cpu", "stop"},
|
|
func(s []string) error {
|
|
return unexpectedArgsError(s, 4)
|
|
},
|
|
serverStopCPUProfileFunc,
|
|
)
|
|
}
|
|
|
|
var serverHelpFunc = func(s []string) error {
|
|
helpFmt := helpFormatter{
|
|
appName: "mita",
|
|
entries: []helpCmdEntry{
|
|
{
|
|
cmd: "help",
|
|
help: []string{"Show mita server help."},
|
|
},
|
|
{
|
|
cmd: "start",
|
|
help: []string{"Start mita server proxy service."},
|
|
},
|
|
{
|
|
cmd: "stop",
|
|
help: []string{"Stop mita server proxy service."},
|
|
},
|
|
{
|
|
cmd: "reload",
|
|
help: []string{"Reload mita server configuration without stopping proxy service."},
|
|
},
|
|
{
|
|
cmd: "status",
|
|
help: []string{"Check mita server proxy service status."},
|
|
},
|
|
{
|
|
cmd: "apply config <JSON_FILE>",
|
|
help: []string{
|
|
"Apply server configuration patch from a file.",
|
|
"It merges the patch with existing server configuration.",
|
|
},
|
|
},
|
|
{
|
|
cmd: "describe config",
|
|
help: []string{"Show current server configuration."},
|
|
},
|
|
{
|
|
cmd: "describe effective-traffic-pattern",
|
|
help: []string{"Show effective traffic pattern."},
|
|
},
|
|
{
|
|
cmd: "export traffic-pattern",
|
|
help: []string{"Export traffic pattern as an encoded base64 string."},
|
|
},
|
|
{
|
|
cmd: "explain traffic-pattern <STRING>",
|
|
help: []string{"Decode and explain a traffic pattern from an encoded base64 string."},
|
|
},
|
|
{
|
|
cmd: "delete user <USER_NAME>",
|
|
help: []string{"Delete a user from server configuration."},
|
|
},
|
|
{
|
|
cmd: "get metrics",
|
|
help: []string{"Get mita server metrics."},
|
|
},
|
|
{
|
|
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."},
|
|
},
|
|
{
|
|
cmd: "check update",
|
|
help: []string{"Check mita server update."},
|
|
},
|
|
},
|
|
advanced: []helpCmdEntry{
|
|
{
|
|
cmd: "run",
|
|
help: []string{
|
|
"Run mita server in foreground.",
|
|
"Use environment variable MITA_CONFIG_JSON_FILE to load configuration.",
|
|
},
|
|
},
|
|
{
|
|
cmd: "describe build",
|
|
help: []string{"Show mita build info."},
|
|
},
|
|
{
|
|
cmd: "get thread-dump",
|
|
help: []string{"Get mita server thread dump."},
|
|
},
|
|
{
|
|
cmd: "get heap-profile <GZ_FILE>",
|
|
help: []string{"Get mita server heap profile and save results to the file."},
|
|
},
|
|
{
|
|
cmd: "get memory-statistics",
|
|
help: []string{"Get mita server memory statistics."},
|
|
},
|
|
{
|
|
cmd: "profile cpu start <GZ_FILE>",
|
|
help: []string{"Start mita server CPU profile and save results to the file."},
|
|
},
|
|
{
|
|
cmd: "profile cpu stop",
|
|
help: []string{"Stop mita server CPU profile."},
|
|
},
|
|
},
|
|
}
|
|
helpFmt.print()
|
|
return nil
|
|
}
|
|
|
|
var serverStartFunc = func(s []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)
|
|
}
|
|
if err := appctl.IsServerProxyRunning(appStatus); err == nil {
|
|
log.Infof("mita server proxy is running")
|
|
return nil
|
|
}
|
|
|
|
// Start server proxy.
|
|
client, err := appctl.NewServerManagementRPCClient()
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.CreateServerManagementRPCClientFailedErr, err)
|
|
}
|
|
timedctx, cancelFunc := context.WithTimeout(context.Background(), appctl.RPCTimeout)
|
|
defer cancelFunc()
|
|
_, err = client.Start(timedctx, &emptypb.Empty{})
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.StartServerProxyFailedErr, err)
|
|
}
|
|
log.Infof("mita server proxy is started")
|
|
return nil
|
|
}
|
|
|
|
var serverRunFunc = func(s []string) error {
|
|
if _, found := os.LookupEnv("MITA_LOG_NO_TIMESTAMP"); found {
|
|
log.SetFormatter(&log.DaemonFormatter{NoTimestamp: true})
|
|
} else {
|
|
log.SetFormatter(&log.DaemonFormatter{})
|
|
}
|
|
|
|
appctl.SetAppStatus(appctlpb.AppStatus_IDLE)
|
|
|
|
var rpcTasks sync.WaitGroup
|
|
rpcTasks.Add(1)
|
|
|
|
// Run the RPC server in the background.
|
|
go func() {
|
|
rpcAddr := appctl.ServerUDS()
|
|
if err := syscall.Unlink(rpcAddr); err != nil {
|
|
// Unlink() fails when the file path doesn't exist, which is not a big problem.
|
|
log.Debugf("syscall.Unlink(%q) failed: %v", rpcAddr, err)
|
|
}
|
|
rpcListener, err := net.Listen("unix", rpcAddr)
|
|
if err != nil {
|
|
log.Fatalf("listen on RPC address %q failed: %v", rpcAddr, err)
|
|
}
|
|
if _, found := os.LookupEnv("MITA_INSECURE_UDS"); !found {
|
|
if err = updateServerUDSPermission(); err != nil {
|
|
log.Fatalf("update server unix domain socket permission failed: %v", err)
|
|
}
|
|
}
|
|
grpcServer := grpc.NewServer(grpc.MaxRecvMsgSize(appctl.MaxRecvMsgSize))
|
|
appctl.SetServerRPCServerRef(grpcServer)
|
|
appctlgrpc.RegisterServerManagementServiceServer(grpcServer, appctl.NewServerManagementService())
|
|
reflection.Register(grpcServer)
|
|
close(appctl.ServerRPCServerStarted)
|
|
log.Infof("mita server daemon RPC server is running")
|
|
if err = grpcServer.Serve(rpcListener); err != nil {
|
|
log.Fatalf("run gRPC server failed: %v", err)
|
|
}
|
|
log.Infof("mita server daemon RPC server is stopped")
|
|
rpcTasks.Done()
|
|
}()
|
|
<-appctl.ServerRPCServerStarted
|
|
|
|
// Load server config. If server config file doesn't exist, create a new one.
|
|
config, err := appctl.LoadServerConfig()
|
|
if err != nil {
|
|
if err == stderror.ErrFileNotExist {
|
|
if err = appctl.StoreServerConfig(&appctlpb.ServerConfig{}); err != nil {
|
|
return fmt.Errorf(stderror.CreateEmptyServerConfigFailedErr, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Load server config again if needed.
|
|
if config == nil {
|
|
config, err = appctl.LoadServerConfig()
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.GetServerConfigFailedErr, err)
|
|
}
|
|
}
|
|
|
|
// Set logging level based on server config.
|
|
loggingLevel := config.GetLoggingLevel().String()
|
|
if loggingLevel != appctlpb.LoggingLevel_DEFAULT.String() {
|
|
log.SetLevel(loggingLevel)
|
|
}
|
|
|
|
// Load previous metrics if possible.
|
|
if err := os.MkdirAll("/var/lib/mita", 0775); err == nil {
|
|
const metricsDumpPath = "/var/lib/mita/metrics.pb"
|
|
metrics.SetMetricsDumpFilePath(metricsDumpPath)
|
|
if err := metrics.LoadMetricsFromDump(); err == nil {
|
|
log.Infof("Loaded previous metrics from %s", metricsDumpPath)
|
|
} else {
|
|
log.Infof("Unable to load previous metrics: %v", err)
|
|
}
|
|
if err := metrics.EnableMetricsDump(); err != nil {
|
|
log.Warnf("Failed to enable metrics dump: %v", err)
|
|
}
|
|
}
|
|
|
|
// Disable client side metrics.
|
|
if clientDecryptionMetricGroup := metrics.GetMetricGroupByName(cipher.ClientDecryptionMetricGroupName); clientDecryptionMetricGroup != nil {
|
|
clientDecryptionMetricGroup.DisableLogging()
|
|
}
|
|
if httpMetricGroup := metrics.GetMetricGroupByName(socks5.HTTPMetricGroupName); httpMetricGroup != nil {
|
|
httpMetricGroup.DisableLogging()
|
|
}
|
|
|
|
// Detect and log TCP congestion control algorithm.
|
|
if algo := common.TCPCongestionControlAlgorithm(); algo != "" {
|
|
log.Infof("TCP congestion control algorithm is %q", algo)
|
|
}
|
|
|
|
// Start proxy if server config is valid.
|
|
if err = appctl.ValidateFullServerConfig(config); err == nil {
|
|
appctl.SetAppStatus(appctlpb.AppStatus_STARTING)
|
|
|
|
trafficPattern, err := trafficpattern.NewConfig(config.TrafficPattern)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
mux := protocol.NewMux(false).
|
|
SetTrafficPattern(trafficPattern).
|
|
SetServerUsers(appctlcommon.UserListToMap(config.GetUsers())).
|
|
SetServerUserHintIsMandatory(config.GetAdvancedSettings().GetUserHintIsMandatory())
|
|
appctl.SetServerMuxRef(mux)
|
|
mtu := common.DefaultMTU
|
|
if config.GetMtu() != 0 {
|
|
mtu = int(config.GetMtu())
|
|
}
|
|
endpoints, err := appctlcommon.PortBindingsToUnderlayProperties(config.GetPortBindings(), mtu)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
mux.SetEndpoints(endpoints)
|
|
|
|
// Create the egress socks5 server.
|
|
socks5Config := &socks5.Config{
|
|
AuthOpts: socks5.Auth{
|
|
ClientSideAuthentication: true,
|
|
},
|
|
DualStackPreference: common.DualStackPreference(config.GetDns().GetDualStack()),
|
|
Egress: config.GetEgress(),
|
|
HandshakeTimeout: 10 * time.Second,
|
|
Users: appctlcommon.UserListToMap(config.GetUsers()),
|
|
}
|
|
socks5Server, err := socks5.New(socks5Config)
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.CreateSocks5ServerFailedErr, err)
|
|
}
|
|
appctl.SetSocks5Server(socks5Server)
|
|
|
|
// Run the egress socks5 server in the background.
|
|
var proxyTasks sync.WaitGroup
|
|
var initProxyTasks sync.WaitGroup
|
|
proxyTasks.Add(1)
|
|
initProxyTasks.Add(1)
|
|
go func() {
|
|
if err = mux.Start(); err != nil {
|
|
log.Fatalf("socks5 server listening failed: %v", err)
|
|
}
|
|
initProxyTasks.Done()
|
|
|
|
log.Infof("mita server daemon socks5 server is running")
|
|
if err = socks5Server.Serve(mux); err != nil {
|
|
log.Fatalf("run socks5 server failed: %v", err)
|
|
}
|
|
log.Infof("mita server daemon socks5 server is stopped")
|
|
proxyTasks.Done()
|
|
}()
|
|
|
|
initProxyTasks.Wait()
|
|
|
|
if config.GetAdvancedSettings().GetMetricsLoggingInterval() != "" {
|
|
metricsDuration, err := time.ParseDuration(config.GetAdvancedSettings().GetMetricsLoggingInterval())
|
|
if err != nil {
|
|
log.Warnf("Failed to parse metrics logging interval %q from server configuration: %v", config.GetAdvancedSettings().GetMetricsLoggingInterval(), err)
|
|
} else {
|
|
if err := metrics.SetLoggingDuration(metricsDuration); err != nil {
|
|
log.Warnf("Failed to set metrics logging duration: %v", err)
|
|
}
|
|
}
|
|
}
|
|
metrics.EnableLogging()
|
|
|
|
appctl.SetAppStatus(appctlpb.AppStatus_RUNNING)
|
|
log.Debugf("Started proxy after %v", appctl.Elapsed())
|
|
proxyTasks.Wait()
|
|
}
|
|
|
|
// If fails to validate server configuration, do nothing.
|
|
// Most likely the server configuration is empty.
|
|
// It will be set by new RPC calls.
|
|
|
|
rpcTasks.Wait()
|
|
|
|
// Stop CPU profiling, if previously started.
|
|
pprof.StopCPUProfile()
|
|
|
|
log.Infof("mita server daemon exit now")
|
|
return nil
|
|
}
|
|
|
|
var serverStopFunc = func(s []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)
|
|
}
|
|
if err := appctl.IsServerProxyRunning(appStatus); err != nil {
|
|
return fmt.Errorf(stderror.ServerProxyNotRunningErr, err)
|
|
}
|
|
|
|
// Stop server proxy.
|
|
client, err := appctl.NewServerManagementRPCClient()
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.CreateServerManagementRPCClientFailedErr, err)
|
|
}
|
|
timedctx, cancelFunc := context.WithTimeout(context.Background(), appctl.RPCTimeout)
|
|
defer cancelFunc()
|
|
if _, err = client.Stop(timedctx, &emptypb.Empty{}); err != nil {
|
|
return fmt.Errorf(stderror.StopServerProxyFailedErr, err)
|
|
}
|
|
log.Infof("mita server proxy is stopped")
|
|
return nil
|
|
}
|
|
|
|
var serverReloadFunc = func(s []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()
|
|
if _, err = client.Reload(timedctx, &emptypb.Empty{}); err != nil {
|
|
return fmt.Errorf(stderror.ReloadServerFailedErr, err)
|
|
}
|
|
log.Infof("mita server is reloaded")
|
|
return nil
|
|
}
|
|
|
|
var serverStatusFunc = func(s []string) error {
|
|
appStatus, err := appctl.GetServerStatusWithRPC(context.Background())
|
|
if err != nil {
|
|
if stderror.IsConnRefused(err) {
|
|
return fmt.Errorf(stderror.ServerNotRunningWithCommand)
|
|
} else if stderror.IsPermissionDenied(err) {
|
|
currentUser, err := user.Current()
|
|
if err != nil {
|
|
cmd := strings.Join(s, " ")
|
|
return fmt.Errorf("unable to determine the OS user which executed command %q", cmd)
|
|
}
|
|
return fmt.Errorf("unable to connect to mita server daemon via %q; please retry after running \"sudo usermod -a -G mita %s\" command and logout the system, then login again", appctl.ServerUDS(), currentUser.Username)
|
|
} else {
|
|
return fmt.Errorf(stderror.GetServerStatusFailedErr, err)
|
|
}
|
|
}
|
|
if err := appctl.IsServerDaemonRunning(appStatus); err != nil {
|
|
return fmt.Errorf(stderror.ServerNotRunningErr, err)
|
|
}
|
|
if err := appctl.IsServerProxyRunning(appStatus); err != nil {
|
|
log.Infof("%s", err.Error())
|
|
} else {
|
|
log.Infof("mita server status is %q", appctlpb.AppStatus_RUNNING.String())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var serverApplyConfigFunc = func(s []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)
|
|
}
|
|
|
|
path := s[3]
|
|
b, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return fmt.Errorf("os.ReadFile(%q) failed: %w", path, err)
|
|
}
|
|
patch := &appctlpb.ServerConfig{}
|
|
if err = common.UnmarshalJSON(b, patch); err != nil {
|
|
return fmt.Errorf("common.UnmarshalJSON() failed: %w", err)
|
|
}
|
|
if err := appctl.ValidateServerConfigPatch(patch); err != nil {
|
|
return fmt.Errorf(stderror.ValidateServerConfigPatchFailedErr, err)
|
|
}
|
|
|
|
client, err := appctl.NewServerManagementRPCClient()
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.CreateServerManagementRPCClientFailedErr, err)
|
|
}
|
|
timedctx, cancelFunc := context.WithTimeout(context.Background(), appctl.RPCTimeout)
|
|
defer cancelFunc()
|
|
_, err = client.SetConfig(timedctx, patch)
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.SetServerConfigFailedErr, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var serverDescribeConfigFunc = func(s []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()
|
|
config, err := client.GetConfig(timedctx, &emptypb.Empty{})
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.GetServerConfigFailedErr, err)
|
|
}
|
|
jsonBytes, err := common.MarshalJSON(config)
|
|
if err != nil {
|
|
return fmt.Errorf("common.MarshalJSON() failed: %w", err)
|
|
}
|
|
log.Infof("%s", string(jsonBytes))
|
|
return nil
|
|
}
|
|
|
|
var serverDescribeEffectiveTrafficPatternFunc = func(s []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()
|
|
config, err := client.GetConfig(timedctx, &emptypb.Empty{})
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.GetServerConfigFailedErr, err)
|
|
}
|
|
tp, err := trafficpattern.NewConfig(config.GetTrafficPattern())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
jsonBytes, err := common.MarshalJSON(tp.Effective())
|
|
if err != nil {
|
|
return fmt.Errorf("common.MarshalJSON() failed: %w", err)
|
|
}
|
|
log.Infof("%s", string(jsonBytes))
|
|
return nil
|
|
}
|
|
|
|
var serverExportTrafficPatternFunc = func(s []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()
|
|
config, err := client.GetConfig(timedctx, &emptypb.Empty{})
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.GetServerConfigFailedErr, err)
|
|
}
|
|
log.Infof("%s", trafficpattern.Encode(config.GetTrafficPattern()))
|
|
return nil
|
|
}
|
|
|
|
var serverDeleteUserFunc = func(s []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()
|
|
config, err := client.GetConfig(timedctx, &emptypb.Empty{})
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.GetServerConfigFailedErr, err)
|
|
}
|
|
users := config.GetUsers()
|
|
remaining := make([]*appctlpb.User, 0)
|
|
for _, user := range users {
|
|
shouldDelete := false
|
|
for _, toDelete := range s[3:] {
|
|
if user.GetName() == toDelete {
|
|
shouldDelete = true
|
|
break
|
|
}
|
|
}
|
|
if !shouldDelete {
|
|
remaining = append(remaining, user)
|
|
}
|
|
}
|
|
config.Users = remaining
|
|
_, err = client.SetConfig(timedctx, config)
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.SetServerConfigFailedErr, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var serverCheckUpdateFunc = func(s []string) error {
|
|
_, msg, err := updater.CheckUpdate("")
|
|
if err != nil {
|
|
return fmt.Errorf("check update failed: %w", err)
|
|
}
|
|
log.Infof("%s", msg)
|
|
return nil
|
|
}
|
|
|
|
var serverGetMetricsFunc = func(s []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()
|
|
metrics, err := client.GetMetrics(timedctx, &emptypb.Empty{})
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.GetMetricsFailedErr, err)
|
|
}
|
|
log.Infof("%s", metrics.GetJson())
|
|
return nil
|
|
}
|
|
|
|
var serverGetConnectionsFunc = func(s []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()
|
|
info, err := client.GetSessionInfoList(timedctx, &emptypb.Empty{})
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.GetConnectionsFailedErr, err)
|
|
}
|
|
printSessionInfoList(info)
|
|
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, &emptypb.Empty{})
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.GetUsersFailedErr, err)
|
|
}
|
|
|
|
header := []string{
|
|
"User",
|
|
"LastActive",
|
|
"1DayDown",
|
|
"1DayUp",
|
|
"7DaysDown",
|
|
"7DaysUp",
|
|
"30DaysDown",
|
|
"30DaysUp",
|
|
}
|
|
table := make([][]string, 0)
|
|
table = append(table, header)
|
|
for _, userWithMetrics := range userWithMetricsList.GetItems() {
|
|
row := make([]string, 8)
|
|
row[0] = userWithMetrics.GetUser().GetName()
|
|
|
|
// Collect download and upload metrics of this user.
|
|
var down, up *metrics.Counter
|
|
for _, metric := range userWithMetrics.GetMetrics() {
|
|
switch metric.GetName() {
|
|
case metrics.UserMetricDownloadBytes:
|
|
downMetric, err := metrics.FromMetricPB(metric)
|
|
if err != nil {
|
|
return fmt.Errorf("metrics.FromMetricPB() failed: %w", err)
|
|
}
|
|
down = downMetric.(*metrics.Counter)
|
|
case metrics.UserMetricUploadBytes:
|
|
upMetric, err := metrics.FromMetricPB(metric)
|
|
if err != nil {
|
|
return fmt.Errorf("metrics.FromMetricPB() failed: %w", err)
|
|
}
|
|
up = upMetric.(*metrics.Counter)
|
|
}
|
|
}
|
|
|
|
var lastDownloadTime, lastUploadTime time.Time
|
|
now := time.Now()
|
|
if down != nil {
|
|
lastDownloadTime = down.LastUpdateTime()
|
|
row[2] = common.ByteCountIEC(down.DeltaBetween(now.Add(-24*time.Hour), now))
|
|
row[4] = common.ByteCountIEC(down.DeltaBetween(now.Add(-168*time.Hour), now))
|
|
row[6] = common.ByteCountIEC(down.DeltaBetween(now.Add(-720*time.Hour), now))
|
|
} else {
|
|
row[2] = "-"
|
|
row[4] = "-"
|
|
row[6] = "-"
|
|
}
|
|
if up != nil {
|
|
lastUploadTime = up.LastUpdateTime()
|
|
row[3] = common.ByteCountIEC(up.DeltaBetween(now.Add(-24*time.Hour), now))
|
|
row[5] = common.ByteCountIEC(up.DeltaBetween(now.Add(-168*time.Hour), now))
|
|
row[7] = common.ByteCountIEC(up.DeltaBetween(now.Add(-720*time.Hour), now))
|
|
} else {
|
|
row[3] = "-"
|
|
row[5] = "-"
|
|
row[7] = "-"
|
|
}
|
|
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, &emptypb.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
|
|
for _, metric := range userWithMetrics.GetMetrics() {
|
|
switch metric.GetName() {
|
|
case metrics.UserMetricDownloadBytes:
|
|
downMetric, err := metrics.FromMetricPB(metric)
|
|
if err != nil {
|
|
return fmt.Errorf("metrics.FromMetricPB() failed: %w", err)
|
|
}
|
|
down = downMetric.(*metrics.Counter)
|
|
case metrics.UserMetricUploadBytes:
|
|
upMetric, err := metrics.FromMetricPB(metric)
|
|
if err != nil {
|
|
return fmt.Errorf("metrics.FromMetricPB() failed: %w", err)
|
|
}
|
|
up = upMetric.(*metrics.Counter)
|
|
}
|
|
}
|
|
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 {
|
|
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()
|
|
dump, err := client.GetThreadDump(timedctx, &emptypb.Empty{})
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.GetThreadDumpFailedErr, err)
|
|
}
|
|
log.Infof("%s", dump.GetThreadDump())
|
|
return nil
|
|
}
|
|
|
|
var serverGetHeapProfileFunc = func(s []string) error {
|
|
appStatus, err := appctl.GetServerStatusWithRPC(context.Background())
|
|
if err != nil {
|
|
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()
|
|
if _, err := client.GetHeapProfile(timedctx, &appctlpb.ProfileSavePath{FilePath: proto.String(s[3])}); err != nil {
|
|
return fmt.Errorf(stderror.GetHeapProfileFailedErr, err)
|
|
}
|
|
log.Infof("heap profile is saved to %q", s[3])
|
|
return nil
|
|
}
|
|
|
|
var serverGetMemoryStatisticsFunc = func(s []string) error {
|
|
appStatus, err := appctl.GetServerStatusWithRPC(context.Background())
|
|
if err != nil {
|
|
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()
|
|
memStats, err := client.GetMemoryStatistics(timedctx, &emptypb.Empty{})
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.GetMemoryStatisticsFailedErr, err)
|
|
}
|
|
json, err := common.MarshalJSON(memStats)
|
|
if err != nil {
|
|
return fmt.Errorf(stderror.GetMemoryStatisticsFailedErr, err)
|
|
}
|
|
log.Infof("%s", string(json))
|
|
return nil
|
|
}
|
|
|
|
var serverStartCPUProfileFunc = func(s []string) error {
|
|
appStatus, err := appctl.GetServerStatusWithRPC(context.Background())
|
|
if err != nil {
|
|
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()
|
|
if _, err := client.StartCPUProfile(timedctx, &appctlpb.ProfileSavePath{FilePath: proto.String(s[4])}); err != nil {
|
|
return fmt.Errorf(stderror.StartCPUProfileFailedErr, err)
|
|
}
|
|
log.Infof("CPU profile will be saved to %q", s[4])
|
|
return nil
|
|
}
|
|
|
|
var serverStopCPUProfileFunc = func(s []string) error {
|
|
appStatus, err := appctl.GetServerStatusWithRPC(context.Background())
|
|
if err != nil {
|
|
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()
|
|
client.StopCPUProfile(timedctx, &emptypb.Empty{})
|
|
return nil
|
|
}
|
|
|
|
// Update server unix domain socket permission to 770, belongs to mita:mita.
|
|
func updateServerUDSPermission() error {
|
|
mitaUidStr, err := getUid("mita")
|
|
if err != nil {
|
|
return fmt.Errorf("getUid(%q) failed: %w", "mita", err)
|
|
}
|
|
mitaUid, err := strconv.Atoi(mitaUidStr)
|
|
if err != nil {
|
|
return fmt.Errorf("convert mita UID with strconv.Atoi(%q) failed: %w", mitaUidStr, err)
|
|
}
|
|
mitaGidStr, err := getGid("mita")
|
|
if err != nil {
|
|
return fmt.Errorf("getGid(%q) failed: %w", "mita", err)
|
|
}
|
|
mitaGid, err := strconv.Atoi(mitaGidStr)
|
|
if err != nil {
|
|
return fmt.Errorf("convert mita UID with strconv.Atoi(%q) failed: %w", mitaGidStr, err)
|
|
}
|
|
if err = os.Chown(appctl.ServerUDS(), mitaUid, mitaGid); err != nil {
|
|
return fmt.Errorf("os.Chown(%q) failed: %w", appctl.ServerUDS(), err)
|
|
}
|
|
if err = os.Chmod(appctl.ServerUDS(), 0770); err != nil {
|
|
return fmt.Errorf("os.Chmod(%q) failed: %w", appctl.ServerUDS(), err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getUid(username string) (string, error) {
|
|
u, err := user.Lookup(username)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return u.Uid, nil
|
|
}
|
|
|
|
func getGid(username string) (string, error) {
|
|
u, err := user.Lookup(username)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return u.Gid, nil
|
|
}
|