refactor: update agent and core utilities (#12621)

This commit is contained in:
ssongliu
2026-05-21 12:35:09 +08:00
committed by zhengkunwang223
parent 84a27de792
commit d55982bde0
86 changed files with 1854 additions and 924 deletions
+2 -2
View File
@@ -990,7 +990,7 @@ func validateAgentConfigFileContent(agentType, content string) error {
}
func getOpenclawNPMRegistry(containerName string) (string, error) {
registry, err := runDockerExecWithStdout(20*time.Second, containerName, "npm", "get", "registry")
registry, err := cmd.RunDockerExecWithStdout(20*time.Second, containerName, "npm", "get", "registry")
if err != nil {
return "", err
}
@@ -1002,7 +1002,7 @@ func getOpenclawNPMRegistry(containerName string) (string, error) {
}
func setOpenclawNPMRegistry(containerName, registry string) error {
return cmd.RunDefaultBashCf("docker exec %s npm set registry %q", containerName, registry)
return cmd.NewCommandMgr().Run("docker", "exec", containerName, "npm", "set", "registry", registry)
}
func (a AgentService) loadAgentAndInstall(agentID uint) (*model.Agent, *model.AppInstall, error) {
+5 -11
View File
@@ -23,7 +23,7 @@ func (a AgentService) CreateRole(req dto.AgentRoleCreateReq) (*dto.AgentRoleCrea
return nil, err
}
args := []string{"exec", install.ContainerName, "openclaw", "agents", "add", req.Name}
args := []string{"openclaw", "agents", "add", req.Name}
workspace := "/home/node/.openclaw/workspace-agent_" + req.Name
agentDir := "/home/node/.openclaw/agents/" + req.Name
args = append(args, "--workspace", workspace)
@@ -43,8 +43,7 @@ func (a AgentService) CreateRole(req dto.AgentRoleCreateReq) (*dto.AgentRoleCrea
args = append(args, "--agent-dir", agentDir)
args = append(args, "--non-interactive", "--json")
mgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Minute))
output, err := mgr.RunWithStdout("docker", args...)
output, err := cmd.RunDockerExecWithStdout(5*time.Minute, install.ContainerName, args...)
if err != nil {
return nil, err
}
@@ -125,10 +124,8 @@ func (a AgentService) DeleteRole(req dto.AgentRoleDeleteReq) error {
return buserr.New("ErrRecordNotFound")
}
args := []string{"exec", install.ContainerName, "openclaw", "agents", "delete", req.ID, "--force"}
mgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Minute))
if _, err = mgr.RunWithStdout("docker", args...); err != nil {
args := []string{"openclaw", "agents", "delete", req.ID, "--force"}
if _, err = cmd.RunDockerExecWithStdout(5*time.Minute, install.ContainerName, args...); err != nil {
return err
}
if target.Workspace != "" {
@@ -171,8 +168,6 @@ func (a AgentService) operateRoleBinding(req dto.AgentRoleBindReq, action string
return buserr.New("ErrInvalidParams")
}
args := []string{
"exec",
install.ContainerName,
"openclaw",
"agents",
action,
@@ -182,8 +177,7 @@ func (a AgentService) operateRoleBinding(req dto.AgentRoleBindReq, action string
binding,
}
args = append(args, "--json")
mgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Minute))
_, err = mgr.RunWithStdout("docker", args...)
_, err = cmd.RunDockerExecWithStdout(5*time.Minute, install.ContainerName, args...)
return err
}
+52 -23
View File
@@ -372,14 +372,14 @@ func (a AgentService) InstallPlugin(req dto.AgentPluginInstallReq) error {
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(10*time.Minute))
if req.Type == "qqbot" {
legacyPluginPath := path.Join(openclawPluginBaseDir, "qqbot")
if err := mgr.RunBashCf("docker exec %s test -d %s", install.ContainerName, legacyPluginPath); err == nil {
if _, err := mgr.RunWithStdout("docker", "exec", "-i", install.ContainerName, "sh", "-c", buildOpenclawPluginUninstallScript("qqbot")); err != nil {
if err := mgr.Run("docker", "exec", install.ContainerName, "test", "-d", legacyPluginPath); err == nil {
if err := uninstallOpenclawPlugin(mgr, install.ContainerName, "qqbot"); err != nil {
return err
}
time.Sleep(2 * time.Second)
}
}
if _, err := mgr.RunWithStdout("docker", "exec", install.ContainerName, "sh", "-c", buildOpenclawPluginInstallScript(spec, pluginID)); err != nil {
if err := installOpenclawPlugin(mgr, install.ContainerName, spec, pluginID); err != nil {
return err
}
conf, err := readOpenclawConfig(agent.ConfigPath)
@@ -415,11 +415,11 @@ func (a AgentService) UpgradePlugin(req dto.AgentPluginUpgradeReq) error {
}
upgradeTask.AddSubTask("Upgrade OpenClaw plugin", func(t *task.Task) error {
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(10*time.Minute))
if _, err := mgr.RunWithStdout("docker", "exec", "-i", install.ContainerName, "sh", "-c", buildOpenclawPluginUninstallScript(pluginID)); err != nil {
if err := uninstallOpenclawPlugin(mgr, install.ContainerName, pluginID); err != nil {
return err
}
time.Sleep(2 * time.Second)
if _, err := mgr.RunWithStdout("docker", "exec", install.ContainerName, "sh", "-c", buildOpenclawPluginInstallScript(spec, pluginID)); err != nil {
if err := installOpenclawPlugin(mgr, install.ContainerName, spec, pluginID); err != nil {
return err
}
conf, err := readOpenclawConfig(agent.ConfigPath)
@@ -455,7 +455,7 @@ func (a AgentService) UninstallPlugin(req dto.AgentPluginUninstallReq) error {
}
uninstallTask.AddSubTask("Uninstall OpenClaw plugin", func(t *task.Task) error {
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(10*time.Minute))
if _, err := mgr.RunWithStdout("docker", "exec", "-i", install.ContainerName, "sh", "-c", buildOpenclawPluginUninstallScript(pluginID)); err != nil {
if err := uninstallOpenclawPlugin(mgr, install.ContainerName, pluginID); err != nil {
return err
}
conf, err := readOpenclawConfig(agent.ConfigPath)
@@ -487,7 +487,7 @@ func (a AgentService) LoginWeixinChannel(req dto.AgentWeixinLoginReq) error {
if agent.AgentType == constant.AppHermesAgent {
return mgr.Run("docker", buildHermesWeixinLoginArgs(install.ContainerName)...)
}
return mgr.RunBashCf("docker exec %s openclaw channels login --channel openclaw-weixin", install.ContainerName)
return mgr.Run("docker", "exec", install.ContainerName, "openclaw", "channels", "login", "--channel", "openclaw-weixin")
}, nil)
if agent.AgentType == constant.AppHermesAgent {
loginTask.AddSubTask("Restart Hermes-Agent container", func(t *task.Task) error {
@@ -584,17 +584,26 @@ func (a AgentService) ApproveChannelPairing(req dto.AgentChannelPairingApproveRe
return validateHermesPairingApproveResult(output, err)
}
if req.AccountID != "" {
return cmd.RunDefaultBashCf(
"docker exec %s openclaw pairing approve %s %q --account %q",
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).Run(
"docker",
"exec",
install.ContainerName,
"openclaw",
"pairing",
"approve",
req.Type,
req.PairingCode,
"--account",
req.AccountID,
)
}
return cmd.RunDefaultBashCf(
"docker exec %s openclaw pairing approve %s %q",
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).Run(
"docker",
"exec",
install.ContainerName,
"openclaw",
"pairing",
"approve",
req.Type,
req.PairingCode,
)
@@ -1289,20 +1298,40 @@ func appendPluginAllow(conf map[string]interface{}, pluginID string) {
plugins["allow"] = append(allow, pluginID)
}
func buildOpenclawPluginInstallScript(spec, pluginID string) string {
return fmt.Sprintf(
"set -e; workdir=%s/%s; rm -rf \"$workdir\"; mkdir -p \"$workdir\"; cd \"$workdir\"; npm pack --silent %q >/dev/null 2>&1; pkg=$(find \"$workdir\" -maxdepth 1 -type f -name '*.tgz' | head -n 1); printf '%%s\\n' \"$pkg\"; openclaw plugins install \"$pkg\" --dangerously-force-unsafe-install; rm -rf \"$workdir\"",
openclawPluginPackageTmpDir,
pluginID,
spec,
)
func installOpenclawPlugin(mgr *cmd.CommandHelper, containerName, spec, pluginID string) error {
workdir := path.Join(openclawPluginPackageTmpDir, pluginID)
defer func() {
_ = mgr.Run("docker", "exec", containerName, "rm", "-rf", workdir)
}()
if err := mgr.Run("docker", "exec", containerName, "rm", "-rf", workdir); err != nil {
return err
}
if err := mgr.Run("docker", "exec", containerName, "mkdir", "-p", workdir); err != nil {
return err
}
if err := mgr.Run("docker", "exec", "-w", workdir, containerName, "npm", "pack", "--silent", spec); err != nil {
return err
}
pkgPath, err := mgr.RunWithStdout("docker", "exec", containerName, "find", workdir, "-maxdepth", "1", "-type", "f", "-name", "*.tgz", "-print", "-quit")
if err != nil {
return err
}
pkgPath = strings.TrimSpace(pkgPath)
if pkgPath == "" {
return fmt.Errorf("openclaw plugin package not found")
}
return mgr.Run("docker", "exec", containerName, "openclaw", "plugins", "install", pkgPath, "--dangerously-force-unsafe-install")
}
func buildOpenclawPluginUninstallScript(pluginID string) string {
return fmt.Sprintf(
"set +e; printf 'yes\\n' | openclaw plugins uninstall %s; code=$?; if [ \"$code\" -eq 137 ]; then exit 0; fi; exit \"$code\"",
pluginID,
func uninstallOpenclawPlugin(mgr *cmd.CommandHelper, containerName, pluginID string) error {
_, err := mgr.RunPipe(
cmd.PipeCommand{Name: "printf", Args: []string{"yes\n"}},
cmd.PipeCommand{Name: "docker", Args: []string{"exec", "-i", containerName, "openclaw", "plugins", "uninstall", pluginID}},
)
if err != nil && strings.Contains(err.Error(), "exit status 137") {
return nil
}
return err
}
func resolvePluginMeta(pluginType string) (string, string, error) {
@@ -1357,7 +1386,7 @@ func loadOpenclawPluginLatestVersion(containerName, pluginType string) (string,
if err != nil {
return "", err
}
output, err := runDockerExecWithStdout(20*time.Second, containerName, "npm", "view", spec, "version", "--json")
output, err := cmd.RunDockerExecWithStdout(20*time.Second, containerName, "npm", "view", spec, "version", "--json")
if err != nil {
return "", err
}
+2 -1
View File
@@ -10,6 +10,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
)
const (
@@ -101,7 +102,7 @@ func countOpenclawConfiguredChannels(conf map[string]interface{}) int {
}
func loadOpenclawOverviewSkillStats(containerName string) (int, error) {
output, err := runDockerExecWithStdout(5*time.Minute, containerName, "sh", "-c", "openclaw skills list --json 2>&1")
output, err := cmd.RunDockerExecWithStdout(5*time.Minute, containerName, "openclaw", "skills", "list", "--json")
if err != nil {
return 0, err
}
+22 -16
View File
@@ -55,7 +55,7 @@ func (a AgentService) ListSkills(req dto.AgentIDReq) ([]dto.AgentSkillItem, erro
if agent.AgentType != constant.AppOpenclaw {
return nil, fmt.Errorf("%s does not support", agent.AgentType)
}
output, err := runDockerExecWithStdout(5*time.Minute, install.ContainerName, "sh", "-c", "openclaw skills list --json 2>&1")
output, err := cmd.RunDockerExecWithStdout(5*time.Minute, install.ContainerName, "openclaw", "skills", "list", "--json")
if err != nil {
return nil, err
}
@@ -143,7 +143,7 @@ func (a AgentService) InstallSkill(req dto.AgentSkillInstallReq) error {
}
installTask.AddSubTask("Install OpenClaw skill", func(t *task.Task) error {
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(20*time.Minute))
return mgr.Run("docker", "exec", install.ContainerName, "sh", "-c", buildOpenclawSkillInstallCommand(req.Source, req.Slug))
return installOpenclawSkill(mgr, install.ContainerName, req.Source, req.Slug)
}, nil)
go func() {
if err := installTask.Execute(); err != nil {
@@ -205,9 +205,9 @@ func parseOpenclawSkillsList(output string) ([]dto.AgentSkillItem, error) {
func loadOpenclawSkillSearchOutput(containerName, source, keyword string) (string, error) {
switch source {
case "skillhub":
return runDockerExecWithStdout(2*time.Minute, containerName, "skillhub", "search", keyword, "--json")
return cmd.RunDockerExecWithStdout(2*time.Minute, containerName, "skillhub", "search", keyword, "--json")
default:
return runDockerExecWithStdout(
return cmd.RunDockerExecWithStdout(
2*time.Minute,
containerName,
"sh",
@@ -261,22 +261,28 @@ func parseClawhubSearchResult(output, source string) []dto.AgentSkillSearchItem
return items
}
func buildOpenclawSkillInstallCommand(source, slug string) string {
func installOpenclawSkill(mgr *cmd.CommandHelper, containerName, source, slug string) error {
if err := mgr.Run("docker", "exec", containerName, "mkdir", "-p", openclawManagedSkillsDir); err != nil {
return err
}
switch source {
case "clawhub-global", "clawhub-cn":
return fmt.Sprintf(
"mkdir -p %s && CLAWHUB_REGISTRY=%q clawhub --workdir /home/node/.openclaw --dir skills install %q",
openclawManagedSkillsDir,
resolveClawhubRegistry(source),
return mgr.Run(
"docker",
"exec",
"-e",
"CLAWHUB_REGISTRY="+resolveClawhubRegistry(source),
containerName,
"clawhub",
"--workdir",
"/home/node/.openclaw",
"--dir",
"skills",
"install",
slug,
)
default:
return fmt.Sprintf(
"mkdir -p %s && skillhub --dir %s install %q",
openclawManagedSkillsDir,
openclawManagedSkillsDir,
slug,
)
return mgr.Run("docker", "exec", containerName, "skillhub", "--dir", openclawManagedSkillsDir, "install", slug)
}
}
@@ -290,7 +296,7 @@ func resolveClawhubRegistry(source string) string {
}
func getOpenclawSkillKey(containerName, name string) (string, error) {
output, err := runDockerExecWithStdout(2*time.Minute, containerName, "sh", "-c", fmt.Sprintf("openclaw skills info %q --json 2>&1", name))
output, err := cmd.RunDockerExecWithStdout(2*time.Minute, containerName, "openclaw", "skills", "info", name, "--json")
if err != nil {
return "", err
}
-6
View File
@@ -19,7 +19,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
@@ -60,11 +59,6 @@ func ensureContainerRunning(containerName string) error {
return nil
}
func runDockerExecWithStdout(timeout time.Duration, containerName string, args ...string) (string, error) {
commandArgs := append([]string{"exec", containerName}, args...)
return cmd.NewCommandMgr(cmd.WithTimeout(timeout)).RunWithStdout("docker", commandArgs...)
}
func resolveAgentAccountInput(provider, apiKey, baseURL string) (resolvedAgentAccountInput, error) {
resolvedAPIKey := strings.TrimSpace(apiKey)
resolvedBaseURL := strings.TrimSpace(baseURL)
+8 -5
View File
@@ -77,7 +77,7 @@ func (u *AIToolService) LoadDetail(name string) (string, error) {
if err != nil {
return "", err
}
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s ollama show %s", containerName, name)
stdout, err := cmd.RunDockerExecWithStdout(20*time.Second, containerName, "ollama", "show", name)
if err != nil {
return "", err
}
@@ -137,7 +137,7 @@ func (u *AIToolService) Close(name string) error {
if err != nil {
return err
}
if err := cmd.RunDefaultBashCf("docker exec %s ollama stop %s", containerName, name); err != nil {
if err := cmd.NewCommandMgr().Run("docker", "exec", containerName, "ollama", "stop", name); err != nil {
return fmt.Errorf("handle ollama stop %s failed, %v", name, err)
}
return nil
@@ -194,7 +194,7 @@ func (u *AIToolService) Delete(req dto.ForceDelete) error {
}
for _, item := range ollamaList {
if item.Status != constant.StatusDeleted {
if err := cmd.RunDefaultBashCf("docker exec %s ollama rm %s", containerName, item.Name); err != nil && !req.ForceDelete {
if err := cmd.NewCommandMgr().Run("docker", "exec", containerName, "ollama", "rm", item.Name); err != nil && !req.ForceDelete {
return fmt.Errorf("handle ollama rm %s failed, %v", item.Name, err)
}
}
@@ -210,7 +210,7 @@ func (u *AIToolService) Sync() ([]dto.OllamaModelDropList, error) {
if err != nil {
return nil, err
}
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s ollama list", containerName)
stdout, err := cmd.RunDockerExecWithStdout(20*time.Second, containerName, "ollama", "list")
if err != nil {
return nil, err
}
@@ -382,12 +382,15 @@ func LoadContainerName() (string, error) {
}
func loadModelSize(name string, containerName string) (string, error) {
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s ollama list | grep %s", containerName, name)
stdout, err := cmd.RunDockerExecWithStdout(20*time.Second, containerName, "ollama", "list")
if err != nil {
return "", err
}
lines := strings.Split(stdout, "\n")
for _, line := range lines {
if !strings.Contains(line, name) {
continue
}
parts := strings.Fields(line)
if len(parts) < 5 {
continue
+22 -8
View File
@@ -3,6 +3,12 @@ package service
import (
"encoding/json"
"fmt"
"mime"
"sort"
"strings"
"sync"
"time"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
@@ -15,11 +21,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/email"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
"github.com/shirou/gopsutil/v4/disk"
"mime"
"sort"
"strings"
"sync"
"time"
)
type AlertService struct{}
@@ -308,12 +309,25 @@ func (a AlertService) GetDisks() ([]dto.DiskDTO, error) {
func executeDiskCommand() (string, error) {
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(2 * time.Second))
stdout, err := cmdMgr.RunWithStdoutBashC("df -hT -P | grep '/' | grep -v tmpfs | grep -v 'snap/core' | grep -v udev")
stdout, err := cmdMgr.RunWithStdout("df", "-hT", "-P")
if err != nil {
cmdMgr2 := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Second))
stdout, err = cmdMgr2.RunWithStdoutBashC("df -lhT -P | grep '/' | grep -v tmpfs | grep -v 'snap/core' | grep -v udev")
stdout, err = cmdMgr2.RunWithStdout("df", "-lhT", "-P")
}
return stdout, err
if err != nil {
return stdout, err
}
var lines []string
for _, line := range strings.Split(stdout, "\n") {
if !strings.Contains(line, "/") || strings.Contains(line, "tmpfs") || strings.Contains(line, "snap/core") || strings.Contains(line, "udev") {
continue
}
lines = append(lines, line)
}
if len(lines) == 0 {
return "", nil
}
return strings.Join(lines, "\n"), nil
}
func shouldExclude(fields []string, mountPoint string, excludes map[string]struct{}) bool {
+36 -4
View File
@@ -665,7 +665,7 @@ func buildNginx(parentTask *task.Task) error {
logStr := fmt.Sprintf("%s %s", i18n.GetMsgByKey("TaskBuild"), i18n.GetMsgByKey("Image"))
parentTask.LogStart(logStr)
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*parentTask), cmd.WithTimeout(60*time.Minute))
if err = cmdMgr.RunBashCf("docker compose -f %s build", nginxInstall.GetComposePath()); err != nil {
if err = cmdMgr.Run("docker", "compose", "-f", nginxInstall.GetComposePath(), "build"); err != nil {
return err
}
parentTask.LogSuccess(logStr)
@@ -768,8 +768,7 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
}
}
command := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rn %s/* %s || true", detailDir, install.GetPath()))
_, _ = command.CombinedOutput()
_ = copyAppDetailMissing(fileOp, detailDir, install.GetPath())
if install.App.Key == constant.AppOpenresty {
installBuildDir := path.Join(install.GetPath(), "build")
detailBuildDir := path.Join(detailDir, "build")
@@ -1129,7 +1128,7 @@ func runScript(task *task.Task, appInstall *model.AppInstall, operate string) er
task.LogStart(logStr)
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute), cmd.WithWorkDir(workDir))
if err := cmdMgr.RunBashC(scriptPath); err != nil {
if err := cmdMgr.Run("bash", scriptPath); err != nil {
task.LogFailedWithErr(logStr, err)
return err
}
@@ -1935,6 +1934,39 @@ func isHostModel(dockerCompose string) bool {
return false
}
func copyAppDetailMissing(fileOp files.FileOp, srcDir, dstDir string) error {
entries, err := os.ReadDir(srcDir)
if err != nil {
return err
}
for _, entry := range entries {
if strings.HasPrefix(entry.Name(), ".") {
continue
}
srcPath := path.Join(srcDir, entry.Name())
dstPath := path.Join(dstDir, entry.Name())
if !fileOp.Stat(dstPath) {
if entry.IsDir() {
if err := fileOp.CopyDir(srcPath, dstDir); err != nil {
return err
}
continue
}
if err := fileOp.CopyFile(srcPath, dstDir); err != nil {
return err
}
continue
}
if !entry.IsDir() {
continue
}
if err := copyAppDetailMissing(fileOp, srcPath, dstPath); err != nil {
return err
}
}
return nil
}
func getRestartPolicy(yml string) string {
var project docker.ComposeProject
if err := yaml.Unmarshal([]byte(yml), &project); err != nil {
+3 -3
View File
@@ -97,7 +97,7 @@ func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, recordID
}
}
if err := cmd.RunDefaultBashCf("docker exec %s redis-cli -a %s --no-auth-warning save", redisInfo.ContainerName, redisInfo.Password); err != nil {
if err := cmd.NewCommandMgr().Run("docker", "exec", redisInfo.ContainerName, "redis-cli", "-a", redisInfo.Password, "--no-auth-warning", "save"); err != nil {
return err
}
@@ -109,13 +109,13 @@ func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, recordID
return nil
}
if strings.HasSuffix(fileName, ".aof") {
if err := cmd.RunDefaultBashCf("docker cp %s:/data/appendonly.aof %s/%s", redisInfo.ContainerName, backupDir, fileName); err != nil {
if err := cmd.NewCommandMgr().Run("docker", "cp", redisInfo.ContainerName+":/data/appendonly.aof", path.Join(backupDir, fileName)); err != nil {
return err
}
return nil
}
if err := cmd.RunDefaultBashCf("docker cp %s:/data/dump.rdb %s/%s", redisInfo.ContainerName, backupDir, fileName); err != nil {
if err := cmd.NewCommandMgr().Run("docker", "cp", redisInfo.ContainerName+":/data/dump.rdb", path.Join(backupDir, fileName)); err != nil {
return err
}
return nil
+1 -1
View File
@@ -178,7 +178,7 @@ func handleWebsiteRecover(website *model.Website, parentTask *task.Task, recover
if err = fileOp.TarGzExtractPro(fmt.Sprintf("%s/%s.web.tar.gz", tmpPath, website.Alias), GetOpenrestyDir(SitesRootDir), ""); err != nil {
return err
}
if err := cmd.RunDefaultBashCf("docker exec -i %s nginx -s reload", nginxInfo.ContainerName); err != nil {
if err := cmd.NewCommandMgr().Run("docker", "exec", "-i", nginxInfo.ContainerName, "nginx", "-s", "reload"); err != nil {
return err
}
oldWebsite.ID = website.ID
+3 -2
View File
@@ -86,8 +86,9 @@ func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
baseInfo.IsActive = false
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second))
if baseInfo.IsActive {
version, err := cmd.RunDefaultWithStdoutBashC("clamdscan --version")
version, err := cmdMgr.RunWithStdout("clamdscan", "--version")
if err == nil {
if strings.Contains(version, "/") {
baseInfo.Version = strings.TrimPrefix(strings.Split(version, "/")[0], "ClamAV ")
@@ -99,7 +100,7 @@ func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
_ = clam.CheckWithStopAll(false, clamRepo)
}
if baseInfo.FreshIsActive {
version, err := cmd.RunDefaultWithStdoutBashC("freshclam --version")
version, err := cmdMgr.RunWithStdout("freshclam", "--version")
if err == nil {
if strings.Contains(version, "/") {
baseInfo.FreshVersion = strings.TrimPrefix(strings.Split(version, "/")[0], "ClamAV ")
+4 -5
View File
@@ -1167,7 +1167,7 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
func (u *ContainerService) LoadUsers(req dto.OperationWithName) []string {
var users []string
std, err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout("docker", "exec", req.Name, "cat", "/etc/passwd")
std, err := cmd.RunDockerExecWithStdout(20*time.Second, req.Name, "cat", "/etc/passwd")
if err != nil {
return users
}
@@ -1440,7 +1440,7 @@ func (u *ContainerService) DownloadContainerFile(req dto.ContainerFileReq) (io.R
fileName = "container-file"
}
if stat.Mode.IsDir() {
if _, err := runContainerCommand(cli, req.ContainerID, []string{"sh", "-c", "command -v tar >/dev/null 2>&1"}); err != nil {
if _, err := runContainerCommand(cli, req.ContainerID, []string{"tar", "--help"}); err != nil {
_ = cli.Close()
return nil, "", "", fmt.Errorf("tar command not found in container")
}
@@ -1620,9 +1620,8 @@ func toContainerFileInfo(filePath string, stat container.PathStat, isDir bool) d
}
func isContainerDir(cli *client.Client, containerID, targetPath string) (bool, error) {
_, err := runContainerCommand(cli, containerID, []string{
"sh", "-c", "[ -d \"$1\" ]", "sh", targetPath,
})
checkPath := strings.TrimSuffix(targetPath, "/") + "/."
_, err := runContainerCommand(cli, containerID, []string{"ls", "-d", "--", checkPath})
if err != nil {
return false, err
}
+2 -2
View File
@@ -391,7 +391,7 @@ func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime t
func backupLogFile(dstFilePath, websiteLogDir string, fileOp files.FileOp) error {
cmdMgr := cmd.NewCommandMgr()
if err := cmdMgr.RunBashCf("tar -czf %s -C %s %s", dstFilePath, websiteLogDir, strings.Join([]string{"access.log", "error.log"}, " ")); err != nil {
if err := cmdMgr.Run("tar", "-czf", dstFilePath, "-C", websiteLogDir, "access.log", "error.log"); err != nil {
dstDir := pathUtils.Dir(dstFilePath)
if err = fileOp.Copy(pathUtils.Join(websiteLogDir, "access.log"), dstDir); err != nil {
return err
@@ -399,7 +399,7 @@ func backupLogFile(dstFilePath, websiteLogDir string, fileOp files.FileOp) error
if err = fileOp.Copy(pathUtils.Join(websiteLogDir, "error.log"), dstDir); err != nil {
return err
}
if err = cmdMgr.RunBashCf("tar -czf %s -C %s %s", dstFilePath, dstDir, strings.Join([]string{"access.log", "error.log"}, " ")); err != nil {
if err = cmdMgr.Run("tar", "-czf", dstFilePath, "-C", dstDir, "access.log", "error.log"); err != nil {
return err
}
_ = fileOp.DeleteFile(pathUtils.Join(dstDir, "access.log"))
+12 -4
View File
@@ -60,7 +60,9 @@ func (u *DashboardService) Restart(operation string) error {
case "system":
{
go func() {
if err := cmd.RunDefaultBashCf("%s reboot", cmd.SudoHandleCmd()); err != nil {
cmdMgr := cmd.NewCommandMgr()
err := cmdMgr.RunWithOptionalSudo("reboot")
if err != nil {
global.LOG.Errorf("handle reboot failed, %v", err)
}
}()
@@ -429,11 +431,17 @@ type diskInfo struct {
func loadDiskInfo() []dto.DiskInfo {
var datas []dto.DiskInfo
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(2 * time.Second))
format := `awk 'NR>1 && !/tmpfs|snap\/core|udev/ {printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", $1, $2, $3, $4, $5, $6, $7}'`
stdout, err := cmdMgr.RunWithStdout("bash", "-c", `timeout 2 df -hT -P | `+format)
format := `NR>1 && !/tmpfs|snap\/core|udev/ {printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", $1, $2, $3, $4, $5, $6, $7}`
stdout, err := cmdMgr.RunPipe(
cmd.PipeCommand{Name: "df", Args: []string{"-hT", "-P"}},
cmd.PipeCommand{Name: "awk", Args: []string{format}},
)
if err != nil {
cmdMgr2 := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Second))
stdout, err = cmdMgr2.RunWithStdout("bash", "-c", `timeout 1 df -lhT -P | `+format)
stdout, err = cmdMgr2.RunPipe(
cmd.PipeCommand{Name: "df", Args: []string{"-lhT", "-P"}},
cmd.PipeCommand{Name: "awk", Args: []string{format}},
)
if err != nil {
return datas
}
+8 -5
View File
@@ -82,7 +82,7 @@ func (u *DeviceService) LoadBaseInfo() (dto.DeviceBaseInfo, error) {
}
func (u *DeviceService) LoadTimeZone() ([]string, error) {
std, err := cmd.NewCommandMgr(cmd.WithTimeout(10 * time.Minute)).RunWithStdoutBashC("timedatectl list-timezones")
std, err := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute)).RunWithStdout("timedatectl", "list-timezones")
if err != nil {
return []string{}, err
}
@@ -223,7 +223,9 @@ func (u *DeviceService) UpdatePasswd(req dto.ChangePasswd) error {
if cmd.CheckIllegal(req.User, req.Passwd) {
return buserr.New("ErrCmdIllegal")
}
if err := cmd.RunDefaultBashCf("%s echo '%s:%s' | %s chpasswd", cmd.SudoHandleCmd(), req.User, req.Passwd, cmd.SudoHandleCmd()); err != nil {
cmdItem := cmd.ExecCommandWithOptionalSudo("chpasswd")
cmdItem.Stdin = strings.NewReader(req.User + ":" + req.Passwd)
if err := cmdItem.Run(); err != nil {
if strings.Contains(err.Error(), "does not exist") {
return buserr.New("ErrNotExistUser")
}
@@ -393,7 +395,7 @@ func loadHosts() []dto.HostHelper {
}
func loadHostname() string {
std, err := cmd.RunDefaultWithStdoutBashC("hostname")
std, err := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second)).RunWithStdout("hostname")
if err != nil {
return ""
}
@@ -401,7 +403,7 @@ func loadHostname() string {
}
func loadUser() string {
std, err := cmd.RunDefaultWithStdoutBashC("whoami")
std, err := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second)).RunWithStdout("whoami")
if err != nil {
return ""
}
@@ -410,7 +412,8 @@ func loadUser() string {
func loadSwap() []dto.SwapHelper {
var data []dto.SwapHelper
std, err := cmd.RunDefaultWithStdoutBashCf("%s swapon --summary", cmd.SudoHandleCmd())
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second))
std, err := cmdMgr.RunWithOptionalSudoAndStdout("swapon", "--summary")
if err != nil {
return data
}
+3 -2
View File
@@ -30,7 +30,8 @@ func NewIDiskService() IDiskService {
func (s *DiskService) GetCompleteDiskInfo() (*response.CompleteDiskInfo, error) {
var diskInfos []response.DiskBasicInfo
output, err := cmd.RunDefaultWithStdoutBashC("lsblk -J -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA")
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second))
output, err := cmdMgr.RunWithStdout("lsblk", "-J", "-o", "NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA")
if err == nil {
diskInfos, err = parseLsblkJsonOutput(output)
if err == nil {
@@ -38,7 +39,7 @@ func (s *DiskService) GetCompleteDiskInfo() (*response.CompleteDiskInfo, error)
return &result, nil
}
}
output, err = cmd.RunDefaultWithStdoutBashC("lsblk -P -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA")
output, err = cmdMgr.RunWithStdout("lsblk", "-P", "-o", "NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA")
if err != nil {
return nil, fmt.Errorf("failed to run lsblk command: %v", err)
}
+10 -4
View File
@@ -4,11 +4,13 @@ import (
"bufio"
"encoding/json"
"fmt"
"github.com/1Panel-dev/1Panel/agent/utils/re"
"os"
"os/exec"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/utils/re"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
@@ -386,10 +388,14 @@ func getParentDevice(device string) string {
}
func getDiskUsageInfo(device string) (size, used, avail string, usePercent int, err error) {
output, err := cmd.RunDefaultWithStdoutBashC(fmt.Sprintf("df -h %s | tail -1", device))
output, err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout("df", "-h", device)
if err != nil {
return "", "", "", 0, nil
}
lines := strings.Split(strings.TrimSpace(output), "\n")
if len(lines) > 1 {
output = lines[len(lines)-1]
}
fields := strings.Fields(output)
if len(fields) >= 5 {
@@ -515,7 +521,7 @@ func removeFromFstab(mountPoint string) error {
}
func getFilesystemType(device string) (string, error) {
output, err := cmd.RunDefaultWithStdoutBashC(fmt.Sprintf("blkid -o value -s TYPE %s", device))
output, err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout("blkid", "-o", "value", "-s", "TYPE", device)
if err != nil {
return "", err
}
@@ -575,7 +581,7 @@ func parseSizeToBytes(sizeStr string) int64 {
}
func getDeviceUUID(device string) (string, error) {
output, err := cmd.RunDefaultWithStdoutBashC(fmt.Sprintf("blkid -s UUID -o value %s", device))
output, err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout("blkid", "-s", "UUID", "-o", "value", device)
if err != nil {
return "", err
}
+4 -3
View File
@@ -8,6 +8,7 @@ import (
"os"
"path"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/constant"
@@ -89,8 +90,8 @@ func (u *DockerService) LoadDockerConf() (*dto.DaemonJsonConf, error) {
data.Version = itemVersion.Version
}
data.IsSwarm = false
stdout2, _ := cmd.RunDefaultWithStdoutBashC("docker info | grep Swarm")
if string(stdout2) == " Swarm: active\n" {
stdout2, _ := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout("docker", "info")
if strings.Contains(stdout2, "Swarm: active") {
data.IsSwarm = true
}
if _, err := os.Stat(constant.DaemonJsonPath); err != nil {
@@ -423,7 +424,7 @@ func validateDockerConfig() error {
if !cmd.Which("dockerd") {
return nil
}
stdout, err := cmd.RunDefaultWithStdoutBashC("dockerd --validate")
stdout, err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout("dockerd", "--validate")
if strings.Contains(stdout, "unknown flag: --validate") {
return nil
}
+5 -5
View File
@@ -71,7 +71,7 @@ func (h *HostToolService) GetToolStatus(req request.HostToolReq) (*response.Host
supervisorConfig.ServiceName = serviceNameSet.Value
}
versionRes, _ := cmd.RunDefaultWithStdoutBashC("supervisord -v")
versionRes, _ := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout("supervisord", "-v")
supervisorConfig.Version = strings.TrimSuffix(versionRes, "\n")
_, ctlRrr := exec.LookPath("supervisorctl")
supervisorConfig.CtlExist = ctlRrr == nil
@@ -550,8 +550,8 @@ func operateSupervisorCtl(operate, name, group, includeDir, containerName string
err error
)
if containerName != "" {
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(30 * time.Second))
output, err = cmdMgr.RunWithStdoutBashCf("docker exec %s supervisorctl %s", containerName, strings.Join(processNames, " "))
args := append([]string{"supervisorctl"}, processNames...)
output, err = cmd.RunDockerExecWithStdout(30*time.Second, containerName, args...)
} else {
var out []byte
out, err = exec.Command("supervisorctl", processNames...).Output()
@@ -593,8 +593,8 @@ func getProcessStatus(config *response.SupervisorProcessConfig, containerName st
)
processNames = append(processNames, getProcessName(config.Name, config.Numprocs)...)
if containerName != "" {
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(3 * time.Second))
output, err = cmdMgr.RunWithStdoutBashCf("docker exec %s supervisorctl %s", containerName, strings.Join(processNames, " "))
args := append([]string{"supervisorctl"}, processNames...)
output, err = cmd.RunDockerExecWithStdout(3*time.Second, containerName, args...)
} else {
var out []byte
out, err = exec.Command("supervisorctl", processNames...).Output()
+6 -6
View File
@@ -359,10 +359,10 @@ func loadBindNumber(chain string) int {
}
func initPreRules() error {
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.IoRuleIn); err != nil {
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, "-i", "lo", "-j", "ACCEPT", "-m", "comment", "--comment", "Loopback Whitelist"); err != nil {
return err
}
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.EstablishedRule); err != nil {
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT", "-m", "comment", "--comment", "ESTABLISHED Whitelist"); err != nil {
return err
}
panelPort := LoadPanelPort()
@@ -371,17 +371,17 @@ func initPreRules() error {
}
ports := []string{"80", "443", panelPort, loadSSHPort()}
for _, item := range ports {
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, fmt.Sprintf("-p tcp -m tcp --dport %v -j ACCEPT", item)); err != nil {
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, "-p", "tcp", "-m", "tcp", "--dport", item, "-j", "ACCEPT"); err != nil {
return err
}
}
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, "-p udp -m udp --dport 443 -j ACCEPT"); err != nil {
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, "-p", "udp", "-m", "udp", "--dport", "443", "-j", "ACCEPT"); err != nil {
return err
}
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.DropAllTcp); err != nil {
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, "-p", "tcp", "-j", "DROP"); err != nil {
return err
}
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.DropAllUdp); err != nil {
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, "-p", "udp", "-j", "DROP"); err != nil {
return err
}
return nil
+1 -1
View File
@@ -241,7 +241,7 @@ func (n NginxService) Build(req request.NginxBuildReq) error {
}
buildTask.AddSubTaskWithOps("", func(t *task.Task) error {
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*buildTask), cmd.WithTimeout(120*time.Minute))
if err = cmdMgr.RunBashCf("docker compose -f %s build", nginxInstall.GetComposePath()); err != nil {
if err = cmdMgr.Run("docker", "compose", "-f", nginxInstall.GetComposePath(), "build"); err != nil {
return err
}
_, err = compose.DownAndUp(nginxInstall.GetComposePath())
+2 -8
View File
@@ -240,16 +240,10 @@ func getNginxParamsFromStaticFile(scope dto.NginxKey, newParams []dto.NginxParam
func opNginx(containerName, operate string) error {
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second))
cmdStr := fmt.Sprintf("docker exec -i %s nginx ", containerName)
if operate == constant.NginxCheck {
cmdStr = cmdStr + "-t"
} else {
cmdStr = cmdStr + "-s reload"
return cmdMgr.Run("docker", "exec", "-i", containerName, "nginx", "-t")
}
if err := cmdMgr.RunBashC(cmdStr); err != nil {
return err
}
return nil
return cmdMgr.Run("docker", "exec", "-i", containerName, "nginx", "-s", "reload")
}
func nginxCheckAndReload(oldContent string, filePath string, containerName string) error {
+5 -2
View File
@@ -630,11 +630,14 @@ func (r *RuntimeService) OperateNodeModules(req request.NodeModuleOperateReq) er
return err
}
operation := getOperation(req.Operate, req.PkgManager)
execScript := fmt.Sprintf("%s %s %s", req.PkgManager, operation, req.Module)
execArgs := []string{"exec", "-i", containerName, req.PkgManager, operation}
if strings.TrimSpace(req.Module) != "" {
execArgs = append(execArgs, req.Module)
}
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Minute)
defer cancel()
installCmd := exec.CommandContext(ctx, "docker", "exec", "-i", containerName, "bash", "-c", execScript)
installCmd := exec.CommandContext(ctx, "docker", execArgs...)
output, err := installCmd.CombinedOutput()
if err != nil {
return fmt.Errorf("failed to execute command: %s, error: %w", string(output), err)
+10 -3
View File
@@ -15,7 +15,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
fileUtils "github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/docker/docker/api/types/image"
@@ -303,12 +302,20 @@ func loadAppImage(list []dto.DataTree) []dto.DataTree {
if list[i].IsLocal {
appPath = path.Join(global.Dir.AppDir, "local", strings.TrimPrefix(list[i].Key, "local"), list[i].Name)
}
stdout, err := cmd.RunDefaultWithStdoutBashCf("cat %s | grep image: ", path.Join(appPath, "docker-compose.yml"))
composePath := path.Join(appPath, "docker-compose.yml")
content, err := os.ReadFile(composePath)
if err != nil {
list[i].Children = append(list[i].Children, itemAppImage)
continue
}
itemAppImage.Name = strings.ReplaceAll(strings.ReplaceAll(strings.TrimSpace(stdout), "\n", ""), "image: ", "")
for _, line := range strings.Split(string(content), "\n") {
trimmed := strings.TrimSpace(line)
if !strings.HasPrefix(trimmed, "image:") {
continue
}
itemAppImage.Name = strings.TrimSpace(strings.TrimPrefix(trimmed, "image:"))
break
}
for _, imageItem := range imageList {
for _, tag := range imageItem.RepoTags {
if tag == itemAppImage.Name {
+6 -1
View File
@@ -405,7 +405,12 @@ func snapAppImage(snap snapHelper, req dto.SnapshotCreate, targetDir string) err
if len(imageList) != 0 {
snap.Task.Log(strings.Join(imageList, " "))
snap.Task.Logf("docker save %s | gzip -c > %s", strings.Join(imageList, " "), path.Join(targetDir, "images.tar.gz"))
if err := cmd.RunDefaultBashCf("docker save %s | gzip -c > %s", strings.Join(imageList, " "), path.Join(targetDir, "images.tar.gz")); err != nil {
outputPath := path.Join(targetDir, "images.tar.gz")
cmdMgr := cmd.NewCommandMgr(cmd.WithOutputFile(outputPath))
if _, err := cmdMgr.RunPipe(
cmd.PipeCommand{Name: "docker", Args: append([]string{"save"}, imageList...)},
cmd.PipeCommand{Name: "gzip", Args: []string{"-c"}},
); err != nil {
snap.Task.LogFailedWithErr(i18n.GetMsgByKey("SnapDockerSave"), err)
return err
}
+11 -3
View File
@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path"
"strings"
"time"
@@ -376,7 +377,15 @@ func recoverAppData(src string, itemHelper *snapRecoverHelper) error {
itemHelper.Task.Log(i18n.GetMsgByKey("RecoverAppEmpty"))
return nil
}
if err := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute)).RunBashCf("docker load < %s", path.Join(src, "images.tar.gz")); err != nil {
imageFile, err := os.Open(path.Join(src, "images.tar.gz"))
if err != nil {
itemHelper.Task.LogFailedWithErr(i18n.GetMsgByKey("RecoverAppImage"), err)
return fmt.Errorf("docker load images failed, %v", err)
}
defer func() { _ = imageFile.Close() }()
loadCmd := exec.Command("docker", "load")
loadCmd.Stdin = imageFile
if err := loadCmd.Run(); err != nil {
itemHelper.Task.LogFailedWithErr(i18n.GetMsgByKey("RecoverAppImage"), err)
return fmt.Errorf("docker load images failed, %v", err)
}
@@ -457,8 +466,7 @@ func restartCompose(composePath string, itemHelper *snapRecoverHelper) error {
if _, err := os.Stat(pathItem); err != nil {
continue
}
upCmd := fmt.Sprintf("docker compose -f %s up -d", pathItem)
if err := cmd.RunDefaultBashC(upCmd); err != nil {
if err := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute)).Run("docker", "compose", "-f", pathItem, "up", "-d"); err != nil {
itemHelper.Task.LogFailedWithErr(i18n.GetMsgByKey("RecoverCompose"), err)
continue
}
+47 -27
View File
@@ -586,12 +586,12 @@ func (u *SSHService) LoadLog(ctx *gin.Context, req dto.SearchSSHLog) (int64, []d
}
fileList = sortFileList(fileList)
command := ""
filter := ""
if len(req.Info) != 0 {
if cmd.CheckIllegal(req.Info) {
return 0, data, buserr.New("ErrCmdIllegal")
}
command = fmt.Sprintf(" | grep '%s'", req.Info)
filter = req.Info
}
showCountFrom := (req.Page - 1) * req.PageSize
@@ -599,28 +599,7 @@ func (u *SSHService) LoadLog(ctx *gin.Context, req dto.SearchSSHLog) (int64, []d
nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
itemFailed, itemTotal := 0, 0
for _, file := range fileList {
commandItem := ""
if strings.HasPrefix(path.Base(file.Name), "secure") {
switch req.Status {
case constant.StatusSuccess:
commandItem = fmt.Sprintf("cat %s | grep -a Accepted %s", file.Name, command)
case constant.StatusFailed:
commandItem = fmt.Sprintf("cat %s | grep -a 'Failed password for' %s", file.Name, command)
default:
commandItem = fmt.Sprintf("cat %s | grep -aE '(Failed password for|Accepted)' %s", file.Name, command)
}
}
if strings.HasPrefix(path.Base(file.Name), "auth.log") {
switch req.Status {
case constant.StatusSuccess:
commandItem = fmt.Sprintf("cat %s | grep -a Accepted %s", file.Name, command)
case constant.StatusFailed:
commandItem = fmt.Sprintf("cat %s | grep -aE 'Failed password for|Connection closed by authenticating user' %s", file.Name, command)
default:
commandItem = fmt.Sprintf("cat %s | grep -aE \"(Failed password for|Connection closed by authenticating user|Accepted)\" %s", file.Name, command)
}
}
dataItem, successCount, failedCount := loadSSHData(ctx, commandItem, showCountFrom, showCountTo, file.Year, nyc)
dataItem, successCount, failedCount := loadSSHData(ctx, file.Name, path.Base(file.Name), req.Status, filter, showCountFrom, showCountTo, file.Year, nyc)
itemFailed += failedCount
itemTotal += successCount + failedCount
showCountFrom = showCountFrom - (successCount + failedCount)
@@ -1124,7 +1103,7 @@ func isSSHConfigPathAllowed(fileName string) bool {
return false
}
func loadSSHData(ctx *gin.Context, command string, showCountFrom, showCountTo, currentYear int, nyc *time.Location) ([]dto.SSHHistory, int, int) {
func loadSSHData(ctx *gin.Context, filePath, fileName, status, filter string, showCountFrom, showCountTo, currentYear int, nyc *time.Location) ([]dto.SSHHistory, int, int) {
var (
datas []dto.SSHHistory
successCount int
@@ -1134,12 +1113,15 @@ func loadSSHData(ctx *gin.Context, command string, showCountFrom, showCountTo, c
if err != nil {
return datas, 0, 0
}
stdout, err := cmd.RunDefaultWithStdoutBashC(command)
content, err := os.ReadFile(filePath)
if err != nil {
return datas, 0, 0
}
lines := strings.Split(stdout, "\n")
lines := strings.Split(string(content), "\n")
for i := len(lines) - 1; i >= 0; i-- {
if !matchSSHLogLine(fileName, status, filter, lines[i]) {
continue
}
var itemData dto.SSHHistory
switch {
case strings.Contains(lines[i], "Failed password for"):
@@ -1177,6 +1159,44 @@ func loadSSHData(ctx *gin.Context, command string, showCountFrom, showCountTo, c
return datas, successCount, failedCount
}
func matchSSHLogLine(fileName, status, filter, line string) bool {
if filter != "" && !strings.Contains(line, filter) {
return false
}
containsFailed := strings.Contains(line, "Failed password for")
containsClosed := strings.Contains(line, "Connection closed by authenticating user")
containsAccepted := strings.Contains(line, "Accepted ")
switch {
case strings.HasPrefix(fileName, "secure"):
switch status {
case constant.StatusSuccess:
return containsAccepted
case constant.StatusFailed:
return containsFailed
default:
return containsFailed || containsAccepted
}
case strings.HasPrefix(fileName, "auth.log"):
switch status {
case constant.StatusSuccess:
return containsAccepted
case constant.StatusFailed:
return containsFailed || containsClosed
default:
return containsFailed || containsClosed || containsAccepted
}
default:
switch status {
case constant.StatusSuccess:
return containsAccepted
case constant.StatusFailed:
return containsFailed || containsClosed
default:
return containsFailed || containsClosed || containsAccepted
}
}
}
func loadSuccessDatas(line string) dto.SSHHistory {
var data dto.SSHHistory
parts := strings.Fields(line)
+1 -3
View File
@@ -28,7 +28,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/ssl"
"github.com/go-acme/lego/v4/certcrypto"
@@ -378,8 +377,7 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) (*model.Website
workDir = websiteSSL.Dir
}
logger.Println(i18n.GetMsgByKey("ExecShellStart"))
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(30*time.Minute), cmd.WithLogger(logger), cmd.WithWorkDir(workDir))
if err = cmdMgr.RunBashC(websiteSSL.Shell); err != nil {
if err = runShellScriptFile(workDir, websiteSSL.Shell, logger); err != nil {
logger.Println(i18n.GetMsgWithMap("ErrExecShell", map[string]interface{}{"err": err.Error()}))
} else {
logger.Println(i18n.GetMsgByKey("ExecShellSuccess"))
+18 -2
View File
@@ -402,8 +402,7 @@ func (w WebsiteSSLService) obtainSSL(id uint, autoRenew bool) error {
workDir = websiteSSL.Dir
}
printSSLLog(logger, "ExecShellStart", nil)
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(30*time.Minute), cmd.WithLogger(logger), cmd.WithWorkDir(workDir))
if err = cmdMgr.RunBashC(websiteSSL.Shell); err != nil {
if err = runShellScriptFile(workDir, websiteSSL.Shell, logger); err != nil {
printSSLLog(logger, "ErrExecShell", map[string]interface{}{"err": err.Error()})
} else {
printSSLLog(logger, "ExecShellSuccess", nil)
@@ -447,6 +446,23 @@ func (w WebsiteSSLService) obtainSSL(id uint, autoRenew bool) error {
return nil
}
func runShellScriptFile(workDir, shell string, logger *log.Logger) error {
file, err := os.CreateTemp("", "1panel-shell-*.sh")
if err != nil {
return err
}
defer func() { _ = os.Remove(file.Name()) }()
if _, err := file.WriteString(shell); err != nil {
_ = file.Close()
return err
}
if err := file.Close(); err != nil {
return err
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(30*time.Minute), cmd.WithLogger(logger), cmd.WithWorkDir(workDir))
return cmdMgr.Run("bash", file.Name())
}
func handleError(websiteSSL *model.WebsiteSSL, err error) {
if websiteSSL.Status == constant.SSLInit || websiteSSL.Status == constant.SSLError {
websiteSSL.Status = constant.StatusError
+1 -1
View File
@@ -1221,7 +1221,7 @@ func checkIsLinkApp(website model.Website) bool {
func chownRootDir(path string) error {
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Second))
if err := cmdMgr.RunBashCf(`chown -R 1000:1000 "%s"`, path); err != nil {
if err := cmdMgr.Run("chown", "-R", "1000:1000", path); err != nil {
return err
}
return nil
+1 -1
View File
@@ -71,7 +71,7 @@ func Init() {
global.LOG.Errorf("find 1panel service port failed")
return
}
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, fmt.Sprintf("-p tcp -m tcp --dport %v -j ACCEPT", panelPort)); err != nil {
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, "-p", "tcp", "-m", "tcp", "--dport", panelPort, "-j", "ACCEPT"); err != nil {
global.LOG.Errorf("add port accept rule %v failed, err: %v", panelPort, err)
return
}
+2 -3
View File
@@ -2,6 +2,7 @@ package hook
import (
"os"
"os/exec"
"strings"
"github.com/1Panel-dev/1Panel/agent/app/dto"
@@ -11,7 +12,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/alert_push"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
)
@@ -140,11 +140,10 @@ func loadLocalDir() {
}
func initDockerConf() {
stdout, err := cmd.RunDefaultWithStdoutBashC("which docker")
dockerPath, err := exec.LookPath("docker")
if err != nil {
return
}
dockerPath := stdout
if strings.Contains(dockerPath, "snap") {
constant.DaemonJsonPath = "/var/snap/docker/current/config/daemon.json"
}
+7 -3
View File
@@ -62,7 +62,7 @@ func initLang() {
downloadLangFromRemote(fileOp)
return
}
if err := cmd.RunDefaultBashCf("cp -r %s %s", path.Join(tmpPath, "lang"), "/usr/local/bin/"); err != nil {
if err := cmd.NewCommandMgr().Run("cp", "-r", "--", path.Join(tmpPath, "lang"), "/usr/local/bin/"); err != nil {
global.LOG.Errorf("load lang from package failed, %v", err)
return
}
@@ -73,7 +73,11 @@ func initLang() {
downloadGeoFromRemote(fileOp, geoPath)
return
}
if err := cmd.RunDefaultBashCf("mkdir %s && cp %s %s/", path.Dir(geoPath), path.Join(tmpPath, "GeoIP.mmdb"), path.Dir(geoPath)); err != nil {
if err := os.MkdirAll(path.Dir(geoPath), os.ModePerm); err != nil {
global.LOG.Errorf("load geo ip from package failed, %v", err)
return
}
if err := cmd.NewCommandMgr().Run("cp", "--", path.Join(tmpPath, "GeoIP.mmdb"), path.Dir(geoPath)+"/"); err != nil {
global.LOG.Errorf("load geo ip from package failed, %v", err)
return
}
@@ -114,7 +118,7 @@ func downloadLangFromRemote(fileOp files.FileOp) {
global.LOG.Error("download lang.tar.gz failed, no such file")
return
}
if err := cmd.RunDefaultBashCf("tar zxvfC %s %s", "/usr/local/bin/lang.tar.gz", "/usr/local/bin/"); err != nil {
if err := cmd.NewCommandMgr().Run("tar", "zxvfC", "/usr/local/bin/lang.tar.gz", "/usr/local/bin/"); err != nil {
global.LOG.Errorf("decompress lang.tar.gz failed, %v", err)
return
}
+2 -1
View File
@@ -5,6 +5,7 @@ import (
"net"
"net/http"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/global"
@@ -29,7 +30,7 @@ func Certificate() gin.HandlerFunc {
return
}
masterProxyID := c.Request.Header.Get("Proxy-Id")
proxyID, err := cmd.RunDefaultWithStdoutBashC("cat /etc/1panel/.nodeProxyID")
proxyID, err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout("cat", "/etc/1panel/.nodeProxyID")
if err == nil && len(proxyID) != 0 && strings.TrimSpace(proxyID) != strings.TrimSpace(masterProxyID) {
helper.InternalServer(c, fmt.Errorf("err proxy id"))
return
+1 -1
View File
@@ -24,7 +24,7 @@ func New() (bool, NvidiaSMI) {
func (n NvidiaSMI) LoadGpuInfo() (*common.GpuInfo, error) {
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Second))
itemData, err := cmdMgr.RunWithStdoutBashC("nvidia-smi -q -x")
itemData, err := cmdMgr.RunWithStdout("nvidia-smi", "-q", "-x")
if err != nil {
return nil, fmt.Errorf("calling nvidia-smi failed, %v", err)
}
+7 -7
View File
@@ -34,12 +34,12 @@ func (x XpuSMI) loadDeviceData(device Device, wg *sync.WaitGroup, res *[]XPUSimp
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Second))
go func() {
defer wgCmd.Done()
xpuData, xpuErr = cmdMgr.RunWithStdoutBashCf("xpu-smi discovery -d %d -j", device.DeviceID)
xpuData, xpuErr = cmdMgr.RunWithStdout("xpu-smi", "discovery", "-d", strconv.Itoa(device.DeviceID), "-j")
}()
go func() {
defer wgCmd.Done()
statsData, statsErr = cmdMgr.RunWithStdoutBashCf("xpu-smi stats -d %d -j", device.DeviceID)
statsData, statsErr = cmdMgr.RunWithStdout("xpu-smi", "stats", "-d", strconv.Itoa(device.DeviceID), "-j")
}()
wgCmd.Wait()
@@ -93,7 +93,7 @@ func (x XpuSMI) loadDeviceData(device Device, wg *sync.WaitGroup, res *[]XPUSimp
func (x XpuSMI) LoadDashData() ([]XPUSimpleInfo, error) {
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Second))
data, err := cmdMgr.RunWithStdoutBashC("xpu-smi discovery -j")
data, err := cmdMgr.RunWithStdout("xpu-smi", "discovery", "-j")
if err != nil {
return nil, fmt.Errorf("calling xpu-smi failed, %v", err)
}
@@ -122,7 +122,7 @@ func (x XpuSMI) LoadDashData() ([]XPUSimpleInfo, error) {
func (x XpuSMI) LoadGpuInfo() (*XpuInfo, error) {
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Second))
data, err := cmdMgr.RunWithStdoutBashC("xpu-smi discovery -j")
data, err := cmdMgr.RunWithStdout("xpu-smi", "discovery", "-j")
if err != nil {
return nil, fmt.Errorf("calling xpu-smi failed, %v", err)
}
@@ -144,7 +144,7 @@ func (x XpuSMI) LoadGpuInfo() (*XpuInfo, error) {
wg.Wait()
processData, err := cmdMgr.RunWithStdoutBashC("xpu-smi ps -j")
processData, err := cmdMgr.RunWithStdout("xpu-smi", "ps", "-j")
if err != nil {
return nil, fmt.Errorf("calling xpu-smi ps failed, %s", err)
}
@@ -194,12 +194,12 @@ func (x XpuSMI) loadDeviceInfo(device Device, wg *sync.WaitGroup, res *XpuInfo,
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Second))
go func() {
defer wgCmd.Done()
xpuData, xpuErr = cmdMgr.RunWithStdoutBashCf("xpu-smi discovery -d %d -j", device.DeviceID)
xpuData, xpuErr = cmdMgr.RunWithStdout("xpu-smi", "discovery", "-d", strconv.Itoa(device.DeviceID), "-j")
}()
go func() {
defer wgCmd.Done()
statsData, statsErr = cmdMgr.RunWithStdoutBashCf("xpu-smi stats -d %d -j", device.DeviceID)
statsData, statsErr = cmdMgr.RunWithStdout("xpu-smi", "stats", "-d", strconv.Itoa(device.DeviceID), "-j")
}()
wgCmd.Wait()
+1 -1
View File
@@ -34,7 +34,7 @@ func AddScanTask(taskItem *task.Task, clam model.Clam, timeNow string) {
}
taskItem.Logf("clamdscan --fdpass %s %s", strategy, clam.Path)
mgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(time.Duration(clam.Timeout)*time.Second), cmd.WithTask(*taskItem))
if err := mgr.RunBashCf("clamdscan --fdpass %s %s", strategy, clam.Path); err != nil {
if err := mgr.Run("clamdscan", "--fdpass", strategy, clam.Path); err != nil {
return fmt.Errorf("clamdscan failed, %v", err)
}
return nil
+41 -19
View File
@@ -1,8 +1,17 @@
package cmd
import (
"bytes"
"io"
"os"
"os/exec"
"strings"
"sync"
)
var (
sudoHandleCmd string
sudoCheckOnce sync.Once
)
func CheckIllegal(args ...string) bool {
@@ -21,27 +30,40 @@ func CheckIllegal(args ...string) bool {
}
func SudoHandleCmd() string {
cmd := exec.Command("sudo", "-n", "ls")
if err := cmd.Run(); err == nil {
return "sudo "
sudoCheckOnce.Do(func() {
cmd := exec.Command("sudo", "-n", "ls")
if err := cmd.Run(); err == nil {
sudoHandleCmd = "sudo "
}
})
return sudoHandleCmd
}
func WrapWithOptionalSudo(name string, args ...string) (string, []string) {
if SudoHandleCmd() == "" {
return name, args
}
return ""
return "sudo", append([]string{"-n", name}, args...)
}
func ExecCommandWithOptionalSudo(name string, args ...string) *exec.Cmd {
commandName, commandArgs := WrapWithOptionalSudo(name, args...)
return exec.Command(commandName, commandArgs...)
}
func WriteFileWithOptionalSudo(name string, data []byte, perm os.FileMode) error {
if err := os.WriteFile(name, data, perm); err == nil {
return nil
} else if SudoHandleCmd() == "" {
return err
}
command := exec.Command("sudo", "-n", "tee", name)
command.Stdin = bytes.NewReader(data)
command.Stdout = io.Discard
return command.Run()
}
func Which(name string) bool {
// Prefer Go's built-in PATH lookup so we don't depend on an external
// `which` binary, which is not installed by default on minimal
// distributions (e.g. Arch Linux, some Alpine images, slim containers).
// See 1Panel-dev/1Panel#12605.
if _, err := exec.LookPath(name); err == nil {
return true
}
// Fall back to shelling out for environments where PATH inside the
// agent process differs from the user's interactive shell PATH (the
// previous behaviour, preserved for compatibility).
stdout, err := RunDefaultWithStdoutBashCf("which %s", name)
if err != nil || (len(strings.ReplaceAll(stdout, "\n", "")) == 0) {
return false
}
return true
_, err := exec.LookPath(name)
return err == nil
}
+286 -61
View File
@@ -5,10 +5,12 @@ import (
"context"
"errors"
"fmt"
"io"
"log"
"os"
"os/exec"
"strings"
"sync"
"syscall"
"time"
@@ -17,11 +19,14 @@ import (
"github.com/1Panel-dev/1Panel/agent/constant"
)
const maxStreamOutputCapture = 64 * 1024
type CommandHelper struct {
context context.Context
workDir string
outputFile string
scriptPath string
env []string
timeout time.Duration
taskItem *task.Task
logger *log.Logger
@@ -30,6 +35,46 @@ type CommandHelper struct {
type Option func(*CommandHelper)
type PipeCommand struct {
Name string
Args []string
Env []string
Dir string
Stdin io.Reader
}
type lockedBuffer struct {
mu sync.Mutex
buf bytes.Buffer
limit int
truncated int
}
func (b *lockedBuffer) Write(p []byte) (int, error) {
b.mu.Lock()
defer b.mu.Unlock()
if b.limit > 0 && b.buf.Len() >= b.limit {
b.truncated += len(p)
return len(p), nil
}
if b.limit > 0 && b.buf.Len()+len(p) > b.limit {
keep := b.limit - b.buf.Len()
_, _ = b.buf.Write(p[:keep])
b.truncated += len(p) - keep
return len(p), nil
}
return b.buf.Write(p)
}
func (b *lockedBuffer) String() string {
b.mu.Lock()
defer b.mu.Unlock()
if b.truncated == 0 {
return b.buf.String()
}
return fmt.Sprintf("%s\n... truncated %d bytes ...", b.buf.String(), b.truncated)
}
func NewCommandMgr(opts ...Option) *CommandHelper {
s := &CommandHelper{}
for _, opt := range opts {
@@ -38,82 +83,214 @@ func NewCommandMgr(opts ...Option) *CommandHelper {
return s
}
func RunDefaultBashC(command string) error {
mgr := NewCommandMgr()
return mgr.RunBashC(command)
func RunDockerExecWithStdout(timeout time.Duration, containerName string, args ...string) (string, error) {
commandArgs := append([]string{"exec", containerName}, args...)
return NewCommandMgr(WithTimeout(timeout)).RunWithStdout("docker", commandArgs...)
}
func RunDefaultBashCf(command string, arg ...interface{}) error {
mgr := NewCommandMgr()
return mgr.RunBashCf(command, arg...)
}
func RunDefaultWithStdoutBashC(command string) (string, error) {
mgr := NewCommandMgr(WithTimeout(20 * time.Second))
return mgr.RunWithStdoutBashC(command)
}
func RunDefaultWithStdoutBashCf(command string, arg ...interface{}) (string, error) {
mgr := NewCommandMgr(WithTimeout(20 * time.Second))
return mgr.RunWithStdoutBashCf(command, arg...)
}
func RunDefaultWithStdoutBashCfAndTimeOut(command string, timeout time.Duration, arg ...interface{}) (string, error) {
mgr := NewCommandMgr(WithTimeout(timeout))
return mgr.RunWithStdoutBashCf(command, arg...)
func RunDockerExec(timeout time.Duration, containerName string, args ...string) error {
commandArgs := append([]string{"exec", containerName}, args...)
return NewCommandMgr(WithTimeout(timeout)).Run("docker", commandArgs...)
}
func (c *CommandHelper) Run(name string, arg ...string) error {
_, err := c.run(name, arg...)
return err
}
func (c *CommandHelper) RunBashCWithArgs(arg ...string) error {
arg = append([]string{"-c"}, arg...)
_, err := c.run("bash", arg...)
return err
}
func (c *CommandHelper) RunBashC(command string) error {
if _, err := c.run("bash", "-c", command); err != nil {
return err
}
return nil
}
func (c *CommandHelper) RunBashCf(command string, arg ...interface{}) error {
if _, err := c.run("bash", "-c", fmt.Sprintf(command, arg...)); err != nil {
return err
}
return nil
func (c *CommandHelper) RunWithOptionalSudo(name string, arg ...string) error {
commandName, commandArgs := WrapWithOptionalSudo(name, arg...)
return c.Run(commandName, commandArgs...)
}
func (c *CommandHelper) RunWithStdout(name string, arg ...string) (string, error) {
return c.run(name, arg...)
}
func (c *CommandHelper) RunWithStdoutBashC(command string) (string, error) {
return c.run("bash", "-c", command)
func (c *CommandHelper) RunWithOptionalSudoAndStdout(name string, arg ...string) (string, error) {
commandName, commandArgs := WrapWithOptionalSudo(name, arg...)
return c.RunWithStdout(commandName, commandArgs...)
}
func (c *CommandHelper) RunWithStdoutBashCf(command string, arg ...interface{}) (string, error) {
return c.run("bash", "-c", fmt.Sprintf(command, arg...))
func (c *CommandHelper) RunPipe(commands ...PipeCommand) (string, error) {
if len(commands) == 0 {
return "", nil
}
ctx, cancel := c.pipeContext()
if cancel != nil {
defer cancel()
}
cmds := c.buildPipeCommands(ctx, commands)
customWriter := &CustomWriter{taskItem: c.taskItem}
var outputFile *os.File
limitOutputCapture := c.taskItem != nil || c.logger != nil || len(c.outputFile) != 0
stdout, stderr := &lockedBuffer{}, &lockedBuffer{}
if limitOutputCapture {
stdout.limit = maxStreamOutputCapture
stderr.limit = maxStreamOutputCapture
}
if commands[0].Stdin != nil {
cmds[0].Stdin = commands[0].Stdin
}
var pipeStderr io.Writer = stderr
var lastStdout io.Writer = stdout
var lastStderr io.Writer = stderr
var streamWriter io.Writer
var streamClosers []io.Closer
if c.taskItem != nil {
streamWriter = customWriter
} else if c.logger != nil {
streamWriter = c.logger.Writer()
if closer, ok := streamWriter.(io.Closer); ok {
streamClosers = append(streamClosers, closer)
}
} else if len(c.outputFile) != 0 {
file, err := os.OpenFile(c.outputFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, constant.FilePerm)
if err != nil {
return "", err
}
outputFile = file
lastStdout = outputFile
}
if streamWriter != nil {
pipeStderr = io.MultiWriter(stderr, streamWriter)
lastStdout = io.MultiWriter(stdout, streamWriter)
lastStderr = io.MultiWriter(stderr, streamWriter)
}
defer func() {
if c.taskItem != nil {
customWriter.Flush()
}
for _, closer := range streamClosers {
_ = closer.Close()
}
if outputFile != nil {
_ = outputFile.Close()
}
}()
if err := connectPipeCommands(cmds, lastStdout, lastStderr, pipeStderr); err != nil {
return "", err
}
if err := startPipeCommands(cmds); err != nil {
return handleErrString(stdout.String(), stderr.String(), c.IgnoreExist1, err)
}
runErr := waitPipeCommands(ctx, cmds)
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
return "", buserr.New("ErrCmdTimeout")
}
if errors.Is(ctx.Err(), context.Canceled) {
return "", buserr.New("ErrShutDown")
}
if runErr != nil {
return handleErrString(stdout.String(), stderr.String(), c.IgnoreExist1, runErr)
}
return stdout.String(), nil
}
func (c *CommandHelper) pipeContext() (context.Context, context.CancelFunc) {
ctx := c.context
if ctx == nil {
ctx = context.Background()
}
if c.timeout == 0 {
return ctx, nil
}
return context.WithTimeout(ctx, c.timeout)
}
func (c *CommandHelper) buildPipeCommands(ctx context.Context, commands []PipeCommand) []*exec.Cmd {
cmds := make([]*exec.Cmd, 0, len(commands))
for _, item := range commands {
cmdItem := exec.CommandContext(ctx, item.Name, item.Args...)
cmdItem.Env = append(os.Environ(), c.env...)
cmdItem.Env = append(cmdItem.Env, item.Env...)
cmdItem.Dir = c.workDir
if item.Dir != "" {
cmdItem.Dir = item.Dir
}
cmdItem.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
}
cmds = append(cmds, cmdItem)
}
return cmds
}
func connectPipeCommands(cmds []*exec.Cmd, stdout, stderr, pipeStderr io.Writer) error {
for i := 0; i < len(cmds)-1; i++ {
pipe, err := cmds[i].StdoutPipe()
if err != nil {
return err
}
cmds[i+1].Stdin = pipe
cmds[i].Stderr = pipeStderr
}
last := cmds[len(cmds)-1]
last.Stdout = stdout
last.Stderr = stderr
return nil
}
func startPipeCommands(cmds []*exec.Cmd) error {
for i := len(cmds) - 1; i >= 0; i-- {
if err := cmds[i].Start(); err != nil {
killStarted(cmds[i+1:])
return err
}
}
return nil
}
func waitPipeCommands(ctx context.Context, cmds []*exec.Cmd) error {
done := make(chan error, 1)
go func() {
var runErr error
for _, item := range cmds {
if err := item.Wait(); err != nil && runErr == nil {
runErr = err
}
}
done <- runErr
}()
select {
case runErr := <-done:
return runErr
case <-ctx.Done():
killProcessGroups(cmds)
return <-done
}
}
func (c *CommandHelper) run(name string, arg ...string) (string, error) {
var cmd *exec.Cmd
var newContext context.Context
var cancel context.CancelFunc
var outputFile *os.File
if c.timeout != 0 {
if c.context == nil {
newContext, cancel = context.WithTimeout(context.Background(), c.timeout)
defer cancel()
} else {
newContext, cancel = context.WithTimeout(c.context, c.timeout)
defer cancel()
}
defer cancel()
} else if c.context != nil {
newContext = c.context
}
if len(c.scriptPath) != 0 {
if newContext != nil {
cmd = exec.CommandContext(newContext, "bash", c.scriptPath)
} else {
cmd = exec.Command("bash", c.scriptPath)
}
} else if newContext != nil {
cmd = exec.CommandContext(newContext, name, arg...)
} else {
if c.context == nil {
newContext = context.Background()
cmd = exec.Command(name, arg...)
} else {
newContext = c.context
cmd = exec.CommandContext(c.context, name, arg...)
}
cmd = exec.Command(name, arg...)
}
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
@@ -121,36 +298,50 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
customWriter := &CustomWriter{taskItem: c.taskItem}
var stdout, stderr bytes.Buffer
var loggerClosers []io.Closer
if c.taskItem != nil {
cmd.Stdout = customWriter
cmd.Stderr = customWriter
} else if c.logger != nil {
cmd.Stdout = c.logger.Writer()
cmd.Stderr = c.logger.Writer()
stdoutWriter := c.logger.Writer()
stderrWriter := c.logger.Writer()
if closer, ok := stdoutWriter.(io.Closer); ok {
loggerClosers = append(loggerClosers, closer)
}
if closer, ok := stderrWriter.(io.Closer); ok {
loggerClosers = append(loggerClosers, closer)
}
cmd.Stdout = stdoutWriter
cmd.Stderr = stderrWriter
} else if len(c.outputFile) != 0 {
file, err := os.OpenFile(c.outputFile, os.O_WRONLY|os.O_CREATE, constant.FilePerm)
if err != nil {
return "", err
}
defer file.Close()
cmd.Stdout = file
cmd.Stderr = file
} else if len(c.scriptPath) != 0 {
cmd = exec.Command("bash", c.scriptPath)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
outputFile = file
cmd.Stdout = outputFile
cmd.Stderr = outputFile
} else {
cmd.Stdout = &stdout
cmd.Stderr = &stderr
}
env := os.Environ()
env = append(env, c.env...)
cmd.Env = env
if len(c.workDir) != 0 {
cmd.Dir = c.workDir
}
defer func() {
for _, closer := range loggerClosers {
_ = closer.Close()
}
if outputFile != nil {
_ = outputFile.Close()
}
}()
if err := cmd.Start(); err != nil {
return "", fmt.Errorf("cmd.Start() failed with '%s'\n", err)
return "", fmt.Errorf("cmd start failed: %w", err)
}
if c.taskItem != nil {
defer customWriter.Flush()
@@ -166,9 +357,10 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
return handleErr(stdout, stderr, c.IgnoreExist1, err)
}
return stdout.String(), nil
case <-newContext.Done():
case <-contextDone(newContext):
if cmd.Process != nil && cmd.Process.Pid > 0 {
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
killErr := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
_ = killErr
}
var err error
switch newContext.Err() {
@@ -184,6 +376,30 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
}
}
func contextDone(ctx context.Context) <-chan struct{} {
if ctx == nil {
return nil
}
return ctx.Done()
}
func killStarted(cmds []*exec.Cmd) {
killProcessGroups(cmds)
for _, item := range cmds {
if item.Process != nil {
_ = item.Wait()
}
}
}
func killProcessGroups(cmds []*exec.Cmd) {
for _, item := range cmds {
if item.Process != nil {
_ = syscall.Kill(-item.Process.Pid, syscall.SIGKILL)
}
}
}
func WithContext(ctx context.Context) Option {
return func(s *CommandHelper) {
s.context = ctx
@@ -219,6 +435,11 @@ func WithScriptPath(scriptPath string) Option {
s.scriptPath = scriptPath
}
}
func WithEnv(env ...string) Option {
return func(s *CommandHelper) {
s.env = append(s.env, env...)
}
}
func WithIgnoreExist1() Option {
return func(s *CommandHelper) {
s.IgnoreExist1 = true
@@ -250,6 +471,10 @@ func (cw *CustomWriter) Flush() {
}
func handleErr(stdout, stderr bytes.Buffer, ignoreExist1 bool, err error) (string, error) {
return handleErrString(stdout.String(), stderr.String(), ignoreExist1, err)
}
func handleErrString(stdout, stderr string, ignoreExist1 bool, err error) (string, error) {
var exitError *exec.ExitError
if ignoreExist1 && errors.As(err, &exitError) {
if status, ok := exitError.Sys().(syscall.WaitStatus); ok {
@@ -258,8 +483,8 @@ func handleErr(stdout, stderr bytes.Buffer, ignoreExist1 bool, err error) (strin
}
}
}
outItem := stdout.String()
errItem := stderr.String()
outItem := stdout
errItem := stderr
if len(errItem) != 0 && len(outItem) != 0 {
return outItem, fmt.Errorf("stdout: %s; stderr: %s, err: %v", outItem, errItem, err)
}
+14 -22
View File
@@ -18,6 +18,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/ctl_conf"
"github.com/1Panel-dev/1Panel/agent/utils/re"
"golang.org/x/net/idna"
)
@@ -289,18 +290,21 @@ func LoadTimeZoneByCmd() string {
if _, err := time.LoadLocation(loc); err != nil {
loc = "Asia/Shanghai"
}
std, err := cmd.RunDefaultWithStdoutBashC("timedatectl | grep 'Time zone'")
std, err := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second)).RunWithStdout("timedatectl")
if err != nil {
return loc
}
fields := strings.Fields(string(std))
if len(fields) != 5 {
return loc
for _, line := range strings.Split(std, "\n") {
fields := strings.Fields(line)
if len(fields) != 5 || fields[0] != "Time" || fields[1] != "zone:" {
continue
}
if _, err := time.LoadLocation(fields[2]); err != nil {
return loc
}
return fields[2]
}
if _, err := time.LoadLocation(fields[2]); err != nil {
return loc
}
return fields[2]
return loc
}
func IsValidDomain(domain string) bool {
@@ -442,20 +446,8 @@ func GetDockerComposeCommand() string {
}
func LoadParams(param string) string {
stdout, err := cmd.RunDefaultWithStdoutBashCf("grep '^%s=' /usr/local/bin/1pctl | cut -d'=' -f2", param)
if err != nil {
panic(err)
}
info := strings.ReplaceAll(stdout, "\n", "")
if len(info) == 0 || info == `""` {
panic(fmt.Sprintf("error `%s` find in /usr/local/bin/1pctl", param))
}
return info
return ctl_conf.Load(param)
}
func LoadParamsWithoutPanic(param string) string {
stdout, err := cmd.RunDefaultWithStdoutBashCf("grep '^%s=' /usr/local/bin/1pctl | cut -d'=' -f2", param)
if err != nil {
return ""
}
return strings.ReplaceAll(stdout, "\n", "")
return ctl_conf.LoadWithoutPanic(param)
}
+1 -2
View File
@@ -2,7 +2,6 @@ package manager
import (
"errors"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
@@ -19,5 +18,5 @@ func handlerErr(out string, err error) error {
}
func run(name string, args ...string) (string, error) {
return cmd.NewCommandMgr(cmd.WithTimeout(10*time.Second)).RunWithStdoutBashCf("LANGUAGE=en_US:en %s %s", name, strings.Join(args, " "))
return cmd.NewCommandMgr(cmd.WithTimeout(10*time.Second), cmd.WithEnv("LANGUAGE=en_US:en")).RunWithStdout(name, args...)
}
+28 -10
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
)
@@ -18,18 +19,11 @@ func (s *Openrc) Name() string {
return "openrc"
}
func (s *Openrc) IsActive(serviceName string) (bool, error) {
out, err := cmd.RunDefaultWithStdoutBashCf("if service %s status >/dev/null 2>&1; then echo 'active'; else echo 'inactive'; fi", serviceName)
if err != nil {
return false, err
}
return out == "active\n", nil
_, err := cmd.NewCommandMgr().RunWithStdout("service", serviceName, "status")
return err == nil, nil
}
func (s *Openrc) IsEnable(serviceName string) (bool, error) {
out, err := cmd.RunDefaultWithStdoutBashCf("if ls /etc/rc*.d/S*%s >/dev/null 2>&1; then echo 'enabled'; else echo 'disabled'; fi", serviceName)
if err != nil {
return false, err
}
return out == "enabled\n", nil
return isSysvServiceEnabled(serviceName)
}
func (s *Openrc) IsExist(serviceName string) (bool, error) {
_, err := os.Stat(filepath.Join("/etc/init.d", serviceName))
@@ -59,3 +53,27 @@ func (s *Openrc) Operate(operate, serviceName string) error {
func (s *Openrc) Reload() error {
return nil
}
func isSysvServiceEnabled(serviceName string) (bool, error) {
entries, err := os.ReadDir("/etc")
if err != nil {
return false, err
}
for _, entry := range entries {
name := entry.Name()
if !entry.IsDir() || !strings.HasPrefix(name, "rc") || !strings.HasSuffix(name, ".d") {
continue
}
items, err := os.ReadDir(filepath.Join("/etc", name))
if err != nil {
continue
}
for _, item := range items {
itemName := item.Name()
if strings.HasPrefix(itemName, "S") && strings.HasSuffix(itemName, serviceName) {
return true, nil
}
}
}
return false, nil
}
+3 -10
View File
@@ -18,18 +18,11 @@ func (s *Sysvinit) Name() string {
return "sysvinit"
}
func (s *Sysvinit) IsActive(serviceName string) (bool, error) {
out, err := cmd.RunDefaultWithStdoutBashCf("if service %s status >/dev/null 2>&1; then echo 'active'; else echo 'inactive'; fi", serviceName)
if err != nil {
return false, err
}
return out == "active\n", nil
_, err := cmd.NewCommandMgr().RunWithStdout("service", serviceName, "status")
return err == nil, nil
}
func (s *Sysvinit) IsEnable(serviceName string) (bool, error) {
out, err := cmd.RunDefaultWithStdoutBashCf("if ls /etc/rc*.d/S*%s >/dev/null 2>&1; then echo 'enabled'; else echo 'disabled'; fi", serviceName)
if err != nil {
return false, err
}
return out == "enabled\n", nil
return isSysvServiceEnabled(serviceName)
}
func (s *Sysvinit) IsExist(serviceName string) (bool, error) {
_, err := os.Stat(filepath.Join("/etc/init.d", serviceName))
+88
View File
@@ -0,0 +1,88 @@
package ctl_conf
import (
"fmt"
"os"
"strings"
)
const defaultFile = "/usr/local/bin/1pctl"
func Load(key string) string {
info, err := LoadFromFile(defaultFile, key)
if err != nil {
panic(err)
}
if len(info) == 0 || info == `""` {
panic(fmt.Sprintf("error `%s` find in %s", key, defaultFile))
}
return info
}
func LoadWithoutPanic(key string) string {
info, err := LoadFromFile(defaultFile, key)
if err != nil {
return ""
}
return info
}
func LoadFromFile(filePath, key string) (string, error) {
data, err := os.ReadFile(filePath)
if err != nil {
return "", err
}
prefix := key + "="
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, prefix) {
return strings.TrimSpace(strings.TrimPrefix(line, prefix)), nil
}
}
return "", fmt.Errorf("error `%s` find in %s", key, filePath)
}
func UpdateInFile(filePath, key, value string) error {
return rewriteFile(filePath, func(line string) (string, bool) {
if strings.HasPrefix(line, key+"=") {
return key + "=" + value, true
}
return line, true
})
}
func RemoveValueFromFile(filePath, key, value string) error {
target := key + "=" + value
return rewriteFile(filePath, func(line string) (string, bool) {
return line, line != target
})
}
func rewriteFile(filePath string, rewrite func(string) (string, bool)) error {
info, err := os.Stat(filePath)
if err != nil {
return err
}
data, err := os.ReadFile(filePath)
if err != nil {
return err
}
content := rewriteLines(string(data), rewrite)
return os.WriteFile(filePath, []byte(content), info.Mode().Perm())
}
func rewriteLines(data string, rewrite func(string) (string, bool)) string {
hasTrailingNewline := strings.HasSuffix(data, "\n")
lines := strings.Split(strings.TrimSuffix(data, "\n"), "\n")
rewritten := make([]string, 0, len(lines))
for _, line := range lines {
newLine, keep := rewrite(line)
if keep {
rewritten = append(rewritten, newLine)
}
}
content := strings.Join(rewritten, "\n")
if hasTrailingNewline {
content += "\n"
}
return content
}
+61 -73
View File
@@ -173,14 +173,23 @@ func (f FileOp) CleanDir(dst string) error {
if IsProtected(dst) {
return buserr.New("ErrPathNotDelete")
}
return cmd.RunDefaultBashCf("rm -rf %s/*", dst)
items, err := afero.ReadDir(f.Fs, dst)
if err != nil {
return err
}
for _, item := range items {
if err := f.Fs.RemoveAll(filepath.Join(dst, item.Name())); err != nil {
return err
}
}
return nil
}
func (f FileOp) RmRf(dst string) error {
if IsProtected(dst) {
return buserr.New("ErrPathNotDelete")
}
return cmd.RunDefaultBashCf("rm -rf %s", dst)
return f.Fs.RemoveAll(dst)
}
func (f FileOp) WriteFile(dst string, in io.Reader, mode fs.FileMode) error {
@@ -231,48 +240,48 @@ func (f FileOp) SaveFileWithByte(dst string, content []byte, mode fs.FileMode) e
}
func (f FileOp) ChownR(dst string, uid string, gid string, sub bool) error {
cmdStr := fmt.Sprintf(`%s chown %s:%s "%s"`, cmd.SudoHandleCmd(), uid, gid, dst)
args := []string{uid + ":" + gid, dst}
if sub {
cmdStr = fmt.Sprintf(`chown -R %s:%s "%s"`, uid, gid, dst)
args = append([]string{"-R", uid + ":" + gid}, dst)
}
timeout := cmdDefaultTimeout
if sub {
timeout = cmdRecursiveTimeout
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(timeout))
if err := cmdMgr.RunBashC(cmdStr); err != nil {
if err := cmdMgr.RunWithOptionalSudo("chown", args...); err != nil {
return err
}
return nil
}
func (f FileOp) ChmodR(dst string, mode int64, sub bool) error {
cmdStr := fmt.Sprintf(`%s chmod %v "%s"`, cmd.SudoHandleCmd(), fmt.Sprintf("%04o", mode), dst)
args := []string{fmt.Sprintf("%04o", mode), dst}
if sub {
cmdStr = fmt.Sprintf(`%s chmod -R %v "%s"`, cmd.SudoHandleCmd(), fmt.Sprintf("%04o", mode), dst)
args = append([]string{"-R", fmt.Sprintf("%04o", mode)}, dst)
}
timeout := cmdDefaultTimeout
if sub {
timeout = cmdRecursiveTimeout
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(timeout))
if err := cmdMgr.RunBashC(cmdStr); err != nil {
if err := cmdMgr.RunWithOptionalSudo("chmod", args...); err != nil {
return err
}
return nil
}
func (f FileOp) ChmodRWithMode(dst string, mode fs.FileMode, sub bool) error {
cmdStr := fmt.Sprintf(`%s chmod %v "%s"`, cmd.SudoHandleCmd(), fmt.Sprintf("%o", mode.Perm()), dst)
args := []string{fmt.Sprintf("%o", mode.Perm()), dst}
if sub {
cmdStr = fmt.Sprintf(`%s chmod -R %v "%s"`, cmd.SudoHandleCmd(), fmt.Sprintf("%o", mode.Perm()), dst)
args = append([]string{"-R", fmt.Sprintf("%o", mode.Perm())}, dst)
}
timeout := cmdDefaultTimeout
if sub {
timeout = cmdRecursiveTimeout
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(timeout))
if err := cmdMgr.RunBashC(cmdStr); err != nil {
if err := cmdMgr.RunWithOptionalSudo("chmod", args...); err != nil {
return err
}
return nil
@@ -285,23 +294,18 @@ func (f FileOp) ChownRPaths(paths []string, uid string, gid string, sub bool) er
if len(paths) == 1 {
return f.ChownR(paths[0], uid, gid, sub)
}
quoted := make([]string, len(paths))
for i, p := range paths {
quoted[i] = fmt.Sprintf(`"%s"`, p)
}
args := strings.Join(quoted, " ")
var cmdStr string
args := []string{uid + ":" + gid}
if sub {
cmdStr = fmt.Sprintf(`chown -R %s:%s %s`, uid, gid, args)
args = append([]string{"-R", uid + ":" + gid}, paths...)
} else {
cmdStr = fmt.Sprintf(`%s chown %s:%s %s`, cmd.SudoHandleCmd(), uid, gid, args)
args = append(args, paths...)
}
timeout := cmdDefaultTimeout
if sub {
timeout = cmdRecursiveTimeout
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(timeout))
if err := cmdMgr.RunBashC(cmdStr); err != nil {
if err := cmdMgr.RunWithOptionalSudo("chown", args...); err != nil {
return err
}
return nil
@@ -314,24 +318,19 @@ func (f FileOp) ChmodRPaths(paths []string, mode int64, sub bool) error {
if len(paths) == 1 {
return f.ChmodR(paths[0], mode, sub)
}
quoted := make([]string, len(paths))
for i, p := range paths {
quoted[i] = fmt.Sprintf(`"%s"`, p)
}
args := strings.Join(quoted, " ")
modeStr := fmt.Sprintf("%04o", mode)
var cmdStr string
args := []string{modeStr}
if sub {
cmdStr = fmt.Sprintf(`%s chmod -R %s %s`, cmd.SudoHandleCmd(), modeStr, args)
args = append([]string{"-R", modeStr}, paths...)
} else {
cmdStr = fmt.Sprintf(`%s chmod %s %s`, cmd.SudoHandleCmd(), modeStr, args)
args = append(args, paths...)
}
timeout := cmdDefaultTimeout
if sub {
timeout = cmdRecursiveTimeout
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(timeout))
if err := cmdMgr.RunBashC(cmdStr); err != nil {
if err := cmdMgr.RunWithOptionalSudo("chmod", args...); err != nil {
return err
}
return nil
@@ -543,19 +542,20 @@ func (f FileOp) Cut(oldPaths []string, dst, name string, cover bool) error {
dstPath = dst
coverFlag = "-f"
}
var quotedPaths []string
for _, p := range oldPaths {
quotedPaths = append(quotedPaths, fmt.Sprintf("'%s'", p))
args := []string{}
if coverFlag != "" {
args = append(args, coverFlag)
}
mvCommand := fmt.Sprintf("mv %s %s '%s'", coverFlag, strings.Join(quotedPaths, " "), dstPath)
if err := cmd.RunDefaultBashC(mvCommand); err != nil {
args = append(args, oldPaths...)
args = append(args, dstPath)
if err := cmd.NewCommandMgr().Run("mv", args...); err != nil {
return err
}
return nil
}
func (f FileOp) Mv(oldPath, dstPath string) error {
if err := cmd.RunDefaultBashCf(`mv '%s' '%s'`, oldPath, dstPath); err != nil {
if err := cmd.NewCommandMgr().Run("mv", oldPath, dstPath); err != nil {
return err
}
return nil
@@ -611,22 +611,22 @@ func (f FileOp) CopyAndReName(src, dst, name string, cover bool) error {
if name != "" && !cover {
dstPath = filepath.Join(dst, name)
}
return cmd.RunDefaultBashCf(`cp -rfp '%s' '%s'`, src, dstPath)
return cmd.NewCommandMgr().Run("cp", "-rfp", src, dstPath)
} else {
dstPath := filepath.Join(dst, name)
if cover {
dstPath = dst
}
return cmd.RunDefaultBashCf(`cp -fp '%s' '%s'`, src, dstPath)
return cmd.NewCommandMgr().Run("cp", "-fp", src, dstPath)
}
}
func (f FileOp) CopyDirWithNewName(src, dst, newName string) error {
if newName == "." || newName == "" {
return cmd.RunDefaultBashCf(`cp -rfp '%s'/. '%s'`, src, dst)
return cmd.NewCommandMgr().Run("cp", "-rfp", filepath.Clean(src)+"/.", dst)
}
dstDir := filepath.Join(dst, newName)
return cmd.RunDefaultBashCf(`cp -rfp '%s' '%s'`, src, dstDir)
return cmd.NewCommandMgr().Run("cp", "-rfp", src, dstDir)
}
func (f FileOp) CopyDir(src, dst string) error {
@@ -638,7 +638,7 @@ func (f FileOp) CopyDir(src, dst string) error {
if err = f.Fs.MkdirAll(dstDir, srcInfo.Mode()); err != nil {
return err
}
return cmd.NewCommandMgr(cmd.WithIgnoreExist1()).RunBashCf(`cp -rfp '%s' '%s'`, src, dst+"/")
return cmd.NewCommandMgr(cmd.WithIgnoreExist1()).Run("cp", "-rfp", src, dst+"/")
}
func (f FileOp) CopyDirWithExclude(src, dst string, excludeNames []string) error {
@@ -651,7 +651,7 @@ func (f FileOp) CopyDirWithExclude(src, dst string, excludeNames []string) error
return err
}
if len(excludeNames) == 0 {
return cmd.NewCommandMgr(cmd.WithIgnoreExist1()).RunBashCf(`cp -rfp '%s' '%s'`, src, dst+"/")
return cmd.NewCommandMgr(cmd.WithIgnoreExist1()).Run("cp", "-rfp", src, dst+"/")
}
tmpFiles, err := os.ReadDir(src)
if err != nil {
@@ -684,7 +684,7 @@ func (f FileOp) CopyDirWithExclude(src, dst string, excludeNames []string) error
func (f FileOp) CopyFile(src, dst string) error {
dst = filepath.Clean(dst) + string(filepath.Separator)
return cmd.NewCommandMgr(cmd.WithIgnoreExist1()).RunBashCf(`cp -fp '%s' '%s'`, src, dst+"/")
return cmd.NewCommandMgr(cmd.WithIgnoreExist1()).Run("cp", "-fp", src, dst+"/")
}
func (f FileOp) GetDirSize(path string) (int64, error) {
@@ -1263,10 +1263,8 @@ func (f FileOp) TarGzCompressPro(withDir bool, src, dst, secret, exclusionRules
workdir = path.Dir(src)
srcItem = path.Base(src)
}
commands := ""
exMap := make(map[string]struct{})
exStr := ""
excludeArgs := []string{}
excludes := strings.Split(exclusionRules, ",")
for _, exclude := range excludes {
if len(exclude) == 0 {
@@ -1278,19 +1276,18 @@ func (f FileOp) TarGzCompressPro(withDir bool, src, dst, secret, exclusionRules
if _, ok := exMap[exclude]; ok {
continue
}
exStr += fmt.Sprintf(" --exclude '%s'", exclude)
excludeArgs = append(excludeArgs, "--exclude", exclude)
exMap[exclude] = struct{}{}
}
tarArgs := append([]string{}, excludeArgs...)
if len(secret) != 0 {
commands = fmt.Sprintf("tar %s -zcf - %s | openssl enc -aes-256-cbc -salt -k '%s' -out %s", exStr, srcItem, secret, dst)
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" '%s' ", secret), " ****** "))
cmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(workdir), cmd.WithIgnoreExist1())
return runTarGzEncryptToFile(cmdMgr, dst, secret, append(tarArgs, srcItem)...)
} else {
commands = fmt.Sprintf("tar -zcf %s %s %s", dst, exStr, srcItem)
global.LOG.Debug(commands)
cmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(workdir), cmd.WithIgnoreExist1())
return runTarGzToFile(cmdMgr, dst, append(tarArgs, srcItem)...)
}
cmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(workdir), cmd.WithIgnoreExist1())
return cmdMgr.RunBashC(commands)
}
func (f FileOp) TarGzFilesWithCompressPro(list []string, dst, secret string) error {
@@ -1300,20 +1297,17 @@ func (f FileOp) TarGzFilesWithCompressPro(list []string, dst, secret string) err
}
}
var filelist []string
var tarArgs []string
for _, item := range list {
filelist = append(filelist, "-C '"+path.Dir(item)+"' '"+path.Base(item)+"' ")
tarArgs = append(tarArgs, "-C", path.Dir(item), path.Base(item))
}
commands := ""
if len(secret) != 0 {
commands = fmt.Sprintf("tar -zcf - %s | openssl enc -aes-256-cbc -salt -k '%s' -out %s", strings.Join(filelist, " "), secret, dst)
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" '%s' ", secret), " ****** "))
cmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1())
return runTarGzEncryptToFile(cmdMgr, dst, secret, tarArgs...)
} else {
commands = fmt.Sprintf("tar -zcf %s %s", dst, strings.Join(filelist, " "))
global.LOG.Debug(commands)
cmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1())
return runTarGzToFile(cmdMgr, dst, tarArgs...)
}
cmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1())
return cmdMgr.RunBashC(commands)
}
func (f FileOp) TarGzExtractPro(src, dst string, secret string) error {
@@ -1323,19 +1317,13 @@ func (f FileOp) TarGzExtractPro(src, dst string, secret string) error {
}
}
commands := ""
if len(secret) != 0 {
commands = fmt.Sprintf("openssl enc -d -aes-256-cbc -salt -k '%s' -in %s | tar -zxf - > /root/log", secret, src)
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" '%s' ", secret), " ****** "))
cmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(dst), cmd.WithIgnoreExist1())
return runTarGzDecryptToDir(cmdMgr, src, dst, secret, true)
} else {
commands = fmt.Sprintf("tar zxvf %s", src)
global.LOG.Debug(commands)
cmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(dst), cmd.WithIgnoreExist1())
return runTarGzExtractToDir(cmdMgr, src, dst)
}
cmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(dst), cmd.WithIgnoreExist1())
if len(secret) == 0 {
return cmdMgr.Run("tar", "zxvf", src)
}
return cmdMgr.RunBashC(commands)
}
func CopyCustomAppFile(srcPath, dstPath string) error {
if _, err := os.Stat(srcPath); os.IsNotExist(err) {
@@ -1375,7 +1363,7 @@ func CopyCustomAppFile(srcPath, dstPath string) error {
func OpensslEncrypt(filePath, secret string) error {
tmpName := path.Join(path.Dir(filePath), "tmp_"+path.Base(filePath))
if err := cmd.RunDefaultBashCf("MY_PASS='%s' openssl enc -aes-256-cbc -salt -pass env:MY_PASS -in %s -out %s", secret, filePath, tmpName); err != nil {
if err := cmd.NewCommandMgr(cmd.WithEnv("MY_PASS="+secret)).Run("openssl", "enc", "-aes-256-cbc", "-salt", "-pass", "env:MY_PASS", "-in", filePath, "-out", tmpName); err != nil {
_ = os.Remove(tmpName)
return err
}
@@ -1384,7 +1372,7 @@ func OpensslEncrypt(filePath, secret string) error {
func OpensslDecrypt(filePath, secret string) error {
tmpName := path.Join(path.Dir(filePath), "tmp_"+path.Base(filePath))
if err := cmd.RunDefaultBashCf("MY_PASS='%s' openssl enc -aes-256-cbc -d -salt -pass env:MY_PASS -in %s -out %s", secret, filePath, tmpName); err != nil {
if err := cmd.NewCommandMgr(cmd.WithEnv("MY_PASS="+secret)).Run("openssl", "enc", "-aes-256-cbc", "-d", "-salt", "-pass", "env:MY_PASS", "-in", filePath, "-out", tmpName); err != nil {
if strings.Contains(err.Error(), "bad decrypt") || strings.Contains(err.Error(), "bad magic number") {
return buserr.New("ErrBadDecrypt")
}
+1 -1
View File
@@ -23,7 +23,7 @@ func (z RarArchiver) Extract(ctx context.Context, filePath, dstDir string, _ str
if err := checkCmdAvailability("unrar"); err != nil {
return err
}
return cmd.NewCommandMgr(cmd.WithContext(ctx)).RunBashCf("unrar x -y -o+ %q %q", filePath, dstDir)
return cmd.NewCommandMgr().Run("unrar", "x", "-y", "-o+", filePath, dstDir)
}
func (z RarArchiver) Compress(ctx context.Context, sourcePaths []string, dstFile string, _ string) (err error) {
+1 -1
View File
@@ -19,7 +19,7 @@ func NewTarArchiver(compressType CompressType) ShellArchiver {
}
func (t TarArchiver) Extract(ctx context.Context, FilePath string, dstDir string, secret string) error {
return cmd.NewCommandMgr(cmd.WithContext(ctx)).RunBashCf("%s %s \"%s\" -C \"%s\"", t.Cmd, t.getOptionStr("extract"), FilePath, dstDir)
return cmd.NewCommandMgr(cmd.WithContext(ctx)).Run(t.Cmd, t.getOptionStr("extract"), FilePath, "-C", dstDir)
}
func (t TarArchiver) Compress(ctx context.Context, sourcePaths []string, dstFile string, secret string) error {
+36 -21
View File
@@ -6,7 +6,6 @@ import (
"os"
"path"
"path/filepath"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/global"
@@ -25,20 +24,11 @@ func (t TarGzArchiver) Extract(ctx context.Context, filePath, dstDir string, sec
if err := os.MkdirAll(dstDir, 0755); err != nil {
return fmt.Errorf("failed to create destination dir: %w", err)
}
var err error
commands := ""
if len(secret) != 0 {
extraCmd := fmt.Sprintf("openssl enc -d -aes-256-cbc -k '%s' -in '%s' | ", secret, filePath)
commands = fmt.Sprintf("%s tar -zxvf - -C '%s' > /dev/null 2>&1", extraCmd, dstDir)
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" %s ", secret), "******"))
return runTarGzDecryptToDir(cmd.NewCommandMgr(cmd.WithIgnoreExist1()), filePath, dstDir, secret, false)
} else {
commands = fmt.Sprintf("tar -zxvf '%s' -C '%s' > /dev/null 2>&1", filePath, dstDir)
global.LOG.Debug(commands)
return runTarGzExtractToDir(cmd.NewCommandMgr(cmd.WithIgnoreExist1()), filePath, dstDir)
}
if err = cmd.NewCommandMgr(cmd.WithContext(ctx)).RunBashC(commands); err != nil {
return err
}
return nil
}
func (t TarGzArchiver) Compress(ctx context.Context, sourcePaths []string, dstFile string, secret string) error {
@@ -54,23 +44,17 @@ func (t TarGzArchiver) Compress(ctx context.Context, sourcePaths []string, dstFi
var itemDirs []string
for _, item := range sourcePaths {
itemDirs = append(itemDirs, fmt.Sprintf("\"%s\"", filepath.Base(item)))
itemDirs = append(itemDirs, filepath.Base(item))
}
itemDir := strings.Join(itemDirs, " ")
aheadDir := filepath.Dir(sourcePaths[0])
if len(aheadDir) == 0 {
aheadDir = "/"
}
commands := ""
if len(secret) != 0 {
extraCmd := fmt.Sprintf("| openssl enc -aes-256-cbc -salt -k '%s' -out '%s'", secret, tmpFile)
commands = fmt.Sprintf("tar -zcf - -C \"%s\" %s %s", aheadDir, itemDir, extraCmd)
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" '%s' ", secret), " ****** "))
err = runTarGzEncryptToFile(cmd.NewCommandMgr(cmd.WithContext(ctx)), tmpFile, secret, append([]string{"-C", aheadDir}, itemDirs...)...)
} else {
commands = fmt.Sprintf("tar -zcf \"%s\" -C \"%s\" %s", tmpFile, aheadDir, itemDir)
global.LOG.Debug(commands)
err = runTarGzToFile(cmd.NewCommandMgr(cmd.WithContext(ctx)), tmpFile, append([]string{"-C", aheadDir}, itemDirs...)...)
}
err = cmd.NewCommandMgr(cmd.WithContext(ctx)).RunBashC(commands)
if err != nil {
return err
}
@@ -79,3 +63,34 @@ func (t TarGzArchiver) Compress(ctx context.Context, sourcePaths []string, dstFi
}
return nil
}
func runTarGzToFile(cmdMgr *cmd.CommandHelper, dst string, tarArgs ...string) error {
args := append([]string{"-zcf", dst}, tarArgs...)
return cmdMgr.Run("tar", args...)
}
func runTarGzEncryptToFile(cmdMgr *cmd.CommandHelper, dst, secret string, tarArgs ...string) error {
args := append([]string{"-zcf", "-"}, tarArgs...)
_, err := cmdMgr.RunPipe(
cmd.PipeCommand{Name: "tar", Args: args},
cmd.PipeCommand{Name: "openssl", Args: []string{"enc", "-aes-256-cbc", "-salt", "-pass", "env:BACKUP_SECRET", "-out", dst}, Env: []string{"BACKUP_SECRET=" + secret}},
)
return err
}
func runTarGzExtractToDir(cmdMgr *cmd.CommandHelper, src, dst string) error {
return cmdMgr.Run("tar", "-zxvf", src, "-C", dst)
}
func runTarGzDecryptToDir(cmdMgr *cmd.CommandHelper, src, dst, secret string, withSalt bool) error {
opensslArgs := []string{"enc", "-d", "-aes-256-cbc"}
if withSalt {
opensslArgs = append(opensslArgs, "-salt")
}
opensslArgs = append(opensslArgs, "-pass", "env:BACKUP_SECRET", "-in", src)
_, err := cmdMgr.RunPipe(
cmd.PipeCommand{Name: "openssl", Args: opensslArgs, Env: []string{"BACKUP_SECRET=" + secret}},
cmd.PipeCommand{Name: "tar", Args: []string{"-zxf", "-", "-C", dst}},
)
return err
}
+1 -1
View File
@@ -23,7 +23,7 @@ func (z X7zArchiver) Extract(ctx context.Context, filePath, dstDir string, _ str
if err := checkCmdAvailability("7z"); err != nil {
return err
}
return cmd.NewCommandMgr(cmd.WithContext(ctx)).RunBashCf("7z x -y -o%q %q", dstDir, filePath)
return cmd.NewCommandMgr(cmd.WithContext(ctx)).Run("7z", "x", "-y", "-o"+dstDir, filePath)
}
func (z X7zArchiver) Compress(ctx context.Context, sourcePaths []string, dstFile string, _ string) (err error) {
+1 -1
View File
@@ -24,7 +24,7 @@ func (z ZipArchiver) Extract(ctx context.Context, filePath, dstDir string, secre
if err := checkCmdAvailability("unzip"); err != nil {
return err
}
return cmd.NewCommandMgr(cmd.WithContext(ctx)).RunBashCf("unzip -qo %s -d %s", filePath, dstDir)
return cmd.NewCommandMgr(cmd.WithContext(ctx)).Run("unzip", "-qo", filePath, "-d", dstDir)
}
func (z ZipArchiver) Compress(ctx context.Context, sourcePaths []string, dstFile string, _ string) error {
+8 -15
View File
@@ -77,17 +77,16 @@ func UpdatePingStatus(enable string) error {
const panelSysctlPath = "/etc/sysctl.d/98-onepanel.conf"
var targetPath string
var applyCmd string
applyArgs := []string{"-p"}
if _, err := os.Stat(confPath); os.IsNotExist(err) {
targetPath = panelSysctlPath
applyCmd = fmt.Sprintf("%s sysctl --system", cmd.SudoHandleCmd())
if err := cmd.RunDefaultBashCf("%s mkdir -p /etc/sysctl.d", cmd.SudoHandleCmd()); err != nil {
applyArgs = []string{"--system"}
if err := cmd.NewCommandMgr().RunWithOptionalSudo("mkdir", "-p", "/etc/sysctl.d"); err != nil {
return fmt.Errorf("failed to create directory /etc/sysctl.d: %v", err)
}
} else {
targetPath = confPath
applyCmd = fmt.Sprintf("%s sysctl -p", cmd.SudoHandleCmd())
}
lineBytes, err := os.ReadFile(targetPath)
@@ -95,14 +94,14 @@ func UpdatePingStatus(enable string) error {
return fmt.Errorf("failed to read %s: %v", targetPath, err)
}
if err := cmd.RunDefaultBashCf("echo %s | %s tee /proc/sys/net/ipv4/icmp_echo_ignore_all > /dev/null", enable, cmd.SudoHandleCmd()); err != nil {
if err := cmd.WriteFileWithOptionalSudo("/proc/sys/net/ipv4/icmp_echo_ignore_all", []byte(enable), constant.FilePerm); err != nil {
return fmt.Errorf("failed to apply ipv4 ping status temporarily: %v", err)
}
var hasIpv6 bool
if _, err := os.Stat("/proc/sys/net/ipv6/icmp/echo_ignore_all"); err == nil {
hasIpv6 = true
if err := cmd.RunDefaultBashCf("echo %s | %s tee /proc/sys/net/ipv6/icmp/echo_ignore_all > /dev/null", enable, cmd.SudoHandleCmd()); err != nil {
if err := cmd.WriteFileWithOptionalSudo("/proc/sys/net/ipv6/icmp/echo_ignore_all", []byte(enable), constant.FilePerm); err != nil {
global.LOG.Warnf("failed to apply ipv6 ping status temporarily: %v", err)
}
}
@@ -136,18 +135,12 @@ func UpdatePingStatus(enable string) error {
newFiles = append(newFiles, "net.ipv6.icmp.echo_ignore_all="+enable)
}
file, err := os.OpenFile(targetPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, constant.FilePerm)
if err != nil {
return fmt.Errorf("failed to open %s: %v", targetPath, err)
}
defer file.Close()
if _, err = file.WriteString(strings.Join(newFiles, "\n")); err != nil {
if err = cmd.WriteFileWithOptionalSudo(targetPath, []byte(strings.Join(newFiles, "\n")), constant.FilePerm); err != nil {
return fmt.Errorf("failed to write to %s: %v", targetPath, err)
}
if err := cmd.RunDefaultBashC(applyCmd); err != nil {
global.LOG.Warnf("failed to apply persistent config with '%s': %v", applyCmd, err)
if err := cmd.NewCommandMgr().RunWithOptionalSudo("sysctl", applyArgs...); err != nil {
global.LOG.Warnf("failed to apply persistent config: %v", err)
}
return nil
+17 -16
View File
@@ -22,12 +22,12 @@ func (f *Firewall) Name() string {
}
func (f *Firewall) Status() (bool, error) {
stdout, _ := cmd.RunDefaultWithStdoutBashC("LANGUAGE=en_US:en firewall-cmd --state")
stdout, _ := cmd.NewCommandMgr(cmd.WithEnv("LANGUAGE=en_US:en")).RunWithStdout("firewall-cmd", "--state")
return stdout == "running\n", nil
}
func (f *Firewall) Version() (string, error) {
stdout, err := cmd.RunDefaultWithStdoutBashC("LANGUAGE=en_US:en firewall-cmd --version")
stdout, err := cmd.NewCommandMgr(cmd.WithEnv("LANGUAGE=en_US:en")).RunWithStdout("firewall-cmd", "--version")
if err != nil {
return "", fmt.Errorf("load the firewall version failed, %v", err)
}
@@ -56,7 +56,7 @@ func (f *Firewall) Restart() error {
}
func (f *Firewall) Reload() error {
if err := cmd.RunDefaultBashC("firewall-cmd --reload"); err != nil {
if err := cmd.NewCommandMgr().Run("firewall-cmd", "--reload"); err != nil {
return fmt.Errorf("reload firewall failed, err: %v", err)
}
return nil
@@ -68,7 +68,7 @@ func (f *Firewall) ListPort() ([]FireInfo, error) {
wg.Add(2)
go func() {
defer wg.Done()
stdout, err := cmd.RunDefaultWithStdoutBashC("firewall-cmd --zone=public --list-ports")
stdout, err := cmd.NewCommandMgr().RunWithStdout("firewall-cmd", "--zone=public", "--list-ports")
if err != nil {
return
}
@@ -89,7 +89,7 @@ func (f *Firewall) ListPort() ([]FireInfo, error) {
go func() {
defer wg.Done()
stdout1, err := cmd.RunDefaultWithStdoutBashC("firewall-cmd --zone=public --list-rich-rules")
stdout1, err := cmd.NewCommandMgr().RunWithStdout("firewall-cmd", "--zone=public", "--list-rich-rules")
if err != nil {
return
}
@@ -112,7 +112,7 @@ func (f *Firewall) ListForward() ([]FireInfo, error) {
if err := f.EnableForward(); err != nil {
global.LOG.Errorf("init port forward failed, err: %v", err)
}
stdout, err := cmd.RunDefaultWithStdoutBashC("firewall-cmd --zone=public --list-forward-ports")
stdout, err := cmd.NewCommandMgr().RunWithStdout("firewall-cmd", "--zone=public", "--list-forward-ports")
if err != nil {
return nil, err
}
@@ -137,7 +137,7 @@ func (f *Firewall) ListForward() ([]FireInfo, error) {
}
func (f *Firewall) ListAddress() ([]FireInfo, error) {
stdout, err := cmd.RunDefaultWithStdoutBashC("firewall-cmd --zone=public --list-rich-rules")
stdout, err := cmd.NewCommandMgr().RunWithStdout("firewall-cmd", "--zone=public", "--list-rich-rules")
if err != nil {
return nil, err
}
@@ -160,7 +160,7 @@ func (f *Firewall) Port(port FireInfo, operation string) error {
return buserr.New("ErrCmdIllegal")
}
if err := cmd.RunDefaultBashCf("firewall-cmd --zone=public --%s-port=%s/%s --permanent", operation, port.Port, port.Protocol); err != nil {
if err := cmd.NewCommandMgr().Run("firewall-cmd", "--zone=public", "--"+operation+"-port="+port.Port+"/"+port.Protocol, "--permanent"); err != nil {
return fmt.Errorf("%s (port: %s/%s strategy: %s) failed, %v", operation, port.Port, port.Protocol, port.Strategy, err)
}
return nil
@@ -184,12 +184,13 @@ func (f *Firewall) RichRules(rule FireInfo, operation string) error {
ruleStr += fmt.Sprintf("protocol=%s ", rule.Protocol)
}
ruleStr += rule.Strategy
if err := cmd.RunDefaultBashCf("firewall-cmd --zone=public --%s-rich-rule '%s' --permanent", operation, ruleStr); err != nil {
if err := cmd.NewCommandMgr().Run("firewall-cmd", "--zone=public", "--"+operation+"-rich-rule", ruleStr, "--permanent"); err != nil {
return fmt.Errorf("%s rich rules (%s) failed, %v", operation, ruleStr, err)
}
if len(rule.Address) == 0 {
if err := cmd.RunDefaultBashCf("firewall-cmd --zone=public --%s-rich-rule '%s' --permanent", operation, strings.ReplaceAll(ruleStr, "family=ipv4 ", "family=ipv6 ")); err != nil {
return fmt.Errorf("%s rich rules (%s) failed, %v", operation, strings.ReplaceAll(ruleStr, "family=ipv4 ", "family=ipv6 "), err)
ipv6Rule := strings.ReplaceAll(ruleStr, "family=ipv4 ", "family=ipv6 ")
if err := cmd.NewCommandMgr().Run("firewall-cmd", "--zone=public", "--"+operation+"-rich-rule", ipv6Rule, "--permanent"); err != nil {
return fmt.Errorf("%s rich rules (%s) failed, %v", operation, ipv6Rule, err)
}
}
return nil
@@ -199,12 +200,12 @@ func (f *Firewall) PortForward(info Forward, operation string) error {
if cmd.CheckIllegal(operation, info.Port, info.Protocol, info.TargetIP, info.TargetPort) {
return buserr.New("ErrCmdIllegal")
}
ruleStr := fmt.Sprintf("firewall-cmd --zone=public --%s-forward-port=port=%s:proto=%s:toport=%s --permanent", operation, info.Port, info.Protocol, info.TargetPort)
forwardRule := fmt.Sprintf("--%s-forward-port=port=%s:proto=%s:toport=%s", operation, info.Port, info.Protocol, info.TargetPort)
if info.TargetIP != "" && info.TargetIP != "127.0.0.1" && info.TargetIP != "localhost" {
ruleStr = fmt.Sprintf("firewall-cmd --zone=public --%s-forward-port=port=%s:proto=%s:toaddr=%s:toport=%s --permanent", operation, info.Port, info.Protocol, info.TargetIP, info.TargetPort)
forwardRule = fmt.Sprintf("--%s-forward-port=port=%s:proto=%s:toaddr=%s:toport=%s", operation, info.Port, info.Protocol, info.TargetIP, info.TargetPort)
}
if err := cmd.RunDefaultBashC(ruleStr); err != nil {
if err := cmd.NewCommandMgr().Run("firewall-cmd", "--zone=public", forwardRule, "--permanent"); err != nil {
return fmt.Errorf("%s port forward failed, %s", operation, err)
}
if err := f.Reload(); err != nil {
@@ -236,10 +237,10 @@ func (f *Firewall) loadInfo(line string) FireInfo {
}
func (f *Firewall) EnableForward() error {
stdout, err := cmd.RunDefaultWithStdoutBashC("firewall-cmd --zone=public --query-masquerade")
stdout, err := cmd.NewCommandMgr().RunWithStdout("firewall-cmd", "--zone=public", "--query-masquerade")
if err != nil {
if strings.HasSuffix(strings.TrimSpace(stdout), "no") {
if err := cmd.RunDefaultBashC("firewall-cmd --zone=public --add-masquerade --permanent"); err != nil {
if err := cmd.NewCommandMgr().Run("firewall-cmd", "--zone=public", "--add-masquerade", "--permanent"); err != nil {
return err
}
return f.Reload()
+23 -18
View File
@@ -2,8 +2,10 @@ package client
import (
"fmt"
"os"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/global"
@@ -22,11 +24,12 @@ func (i *Iptables) Name() string {
}
func (i *Iptables) Status() (bool, error) {
stdout, err := cmd.RunDefaultWithStdoutBashC("iptables -L -n | head -1")
stdout, err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout("iptables", "-L", "-n")
if err != nil {
return false, err
}
return strings.Contains(stdout, "Chain"), nil
firstLine := strings.Split(strings.TrimSpace(stdout), "\n")[0]
return strings.Contains(firstLine, "Chain"), nil
}
func (i *Iptables) Start() error {
@@ -46,7 +49,7 @@ func (i *Iptables) Reload() error {
}
func (i *Iptables) Version() (string, error) {
stdout, err := cmd.RunDefaultWithStdoutBashC("iptables --version")
stdout, err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout("iptables", "--version")
if err != nil {
return "", fmt.Errorf("failed to get iptables version: %w", err)
}
@@ -131,15 +134,13 @@ func (i *Iptables) Port(port FireInfo, operation string) error {
action = "DROP"
}
ruleArgs := []string{fmt.Sprintf("-p %s", protocol)}
ruleArgs = append(ruleArgs, fmt.Sprintf("--dport %s", portSpec), fmt.Sprintf("-j %s", action))
ruleSpec := strings.Join(ruleArgs, " ")
ruleArgs := []string{"-p", protocol, "--dport", portSpec, "-j", action}
if operation == "add" {
if err := iptables.AddRule(iptables.FilterTab, port.Chain, ruleSpec); err != nil {
if err := iptables.AddRule(iptables.FilterTab, port.Chain, ruleArgs...); err != nil {
return err
}
} else {
if err := iptables.DeleteRule(iptables.FilterTab, port.Chain, ruleSpec); err != nil {
if err := iptables.DeleteRule(iptables.FilterTab, port.Chain, ruleArgs...); err != nil {
return err
}
}
@@ -174,7 +175,7 @@ func (i *Iptables) RichRules(rule FireInfo, operation string) error {
var ruleArgs []string
if address != "" {
ruleArgs = append(ruleArgs, fmt.Sprintf("-s %s", address))
ruleArgs = append(ruleArgs, "-s", address)
}
protocol := strings.TrimSpace(rule.Protocol)
@@ -183,7 +184,7 @@ func (i *Iptables) RichRules(rule FireInfo, operation string) error {
}
if protocol != "" {
ruleArgs = append(ruleArgs, fmt.Sprintf("-p %s", protocol))
ruleArgs = append(ruleArgs, "-p", protocol)
}
if rule.Port != "" {
@@ -194,17 +195,16 @@ func (i *Iptables) RichRules(rule FireInfo, operation string) error {
if protocol == "" {
return fmt.Errorf("protocol is required when specifying a port")
}
ruleArgs = append(ruleArgs, fmt.Sprintf("--dport %s", portSegment))
ruleArgs = append(ruleArgs, "--dport", portSegment)
}
ruleArgs = append(ruleArgs, fmt.Sprintf("-j %s", action))
ruleSpec := strings.Join(ruleArgs, " ")
ruleArgs = append(ruleArgs, "-j", action)
if operation == "add" {
if err := iptables.AddRule(iptables.FilterTab, rule.Chain, ruleSpec); err != nil {
if err := iptables.AddRule(iptables.FilterTab, rule.Chain, ruleArgs...); err != nil {
return err
}
} else {
if err := iptables.DeleteRule(iptables.FilterTab, rule.Chain, ruleSpec); err != nil {
if err := iptables.DeleteRule(iptables.FilterTab, rule.Chain, ruleArgs...); err != nil {
return err
}
}
@@ -232,11 +232,16 @@ func (i *Iptables) ListForward() ([]FireInfo, error) {
}
func EnableIptablesForward() error {
if err := cmd.RunDefaultBashC("echo 1 > /proc/sys/net/ipv4/ip_forward"); err != nil {
if err := cmd.WriteFileWithOptionalSudo("/proc/sys/net/ipv4/ip_forward", []byte("1"), 0644); err != nil {
return fmt.Errorf("failed to enable IP forwarding: %w", err)
}
_ = cmd.RunDefaultBashC("grep -q '^net.ipv4.ip_forward' /etc/sysctl.conf || echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf")
_ = cmd.RunDefaultBashC("sysctl -p")
if data, err := os.ReadFile("/etc/sysctl.conf"); err == nil {
if !strings.Contains(string(data), "net.ipv4.ip_forward") {
content := strings.TrimRight(string(data), "\n") + "\nnet.ipv4.ip_forward = 1\n"
_ = cmd.WriteFileWithOptionalSudo("/etc/sysctl.conf", []byte(content), 0644)
}
}
_ = cmd.NewCommandMgr().RunWithOptionalSudo("sysctl", "-p")
if err := iptables.AddChainWithAppend(iptables.NatTab, "PREROUTING", iptables.Chain1PanelPreRouting); err != nil {
return err
+56 -32
View File
@@ -24,8 +24,8 @@ const (
)
const (
EstablishedRule = "-m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -m comment --comment 'ESTABLISHED Whitelist'"
IoRuleIn = "-i lo -j ACCEPT -m comment --comment 'Loopback Whitelist'"
EstablishedRule = "-m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -m comment --comment \"ESTABLISHED Whitelist\""
IoRuleIn = "-i lo -j ACCEPT -m comment --comment \"Loopback Whitelist\""
DropAllTcp = "-p tcp -j DROP"
DropAllUdp = "-p udp -j DROP"
AllowSSH = "-p tcp --dport ssh -j ACCEPT"
@@ -43,72 +43,91 @@ const (
NatTab = "nat"
)
func RunWithStd(tab, rule string) (string, error) {
cmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(60*time.Second))
stdout, err := cmdMgr.RunWithStdoutBashCf("%s iptables -w -t %s %s", cmd.SudoHandleCmd(), tab, rule)
func runIptables(tab string, ignoreExist1, withWait bool, ruleArgs ...string) (string, error) {
options := []cmd.Option{cmd.WithTimeout(60 * time.Second)}
if ignoreExist1 {
options = append(options, cmd.WithIgnoreExist1())
}
cmdMgr := cmd.NewCommandMgr(options...)
args := []string{"-t", tab}
if withWait {
args = append(args, "-w")
}
args = append(args, ruleArgs...)
return cmdMgr.RunWithOptionalSudoAndStdout("iptables", args...)
}
func RunWithStd(tab string, args ...string) (string, error) {
stdout, err := runIptables(tab, true, true, args...)
if err != nil {
global.LOG.Errorf("iptables command failed [table=%s, rule=%s]: %v", tab, rule, err)
global.LOG.Errorf("iptables command failed [table=%s, args=%s]: %v", tab, strings.Join(args, " "), err)
return stdout, err
}
return stdout, nil
}
func RunWithoutIgnore(tab, rule string) (string, error) {
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(60 * time.Second))
stdout, err := cmdMgr.RunWithStdoutBashCf("%s iptables -t %s %s", cmd.SudoHandleCmd(), tab, rule)
func RunWithoutIgnore(tab string, args ...string) (string, error) {
stdout, err := runIptables(tab, false, false, args...)
if err != nil {
return stdout, err
}
return stdout, nil
}
func Run(tab, rule string) error {
if _, err := RunWithStd(tab, rule); err != nil {
func Run(tab string, args ...string) error {
if _, err := RunWithStd(tab, args...); err != nil {
return err
}
return nil
}
func NewChain(tab, chain string) error {
return Run(tab, "-N "+chain)
return Run(tab, "-N", chain)
}
func ClearChain(tab, chain string) error {
return Run(tab, "-F "+chain)
return Run(tab, "-F", chain)
}
func AddRule(tab, chain, rule string) error {
if CheckRuleExist(tab, chain, rule) {
func AddRule(tab, chain string, ruleArgs ...string) error {
if CheckRuleExist(tab, chain, ruleArgs...) {
return nil
}
return Run(tab, fmt.Sprintf("-A %s %s", chain, rule))
args := append([]string{"-A", chain}, ruleArgs...)
return Run(tab, args...)
}
func DeleteRule(tab, chain, rule string) error {
return Run(tab, fmt.Sprintf("-D %s %s", chain, rule))
func DeleteRule(tab, chain string, ruleArgs ...string) error {
args := append([]string{"-D", chain}, ruleArgs...)
return Run(tab, args...)
}
func CheckChainExist(tab, chain string) (bool, error) {
stdout, err := RunWithStd(tab, fmt.Sprintf("-S | grep -w 'N %s'", chain))
stdout, err := RunWithStd(tab, "-S")
if err != nil {
global.LOG.Errorf("check chain %s from tab %s exist failed, err: %v", chain, tab, err)
return false, fmt.Errorf("check chain %s from tab %s exist failed, err: %v", chain, tab, err)
}
if strings.TrimSpace(stdout) == "" {
return false, nil
for _, line := range strings.Split(stdout, "\n") {
if strings.TrimSpace(line) == "-N "+chain {
return true, nil
}
}
return true, nil
return false, nil
}
func CheckChainBind(tab, parentChain, chain string) (bool, error) {
stdout, err := RunWithStd(tab, fmt.Sprintf("-S %s | grep -- '-j %s'", parentChain, chain))
stdout, err := RunWithStd(tab, "-S", parentChain)
if err != nil {
global.LOG.Errorf("check chain %s from tab %s is bind to %s failed, err: %v", chain, tab, parentChain, err)
return false, fmt.Errorf("check chain %s from tab %s is bind to %s failed, err: %v", chain, tab, parentChain, err)
}
if strings.TrimSpace(stdout) == "" {
return false, nil
for _, line := range strings.Split(stdout, "\n") {
if strings.Contains(line, "-j "+chain) {
return true, nil
}
}
return true, nil
return false, nil
}
func CheckRuleExist(tab, chain, rule string) bool {
_, err := RunWithoutIgnore(tab, fmt.Sprintf("-C %s %s", chain, rule))
func CheckRuleExist(tab, chain string, ruleArgs ...string) bool {
args := append([]string{"-C", chain}, ruleArgs...)
_, err := RunWithoutIgnore(tab, args...)
return err == nil
}
@@ -130,7 +149,7 @@ func BindChain(tab, targetChain, chain string, position int) error {
return fmt.Errorf("find chain %s number from %s failed, err: %w", chain, targetChain, err)
}
if line == 0 {
if err := Run(tab, fmt.Sprintf("-I %s %d -j %s", targetChain, position, chain)); err != nil {
if err := Run(tab, "-I", targetChain, strconv.Itoa(position), "-j", chain); err != nil {
return fmt.Errorf("bind chain %s to %s failed, err: %w", chain, targetChain, err)
}
}
@@ -142,13 +161,18 @@ func UnbindChain(tab, targetChain, chain string) error {
return fmt.Errorf("find chain %s number from %s failed, err: %w", chain, targetChain, err)
}
if line != 0 {
return Run(tab, fmt.Sprintf("-D %s %v", targetChain, line))
return Run(tab, "-D", targetChain, strconv.Itoa(line))
}
return nil
}
func FindChainNum(tab, targetChain, chain string) (int, error) {
stdout, err := RunWithStd(tab, fmt.Sprintf("-L %s --line-numbers -n | grep -w %s", targetChain, chain))
cmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(60*time.Second))
commandName, commandArgs := cmd.WrapWithOptionalSudo("iptables", "-w", "-t", tab, "-L", targetChain, "--line-numbers", "-n")
stdout, err := cmdMgr.RunPipe(
cmd.PipeCommand{Name: commandName, Args: commandArgs},
cmd.PipeCommand{Name: "grep", Args: []string{"-w", chain}},
)
if err != nil {
return 0, fmt.Errorf("failed to list rules in chain %s: %w", targetChain, err)
}
@@ -190,5 +214,5 @@ func AddChainWithAppend(tab, parentChain, chain string) error {
return nil
}
func AppendChain(tab string, parentChain, chain string) error {
return Run(tab, fmt.Sprintf("-A %s -j %s", parentChain, chain))
return Run(tab, "-A", parentChain, "-j", chain)
}
+21 -23
View File
@@ -2,8 +2,8 @@ package iptables
import (
"fmt"
"os"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/global"
@@ -26,47 +26,47 @@ func AddFilterRule(chain string, policy FilterRules) error {
if err := validateRuleSafety(policy, chain); err != nil {
return err
}
iptablesArg := fmt.Sprintf("-A %s", chain)
args := []string{"-A", chain}
if policy.Protocol != "" {
iptablesArg += fmt.Sprintf(" -p %s", policy.Protocol)
args = append(args, "-p", policy.Protocol)
}
if len(policy.SrcPort) != 0 {
iptablesArg += fmt.Sprintf(" --sport %s", policy.SrcPort)
args = append(args, "--sport", policy.SrcPort)
}
if len(policy.DstPort) != 0 {
iptablesArg += fmt.Sprintf(" --dport %s", policy.DstPort)
args = append(args, "--dport", policy.DstPort)
}
if policy.SrcIP != "" {
iptablesArg += fmt.Sprintf(" -s %s", policy.SrcIP)
args = append(args, "-s", policy.SrcIP)
}
if policy.DstIP != "" {
iptablesArg += fmt.Sprintf(" -d %s", policy.DstIP)
args = append(args, "-d", policy.DstIP)
}
iptablesArg += fmt.Sprintf(" -j %s", policy.Strategy)
args = append(args, "-j", policy.Strategy)
return Run(FilterTab, iptablesArg)
return Run(FilterTab, args...)
}
func DeleteFilterRule(chain string, policy FilterRules) error {
iptablesArg := fmt.Sprintf("-D %s", chain)
args := []string{"-D", chain}
if policy.Protocol != "" {
iptablesArg += fmt.Sprintf(" -p %s", policy.Protocol)
args = append(args, "-p", policy.Protocol)
}
if len(policy.SrcPort) != 0 {
iptablesArg += fmt.Sprintf(" --sport %s", policy.SrcPort)
args = append(args, "--sport", policy.SrcPort)
}
if len(policy.DstPort) != 0 {
iptablesArg += fmt.Sprintf(" --dport %s", policy.DstPort)
args = append(args, "--dport", policy.DstPort)
}
if policy.SrcIP != "" {
iptablesArg += fmt.Sprintf(" -s %s", policy.SrcIP)
args = append(args, "-s", policy.SrcIP)
}
if policy.DstIP != "" {
iptablesArg += fmt.Sprintf(" -d %s", policy.DstIP)
args = append(args, "-d", policy.DstIP)
}
iptablesArg += fmt.Sprintf(" -j %s", policy.Strategy)
args = append(args, "-j", policy.Strategy)
return Run(FilterTab, iptablesArg)
return Run(FilterTab, args...)
}
func ReadFilterRulesByChain(chain string) ([]FilterRules, error) {
@@ -74,8 +74,7 @@ func ReadFilterRulesByChain(chain string) ([]FilterRules, error) {
if cmd.CheckIllegal(chain) {
return rules, buserr.New("ErrCmdIllegal")
}
cmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(20*time.Second))
stdout, err := cmdMgr.RunWithStdoutBashCf("%s iptables -w -t %s -nL %s", cmd.SudoHandleCmd(), FilterTab, chain)
stdout, err := RunWithStd(FilterTab, "-nL", chain)
if err != nil {
return rules, fmt.Errorf("load filter fules by chain %s failed, %v", chain, err)
}
@@ -107,8 +106,7 @@ func LoadDefaultStrategy(chain string) (string, error) {
if cmd.CheckIllegal(chain) {
return "", buserr.New("ErrCmdIllegal")
}
cmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(20*time.Second))
stdout, err := cmdMgr.RunWithStdoutBashCf("%s iptables -w -t %s -L %s", cmd.SudoHandleCmd(), FilterTab, chain)
stdout, err := RunWithStd(FilterTab, "-L", chain)
if err != nil {
return "", fmt.Errorf("load filter fules by chain %s failed, %v", chain, err)
}
@@ -170,12 +168,12 @@ func LoadInitStatus(clientName, tab string) (bool, bool) {
}
return checkWithInitAndBind(initRules, bindRules, lines)
case "forward":
stdout, err := cmd.RunDefaultWithStdoutBashC("cat /proc/sys/net/ipv4/ip_forward")
data, err := os.ReadFile("/proc/sys/net/ipv4/ip_forward")
if err != nil {
global.LOG.Errorf("check /proc/sys/net/ipv4/ip_forward failed, err: %v", err)
return false, false
}
if strings.TrimSpace(stdout) == "0" {
if strings.TrimSpace(string(data)) == "0" {
return false, false
}
natRules, err := RunWithStd(NatTab, "-S")
+16 -17
View File
@@ -1,7 +1,6 @@
package iptables
import (
"fmt"
"strings"
)
@@ -9,33 +8,33 @@ func AddForward(protocol, srcPort, dest, destPort, iface string, save bool) erro
srcPort = strings.ReplaceAll(srcPort, "-", ":")
itemDstPort := strings.ReplaceAll(destPort, "-", ":")
if dest != "" && dest != "127.0.0.1" && dest != "localhost" {
iptablesArg := fmt.Sprintf("-A %s", Chain1PanelPreRouting)
args := []string{"-A", Chain1PanelPreRouting}
if iface != "" {
iptablesArg += fmt.Sprintf(" -i %s", iface)
args = append(args, "-i", iface)
}
iptablesArg += fmt.Sprintf(" -p %s --dport %s -j DNAT --to-destination %s:%s", protocol, srcPort, dest, destPort)
if err := Run(NatTab, iptablesArg); err != nil {
args = append(args, "-p", protocol, "--dport", srcPort, "-j", "DNAT", "--to-destination", dest+":"+destPort)
if err := Run(NatTab, args...); err != nil {
return err
}
if err := Run(NatTab, fmt.Sprintf("-A %s -d %s -p %s --dport %s -j MASQUERADE", Chain1PanelPostRouting, dest, protocol, itemDstPort)); err != nil {
if err := Run(NatTab, "-A", Chain1PanelPostRouting, "-d", dest, "-p", protocol, "--dport", itemDstPort, "-j", "MASQUERADE"); err != nil {
return err
}
if err := Run(FilterTab, fmt.Sprintf("-A %s -d %s -p %s --dport %s -j ACCEPT", Chain1PanelForward, dest, protocol, itemDstPort)); err != nil {
if err := Run(FilterTab, "-A", Chain1PanelForward, "-d", dest, "-p", protocol, "--dport", itemDstPort, "-j", "ACCEPT"); err != nil {
return err
}
if err := Run(FilterTab, fmt.Sprintf("-A %s -s %s -p %s --sport %s -j ACCEPT", Chain1PanelForward, dest, protocol, itemDstPort)); err != nil {
if err := Run(FilterTab, "-A", Chain1PanelForward, "-s", dest, "-p", protocol, "--sport", itemDstPort, "-j", "ACCEPT"); err != nil {
return err
}
} else {
iptablesArg := fmt.Sprintf("-A %s", Chain1PanelPreRouting)
args := []string{"-A", Chain1PanelPreRouting}
if iface != "" {
iptablesArg += fmt.Sprintf(" -i %s", iface)
args = append(args, "-i", iface)
}
iptablesArg += fmt.Sprintf(" -p %s --dport %s -j REDIRECT --to-port %s", protocol, srcPort, destPort)
if err := Run(NatTab, iptablesArg); err != nil {
args = append(args, "-p", protocol, "--dport", srcPort, "-j", "REDIRECT", "--to-port", destPort)
if err := Run(NatTab, args...); err != nil {
return err
}
}
@@ -44,20 +43,20 @@ func AddForward(protocol, srcPort, dest, destPort, iface string, save bool) erro
func DeleteForward(num string, protocol, srcPort, dest, destPort, iface string) error {
itemDstPort := strings.ReplaceAll(destPort, "-", ":")
if err := Run(NatTab, fmt.Sprintf("-D %s %s", Chain1PanelPreRouting, num)); err != nil {
if err := Run(NatTab, "-D", Chain1PanelPreRouting, num); err != nil {
return err
}
if dest != "" && dest != "127.0.0.1" && dest != "localhost" {
if err := Run(NatTab, fmt.Sprintf("-D %s -d %s -p %s --dport %s -j MASQUERADE", Chain1PanelPostRouting, dest, protocol, itemDstPort)); err != nil {
if err := Run(NatTab, "-D", Chain1PanelPostRouting, "-d", dest, "-p", protocol, "--dport", itemDstPort, "-j", "MASQUERADE"); err != nil {
return err
}
if err := Run(FilterTab, fmt.Sprintf("-D %s -d %s -p %s --dport %s -j ACCEPT", Chain1PanelForward, dest, protocol, itemDstPort)); err != nil {
if err := Run(FilterTab, "-D", Chain1PanelForward, "-d", dest, "-p", protocol, "--dport", itemDstPort, "-j", "ACCEPT"); err != nil {
return err
}
if err := Run(FilterTab, fmt.Sprintf("-D %s -s %s -p %s --sport %s -j ACCEPT", Chain1PanelForward, dest, protocol, itemDstPort)); err != nil {
if err := Run(FilterTab, "-D", Chain1PanelForward, "-s", dest, "-p", protocol, "--sport", itemDstPort, "-j", "ACCEPT"); err != nil {
return err
}
}
@@ -68,7 +67,7 @@ func ListForward(chain ...string) ([]IptablesNatInfo, error) {
if len(chain) == 0 {
chain = append(chain, Chain1PanelPreRouting)
}
stdout, err := RunWithStd(NatTab, fmt.Sprintf("-nvL %s --line-numbers", chain[0]))
stdout, err := RunWithStd(NatTab, "-nvL", chain[0], "--line-numbers")
if err != nil {
return nil, err
}
@@ -2,12 +2,14 @@ package iptables
import (
"bufio"
"bytes"
"fmt"
"os"
"path"
"strings"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
)
const (
@@ -24,7 +26,7 @@ const (
func SaveRulesToFile(tab, chain, fileName string) error {
rulesFile := path.Join(global.Dir.FirewallDir, fileName)
stdout, err := RunWithStd(tab, fmt.Sprintf("-S %s", chain))
stdout, err := RunWithStd(tab, "-S", chain)
if err != nil {
return fmt.Errorf("failed to list %s rules: %w", chain, err)
}
@@ -80,7 +82,7 @@ func LoadRulesFromFile(tab, chain, fileName string) error {
for _, rule := range rules {
if strings.HasPrefix(rule, fmt.Sprintf("-A %s", chain)) {
if err := Run(tab, rule); err != nil {
if err := restoreRule(tab, rule); err != nil {
global.LOG.Errorf("apply rule '%s' failed, err: %v", rule, err)
}
}
@@ -88,3 +90,14 @@ func LoadRulesFromFile(tab, chain, fileName string) error {
return nil
}
func restoreRule(tab, rule string) error {
restoreInput := fmt.Sprintf("*%s\n%s\nCOMMIT\n", tab, rule)
commandName, commandArgs := cmd.WrapWithOptionalSudo("iptables-restore", "-n")
_, err := cmd.NewCommandMgr().RunPipe(cmd.PipeCommand{
Name: commandName,
Args: commandArgs,
Stdin: bytes.NewReader([]byte(restoreInput)),
})
return err
}
+41 -26
View File
@@ -2,6 +2,7 @@ package client
import (
"fmt"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/agent/buserr"
@@ -24,35 +25,34 @@ func (f *Ufw) Name() string {
}
func (f *Ufw) Status() (bool, error) {
stdout, _ := cmd.RunDefaultWithStdoutBashCf("%s status | grep Status", f.CmdStr)
if stdout == "Status: active\n" {
stdout, _ := f.runWithStdout("status")
if strings.Contains(stdout, "Status: active") {
return true, nil
}
stdout1, _ := cmd.RunDefaultWithStdoutBashCf("%s status | grep 状态", f.CmdStr)
if stdout1 == "状态: 激活\n" {
if strings.Contains(stdout, "状态: 激活") {
return true, nil
}
return false, nil
}
func (f *Ufw) Version() (string, error) {
stdout, err := cmd.RunDefaultWithStdoutBashCf("%s version | grep ufw", f.CmdStr)
stdout, err := f.runWithStdout("version")
if err != nil {
return "", fmt.Errorf("load the firewall status failed, %v", err)
}
info := strings.ReplaceAll(stdout, "\n", "")
info := strings.Split(strings.TrimSpace(stdout), "\n")[0]
return strings.ReplaceAll(info, "ufw ", ""), nil
}
func (f *Ufw) Start() error {
if err := cmd.RunDefaultBashCf("echo y | %s enable", f.CmdStr); err != nil {
if err := f.run("--force", "enable"); err != nil {
return fmt.Errorf("enable the firewall failed, %v", err)
}
return nil
}
func (f *Ufw) Stop() error {
if err := cmd.RunDefaultBashCf("%s disable", f.CmdStr); err != nil {
if err := f.run("disable"); err != nil {
return fmt.Errorf("stop the firewall failed, %v", err)
}
return nil
@@ -73,7 +73,7 @@ func (f *Ufw) Reload() error {
}
func (f *Ufw) ListPort() ([]FireInfo, error) {
stdout, err := cmd.RunDefaultWithStdoutBashCf("%s status verbose", f.CmdStr)
stdout, err := f.runWithStdout("status", "verbose")
if err != nil {
return nil, err
}
@@ -98,7 +98,7 @@ func (f *Ufw) ListPort() ([]FireInfo, error) {
}
func (f *Ufw) ListAddress() ([]FireInfo, error) {
stdout, err := cmd.RunDefaultWithStdoutBashCf("%s status verbose", f.CmdStr)
stdout, err := f.runWithStdout("status", "verbose")
if err != nil {
return nil, err
}
@@ -141,15 +141,15 @@ func (f *Ufw) Port(port FireInfo, operation string) error {
return buserr.New("ErrCmdIllegal")
}
command := fmt.Sprintf("%s %s %s", f.CmdStr, port.Strategy, port.Port)
args := []string{port.Strategy, port.Port}
if operation == "remove" {
command = fmt.Sprintf("%s delete %s %s", f.CmdStr, port.Strategy, port.Port)
args = []string{"delete", port.Strategy, port.Port}
}
if len(port.Protocol) != 0 {
command += fmt.Sprintf("/%s", port.Protocol)
args[len(args)-1] += "/" + port.Protocol
}
if err := cmd.RunDefaultBashC(command); err != nil {
return fmt.Errorf("%s (%s) failed, %v", operation, command, err)
if err := f.run(args...); err != nil {
return fmt.Errorf("%s (%s) failed, %v", operation, strings.Join(args, " "), err)
}
return nil
}
@@ -169,31 +169,36 @@ func (f *Ufw) RichRules(rule FireInfo, operation string) error {
}
insertNum := f.loadInsertNum(rule, operation)
ruleStr := fmt.Sprintf("%s insert %d %s ", f.CmdStr, insertNum, rule.Strategy)
args := []string{"insert", strconv.Itoa(insertNum), rule.Strategy}
if operation == "remove" {
ruleStr = fmt.Sprintf("%s delete %s ", f.CmdStr, rule.Strategy)
args = []string{"delete", rule.Strategy}
}
if len(rule.Protocol) != 0 {
ruleStr += fmt.Sprintf("proto %s ", rule.Protocol)
args = append(args, "proto", rule.Protocol)
}
if strings.Contains(rule.Address, "-") {
ruleStr += fmt.Sprintf("from %s to %s ", strings.Split(rule.Address, "-")[0], strings.Split(rule.Address, "-")[1])
parts := strings.Split(rule.Address, "-")
args = append(args, "from", parts[0], "to", parts[1])
} else {
ruleStr += fmt.Sprintf("from %s ", rule.Address)
args = append(args, "from", rule.Address)
}
if len(rule.Port) != 0 {
ruleStr += fmt.Sprintf("to any port %s ", rule.Port)
args = append(args, "to", "any", "port", rule.Port)
}
stdout, err := cmd.RunDefaultWithStdoutBashC(ruleStr)
stdout, err := f.runWithStdout(args...)
if err != nil {
if strings.Contains(stdout, "ERROR: Invalid position") || strings.Contains(stdout, "ERROR: 无效位置") {
if err := cmd.RunDefaultBashC(strings.ReplaceAll(ruleStr, "insert 1 ", "")); err != nil {
return fmt.Errorf("%s rich rules (%s), failed, %v", operation, ruleStr, err)
fallbackArgs := args
if len(args) >= 2 && args[0] == "insert" {
fallbackArgs = append([]string{}, args[2:]...)
}
if err := f.run(fallbackArgs...); err != nil {
return fmt.Errorf("%s rich rules (%s), failed, %v", operation, strings.Join(args, " "), err)
}
return nil
}
return fmt.Errorf("%s rich rules (%s), failed, %v", operation, ruleStr, err)
return fmt.Errorf("%s rich rules (%s), failed, %v", operation, strings.Join(args, " "), err)
}
return nil
}
@@ -259,7 +264,7 @@ func (f *Ufw) loadInsertNum(rule FireInfo, operation string) int {
if !strings.Contains(rule.Address, ":") || operation == "remove" {
return 1
}
rules, err := cmd.RunDefaultWithStdoutBashCf("%s status numbered", f.CmdStr)
rules, err := f.runWithStdout("status", "numbered")
if err != nil {
global.LOG.Errorf("load ufw rules failed, err: %v", err)
return 1
@@ -277,3 +282,13 @@ func (f *Ufw) loadInsertNum(rule FireInfo, operation string) int {
}
return i
}
func (f *Ufw) run(args ...string) error {
_, err := f.runWithStdout(args...)
return err
}
func (f *Ufw) runWithStdout(args ...string) (string, error) {
cmdMgr := cmd.NewCommandMgr(cmd.WithEnv("LANGUAGE=en_US:en"))
return cmdMgr.RunWithOptionalSudoAndStdout("ufw", args...)
}
+7 -18
View File
@@ -1,7 +1,6 @@
package client
import (
"bytes"
"compress/gzip"
"context"
"errors"
@@ -16,6 +15,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/files"
)
@@ -229,11 +229,6 @@ func (r *Local) Backup(info BackupInfo) error {
return fmt.Errorf("mkdir %s failed, err: %v", info.TargetDir, err)
}
}
outfile, err := os.OpenFile(path.Join(info.TargetDir, info.FileName), os.O_RDWR|os.O_CREATE, constant.DirPerm)
if err != nil {
return fmt.Errorf("open file %s failed, err: %v", path.Join(info.TargetDir, info.FileName), err)
}
defer func() { _ = outfile.Close() }()
dumpCmd := "mysqldump"
if r.Type == constant.AppMariaDB {
dumpCmd = "mariadb-dump"
@@ -250,19 +245,13 @@ func (r *Local) Backup(info BackupInfo) error {
args = append(args, arg)
}
args = append(args, info.Name)
cmd := exec.Command("docker", args...)
var stderr bytes.Buffer
cmd.Stderr = &stderr
gzipCmd := exec.Command("gzip", "-cf")
gzipCmd.Stdin, _ = cmd.StdoutPipe()
gzipCmd.Stdout = outfile
_ = gzipCmd.Start()
if err := cmd.Run(); err != nil {
return fmt.Errorf("handle backup database failed, err: %v", stderr.String())
cmdMgr := cmd.NewCommandMgr(cmd.WithOutputFile(path.Join(info.TargetDir, info.FileName)))
if _, err := cmdMgr.RunPipe(
cmd.PipeCommand{Name: "docker", Args: args},
cmd.PipeCommand{Name: "gzip", Args: []string{"-cf"}},
); err != nil {
return fmt.Errorf("handle backup database failed, err: %v", err)
}
_ = gzipCmd.Wait()
return nil
}
+43 -27
View File
@@ -1,7 +1,6 @@
package client
import (
"bytes"
"compress/gzip"
"context"
"database/sql"
@@ -244,11 +243,6 @@ func (r *Remote) Backup(info BackupInfo) error {
return fmt.Errorf("mkdir %s failed, err: %v", info.TargetDir, err)
}
}
outfile, err := os.OpenFile(path.Join(info.TargetDir, info.FileName), os.O_RDWR|os.O_CREATE, constant.DirPerm)
if err != nil {
return fmt.Errorf("open file %s failed, err: %v", path.Join(info.TargetDir, info.FileName), err)
}
defer func() { _ = outfile.Close() }()
dumpCmd := "mysqldump"
if r.Type == constant.AppMariaDB {
dumpCmd = "mariadb-dump"
@@ -268,23 +262,32 @@ func (r *Remote) Backup(info BackupInfo) error {
args = append(args, arg)
}
backupCmd := fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c '%s %s -h %s -P %d -u%s -p%s %s --default-character-set=%s %s'",
image, dumpCmd, strings.Join(args, " "), r.Address, r.Port, r.User, r.Password, sslSkip(info.Version, r.Type), info.Format, info.Name)
global.LOG.Debug(strings.ReplaceAll(backupCmd, r.Password, "******"))
cmd := exec.Command("bash", "-c", backupCmd)
var stderr bytes.Buffer
cmd.Stderr = &stderr
gzipCmd := exec.Command("gzip", "-cf")
gzipCmd.Stdin, _ = cmd.StdoutPipe()
gzipCmd.Stdout = outfile
_ = gzipCmd.Start()
if err := cmd.Run(); err != nil {
return fmt.Errorf("handle backup database failed, err: %v", stderr.String())
backupArgs := []string{"run", "--rm", "--net=host", "-i", image, dumpCmd}
backupArgs = append(backupArgs, args...)
backupArgs = append(
backupArgs,
"-h", r.Address,
"-P", fmt.Sprintf("%d", r.Port),
"-u"+r.User,
"-p"+r.Password,
sslSkip(info.Version, r.Type),
"--default-character-set="+info.Format,
info.Name,
)
debugArgs := append([]string{}, backupArgs...)
for i, arg := range debugArgs {
if strings.Contains(arg, r.Password) {
debugArgs[i] = strings.ReplaceAll(arg, r.Password, "******")
}
}
global.LOG.Debug("docker " + strings.Join(debugArgs, " "))
cmdMgr := cmd.NewCommandMgr(cmd.WithOutputFile(path.Join(info.TargetDir, info.FileName)))
if _, err := cmdMgr.RunPipe(
cmd.PipeCommand{Name: "docker", Args: backupArgs},
cmd.PipeCommand{Name: "gzip", Args: []string{"-cf"}},
); err != nil {
return fmt.Errorf("handle backup database failed, err: %v", err)
}
_ = gzipCmd.Wait()
return nil
}
@@ -300,11 +303,24 @@ func (r *Remote) Recover(info RecoverInfo) error {
return err
}
recoverCmd := fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c '%s -h %s -P %d -u%s -p%s %s --default-character-set=%s %s'",
image, r.Type, r.Address, r.Port, r.User, r.Password, sslSkip(info.Version, r.Type), info.Format, info.Name)
global.LOG.Debug(strings.ReplaceAll(recoverCmd, r.Password, "******"))
cmd := exec.Command("bash", "-c", recoverCmd)
recoverArgs := []string{
"run", "--rm", "--net=host", "-i", image, r.Type,
"-h", r.Address,
"-P", fmt.Sprintf("%d", r.Port),
"-u" + r.User,
"-p" + r.Password,
sslSkip(info.Version, r.Type),
"--default-character-set=" + info.Format,
info.Name,
}
debugArgs := append([]string{}, recoverArgs...)
for i, arg := range debugArgs {
if strings.Contains(arg, r.Password) {
debugArgs[i] = strings.ReplaceAll(arg, r.Password, "******")
}
}
global.LOG.Debug("docker " + strings.Join(debugArgs, " "))
cmd := exec.Command("docker", recoverArgs...)
if strings.HasSuffix(info.SourceFile, ".gz") {
gzipFile, err := os.Open(info.SourceFile)
+4 -2
View File
@@ -62,7 +62,8 @@ func GetRemoteTime(site string) (time.Time, error) {
func UpdateSystemTime(dateTime string) error {
system := runtime.GOOS
if system == "linux" {
if err := cmd.RunDefaultBashCf(`%s date -s "%s"`, cmd.SudoHandleCmd(), dateTime); err != nil {
cmdMgr := cmd.NewCommandMgr()
if err := cmdMgr.RunWithOptionalSudo("date", "-s", dateTime); err != nil {
return fmt.Errorf("update system time failed, %v", err)
}
return nil
@@ -73,7 +74,8 @@ func UpdateSystemTime(dateTime string) error {
func UpdateSystemTimeZone(timezone string) error {
system := runtime.GOOS
if system == "linux" {
if err := cmd.RunDefaultBashCf(`%s timedatectl set-timezone "%s"`, cmd.SudoHandleCmd(), timezone); err != nil {
cmdMgr := cmd.NewCommandMgr()
if err := cmdMgr.RunWithOptionalSudo("timedatectl", "set-timezone", timezone); err != nil {
return fmt.Errorf("update system time zone failed, %v", err)
}
return nil
+9 -24
View File
@@ -1,7 +1,6 @@
package client
import (
"bytes"
"compress/gzip"
"context"
"errors"
@@ -14,8 +13,8 @@ import (
"time"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/files"
)
@@ -135,29 +134,15 @@ func (r *Local) Backup(info BackupInfo) error {
return fmt.Errorf("mkdir %s failed, err: %v", info.TargetDir, err)
}
}
outfile, err := os.OpenFile(path.Join(info.TargetDir, info.FileName), os.O_RDWR|os.O_CREATE, constant.DirPerm)
if err != nil {
return fmt.Errorf("open file %s failed, err: %v", path.Join(info.TargetDir, info.FileName), err)
}
defer outfile.Close()
global.LOG.Infof("start to pg_dump | gzip > %s.gzip", info.TargetDir+"/"+info.FileName)
cmd := exec.Command("docker", "exec", "-i", r.ContainerName,
"sh", "-c",
fmt.Sprintf("PGPASSWORD=%s pg_dump -F c -U %s -d %s", r.Password, r.Username, info.Name),
)
var stderr bytes.Buffer
cmd.Stderr = &stderr
gzipCmd := exec.Command("gzip", "-cf")
gzipCmd.Stdin, _ = cmd.StdoutPipe()
gzipCmd.Stdout = outfile
_ = gzipCmd.Start()
if err := cmd.Run(); err != nil {
return fmt.Errorf("handle backup database failed, err: %v", stderr.String())
cmdMgr := cmd.NewCommandMgr(cmd.WithOutputFile(path.Join(info.TargetDir, info.FileName)))
if _, err := cmdMgr.RunPipe(
cmd.PipeCommand{Name: "docker", Args: []string{"exec", "-i", "-e", "PGPASSWORD=" + r.Password, r.ContainerName, "pg_dump", "-F", "c", "-U", r.Username, "-d", info.Name}},
cmd.PipeCommand{Name: "gzip", Args: []string{"-cf"}},
); err != nil {
return fmt.Errorf("handle backup database failed, err: %v", err)
}
_ = gzipCmd.Wait()
return nil
}
@@ -165,8 +150,8 @@ func (r *Local) Recover(info RecoverInfo) error {
fi, _ := os.Open(info.SourceFile)
defer fi.Close()
cmd := exec.Command("docker", "exec", "-i", r.ContainerName, "sh", "-c",
fmt.Sprintf("PGPASSWORD=%s pg_restore -F c -c --if-exists --no-owner -U %s -d %s", r.Password, r.Username, info.Name),
cmd := exec.Command("docker", "exec", "-i", "-e", "PGPASSWORD="+r.Password, r.ContainerName,
"pg_restore", "-F", "c", "-c", "--if-exists", "--no-owner", "-U", r.Username, "-d", info.Name,
)
if strings.HasSuffix(info.SourceFile, ".gz") {
gzipFile, err := os.Open(info.SourceFile)
+103 -13
View File
@@ -2,6 +2,7 @@ package client
import (
"bufio"
"bytes"
"context"
"database/sql"
"fmt"
@@ -24,6 +25,37 @@ import (
_ "github.com/jackc/pgx/v5/stdlib"
)
const maxPgDumpStderrCapture = 64 * 1024
var pgDumpMagic = []byte("PGDMP")
type limitedBuffer struct {
buf bytes.Buffer
limit int
truncated int
}
func (b *limitedBuffer) Write(p []byte) (int, error) {
if b.limit > 0 && b.buf.Len() >= b.limit {
b.truncated += len(p)
return len(p), nil
}
if b.limit > 0 && b.buf.Len()+len(p) > b.limit {
keep := b.limit - b.buf.Len()
_, _ = b.buf.Write(p[:keep])
b.truncated += len(p) - keep
return len(p), nil
}
return b.buf.Write(p)
}
func (b *limitedBuffer) String() string {
if b.truncated == 0 {
return b.buf.String()
}
return fmt.Sprintf("%s\n... truncated %d bytes ...", b.buf.String(), b.truncated)
}
type Remote struct {
Client *sql.DB
From string
@@ -160,21 +192,51 @@ func (r *Remote) Backup(info BackupInfo) error {
}
}
fileNameItem := info.TargetDir + "/" + strings.TrimSuffix(info.FileName, ".gz")
backupCommand := exec.Command("bash", "-c",
fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c 'PGPASSWORD='\\''%s'\\'' pg_dump -h %s -p %d --no-owner -Fc -U %s %s' > %s",
imageTag, r.Password, r.Address, r.Port, r.User, info.Name, fileNameItem))
_ = backupCommand.Run()
b := make([]byte, 5)
n := []byte{80, 71, 68, 77, 80}
backupFile, err := os.OpenFile(fileNameItem, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
if err != nil {
return err
}
backupFileClosed := false
defer func() {
if !backupFileClosed {
_ = backupFile.Close()
}
}()
backupCommand := exec.Command(
"docker",
"run", "--rm", "--net=host", "-i",
"-e", "PGPASSWORD="+r.Password,
imageTag,
"pg_dump",
"-h", r.Address,
"-p", fmt.Sprintf("%d", r.Port),
"--no-owner",
"-Fc",
"-U", r.User,
info.Name,
)
backupCommand.Stdout = backupFile
stderr := &limitedBuffer{limit: maxPgDumpStderrCapture}
backupCommand.Stderr = stderr
if err := backupCommand.Run(); err != nil {
return fmt.Errorf("backup failed, stderr: %s, err: %v", strings.TrimSpace(stderr.String()), err)
}
if err := backupFile.Close(); err != nil {
return fmt.Errorf("close backup file failed, err: %v", err)
}
backupFileClosed = true
b := make([]byte, len(pgDumpMagic))
handle, err := os.OpenFile(fileNameItem, os.O_RDONLY, os.ModePerm)
if err != nil {
return fmt.Errorf("backup file not found,err:%v", err)
}
defer handle.Close()
_, _ = handle.Read(b)
if string(b) != string(n) {
errBytes, _ := os.ReadFile(fileNameItem)
return fmt.Errorf("backup failed, err: %s", string(errBytes))
if _, err := io.ReadFull(handle, b); err != nil {
return fmt.Errorf("read backup header failed, stderr: %s, err: %v", strings.TrimSpace(stderr.String()), err)
}
if !bytes.Equal(b, pgDumpMagic) {
return fmt.Errorf("backup failed, invalid pg dump header: %q, stderr: %s", string(b), strings.TrimSpace(stderr.String()))
}
gzipCmd := exec.Command("gzip", fileNameItem)
@@ -207,9 +269,32 @@ func (r *Remote) Recover(info RecoverInfo) error {
_, _ = gzipCmd.CombinedOutput()
}()
}
recoverCommand := exec.Command("bash", "-c",
fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c 'PGPASSWORD='\\''%s'\\'' pg_restore -h %s -p %d --verbose --clean --no-privileges --no-owner -Fc -c --if-exists --no-owner -U %s -d %s --role=%s' < %s",
imageTag, r.Password, r.Address, r.Port, r.User, info.Name, info.Username, fileName))
restoreFile, err := os.Open(fileName)
if err != nil {
return err
}
defer restoreFile.Close()
recoverCommand := exec.Command(
"docker",
"run", "--rm", "--net=host", "-i",
"-e", "PGPASSWORD="+r.Password,
imageTag,
"pg_restore",
"-h", r.Address,
"-p", fmt.Sprintf("%d", r.Port),
"--verbose",
"--clean",
"--no-privileges",
"--no-owner",
"-Fc",
"-c",
"--if-exists",
"--no-owner",
"-U", r.User,
"-d", info.Name,
"--role="+info.Username,
)
recoverCommand.Stdin = restoreFile
pipe, _ := recoverCommand.StdoutPipe()
stderrPipe, _ := recoverCommand.StderrPipe()
defer pipe.Close()
@@ -230,6 +315,11 @@ func (r *Remote) Recover(info RecoverInfo) error {
}
global.LOG.Infof("[PostgreSQL] DB:[%s] Restoring: %s", info.Name, readString)
}
if err := recoverCommand.Wait(); err != nil {
all, _ := io.ReadAll(stderrPipe)
global.LOG.Errorf("[PostgreSQL] DB:[%s] Recover Error: %s", info.Name, string(all))
return err
}
return nil
}
+16 -13
View File
@@ -47,7 +47,7 @@ func (f *Fail2ban) Status() (bool, bool, bool) {
}
func (f *Fail2ban) Version() string {
stdout, err := cmd.RunDefaultWithStdoutBashC("fail2ban-client version")
stdout, err := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second)).RunWithStdout("fail2ban-client", "version")
if err != nil {
global.LOG.Errorf("load the fail2ban version failed, %v", err)
return "-"
@@ -63,7 +63,7 @@ func (f *Fail2ban) Operate(operate string) error {
}
return nil
case "reload":
if err := cmd.RunDefaultBashC("fail2ban-client reload"); err != nil {
if err := cmd.NewCommandMgr().Run("fail2ban-client", "reload"); err != nil {
return fmt.Errorf("fail2ban-client reload, %v", err)
}
return nil
@@ -91,27 +91,30 @@ func (f *Fail2ban) ReBanIPs(ips []string) error {
func (f *Fail2ban) ListBanned() ([]string, error) {
var lists []string
stdout, err := cmd.RunDefaultWithStdoutBashC("fail2ban-client status sshd | grep 'Banned IP list:'")
stdout, err := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second)).RunWithStdout("fail2ban-client", "status", "sshd")
if err != nil {
return lists, err
}
itemList := strings.Split(strings.Trim(stdout, "\n"), "Banned IP list:")
if len(itemList) != 2 {
return lists, nil
}
ips := strings.Fields(itemList[1])
for _, item := range ips {
if len(item) != 0 {
lists = append(lists, item)
for _, line := range strings.Split(strings.Trim(stdout, "\n"), "\n") {
itemList := strings.Split(line, "Banned IP list:")
if len(itemList) != 2 {
continue
}
ips := strings.Fields(itemList[1])
for _, item := range ips {
if len(item) != 0 {
lists = append(lists, item)
}
}
break
}
return lists, nil
}
func (f *Fail2ban) ListIgnore() ([]string, error) {
var lists []string
stdout, err := cmd.RunDefaultWithStdoutBashC("fail2ban-client get sshd ignoreip")
stdout, err := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second)).RunWithStdout("fail2ban-client", "get", "sshd", "ignoreip")
if err != nil {
return lists, err
}
+36 -21
View File
@@ -63,7 +63,7 @@ func NewFtpClient() (*Ftp, error) {
groupItem, err := user.LookupGroupId("1000")
if err == nil {
if err := cmd.RunDefaultBashCf("useradd -u 1000 -g %s %s", groupItem.Name, "1panel"); err != nil {
if err := cmd.NewCommandMgr().Run("useradd", "-u", "1000", "-g", groupItem.Name, "1panel"); err != nil {
return nil, err
}
return &Ftp{DefaultUser: "1panel", DefaultGroup: groupItem.Name}, nil
@@ -71,10 +71,10 @@ func NewFtpClient() (*Ftp, error) {
if err.Error() != user.UnknownGroupIdError("1000").Error() {
return nil, err
}
if err := cmd.RunDefaultBashC("groupadd -g 1000 1panel"); err != nil {
if err := cmd.NewCommandMgr().Run("groupadd", "-g", "1000", "1panel"); err != nil {
return nil, err
}
if err := cmd.RunDefaultBashC("useradd -u 1000 -g 1panel 1panel"); err != nil {
if err := cmd.NewCommandMgr().Run("useradd", "-u", "1000", "-g", "1panel", "1panel"); err != nil {
return nil, err
}
return &Ftp{DefaultUser: "1panel", DefaultGroup: "1panel"}, nil
@@ -118,7 +118,7 @@ func (f *Ftp) UserAdd(username, passwd, path string) error {
return err
}
_ = f.Reload()
if err := cmd.RunDefaultBashCf("chown -R %s:%s %s", f.DefaultUser, f.DefaultGroup, path); err != nil {
if err := cmd.NewCommandMgr().Run("chown", "-R", f.DefaultUser+":"+f.DefaultGroup, path); err != nil {
return err
}
return nil
@@ -128,7 +128,7 @@ func (f *Ftp) UserDel(username string) error {
if cmd.CheckIllegal(username) {
return buserr.New("ErrCmdIllegal")
}
if err := cmd.RunDefaultBashCf("pure-pw userdel %s", username); err != nil {
if err := cmd.NewCommandMgr().Run("pure-pw", "userdel", username); err != nil {
return err
}
_ = f.Reload()
@@ -189,10 +189,10 @@ func (f *Ftp) SetPath(username, path string) error {
if cmd.CheckIllegal(username, path) {
return buserr.New("ErrCmdIllegal")
}
if err := cmd.RunDefaultBashCf("pure-pw usermod %s -d %s", username, path); err != nil {
if err := cmd.NewCommandMgr().Run("pure-pw", "usermod", username, "-d", path); err != nil {
return err
}
if err := cmd.RunDefaultBashCf("chown -R %s:%s %s", f.DefaultUser, f.DefaultGroup, path); err != nil {
if err := cmd.NewCommandMgr().Run("chown", "-R", f.DefaultUser+":"+f.DefaultGroup, path); err != nil {
return err
}
return nil
@@ -202,18 +202,18 @@ func (f *Ftp) SetStatus(username, status string) error {
if cmd.CheckIllegal(username, status) {
return buserr.New("ErrCmdIllegal")
}
statusItem := "''"
statusItem := ""
if status == constant.StatusDisable {
statusItem = "1"
}
if err := cmd.RunDefaultBashCf("pure-pw usermod %s -r %s", username, statusItem); err != nil {
if err := cmd.NewCommandMgr().Run("pure-pw", "usermod", username, "-r", statusItem); err != nil {
return err
}
return nil
}
func (f *Ftp) LoadList() ([]FtpList, error) {
std, err := cmd.RunDefaultWithStdoutBashC("pure-pw list")
std, err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout("pure-pw", "list")
if err != nil {
return nil, err
}
@@ -224,14 +224,20 @@ func (f *Ftp) LoadList() ([]FtpList, error) {
if len(parts) < 2 {
continue
}
std2, err := cmd.RunDefaultWithStdoutBashCf("pure-pw show %s | grep 'Allowed client IPs :'", parts[0])
std2, err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout("pure-pw", "show", parts[0])
if err != nil {
global.LOG.Errorf("handle pure-pw show %s failed, %v", parts[0], err)
continue
}
status := constant.StatusDisable
itemStd := strings.ReplaceAll(std2, "\n", "")
if len(strings.TrimSpace(strings.ReplaceAll(itemStd, "Allowed client IPs :", ""))) == 0 {
allowedLine := ""
for _, line := range strings.Split(std2, "\n") {
if strings.Contains(line, "Allowed client IPs :") {
allowedLine = line
break
}
}
if len(strings.TrimSpace(strings.ReplaceAll(allowedLine, "Allowed client IPs :", ""))) == 0 {
status = constant.StatusEnable
}
lists = append(lists, FtpList{User: parts[0], Path: strings.ReplaceAll(parts[1], "/./", ""), Status: status})
@@ -240,7 +246,7 @@ func (f *Ftp) LoadList() ([]FtpList, error) {
}
func (f *Ftp) Reload() error {
if err := cmd.RunDefaultBashC("pure-pw mkdb"); err != nil {
if err := cmd.NewCommandMgr().Run("pure-pw", "mkdb"); err != nil {
return err
}
return nil
@@ -250,19 +256,28 @@ func (f *Ftp) LoadLogs(user, operation string) ([]FtpLog, error) {
var logs []FtpLog
logItem := ""
if _, err := os.Stat("/etc/pure-ftpd/conf"); err != nil && os.IsNotExist(err) {
std, err := cmd.RunDefaultWithStdoutBashC("cat /etc/pure-ftpd/pure-ftpd.conf | grep AltLog | grep clf:")
logItem = "/var/log/pureftpd.log"
if err == nil && !strings.HasPrefix(std, "#") {
logItem = std
data, readErr := os.ReadFile("/etc/pure-ftpd/pure-ftpd.conf")
if readErr == nil {
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(strings.TrimSpace(line), "#") || !strings.Contains(line, "AltLog") || !strings.Contains(line, "clf:") {
continue
}
logItem = line
break
}
}
} else {
if err != nil {
return logs, err
}
std, err := cmd.RunDefaultWithStdoutBashC("cat /etc/pure-ftpd/conf/AltLog")
logItem = "/var/log/pure-ftpd/transfer.log"
if err != nil && !strings.HasPrefix(std, "#") {
logItem = std
data, readErr := os.ReadFile("/etc/pure-ftpd/conf/AltLog")
if readErr == nil {
std := string(data)
if !strings.HasPrefix(strings.TrimSpace(std), "#") {
logItem = std
}
}
}
@@ -298,7 +313,7 @@ func (f *Ftp) LoadLogs(user, operation string) ([]FtpLog, error) {
}
func handleGunzip(path string) error {
if err := cmd.RunDefaultBashCf("gunzip %s", path); err != nil {
if err := cmd.NewCommandMgr().Run("gunzip", path); err != nil {
return err
}
return nil
+24 -2
View File
@@ -1,16 +1,22 @@
package service
import (
"bytes"
"fmt"
"net/http"
"github.com/1Panel-dev/1Panel/core/buserr"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/utils/cmd"
"github.com/1Panel-dev/1Panel/core/utils/common"
geo2 "github.com/1Panel-dev/1Panel/core/utils/geo"
"github.com/1Panel-dev/1Panel/core/utils/req_helper"
"github.com/gin-gonic/gin"
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/utils/cmd"
"github.com/jinzhu/copier"
)
@@ -109,5 +115,21 @@ func (u *LogService) CleanLogs(logtype string) error {
}
func writeLogs(version string) {
_, _ = cmd.RunDefaultWithStdoutBashCf("curl -sfL %s | sh -s 1p upgrade %s", logs, version)
_ = runRemoteShellScript(logs, "1p", "upgrade", version)
}
func runRemoteShellScript(url string, args ...string) error {
statusCode, script, err := req_helper.HandleRequestWithProxy(url, http.MethodGet, constant.TimeOut20s)
if err != nil {
return err
}
if statusCode < http.StatusOK || statusCode >= http.StatusMultipleChoices {
return fmt.Errorf("download script failed, status code: %d", statusCode)
}
_, err = cmd.NewCommandMgr().RunPipe(cmd.PipeCommand{
Name: "sh",
Args: append([]string{"-s"}, args...),
Stdin: bytes.NewReader(script),
})
return err
}
+5 -4
View File
@@ -21,6 +21,7 @@ import (
"github.com/1Panel-dev/1Panel/core/utils/cmd"
"github.com/1Panel-dev/1Panel/core/utils/common"
"github.com/1Panel-dev/1Panel/core/utils/controller"
"github.com/1Panel-dev/1Panel/core/utils/ctl_conf"
"github.com/1Panel-dev/1Panel/core/utils/files"
"github.com/1Panel-dev/1Panel/core/utils/req_helper"
"github.com/1Panel-dev/1Panel/core/utils/xpack"
@@ -173,7 +174,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
fileName := fmt.Sprintf("1panel-%s-%s-%s.tar.gz", req.Version, "linux", itemArch)
_ = settingRepo.Update("SystemStatus", "Upgrading")
go func() {
oldLang := common.LoadParams("LANGUAGE")
oldLang := ctl_conf.Load("LANGUAGE")
if err := files.DownloadFileWithProxyStream(downloadPath+"/"+fileName, downloadDir+"/"+fileName); err != nil {
global.LOG.Errorf("download service file failed, err: %v", err)
_ = settingRepo.Update("SystemStatus", "Free")
@@ -219,12 +220,12 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
u.handleRollback(originalDir, 2, svcInfo)
return
}
if _, err := cmd.RunDefaultWithStdoutBashCf("sed -i -e 's#BASE_DIR=.*#BASE_DIR=%s#g' /usr/local/bin/1pctl", global.CONF.Base.InstallDir); err != nil {
if err := ctl_conf.UpdateInFile("/usr/local/bin/1pctl", "BASE_DIR", global.CONF.Base.InstallDir); err != nil {
global.LOG.Errorf("upgrade basedir in 1pctl failed, err: %v", err)
u.handleRollback(originalDir, 2, svcInfo)
return
}
if _, err := cmd.RunDefaultWithStdoutBashCf("sed -i -e 's#LANGUAGE=.*#LANGUAGE=%s#g' /usr/local/bin/1pctl", oldLang); err != nil {
if err := ctl_conf.UpdateInFile("/usr/local/bin/1pctl", "LANGUAGE", oldLang); err != nil {
global.LOG.Errorf("upgrade basedir in 1pctl failed, err: %v", err)
u.handleRollback(originalDir, 2, svcInfo)
return
@@ -546,7 +547,7 @@ func (u *UpgradeService) loadReleaseNotes(path string) (string, error) {
}
func loadArch() (string, error) {
std, err := cmd.RunDefaultWithStdoutBashC("uname -a")
std, err := cmd.NewCommandMgr().RunWithStdout("uname", "-a")
if err != nil {
return "", fmt.Errorf("std: %s, err: %s", std, err.Error())
}
+8 -9
View File
@@ -12,6 +12,7 @@ import (
cmdUtils "github.com/1Panel-dev/1Panel/core/utils/cmd"
"github.com/1Panel-dev/1Panel/core/utils/common"
"github.com/1Panel-dev/1Panel/core/utils/controller"
"github.com/1Panel-dev/1Panel/core/utils/ctl_conf"
"github.com/1Panel-dev/1Panel/core/utils/files"
"github.com/spf13/cobra"
@@ -29,11 +30,10 @@ var restoreCmd = &cobra.Command{
fmt.Println(i18n.GetMsgWithMapForCmd("SudoHelper", map[string]interface{}{"cmd": "sudo 1pctl restore"}))
return nil
}
stdout, err := cmdUtils.RunDefaultWithStdoutBashC("grep '^BASE_DIR=' /usr/local/bin/1pctl | cut -d'=' -f2")
baseDir, err := ctl_conf.LoadFromFile("/usr/local/bin/1pctl", "BASE_DIR")
if err != nil {
return fmt.Errorf("handle load `BASE_DIR` failed, err: %v", err)
}
baseDir := strings.ReplaceAll(stdout, "\n", "")
upgradeDir := path.Join(baseDir, "1panel", "tmp", "upgrade")
tmpPath, err := loadRestorePath(upgradeDir)
@@ -59,17 +59,17 @@ var restoreCmd = &cobra.Command{
if err := files.CopyItem(false, true, path.Join(tmpPath, "GeoIP.mmdb"), path.Join(baseDir, "1panel/geo")); err != nil {
return err
}
sudo := cmdUtils.SudoHandleCmd()
_, _ = cmdUtils.RunDefaultWithStdoutBashCf("%s chmod 755 /usr/local/bin/1panel-agent /usr/local/bin/1panel-core", sudo)
_, _ = cmdUtils.NewCommandMgr().RunWithStdout("chmod", "755", "/usr/local/bin/1panel-agent", "/usr/local/bin/1panel-core")
fmt.Println(i18n.GetMsgByKeyForCmd("RestoreStep2"))
if err := files.CopyItem(false, true, path.Join(tmpPath, "1pctl"), "/usr/local/bin"); err != nil {
return err
}
_, _ = cmdUtils.RunDefaultWithStdoutBashCf("%s chmod 755 /usr/local/bin/1pctl", sudo)
_, _ = cmdUtils.RunDefaultWithStdoutBashCf("cp -r %s /usr/local/bin", path.Join(tmpPath, "lang"))
_, _ = cmdUtils.NewCommandMgr().RunWithStdout("chmod", "755", "/usr/local/bin/1pctl")
_, _ = cmdUtils.NewCommandMgr().RunWithStdout("cp", "-r", "--", path.Join(tmpPath, "lang"), "/usr/local/bin")
geoPath := path.Join(global.CONF.Base.InstallDir, "1panel/geo")
_, _ = cmdUtils.RunDefaultWithStdoutBashCf("mkdir %s && cp %s %s/", geoPath, path.Join(tmpPath, "GeoIP.mmdb"), geoPath)
_ = os.MkdirAll(geoPath, os.ModePerm)
_, _ = cmdUtils.NewCommandMgr().RunWithStdout("cp", "--", path.Join(tmpPath, "GeoIP.mmdb"), geoPath+"/")
fmt.Println(i18n.GetMsgByKeyForCmd("RestoreStep3"))
svcBasePath, _ := controller.GetServicePath("")
@@ -137,11 +137,10 @@ func loadRestorePath(upgradeDir string) (string, error) {
}
func loadRollbackVersion(upgradeDir string) string {
stdout, err := cmdUtils.RunDefaultWithStdoutBashCf("grep '^ORIGINAL_VERSION=' %s/1pctl | cut -d'=' -f2", upgradeDir)
info, err := ctl_conf.LoadFromFile(path.Join(upgradeDir, "1pctl"), "ORIGINAL_VERSION")
if err != nil {
return "-"
}
info := strings.ReplaceAll(stdout, "\n", "")
if len(info) == 0 || info == `""` {
return "-"
}
+2 -3
View File
@@ -8,7 +8,7 @@ import (
"time"
"github.com/1Panel-dev/1Panel/core/server"
cmdUtils "github.com/1Panel-dev/1Panel/core/utils/cmd"
"github.com/1Panel-dev/1Panel/core/utils/ctl_conf"
"github.com/glebarez/sqlite"
"github.com/spf13/cobra"
"gorm.io/gorm"
@@ -38,11 +38,10 @@ type setting struct {
}
func loadDBConn(dbName string) (*gorm.DB, error) {
stdout, err := cmdUtils.RunDefaultWithStdoutBashC("grep '^BASE_DIR=' /usr/local/bin/1pctl | cut -d'=' -f2")
baseDir, err := ctl_conf.LoadFromFile("/usr/local/bin/1pctl", "BASE_DIR")
if err != nil {
return nil, fmt.Errorf("handle load `BASE_DIR` failed, err: %v", err)
}
baseDir := strings.ReplaceAll(stdout, "\n", "")
if len(baseDir) == 0 {
return nil, fmt.Errorf("error `BASE_DIR` find in /usr/local/bin/1pctl \n")
}
+1 -1
View File
@@ -237,7 +237,7 @@ func port() {
fmt.Println("\n" + i18n.GetMsgByKeyForCmd("UpdateSuccessful"))
fmt.Println(i18n.GetMsgWithMapForCmd("UpdatePortResult", map[string]interface{}{"name": newPortStr}))
std, err := cmd.RunDefaultWithStdoutBashC("1pctl restart core")
std, err := cmd.NewCommandMgr().RunWithStdout("1pctl", "restart", "core")
if err != nil {
fmt.Println(std)
}
+2 -4
View File
@@ -3,12 +3,12 @@ package i18n
import (
"embed"
"fmt"
"os/exec"
"strings"
"sync/atomic"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/utils/ctl_conf"
"github.com/gin-gonic/gin"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
@@ -209,12 +209,10 @@ func getLanguageFromDBInternal() string {
return lang
}
func getLanguageFrom1pctl() string {
cmd := exec.Command("bash", "-c", "grep '^LANGUAGE=' /usr/local/bin/1pctl | cut -d'=' -f2")
stdout, err := cmd.CombinedOutput()
info, err := ctl_conf.LoadFromFile("/usr/local/bin/1pctl", "LANGUAGE")
if err != nil {
panic(err)
}
info := strings.ReplaceAll(string(stdout), "\n", "")
if len(info) == 0 || info == `""` {
panic("error `LANGUAGE` find in /usr/local/bin/1pctl")
}
+7 -3
View File
@@ -61,7 +61,7 @@ func initLang() {
downloadLangFromRemote()
return
}
if err := cmd.RunDefaultBashCf("cp -r %s %s", path.Join(tmpPath, "lang"), "/usr/local/bin/"); err != nil {
if err := cmd.NewCommandMgr().Run("cp", "-r", "--", path.Join(tmpPath, "lang"), "/usr/local/bin/"); err != nil {
global.LOG.Errorf("load lang from package failed, %v", err)
return
}
@@ -72,7 +72,11 @@ func initLang() {
downloadGeoFromRemote(geoPath)
return
}
if err := cmd.RunDefaultBashCf("mkdir %s && cp %s %s/", path.Dir(geoPath), path.Join(tmpPath, "GeoIP.mmdb"), path.Dir(geoPath)); err != nil {
if err := os.MkdirAll(path.Dir(geoPath), os.ModePerm); err != nil {
global.LOG.Errorf("mkdir geo ip dir failed, %v", err)
return
}
if err := cmd.NewCommandMgr().Run("cp", "--", path.Join(tmpPath, "GeoIP.mmdb"), path.Dir(geoPath)+"/"); err != nil {
global.LOG.Errorf("load geo ip from package failed, %v", err)
return
}
@@ -113,7 +117,7 @@ func downloadLangFromRemote() {
global.LOG.Error("download lang.tar.gz failed, no such file")
return
}
if err := cmd.RunDefaultBashCf("tar zxvfC %s %s", "/usr/local/bin/lang.tar.gz", "/usr/local/bin/"); err != nil {
if err := cmd.NewCommandMgr().Run("tar", "zxvfC", "/usr/local/bin/lang.tar.gz", "/usr/local/bin/"); err != nil {
global.LOG.Errorf("decompress lang.tar.gz failed, %v", err)
return
}
+7 -7
View File
@@ -1,14 +1,15 @@
package hook
import (
"os/exec"
"strings"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/app/service"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/utils/cmd"
"github.com/1Panel-dev/1Panel/core/utils/common"
"github.com/1Panel-dev/1Panel/core/utils/ctl_conf"
"github.com/1Panel-dev/1Panel/core/utils/encrypt"
"github.com/1Panel-dev/1Panel/core/utils/xpack"
)
@@ -37,12 +38,12 @@ func handleUserInfo(tags string, settingRepo repo.ISettingRepo) {
}
settingMap := make(map[string]string)
if tags == "use_existing" {
settingMap["ServerPort"] = common.LoadParams("ORIGINAL_PORT")
settingMap["ServerPort"] = ctl_conf.Load("ORIGINAL_PORT")
global.CONF.Conn.Port = settingMap["ServerPort"]
settingMap["UserName"] = global.CONF.Base.Username
settingMap["Password"] = global.CONF.Base.Password
settingMap["SecurityEntrance"] = global.CONF.Conn.Entrance
settingMap["SystemVersion"] = common.LoadParams("ORIGINAL_VERSION")
settingMap["SystemVersion"] = ctl_conf.Load("ORIGINAL_VERSION")
global.CONF.Base.Version = settingMap["SystemVersion"]
settingMap["Language"] = global.CONF.Base.Language
}
@@ -81,8 +82,8 @@ func handleUserInfo(tags string, settingRepo repo.ISettingRepo) {
}
}
_, _ = cmd.RunDefaultWithStdoutBashCf("%s sed -i '/CHANGE_USER_INFO=%v/d' /usr/local/bin/1pctl", cmd.SudoHandleCmd(), global.CONF.Base.ChangeUserInfo)
_, _ = cmd.RunDefaultWithStdoutBashCf("%s sed -i -e 's#ORIGINAL_PASSWORD=.*#ORIGINAL_PASSWORD=**********#g' /usr/local/bin/1pctl", cmd.SudoHandleCmd())
_ = ctl_conf.RemoveValueFromFile("/usr/local/bin/1pctl", "CHANGE_USER_INFO", global.CONF.Base.ChangeUserInfo)
_ = ctl_conf.UpdateInFile("/usr/local/bin/1pctl", "ORIGINAL_PASSWORD", "**********")
}
func generateKey() {
@@ -92,11 +93,10 @@ func generateKey() {
}
func initDockerConf() {
stdout, err := cmd.RunDefaultWithStdoutBashC("which docker")
dockerPath, err := exec.LookPath("docker")
if err != nil {
return
}
dockerPath := stdout
if strings.Contains(dockerPath, "snap") {
constant.DaemonJsonPath = "/var/snap/docker/current/config/daemon.json"
}
+2 -2
View File
@@ -14,8 +14,8 @@ import (
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/init/migration/helper"
"github.com/1Panel-dev/1Panel/core/utils/cmd"
"github.com/1Panel-dev/1Panel/core/utils/common"
"github.com/1Panel-dev/1Panel/core/utils/ctl_conf"
"github.com/1Panel-dev/1Panel/core/utils/encrypt"
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
@@ -55,7 +55,7 @@ var InitSetting = &gormigrate.Migration{
return err
}
}
_, _ = cmd.RunDefaultWithStdoutBashCf("%s sed -i -e 's#ORIGINAL_PASSWORD=.*#ORIGINAL_PASSWORD=**********#g' /usr/local/bin/1pctl", cmd.SudoHandleCmd())
_ = ctl_conf.UpdateInFile("/usr/local/bin/1pctl", "ORIGINAL_PASSWORD", "**********")
if err := tx.Create(&model.Setting{Key: "Theme", Value: "light"}).Error; err != nil {
return err
}
+10 -16
View File
@@ -5,12 +5,10 @@ import (
"fmt"
"os"
"path"
"strings"
"github.com/1Panel-dev/1Panel/core/cmd/server/conf"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/utils/cmd"
"github.com/1Panel-dev/1Panel/core/utils/common"
"github.com/1Panel-dev/1Panel/core/utils/ctl_conf"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"
@@ -40,14 +38,14 @@ func Init() {
panic(fmt.Errorf("fatal error config file: %s", err))
}
} else {
baseDir = common.LoadParams("BASE_DIR")
port = common.LoadParams("ORIGINAL_PORT")
version = common.LoadParams("ORIGINAL_VERSION")
username = common.LoadParams("ORIGINAL_USERNAME")
password = common.LoadParams("ORIGINAL_PASSWORD")
entrance = common.LoadParams("ORIGINAL_ENTRANCE")
language = common.LoadParams("LANGUAGE")
edition = common.LoadParamsWithoutPanic("PANEL_EDITION")
baseDir = ctl_conf.Load("BASE_DIR")
port = ctl_conf.Load("ORIGINAL_PORT")
version = ctl_conf.Load("ORIGINAL_VERSION")
username = ctl_conf.Load("ORIGINAL_USERNAME")
password = ctl_conf.Load("ORIGINAL_PASSWORD")
entrance = ctl_conf.Load("ORIGINAL_ENTRANCE")
language = ctl_conf.Load("LANGUAGE")
edition = ctl_conf.LoadWithoutPanic("PANEL_EDITION")
reader := bytes.NewReader(conf.AppYaml)
if err := v.ReadConfig(reader); err != nil {
@@ -106,9 +104,5 @@ func Init() {
}
func loadChangeInfo() string {
stdout, err := cmd.RunDefaultWithStdoutBashC("grep '^CHANGE_USER_INFO=' /usr/local/bin/1pctl | cut -d'=' -f2")
if err != nil {
return ""
}
return strings.ReplaceAll(stdout, "\n", "")
return ctl_conf.LoadWithoutPanic("CHANGE_USER_INFO")
}
+2 -52
View File
@@ -1,11 +1,7 @@
package cmd
import (
"bufio"
"fmt"
"io"
"os/exec"
"strings"
)
func SudoHandleCmd() string {
@@ -17,52 +13,6 @@ func SudoHandleCmd() string {
}
func Which(name string) bool {
// Prefer Go's built-in PATH lookup so we don't depend on an external
// `which` binary, which is not installed by default on minimal
// distributions (e.g. Arch Linux, some Alpine images, slim containers).
// See 1Panel-dev/1Panel#12605.
if _, err := exec.LookPath(name); err == nil {
return true
}
// Fall back to shelling out for environments where PATH inside the
// agent process differs from the user's interactive shell PATH (the
// previous behaviour, preserved for compatibility).
stdout, err := RunDefaultWithStdoutBashCf("which %s", name)
if err != nil || (len(strings.ReplaceAll(stdout, "\n", "")) == 0) {
return false
}
return true
}
func ExecWithStreamOutput(command string, outputCallback func(string)) error {
cmd := exec.Command("bash", "-c", command)
stdout, err := cmd.StdoutPipe()
if err != nil {
return fmt.Errorf("failed to get stdout: %w", err)
}
stderr, err := cmd.StderrPipe()
if err != nil {
return fmt.Errorf("failed to get stderr: %w", err)
}
if err := cmd.Start(); err != nil {
return fmt.Errorf("failed to start command: %w", err)
}
go streamReader(stdout, outputCallback)
go streamReader(stderr, outputCallback)
if err := cmd.Wait(); err != nil {
return fmt.Errorf("command finished with error: %w", err)
}
return nil
}
func streamReader(reader io.ReadCloser, callback func(string)) {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
callback(scanner.Text())
}
_, err := exec.LookPath(name)
return err == nil
}
+243 -53
View File
@@ -5,10 +5,12 @@ import (
"context"
"errors"
"fmt"
"io"
"log"
"os"
"os/exec"
"strings"
"sync"
"syscall"
"time"
@@ -17,10 +19,12 @@ import (
"github.com/1Panel-dev/1Panel/core/constant"
)
const maxStreamOutputCapture = 64 * 1024
type CommandHelper struct {
workDir string
outputFile string
scriptPath string
env []string
timeout time.Duration
taskItem *task.Task
logger *log.Logger
@@ -29,6 +33,46 @@ type CommandHelper struct {
type Option func(*CommandHelper)
type PipeCommand struct {
Name string
Args []string
Env []string
Dir string
Stdin io.Reader
}
type lockedBuffer struct {
mu sync.Mutex
buf bytes.Buffer
limit int
truncated int
}
func (b *lockedBuffer) Write(p []byte) (int, error) {
b.mu.Lock()
defer b.mu.Unlock()
if b.limit > 0 && b.buf.Len() >= b.limit {
b.truncated += len(p)
return len(p), nil
}
if b.limit > 0 && b.buf.Len()+len(p) > b.limit {
keep := b.limit - b.buf.Len()
_, _ = b.buf.Write(p[:keep])
b.truncated += len(p) - keep
return len(p), nil
}
return b.buf.Write(p)
}
func (b *lockedBuffer) String() string {
b.mu.Lock()
defer b.mu.Unlock()
if b.truncated == 0 {
return b.buf.String()
}
return fmt.Sprintf("%s\n... truncated %d bytes ...", b.buf.String(), b.truncated)
}
func NewCommandMgr(opts ...Option) *CommandHelper {
s := &CommandHelper{}
for _, opt := range opts {
@@ -37,49 +81,160 @@ func NewCommandMgr(opts ...Option) *CommandHelper {
return s
}
func RunDefaultBashC(command string) error {
mgr := NewCommandMgr()
return mgr.RunBashC(command)
}
func RunDefaultBashCf(command string, arg ...interface{}) error {
mgr := NewCommandMgr()
return mgr.RunBashCf(command, arg...)
}
func RunDefaultWithStdoutBashC(command string) (string, error) {
mgr := NewCommandMgr(WithTimeout(20 * time.Second))
return mgr.RunWithStdoutBashC(command)
}
func RunDefaultWithStdoutBashCf(command string, arg ...interface{}) (string, error) {
mgr := NewCommandMgr(WithTimeout(20 * time.Second))
return mgr.RunWithStdoutBashCf(command, arg...)
}
func (c *CommandHelper) Run(name string, arg ...string) error {
_, err := c.run(name, arg...)
return err
}
func (c *CommandHelper) RunBashCWithArgs(arg ...string) error {
arg = append([]string{"-c"}, arg...)
_, err := c.run("bash", arg...)
return err
}
func (c *CommandHelper) RunBashC(command string) error {
_, err := c.run("bash", "-c", command)
return err
}
func (c *CommandHelper) RunBashCf(command string, arg ...interface{}) error {
_, err := c.run("bash", "-c", fmt.Sprintf(command, arg...))
return err
}
func (c *CommandHelper) RunWithStdout(name string, arg ...string) (string, error) {
return c.run(name, arg...)
}
func (c *CommandHelper) RunWithStdoutBashC(command string) (string, error) {
return c.run("bash", "-c", command)
func (c *CommandHelper) RunPipe(commands ...PipeCommand) (string, error) {
if len(commands) == 0 {
return "", nil
}
ctx, cancel := c.pipeContext()
if cancel != nil {
defer cancel()
}
cmds := c.buildPipeCommands(ctx, commands)
customWriter := &CustomWriter{taskItem: c.taskItem}
var outputFile *os.File
limitOutputCapture := c.taskItem != nil || c.logger != nil || len(c.outputFile) != 0
stdout, stderr := &lockedBuffer{}, &lockedBuffer{}
if limitOutputCapture {
stdout.limit = maxStreamOutputCapture
stderr.limit = maxStreamOutputCapture
}
if commands[0].Stdin != nil {
cmds[0].Stdin = commands[0].Stdin
}
var pipeStderr io.Writer = stderr
var lastStdout io.Writer = stdout
var lastStderr io.Writer = stderr
var streamWriter io.Writer
var streamClosers []io.Closer
if c.taskItem != nil {
streamWriter = customWriter
} else if c.logger != nil {
streamWriter = c.logger.Writer()
if closer, ok := streamWriter.(io.Closer); ok {
streamClosers = append(streamClosers, closer)
}
} else if len(c.outputFile) != 0 {
file, err := os.OpenFile(c.outputFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, constant.FilePerm)
if err != nil {
return "", err
}
outputFile = file
lastStdout = outputFile
}
if streamWriter != nil {
pipeStderr = io.MultiWriter(stderr, streamWriter)
lastStdout = io.MultiWriter(stdout, streamWriter)
lastStderr = io.MultiWriter(stderr, streamWriter)
}
defer func() {
if c.taskItem != nil {
customWriter.Flush()
}
for _, closer := range streamClosers {
_ = closer.Close()
}
if outputFile != nil {
_ = outputFile.Close()
}
}()
if err := connectPipeCommands(cmds, lastStdout, lastStderr, pipeStderr); err != nil {
return "", err
}
if err := startPipeCommands(cmds); err != nil {
return handleErrString(stdout.String(), stderr.String(), c.IgnoreExist1, err)
}
runErr := waitPipeCommands(ctx, cmds)
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
return "", buserr.New("ErrCmdTimeout")
}
if runErr != nil {
return handleErrString(stdout.String(), stderr.String(), c.IgnoreExist1, runErr)
}
return stdout.String(), nil
}
func (c *CommandHelper) RunWithStdoutBashCf(command string, arg ...interface{}) (string, error) {
return c.run("bash", "-c", fmt.Sprintf(command, arg...))
func (c *CommandHelper) pipeContext() (context.Context, context.CancelFunc) {
ctx := context.Background()
if c.timeout == 0 {
return ctx, nil
}
return context.WithTimeout(ctx, c.timeout)
}
func (c *CommandHelper) buildPipeCommands(ctx context.Context, commands []PipeCommand) []*exec.Cmd {
cmds := make([]*exec.Cmd, 0, len(commands))
for _, item := range commands {
cmdItem := exec.CommandContext(ctx, item.Name, item.Args...)
cmdItem.Env = append(os.Environ(), c.env...)
cmdItem.Env = append(cmdItem.Env, item.Env...)
cmdItem.Dir = c.workDir
if item.Dir != "" {
cmdItem.Dir = item.Dir
}
cmdItem.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
}
cmds = append(cmds, cmdItem)
}
return cmds
}
func connectPipeCommands(cmds []*exec.Cmd, stdout, stderr, pipeStderr io.Writer) error {
for i := 0; i < len(cmds)-1; i++ {
pipe, err := cmds[i].StdoutPipe()
if err != nil {
return err
}
cmds[i+1].Stdin = pipe
cmds[i].Stderr = pipeStderr
}
last := cmds[len(cmds)-1]
last.Stdout = stdout
last.Stderr = stderr
return nil
}
func startPipeCommands(cmds []*exec.Cmd) error {
for i := len(cmds) - 1; i >= 0; i-- {
if err := cmds[i].Start(); err != nil {
killStarted(cmds[i+1:])
return err
}
}
return nil
}
func waitPipeCommands(ctx context.Context, cmds []*exec.Cmd) error {
done := make(chan error, 1)
go func() {
var runErr error
for _, item := range cmds {
if err := item.Wait(); err != nil && runErr == nil {
runErr = err
}
}
done <- runErr
}()
select {
case runErr := <-done:
return runErr
case <-ctx.Done():
killProcessGroups(cmds)
return <-done
}
}
func (c *CommandHelper) run(name string, arg ...string) (string, error) {
@@ -97,12 +252,21 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
customWriter := &CustomWriter{taskItem: c.taskItem}
var stdout, stderr bytes.Buffer
var loggerClosers []io.Closer
if c.taskItem != nil {
cmd.Stdout = customWriter
cmd.Stderr = customWriter
} else if c.logger != nil {
cmd.Stdout = c.logger.Writer()
cmd.Stderr = c.logger.Writer()
stdoutWriter := c.logger.Writer()
stderrWriter := c.logger.Writer()
if closer, ok := stdoutWriter.(io.Closer); ok {
loggerClosers = append(loggerClosers, closer)
}
if closer, ok := stderrWriter.(io.Closer); ok {
loggerClosers = append(loggerClosers, closer)
}
cmd.Stdout = stdoutWriter
cmd.Stderr = stderrWriter
} else if len(c.outputFile) != 0 {
file, err := os.OpenFile(c.outputFile, os.O_WRONLY|os.O_CREATE, constant.FilePerm)
if err != nil {
@@ -111,19 +275,21 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
defer file.Close()
cmd.Stdout = file
cmd.Stderr = file
} else if len(c.scriptPath) != 0 {
cmd.Stdout = &stdout
cmd.Stderr = &stderr
cmd = exec.Command("bash", c.scriptPath)
} else {
cmd.Stdout = &stdout
cmd.Stderr = &stderr
}
env := os.Environ()
env = append(env, c.env...)
cmd.Env = env
if len(c.workDir) != 0 {
cmd.Dir = c.workDir
}
defer func() {
for _, closer := range loggerClosers {
_ = closer.Close()
}
}()
if c.timeout != 0 {
err := cmd.Run()
@@ -140,12 +306,32 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
}
err := cmd.Run()
if c.taskItem != nil {
customWriter.Flush()
}
if err != nil {
return handleErr(stdout, stderr, c.IgnoreExist1, err)
}
return stdout.String(), nil
}
func killStarted(cmds []*exec.Cmd) {
killProcessGroups(cmds)
for _, item := range cmds {
if item.Process != nil {
_ = item.Wait()
}
}
}
func killProcessGroups(cmds []*exec.Cmd) {
for _, item := range cmds {
if item.Process != nil {
_ = syscall.Kill(-item.Process.Pid, syscall.SIGKILL)
}
}
}
func WithOutputFile(outputFile string) Option {
return func(s *CommandHelper) {
s.outputFile = outputFile
@@ -171,9 +357,9 @@ func WithWorkDir(workDir string) Option {
s.workDir = workDir
}
}
func WithScriptPath(scriptPath string) Option {
func WithEnv(env ...string) Option {
return func(s *CommandHelper) {
s.scriptPath = scriptPath
s.env = append(s.env, env...)
}
}
func WithIgnoreExist1() Option {
@@ -207,6 +393,10 @@ func (cw *CustomWriter) Flush() {
}
func handleErr(stdout, stderr bytes.Buffer, ignoreExist1 bool, err error) (string, error) {
return handleErrString(stdout.String(), stderr.String(), ignoreExist1, err)
}
func handleErrString(stdout, stderr string, ignoreExist1 bool, err error) (string, error) {
var exitError *exec.ExitError
if ignoreExist1 && errors.As(err, &exitError) {
if status, ok := exitError.Sys().(syscall.WaitStatus); ok {
@@ -215,16 +405,16 @@ func handleErr(stdout, stderr bytes.Buffer, ignoreExist1 bool, err error) (strin
}
}
}
errMsg := ""
if len(stderr.String()) != 0 {
errMsg = fmt.Sprintf("stderr: %s", stderr.String())
outItem := stdout
errItem := stderr
if len(errItem) != 0 && len(outItem) != 0 {
return outItem, fmt.Errorf("stdout: %s; stderr: %s, err: %v", outItem, errItem, err)
}
if len(stdout.String()) != 0 {
if len(errMsg) != 0 {
errMsg = fmt.Sprintf("%s; stdout: %s", errMsg, stdout.String())
} else {
errMsg = fmt.Sprintf("stdout: %s", stdout.String())
}
if len(errItem) != 0 {
return outItem, fmt.Errorf("stderr: %s, err: %v", errItem, err)
}
return errMsg, err
if len(outItem) != 0 {
return outItem, fmt.Errorf("stdout: %s, err: %v", outItem, err)
}
return "", err
}
+1 -20
View File
@@ -147,7 +147,7 @@ func UniqueUints(items []uint) []uint {
}
func LoadArch() (string, error) {
std, err := cmd.RunDefaultWithStdoutBashC("uname -a")
std, err := cmd.NewCommandMgr().RunWithStdout("uname", "-a")
if err != nil {
return "", fmt.Errorf("std: %s, err: %s", std, err.Error())
}
@@ -245,25 +245,6 @@ func HandleIPList(content string) ([]string, error) {
return res, nil
}
func LoadParams(param string) string {
stdout, err := cmd.RunDefaultWithStdoutBashCf("grep '^%s=' /usr/local/bin/1pctl | cut -d'=' -f2", param)
if err != nil {
panic(err)
}
info := strings.ReplaceAll(stdout, "\n", "")
if len(info) == 0 || info == `""` {
panic(fmt.Sprintf("error `%s` find in /usr/local/bin/1pctl", param))
}
return info
}
func LoadParamsWithoutPanic(param string) string {
stdout, err := cmd.RunDefaultWithStdoutBashCf("grep '^%s=' /usr/local/bin/1pctl | cut -d'=' -f2", param)
if err != nil {
return ""
}
return strings.ReplaceAll(stdout, "\n", "")
}
func GetRealClientIP(c *gin.Context) string {
addr := c.Request.RemoteAddr
if ip, _, err := net.SplitHostPort(addr); err == nil {
+11 -8
View File
@@ -13,18 +13,21 @@ func LoadTimeZoneByCmd() string {
if _, err := time.LoadLocation(loc); err != nil {
loc = "Asia/Shanghai"
}
std, err := cmd.RunDefaultWithStdoutBashC("timedatectl | grep 'Time zone'")
std, err := cmd.NewCommandMgr().RunWithStdout("timedatectl")
if err != nil {
return loc
}
fields := strings.Fields(string(std))
if len(fields) != 5 {
return loc
for _, line := range strings.Split(std, "\n") {
fields := strings.Fields(line)
if len(fields) != 5 || fields[0] != "Time" || fields[1] != "zone:" {
continue
}
if _, err := time.LoadLocation(fields[2]); err != nil {
return loc
}
return fields[2]
}
if _, err := time.LoadLocation(fields[2]); err != nil {
return loc
}
return fields[2]
return loc
}
func LoadExpiredLocation() *time.Location {
+1 -1
View File
@@ -21,7 +21,7 @@ func handlerErr(out string, err error) error {
func run(client *ssh.SSHClient, name string, args ...string) (string, error) {
if client == nil {
return cmd.NewCommandMgr(cmd.WithTimeout(10*time.Second)).RunWithStdoutBashCf("LANGUAGE=en_US:en %s %s", name, strings.Join(args, " "))
return cmd.NewCommandMgr(cmd.WithTimeout(10*time.Second), cmd.WithEnv("LANGUAGE=en_US:en")).RunWithStdout(name, args...)
}
return client.Runf("LANGUAGE=en_US:en %s %s", name, strings.Join(args, " "))
}
+28 -10
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/1Panel-dev/1Panel/core/utils/cmd"
"github.com/1Panel-dev/1Panel/core/utils/ssh"
@@ -22,18 +23,11 @@ func (s *Openrc) Name() string {
return "openrc"
}
func (s *Openrc) IsActive(serviceName string) (bool, error) {
out, err := cmd.RunDefaultWithStdoutBashCf("if service %s status >/dev/null 2>&1; then echo 'active'; else echo 'inactive'; fi", serviceName)
if err != nil {
return false, err
}
return out == "active\n", nil
_, err := cmd.NewCommandMgr().RunWithStdout("service", serviceName, "status")
return err == nil, nil
}
func (s *Openrc) IsEnable(serviceName string) (bool, error) {
out, err := cmd.RunDefaultWithStdoutBashCf("if ls /etc/rc*.d/S*%s >/dev/null 2>&1; then echo 'enabled'; else echo 'disabled'; fi", serviceName)
if err != nil {
return false, err
}
return out == "enabled\n", nil
return isSysvServiceEnabled(serviceName)
}
func (s *Openrc) IsExist(serviceName string) (bool, error) {
if _, err := os.Stat(filepath.Join("/etc/init.d", serviceName)); err != nil {
@@ -62,3 +56,27 @@ func (s *Openrc) Operate(operate, serviceName string) error {
func (s *Openrc) Reload() error {
return nil
}
func isSysvServiceEnabled(serviceName string) (bool, error) {
entries, err := os.ReadDir("/etc")
if err != nil {
return false, err
}
for _, entry := range entries {
name := entry.Name()
if !entry.IsDir() || !strings.HasPrefix(name, "rc") || !strings.HasSuffix(name, ".d") {
continue
}
items, err := os.ReadDir(filepath.Join("/etc", name))
if err != nil {
continue
}
for _, item := range items {
itemName := item.Name()
if strings.HasPrefix(itemName, "S") && strings.HasSuffix(itemName, serviceName) {
return true, nil
}
}
}
return false, nil
}
+3 -10
View File
@@ -22,18 +22,11 @@ func (s *Sysvinit) Name() string {
return "sysvinit"
}
func (s *Sysvinit) IsActive(serviceName string) (bool, error) {
out, err := cmd.RunDefaultWithStdoutBashCf("if service %s status >/dev/null 2>&1; then echo 'active'; else echo 'inactive'; fi", serviceName)
if err != nil {
return false, err
}
return out == "active\n", nil
_, err := cmd.NewCommandMgr().RunWithStdout("service", serviceName, "status")
return err == nil, nil
}
func (s *Sysvinit) IsEnable(serviceName string) (bool, error) {
out, err := cmd.RunDefaultWithStdoutBashCf("if ls /etc/rc*.d/S*%s >/dev/null 2>&1; then echo 'enabled'; else echo 'disabled'; fi", serviceName)
if err != nil {
return false, err
}
return out == "enabled\n", nil
return isSysvServiceEnabled(serviceName)
}
func (s *Sysvinit) IsExist(serviceName string) (bool, error) {
if _, err := os.Stat(filepath.Join("/etc/init.d", serviceName)); err != nil {
+88
View File
@@ -0,0 +1,88 @@
package ctl_conf
import (
"fmt"
"os"
"strings"
)
const defaultFile = "/usr/local/bin/1pctl"
func Load(key string) string {
info, err := LoadFromFile(defaultFile, key)
if err != nil {
panic(err)
}
if len(info) == 0 || info == `""` {
panic(fmt.Sprintf("error `%s` find in %s", key, defaultFile))
}
return info
}
func LoadWithoutPanic(key string) string {
info, err := LoadFromFile(defaultFile, key)
if err != nil {
return ""
}
return info
}
func LoadFromFile(filePath, key string) (string, error) {
data, err := os.ReadFile(filePath)
if err != nil {
return "", err
}
prefix := key + "="
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, prefix) {
return strings.TrimSpace(strings.TrimPrefix(line, prefix)), nil
}
}
return "", fmt.Errorf("error `%s` find in %s", key, filePath)
}
func UpdateInFile(filePath, key, value string) error {
return rewriteFile(filePath, func(line string) (string, bool) {
if strings.HasPrefix(line, key+"=") {
return key + "=" + value, true
}
return line, true
})
}
func RemoveValueFromFile(filePath, key, value string) error {
target := key + "=" + value
return rewriteFile(filePath, func(line string) (string, bool) {
return line, line != target
})
}
func rewriteFile(filePath string, rewrite func(string) (string, bool)) error {
info, err := os.Stat(filePath)
if err != nil {
return err
}
data, err := os.ReadFile(filePath)
if err != nil {
return err
}
content := rewriteLines(string(data), rewrite)
return os.WriteFile(filePath, []byte(content), info.Mode().Perm())
}
func rewriteLines(data string, rewrite func(string) (string, bool)) string {
hasTrailingNewline := strings.HasSuffix(data, "\n")
lines := strings.Split(strings.TrimSuffix(data, "\n"), "\n")
rewritten := make([]string, 0, len(lines))
for _, line := range lines {
newLine, keep := rewrite(line)
if keep {
rewritten = append(rewritten, newLine)
}
}
content := strings.Join(rewritten, "\n")
if hasTrailingNewline {
content += "\n"
}
return content
}
+90 -36
View File
@@ -61,13 +61,15 @@ func CopyItem(isDir, withName bool, src, dst string) error {
_ = os.MkdirAll(dst, srcInfo.Mode())
}
}
cmdStr := fmt.Sprintf(`cp -rf %s %s`, src, dst+"/")
cmdArgs := []string{"-rf", "--", src, dst + "/"}
cmdText := fmt.Sprintf("cp -rf -- %s %s", src, dst+"/")
if !isDir {
cmdStr = fmt.Sprintf(`cp -f %s %s`, src, dst+"/")
cmdArgs = []string{"-f", "--", src, dst + "/"}
cmdText = fmt.Sprintf("cp -f -- %s %s", src, dst+"/")
}
stdout, err := cmd.NewCommandMgr(cmd.WithTimeout(60 * time.Second)).RunWithStdoutBashC(cmdStr)
stdout, err := cmd.NewCommandMgr(cmd.WithTimeout(60*time.Second)).RunWithStdout("cp", cmdArgs...)
if err != nil {
return fmt.Errorf("handle %s failed, stdout: %s, err: %v", cmdStr, stdout, err)
return fmt.Errorf("handle %s failed, stdout: %s, err: %v", cmdText, stdout, err)
}
return nil
}
@@ -82,10 +84,11 @@ func CopyFileWithRename(src, dst string) error {
_ = os.MkdirAll(path.Dir(dst), srcInfo.Mode())
}
}
if err := cmd.RunDefaultBashCf("cp -f %s %s.tmp", src, dst); err != nil {
cmdMgr := cmd.NewCommandMgr()
if err := cmdMgr.Run("cp", "-f", "--", src, dst+".tmp"); err != nil {
return fmt.Errorf("handle cp file failed, err: %v", err)
}
if err = cmd.RunDefaultBashCf("mv %s.tmp %s", dst, dst); err != nil {
if err = cmdMgr.Run("mv", "--", dst+".tmp", dst); err != nil {
return err
}
return nil
@@ -98,9 +101,33 @@ func HandleTar(sourceDir, targetDir, name, exclusionRules string, secret string)
}
}
targetFile := path.Join(targetDir, name)
excludeText, excludeArgs := buildTarExcludeArgs(exclusionRules)
tarPathText, tarPathArgs := buildTarPathArgs(sourceDir)
if len(secret) != 0 {
logTarEncryptCommand(targetFile, excludeText, tarPathText, secret)
stdout, err := runTarEncrypt(targetFile, excludeArgs, tarPathArgs, secret)
if err != nil && len(stdout) != 0 {
global.LOG.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
return fmt.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
}
return nil
}
global.LOG.Debug(fmt.Sprintf("tar -zcf %s %s %s", targetFile, excludeText, tarPathText))
stdout, err := runTar(targetFile, excludeArgs, tarPathArgs)
if err != nil && len(stdout) != 0 {
global.LOG.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
return fmt.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
}
return nil
}
func buildTarExcludeArgs(exclusionRules string) (string, []string) {
exMap := make(map[string]struct{})
excludes := strings.Split(exclusionRules, ",")
excludeRules := ""
excludeArgs := []string{}
for _, exclude := range excludes {
if len(exclude) == 0 {
continue
@@ -109,39 +136,40 @@ func HandleTar(sourceDir, targetDir, name, exclusionRules string, secret string)
continue
}
excludeRules += fmt.Sprintf(" --exclude '%s'", exclude)
excludeArgs = append(excludeArgs, "--exclude", exclude)
exMap[exclude] = struct{}{}
}
path := ""
return excludeRules, excludeArgs
}
func buildTarPathArgs(sourceDir string) (string, []string) {
tarPath := ""
tarPathArgs := []string{}
if strings.Contains(sourceDir, "/") {
itemDir := strings.ReplaceAll(sourceDir[strings.LastIndex(sourceDir, "/"):], "/", "")
aheadDir := sourceDir[:strings.LastIndex(sourceDir, "/")]
if len(aheadDir) == 0 {
aheadDir = "/"
}
path += fmt.Sprintf("-C %s %s", aheadDir, itemDir)
tarPath += fmt.Sprintf("-C %s %s", aheadDir, itemDir)
tarPathArgs = append(tarPathArgs, "-C", aheadDir, itemDir)
} else {
path = sourceDir
tarPath = sourceDir
tarPathArgs = append(tarPathArgs, sourceDir)
}
return tarPath, tarPathArgs
}
commands := ""
func logTarEncryptCommand(targetFile, excludeRules, tarPath, secret string) {
extraCmd := "| openssl enc -aes-256-cbc -salt -k '" + secret + "' -out"
command := fmt.Sprintf("tar -zcf %s %s %s %s", " -"+excludeRules, tarPath, extraCmd, targetFile)
global.LOG.Debug(strings.ReplaceAll(command, fmt.Sprintf(" '%s' ", secret), " ****** "))
}
if len(secret) != 0 {
extraCmd := "| openssl enc -aes-256-cbc -salt -k '" + secret + "' -out"
commands = fmt.Sprintf("tar -zcf %s %s %s %s", " -"+excludeRules, path, extraCmd, targetDir+"/"+name)
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" '%s' ", secret), " ****** "))
} else {
commands = fmt.Sprintf("tar -zcf %s %s %s", targetDir+"/"+name, excludeRules, path)
global.LOG.Debug(commands)
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(24*time.Hour), cmd.WithIgnoreExist1())
stdout, err := cmdMgr.RunWithStdoutBashC(commands)
if err != nil {
if len(stdout) != 0 {
global.LOG.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
return fmt.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
}
}
return nil
func runTar(targetFile string, excludeArgs, tarPathArgs []string) (string, error) {
tarArgs := append([]string{"-zcf", targetFile}, excludeArgs...)
tarArgs = append(tarArgs, tarPathArgs...)
return cmd.NewCommandMgr(cmd.WithTimeout(24*time.Hour), cmd.WithIgnoreExist1()).RunWithStdout("tar", tarArgs...)
}
func HandleUnTar(sourceFile, targetDir string, secret string) error {
@@ -150,18 +178,18 @@ func HandleUnTar(sourceFile, targetDir string, secret string) error {
return err
}
}
commands := ""
if len(secret) != 0 {
extraCmd := "openssl enc -d -aes-256-cbc -k '" + secret + "' -in " + sourceFile + " | "
commands = fmt.Sprintf("%s tar -zxvf - -C %s", extraCmd, targetDir+" > /dev/null 2>&1")
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" '%s' ", secret), " ****** "))
} else {
commands = fmt.Sprintf("tar zxvf '%s' -C '%s'", sourceFile, targetDir)
global.LOG.Debug(commands)
logTarDecryptCommand(sourceFile, targetDir, secret)
stdout, err := runTarDecrypt(sourceFile, targetDir, secret)
if err != nil {
global.LOG.Errorf("do handle untar failed, stdout: %s, err: %v", stdout, err)
return errors.New(stdout)
}
return nil
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(24 * time.Hour))
stdout, err := cmdMgr.RunWithStdoutBashC(commands)
global.LOG.Debug(fmt.Sprintf("tar zxvf '%s' -C '%s'", sourceFile, targetDir))
stdout, err := runUnTar(sourceFile, targetDir)
if err != nil {
global.LOG.Errorf("do handle untar failed, stdout: %s, err: %v", stdout, err)
return errors.New(stdout)
@@ -169,6 +197,32 @@ func HandleUnTar(sourceFile, targetDir string, secret string) error {
return nil
}
func logTarDecryptCommand(sourceFile, targetDir, secret string) {
extraCmd := "openssl enc -d -aes-256-cbc -k '" + secret + "' -in " + sourceFile + " | "
command := fmt.Sprintf("%s tar -zxvf - -C %s", extraCmd, targetDir+" > /dev/null 2>&1")
global.LOG.Debug(strings.ReplaceAll(command, fmt.Sprintf(" '%s' ", secret), " ****** "))
}
func runUnTar(sourceFile, targetDir string) (string, error) {
return cmd.NewCommandMgr(cmd.WithTimeout(24*time.Hour)).RunWithStdout("tar", "zxf", sourceFile, "-C", targetDir)
}
func runTarEncrypt(targetFile string, excludeArgs, tarPathArgs []string, secret string) (string, error) {
tarArgs := append([]string{"-zcf", "-"}, excludeArgs...)
tarArgs = append(tarArgs, tarPathArgs...)
return cmd.NewCommandMgr(cmd.WithTimeout(24*time.Hour)).RunPipe(
cmd.PipeCommand{Name: "tar", Args: tarArgs},
cmd.PipeCommand{Name: "openssl", Args: []string{"enc", "-aes-256-cbc", "-salt", "-pass", "env:BACKUP_SECRET", "-out", targetFile}, Env: []string{"BACKUP_SECRET=" + secret}},
)
}
func runTarDecrypt(sourceFile, targetDir, secret string) (string, error) {
return cmd.NewCommandMgr(cmd.WithTimeout(24*time.Hour)).RunPipe(
cmd.PipeCommand{Name: "openssl", Args: []string{"enc", "-d", "-aes-256-cbc", "-pass", "env:BACKUP_SECRET", "-in", sourceFile}, Env: []string{"BACKUP_SECRET=" + secret}},
cmd.PipeCommand{Name: "tar", Args: []string{"-zxf", "-", "-C", targetDir}},
)
}
func DownloadFile(url, dst string) error {
resp, err := req_helper.HandleGet(url)
if err != nil {
+9 -8
View File
@@ -2,6 +2,7 @@ package firewall
import (
"fmt"
"strings"
"github.com/1Panel-dev/1Panel/core/utils/cmd"
)
@@ -9,7 +10,7 @@ import (
func UpdatePort(oldPort, newPort string) error {
firewalld := cmd.Which("firewalld")
if firewalld {
status, _ := cmd.RunDefaultWithStdoutBashC("LANGUAGE=en_US:en firewall-cmd --state")
status, _ := cmd.NewCommandMgr(cmd.WithEnv("LANGUAGE=en_US:en")).RunWithStdout("firewall-cmd", "--state")
isRunning := status == "running\n"
if isRunning {
return firewallUpdatePort(oldPort, newPort)
@@ -20,8 +21,8 @@ func UpdatePort(oldPort, newPort string) error {
if !ufw {
return nil
}
status, _ := cmd.RunDefaultWithStdoutBashC("LANGUAGE=en_US:en ufw status | grep Status")
isRuning := status == "Status: active\n"
status, _ := cmd.NewCommandMgr(cmd.WithEnv("LANGUAGE=en_US:en")).RunWithStdout("ufw", "status")
isRuning := strings.Contains(status, "Status: active")
if isRuning {
return ufwUpdatePort(oldPort, newPort)
}
@@ -29,22 +30,22 @@ func UpdatePort(oldPort, newPort string) error {
}
func firewallUpdatePort(oldPort, newPort string) error {
stdout, err := cmd.RunDefaultWithStdoutBashCf("firewall-cmd --zone=public --add-port=%s/tcp --permanent", newPort)
stdout, err := cmd.NewCommandMgr().RunWithStdout("firewall-cmd", "--zone=public", "--add-port="+newPort+"/tcp", "--permanent")
if err != nil {
return fmt.Errorf("add (port: %s/tcp) failed, err: %s", newPort, stdout)
}
_, _ = cmd.RunDefaultWithStdoutBashCf("firewall-cmd --zone=public --remove-port=%s/tcp --permanent", oldPort)
_, _ = cmd.RunDefaultWithStdoutBashC("firewall-cmd --reload")
_, _ = cmd.NewCommandMgr().RunWithStdout("firewall-cmd", "--zone=public", "--remove-port="+oldPort+"/tcp", "--permanent")
_, _ = cmd.NewCommandMgr().RunWithStdout("firewall-cmd", "--reload")
return nil
}
func ufwUpdatePort(oldPort, newPort string) error {
stdout, err := cmd.RunDefaultWithStdoutBashCf("ufw allow %s", newPort)
stdout, err := cmd.NewCommandMgr().RunWithStdout("ufw", "allow", newPort)
if err != nil {
return fmt.Errorf("add (port: %s/tcp) failed, err: %s", newPort, stdout)
}
_, _ = cmd.RunDefaultWithStdoutBashCf("ufw delete allow %s", oldPort)
_, _ = cmd.NewCommandMgr().RunWithStdout("ufw", "delete", "allow", oldPort)
return nil
}