Просмотр исходного кода

Fixed #TASK_QT-9810 对接日志

Carl 1 год назад
Родитель
Сommit
85479c4bae
86 измененных файлов с 8475 добавлено и 247 удалено
  1. 1 1
      Makefile
  2. 1 1
      cgroup/cgroup.go
  3. 1 1
      cgroup/io.go
  4. 1 1
      cgroup/utils.go
  5. 1 1
      common/net.go
  6. 3 3
      containers/apm_register_app.go
  7. 2 5
      containers/apm_white_list.go
  8. 1 1
      containers/cilium.go
  9. 1 1
      containers/conntrack.go
  10. 6 9
      containers/container.go
  11. 17 12
      containers/container_apm.go
  12. 1 1
      containers/containerd.go
  13. 1 1
      containers/crio.go
  14. 1 1
      containers/dotnet.go
  15. 1 1
      containers/jvm.go
  16. 1 1
      containers/l7.go
  17. 14 9
      containers/registry.go
  18. 15 13
      containers/stack.go
  19. 10 9
      containers/util.go
  20. 4 0
      dist/.gitignore
  21. 87 0
      dist/README.md
  22. 238 0
      dist/package_dir/bin/agentctl
  23. 6 0
      dist/package_dir/bin/start.sh
  24. 3 0
      dist/package_dir/bin/stop.sh
  25. 2926 0
      dist/scripts/install_temp.sh
  26. 611 0
      dist/scripts/package.sh
  27. 1111 0
      dist/scripts/uninstall.sh
  28. BIN
      dist/scripts/xzdec
  29. 1 1
      ebpftracer/init.go
  30. 6 8
      ebpftracer/jvm.go
  31. 4 5
      ebpftracer/stack.go
  32. 22 23
      ebpftracer/tls.go
  33. 11 14
      ebpftracer/tracer.go
  34. 1 1
      ebpftracer/tracer/allocate.go
  35. 2 2
      ebpftracer/tracer/filter.go
  36. 8 7
      ebpftracer/tracer/inject/inject_linux_amd64.go
  37. 9 8
      ebpftracer/tracer/offset.go
  38. 2 2
      ebpftracer/tracer/ptrace/ptrace_linux.go
  39. 29 30
      ebpftracer/tracer/socket.go
  40. 2 0
      go.mod
  41. 4 0
      go.sum
  42. 1 1
      logs/journald_reader.go
  43. 181 0
      logs/log.go
  44. 1 1
      logs/otel.go
  45. 1 1
      logs/tail_reader.go
  46. 34 19
      logs/tail_reader_test.go
  47. 1 1
      node/collector.go
  48. 1 1
      node/disk.go
  49. 1 1
      node/memory.go
  50. 1 1
      node/metadata/aws.go
  51. 1 1
      node/metadata/azure.go
  52. 1 1
      node/metadata/digital_ocean.go
  53. 1 1
      node/metadata/gcp.go
  54. 1 1
      node/metadata/hetzner.go
  55. 1 1
      node/metadata/metadata.go
  56. 1 1
      pinger/pinger.go
  57. 5 5
      pkg/go.opentelemetry.io/otel/example/otel-collector/go.mod
  58. 5 0
      pkg/go.opentelemetry.io/otel/example/otel-collector/go.sum
  59. 24 3
      pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/go.mod
  60. 101 8
      pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/go.sum
  61. 5 5
      pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/go.mod
  62. 5 0
      pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/go.sum
  63. 23 0
      pkg/lumberjack/.gitignore
  64. 6 0
      pkg/lumberjack/.travis.yml
  65. 21 0
      pkg/lumberjack/LICENSE
  66. 179 0
      pkg/lumberjack/README.md
  67. 11 0
      pkg/lumberjack/chown.go
  68. 19 0
      pkg/lumberjack/chown_linux.go
  69. 19 0
      pkg/lumberjack/example_test.go
  70. 9 0
      pkg/lumberjack/go.mod
  71. 205 0
      pkg/lumberjack/linux_test.go
  72. 562 0
      pkg/lumberjack/lumberjack.go
  73. 816 0
      pkg/lumberjack/lumberjack_test.go
  74. 27 0
      pkg/lumberjack/rotate_test.go
  75. 91 0
      pkg/lumberjack/testing_test.go
  76. 1 1
      profiling/profiling.go
  77. 1 1
      prom/agent.go
  78. 1 1
      prom/wrappers.go
  79. 2 1
      tracing/apm_tracing.go
  80. 3 3
      tracing/tracing.go
  81. 27 0
      utils/enums/enums.go
  82. 0 4
      utils/id.go
  83. 2 1
      utils/kernel.go
  84. 14 2
      utils/modelse/models.go
  85. 893 1
      utils/util.go
  86. 4 6
      utils/worker/serverWorker.go

+ 1 - 1
Makefile

@@ -18,7 +18,7 @@ endif
 all: c-build go-build
 all: c-build go-build
 
 
 build:
 build:
-	CGO_ENABLED=1 go build -gcflags="all=-N -l" -buildvcs=false -o euspace
+	CGO_ENABLED=1 go build -gcflags="all=-N -l" -buildvcs=false -o dist/package_dir/bin/euspace
 c:
 c:
 	docker exec -it 62d0676aa0b7 sh -c 'cd /opt/github/euspace/ebpftracer && make all ${PARAMS}'
 	docker exec -it 62d0676aa0b7 sh -c 'cd /opt/github/euspace/ebpftracer && make all ${PARAMS}'
 c-build: c
 c-build: c

+ 1 - 1
cgroup/cgroup.go

@@ -10,7 +10,7 @@ import (
 
 
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/flags"
 	"github.com/coroot/coroot-node-agent/flags"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 var (
 var (

+ 1 - 1
cgroup/io.go

@@ -6,7 +6,7 @@ import (
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 
 
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 type IOStat struct {
 type IOStat struct {

+ 1 - 1
cgroup/utils.go

@@ -5,7 +5,7 @@ import (
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 
 
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 func readVariablesFromFile(filePath string) (map[string]uint64, error) {
 func readVariablesFromFile(filePath string) (map[string]uint64, error) {

+ 1 - 1
common/net.go

@@ -5,8 +5,8 @@ import (
 	"strings"
 	"strings"
 
 
 	"github.com/coroot/coroot-node-agent/flags"
 	"github.com/coroot/coroot-node-agent/flags"
+	klog "github.com/sirupsen/logrus"
 	"inet.af/netaddr"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 )
 
 
 var (
 var (

+ 3 - 3
containers/apm_register_app.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"fmt"
 	"github.com/coroot/coroot-node-agent/utils"
 	"github.com/coroot/coroot-node-agent/utils"
 	. "github.com/coroot/coroot-node-agent/utils/modelse"
 	. "github.com/coroot/coroot-node-agent/utils/modelse"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 func (c *Container) RegisterAppInfo(r *Registry, pid uint32) error {
 func (c *Container) RegisterAppInfo(r *Registry, pid uint32) error {
@@ -20,7 +21,6 @@ func (c *Container) RegisterAppInfo(r *Registry, pid uint32) error {
 		}
 		}
 		c.AppInfo.ServiceName = c.AppInfo.CodeType.ServiceTypeString()
 		c.AppInfo.ServiceName = c.AppInfo.CodeType.ServiceTypeString()
 		// 注册
 		// 注册
-		fmt.Println("注册")
 		hostId, _ := utils.GetHostID()
 		hostId, _ := utils.GetHostID()
 		registerAppReq := RegisterAppReq{
 		registerAppReq := RegisterAppReq{
 			AppId:       c.AppInfo.AppId,
 			AppId:       c.AppInfo.AppId,
@@ -34,10 +34,10 @@ func (c *Container) RegisterAppInfo(r *Registry, pid uint32) error {
 			App_type:    1,
 			App_type:    1,
 			HostId:      hostId,
 			HostId:      hostId,
 		}
 		}
-		registerAppReq.Print()
-
+		klog.Infof("Register App: %s", registerAppReq.String())
 		err = r.connServer.RegisterApp(registerAppReq)
 		err = r.connServer.RegisterApp(registerAppReq)
 		if err != nil {
 		if err != nil {
+			klog.WithError(err).Errorf("Failed Register App %s", registerAppReq.String())
 			return err
 			return err
 		}
 		}
 
 

+ 2 - 5
containers/apm_white_list.go

@@ -1,9 +1,7 @@
 package containers
 package containers
 
 
 import (
 import (
-	"fmt"
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/common"
-	"github.com/coroot/coroot-node-agent/utils"
 	. "github.com/coroot/coroot-node-agent/utils/modelse"
 	. "github.com/coroot/coroot-node-agent/utils/modelse"
 	log "github.com/sirupsen/logrus"
 	log "github.com/sirupsen/logrus"
 )
 )
@@ -29,9 +27,8 @@ func (r *Registry) getWhiteList() (bool, error) {
 	if common.IsOpenFilter() {
 	if common.IsOpenFilter() {
 		return false, nil
 		return false, nil
 	}
 	}
-	utils.GetHostID()
-	hostId, _ := utils.GetHostID()
-	fmt.Println(hostId)
+	//hostId, _ := utils.GetHostID()
+	//fmt.Println(hostId)
 	whiteListReq := WhiteListReq{
 	whiteListReq := WhiteListReq{
 		HostId:    10154813500555812,
 		HostId:    10154813500555812,
 		AccountId: 110,
 		AccountId: 110,

+ 1 - 1
containers/cilium.go

@@ -11,8 +11,8 @@ import (
 	"github.com/cilium/cilium/pkg/tuple"
 	"github.com/cilium/cilium/pkg/tuple"
 	"github.com/cilium/cilium/pkg/u8proto"
 	"github.com/cilium/cilium/pkg/u8proto"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/coroot-node-agent/proc"
+	klog "github.com/sirupsen/logrus"
 	"inet.af/netaddr"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 )
 
 
 var (
 var (

+ 1 - 1
containers/conntrack.go

@@ -5,9 +5,9 @@ import (
 
 
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/florianl/go-conntrack"
 	"github.com/florianl/go-conntrack"
+	klog "github.com/sirupsen/logrus"
 	"github.com/vishvananda/netns"
 	"github.com/vishvananda/netns"
 	"inet.af/netaddr"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 )
 
 
 type Conntrack struct {
 type Conntrack struct {

+ 6 - 9
containers/container.go

@@ -2,7 +2,6 @@ package containers
 
 
 import (
 import (
 	debugelf "debug/elf"
 	debugelf "debug/elf"
-	"fmt"
 	. "github.com/coroot/coroot-node-agent/utils/modelse"
 	. "github.com/coroot/coroot-node-agent/utils/modelse"
 	"os"
 	"os"
 	"strings"
 	"strings"
@@ -24,9 +23,9 @@ import (
 	"github.com/coroot/coroot-node-agent/tracing"
 	"github.com/coroot/coroot-node-agent/tracing"
 	"github.com/coroot/logparser"
 	"github.com/coroot/logparser"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus"
+	klog "github.com/sirupsen/logrus"
 	"github.com/vishvananda/netns"
 	"github.com/vishvananda/netns"
 	"inet.af/netaddr"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 )
 
 
 var (
 var (
@@ -903,7 +902,7 @@ func (c *Container) runLogParser(logPath string) {
 			parser.Stop()
 			parser.Stop()
 			return
 			return
 		}
 		}
-		klog.InfoS("started varlog logparser", "cg", c.cgroup.Id, "log", logPath)
+		klog.Infoln("started varlog logparser", "cg", c.cgroup.Id, "log", logPath)
 		c.logParsers[logPath] = &LogParser{parser: parser, stop: reader.Stop}
 		c.logParsers[logPath] = &LogParser{parser: parser, stop: reader.Stop}
 		return
 		return
 	}
 	}
@@ -919,7 +918,7 @@ func (c *Container) runLogParser(logPath string) {
 		stop := func() {
 		stop := func() {
 			JournaldUnsubscribe(c.cgroup)
 			JournaldUnsubscribe(c.cgroup)
 		}
 		}
-		klog.InfoS("started journald logparser", "cg", c.cgroup.Id)
+		klog.Infoln("started journald logparser", "cg", c.cgroup.Id)
 		c.logParsers["journald"] = &LogParser{parser: parser, stop: stop}
 		c.logParsers["journald"] = &LogParser{parser: parser, stop: stop}
 
 
 	case cgroup.ContainerTypeDocker, cgroup.ContainerTypeContainerd, cgroup.ContainerTypeCrio:
 	case cgroup.ContainerTypeDocker, cgroup.ContainerTypeContainerd, cgroup.ContainerTypeCrio:
@@ -938,7 +937,7 @@ func (c *Container) runLogParser(logPath string) {
 			parser.Stop()
 			parser.Stop()
 			return
 			return
 		}
 		}
-		klog.InfoS("started container logparser", "cg", c.cgroup.Id)
+		klog.Infoln("started container logparser", "cg", c.cgroup.Id)
 		c.logParsers["stdout/stderr"] = &LogParser{parser: parser, stop: reader.Stop}
 		c.logParsers["stdout/stderr"] = &LogParser{parser: parser, stop: reader.Stop}
 	}
 	}
 }
 }
@@ -1102,7 +1101,6 @@ func (c *Container) attachUprobes(tracer *ebpftracer.Tracer, pid uint32) error {
 		return nil
 		return nil
 	}
 	}
 	codeType := c.GetCodeTypeFromCache(pid)
 	codeType := c.GetCodeTypeFromCache(pid)
-	fmt.Println("=============attachUprobes", codeType.String())
 	if codeType.IsUnknownCode() {
 	if codeType.IsUnknownCode() {
 		return nil
 		return nil
 	}
 	}
@@ -1118,7 +1116,6 @@ func (c *Container) attachUprobes(tracer *ebpftracer.Tracer, pid uint32) error {
 		err = c.attachNetCoreUprobes(tracer, pid)
 		err = c.attachNetCoreUprobes(tracer, pid)
 	}
 	}
 	if err != nil {
 	if err != nil {
-		fmt.Println(err)
 		c.errorClose(pid)
 		c.errorClose(pid)
 		return err
 		return err
 	}
 	}
@@ -1170,8 +1167,8 @@ func (c *Container) attachJVMUprobes(tracer *ebpftracer.Tracer, pid uint32) erro
 	if p == nil {
 	if p == nil {
 		return nil
 		return nil
 	}
 	}
+	codeType := c.GetCodeTypeFromCache(pid)
 	if !p.jvmUprobesChecked {
 	if !p.jvmUprobesChecked {
-		codeType := c.GetCodeTypeFromCache(pid)
 		tracer.InitKProcInfo(pid, c.instanceID, uint16(codeType))
 		tracer.InitKProcInfo(pid, c.instanceID, uint16(codeType))
 
 
 		libNioProbes, err := tracer.AttachJavaNioReadUprobes(pid, codeType)
 		libNioProbes, err := tracer.AttachJavaNioReadUprobes(pid, codeType)
@@ -1189,7 +1186,7 @@ func (c *Container) attachJVMUprobes(tracer *ebpftracer.Tracer, pid uint32) erro
 		p.uprobes = append(p.uprobes, libNetProbes...)
 		p.uprobes = append(p.uprobes, libNetProbes...)
 		p.jvmUprobesChecked = true
 		p.jvmUprobesChecked = true
 	} else {
 	} else {
-		fmt.Println("[attach] already attach.")
+		klog.Infof("[attach] %s-%d already attach", codeType.String(), pid)
 	}
 	}
 	return nil
 	return nil
 }
 }

+ 17 - 12
containers/container_apm.go

@@ -12,6 +12,7 @@ import (
 	"github.com/coroot/coroot-node-agent/tracing"
 	"github.com/coroot/coroot-node-agent/tracing"
 	"github.com/coroot/coroot-node-agent/utils"
 	"github.com/coroot/coroot-node-agent/utils"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
+	klog "github.com/sirupsen/logrus"
 	"inet.af/netaddr"
 	"inet.af/netaddr"
 	"os"
 	"os"
 	"sort"
 	"sort"
@@ -67,7 +68,7 @@ func (c *Container) InitTrace(traceId uint64, r *l7.RequestData) error {
 func (c *Container) SendEvent(t *tracing.Trace, traceID uint64) {
 func (c *Container) SendEvent(t *tracing.Trace, traceID uint64) {
 	if t.AllEventReady(traceID) {
 	if t.AllEventReady(traceID) {
 		t.SendEvent()
 		t.SendEvent()
-		fmt.Printf("----send:%d \n", traceID)
+		klog.Infof("SendEvent %d", traceID)
 		//fmt.Println(t.GetSpan())
 		//fmt.Println(t.GetSpan())
 		//fmt.Println("===============")
 		//fmt.Println("===============")
 		delete(c.traceMap, traceID)
 		delete(c.traceMap, traceID)
@@ -90,12 +91,8 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 	}
 	}
 
 
 	if r.Protocol == l7.ProtocolTrace {
 	if r.Protocol == l7.ProtocolTrace {
-		//fmt.Println("r.TraceStart:", r.TraceStart)
-		//fmt.Println("r.TraceEnd:", r.TraceEnd)
-
 		if r.TraceStart == 1 {
 		if r.TraceStart == 1 {
-			fmt.Println("====ProtocolTrace start====", r.TraceId)
-
+			klog.Infof("====ProtocolTrace start==== %d %d", pid, r.TraceId)
 			trace, err := c.getOrInitTrace(r.TraceId)
 			trace, err := c.getOrInitTrace(r.TraceId)
 			if err == nil {
 			if err == nil {
 				method, path, hostIp, port := l7.ParseHttpHost(r.Payload)
 				method, path, hostIp, port := l7.ParseHttpHost(r.Payload)
@@ -108,7 +105,7 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 			return nil
 			return nil
 		}
 		}
 		if r.TraceEnd == 1 {
 		if r.TraceEnd == 1 {
-			fmt.Println("====ProtocolTrace end====", r.TraceId, r.EventCount)
+			klog.Infof("====ProtocolTrace end==== %d %d", pid, r.TraceId)
 			trace, err := c.getOrInitTrace(r.TraceId)
 			trace, err := c.getOrInitTrace(r.TraceId)
 			if err == nil {
 			if err == nil {
 				trace.TraceEndEvent(r)
 				trace.TraceEndEvent(r)
@@ -447,23 +444,31 @@ func (c *Container) verifyAttachConditions(r *Registry, pid uint32) bool {
 	p := c.processes[pid]
 	p := c.processes[pid]
 	if p != nil && c.checkEventReady() {
 	if p != nil && c.checkEventReady() {
 		codeType := c.GetCodeTypeFromCache(pid)
 		codeType := c.GetCodeTypeFromCache(pid)
+		if codeType.IsUnknownCode() {
+			klog.WithField("pid", pid).Infof("[verify] unknown language.")
+			return false
+		}
 		cmdline := p.GetCmdline()
 		cmdline := p.GetCmdline()
-
 		if len(cmdline) == 0 {
 		if len(cmdline) == 0 {
 			return false
 			return false
 		}
 		}
-		fmt.Println(r.getWhiteListByCodeType(codeType))
+		whiteListByCode := r.getWhiteListByCodeType(codeType)
+		klog.WithField("pid", pid).WithField("codeType", codeType.String()).
+			Infof("[verify] white list %v", whiteListByCode)
 		// 当前语言的白名单规则
 		// 当前语言的白名单规则
-		for _, setting := range r.getWhiteListByCodeType(codeType) {
+		for _, setting := range whiteListByCode {
 			ruleVal := setting.Filters
 			ruleVal := setting.Filters
 			if ruleVal == "" {
 			if ruleVal == "" {
 				continue
 				continue
 			}
 			}
-			fmt.Println("strings.Contains(cmdline, ruleVal)")
 			// 判断规则
 			// 判断规则
 			if strings.Contains(cmdline, ruleVal) {
 			if strings.Contains(cmdline, ruleVal) {
 				c.WhiteSettingInfo = setting
 				c.WhiteSettingInfo = setting
-				fmt.Printf("checkEventReady %v [%d] cmdline='%s' len(cmdline)[%d] %s\n", c.checkEventReady(), pid, cmdline, len(cmdline), ruleVal)
+				klog.WithField("pid", pid).
+					WithField("ruleVal", ruleVal).
+					WithField("cmdline", cmdline).
+					WithField("event ready", c.checkEventReady()).
+					Infoln("[verify] check successful.")
 				return true
 				return true
 			}
 			}
 		}
 		}

+ 1 - 1
containers/containerd.go

@@ -13,7 +13,7 @@ import (
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/logparser"
 	"github.com/coroot/logparser"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 const containerdTimeout = 30 * time.Second
 const containerdTimeout = 30 * time.Second

+ 1 - 1
containers/crio.go

@@ -13,7 +13,7 @@ import (
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/logparser"
 	"github.com/coroot/logparser"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 const crioTimeout = 30 * time.Second
 const crioTimeout = 30 * time.Second

+ 1 - 1
containers/dotnet.go

@@ -19,7 +19,7 @@ import (
 	"github.com/pyroscope-io/dotnetdiag"
 	"github.com/pyroscope-io/dotnetdiag"
 	"github.com/pyroscope-io/dotnetdiag/nettrace"
 	"github.com/pyroscope-io/dotnetdiag/nettrace"
 	"github.com/pyroscope-io/dotnetdiag/nettrace/typecode"
 	"github.com/pyroscope-io/dotnetdiag/nettrace/typecode"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 const (
 const (

+ 1 - 1
containers/jvm.go

@@ -10,8 +10,8 @@ import (
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus"
+	klog "github.com/sirupsen/logrus"
 	"github.com/xin053/hsperfdata"
 	"github.com/xin053/hsperfdata"
-	"k8s.io/klog/v2"
 )
 )
 
 
 func isJvm(cmdline []byte) bool {
 func isJvm(cmdline []byte) bool {

+ 1 - 1
containers/l7.go

@@ -5,8 +5,8 @@ import (
 
 
 	"github.com/coroot/coroot-node-agent/ebpftracer/l7"
 	"github.com/coroot/coroot-node-agent/ebpftracer/l7"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus"
+	klog "github.com/sirupsen/logrus"
 	"inet.af/netaddr"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 )
 
 
 type L7Metrics struct {
 type L7Metrics struct {

+ 14 - 9
containers/registry.go

@@ -20,9 +20,9 @@ import (
 	"github.com/coroot/coroot-node-agent/flags"
 	"github.com/coroot/coroot-node-agent/flags"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus"
+	klog "github.com/sirupsen/logrus"
 	"github.com/vishvananda/netns"
 	"github.com/vishvananda/netns"
 	"inet.af/netaddr"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 )
 
 
 var (
 var (
@@ -180,7 +180,6 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 	for {
 	for {
 		select {
 		select {
 		case now := <-gcTicker.C:
 		case now := <-gcTicker.C:
-			// todo IDS
 			_, err := r.getWhiteList()
 			_, err := r.getWhiteList()
 			if err != nil {
 			if err != nil {
 				log.WithError(err).Errorf("connWhiteList error")
 				log.WithError(err).Errorf("connWhiteList error")
@@ -191,14 +190,20 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 					if verifyAttachConditions {
 					if verifyAttachConditions {
 						err = c.RegisterAppInfo(r, pid)
 						err = c.RegisterAppInfo(r, pid)
 						if err == nil {
 						if err == nil {
-							fmt.Println("start attcah -------", pid)
+							klog.WithField("pid", pid).Infoln("[Registry] Attach uprobes.")
 							err = c.attachUprobes(r.tracer, pid)
 							err = c.attachUprobes(r.tracer, pid)
+							if err != nil {
+								klog.WithField("pid", pid).WithError(err).Errorf("[Registry] Failed attach uprobes error!")
+							} else {
+								klog.WithField("pid", pid).Infoln("[Registry] Attach uprobes success!")
+							}
+							klog.WithField("pid", pid).Infoln("[Registry] Attach app stack.")
 							err = c.stackTrace(r.tracer, pid)
 							err = c.stackTrace(r.tracer, pid)
 							if err != nil {
 							if err != nil {
-								klog.Errorf("Stack trace error", err)
+								klog.WithField("pid", pid).WithError(err).Errorf("[Registry][end] Failed attach stack trace!")
+							} else {
+								klog.WithField("pid", pid).Infoln("[Registry] Attach Stack success!")
 							}
 							}
-						} else {
-							klog.Warningln(err)
 						}
 						}
 					}
 					}
 
 
@@ -428,9 +433,9 @@ func (r *Registry) getOrCreateContainer(pid uint32) *Container {
 	//klog.Infof("calculated container id %d -> %s -> %s", pid, cg.Id, id)
 	//klog.Infof("calculated container id %d -> %s -> %s", pid, cg.Id, id)
 	if id == "" {
 	if id == "" {
 		if cg.Id == "/init.scope" && pid != 1 {
 		if cg.Id == "/init.scope" && pid != 1 {
-			klog.InfoS("ignoring without persisting", "cg", cg.Id, "pid", pid)
+			klog.Infoln("ignoring without persisting", "cg", cg.Id, "pid", pid)
 		} else {
 		} else {
-			klog.InfoS("ignoring", "cg", cg.Id, "pid", pid)
+			klog.Infoln("ignoring", "cg", cg.Id, "pid", pid)
 			r.containersByPid[pid] = nil
 			r.containersByPid[pid] = nil
 		}
 		}
 		return nil
 		return nil
@@ -458,7 +463,7 @@ func (r *Registry) getOrCreateContainer(pid uint32) *Container {
 		return nil
 		return nil
 	}
 	}
 
 
-	//klog.InfoS("detected a new container", "pid", pid, "cg", cg.Id, "id", id)
+	//klog.Infoln("detected a new container", "pid", pid, "cg", cg.Id, "id", id)
 	// add ns/workload/podname/pid/ctype
 	// add ns/workload/podname/pid/ctype
 	//sType := fmt.Sprintf("%d", cg.ContainerType)
 	//sType := fmt.Sprintf("%d", cg.ContainerType)
 
 

+ 15 - 13
containers/stack.go

@@ -10,6 +10,7 @@ import (
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer"
 	tracerelf "github.com/coroot/coroot-node-agent/ebpftracer/tracer"
 	tracerelf "github.com/coroot/coroot-node-agent/ebpftracer/tracer"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/coroot-node-agent/proc"
+	klog "github.com/sirupsen/logrus"
 	"golang.org/x/arch/arm64/arm64asm"
 	"golang.org/x/arch/arm64/arm64asm"
 	"golang.org/x/arch/x86/x86asm"
 	"golang.org/x/arch/x86/x86asm"
 	"io"
 	"io"
@@ -69,7 +70,7 @@ func (c *Container) stackTrace(tracer *ebpftracer.Tracer, pid uint32) error {
 		binType = BIN_TYPE
 		binType = BIN_TYPE
 	}
 	}
 
 
-	fmt.Println("UprobesMatchString:::init", MatchString)
+	klog.Infoln("[stack] UprobesMatchString:::init", MatchString)
 	path := proc.Path(uint32(pid), "exe")
 	path := proc.Path(uint32(pid), "exe")
 
 
 	if dbgpath != "" {
 	if dbgpath != "" {
@@ -79,9 +80,9 @@ func (c *Container) stackTrace(tracer *ebpftracer.Tracer, pid uint32) error {
 	}
 	}
 
 
 	c.UprobesMap = map[string]tracerelf.Uprobe{}
 	c.UprobesMap = map[string]tracerelf.Uprobe{}
-	fmt.Println("UprobesMap:::init")
+	klog.Infoln("[stack] UprobesMap start")
 	for _, up := range c.Uprobes {
 	for _, up := range c.Uprobes {
-		fmt.Println("UprobesMap:::", up.Funcname, up.Address, up.AbsOffset)
+		klog.Debugf("[stack] UprobesMap %s %d %d", up.Funcname, up.Address, up.AbsOffset)
 		c.UprobesMap[fmt.Sprintf("%s-%s", up.Funcname, up.Address+up.AbsOffset)] = up
 		c.UprobesMap[fmt.Sprintf("%s-%s", up.Funcname, up.Address+up.AbsOffset)] = up
 	}
 	}
 	//codeType := c.GetCodeTypeFromCache(pid)
 	//codeType := c.GetCodeTypeFromCache(pid)
@@ -265,18 +266,18 @@ func (c *Container) getUprobes(path string, MatchString string) ([]tracer.Uprobe
 	// 符号表组装成键值 map,方便使用
 	// 符号表组装成键值 map,方便使用
 	symnames := map[string]debugelf.Symbol{}
 	symnames := map[string]debugelf.Symbol{}
 	for _, symbol := range symbols {
 	for _, symbol := range symbols {
-		fmt.Println(symbol.Name, symbol)
+		klog.Debugf("[stack] %v %v", symbol.Name, symbol)
 		symnames[symbol.Name] = symbol
 		symnames[symbol.Name] = symbol
 	}
 	}
 
 
 	textSection := elfFile.Section(".text")
 	textSection := elfFile.Section(".text")
 	if textSection == nil {
 	if textSection == nil {
-		fmt.Println("no text section", nil)
+		klog.Infoln("[stack] no text section")
 		return nil, nil
 		return nil, nil
 	}
 	}
 	textSectionData, err := textSection.Data()
 	textSectionData, err := textSection.Data()
 	if err != nil {
 	if err != nil {
-		fmt.Println("failed to read text section", err)
+		klog.WithError(err).Errorf("[stack] Failed to read text section")
 		return nil, nil
 		return nil, nil
 	}
 	}
 	textSectionLen := uint64(len(textSectionData) - 1)
 	textSectionLen := uint64(len(textSectionData) - 1)
@@ -292,19 +293,20 @@ func (c *Container) getUprobes(path string, MatchString string) ([]tracer.Uprobe
 		// found, err := regexp.MatchString("main.*", symbol.Name)
 		// found, err := regexp.MatchString("main.*", symbol.Name)
 
 
 		if err != nil {
 		if err != nil {
-			log.Fatal(err)
+			klog.WithError(err).Errorln("[stack] found error")
+			return nil, err
 		}
 		}
 
 
 		if found {
 		if found {
 			// 匹配到了加入 attachFuncs 列表
 			// 匹配到了加入 attachFuncs 列表
-			fmt.Printf("Fuck This: %s, %x\n", symbol.Name, symbol.Value)
+			klog.Debugf("[stack] Fuck This: %s, %x", symbol.Name, symbol.Value)
 			// attachFuncs = append(attachFuncs, symbol.Name)
 			// attachFuncs = append(attachFuncs, symbol.Name)
 			// 根据函数名拿到当前函数的符号结构体
 			// 根据函数名拿到当前函数的符号结构体
 			sym := symnames[symbol.Name]
 			sym := symnames[symbol.Name]
-			if err != nil {
-				fmt.Printf("symnames[symbol.Name]", symbol.Name, err)
-				return nil, err
-			}
+			//if err != nil {
+			//	klog.WithError(err).Errorf("symnames[symbol.Name] %s", symbol.Name)
+			//	return nil, err
+			//}
 
 
 			address := sym.Value
 			address := sym.Value
 			for _, p := range elfFile.Progs {
 			for _, p := range elfFile.Progs {
@@ -356,7 +358,7 @@ func getRbpEnterOffsets(machine elf.Machine, instructions []byte) int {
 		for i := 0; i < len(instructions); {
 		for i := 0; i < len(instructions); {
 			ins, err := x86asm.Decode(instructions[i:], 64)
 			ins, err := x86asm.Decode(instructions[i:], 64)
 			if err == nil && ins.Op == x86asm.LEA && ins.Args[0].String() == "RBP" {
 			if err == nil && ins.Op == x86asm.LEA && ins.Args[0].String() == "RBP" {
-				fmt.Printf("getRbpEnterOffsets: %v, %s, %s\n", ins, ins.Args[0].String(), ins.Args[1].String())
+				klog.Infof("[stack] getRbpEnterOffsets: %v, %s, %s", ins, ins.Args[0].String(), ins.Args[1].String())
 				return i
 				return i
 			}
 			}
 			i += ins.Len
 			i += ins.Len

+ 10 - 9
containers/util.go

@@ -4,8 +4,8 @@ import (
 	"debug/elf"
 	"debug/elf"
 	"fmt"
 	"fmt"
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/common"
+	klog "github.com/sirupsen/logrus"
 	"io/ioutil"
 	"io/ioutil"
-	"log"
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"
 	"regexp"
 	"regexp"
@@ -18,28 +18,29 @@ var libjvmRegex = regexp.MustCompile(`.*/libjvm\.so`)
 func GetExeType(pid uint32) common.CodeType {
 func GetExeType(pid uint32) common.CodeType {
 	mapsFilePath := fmt.Sprintf("/proc/%d/maps", pid)
 	mapsFilePath := fmt.Sprintf("/proc/%d/maps", pid)
 
 
-	data, err := ioutil.ReadFile(mapsFilePath)
+	data, err := os.ReadFile(mapsFilePath)
 	if err != nil {
 	if err != nil {
-		log.Fatalf("Failed to read %s: %s", mapsFilePath, err)
+		klog.WithError(err).Errorf("Failed to read %s: %s", mapsFilePath)
+		return common.CodeTypeUnknown
 	}
 	}
 
 
 	content := string(data)
 	content := string(data)
 
 
 	if libjvmRegex.MatchString(content) {
 	if libjvmRegex.MatchString(content) {
-		fmt.Println("is java process")
+		//fmt.Println("is java process")
 		if isJavaAotProcess(pid) {
 		if isJavaAotProcess(pid) {
-			fmt.Println("is javaAot process")
+			//fmt.Println("is javaAot process")
 			return common.CodeTypeJavaAot
 			return common.CodeTypeJavaAot
 		}
 		}
 		return common.CodeTypeJava
 		return common.CodeTypeJava
 	} else if isJavaAotProcess(pid) {
 	} else if isJavaAotProcess(pid) {
-		fmt.Println("is javaAot process")
+		//fmt.Println("is javaAot process")
 		return common.CodeTypeJavaAot
 		return common.CodeTypeJavaAot
 	} else if isGoProcess(pid) {
 	} else if isGoProcess(pid) {
-		fmt.Println("is go process")
+		//fmt.Println("is go process")
 		return common.CodeTypeGo
 		return common.CodeTypeGo
 	} else if isNetCoreProcess(pid) {
 	} else if isNetCoreProcess(pid) {
-		fmt.Println("is netcore process")
+		//	fmt.Println("is netcore process")
 		return common.CodeTypeNetCoreAot
 		return common.CodeTypeNetCoreAot
 	}
 	}
 	return common.CodeTypeUnknown
 	return common.CodeTypeUnknown
@@ -105,7 +106,7 @@ func isGoProcess(pid uint32) bool {
 
 
 	gopclntabSection := ef.Section(".gopclntab")
 	gopclntabSection := ef.Section(".gopclntab")
 	if gopclntabSection != nil {
 	if gopclntabSection != nil {
-		fmt.Println("is a go process")
+		//fmt.Println("is a go process")
 		return true
 		return true
 	}
 	}
 	return false
 	return false

+ 4 - 0
dist/.gitignore

@@ -0,0 +1,4 @@
+.idea
+installer.version
+Cloudwise-euspace-installer*.sh
+logs/*

+ 87 - 0
dist/README.md

@@ -0,0 +1,87 @@
+
+## package.sh 介绍
+
+[scripts/package.sh](../../scripts/package.sh) 主要是将 SmartAgent 安装目录下所有内容(脚本无自动去除无用目录或内容功能)压缩,
+通过`base64`编码后写入 [scripts/install_temp.sh](../../scripts/install_temp.sh) 中,
+最终生成安装文件 `cwserveragent-installer-version.sh` (version为SmartSgent版本)。
+
+### 安装帮助
+
+执行命令 `sh scripts/package.sh -h` 即可查看安装帮助详情。详情如下:
+
+```
+Usage: package.sh [-h] [-v] [DEBUG=true|false] [AGENT_VERSION=8.6]
+
+
+-h, --help     Display this help and exit.
+-v, --version  Print version and exit.
+
+RELEASE        Default true; Whether to delete the publish parameter in the script.
+VERSION        Configure the Cloudwise SmartAgent version.
+DEBUG          Default false; 1、Debug mode executed script package.sh;
+               2、Remove the debug log from the scripts (Cloudwise-SmartAgent-Linux-1.2.0.sh、smartagent、uninstall.sh).
+
+```
+
+### smartagent目录结构
+
+打包前目录结构(请保持目录及内容最简)
+```
+smartagent
+├── cwserveragent-installer-1.1.0.sh
+├── bin
+│   ├── CW-ServerAgent
+│   └── safe-rm
+├── conf
+│   └── server-agent.ini
+└── scripts
+    ├── install_temp.sh
+    ├── package.sh
+    ├── cw-serveragent
+    ├── uninstall.sh
+    ├── uninstall_temp.sh
+    └── xzdec
+```
+
+打包后目录结构
+```
+smartagent
+├── cwserveragent-installer-1.1.0.sh
+├── installer.version
+├── bin
+│   ├── CW-ServerAgent
+│   └── safe-rm
+├── conf
+│   └── server-agent.ini
+└── scripts
+    ├── install_temp.sh
+    ├── package.sh
+    ├── cw-serveragent
+    ├── uninstall.sh
+    ├── uninstall_temp.sh
+    └── xzdec
+```
+
+安装后目录结构
+
+```
+├── bin
+│   ├── CW-ServerAgent
+│   └── safe-rm
+├── conf
+│   ├── cwserveragent.conf
+│   ├── installation.conf
+│   └── server-agent.ini
+├── installer.version
+├── logs
+│   └── serveragent
+│       ├── CW-ServerAgent.error.log
+│       ├── CW-ServerAgent.fatal.log
+│       └── CW-ServerAgent.panic.log
+├── runtime
+│   └── .pid
+├── scripts
+│   ├── cw-serveragent
+│   └── uninstall.sh
+└── uninstall.sh
+```

+ 238 - 0
dist/package_dir/bin/agentctl

@@ -0,0 +1,238 @@
+#!/bin/bash
+
+#== 脚本执行中遇到不存在的变量就报错
+set -o nounset
+#== 脚本执行中报错即刻退出
+set -o errexit
+#== 脚本执行只要一个子命令失败,整个管道命令就失败
+set -o pipefail
+
+readonly BRAND_FORMAL_NAME="Cloudwise"
+readonly BRAND_PRODUCT_NAME="euspace"
+readonly BRAND_PARENT_PRODUCT_NAME="omniagent"
+readonly BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME}"
+readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
+readonly BRAND_PRODUCT_NAME_LOWER="euspace"
+#== **********************************************************
+#==  配置目录
+#== **********************************************************
+readonly INSTALL_BASE=/opt
+#== /opt/cloudwise/omniagent/agents 目录
+readonly BASE_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents
+#== cloudwise/omniagent/agents/agent
+readonly INSTALL_FOLDER=${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents/${BRAND_PRODUCT_NAME_LOWER}
+#== cloudwise/omniagent/agents/agent/version
+readonly INSTALL_VERSION_FOLDER=${INSTALL_FOLDER}/current
+#== /opt/cloudwise/omniagent/agents/agent/version
+readonly INSTALL_DIR=${INSTALL_BASE}/${INSTALL_VERSION_FOLDER}
+#== /opt/cloudwise/omniagent/agents/agent
+readonly AGENT_BASE_DIR=${INSTALL_BASE}/${INSTALL_FOLDER}
+
+readonly AGENT_BIN_DIR="${INSTALL_DIR}/bin"
+readonly AGENT_LOG_DIR="${INSTALL_DIR}/logs"
+
+#readonly AGENT_CONF_DIR="${INSTALL_DIR}/conf"
+#readonly MAIN_CONF_FILE="${AGENT_CONF_DIR}/${AGENT_MAIN_CONF}"
+readonly EXIT_CODE_OK=0
+readonly EXIT_CODE_GENERIC_ERROR=1
+
+#== **********************************************************
+#==  配置重要文件名称
+#== **********************************************************
+readonly AGENT_PROC="euspace"
+readonly AGENT_CONFIG_PATH=""
+
+readonly AGENT_PID_FILE="${INSTALL_DIR}/bin/${AGENT_PROC}.pid"
+
+
+getDaemonPid(){
+    local agent="${1}"
+    #== 约定pid文件
+    local pidFilePath="${AGENT_PID_FILE}"
+    #== 二进制文件绝对路径
+    # shellcheck disable=SC2155
+    local procPath="$(readlink -m "${AGENT_BIN_DIR}/${agent}" 2>&1)"
+    local cmdline
+    local pid
+    #== 查询pid文件
+    if [ -s "${pidFilePath}" ] ; then
+      pid="$(cat "${pidFilePath}" 2>&1)"
+      cmdline="$(cat /proc/${pid}/cmdline 2>&1 | xargs -0 echo -n)"
+      if [ -d "/proc/${pid}" ] && ( [[ ${cmdline} =~ ${procPath} ]] || [[ ${cmdline} =~ ${AGENT_BIN_DIR}/${agent} ]] ); then
+#        toLogInfo "[${agent}] Find pid by ${pidFilePath} [content:${pid}][cmdline:${cmdline}]"
+          printf '%s' ${pid}
+          return 0
+      else
+        #== 进程绝对路径查询
+#        toLogWarning "[${agent}] Pidfile exist [content:${pid}]. But process not find or not myself. [cmdline:${cmdline}]"
+        if ! pid="$(listProcesses "${agent} pid" "${procPath}|${AGENT_BIN_DIR}/${agent}" "sudo|tail")"; then
+#          toLogWarning "[${agent}] Not find process grep '${procPath}'"
+          return 1
+        fi
+        cmdline="$(cat /proc/${pid}/cmdline 2>&1 | xargs -0 echo -n)"
+        if ! [[ ${cmdline} =~ ${procPath} || ${cmdline} =~ ${AGENT_BIN_DIR}/${agent} ]]; then
+#            toLogWarning "[${agent}] Find process success. But process not myself. [cmdline:${cmdline}]"
+            return 1
+        fi
+#        toLogInfo "[${agent}] Find pid by process name [${procPath}|${AGENT_BIN_DIR}/${agent}]"
+      fi
+    else
+      #== 进程绝对路径查询
+#      toLogWarning "[${agent}] Pidfile [${pidFilePath}] not find. Use abs path queries."
+      if ! pid="$(listProcesses "${agent} pid" "${procPath}|${AGENT_BIN_DIR}/${agent}" "sudo|tail")"; then
+#        toLogWarning "[${agent}] Not find process by grep '${procPath}'"
+        return 1
+      fi
+      cmdline="$(cat /proc/${pid}/cmdline 2>&1 | xargs -0 echo -n)"
+      if ! [[ ${cmdline} =~ ${procPath} || ${cmdline} =~ ${AGENT_BIN_DIR}/${agent} ]]; then
+#          toLogWarning "[${agent}] Find process success. But process not myself. [cmdline:${cmdline}]"
+          return 1
+      fi
+#      toLogInfo "[${agent}] Find pid by process name [${procPath}|${AGENT_BIN_DIR}/${agent}]."
+    fi
+
+    printf '%s' ${pid}
+    return 0
+}
+
+listProcesses() {
+  local errorMessage="${1}"
+  local includeRegex="${2}"
+
+  local excludeRegex=" grep"
+  if [ "${3}" ]; then
+    excludeRegex="grep|${3}"
+  fi
+
+#  toLogInfo "#DEBUG== listProcesses command: ps -e -o \"pid,args\" 2>&1 | grep -E "${includeRegex}" | awk '{{ print \$1,\$2 }}' | grep -vE \"${excludeRegex}\""
+  local output
+  if ! output="$(ps -e -o "pid,args" 2>&1 | grep -E "${includeRegex}" | awk '{{ print $1,$2 }}')"; then
+#    toLogError "Failed to get ${errorMessage}, output: ${output}"
+    return 1
+  fi
+
+  local foundProcesses="$(printf '%s' "${output}" | grep -vE "${excludeRegex}")"
+  if [ ! "${foundProcesses}" ]; then
+    return 1
+  fi
+
+  printf '%s' "${foundProcesses}" | awk '{{ print $1 }}'
+  return 0
+}
+
+getAgentPid() {
+  local DAEMONPID="$(getDaemonPid "${AGENT_PROC}")"
+  if [ -z ${DAEMONPID} ]; then
+      return 1
+  fi
+  printf '%s' "${DAEMONPID}"
+
+}
+
+startAgent() {
+  local pid
+  if [ -f "${AGENT_BIN_DIR}/${AGENT_PROC}" ]; then
+    ${AGENT_BIN_DIR}/start.sh "${AGENT_BIN_DIR}/${AGENT_PROC}"
+#    local params="server --port 31767"
+#    if [ -f "/etc/chaosd/pki/ca.crt" ] && [ -f "/etc/chaosd/pki/chaosd.crt" ] && [ -f "/etc/chaosd/pki/chaosd.key" ]; then
+#        params="server  --https-port 31768 --CA=/etc/chaosd/pki/ca.crt --cert=/etc/chaosd/pki/chaosd.crt --key=/etc/chaosd/pki/chaosd.key"
+#    fi
+#    pid=$(nohup ${AGENT_BIN_DIR}/${AGENT_PROC} ${params}>>"${AGENT_LOG_DIR}/service.log" 2>&1 & echo $!)
+#    echo ${pid} > "${AGENT_PID_FILE}"
+  else
+    echo "${AGENT_BIN_DIR}/${AGENT_PROC} not exist."
+    exit "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+}
+
+agentStart() {
+  local agentPid
+  if agentPid="$(getAgentPid)"; then
+    echo "${BRAND_AGENT_PRODUCT_NAME} is running. Agent pid: ${agentPid}."
+    return
+  fi
+
+  startAgent
+	local exitCode=$?
+	if [ "${exitCode}" -ne 0 ]; then
+		echo "Failed to start ${BRAND_AGENT_PRODUCT_NAME} service."
+		exit "${EXIT_CODE_GENERIC_ERROR}"
+	fi
+
+  if agentPid="$(getAgentPid)"; then
+    echo "${BRAND_AGENT_PRODUCT_NAME} service started. Agent pid: ${agentPid}."
+  else
+    echo "${BRAND_AGENT_PRODUCT_NAME} not start!"
+    exit "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+agentStop() {
+  local agentPid
+  if agentPid="$(getAgentPid)"; then
+    ${AGENT_BIN_DIR}/stop.sh ${agentPid}
+#    kill ${agentPid}
+    echo "${BRAND_AGENT_PRODUCT_NAME} service stopped success."
+    cat /dev/null > "${AGENT_PID_FILE}"
+  else
+      echo "${BRAND_AGENT_PRODUCT_NAME} is stopped. service stopped success."
+  fi
+}
+
+agentRestart(){
+  agentStop
+  sleep 1
+  agentStart
+}
+
+agentStatus() {
+  local agentPid
+	local statusOutput
+  if agentPid="$(getAgentPid)"; then
+    statusOutput="${BRAND_AGENT_PRODUCT_NAME} is running. ${BRAND_AGENT_PRODUCT_NAME} pid: ${agentPid}"
+  else
+    statusOutput="${BRAND_AGENT_PRODUCT_NAME} is not running. ${BRAND_AGENT_PRODUCT_NAME} process not found or stop."
+  fi
+
+  echo "${statusOutput}"
+}
+
+info() {
+  local agentPid="$(getAgentPid)"
+  local version=$(cat "${INSTALL_DIR}/installer.version" | tr -d '\n')
+  printf '{"pid":%d,"version":"%s","agent_id":"%s","config":"%s"}' "${agentPid}" "${version}" "${BRAND_PRODUCT_NAME_LOWER}" "${AGENT_CONFIG_PATH}"
+}
+
+################################################################################
+#
+# Script start
+#
+################################################################################
+
+main() {
+  case "$1" in
+  start)
+    agentStart
+    ;;
+  stop)
+    agentStop
+    ;;
+  restart)
+    agentRestart
+    ;;
+  status)
+    agentStatus
+    ;;
+  info)
+    info
+    ;;
+  *)
+    toConsoleInfo "usage: $0 {start|stop|restart|status}"
+    ;;
+  esac
+
+  exit "${EXIT_CODE_OK}"
+}
+
+main "$@"

+ 6 - 0
dist/package_dir/bin/start.sh

@@ -0,0 +1,6 @@
+#!/bin/bash
+echo "start in start.sh"
+export TRACES_ENDPOINT=http://10.0.12.192:18080/docp/api/v2/data/receive
+pid=$(nohup $1 --listen="0.0.0.0:8123">>"/dev/null" 2>&1 & echo $!)
+AGENT_PID_FILE="$1.pid"
+echo ${pid} > "${AGENT_PID_FILE}"

+ 3 - 0
dist/package_dir/bin/stop.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+echo "stop in stop.sh"
+kill $1

+ 2926 - 0
dist/scripts/install_temp.sh

@@ -0,0 +1,2926 @@
+#!/bin/bash
+#== AIX(Advanced Interactive eXecutive)是IBM基于AT&T Unix System V开发的一套类UNIX操作系统,运行在IBM专有的Power系列芯片设计的小型机硬件系统之上
+#== 以【#== 】开头的注释会在打包时删除
+#DEBUG== 以【#DEBUG== 】开头的注释会在调试打包时保留,正式打包时删除
+
+
+#==脚本执行中遇到不存在的变量就报错
+set -o nounset
+#==脚本执行中报错即刻退出
+set -o errexit
+#==脚本执行只要一个子命令失败,整个管道命令就失败
+set -o pipefail
+
+readonly BRAND_FORMAL_NAME="Cloudwise"
+readonly BRAND_PRODUCT_NAME="euspace"
+readonly BRAND_PARENT_PRODUCT_NAME="omniagent"
+readonly BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME}"
+readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
+readonly BRAND_PRODUCT_NAME_LOWER="euspace"
+readonly BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME_CLOUDWISE="cloudwise"
+
+readonly AIX_DF_SPECIFIC_FLAG=
+readonly CONF_LD_PRELOAD=
+
+PARAM_DISABLE_SYSTEM_LOGS_ACCESS=
+
+#== 安装包版本项
+readonly AGENT_BUILD_DATE=25.09.2020
+readonly AGENT_INSTALLER_VERSION=1.2.0
+readonly AGENT_BUILD_DATE_INFO=""
+readonly AGENT_BUILD_TAG=""
+readonly AGENT_BUILD_UPLOADER=""
+#== 安装包版本项
+
+#== **********************************************************
+#==  配置重要文件名称
+#== **********************************************************
+#== CW-ServerAgent
+readonly AGENT_PROC="euspace"
+#== **********************************************************
+#==  配置目录
+#== **********************************************************
+readonly INSTALL_BASE=/opt
+#== /opt/cloudwise/oneagent/agents 目录
+readonly BASE_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents
+#== cloudwise/oneagent/agents/chaosd
+readonly INSTALL_FOLDER=${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents/${BRAND_PRODUCT_NAME_LOWER}
+#== cloudwise/oneagent/agents/chaosd/version
+readonly INSTALL_VERSION_FOLDER=${INSTALL_FOLDER}/${AGENT_INSTALLER_VERSION}
+#== /opt/cloudwise/oneagent/agents/chaosd/version
+readonly INSTALL_DIR=${INSTALL_BASE}/${INSTALL_VERSION_FOLDER}
+#== /opt/cloudwise/oneagent/agents/chaosd
+readonly AGENT_BASE_DIR=${INSTALL_BASE}/${INSTALL_FOLDER}
+
+#== /opt/cloudwise/cwserveragent/conf
+readonly AGENT_CONF_DIR="${INSTALL_DIR}/conf"
+#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/scripts
+readonly AGENT_SCRIPTS_DIR="${INSTALL_DIR}/scripts"
+#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/runtime
+readonly AGENT_RUNTIME_DIR="${INSTALL_DIR}/runtime"
+#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/bin
+readonly AGENT_BIN_DIR="${INSTALL_DIR}/bin"
+#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/lib
+#== readonly AGENT_LIB_DIR="${INSTALL_DIR}/lib"
+
+
+readonly AGENTS_BASE_DIR="${INSTALL_DIR}/agents"
+
+readonly META_BASE_DIR="${INSTALL_DIR}/meta"
+
+readonly LOG_DIR_NAME="logs"
+#== /opt/cloudwise/cwserveragent/logs
+readonly LOG_DIR=${INSTALL_DIR}/${LOG_DIR_NAME}
+
+#== /var/log/cloudwise/cwserveragent/
+readonly INSTALLER_LOG_DIR="/var/log/${INSTALL_FOLDER}"
+
+
+#== server-agent.ini
+#== hostId文件
+readonly INSTALLER_CONF_FILE_NAME="installation.conf"
+#== 部署在容器内的状态信息
+readonly INSTALLER_CONF_FILE="${AGENT_CONF_DIR}/${INSTALLER_CONF_FILE_NAME}"
+
+#== 【0】=
+readonly INSTALLER_LOCK_FILE="/tmp/${BRAND_FORMAL_NAME_LOWER}_${BRAND_PRODUCT_NAME_LOWER}.lock"
+
+readonly INIT_SYSTEM_SYSV="SysV"
+#== 系统 systemd
+readonly INIT_SYSTEM_SYSTEMD="systemd"
+#== cw-serveragent
+readonly SERVICE_SCRIPT_FILE="cw-oneagent"
+#== cw-serveragent.service
+readonly SYSTEMD_UNIT_FILE_AGENT="${SERVICE_SCRIPT_FILE}.service"
+#== 系统文件目录
+readonly SYSTEMD_UNIT_FILES_DIR="/etc/systemd/system/"
+
+#== 【0】=【退出码】执行成功
+readonly EXIT_CODE_OK=0
+readonly EXIT_CODE_GENERIC_ERROR=1
+readonly EXIT_CODE_NOT_ENOUGH_SPACE=6
+readonly EXIT_CODE_NOT_ENOUGH_MEMORY=7
+#== 【0】=无效参数
+readonly EXIT_CODE_INVALID_PARAM=8
+readonly EXIT_CODE_INSUFFICIENT_PERMISSIONS=9
+
+#== 【退出码】接收信号
+readonly EXIT_CODE_SIGNAL_RECEIVED=12
+readonly EXIT_CODE_ANOTHER_INSTALLER_RUNNING=13
+readonly EXIT_CODE_CORRUPTED_PACKAGE=16
+#== 【退出码】管理系统环境变量配置
+readonly EXIT_CODE_MISCONFIGURED_ENVIRONMENT=17
+readonly EXIT_CODE_UNSUPPORTED_DOWNGRADE=18
+readonly EXIT_CODE_OS_NOT_SUPPORTED=19
+
+readonly EXTERNAL_TAR_SIZE=233820160
+readonly ARTIFACTS_SIZE=953271934
+#**********************************************************
+# Logs
+#**********************************************************
+toLogFile() {
+  if [ -e "${LOG_FILE}" ]; then
+    printf '%s UTC %s\n' "$(date -u +"%Y-%m-%d %H:%M:%S")" "$*" >>"${LOG_FILE}" 2>/dev/null
+  fi
+}
+
+toConsoleOnly() {
+  printf '%s %s\n' "$(date +"%H:%M:%S")" "$*" 2>/dev/null
+}
+
+toLogInfo() {
+  toLogFile "[INFO]" "$@"
+}
+
+toLogWarn() {
+  toLogFile "[WARN]" "$@"
+}
+
+toLogError() {
+  toLogFile "[ERROR]" "$@"
+}
+
+toLogAdaptive() {
+  local success="${1}"
+  shift
+  if [ "${success}" -eq 0 ]; then
+    toLogInfo "$@"
+  else
+    toLogError "$@"
+  fi
+}
+
+toConsoleInfo() {
+  toConsoleOnly "$@"
+  toLogInfo "$@"
+}
+
+toConsoleWarn() {
+  toConsoleOnly "Warn:" "$@"
+  toLogWarn "$@"
+} >&2
+
+toConsoleError() {
+  toConsoleOnly "Error:" "$@"
+  toLogError "$@"
+} >&2
+
+#== 【0】【3】【4】=创建不存在的目录,并设置权限
+createDirIfNotExistAndSetRights() {
+  local dir="${1}"
+  local rights="${2}"
+
+  toConsoleInfo "#DEBUG== createDirIfNotExistAndSetRights --- ${rights} --- ${dir}"
+  if [ ! -d "${dir}" ]; then
+    toLogInfo "Creating directory ${dir} with rights ${rights}"
+    local message
+    if ! message="$(mkdir -p "${dir}" 2>&1)"; then
+      toConsoleWarn "Cannot create ${dir} directory."
+      toLogWarn "Error message: ${message}"
+      return 1
+    fi
+  fi
+
+  if ! message="$(chmod "${rights}" "${dir}" 2>&1)"; then
+    toConsoleWarn "Cannot change permisions of ${dir} directory to ${rights}."
+    toLogWarn "Error message: ${message}"
+    return 1
+  fi
+
+  return 0
+}
+
+createCurrentVersionSymlink() {
+	toLogInfo "Creating symlink to current version..."
+
+	local currentVersionLink="${AGENT_BASE_DIR}/current"
+	local tempLink="${currentVersionLink}.tmp"
+
+	if ! commandErrorWrapper ln -s "${AGENT_INSTALLER_VERSION}" "${tempLink}"; then
+		toLogError "Failed to create current version link"
+		return
+	fi
+
+	if ! commandErrorWrapper arch_moveReplaceTarget "${tempLink}" "${currentVersionLink}"; then
+		toLogError "Failed to set up current version link"
+		commandErrorWrapper rm -f "${tempLink}"
+		return
+	fi
+
+	toLogInfo "Current version link created: ${currentVersionLink} -> ${AGENT_INSTALLER_VERSION}"
+}
+
+#== 【0】=创建日志目录及日志文件installation_$$.log
+createLogDirsIfMissing() {
+  createDirIfNotExistAndSetRights "${INSTALL_BASE}" u+rwx,g+rx,o+rx
+  createDirIfNotExistAndSetRights "${BASE_INSTALL_DIR}" u+rwx,g+rx,o+rx
+  #== agents根目录
+  createDirIfNotExistAndSetRights "${AGENT_BASE_DIR}" 1775
+  createDirIfNotExistAndSetRights "${INSTALL_DIR}" 1775
+#  createDirIfNotExistAndSetRights "${LOG_DIR}" 1777
+  createDirIfNotExistAndSetRights "${INSTALLER_LOG_DIR}" 1777
+
+#  for agentLog in ${PLUGIN_LOG_NAMES}; do
+#    createDirIfNotExistAndSetRights "${LOG_DIR}/${agentLog}" 1777
+#  done
+
+  touch "${LOG_FILE}"
+  setRightsForFiles "${LOG_FILE}" 766
+}
+
+#**********************************************************
+# Platform characteristics detection
+#**********************************************************
+
+#== 【5】=获取系统发型版本
+getOsReleasePath() {
+	local osReleasePath="/etc/os-release"
+	if [ ! -f "${osReleasePath}" ]; then
+		osReleasePath="/usr/lib/os-release"
+	fi
+
+	printf '%s' "${osReleasePath}"
+}
+
+parseOsReleaseFile() {
+	local osReleasePath="$(getOsReleasePath)"
+
+	#shellcheck disable=SC1090
+	. "${osReleasePath}"
+	local distrib="${NAME-}"
+	if [ -z "${distrib}" ]; then
+		distrib="${ID-}"
+	fi
+
+	local version="${VERSION_ID-}"
+	if printf '%s' "${distrib}" | grep -iq "debian"; then
+		version="$(cat /etc/debian_version)"
+	elif printf '%s' "${distrib}" | grep -iq "fedora" && [ "${VARIANT_ID-}" = "coreos" ]; then
+		distrib="${distrib} CoreOS"
+	fi
+
+	printf '%s %s' "${distrib}" "${version}"
+}
+
+#== 【5】=获取系统发型版本
+detectLinuxDistribution() {
+	if [ -f /etc/oracle-release ]; then
+		cat /etc/oracle-release
+	elif [ -f /etc/fedora-release ]; then
+		if [ -f "$(getOsReleasePath)" ]; then
+			(
+				parseOsReleaseFile
+			)
+		else
+			cat /etc/fedora-release
+		fi
+	elif [ -f /etc/redhat-release ]; then
+		cat /etc/redhat-release
+	elif [ -f "$(getOsReleasePath)" ]; then
+		(
+			parseOsReleaseFile
+		)
+	elif [ -f /etc/SuSE-release ]; then
+		head -1 /etc/SuSE-release
+	elif [ -f /etc/lsb-release ]; then
+		(
+			. /etc/lsb-release
+			printf "%s %s" "${DISTRIB_ID-}" "${DISTRIB_RELEASE-}"
+		)
+	elif ls /etc/*release* >/dev/null 2>&1; then
+		# Generic fallback
+		cat /etc/*release*
+	else
+		printf "AIX %s" "$(oslevel -s 2>&1)"
+	fi
+}
+
+#== 【3】=检查系统init (INIT_SYSTEM、INIT_SYSTEM_VERSION)
+checkInitSystem() {
+  local version
+  if version="$(systemctl --version 2>&1)"; then
+    if [ -d "${SYSTEMD_UNIT_FILES_DIR}" ]; then
+      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSTEMD}
+    else
+      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
+      toLogWarn "${INIT_SYSTEM_SYSTEMD} was detected but ${SYSTEMD_UNIT_FILES_DIR} does not exist, using ${INIT_SYSTEM_SYSV} handling as a fallback"
+    fi
+  else
+    readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
+    if ! version="$(init --version 2>&1)"; then
+      if ! version="$(chkconfig --version 2>&1)"; then
+        version="$(head -n1 /etc/inittab 2>&1)"
+      fi
+    fi
+  fi
+
+  readonly INIT_SYSTEM_VERSION="$(printf '%s' "${version}" 2>/dev/null | head -n1)"
+}
+
+#== 【3】=设置系统初始化脚本目录
+setLocationOfScripts() {
+  toLogInfo "Determining location of scripts..."
+
+  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSTEMD}"x ] || [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    #== /opt/cloudwise/cwserveragent/scripts
+    readonly INIT_DIR="${AGENT_SCRIPTS_DIR}"
+  else
+    if [ -d "/etc/init.d" ]; then
+      readonly INIT_DIR="/etc/init.d"
+    elif [ -d "/sbin/init.d" ]; then
+      readonly INIT_DIR="/sbin/init.d"
+    elif [ -d "/etc/rc.d" ]; then
+      readonly INIT_DIR="/etc/rc.d"
+    else
+      return 1
+    fi
+  fi
+
+  toLogInfo "Location of scripts ${INIT_DIR}"
+  return 0
+}
+
+#== 【0】=检查系统结构(X86_64\IA64\X86)
+detectArchitecture() {
+  local detected_arch=
+  if isAvailable arch; then
+    #== arch指令主要用于显示当前主机的硬件结构类型,查询结果与uname一致,我们可以看到它输出的结果有:i386、i486、mips、alpha等
+    detected_arch="$(arch | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  if [ -z "${detected_arch}" ]; then
+    detected_arch="$(uname -m | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  printf '%s' "${detected_arch}"
+}
+
+#**********************************************************
+# Misc functions
+#**********************************************************
+
+#== 【1】【4】【5】【6】=获取 agent (64\32)位数lib目录(""或lib64)
+getBinariesFolderByBitness() {
+  local bitness="${1}"
+  if [ "${bitness}" -eq 32 ]; then
+    bitness=""
+  fi
+  printf 'lib%s' "${bitness}"
+}
+
+#== 【1】【2】【5】【6】=获取 agent tools/lib64/smartagentctl 路径
+#== getAgentCtlBinPath
+
+#== 【1】【4】【5】【6】=获取 agent 64位数lib路径(lib64/installaction)
+#== getAgentInstallActionPath
+
+#== 【5】【6】=获取OS bin配置路径 /opt/cloudwise/cwserveragent/lib64/cloudwiseosconfig
+getOsConfigBinPath() {
+  printf "%s" "${INSTALL_DIR}/lib64/${AGENT_OS_CONFIG_BIN}"
+}
+
+#== 【5】=设置agent进程可用
+setProcessAgentEnabled() {
+  local enabled="${1}"
+  toLogInfo "Setting process agent enabled: ${enabled}..."
+  local changeStatus=
+  #== 调用 agent 64位数lib路径(lib64/installaction)执行指令 --set-process-agent-enabled
+  changeStatus=$("$(getAgentInstallActionPath)" --set-process-agent-enabled "${enabled}" 2>&1)
+  toLogAdaptive $? "Process agent enable(${enabled}) status: ${changeStatus}"
+}
+
+#== 【1】【4】【5】【6】=获取命令执行错误信息,并写日志
+commandErrorWrapper() {
+  local command="${*}"
+  local errorFile="/tmp/smartagent_commanderror_$$"
+
+  ${command} 2>"${errorFile}"
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 0 ]; then
+    toLogWarn "Command '${command}' failed, return code: ${returnCode}, message: $(cat "${errorFile}")"
+  fi
+
+  rm -f "${errorFile}"
+
+  return ${returnCode}
+}
+
+#== 【3】=是否独立的namespace
+isNamespaceIsolated() {
+  local pid="${1}"
+  local namespace="${2}"
+  local initNamespaceId
+  local processNamespaceId
+  initNamespaceId="$(readlink "/proc/1/ns/${namespace}" 2>/dev/null | tr -dc '0-9')"
+  processNamespaceId="$(readlink "/proc/${pid}/ns/${namespace}" 2>/dev/null | tr -dc '0-9')"
+
+  if [ ! "${initNamespaceId}" ] || [ ! "${processNamespaceId}" ]; then
+    toLogInfo "Link to /proc/*/ns/${namespace} does not exist"
+    printf 'error'
+    return
+  fi
+
+  if [ "${initNamespaceId}"x != "${processNamespaceId}"x ]; then
+    printf 'true'
+  else
+    printf 'false'
+  fi
+}
+
+#== 【0】=检查是否root
+checkRootAccess() {
+  toConsoleInfo "Checking root privileges..."
+
+  if [ "$(id -u)"x != "0"x ]; then
+    toConsoleError "NOT OK"
+    return 1
+  fi
+
+  toConsoleInfo "OK"
+  return 0
+}
+
+#== 【3】=删除存在的路径
+removeIfExists() {
+  local pathToRemove="${1}"
+  if [ ! -e "${pathToRemove}" ]; then
+    toLogInfo "${pathToRemove} does not exist, skipping removal"
+    return
+  fi
+
+  local output
+  if ! output="$(rm -rf "${pathToRemove}" 2>&1)"; then
+    toLogWarn "Failed to remove ${pathToRemove}: ${output}"
+  fi
+}
+
+#**********************************************************
+# SELinux related functions
+#**********************************************************
+
+#== 【5】【6】=执行 systemctl 命令
+executeSystemctlCommand() {
+  local command="${1}"
+  local unit="${2}"
+
+  if [ "$(id -u)" != 0 ]; then
+    #== 执行使用 os config bin
+    executeUsingOsConfigBin "${command}" "${unit}"
+    return $?
+  fi
+
+  local output=
+  #== shellcheck disable=SC2086
+  #== 执行 systemctl 命令
+  output="$(systemctl "${command}" ${unit} 2>&1)"
+  local exitCode=$?
+
+  if [ ${exitCode} -eq 0 ]; then
+    toLogInfo "Successfully executed: systemctl ${command} ${unit}"
+  else
+    toLogError "Failed to execute: systemctl ${command} ${unit}"
+    toLogError "Command output: ${output}"
+    if [ -n "${unit}" ]; then
+      local reachBackNumSeconds=360
+      toLogError "journalctl output: $(journalctl -u "${unit}" --since=-${reachBackNumSeconds} 2>&1)"
+    fi
+  fi
+
+  return ${exitCode}
+} 2>>"${LOG_FILE}"
+
+#== 【6】=运行初始化命令(通过 service 方式 或 直接运行可执行命令)
+executeInitScriptCommand() {
+  local command=
+  local parameters="$*"
+  local output=
+  local exitCode=
+
+  if isAvailable service; then
+    command="service"
+    parameters="${SERVICE_SCRIPT_FILE} ${parameters}"
+  else
+    command="${INIT_DIR}/${SERVICE_SCRIPT_FILE}"
+  fi
+  output="$("${command}" "${parameters}" 2>&1)"
+  exitCode=$?
+  toLogAdaptive ${exitCode} "Executed ${command} ${parameters}, exitCode = ${exitCode}, output: ${output}"
+  return ${exitCode}
+}
+
+#== 【0】=信号捕获,并执行回调函数
+signalHandler() {
+  local signal="${1}"
+  local callback="${2}"
+  toLogWarn "process received signal: ${signal}"
+  ${callback}
+  exit ${EXIT_CODE_SIGNAL_RECEIVED}
+}
+
+#== 【0】=配置信号捕获和回调
+configureSignalHandling() {
+  local callback="${1}"
+  for signal in HUP INT QUIT ABRT ALRM TERM; do
+    # shellcheck disable=SC2064
+    trap "signalHandler '${signal}' '${callback}'" ${signal}
+  done
+
+  trap "" PIPE
+}
+
+#== 【0】删除权限
+removeSecretsFromString() {
+  printf "%s" "$*" | sed 's#\(LICENSE=\)[[:alnum:]]\{16\}#\1***#'
+}
+
+#== 【3】【5】=检查目录写权限
+testWriteAccessToDir() {
+  local errorFile="/tmp/smartagent_commandError_$$"
+  local dir="${1}"
+  local tmpfilename
+  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    tmpfilename="${dir}/.tmp_${BRAND_PRODUCT_NAME_LOWER}.$$${RANDOM}"
+    touch "${tmpfilename}" 2>"${errorFile}"
+  else
+    tmpfilename="$(mktemp -p "${dir}" ".tmp_${BRAND_PRODUCT_NAME_LOWER}.XXXXXXXXXXXXXX" 2>"${errorFile}")"
+  fi
+
+  #== shellcheck disable=SC2181
+  if [ $? -ne 0 ]; then
+    toLogInfo "$(cat "${errorFile}")"
+    rm -f "${errorFile}"
+    return 1
+  fi
+
+  rm -f "${tmpfilename}" "${errorFile}"
+  return 0
+}
+
+#== 【0】=设置PATH
+setPATH() {
+  local prependToPATH="/usr/sbin:/usr/bin:/sbin:/bin"
+  if [ "${PATH}" ]; then
+    PATH=${prependToPATH}:${PATH}
+  else
+    PATH=${prependToPATH}
+  fi
+}
+
+#== 【5】【6】=是否非root权限
+isNonRootModeEnabled() {
+  #== 调用 agent 64位数lib路径(lib64/installaction)执行指令 --get-drop-root-privileges
+  local output
+  output="$(getValueFromConfigFile "${CONF_FIELD_NM_NON_ROOT_MODE}" "${LEGACY_AGENT_CONF_FILE}" "${PARAM_NON_ROOT_MODE}")"
+  printf '%s' "${output}" | grep -qE "(true|no_ambient)"
+}
+
+#== 【5】=自动启动工具
+runAutostartAddingTool() {
+  local prefix="${1}"
+  local file="${2}"
+  local suffix="${3}"
+  local output
+
+  local command="${prefix} ${file} ${suffix}"
+  toLogInfo "Executing ${command}"
+  output="$(${command} 2>&1)"
+  local status=$?
+  if [ "${output}" ]; then
+    toLogAdaptive ${status} "${output}"
+  fi
+
+  return ${status}
+}
+
+#== 【0】【5】=查找软链接真实地址(通过ls命令)
+readLinkFromLs() {
+  local path="${1}"
+  local result="${path}"
+  local lsOutput
+  local parsedLinkTarget
+  lsOutput="$(ls -dl "${path}" 2>/dev/null)"
+  if printf '%s' "${lsOutput}" | grep -q " -> "; then
+    parsedLinkTarget="$(printf '%s' "${lsOutput}" | sed 's|^.* -> ||')"
+    if [ "${parsedLinkTarget}" ]; then
+      result="${parsedLinkTarget}"
+    else
+      toLogWarn "Failed to parse ls output '${lsOutput}'"
+    fi
+  fi
+  printf '%s' "${result}"
+}
+
+#== 【0】【5】=查找软链接真实地址(通过readlink 或 ls 命令)
+readLink() {
+  local args=-e
+  local path="${1}"
+  if [ "${2}" ]; then
+    args="${1}"
+    path="${2}"
+  fi
+
+  (
+    if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+      path="${PATH}:/opt/freeware/bin"
+    fi
+
+    #== 通过 readlink 命令查找地址
+    if isAvailable readlink; then
+      #shellcheck disable=SC2086
+      readlink ${args} "${path}"
+    else
+      toLogInfo "readlink command not found, falling back to parsing ls output"
+      readLinkFromLs "${path}"
+    fi
+  )
+}
+
+#== 【0】【3】【4】【5】【6】=检查命令是否有效/是否存在
+isAvailable() {
+  command -v "${1}" >/dev/null 2>&1
+}
+
+#== 【0】【1】【3】【5】=读取配置文件配置参数信息
+getValueFromConfigFile() {
+  local key="${1}"
+  local configFile="${2}"
+  local defaultValue="${3}"
+  local value
+  value="$(sed -n "s|^${key}=||p" "${configFile}" 2>/dev/null)"
+
+  if [ "${value}" ]; then
+    printf '%s' "${value}"
+  else
+    printf '%s' "${defaultValue}"
+  fi
+}
+
+#== 【1】【5】=删除配置文件中配置
+removeValueFromConfigFile() {
+  local key="${1}"
+  local configFile="${2}"
+  local output
+  if ! output="$(cp -p "${configFile}" "${configFile}.tmp" 2>&1)"; then
+    toLogWarn "Unable to initialize ${configFile}.tmp file using source file, privileges and ownership will not be preserved: ${output}"
+  fi
+
+  if sed "/^${key}/d" "${configFile}" >"${configFile}.tmp"; then
+    mv -f "${configFile}.tmp" "${configFile}"
+  else
+    toLogWarn "Failed to remove ${key} from ${configFile}"
+    rm -f "${configFile}.tmp"
+  fi
+}
+
+#== 写配置到配置文件信息
+writeParamToConfigFile() {
+  local key="${1}"
+  local newValue="${2}"
+  local configFile="${3}"
+  local value
+  value="$(sed -n "s|^${key}=||p" "${configFile}" 2>/dev/null)"
+  toConsoleInfo "#DEBUG== writeParamToConfigFile --> edit ${key}=${value} to ${key}=${newValue}, configFile: ${configFile}"
+  if [ "${value}" ]; then
+    sed -i "s|^${key}=.*|${key}=${newValue}|" "${configFile}" 2>/dev/null
+  else
+    echo "${key}=${newValue}" >>"${configFile}" 2>/dev/null
+  fi
+}
+
+writeContentToConfigFile() {
+  local newValue="${1}"
+  local configFile="${2}"
+  toConsoleInfo "#DEBUG== writeContentToConfigFile --> edit ${newValue}, configFile: ${configFile}"
+
+  echo "${newValue}" > "${configFile}" 2>/dev/null
+}
+
+#== 修改scripts/smartagent脚本配置信息
+editScriptFileParam() {
+  local key="${1}"
+  local newValue="${2}"
+  local file="${3}"
+  local value
+  value="$(sed -n "s|^${key}=||p" "${file}" 2>/dev/null)"
+  toConsoleInfo "#DEBUG== editScriptFileParam --> edit ${key}=${value} to ${key}=${newValue}, configFile: ${file}"
+  if [ "${value}" ]; then
+    sed -i "s|^${key}=.*|${key}=${newValue}|" "${file}" 2>/dev/null
+  fi
+}
+
+#== 【0】=是否存在多个同时安装操作【通过判断 INSTALLER_LOCK_FILE 】
+isAnotherInstallationRunning() {
+  if [ ! -f "${INSTALLER_LOCK_FILE}" ]; then
+    return 1
+  fi
+  local pidFromLockFile
+  local nameFromLockFile
+  pidFromLockFile="$(head -n 1 "${INSTALLER_LOCK_FILE}")"
+  nameFromLockFile="$(tail -n 1 "${INSTALLER_LOCK_FILE}")"
+  if [ "$(wc -l <"${INSTALLER_LOCK_FILE}")" -ne 2 ] || [ -z "${pidFromLockFile}" ] || [ -z "${nameFromLockFile}" ]; then
+    toConsoleWarn "Installation lock file ${INSTALLER_LOCK_FILE} is damaged, skipping uniqueness check."
+    toConsoleWarn "Lock file contents: '$(cat ${INSTALLER_LOCK_FILE})'"
+    return 1
+  fi
+
+  #== shellcheck disable=SC2009
+  #== 获取正在执行安装的进程信息
+  local foundProcesses
+  foundProcesses="$(pgrep -f "pid,args" 2>&1 | grep -w "${nameFromLockFile}" | grep -v " grep ")"
+  if printf '%s' "${foundProcesses}" | awk '{ print $1 }' | grep -wq "${pidFromLockFile}"; then
+    local errorMessage="Another ${BRAND_PRODUCT_NAME} installer or uninstaller is already running"
+    if printf '%s' "${foundProcesses}" | grep -q "${DOWNLOADS_DIR}"; then
+      errorMessage="${errorMessage} (AutoUpdate is in progress)"
+    fi
+
+    toConsoleError "${errorMessage}, PID ${pidFromLockFile}. Exiting."
+    return 0
+  fi
+
+  toConsoleInfo "Lock file exists but corresponding installation process does not run, contents of lock file: ${pidFromLockFile}, ${nameFromLockFile}."
+  return 1
+}
+
+#== 【0】=创建安装标示文件 /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
+createInstallationLockFile() {
+  printf '%s\n%s\n' "$$" "$0" >"${INSTALLER_LOCK_FILE}" 2>/dev/null
+}
+
+#== 【0】【6】删除安装标示文件 /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
+removeInstallationLockFile() {
+  toLogInfo "Removing installation lock file."
+  rm -f "${INSTALLER_LOCK_FILE}"
+}
+
+#== 【0】基础启动日志信息
+logBasicStartupInformation() {
+  toLogInfo "Command line: $(removeSecretsFromString "${@}")"
+  toLogInfo "Shell options: $-"
+  toLogInfo "Working dir: $(pwd)"
+  toLogInfo "PID: $$"
+  toLogInfo "Parent process: $(
+    printf '\n'
+    ps -o user,pid,ppid,comm -p ${PPID} 2>&1
+  )"
+  toLogInfo "User id: $(id -u)"
+}
+
+#== 解压文件编码
+readonly UNPACK_BINARY=base64
+#== 解压文件编码参数
+readonly UNPACK_BINARY_ARGS="-di"
+
+readonly ARCH_ARCH="X86"
+
+#== 【0】=检查系统指令集(X86_64\IA64\X86)
+arch_checkArchitectureCompatibility() {
+  #== 【0】=检查系统结构(X86_64)
+  local arch
+  arch="$(detectArchitecture)"
+  if [ "${arch}"x = "X86_64"x ] || [ "${arch}"x = "IA64"x ]; then
+    arch="X86_64"
+  else
+    arch="$(uname -m | sed -e 's/i.86/x86/' | sed -e 's/i86pc/x86/' | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  printf '%s' "${arch}"
+  [ "${arch}"x = "X86_64"x ] || [ "${arch}"x = "AARCH64"x ]
+}
+
+#== 【5】=获取 lib 目录
+arch_getLibMacro() {
+  local libMacro=""
+  if [ "${SYSTEM_LIB32}" ]; then
+    #== shellcheck disable=SC2016
+    libMacro="/${LIB}"
+  fi
+  printf "%s" "${libMacro}"
+}
+#== 'timeout' requires gnu-coreutils8, i.e. it is not available on RHEL5, that's why we need this utility function
+#== 【3】【5】=执行命令超时配置
+
+#== 【0】【3】=获取文件权限信息
+arch_getAccessRights() {
+  stat --format='%A' "${1}"
+}
+
+#== 【4】=替换目录
+arch_moveReplaceTarget() {
+  local source="${1}"
+  local target="${2}"
+  mv -fT "${source}" "${target}"
+}
+
+
+#== xz 压缩包文件名 Cloudwise-SmartAgent
+readonly INTERNAL_TAR_FILE_NAME=${BRAND_FORMAL_NAME}-${BRAND_PRODUCT_NAME}.tar.xz
+
+#== -安装日志: installation-{timestamp}.log,如,installation-20220601110912.log
+readonly LOG_FILE="${INSTALLER_LOG_DIR}/installation-$(date -u +"%Y%m%d%H%M%S").log"
+#readonly LOG_FILE="${INSTALLER_LOG_DIR}/installation_$$.log"
+
+
+
+#== 【临时目录】安装临时目录
+readonly TMP_DIR=${INSTALL_DIR}_install_$$
+#== 【临时目录】解压缓存目录
+readonly UNPACK_CACHE=${BASE_INSTALL_DIR}/unpack_cache
+#== 【临时目录】从sh文件读取压缩文件 tarfile_$$.base64
+readonly EXTERNAL_TAR_FILE=${BASE_INSTALL_DIR}/tarfile_$$.base64
+#== 【临时目录】xz 压缩包文件
+readonly INTERNAL_TAR_FILE=${INSTALL_DIR}/${INTERNAL_TAR_FILE_NAME}
+
+#== 【0】=
+readonly INSTALLER_FILE=${0}
+#== 旧配置文件
+readonly LEGACY_AGENT_CONF_FILE="${AGENT_CONF_DIR}/${BRAND_PRODUCT_NAME_LOWER}.conf"
+
+#== 【0】=
+readonly LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS=50
+
+#== 【0】=
+readonly HELP_URL=""
+
+readonly CONF_FIELD_NM_DATA_SERVER="DataServer"
+readonly CONF_FIELD_NM_CONFIG_SERVER="ConfigServer"
+readonly CONF_FIELD_NM_LICENSE="License"
+readonly CONF_FIELD_NM_JSON_CONF="JSON_CONF"
+readonly CONF_FIELD_NM_USER="User"
+readonly CONF_FIELD_NM_GROUP="Group"
+readonly CONF_FIELD_NM_DATA_STORAGE="DataStorage"
+readonly CONF_FIELD_NM_NON_ROOT_MODE="NonRootMode"
+
+
+#== 配置默认用户权限
+readonly BASE_OMNI_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}
+readonly BASE_OMNI_INSTALL_CONF_DIR=${BASE_OMNI_INSTALL_DIR}/conf/installation.conf
+BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${BASE_OMNI_INSTALL_CONF_DIR}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME_CLOUDWISE}")"
+
+#== Those are read from params section appended to installer by the server
+#== 【0】=【参数】数据服务地址
+PARAM_DATA_SERVER=
+#== 【0】=【参数】配置服务地址
+PARAM_CONFIG_SERVER=
+#== 【0】=【参数】license
+PARAM_LICENSE=
+
+PARAM_JSON_CONF=
+
+#== 【0】=【参数】安装路径
+PARAM_INSTALL_DIR=${INSTALL_DIR}
+#== 【0】=【参数】用户
+PARAM_USER=
+#== 【0】=【参数】用户组
+PARAM_GROUP=
+#== 【0】=【参数】非root模式
+PARAM_NON_ROOT_MODE=true
+
+PARAM_USER_LOGIN=false
+
+#== 【0】=【参数】不允许root回退
+PARAM_DISABLE_ROOT_FALLBACK=false
+#== 【0】=【参数】数据存储目录
+PARAM_DATA_STORAGE=
+#== 【0】=【参数】通过容器部署
+#== PARAM_INTERNAL_DEPLOYED_VIA_CONTAINER=false
+#== 【0】=【参数】跳过SELinux策略安装
+#== PARAM_INTERNAL_SKIP_SELINUX_POLICY_INSTALLER=false
+#== 【0】=【参数】是否使用解压缓存
+PARAM_INTERNAL_USE_UNPACK_CACHE=false
+#== 【0】=【参数】是否不使用 dump
+#== PARAM_INTERNAL_DISABLE_DUMPPROC=
+#== 【0】=【参数】是否跳过非root检查
+PARAM_INTERNAL_NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK=false
+#== 【0】【1】=【参数】额外的配置
+#== PARAM_INTERNAL_PASS_THROUGH_SETTERS=
+#== 【0】=检查是否降级安装
+SKIP_DOWNGRADE_CHECK=false
+
+SKIP_PRIVILEGES_CHECK=false
+
+#== 自定义字符串参数
+PARAM_TEST=
+#== 自定义字BOOL参数
+PARAM_BOOL=
+
+#== 【0】=常规日志
+initializeLog() {
+  toConsoleInfo "Installation started, version ${AGENT_INSTALLER_VERSION}, build date: ${AGENT_BUILD_DATE}, PID $$."
+  toLogInfo "Started from: ${INSTALLER_FILE}"
+
+  if [ -f /proc/version ]; then
+    toLogInfo "System version: $(cat /proc/version)"
+  else
+    toLogInfo "System version: $(uname -a)"
+  fi
+
+  toLogInfo "Path: ${PATH}"
+  toLogInfo "INSTALL_DIR: ${INSTALL_DIR}"
+  toLogInfo "Resolved installation path: $(readLink -e "${INSTALL_DIR}" 2>/dev/null)"
+  logBasicStartupInformation "${@}"
+}
+
+#**********************************************************
+# Signing related stuff
+#**********************************************************
+#== 【0】【4】=通过占位分割读取位置
+locateDelimiter() {
+  #== 占位符
+  local delimiter="${1}"
+  #== 从文件结尾读取行数
+  local linesToReadFromEnd="${2}"
+  local linesCount
+  local offset
+  if [ "${linesToReadFromEnd}" ]; then
+    #== 文件总行数n(实际行数=n+1)
+    linesCount="$(wc -l "${INSTALLER_FILE}" | awk '{print $1}')"
+    #== 从后往前读取【linesToReadFromEnd】行
+    offset="$(tail -n"${linesToReadFromEnd}" "${INSTALLER_FILE}" 2>/dev/null | awk '/^'"${delimiter}"'/ { print NR; exit }')"
+    if [ -n "${offset}" ]; then
+      printf "%d" "$((linesCount - linesToReadFromEnd + offset))"
+    fi
+  else
+    #== 读取占位符所在行(实际行数=n+1)
+    awk '/^'"${delimiter}"'/ { print NR; exit }' "${INSTALLER_FILE}"
+  fi
+}
+
+#== 【0】=从指定行范围读取配置参数
+readParam() {
+  local paramName="${1}"
+  local paramsSectionBeggining="${2}"
+  local paramsSectionEnd="${3}"
+
+  sed -n "${paramsSectionBeggining},${paramsSectionEnd} s/^${paramName}=//p" "${INSTALLER_FILE}"
+}
+
+#== 【0】=读取以【----PARAMETERS】开始到 【----PARAMETERS--】之间的参数
+readParamsSection() {
+  local sectionName="----PARAMETERS"
+  local begin
+  local end
+  begin=$(locateDelimiter "${sectionName}" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS})
+  end=$(locateDelimiter "${sectionName}--" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS})
+
+  if [ -z "${begin}" ] || [ -z "${end}" ]; then
+    return
+  fi
+  #== 从指定行范围读取配置参数
+  local value
+  if value="$(readParam PARAM_DATA_SERVER "${begin}" "${end}")"; then
+    PARAM_DATA_SERVER="${value}"
+  fi
+  if value="$(readParam PARAM_CONFIG_SERVER "${begin}" "${end}")"; then
+    PARAM_CONFIG_SERVER="${value}"
+  fi
+  if value="$(readParam PARAM_LICENSE "${begin}" "${end}")"; then
+    PARAM_LICENSE="${value}"
+  fi
+
+  if value="$(readParam PARAM_JSON_CONF "${begin}" "${end}")"; then
+    PARAM_JSON_CONF="${value}"
+  fi
+
+  if value="$(readParam PARAM_USER "${begin}" "${end}")"; then
+    PARAM_USER="${value}"
+  fi
+  if value="$(readParam PARAM_GROUP "${begin}" "${end}")"; then
+    PARAM_GROUP="${value}"
+  fi
+  if value="$(readParam PARAM_NON_ROOT_MODE "${begin}" "${end}")"; then
+    if value="$(getBoolParam "${value}")"; then
+      PARAM_NON_ROOT_MODE="${value}"
+    fi
+  fi
+
+  if value="$(readParam PARAM_USER_LOGIN "${begin}" "${end}")"; then
+    if value="$(getBoolParam "${value}")"; then
+      PARAM_USER_LOGIN="${value}"
+    fi
+  fi
+  toConsoleInfo "#DEBUG== install sh params, DATA_SERVER: ${PARAM_DATA_SERVER}, PARAM_CONFIG_SERVER: ${PARAM_CONFIG_SERVER}, PARAM_LICENSE:${PARAM_LICENSE}"
+  toConsoleInfo "#DEBUG== install sh params, PARAM_USER: ${PARAM_USER}, PARAM_GROUP: ${PARAM_GROUP}, PARAM_USER_LOGIN: ${PARAM_USER_LOGIN}, PARAM_NON_ROOT_MODE:${PARAM_NON_ROOT_MODE}"
+}
+
+#== 【0】【6】=清空安装临时文件
+#== ${INSTALL_DIR}_install_$$
+#== EXTERNAL_TAR_FILE=${INSTALL_DIR}/tarfile_$$.base64
+#== ${INSTALL_DIR}/Dynatrace-OneAgent.tar.xz
+#== ${INSTALL_DIR}/xzdec
+#== /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
+cleanInstallationTemporaryFiles() {
+  toLogInfo "Cleaning installation temporary files"
+
+  rm -f "${EXTERNAL_TAR_FILE}" "${INTERNAL_TAR_FILE}" "${INSTALL_DIR}/xzdec"
+  rm -Rf "${TMP_DIR}"
+
+  local keepInstallationLockFile="${1}"
+  if [ -z "${keepInstallationLockFile}" ]; then
+    removeInstallationLockFile
+  fi
+}
+
+#== 完成安装后清理临时目录
+finishInstallation() {
+  if [ $# -eq 2 ]; then
+    cleanInstallationTemporaryFiles "${2}"
+  else
+    cleanInstallationTemporaryFiles ""
+  fi
+  toLogInfo "Installation finished, PID $$, exit code: ${1}."
+  changeWorkingDir "${CURR_PATH}"
+  if [ "${CONF_LD_PRELOAD}"x = "true"x ]; then
+    exec /bin/bash && exit 0
+  fi
+  exit "${1}"
+}
+
+#**********************************************************
+# Create folders, copy files, set rights
+#**********************************************************
+
+#== 【3】=创建临时目录
+prepareTempFolder() {
+  #== 删除存在的路径
+  removeIfExists "${TMP_DIR}"
+
+  toLogInfo "Creating temporary folder $TMP_DIR"
+  createDirIfNotExistAndSetRights "${TMP_DIR}" 755
+}
+
+#== 【4】=设置文件可执行权限
+setRightsForFiles() {
+  local file="${1}"
+  local perms="${2}"
+
+  if [ -e "${file}" ]; then
+    chmod "${perms}" "${file}"
+  fi
+}
+
+setRightsForDir() {
+  local dir="${1}"
+  local perms="${2}"
+
+  if [ -d "${dir}" ]; then
+    chmod -R "${perms}" "${dir}"
+  fi
+}
+
+#== 【4】=安装类型(f:文件,d:目录)递归设置权限
+chmod4FilesRecursively() {
+  local dir="${1}"
+  local type="${2}"
+  local mask="${3}"
+  find "${dir}" -type "${type}" -exec chmod "${mask}" {} \;
+}
+
+#== 【4】=移动目录到指定位置
+moveFolderToDestination() {
+  local source="${1}"
+  local destination="${2}"
+  local fullDestination
+  fullDestination="${destination}/$(basename "${source}")"
+  local output
+
+  toLogInfo "Moving ${source} to ${destination}"
+  if [ ! -e "${fullDestination}" ]; then
+    if output="$(mv -f "${source}" "${destination}" 2>&1)"; then
+      toLogInfo "Moving Successfully."
+      return
+    fi
+    toLogWarn "Failed to move ${source} to ${destination}: ${output}, attempting to copy"
+  else
+    toLogInfo "${fullDestination} already exists, attempting to copy"
+  fi
+
+  if ! output="$(cp -Rfp "${source}" "${destination}" 2>&1)"; then
+    toLogError "Failed to copy ${source} to ${destination}: ${output}"
+  fi
+}
+
+#== 【4】=将 bin 下版本内容移动到安装目录
+installVersionedContent() {
+  toLogInfo "Installing versioned content..."
+  createDirIfNotExistAndSetRights "${AGENT_BIN_DIR}" 755
+
+  local sourceDir="${TMP_DIR}/bin"
+  if [ ! -d "${AGENT_BIN_DIR}" ]; then
+    moveFolderToDestination "${sourceDir}" "${AGENT_BIN_DIR}"
+    return
+  fi
+
+  toLogInfo "Directory ${AGENT_BIN_DIR} already exist, repairing the directory"
+  rm -rf "${AGENT_BIN_DIR}"
+  moveFolderToDestination "${sourceDir}" "${AGENT_BIN_DIR}"
+}
+
+#== 【4】=创建当前版本软连接
+
+#== 【4】=删除存在的目录
+listAndRemoveDirectoryIfExists() {
+  local directory="${1}"
+  if [ -d "${directory}" ]; then
+    toLogInfo "${directory} exists, removing it."
+    toLogInfo "Contents: $(ls -lR "${directory}")"
+    rm -rf "${directory}"
+  fi
+}
+
+#== 【4】=临时目录中conf 移动到 agent 安装目录中
+setupConfFolder() {
+  toLogInfo "Setup conf folder..."
+  #== 移动目录到指定位置
+  moveFolderToDestination "${TMP_DIR}/conf" "${INSTALL_DIR}"
+
+  chmod 755 "${AGENT_CONF_DIR}"
+
+  toLogInfo "Setup conf done."
+}
+
+#== 【4】=给距离当前目录至少 ${mindepth} 个子目录的所有文件设置权限
+chmodFilesWithMindepth() {
+  local dir="${1}"
+  local mindepth="${2}"
+  local mask="${3}"
+
+  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    #== 安装类型(f:文件,d:目录)递归设置权限
+    chmod4FilesRecursively "${1}" f "${3}"
+  else
+    #== 查找深度距离当前目录至少 ${mindepth} 个子目录的所有文件
+    find "${dir}" -mindepth "${mindepth}" -print0 | xargs -r -0 chmod "${mask}"
+  fi
+}
+
+#== 【4】=配置其他文件(cwserveragent.service、installer.version)
+setupMiscFiles() {
+  toLogInfo "Setup misc files..."
+  #== 创建 cwserveragent.service
+  createSystemdUnitFile
+  #== echo "${AGENT_INSTALLER_VERSION}" >"${INSTALL_DIR}/installer.version"
+  mv -f "${TMP_DIR}/installer.version" "${INSTALL_DIR}/"
+  toLogInfo "Setup misc done."
+}
+
+#== 【4】=创建 cwserveragent.service
+createSystemdUnitFile() {
+  toLogInfo "creating init scripts ${AGENT_SCRIPTS_DIR}"
+  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
+    return
+  fi
+  createDirIfNotExistAndSetRights "${AGENT_SCRIPTS_DIR}" 755
+  cat <<EOF >${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}
+[Unit]
+Description=${BRAND_AGENT_PRODUCT_NAME}
+After=network-online.target
+Wants=network-online.target
+
+[Service]
+User=root
+ExecStart=${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE} start
+ExecStop=${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE} stop
+Type=forking
+#Restart=always
+KillMode=process
+TimeoutSec=240
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+  setRightsForFiles "${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" 755
+  if [ "${INIT_DIR}"x != "${AGENT_SCRIPTS_DIR}"x ]; then
+    cp -f "${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" ${INIT_DIR}
+    toLogInfo "Copy scripts ${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT} to ${INIT_DIR} done."
+  fi
+  toLogInfo "creating init scripts ${AGENT_SCRIPTS_DIR} done."
+}
+
+setupAll() {
+#  moveFolderToDestination "${TMP_DIR}/package_dir/*" "${INSTALL_DIR}"
+#ls  ${TMP_DIR}/package_dir/*
+
+  #== 移动所有文件到目录
+  cp -Rfp ${TMP_DIR}/package_dir/* "${INSTALL_DIR}"
+  local installVersionFile="${INSTALL_DIR}/installer.version"
+  echo ${AGENT_INSTALLER_VERSION} > ${installVersionFile}
+#  mv ${INSTALL_DIR}/package_dir/* ${INSTALL_DIR}
+#     echo "${TMP_DIR}/*" "${INSTALL_DIR}/"
+}
+
+#== 【4】=配置 lib、conf、bin、cwserveragent.service、installer.version、plugins
+setupOptDir() {
+  createDirIfNotExistAndSetRights "${INSTALL_DIR}" 1775
+  #== 临时目录中conf 移动到 agent 安装目录中
+  setupConfFolder
+  #== 将 bin 下版本内容移动到安装目录
+  installVersionedContent
+  #== 配置其他文件(cwserveragent.service、installer.version)
+  setupMiscFiles
+}
+
+#== 【4】=复制临时目录 scripts/cwserveragent 到指定目录下
+copyScriptsToDirectory() {
+  local scriptLocation="${1}"
+  #== scripts/cwserveragent
+  local scriptFile="${TMP_DIR}/scripts/${SERVICE_SCRIPT_FILE}"
+
+  toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} begin."
+
+  local output
+  if ! output="$(cp "${scriptFile}" "${scriptLocation}/" 2>&1)"; then
+    toLogError "Failed to copy ${scriptFile} to ${scriptLocation}, output: ${output}"
+    return
+  fi
+
+  setRightsForFiles "${scriptLocation}/${SERVICE_SCRIPT_FILE}" 755
+  toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} done."
+
+  #== scripts/uninstall.sh
+#  local scriptLocation="${1}"
+#    #== scripts/cwserveragent
+#    local scriptFile="${TMP_DIR}/scripts/uninstall.sh"
+#
+#    toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} begin."
+#
+#    local output
+#    if ! output="$(cp "${scriptFile}" "${scriptLocation}/" 2>&1)"; then
+#      toLogError "Failed to copy ${scriptFile} to ${scriptLocation}, output: ${output}"
+#      return
+#    fi
+#    setRightsForFiles "${scriptLocation}/uninstall.sh" 755
+#    toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} done."
+}
+
+#== 【4】=复制临时目录 scripts/cwserveragent 到指定目录下
+copyScripts() {
+  toLogInfo "Copy scripts..."
+  #== 创建目录 /opt/cloudwise/cwserveragent/scripts
+  createDirIfNotExistAndSetRights "${AGENT_SCRIPTS_DIR}" 755
+  #== 创建目录 /opt/cloudwise/{product}/agents
+  createDirIfNotExistAndSetRights "${AGENTS_BASE_DIR}" 775
+  #== 创建目录 /opt/cloudwise/{product}/meta
+  createDirIfNotExistAndSetRights "${META_BASE_DIR}" 775
+  #== 创建目录 /opt/cloudwise/{product}/runtime
+  createDirIfNotExistAndSetRights "${AGENT_RUNTIME_DIR}" 775
+
+  #== scripts/uninstall.sh
+  local uninstallScript="${TMP_DIR}/scripts/uninstall.sh"
+  toLogInfo "Copy scripts ${uninstallScript} to ${INSTALL_DIR} begin."
+  local output
+  if ! output="$(cp "${uninstallScript}" "${INSTALL_DIR}/uninstall.sh" 2>&1)"; then
+    toLogError "Failed to copy ${uninstallScript} to ${INSTALL_DIR}/uninstall.sh, output: ${output}"
+  else
+    setRightsForFiles "${INSTALL_DIR}/uninstall.sh" 755
+  fi
+  toLogInfo "Copy scripts ${uninstallScript} to ${INSTALL_DIR} done."
+
+  #== 复制临时目录 scripts/ 到安装目录 /opt/cloudwise/cwserveragent/scripts
+  copyScriptsToDirectory "${AGENT_SCRIPTS_DIR}"
+  if [ "${INIT_DIR}"x != "${AGENT_SCRIPTS_DIR}"x ]; then
+    copyScriptsToDirectory "${INIT_DIR}"
+  fi
+  toLogInfo "Copy scripts done."
+}
+
+#== 【4】=调用 agent 64位数lib路径(lib64/installaction)执行指令 --create-cluster-timestamp-file
+createFirstClusterTimestampFile() {
+  toLogInfo "Creating firstClusterTimestamp file"
+  #== 调用 agent 64位数lib路径(lib64/installaction)执行指令 --create-cluster-timestamp-file
+  "$(getAgentInstallActionPath)" "--create-cluster-timestamp-file" >>"${LOG_FILE}" 2>&1
+}
+
+#== 【4】=配置数据存储目录
+setupDataStorageDir() {
+  toLogInfo "Setup datastorage dir..."
+  local dataStorage
+  dataStorage="$(getValueFromConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${LEGACY_AGENT_CONF_FILE}" "${DATA_STORAGE_DIR}")"
+  if [ "${dataStorage}"x = "${LOG_DIR}"x ]; then
+    toLogInfo "Detected legacy data storage setting, changing it to the new default location"
+    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${DATA_STORAGE_DIR}" "${LEGACY_AGENT_CONF_FILE}"
+    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${DATA_STORAGE_DIR}" "${INSTALLER_CONF_FILE}"
+  fi
+
+  if [ "${PARAM_DATA_STORAGE}" ]; then
+    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${PARAM_DATA_STORAGE}" "${LEGACY_AGENT_CONF_FILE}"
+    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${PARAM_DATA_STORAGE}" "${INSTALLER_CONF_FILE}"
+  fi
+
+  toLogInfo "Setup datastorage dir done."
+}
+
+#**********************************************************
+# Processing command line parameters
+#**********************************************************
+#== 【0】=help
+displayHelp() {
+  printf '%s\n' "Usage: $(basename "${INSTALLER_FILE}") [-h] [-v] "
+  #== printf '%s\n' "Usage: $(basename "${INSTALLER_FILE}") [-h] [-v] [DATA_SERVER=https://server_address:server_port] [CONFIG_SERVER=configService] [LICENSE=license] [INSTALL_DIR=install_path]"
+  local beginStr="Usage: "
+  local pad="${#beginStr}"
+
+  printf "\n\n"
+
+  pad=25
+  printf "%-${pad}s%s\n" "-h, --help" "Display this help and exit."
+  printf "%-${pad}s%s\n" "-v, --version" "Print version and exit."
+
+}
+
+printParamMessage() {
+  local paramNm="${1}"
+  local paramValue="${2}"
+  toConsoleInfo "---> Parameter ${paramNm}=${paramValue}."
+}
+
+printOtherParamMessage() {
+  local param="${1}"
+  toConsoleInfo "---> Parameter ${param}"
+}
+
+#== 【0】=大小写转化,并返回判断结果
+istrcmp() {
+  local s1
+  local s2
+  s1="$(printf '%s' "${1}" | tr '[:upper:]' '[:lower:]')"
+  s2="$(printf '%s' "${2}" | tr '[:upper:]' '[:lower:]')"
+  [ "${s1}"x = "${s2}"x ]
+}
+
+#== 【0】=
+isParamTrue() {
+  [ "${1}"x = "1"x ] || [ "${1}"x = "true"x ] || [ "${1}"x = "enable"x ] || [ "${1}"x = "yes"x ]
+}
+#== 【1】【5】=
+isParamFalse() {
+  [ "${1}"x = "0"x ] || [ "${1}"x = "false"x ] || [ "${1}"x = "disable"x ] || [ "${1}"x = "no"x ]
+}
+
+#== 【1】【5】=
+invertBoolValue() {
+  local valueToInvert="${1}"
+  if isParamFalse "${valueToInvert}"; then
+    printf '%s' "true"
+  else
+    printf '%s' "false"
+  fi
+}
+
+#== shellcheck disable=SC2003
+#== 【0】=获取参数值
+getParamValue() {
+  local paramName="${1}"
+  local input="${2}"
+  local paramNameLength="${#paramName}"
+  paramNameLength=$((paramNameLength + 1))
+  local partParam
+  partParam="$(expr substr "${input}" 1 ${paramNameLength})"
+#  partParam=$(substr "${input}" 1 ${paramNameLength})
+  if ! istrcmp "${partParam}" "${paramName}="; then
+    return 1
+  fi
+
+  local valueSeparator=$((paramNameLength + 1))
+  local value
+  value="$(expr substr "${input}" ${valueSeparator} 1000)"
+#  value=$(substr "${input}" ${valueSeparator} 1000)
+  if [ -z "${value}" ]; then
+    return 1
+  fi
+
+  printf '%s' "${value}"
+  return 0
+}
+
+#== 【0】=获取bool参数
+readBoolParam() {
+  local value
+  value="$(getParamValue "${1}" "${2}")"
+
+  if value="$(getBoolParam "${value}")"; then
+    printf "%s" "${value}"
+    return 0
+  fi
+
+  return 1
+}
+
+#== 【0】=获取bool参数
+getBoolParam() {
+  local value="${1}"
+  if [ "${value}" ]; then
+    if isParamFalse "${value}"; then
+      printf "false"
+      return 0
+    fi
+    if isParamTrue "${value}"; then
+      printf "true"
+      return 0
+    fi
+  fi
+
+  return 1
+}
+
+#== 【0】=校验格式是否规范[1、大于4个字符且不能"cw."开头,2、不能超过100字符,3、只能包含字母数字字符,连字符,下划线和点。]
+validateParameter() {
+  local name="${1}"
+  local value="${2}"
+  if [ "$(printf '%s' "${value}" | cut -c -3)"x = "cw."x ]; then
+    toConsoleError "${name} must not begin with 'cw.'"
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "${#value}" -gt 100 ]; then
+    toConsoleError "Maximum allowed length of ${name} is 100."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if printf '%s' "${value}" | grep -q "[^[:alnum:]._-]"; then
+    toConsoleError "${name} can only contain alphanumeric characters, hyphen, underscore and dot."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【0】=验证用户和用户组是否匹配
+validateUserAndGroupParameters() {
+  local user="${1}"
+  local group="${2}"
+  local permittedNameRegex='^[[:alnum:]._][[:alnum:]._-]{2,31}$'
+
+  if [ ! "${group}" ]; then
+    group="${user}"
+  fi
+
+  if ! printf '%s' "${user}" | grep -qE "${permittedNameRegex}"; then
+    toConsoleError "USER can only contain alphanumeric characters, hyphen, underscore and dot, and must have length from 3 to 32 characters."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if ! printf '%s' "${group}" | grep -qE "${permittedNameRegex}"; then
+    toConsoleError "GROUP can only contain alphanumeric characters, hyphen, underscore and dot, and must have length from 3 to 32 characters."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  #== 校验用户/用户组信息
+  validateUserPrimaryGroup "${user}" "${group}"
+}
+
+#== 【0】【3】【5】=获取系统权限信息
+getSystemEntityInfo() {
+  local database="${1}"
+  local valueToCheck="${2}"
+
+  #== 校验命令getent是否存在
+  if ! isAvailable "getent"; then
+    toLogInfo "Command getent is not available"
+    return 2
+  fi
+
+  #== 查看系统权限
+  local output
+  output="$(getent "${database}" "${valueToCheck}" 2>&1)"
+  local returnCode=$?
+
+  if [ "${returnCode}" != 0 ]; then
+    if [ "${returnCode}" != 2 ]; then
+      toLogWarn "Failed to check ${valueToCheck} in ${database} database, message: ${output}, code: ${returnCode}"
+    fi
+    return 1
+  elif [ ! "${output}" ]; then
+    toLogWarn "Failed to get user information: getent returned no output"
+  fi
+
+  printf '%s' "${output}"
+  return 0
+}
+
+#== 【0】【3】=查看用户/用户组信息
+isEntityPassedById() {
+  local database="${1}"
+  local name="${2}"
+
+  local output
+  output="$(getSystemEntityInfo "${database}" "${name}")"
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 0 ]; then
+    if [ ${returnCode} -eq 2 ]; then
+      toLogInfo "Installer will not be able to verify whether entity was passed by name or by ID"
+    fi
+    return 1
+  fi
+
+  local nameFromDatabase
+  nameFromDatabase="$(printf '%s' "${output}" | cut -d: -f1)"
+
+  if [ "${nameFromDatabase}"x = "${name}"x ]; then
+    return 1
+  fi
+
+  toLogWarn "Name from config and from ${database} system database do not match"
+  toLogWarn "Config: ${name}, database: ${nameFromDatabase}"
+  return 0
+}
+
+#== 【0】【3】【5】=用户是否存在
+userExistsInSystem() {
+  local user="${1}"
+
+  getSystemEntityInfo "passwd" "${user}" >/dev/null
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 2 ]; then
+    return ${returnCode}
+  fi
+
+  toLogInfo "Trying to determine user existence using 'id' command"
+  id "${user}" >/dev/null 2>&1
+}
+
+#== 【5】= 查看group
+groupExistsInSystem() {
+  local group="${1}"
+
+  getSystemEntityInfo "group" "${group}" >/dev/null
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 2 ]; then
+    return ${returnCode}
+  fi
+
+  toLogInfo "Installer will not be able to determine group existence"
+  return 1
+}
+
+#== 【3】=用户是否存在
+validateUserExistence() {
+  local user="${1}"
+  if ! userExistsInSystem "${user}"; then
+    toConsoleError "User named '${user}' configured for ${BRAND_PRODUCT_NAME} does not exist. Installation aborted."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【0】【3】=检查用户/用户组信息
+checkIfEntityWasNotPassedById() {
+  local database="${1}"
+  local valueToCheck="${2}"
+  local valueTypeToLog="user"
+
+  if [ "${database}"x = "group"x ]; then
+    valueTypeToLog="group"
+  fi
+
+  if isEntityPassedById "${database}" "${valueToCheck}"; then
+    toConsoleError "\"${valueToCheck}\" is not a ${valueTypeToLog} name but its ID. Installation aborted."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【0】【3】=用户权限下用户组ID
+getUserPrimaryGroupIdForComparison() {
+  local user="${1}"
+
+  local userPrimaryGroupId
+  userPrimaryGroupId="$(getSystemEntityInfo "passwd" "${user}")"
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 2 ]; then
+    printf '%s' "${userPrimaryGroupId}" | cut -d: -f4
+    return ${returnCode}
+  fi
+
+  toLogInfo "Returning user primary group name instead of its id"
+  id -gn "${user}"
+}
+
+#== 【0】【3】=获取用户组信息
+getGroupIdForComparison() {
+  local group="${1}"
+
+  local groupId
+  groupId="$(getSystemEntityInfo "group" "${group}")"
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 2 ]; then
+    printf '%s' "${groupId}" | cut -d: -f3
+    return ${returnCode}
+  fi
+
+  toLogInfo "Returning group name instead of its id"
+  printf '%s' "${group}"
+}
+
+#== 【0】【3】=校验用户/用户组信息
+validateUserPrimaryGroup() {
+  local user="${1}"
+  local group="${2}"
+
+  if ! userExistsInSystem "${user}"; then
+    return
+  fi
+
+  checkIfEntityWasNotPassedById "passwd" "${user}"
+  checkIfEntityWasNotPassedById "group" "${group}"
+
+  #== 获取用户组ID
+  local groupId
+  groupId="$(getGroupIdForComparison "${group}")"
+  #== 获取用户下用户组ID
+  local userPrimaryGroupId
+  userPrimaryGroupId="$(getUserPrimaryGroupIdForComparison "${user}")"
+
+  toConsoleInfo "#DEBUG== group \"${group}\" id: \"${groupId}\", user \"${user}\" primaryGroupId: \"${userPrimaryGroupId}\""
+  if [ "${userPrimaryGroupId}"x != "${groupId}"x ]; then
+    toConsoleError "User named \"${user}\" does not have group named \"${group}\" as its primary group. Installation aborted."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【3】=检查用户/用户组是否存在和匹配
+checkUserAndGroupFromConfig() {
+  local configUser
+  local configGroup
+  configUser="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  configGroup="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+
+  toLogInfo "Checking validity of user account '${configUser}:${configGroup}'"
+
+  if [ "${PARAM_UPGRADE}"x = "yes"x ]; then
+    validateUserExistence "${configUser}"
+  fi
+
+  #== 校验用户/用户组信息
+  validateUserPrimaryGroup "${configUser}" "${configGroup}"
+}
+
+#== 【0】=解析命令行中参数
+parseCommandLineParameters() {
+  local dataServerIsEmpty=true
+  local configServerIsEmpty=true
+  while [ $# -gt 0 ]; do
+    local param="${1}"
+    local value=
+
+    if value=$(getParamValue DATA_SERVER "${param}"); then
+      PARAM_DATA_SERVER="${value}"
+      dataServerIsEmpty=false
+      printParamMessage "DATA_SERVER" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue CONFIG_SERVER "${param}"); then
+      PARAM_CONFIG_SERVER="${value}"
+      configServerIsEmpty=false
+      printParamMessage "CONFIG_SERVER" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue LICENSE "${param}"); then
+      PARAM_LICENSE="${value}"
+      printParamMessage "LICENSE" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue JSON_CONF "${param}"); then
+      PARAM_JSON_CONF="${value}"
+      printParamMessage "JSON_CONF" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue INSTALL_DIR "${param}"); then
+      PARAM_INSTALL_DIR="${value}"
+      printParamMessage "INSTALL_DIR" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue DATA_STORAGE "${param}"); then
+      PARAM_DATA_STORAGE="${value}"
+      printParamMessage "DATA_STORAGE" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(readBoolParam DISABLE_SYSTEM_LOGS_ACCESS "${param}"); then
+      PARAM_DISABLE_SYSTEM_LOGS_ACCESS="${value}"
+      printParamMessage "DISABLE_SYSTEM_LOGS_ACCESS" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue INTERNAL_OVERRIDE_CHECKS "${param}"); then
+      if printf '%s' "${value}" | grep -wq "privileges"; then
+        PARAM_INTERNAL_NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK=true
+      fi
+
+      if printf '%s' "${value}" | grep -wq "downgrade"; then
+        SKIP_DOWNGRADE_CHECK=true
+      fi
+      printParamMessage "INTERNAL_OVERRIDE_CHECKS" "${value}"
+
+      shift
+      continue
+    fi
+
+    if value=$(readBoolParam INTERNAL_USE_UNPACK_CACHE "${param}"); then
+      PARAM_INTERNAL_USE_UNPACK_CACHE="${value}"
+      printParamMessage "INTERNAL_USE_UNPACK_CACHE" "${value}"
+      shift
+      continue
+    fi
+
+    if [ "${ARCH_ARCH}"x != "AIX"x ]; then
+      if value=$(getParamValue USER "${param}"); then
+        PARAM_USER="${value}"
+        printParamMessage "USER" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(getParamValue GROUP "${param}"); then
+        PARAM_GROUP="${value}"
+        printParamMessage "GROUP" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam NON_ROOT_MODE "${param}"); then
+        PARAM_NON_ROOT_MODE="${value}"
+        printParamMessage "NON_ROOT_MODE" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam USER_LOGIN "${param}"); then
+        PARAM_USER_LOGIN="${value}"
+        printParamMessage "USER_LOGIN" "${value}"
+        shift
+        continue
+      fi
+
+      #== 自定义参数获取
+      if value=$(getParamValue TEST "${param}"); then
+        PARAM_TEST="${value}"
+        printParamMessage "TEST" "${value}"
+        shift
+        continue
+      fi
+
+      #== 自定义bool参数
+      if value=$(readBoolParam BOOL "${param}"); then
+        PARAM_BOOL="${value}"
+        printParamMessage "BOOL" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam DISABLE_ROOT_FALLBACK "${param}"); then
+        PARAM_DISABLE_ROOT_FALLBACK="${value}"
+        printParamMessage "DISABLE_ROOT_FALLBACK" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK "${param}"); then
+        PARAM_INTERNAL_NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK="${value}"
+        printParamMessage "NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam INTERNAL_SKIP_SELINUX_POLICY_INSTALLER "${param}"); then
+        #== PARAM_INTERNAL_SKIP_SELINUX_POLICY_INSTALLER="${value}"
+        printParamMessage "INTERNAL_SKIP_SELINUX_POLICY_INSTALLER" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam DOCKER_ENABLED "${param}"); then
+        #== PARAM_INTERNAL_DEPLOYED_VIA_CONTAINER="${value}"
+        printParamMessage "DOCKER_ENABLED" "${value}"
+        shift
+        continue
+      fi
+    fi
+
+    if [ "${ARCH_ARCH}"x != "AIX"x ] && [ "${ARCH_ARCH}"x != "S390"x ]; then
+      if value=$(readBoolParam INTERNAL_DISABLE_DUMPPROC "${param}"); then
+        #== PARAM_INTERNAL_DISABLE_DUMPPROC="${value}"
+        printParamMessage "INTERNAL_DISABLE_DUMPPROC" "${value}"
+        shift
+        continue
+      fi
+    fi
+
+    if [ "${param}"x = "-h"x ] || [ "${param}"x = "--help"x ]; then
+      displayHelp
+      finishInstallation "${EXIT_CODE_OK}"
+    fi
+
+    if [ "${param}"x = "-v"x ] || [ "${param}"x = "--version"x ]; then
+      printf "%s\n" "${AGENT_INSTALLER_VERSION}"
+      finishInstallation "${EXIT_CODE_OK}"
+    fi
+    
+    printOtherParamMessage ${param}
+    shift
+  done
+
+  if [ "${configServerIsEmpty}"x = "true"x ] && [ "${dataServerIsEmpty}"x != "true"x ]; then
+    PARAM_CONFIG_SERVER="${PARAM_DATA_SERVER}"
+  fi
+}
+
+#== 【0】=校验PARAM_INSTALL_DIR是否规范【1、不能包含空格,2、不能在根目录,3、安装路径必须是绝对的】
+validateInstallPathParameter() {
+  if printf '%s' "${PARAM_INSTALL_DIR}" | grep -q "[[:space:]]"; then
+    toConsoleError "Installation path must not contain spaces."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "${PARAM_INSTALL_DIR}"x = "/"x ]; then
+    toConsoleError "Installation path must not point to the filesystem root directory."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "$(printf '%s' "${PARAM_INSTALL_DIR}" | cut -c 1)"x != "/"x ]; then
+    toConsoleError "Installation path must be absolute."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【0】=校验PARAM_DATA_STORAGE是否规范【1、不能包含空格,2、不能在根目录,3、安装路径必须是绝对的,4、数据目录不能放在安装目录下】
+validateDataStorageParameter() {
+  if printf '%s' "${PARAM_DATA_STORAGE}" | grep -q "[[:space:]]"; then
+    toConsoleError "Data storage path must not contain spaces."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "${PARAM_DATA_STORAGE}"x = "/"x ]; then
+    toConsoleError "Data storage path must not point to the filesystem root directory."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "$(printf '%s' "${PARAM_DATA_STORAGE}" | cut -c 1)"x != "/"x ]; then
+    toConsoleError "Data storage path must be absolute."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if printf '%s' "${PARAM_DATA_STORAGE}" | grep -q "^${INSTALL_DIR}"; then
+    toConsoleError "Data storage path must not be located within ${INSTALL_DIR}."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+
+#**********************************************************
+# Config files
+#**********************************************************
+
+#== 【3】【5】=格式化空间大小(将 1024KiB 格式化成 1MiB, 将 1024MiB 格式化成 1GiB,...)
+formatSize() {
+  local sizeInKiB="${1}"
+  local formattedSize
+
+  for symbol in "KiB" "MiB" "GiB" "TiB"; do
+    if printf '%s' "${sizeInKiB}" | awk '$1 >= 1024 { exit 1; }'; then
+      formattedSize="${sizeInKiB} ${symbol}"
+      break
+    fi
+    sizeInKiB="$(printf '%s' "${sizeInKiB}" | awk '{ print $1 / 1024 }')"
+  done
+
+  printf '%s' "${formattedSize}"
+}
+
+#== 【3】【5】=
+cropSizeValue() {
+  local size="${1}"
+  local value
+  local unit
+  value="$(printf '%s' "${size}" | cut -d' ' -f1)"
+  unit="$(printf '%s' "${size}" | cut -d' ' -f2)"
+  printf '%.2f %s' "${value}" "${unit}"
+}
+
+#== 【3】【5】=检查目录可用空间(单位:k)
+checkFreeSpace() {
+  local path="${1}"
+  local requiredSpaceInKiB="${2}"
+  local dfOutput
+  local baseFilesystem
+  local freeSpace
+  local formattedRequiredSpace
+  local formattedFreeSpace
+
+  #== shellcheck disable=SC2086
+  dfOutput="$(df -P ${AIX_DF_SPECIFIC_FLAG} "${path}" | tail -n +2)"
+  #== 文件系统目录
+  baseFilesystem="$(printf "%s" "${dfOutput}" | awk '{ print $NF }')"
+  #== 剩余空间(单位:k)
+  freeSpace="$(printf "%s" "${dfOutput}" | awk '{ print $4 }')"
+
+  #== 格式化空间大小(将 1024KiB 格式化成 1MiB, 将 1024MiB 格式化成 1GiB,...)
+  formattedRequiredSpace="$(formatSize "${requiredSpaceInKiB}")"
+  toLogInfo "Filesystem with ${path} is mounted under ${baseFilesystem}. Space required: ${formattedRequiredSpace}."
+
+  if [ ! "${freeSpace}" ]; then
+    printf 'Cannot determine amount of free space on %s (filesystem mounted under %s)' "${path}" "${baseFilesystem}"
+    return 1
+  fi
+
+  #== 格式化空间大小(将 1024KiB 格式化成 1MiB, 将 1024MiB 格式化成 1GiB,...)
+  formattedFreeSpace="$(formatSize "${freeSpace}")"
+  toLogInfo "Available free space: ${formattedFreeSpace}"
+
+  if [ "${freeSpace}" -lt "${requiredSpaceInKiB}" ]; then
+    printf 'Not enough free space on %s (filesystem mounted under %s). ' "${path}" "${baseFilesystem}"
+    printf 'Required: %s, available: %s' "$(cropSizeValue "${formattedRequiredSpace}")" "$(cropSizeValue "${formattedFreeSpace}")"
+    return 2
+  fi
+
+  printf 'Free space is sufficient'
+  return 0
+}
+
+#== 【3】=检查文件系统类型
+
+#== 【3】=检查安装目录空间
+checkInstallPathFreeSpace() {
+  local externalTarSize=${EXTERNAL_TAR_SIZE}
+  local artifactsSize=${ARTIFACTS_SIZE}
+  #== use 10% additional margin
+  local requiredSpace=$((externalTarSize + artifactsSize * 11 / 10))
+  #== convert to kibibytes
+  requiredSpace=$((requiredSpace / 1024))
+
+  toConsoleInfo "Checking free space in ${INSTALL_DIR}"
+
+  local message
+  #== 检查目录可用空间(单位:k)
+  message="$(checkFreeSpace "${INSTALL_DIR}" "${requiredSpace}")"
+  case $? in
+  0) toLogInfo "${message}" ;;
+  1) toConsoleWarn "${message}. Installation may be incomplete." ;;
+  2)
+    toConsoleError "${message}"
+    finishInstallation "${EXIT_CODE_NOT_ENOUGH_SPACE}"
+    ;;
+  esac
+}
+
+#== 【3】=获取指定路径的文件系统信息
+getFilesystemInfo() {
+  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    mount | grep " ${1} "
+  else
+    grep " ${1} " /proc/self/mounts
+  fi
+}
+
+#== 【3】=检查目录指定权限信息
+checkAccessRightsTo() {
+  local dir="${1}"
+  toLogInfo "Checking access to ${dir}..."
+  #== 获取文件权限信息
+  local accessRights
+  accessRights="$(arch_getAccessRights "${dir}" | cut -c 2-4)"
+  if ! printf '%s' "${accessRights}" | grep -q rwx; then
+    toConsoleError "Insufficient permissions on ${dir}: '${accessRights}'."
+    toLogInfo "$(ls -dl "${dir}" 2>&1)"
+    finishInstallation "${EXIT_CODE_INSUFFICIENT_PERMISSIONS}"
+  fi
+
+  local dfResult
+  local filesystem
+  local filesystemInfo
+  dfResult="$(df -P "${dir}")"
+  #== 获取目录文件系统路径
+  filesystem="$(printf '%s' "${dfResult}" | tail -1 | awk '{ print $NF }')"
+  #== 获取指定路径的文件系统信息
+  filesystemInfo="$(getFilesystemInfo "${filesystem}")"
+  if ! printf '%s' "${filesystemInfo}" | grep -qw rw; then
+    toLogWarn "df-based check determined filesystem mounted under ${filesystem} as readonly, trying fallback."
+    toLogWarn "Filesystem access rights: '${filesystemInfo}'"
+    toLogWarn "df returned: ${dfResult}"
+
+    if ! testWriteAccessToDir "${dir}"; then
+      toConsoleError "readonly filesystem mounted under ${filesystem}"
+      finishInstallation "${EXIT_CODE_INSUFFICIENT_PERMISSIONS}"
+    fi
+  fi
+
+  toLogInfo "Rights on directory ${dir} are sufficient"
+} 2>>"${LOG_FILE}"
+
+#== 【3】=目录是否写权限
+checkIfInstallationPathIsWriteable() {
+  for dir in "${@}"; do
+    if [ -d "${dir}" ]; then
+      checkAccessRightsTo "${dir}"
+      break
+    fi
+  done
+}
+
+#== 【3】=检查目录权限
+checkAccessRightsToDirs() {
+  checkIfInstallationPathIsWriteable "${INSTALL_DIR}" "${BASE_INSTALL_DIR}" "${INSTALL_BASE}" /
+
+  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
+    checkAccessRightsTo "${INIT_DIR}"
+  fi
+}
+
+
+#== 【0】=检查系统是 LINUX、AIX及指令集(X86_64\IA64\X86)
+checkSystemCompatibility() {
+  local expectedPlatform="LINUX"
+  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    expectedPlatform="AIX"
+  fi
+
+  #== 获取系统名称
+  local platform
+  platform="$(uname | sed -e 's/_.*//' | sed -e 's/\///' | tr '[:lower:]' '[:upper:]')"
+  if [ "${platform}"x != "${expectedPlatform}"x ]; then
+    printf "Cannot determine platform or platform not supported: <%s>" "${platform}"
+    return 1
+  fi
+
+  #== 检查系统指令集(X86_64\IA64\X86)
+  local detectedArchitecture
+  if ! detectedArchitecture="$(arch_checkArchitectureCompatibility)"; then
+    printf "Cannot determine architecture or architecture not supported: <%s>" "${detectedArchitecture}"
+    return 1
+  fi
+
+  printf 'Detected platform: %s' "${platform}"
+  if [ "${detectedArchitecture}" ]; then
+    printf ' arch: %s' "${detectedArchitecture}"
+  fi
+
+  return 0
+}
+
+#== 【4】=从sh文件读取压缩文件 tarfile_$$.base64
+separateExternalTar() {
+  toLogInfo "Determining begin of tar archive..."
+  local tarBegin
+  local tarEnd
+  #== 压缩文件开始行数,占位符为【#################ENDOFSCRIPTMARK############】
+  tarBegin="$(locateDelimiter "#################ENDOFSCRIPTMARK############" "")"
+  tarBegin=$((tarBegin + 1))
+  #== 压缩文件结束行数,占位符为【----SIGNED-INSTALLER】
+  tarEnd="$(locateDelimiter "----SIGNED-INSTALLER" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS})"
+  toLogInfo "tarBegin=${tarBegin} tarEnd=${tarEnd}"
+
+  if [ ! "${tarEnd}" ]; then
+    toConsoleError "S/MIME signature is missing, installation package is corrupted."
+    finishInstallation "${EXIT_CODE_CORRUPTED_PACKAGE}"
+  fi
+
+  local tarLength=$((tarEnd - tarBegin))
+  toLogInfo "tarLength=${tarLength}"
+  #== 从sh文件读取压缩文件 tarfile_$$.base64
+  tail -n +"${tarBegin}" "${INSTALLER_FILE}" 2>/dev/null | head -${tarLength} >"${EXTERNAL_TAR_FILE}"
+}
+
+#== 【4】【5】=进入目录
+changeWorkingDir() {
+  if ! cd "${1}"; then
+    toLogError "Failed to change working directory to ${1}"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+#== shellcheck disable=SC2181
+#== 【4】=解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+unpackArchiveWithoutCache() {
+  #== 解压文件编码
+  local base64Binary="${UNPACK_BINARY}"
+  #== 解压文件编码参数
+  local base64BinaryArgs="${UNPACK_BINARY_ARGS}"
+  #== 使用xzdec命令可以进行liblzma为基础的xz文件解压缩
+  local xzBinary="${INSTALL_DIR}/xzdec"
+  #== 进入目录
+  changeWorkingDir "${INSTALL_DIR}"
+
+  if ! isAvailable tar; then
+    toConsoleError "tar binary not found. Setup can't continue"
+    finishInstallation "${EXIT_CODE_MISCONFIGURED_ENVIRONMENT}"
+  fi
+
+  local totalLines
+  if ! isAvailable "${base64Binary}"; then
+    toLogInfo "${base64Binary} not found. Falling back to openssl decode"
+    if ! isAvailable openssl; then
+      toConsoleError "Neither ${base64Binary} nor openssl can be found. Setup can't continue"
+      finishInstallation "${EXIT_CODE_MISCONFIGURED_ENVIRONMENT}"
+    fi
+    #== 如果没有 base64 命令,则使用 openssl 解压文件
+    base64Binary="openssl"
+    base64BinaryArgs="enc -base64 -d -in"
+
+    if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+      #truncate the first and the last one line due to specific format of uuencode on aix
+      totalLines="$(wc -l "${EXTERNAL_TAR_FILE}" | awk '{print $1}')"
+      head -$((totalLines - 1)) "${EXTERNAL_TAR_FILE}" 2>/dev/null | tail +2 >"${EXTERNAL_TAR_FILE}.$$"
+      mv -f "${EXTERNAL_TAR_FILE}.$$" "${EXTERNAL_TAR_FILE}"
+    fi
+  fi
+
+  {
+    #== base64 转编码后,执行 tar 解压
+    "${base64Binary}" "${base64BinaryArgs}" "${EXTERNAL_TAR_FILE}" | tar -x -p -f -
+  } 2>>"${LOG_FILE}"
+
+  #== base64 转编码 或 tar解压失败
+  if [ $? -gt 0 ]; then
+    toConsoleError "Archive is corrupted or memory allocation failed. Installation aborted."
+    finishInstallation "${EXIT_CODE_NOT_ENOUGH_MEMORY}"
+  fi
+
+  #== 进入目录
+  changeWorkingDir "${TMP_DIR}"
+  {
+    #== 使用xzdec命令解压后,执行 tar 解压
+    "${xzBinary}" "${INTERNAL_TAR_FILE}" | tar -x -p -f -
+  } 2>>"${LOG_FILE}"
+
+  if [ $? -gt 0 ]; then
+    toConsoleError "XZ compressed archive is corrupted or memory allocation failed. Installation aborted."
+    finishInstallation "${EXIT_CODE_NOT_ENOUGH_MEMORY}"
+  fi
+  #== 进入目录
+  changeWorkingDir "${INSTALL_DIR}"
+}
+
+#== 【4】=解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+unpackArchive() {
+  toConsoleInfo "Unpacking. This may take a while..."
+
+  #== 【参数】是否使用解压缓存
+  if [ "${PARAM_INTERNAL_USE_UNPACK_CACHE}"x = "true"x ]; then
+    if [ -d "${UNPACK_CACHE}" ]; then
+      toLogInfo "Unpack cache will be used."
+      cp -Rp "${UNPACK_CACHE}"/* "${TMP_DIR}"
+    else
+      toLogInfo "Unpack cache does not exist."
+      mkdir -p "${UNPACK_CACHE}"
+      #== 解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+      unpackArchiveWithoutCache
+      cp -Rp "${TMP_DIR}"/* "${UNPACK_CACHE}"
+    fi
+  else
+    toLogInfo "Unpacking without cache"
+    #== 解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+    unpackArchiveWithoutCache
+  fi
+
+  toConsoleInfo "Unpacking complete."
+}
+
+#== 【3】=检查容器部署、运行情况、配置参数
+#isDeployedInsideOpenVZContainer
+#isProcessRunningInContainer
+#isDeployedViaContainer
+
+
+#== Checking if libc is new enough
+#== 【3】=格式化version
+format_version() {
+  printf '%s' "$@" | awk -F. '{ printf("%03d%03d%03d\n", $1,$2,$3); }'
+}
+
+#== 【3】=格式化路径
+format_patch() {
+  printf '%s' "$@" | tail -n 1 | awk -F- '{ printf("%d\n", $3); }'
+}
+
+#== 【3】=检查是否降级安装
+checkIfDowngrade() {
+  if [ "${SKIP_DOWNGRADE_CHECK}"x = "true"x ]; then
+    toConsoleInfo "Skipped downgrade check"
+    return
+  fi
+
+  local installVersionFile="${INSTALL_DIR}/installer.version"
+  if [ ! -f "${installVersionFile}" ]; then
+    toLogWarn "Could not perform downgrade check, ${installVersionFile} file is missing"
+    return
+  fi
+
+  local oldVersion
+  oldVersion="$(cat "${installVersionFile}")"
+  if [ "$(format_version "${AGENT_INSTALLER_VERSION}")" -eq "$(format_version "${oldVersion}")" ]; then
+    toConsoleError "${BRAND_PRODUCT_NAME} is already installed, please uninstall the old version using [${INSTALL_DIR}/uninstall.sh]."
+#    toLogError "Attempted downgrade from ${oldVersion} to ${AGENT_INSTALLER_VERSION}"
+    finishInstallation "${EXIT_CODE_UNSUPPORTED_DOWNGRADE}"
+  fi
+}
+
+#== 【3】=检查是否已经安装
+checkIfAlreadyInstalled() {
+  if [ -f "${INSTALL_DIR}/uninstall.sh" ] && [ -f "${AGENT_BIN_DIR}/${AGENT_PROC}" ] ; then
+#    toConsoleError "Agent already installed. Uninstalling previous version."
+#    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+
+    checkIfDowngrade
+    PARAM_UPGRADE="yes"
+  else
+#  		if [ -f "${SIF_AGENT_INSTALL_PATH}/lib64/${AGENT_BIN}" ]; then
+#  			sif_toConsoleError "Upgrade is not possible because uninstall script is missing"
+#  			finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+#  		fi
+      local fileNum
+      fileNum=`ls ${INSTALL_DIR} | wc -l`
+      if [ "${fileNum}" -gt 1 ];then
+         toConsoleError "${INSTALL_DIR} is not empty. Please clean or change the installation directory manually."
+         finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+      fi
+  fi
+
+
+
+}
+
+#**********************************************************
+# Init related functions
+#**********************************************************
+
+#== clears dependencies in LSB init script
+#== 【5】=清除LSB init脚本中的依赖项
+clearDependenciesInLSBInit() {
+  local file="${1}"
+  toConsoleInfo "Clearing dependencies in file ${file}"
+  awk '
+		BEGIN {
+			req_start_found=0;
+			req_stop_found=0;
+			REQ_START="# Required-Start:";
+			REQ_STOP="# Required-Stop:";
+			PATTERN_REQ_START="^" REQ_START;
+			PATTERN_REQ_STOP="^" REQ_STOP;
+		}
+		{
+			if ($0 ~ PATTERN_REQ_START && req_start_found == 0) {
+				print REQ_START;
+				req_start_found++;
+			} else if ($0 ~ PATTERN_REQ_STOP && req_stop_found == 0) {
+				print REQ_STOP;
+				req_stop_found++;
+			} else
+				print $0
+		}' "${file}" >"${file}.tmp" && mv -f "${file}.tmp" "${file}"
+
+  chmod +x "${file}"
+}
+
+#== 【5】=指定目录添加自动启动脚本
+addScriptToSystemvAutostart() {
+  local prefix="${1}"
+  local file="${2}"
+  local suffix="${3}"
+
+  toLogInfo "Adding ${file} to autostart"
+  #== 自动启动工具
+  if ! runAutostartAddingTool "${prefix}" "${file}" "${suffix}"; then
+    toLogWarn "Failed to add ${file} script to autostart. Trying without dependencies..."
+    #== 清除LSB init脚本中的依赖项
+    clearDependenciesInLSBInit "${INIT_DIR}/${file}"
+    if ! runAutostartAddingTool "${prefix}" "${file}" "${suffix}"; then
+      toConsoleError "Cannot add ${file} to autostart. For details, see: ${LOG_FILE}"
+    fi
+  fi
+}
+
+#== 【5】=指定目录添加自动启动脚本
+addScriptsToAutostart() {
+  local prefix="${1}"
+  local suffix="${2}"
+  #== 指定目录添加自动启动脚本
+  addScriptToSystemvAutostart "${prefix}" "${SERVICE_SCRIPT_FILE}" "${suffix}"
+}
+
+#== 【5】=设置自动启动
+#setupSystemvAutostart() {
+#  toLogInfo "Adding ${BRAND_AGENT_PRODUCT_NAME} to autostart..."
+#
+#  if [ -x /usr/bin/update-rc.d ]; then #Ubuntu
+#    #== 指定目录添加自动启动脚本
+#    addScriptsToAutostart "/usr/bin/update-rc.d " "defaults"
+#  elif [ -x /usr/sbin/update-rc.d ]; then #Ubuntu
+#    addScriptsToAutostart "/usr/sbin/update-rc.d " "defaults"
+#  elif [ -x /sbin/chkconfig ]; then #RedHat
+#    addScriptsToAutostart "/sbin/chkconfig --add "
+#  elif [ -x /usr/lib/lsb/install_initd ]; then #Suse
+#    addScriptsToAutostart "/usr/lib/lsb/install_initd ${INIT_DIR}/"
+#  elif [ "${ARCH_ARCH}"x = "AIX"x ]; then
+#    #== 【不存在】
+#    arch_setAutostart
+#  else
+#    toConsoleError "Couldn't add ${BRAND_AGENT_PRODUCT_NAME} to autostart. Please adjust and add it manually."
+#  fi
+#}
+
+#== 【5】=设置 cwserveragent.service 中用户
+setServiceScriptUser() {
+  if ! isNonRootModeEnabled; then
+    return
+  fi
+  #== 读取用户名(调用 agent 64位数lib路径(lib64/installaction)执行指令 --get-user)
+  local user
+  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  #== 修改 cwserveragent.service 中用户信息
+  sed -i "s/User=.*/User=${user}/g" "${SYSTEMD_UNIT_FILES_DIR}/${SYSTEMD_UNIT_FILE_AGENT}"
+}
+
+#== 【5】=配置自动启动 cwserveragent.service
+setupSystemdAutostart() {
+  mv -f "${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" "${SYSTEMD_UNIT_FILES_DIR}/"
+  setRightsForFiles "${SYSTEMD_UNIT_FILES_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" 644
+
+  #== 设置 cwserveragent.service 中用户
+  setServiceScriptUser
+
+  if isAvailable restorecon; then
+    restorecon "${SYSTEMD_UNIT_FILES_DIR}/${SYSTEMD_UNIT_FILE_AGENT}"
+  fi
+
+  #== 执行 systemctl 命令配置允许开机启动 cwserveragent.service
+  executeSystemctlCommand enable "${SYSTEMD_UNIT_FILE_AGENT}"
+  #== 执行 systemctl 命令重新加载模块
+  executeSystemctlCommand daemon-reload ""
+}
+
+#== 【6】=运行service 脚本
+execIntoServiceScript() {
+  toConsoleInfo "${SERVICE_SCRIPT_FILE} will be started via exec()"
+  #== 清除安装临时文件
+  cleanInstallationTemporaryFiles
+  toLogInfo "Installation finished, PID $$."
+
+  local command="exec ${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE} exec"
+  toLogInfo "Executing: ${command}"
+  #== 执行 cwserveragent exec
+  ${command}
+
+  toLogError "Could not execute: ${command}"
+  finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+}
+
+#== 【6】=运行agent
+runAgents() {
+  toConsoleInfo "Starting agents..."
+
+  toLogInfo "Using ${INIT_SYSTEM} to start the agent"
+  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
+    #== 运行初始化命令(通过 service start 方式 或 直接运行可执行命令)
+    executeInitScriptCommand start "true"
+  else
+    #== 执行 systemctl start 命令
+    executeSystemctlCommand start "${SYSTEMD_UNIT_FILE_AGENT}"
+  fi
+
+  if [ $? -eq 0 ]; then
+    toConsoleInfo "${SERVICE_SCRIPT_FILE} service started"
+  else
+    toConsoleError "Failed to start service: ${SERVICE_SCRIPT_FILE}, it is possible that your init system is not functioning properly. For details, see: ${LOG_FILE}"
+  fi
+}
+
+#== 【5】=配置自动启动
+#setupAutostart() {
+#  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
+#    #== 设置自动启动
+#    setupSystemvAutostart
+#  else
+#    #== 配置自动启动 cwserveragent.service
+#    setupSystemdAutostart
+#  fi
+#}
+
+#**********************************************************
+# Process agent related functions
+#**********************************************************
+
+#== 【5】=创建 agent 状态文件
+createAgentStateFile() {
+  local path="${1}"
+  local agentStateContents="RUNNING"
+
+  toLogInfo "Writing ${agentStateContents} to ${path} file"
+  {
+    printf "%s" "${agentStateContents}" >"${path}.tmp"
+    mv -f "${path}.tmp" "${path}"
+  } 2>>"${LOG_FILE}"
+}
+
+#== 【0】=检查全路径是否可读权限(目录最深100层)
+checkIfPathIsGloballyReadable() {
+  local path="${1}"
+  local sourcePath="${path}"
+  local maxDepth=100
+  while [ "${path}"x != "/"x ]; do
+    #== 【0】=获取文件权限信息
+    local accessRights
+    accessRights="$(arch_getAccessRights "${path}")"
+    if ! printf '%s' "${accessRights}" | cut -c 8-10 | grep -qE "r.[xt]"; then
+      toConsoleError "Insufficient access rights (${accessRights}) on: ${path}"
+      toConsoleError "${sourcePath} path must be globally readable (r-x permissions for others)."
+      toConsoleError "Please adjust the permissions and then retry the installation."
+      finishInstallation "${EXIT_CODE_INSUFFICIENT_PERMISSIONS}"
+    fi
+
+    path="$(dirname "${path}")"
+
+    maxDepth=$((maxDepth - 1))
+    if [ "${maxDepth}" -eq 0 ]; then
+      toLogWarn "Unable to verify access rights on ${path}"
+      return
+    fi
+  done
+}
+
+#== 【0】=创建 PARAM_INSTALL_DIR -> INSTALL_DIR 软连接
+createSymlinkToInstallLocation() {
+  #== INSTALL_DIR 不能是软链接地址
+  if [ -L "${INSTALL_DIR}" ] && [ ! -e "${INSTALL_DIR}" ]; then
+    toConsoleError "Detected that ${INSTALL_DIR} is a dangling symlink, please remove it and then retry the installation"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== PARAM_INSTALL_DIR 和 INSTALL_DIR 都不能是空地址
+  if [ "${PARAM_INSTALL_DIR}"x = "${INSTALL_DIR}"x ] || [ -z "${PARAM_INSTALL_DIR}" ]; then
+    return
+  fi
+
+  #== INSTALL_DIR 是软连接,并获取 PARAM_INSTALL_DIR 和 INSTALL_DIR 的真实地址
+  if [ -L "${INSTALL_DIR}" ] && [ "$(readLink -m "${PARAM_INSTALL_DIR}")"x = "$(readLink -m "${INSTALL_DIR}")"x ]; then
+    return
+  fi
+
+  #== 目录存在,表示未卸载
+  if [ -e "${INSTALL_DIR}" ]; then
+    toConsoleError "Leftovers from previous agent installation detected"
+    toConsoleError "If you wish to use INSTALL_DIR parameter then perform a cleanup by following these steps:"
+    toConsoleError "1. Uninstall the agent"
+    toConsoleError "2. Restart all applications that have Deep Monitoring enabled (host restart is fine as well)"
+    toConsoleError "3. Remove ${INSTALL_DIR}"
+    toConsoleError "and then retry the installation."
+    toConsoleError "#DEBUG==For further information please visit ${HELP_URL}/command-line-install"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== 创建不存在的目录
+  createDirIfNotExistAndSetRights "${PARAM_INSTALL_DIR}" 1775
+  #== 检查全路径是否可读权限(目录最深100层)
+  checkIfPathIsGloballyReadable "${PARAM_INSTALL_DIR}"
+  #== 创建不存在的目录 /opt/cloudwise
+  createDirIfNotExistAndSetRights "${BASE_INSTALL_DIR}" 755
+
+  #== 创建 PARAM_INSTALL_DIR -> INSTALL_DIR
+  local lnOutput
+  if lnOutput="$(ln -fs "${PARAM_INSTALL_DIR}" "${INSTALL_DIR}" 2>&1)"; then
+    toConsoleInfo "Symlink ${INSTALL_DIR} -> ${PARAM_INSTALL_DIR} created"
+  else
+    toConsoleError "Failed to create symlink ${INSTALL_DIR} -> ${PARAM_INSTALL_DIR}, aborting installation: ${lnOutput}"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+#== 【5】=修改文件系统用户和用户组
+changeFilesOwnership() {
+  local user
+  local group
+  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  group="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+
+  toLogInfo "Changing ownership of files to root:${group}"
+  #== /opt/cloudwise/cwserveragent 目录下用户和用户组
+  commandErrorWrapper chown "${user}:${group}" "${AGENT_BASE_DIR}"
+  commandErrorWrapper chown -R "${user}:${group}" "${INSTALL_DIR}"
+  toLogInfo "Recursively changing group ownership of ${AGENT_CONF_DIR} to ${group}"
+
+}
+
+#== 【5】=文件用户权限检查
+fileCapabilitiesCompatibilityCheck() {
+  #== 获取配置用户
+  local user
+  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  #== 获取OS bin配置路径 /opt/cloudwise/cwserveragent/lib64/cloudwiseosconfig
+  if output="$("$(getOsConfigBinPath)" file-capabilities-compatibility-check "${user}" 2>&1)"; then
+    return 0
+  fi
+
+  if [ "${PARAM_NON_ROOT_MODE}" ]; then
+    toConsoleWarn "Failed to enable non-privileged mode, kernel does not support file capabilities. For details, see: ${LOG_FILE}"
+    toLogWarn "Capabilities test output: ${output}"
+  else
+    toConsoleInfo "Non-privileged mode was not enabled, kernel does not support file capabilities. For details, see: ${LOG_FILE}"
+    toLogInfo "Capabilities test output: ${output}"
+  fi
+  #== 设置删除root权限
+  "$(getAgentInstallActionPath)" "--set-drop-root-privileges" "false" >>"${LOG_FILE}" 2>&1
+  return 1
+}
+
+
+#**********************************************************
+# User and group related functions
+#**********************************************************
+
+#== 【5】=添加用户组
+addGroup() {
+  local group="${1}"
+
+  if groupExistsInSystem "${group}"; then
+    toLogInfo "Group '${group}' already exists"
+    return 0
+  fi
+
+  local errorMessage
+  errorMessage="$(groupadd "${group}" 2>&1)"
+  local returnCode=$?
+
+  case ${returnCode} in
+  0) toLogInfo "Group '${group}' successfully created" ;;
+  9) toLogInfo "Group '${group}' already exists" ;;
+  *)
+    toLogError "Error occured while adding '${group}' group, return code: ${returnCode}, message ${errorMessage}"
+    return 1
+    ;;
+  esac
+  return 0
+}
+
+#== 【5】=添加用户
+addUser() {
+  local user="${1}"
+  local group="${2}"
+  local groupCreated="${3}"
+  local errorMessage
+  local mod="/bin/false"
+  if [ "${PARAM_USER_LOGIN}"x == "true"x ]; then
+    mod="/bin/bash"
+  fi
+  if userExistsInSystem "${user}"; then
+    toLogInfo "User '${user}' already exists."
+    #-- 权限更新
+#    errorMessage="$(usermod -s "${mod}" "${user}" 2>&1)"
+    return 0
+  fi
+
+  if [ "${groupCreated}" -eq 0 ]; then
+    errorMessage="$(useradd -r --shell "${mod}" -g "${group}" "${user}" 2>&1)"
+  else
+    errorMessage="$(useradd -r --shell "${mod}" "${user}" 2>&1)"
+  fi
+
+  local returnCode=$?
+  if [ ${returnCode} -ne 0 ]; then
+    toConsoleError "Failed to create user '${user}'"
+    toLogError "Error occured while adding '${user}' user, return value: ${returnCode}, error message: ${errorMessage}."
+    return 1
+  fi
+
+  toConsoleInfo "User '${user}' added successfully."
+  return 0
+}
+
+#== 【5】=添加用户和用户组信息
+addUserAndGroup() {
+  local user="${1}"
+  local group="${2}"
+  #== 添加用户组
+  addGroup "${group}"
+  #== 添加用户
+  addUser "${user}" "${group}" $?
+}
+
+#== 【5】=配置用户和用户组
+handleUser() {
+  toLogInfo "Processing user and group..."
+  local user
+  local group
+  #== 读取用户名(调用 agent 64位数lib路径(lib64/installaction)执行指令 --get-user)
+  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  #== 读取用户组(调用 agent 64位数lib路径(lib64/installaction)执行指令 --get-group)
+  group="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  #== 添加用户和用户组信息
+  addUserAndGroup "${user}" "${group}"
+}
+
+#== 【4】=获取agent preLoad 安装路径,并校验是否可执行
+checkCompatibilityWithInstallActionBinary() {
+  local output
+  #== 获取agent preLoad 安装路径,并校验是否可执行
+  output="$("$(getAgentInstallActionPathPreInstallation)" "--sanity-check" 2>&1)"
+  local exitCode=$?
+  toLogAdaptive ${exitCode} "Compatibility check exit code = ${exitCode}, output = ${output}"
+  if [ ${exitCode} -ne 0 ] || [ "${output}"x != "SUCCESS"x ]; then
+    toConsoleError "System compatibility check failed, this may be caused by a problem with glibc, dynamic loader or incompatible operating system version."
+    toConsoleError "Detected version: $(detectLinuxDistribution)"
+    toConsoleError "For a list of supported distributions and versions, see: ${HELP_URL}"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+#== 【4】=【部署】解压文件
+extractFiles() {
+  umask 000
+  toConsoleInfo "Extracting..."
+  #== 从sh文件读取压缩文件 tarfile_$$.base64
+  separateExternalTar
+  #== 解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+  unpackArchive
+  umask 022
+}
+
+#== 【3】=检查发现初始化系统信息(INIT_SYSTEM、INIT_SYSTEM_VERSION、INIT_DIR)
+detectInitSystem() {
+  #== 获取初始化系统信息 (INIT_SYSTEM、INIT_SYSTEM_VERSION)
+  checkInitSystem
+  toLogInfo "Detected init system: ${INIT_SYSTEM}, version: ${INIT_SYSTEM_VERSION}"
+  #== 设置系统初始化脚本目录
+  if ! setLocationOfScripts; then
+    toConsoleError "Cannot determine location of init scripts."
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+#== 【4】=【旧配置】读取旧配置参数
+readLegacySetting() {
+  local paramName="${1}"
+  local value
+  value="$(sed -n "s|^${paramName}=||p" "${LEGACY_AGENT_CONF_FILE}" | tr -d '\r' 2>/dev/null)"
+  if [ "${value}" ]; then
+    local valueToPrint="${value}"
+    if [ "${paramName}"x = "${CONF_FIELD_NM_LICENSE}"x ]; then
+      valueToPrint="***"
+    fi
+    toLogInfo "Read legacy value: ${paramName} = ${valueToPrint}"
+  fi
+
+  printf '%s' "${value}"
+}
+
+#== 【4】=【旧配置】迁移旧配置文件
+migrateLegacySettingsFromAgentConf() {
+  toLogInfo "Looking for legacy config to migrate."
+  #== 旧配置文件
+  if [ ! -f "${LEGACY_AGENT_CONF_FILE}" ]; then
+    toLogInfo "Unable to read agent config file, skipping legacy config migration"
+    return
+  fi
+
+  if [ ! "${PARAM_CONFIG_SERVER}" ]; then
+    local configServerValue
+    configServerValue="$(readLegacySetting ${CONF_FIELD_NM_CONFIG_SERVER})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_CONFIG_SERVER}: ${PARAM_CONFIG_SERVER}"
+    if [ "${configServerValue}"x = "http://localhost:8020"x ]; then
+      toLogInfo "Param 'server' has default value set, skipping it"
+    else
+      PARAM_CONFIG_SERVER="${configServerValue}"
+    fi
+  fi
+
+  if [ ! "${PARAM_DATA_SERVER}" ]; then
+    PARAM_DATA_SERVER="$(readLegacySetting ${CONF_FIELD_NM_DATA_SERVER})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_DATA_SERVER}: ${PARAM_DATA_SERVER}"
+  fi
+
+  if [ ! "${PARAM_LICENSE}" ]; then
+    PARAM_LICENSE="$(readLegacySetting ${CONF_FIELD_NM_LICENSE})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_LICENSE}: ${PARAM_LICENSE}"
+  fi
+
+  if [ ! "${PARAM_JSON_CONF}" ]; then
+    PARAM_JSON_CONF="$(readLegacySetting ${CONF_FIELD_NM_JSON_CONF})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_JSON_CONF}: ${PARAM_JSON_CONF}"
+  fi
+
+  if [ ! "${PARAM_USER}" ]; then
+    PARAM_USER="$(readLegacySetting ${CONF_FIELD_NM_USER})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_USER}: ${PARAM_USER}"
+
+    if [ ! "${PARAM_GROUP}" ]; then
+      PARAM_GROUP="$(readLegacySetting ${CONF_FIELD_NM_GROUP})"
+      toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_GROUP}: ${PARAM_GROUP}"
+    fi
+  fi
+}
+listProcesses() {
+  local errorMessage="${1}"
+  local includeRegex="${2}"
+
+  local excludeRegex=" grep"
+  if [ "${3}" ]; then
+    excludeRegex="grep|${3}"
+  fi
+
+  toLogInfo "#DEBUG== listProcesses command: ps -e -o \"pid,args\" 2>&1 | grep -E \"${includeRegex}\" | awk '{{ print \$1,\$2 }}' | grep -vE \"${excludeRegex}\""
+  local output
+  if ! output="$(pgrep -f "pid,args" 2>&1 | grep -E "${includeRegex}" | awk '{{ print $1,$2 }}')"; then
+    toLogError "Failed to get ${errorMessage}, output: ${output}"
+    return 1
+  fi
+
+  local foundProcesses
+  foundProcesses="$(printf '%s' "${output}" | grep -vE "${excludeRegex}")"
+  if [ ! "${foundProcesses}" ]; then
+    return 1
+  fi
+
+  printf '%s' "${foundProcesses}" | awk '{{ print $1 }}'
+  return 0
+}
+
+#== 版本&帮助信息
+preParams() {
+  if [ $# -gt 0 ]; then
+    if [ "${1}"x = "-h"x ] || [ "${1}"x = "--help"x ]; then
+        displayHelp
+        exit "${EXIT_CODE_OK}"
+    fi
+
+    if [ "${1}"x = "-v"x ] || [ "${1}"x = "--version"x ]; then
+       pad=20
+       printf "%-${pad}s%s\n" "Version:" "${AGENT_INSTALLER_VERSION}"
+       printf "%-${pad}s%s\n" "Commit:" "${AGENT_BUILD_TAG}"
+       printf "%-${pad}s%s\n" "Build At(UTC):" "${AGENT_BUILD_DATE_INFO}"
+       printf "%-${pad}s%s\n" "Uploader:" "${AGENT_BUILD_UPLOADER}"
+       exit "${EXIT_CODE_OK}"
+    fi
+  fi
+}
+#**********************************************************
+# Main script functions
+#**********************************************************
+
+#== 【0】=初始化安装环境
+initializeInstallation() {
+  preParams "$@"
+  #== 设置PATH
+  setPATH
+  #== 配置umask权限
+  local initialUmask
+  initialUmask="$(umask)"
+  umask 022
+
+  #== 读取配置参数
+#  readParamsSection
+  #== 解析命令行参数
+  parseCommandLineParameters "$@"
+  #== 校验配置参数
+
+  #== 是否存在多个同时安装操作【通过判断 INSTALLER_LOCK_FILE 】
+  if isAnotherInstallationRunning; then
+    #== 结束安装操作
+    finishInstallation "${EXIT_CODE_ANOTHER_INSTALLER_RUNNING}" "keep_lock_file"
+  fi
+
+  #== 创建安装标示文件 /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
+  createInstallationLockFile
+  #== 配置是否更新
+  PARAM_UPGRADE="no"
+
+  #== 获取并校验系统兼容性【LINUX/AIX  X86_64/IA64】
+  local platformDetectionString
+  if ! platformDetectionString="$(checkSystemCompatibility)"; then
+    toConsoleError "${platformDetectionString}"
+    finishInstallation "${EXIT_CODE_OS_NOT_SUPPORTED}"
+  fi
+
+  #== 创建 PARAM_INSTALL_DIR -> INSTALL_DIR 软连接
+  createSymlinkToInstallLocation
+  #== 创建日志目录及日志文件installation_$$.log
+  createLogDirsIfMissing
+  #== 配置信号捕获,执行清空安装临时目录
+  configureSignalHandling "cleanInstallationTemporaryFiles"
+  #== 常规日志
+  initializeLog "${@}"
+
+  toLogInfo "Initial umask: ${initialUmask}"
+  toConsoleInfo "${platformDetectionString}"
+  toLogInfo "Distribution: $(detectLinuxDistribution)"
+}
+
+#== 【3】=安装前检查
+preInstallationChecks() {
+  #== 检查发现初始化系统信息(INIT_SYSTEM、INIT_SYSTEM_VERSION、INIT_DIR)
+  detectInitSystem
+
+  #== 检查目录权限
+  checkAccessRightsToDirs
+  #== 创建临时目录
+  prepareTempFolder
+  #== 检查是否已经安装
+  checkIfAlreadyInstalled
+
+  if [ ! "${PARAM_USER}" ] && [ "${ARCH_ARCH}"x != "AIX"x ] ; then
+    #== 配置文件中配置用户和用户组是否存在
+    checkUserAndGroupFromConfig
+  fi
+
+  #== 检查安装目录空间
+  checkInstallPathFreeSpace
+
+}
+
+uninstallAgent() {
+	toConsoleInfo "Agent already installed. Uninstalling previous version."
+#	printf '%s' "upgrade" >"${UNINSTALL_INFO_FILE_LEGACY_PATH}"
+
+	#shellcheck disable=SC2086
+	"${INSTALL_DIR}/uninstall.sh" $$ ${SKIP_PRIVILEGES_CHECK} 2>>"${LOG_FILE}"
+
+	local uninstallExitCode=$?
+	if [ ${uninstallExitCode} -gt 0 ]; then
+		toConsoleError "Error during uninstalling, code: ${uninstallExitCode}. Installation aborted."
+		finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+	fi
+
+	toConsoleInfo "Agent uninstalled."
+}
+
+#== 【4】=部署文件
+deployFiles() {
+  if [ "${PARAM_UPGRADE}" = "yes" ]; then
+    uninstallAgent
+  fi
+  #== 【部署】解压文件
+  extractFiles
+  setupAll
+  createCurrentVersionSymlink
+}
+
+#== 【5】=配置安装
+configureInstallation() {
+  #== 配置参数(param、installation.conf)
+  #== 自定义修改配置, sed ,echo ...  参考 applyAgentSettings
+  if [ "${PARAM_TEST}" ]; then
+    toConsoleOnly "Applying test value is: ${PARAM_TEST}"
+  fi
+  #== 获取bool值参数
+  if [ "${PARAM_BOOL}" ]; then
+    toConsoleOnly "Applying bool value is: ${PARAM_BOOL}"
+  fi
+
+
+  if [ "${ARCH_ARCH}"x != "AIX"x ]; then
+    #== 配置用户和用户组
+    if handleUser; then
+      #== 修改文件系统用户和用户组
+      changeFilesOwnership
+    fi
+    #== 设置root权限
+  fi
+  #== 系统配置(策略配置、dump proc、agent 进程配置、自动启动)
+#  setupAutostart
+}
+
+#== 【6】=验证安装状态、运行启动脚本
+postInstallationSteps() {
+  #== 运行agent
+#  runAgents
+  finishInstallation "${EXIT_CODE_OK}"
+}
+
+readonly CURR_PATH="$(pwd)"
+main() {
+  #== 【0】=初始化安装环境
+  initializeInstallation "$@"
+  #== 【3】=安装前检查
+  preInstallationChecks
+  #== 【4】=部署文件
+  deployFiles
+  #== 【5】=配置安装
+  configureInstallation
+  #== 【6】=验证安装状态、运行启动脚本
+  postInstallationSteps
+
+}
+
+#**********************************************************
+# Script start
+#**********************************************************
+main "$@"
+
+################################################
+############# DO NOT REMOVE THIS ###############
+#### DO NOT ADD ANYTHING BELOW THIS COMMENT ####
+################################################
+#################ENDOFSCRIPTMARK################
+
+----SIGNED-INSTALLER

+ 611 - 0
dist/scripts/package.sh

@@ -0,0 +1,611 @@
+#!/bin/bash
+
+AGENT_INSTALLER_VERSION=1.0.0
+
+#== 当前脚本必须放在 smartagent/scripts 目录下
+readonly SCRIPT_PATH=$(dirname $0)
+readonly SCRIPT_DIR=$(cd $SCRIPT_PATH && pwd)
+readonly CURR_FILE_NAME="$(basename "$0")"
+readonly SMARTAGENT_DIR=$(cd $SCRIPT_DIR/../ && pwd)
+readonly SMARTAGENT_PARENT_DIR=$(cd $SCRIPT_DIR/../../ && pwd)
+
+readonly BRAND_FORMAL_NAME="Cloudwise"
+BRAND_PRODUCT_NAME="euspace"
+BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME}-${BRAND_PRODUCT_NAME}"
+readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
+readonly BRAND_PRODUCT_NAME_LOWER="cwserveragent"
+readonly BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME="ucloudwise"
+
+
+readonly BAK_EXT_NAME=".bak"
+#== cw-serveragent
+readonly SERVICE_SCRIPT_FILE="cw-oneagent"
+readonly XZDEC_NAME="xzdec"
+readonly INSTALL_TEMP_FILE_NAME="install_temp.sh"
+readonly INSTALL_VERSION_FILE_NAME="installer.version"
+#== {productName}-installer-{version}.sh 即 cwserveragent-installer-1.1.0.sh
+ARCH=$(uname -m)
+TARGET_INSTALL_FILE_NAME="${BRAND_AGENT_PRODUCT_NAME}-installer-Linux-${ARCH}-${AGENT_INSTALLER_VERSION}.sh"
+#TARGET_INSTALL_FILE_NAME="${BRAND_PRODUCT_NAME_LOWER}-installer-${AGENT_INSTALLER_VERSION}.sh"
+
+TARGET_INSTALL_TMP_FILE="${SMARTAGENT_PARENT_DIR}/${TARGET_INSTALL_FILE_NAME}.tmp"
+
+readonly LOG_DIR="${SMARTAGENT_PARENT_DIR}/logs"
+readonly LOG_FILE_NAME="release.log"
+readonly LOG_FILE="${LOG_DIR}/${LOG_FILE_NAME}"
+
+#== 【0】=【退出码】执行成功
+readonly EXIT_CODE_OK=0
+readonly EXIT_CODE_GENERIC_ERROR=1
+readonly EXIT_CODE_NOT_ENOUGH_SPACE=6
+readonly EXIT_CODE_NOT_ENOUGH_MEMORY=7
+#== 【0】=无效参数
+readonly EXIT_CODE_INVALID_PARAM=8
+readonly EXIT_CODE_INSUFFICIENT_PERMISSIONS=9
+readonly EXIT_CODE_SEMANAGE_NOT_FOUND=10
+readonly EXIT_CODE_WATCHDOG_ALREADY_RUNNING=11
+#== 【退出码】接收信号
+readonly EXIT_CODE_SIGNAL_RECEIVED=12
+readonly EXIT_CODE_ANOTHER_INSTALLER_RUNNING=13
+readonly EXIT_CODE_AGENT_CONTAINER_RUNNING=14
+readonly EXIT_CODE_GLIBC_VERSION_TOO_LOW=15
+readonly EXIT_CODE_CORRUPTED_PACKAGE=16
+#== 【退出码】管理系统环境变量配置
+readonly EXIT_CODE_MISCONFIGURED_ENVIRONMENT=17
+readonly EXIT_CODE_UNSUPPORTED_DOWNGRADE=18
+readonly EXIT_CODE_OS_NOT_SUPPORTED=19
+readonly EXIT_CODE_DEL_DEFAULT_PARAMS=20
+
+readonly LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS=50
+#**********************************************************
+# Logs
+#**********************************************************
+toLogFile() {
+  if [ -e "${LOG_FILE}" ]; then
+    printf '%s UTC %s\n' "$(date -u +"%Y-%m-%d %H:%M:%S")" "$*" >>"${LOG_FILE}" 2>/dev/null
+  fi
+}
+
+toConsoleOnly() {
+  printf '%s %s\n' "$(date +"%H:%M:%S")" "$*" 2>/dev/null
+}
+
+toLogInfo() {
+  toLogFile "[INFO]" "$@"
+}
+
+toLogWarn() {
+  toLogFile "[WARN]" "$@"
+}
+
+toLogError() {
+  toLogFile "[ERROR]" "$@"
+}
+
+toLogAdaptive() {
+  local success="${1}"
+  shift
+  if [ "${success}" -eq 0 ]; then
+    toLogInfo "$@"
+  else
+    toLogError "$@"
+  fi
+}
+
+toConsoleInfo() {
+  toConsoleOnly "$@"
+  toLogInfo "$@"
+}
+
+toConsoleWarn() {
+  toConsoleOnly "Warn:" "$@"
+  toLogWarn "$@"
+} >&2
+
+toConsoleError() {
+  toConsoleOnly "Error:" "$@"
+  toLogError "$@"
+} >&2
+
+#**********************************************************
+# compress
+#**********************************************************
+tarSmartAgent() {
+  toConsoleInfo "There are 5 steps to packing, begin package ${BRAND_AGENT_PRODUCT_NAME}...."
+  #== 将打包脚本(package.sh、xzdec、install_temp.sh、smartagent、uninstall.sh)移动到临时目录
+  for fileNm in "${CURR_FILE_NAME}" "${INSTALL_TEMP_FILE_NAME}" "uninstall.sh"; do
+    mv -f "${SCRIPT_DIR}/${fileNm}" "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}" 2>>"${LOG_FILE}"
+  done
+  mv -f "${SCRIPT_DIR}/${XZDEC_NAME}" "${SMARTAGENT_PARENT_DIR}/" 2>>"${LOG_FILE}"
+  cp -f "${SMARTAGENT_PARENT_DIR}/${INSTALL_TEMP_FILE_NAME}${BAK_EXT_NAME}" "${TARGET_INSTALL_TMP_FILE}" 2>>"${LOG_FILE}"
+
+  for fileNm in "uninstall.sh"; do
+    cp -f "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}" "${SCRIPT_DIR}/${fileNm}" 2>>"${LOG_FILE}"
+  done
+
+  #== 删除安装脚本中注释
+  if [ "${PARAM_DEBUG}"x != "true"x ]; then
+    toConsoleInfo "Delete the type [#==] comment ..."
+    sed -i '/#==/d' "${TARGET_INSTALL_TMP_FILE}"
+#    sed -i '/#==/d' "${SCRIPT_DIR}/${SERVICE_SCRIPT_FILE}"
+    sed -i '/#==/d' "${SCRIPT_DIR}/uninstall.sh"
+    toConsoleInfo "Delete the type [#DEBUG==] comment ..."
+    sed -i '/#DEBUG==/d' "${TARGET_INSTALL_TMP_FILE}"
+#    sed -i '/#DEBUG==/d' "${SCRIPT_DIR}/${SERVICE_SCRIPT_FILE}"
+    sed -i '/#DEBUG==/d' "${SCRIPT_DIR}/uninstall.sh"
+  fi
+
+  if [ "${PARAM_RELEASE}"x = "true"x ]; then
+    deleteDefaultParams "${TARGET_INSTALL_TMP_FILE}"
+  fi
+
+  #== 进入 smartagent 目录下
+  cd "${SMARTAGENT_DIR}"
+  cp -f "${SCRIPT_DIR}/uninstall.sh" ./package_dir
+  #== 执行第一阶段压缩(源码压缩)Cloudwise-SmartAgent.tar
+  toConsoleInfo "step【1】start【tar -cpf "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.tar" ./package_dir/*】to ${BRAND_AGENT_PRODUCT_NAME}.tar"
+  tar -cpf "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.tar" ./package_dir/* >>"${LOG_FILE}" 2>&1
+  rm -f "./package_dir/uninstall.sh"
+
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+  local agentTarSize="$(getStorageSize "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.tar")"
+
+  cd "${SMARTAGENT_PARENT_DIR}"
+  #== 执行第二阶段压缩(源码压缩 Cloudwise-SmartAgent.tar.xz
+  toConsoleInfo "step【2】start【xz -T0 -z ${BRAND_AGENT_PRODUCT_NAME}.tar】to ${BRAND_AGENT_PRODUCT_NAME}.tar.zx"
+  xz -T0 -1 -z "${BRAND_AGENT_PRODUCT_NAME}.tar" -vv
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== 执行第三阶段压缩 Cloudwise-SmartAgent.tar.xz xzdec to Cloudwise-SmartAgent.tar
+  toConsoleInfo "step【3】start【tar -cpf ${BRAND_AGENT_PRODUCT_NAME}.tar ${BRAND_AGENT_PRODUCT_NAME}.tar.xz xzdec】 to ${BRAND_AGENT_PRODUCT_NAME}.tar"
+  tar -cpf "${BRAND_AGENT_PRODUCT_NAME}.tar" "${BRAND_AGENT_PRODUCT_NAME}.tar.xz" xzdec >>"${LOG_FILE}" 2>&1
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== 执行第四阶段压缩 Cloudwise-SmartAgent.tar to Cloudwise-SmartAgent.tar.base64
+  toConsoleInfo "step【4】start【base64 ${BRAND_AGENT_PRODUCT_NAME}.tar > ${BRAND_AGENT_PRODUCT_NAME}.tar.base64】 to ${BRAND_AGENT_PRODUCT_NAME}.tar.base64"
+  base64 "${BRAND_AGENT_PRODUCT_NAME}.tar" >"${BRAND_AGENT_PRODUCT_NAME}.tar.base64" 2>>"${LOG_FILE}"
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== 执行第五阶段将 base64 文件写入安装脚本
+  toConsoleInfo "step【5】start write 【${BRAND_AGENT_PRODUCT_NAME}.tar.base64】 to ${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME}"
+  #== 设置脚本中版本、创建时间、占用空间信息
+  editInstallScriptParam "${TARGET_INSTALL_TMP_FILE}" ${agentTarSize}
+
+  sed -i "/#################ENDOFSCRIPTMARK################/r ${BRAND_AGENT_PRODUCT_NAME}.tar.base64" "${TARGET_INSTALL_TMP_FILE}"
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  toConsoleInfo "${BRAND_AGENT_PRODUCT_NAME} packaging success."
+  finishPackage "${EXIT_CODE_OK}"
+}
+
+buildVersion() {
+  local oldVersion
+  if oldVersion="$(getOldVersion)"; then
+    setInstallVersion "${oldVersion}"
+    removeIfExist "${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME}"
+		toConsoleInfo "Delete file ${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME}."
+  fi
+
+  if [ "${PARAM_VERSION}" ]; then
+    setInstallVersion "${PARAM_VERSION}"
+    return
+  fi
+}
+
+setInstallVersion() {
+  local version="${1}"
+  if [ ! "${version}" ]; then
+    toConsoleWarn "Set install version failed,not found version version."
+    return
+  fi
+
+  AGENT_INSTALLER_VERSION="${version}"
+  BRAND_PRODUCT_NAME="${PARAM_NAME}"
+  BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME}-${BRAND_PRODUCT_NAME}"
+  TARGET_INSTALL_FILE_NAME="${BRAND_AGENT_PRODUCT_NAME}-installer-Linux-${ARCH}-${AGENT_INSTALLER_VERSION}.sh"
+  #== {productName}-installer-{version}.sh 即 cwserveragent-installer-1.1.0.sh
+#  TARGET_INSTALL_FILE_NAME="${BRAND_PRODUCT_NAME_LOWER}-installer-${AGENT_INSTALLER_VERSION}.sh"
+  TARGET_INSTALL_TMP_FILE="${SMARTAGENT_PARENT_DIR}/${TARGET_INSTALL_FILE_NAME}.tmp"
+}
+
+getOldVersion() {
+	local installVersionFile="${SMARTAGENT_DIR}/installer.version"
+	if [ ! -f "${installVersionFile}" ]; then
+		toLogWarn "Could not find ${installVersionFile} file."
+		return 1
+	fi
+
+	local oldVersion="$(cat "${installVersionFile}")"
+	if [ ! "${oldVersion}" ]; then
+		toLogWarn "Could not read install version from ${installVersionFile} file."
+	  return 1
+  fi
+
+  printf '%s' "${oldVersion}"
+  return 0
+}
+
+#== 通过占位分割读取位置
+locateDelimiter() {
+  #== 占位符
+  local delimiter="${1}"
+  #== 从文件结尾读取行数
+  local linesToReadFromEnd="${2}"
+  local filePath="${3}"
+
+  if [ "${linesToReadFromEnd}" ]; then
+    #== 文件总行数n(实际行数=n+1)
+    local linesCount="$(wc -l "${filePath}" | awk '{print $1}')"
+    #== 从后往前读取【linesToReadFromEnd】行
+    local offset="$(tail -n"${linesToReadFromEnd}" "${filePath}" 2>/dev/null | awk '/^'"${delimiter}"'/ { print NR; exit }')"
+    if [ -n "${offset}" ]; then
+      printf "%d" "$((linesCount - linesToReadFromEnd + offset))"
+    fi
+  else
+    #== 读取占位符所在行(实际行数=n+1)
+    awk '/^'"${delimiter}"'/ { print NR; exit }' "${filePath}"
+  fi
+}
+
+#== 删除安装脚本中默认参数
+deleteDefaultParams() {
+  local filePath="${1}"
+  local sectionName="----PARAMETERS"
+  local begin=$(locateDelimiter "${sectionName}" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS} ${filePath})
+  local end=$(locateDelimiter "${sectionName}--" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS} ${filePath})
+
+  if [ -z "${begin}" ] || [ -z "${end}" ]; then
+    return
+  fi
+
+  toConsoleInfo "Begin delete default params from line ${begin} to ${end}..."
+  local output="$(sed -i "${begin},${end}d" "${filePath}") 2>&1"
+  if [ ! "${output}" ]; then
+    toConsoleError "Delete install script file ${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME} default params error:${output}"
+    finishPackage "${EXIT_CODE_DEL_DEFAULT_PARAMS}"
+  fi
+  toConsoleInfo "Delete install script file ${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME} default params success."
+}
+
+#== 设置脚本中版本、创建时间、占用空间信息
+editInstallScriptParam() {
+  local targetFile="${1}"
+  local agentTarSize="${2}"
+  #== 设置版本信息
+  local buildDate="$(date +"%Y-%m-%d")"
+  local buildTag="$(git rev-parse --short HEAD)"
+  local buildDateInfo="$(date +"%Y-%m-%d %H:%M:%S")"
+  local buildUploader="$(hostname)/${USER}"
+  #== 构建日期
+  editScriptFileParam "readonly AGENT_BUILD_DATE" "${buildDate}" "${targetFile}"
+  #== 构建版本
+  editScriptFileParam "readonly AGENT_INSTALLER_VERSION" "${AGENT_INSTALLER_VERSION}" "${targetFile}"
+  #== commitid
+  editScriptFileParam "readonly AGENT_BUILD_TAG" "'${buildTag}'" "${targetFile}"
+  #== 构建日期
+  editScriptFileParam "readonly AGENT_BUILD_DATE_INFO" "'${buildDateInfo}'" "${targetFile}"
+  #== 构建环境信息
+  editScriptFileParam "readonly AGENT_BUILD_UPLOADER" "'${buildUploader}'" "${targetFile}"
+
+  #== 设置占用空间信息
+  local agentDirSize="$(getStorageSize "${SMARTAGENT_DIR}")"
+  local agentBase64FileSize="$(getStorageSize "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.tar.base64")"
+  local agentInstallFileSize="$(getStorageSize "${TARGET_INSTALL_TMP_FILE}")"
+  local artifactsSize="$((agentBase64FileSize + agentInstallFileSize + agentDirSize))";
+  editScriptFileParam "readonly EXTERNAL_TAR_SIZE" "${agentTarSize}" "${targetFile}"
+  editScriptFileParam "readonly ARTIFACTS_SIZE" "${artifactsSize}" "${targetFile}"
+}
+
+getStorageSize() {
+  local dirName="${1}"
+  if [ ! -e "${dirName}" ]; then
+    printf '%s' "0"
+    return 0
+  fi
+  printf '%s' "$(du -sb "${dirName}" | awk '{print $1}')"
+}
+
+finishFinally() {
+  #== 将打包脚本文件移动到当前目录下
+  for fileNm in "${CURR_FILE_NAME}" "${INSTALL_TEMP_FILE_NAME}" "uninstall.sh"; do
+    if [ -f "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}" ]; then
+      mv -f "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}" "${SCRIPT_DIR}/${fileNm}" 2>>"${LOG_FILE}"
+    fi
+  done
+  removeIfExist "${SMARTAGENT_DIR}/uninstall.sh"
+  if [ -f "${SMARTAGENT_PARENT_DIR}/${XZDEC_NAME}" ]; then
+    mv -f "${SMARTAGENT_PARENT_DIR}/${XZDEC_NAME}" "${SCRIPT_DIR}/" 2>>"${LOG_FILE}"
+  fi
+
+  if [ -f "${TARGET_INSTALL_TMP_FILE}" ]; then
+    mv -f "${TARGET_INSTALL_TMP_FILE}" "${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME}" 2>>"${LOG_FILE}"
+  fi
+
+  if [ "${PARAM_DEBUG}"x != "true"x ]; then
+    for fileNmExt in "tar" "tar.xz" "tar.base64"; do
+      removeIfExist "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.${fileNmExt}"
+    done
+  fi
+
+
+}
+
+removeIfExist() {
+  local filePath="${1}"
+  if [ ! -e "${filePath}" ]; then
+    return
+  fi
+  rm -rf "${filePath}" 2>>"${LOG_FILE}"
+}
+
+deleteParentTmpFile() {
+  for fileNm in "${CURR_FILE_NAME}" "${INSTALL_TEMP_FILE_NAME}" "uninstall.sh"; do
+    removeIfExist "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}"
+  done
+  removeIfExist "${SMARTAGENT_PARENT_DIR}/${XZDEC_NAME}"
+  removeIfExist "${TARGET_INSTALL_TMP_FILE}"
+
+  for fileNmExt in "tar" "tar.xz" "tar.base64"; do
+    removeIfExist "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.${fileNmExt}"
+  done
+}
+
+#**********************************************************
+# Param
+#**********************************************************
+PARAM_DEBUG=false
+PARAM_RELEASE=false
+#PARAM_VERSION=1.1.0
+PARAM_NAME=chaosd
+PARAM_AGENT_PROC=$PARAM_NAME
+PARAM_CONFIG_PATH=
+
+#== 大小写转化,并返回判断结果
+istrcmp() {
+  local s1="$(printf '%s' "${1}" | tr '[:upper:]' '[:lower:]')"
+  local s2="$(printf '%s' "${2}" | tr '[:upper:]' '[:lower:]')"
+  [ "${s1}"x = "${s2}"x ]
+}
+
+isParamTrue() {
+  [ "${1}"x = "1"x ] || [ "${1}"x = "true"x ] || [ "${1}"x = "enable"x ] || [ "${1}"x = "yes"x ]
+}
+
+isParamFalse() {
+  [ "${1}"x = "0"x ] || [ "${1}"x = "false"x ] || [ "${1}"x = "disable"x ] || [ "${1}"x = "no"x ]
+}
+
+#== 获取参数值
+getParamValue() {
+  local paramName="${1}"
+  local input="${2}"
+  local paramNameLength="${#paramName}"
+  paramNameLength=$((paramNameLength + 1))
+
+  local partParam="$(expr substr "${input}" 1 ${paramNameLength})"
+  if ! istrcmp "${partParam}" "${paramName}="; then
+    return 1
+  fi
+
+  local valueSeparator=$((paramNameLength + 1))
+  local value="$(expr substr "${input}" ${valueSeparator} 1000)"
+  if [ -z "${value}" ]; then
+    return 1
+  fi
+
+  printf '%s' "${value}"
+  return 0
+}
+
+#== 获取bool参数
+readBoolParam() {
+  local value=
+  if value="$(getParamValue "${1}" "${2}")"; then
+    if isParamFalse "${value}"; then
+      printf "false"
+      return 0
+    fi
+    if isParamTrue "${value}"; then
+      printf "true"
+      return 0
+    fi
+  fi
+
+  return 1
+}
+
+#== 解析命令行中参数
+parseCommandLineParameters() {
+  while [ $# -gt 0 ]; do
+    local param="${1}"
+    local value=
+
+    if value=$(getParamValue RELEASE "${param}"); then
+      PARAM_RELEASE="${value}"
+      printParamMessage "RELEASE" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue VERSION "${param}"); then
+      PARAM_VERSION="${value}"
+      printParamMessage "VERSION" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue NAME "${param}"); then
+      PARAM_NAME="${value}"
+      printParamMessage "NAME" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue CONFIG "${param}"); then
+      PARAM_CONFIG_PATH="${value}"
+      printParamMessage "CONFIG" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue PROC "${param}"); then
+      PARAM_AGENT_PROC="${value}"
+      printParamMessage "PROC" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(readBoolParam DEBUG "${param}"); then
+      PARAM_DEBUG="${value}"
+      printParamMessage "DEBUG" "${value}"
+      shift
+      continue
+    fi
+
+    if [ "${param}"x = "-h"x ] || [ "${param}"x = "--help"x ]; then
+      displayHelp
+      finishPackage "${EXIT_CODE_OK}"
+    fi
+
+    if [ "${param}"x = "-v"x ] || [ "${param}"x = "--version"x ]; then
+      printf "%s\n" "${AGENT_INSTALLER_VERSION}"
+      finishPackage "${EXIT_CODE_OK}"
+    fi
+
+    toConsoleError "Unrecognized parameter: ${param}"
+
+    displayHelp
+    finishPackage "${EXIT_CODE_INVALID_PARAM}"
+  done
+}
+
+displayHelp() {
+  printf '\n'
+  printf '%s\n' "Usage: "${CURR_FILE_NAME}" [-h] [-v] [NAME=pluginName] [PROC=mainProcess] [RELEASE=true|false] [DEBUG=true|false] [VERSION=1.0]"
+  printf '\n\n'
+
+  local pad=15
+  printf "%-${pad}s%s\n" "-h, --help" "Display this help and exit."
+  printf "%-${pad}s%s\n" "-v, --version" "Print version and exit."
+  printf '\n'
+  printf "%-${pad}s%s\n" "RELEASE" "Default true; Whether to delete the publish parameter in the script."
+  printf "%-${pad}s%s\n" "VERSION" "Configure the ${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME} version."
+  printf "%-${pad}s%s\n" "NAME" "Setting agent name like 'bash ./scripts/package.sh NAME=pluginName PROC=mainProcess'"
+  printf "%-${pad}s%s\n" "PROC" "Setting agent main process name."
+  printf "%-${pad}s%s\n" "DEBUG" "Default false; 1、Debug mode executed script ${CURR_FILE_NAME};"
+  printf "%-${pad}s%s\n" "" "2、Remove the debug log from the scripts (${TARGET_INSTALL_FILE_NAME}、${SERVICE_SCRIPT_FILE}、uninstall.sh)."
+}
+
+#== 完成安装后清理临时目录
+finishPackage() {
+  finishFinally
+  toLogInfo "Installation finished, PID $$, exit code: ${1}."
+  exit "${1}"
+}
+
+printParamMessage() {
+  local paramNm="${1}"
+  local paramValue="${2}"
+  toConsoleInfo "---> Parameter ${paramNm}=${paramValue}."
+}
+
+#**********************************************************
+# SELinux related functions
+#**********************************************************
+#== 修改scripts/smartagent脚本配置信息
+editScriptFileParam() {
+  local key="${1}"
+  local newValue="${2}"
+  local file="${3}"
+
+  local value="$(sed -n "s|^${key}=||p" "${file}" 2>/dev/null)"
+  toConsoleInfo "---> edit ${key}=${value} to ${key}=${newValue}, configFile: ${file}"
+  if [ "${value}" ]; then
+    sed -i "s|^${key}=.*|${key}=${newValue}|" "${file}" 2>/dev/null
+  fi
+}
+
+#== 信号捕获,并执行回调函数
+signalHandler() {
+  local signal="${1}"
+  local callback="${2}"
+  toLogWarn "process received signal: ${signal}"
+  ${callback} "${signal}"
+  exit ${EXIT_CODE_SIGNAL_RECEIVED}
+}
+
+#== 配置信号捕获和回调
+configureSignalHandling() {
+  local callback="${1}"
+  for signal in HUP INT QUIT ABRT ALRM TERM; do
+    #== shellcheck disable=SC2064
+    trap "signalHandler ${signal} ${callback}" ${signal}
+  done
+
+  trap "" PIPE
+}
+
+#**********************************************************
+# main
+#**********************************************************
+main() {
+  mkdir -p "${LOG_DIR}"
+  echo "" >"${LOG_FILE}"
+
+  toConsoleInfo "package script run path: ${SCRIPT_DIR}"
+  toConsoleInfo "package logs path: ${LOG_DIR}"
+
+  chmod +x ${SCRIPT_DIR}/*
+
+  #== 解析命令行参数
+  parseCommandLineParameters "$@"
+  toConsoleInfo "-----params PARAM_RELEASE: ${PARAM_RELEASE}, PARAM_DEBUG: ${PARAM_DEBUG}, PARAM_VERSION: ${PARAM_VERSION}"
+
+  configureSignalHandling "finishFinally"
+
+  #== 设置 package 插件名
+  editScriptFileParam "BRAND_PRODUCT_NAME" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/package.sh"
+  #== 设置 install 插件名
+  editScriptFileParam "readonly BRAND_PRODUCT_NAME" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/install_temp.sh"
+  editScriptFileParam "readonly BRAND_PRODUCT_NAME_LOWER" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/install_temp.sh"
+  #== 设置 agentctl 插件名(存在时)
+  if [ -f "${SMARTAGENT_DIR}/package_dir/bin/agentctl" ];then
+    editScriptFileParam "readonly BRAND_PRODUCT_NAME" "\"${PARAM_NAME}\"" "${SMARTAGENT_DIR}/package_dir/bin/agentctl"
+    editScriptFileParam "readonly BRAND_PRODUCT_NAME_LOWER" "\"${PARAM_NAME}\"" "${SMARTAGENT_DIR}/package_dir/bin/agentctl"
+  #== 设置 agentctl 配置文件路径(存在时)
+    editScriptFileParam "readonly AGENT_CONFIG_PATH" "\"${PARAM_CONFIG_PATH}\"" "${SMARTAGENT_DIR}/package_dir/bin/agentctl"
+  fi
+
+  #== 设置 uninstall 插件名
+  editScriptFileParam "readonly BRAND_PRODUCT_NAME" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/uninstall.sh"
+  editScriptFileParam "readonly BRAND_PRODUCT_NAME_LOWER" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/uninstall.sh"
+  #== 设置主进程名
+  if [ -n "${PARAM_AGENT_PROC}" ]; then
+    editScriptFileParam "readonly AGENT_PROC" "\"${PARAM_AGENT_PROC}\"" "${SCRIPT_DIR}/install_temp.sh"
+    editScriptFileParam "readonly AGENT_PROC" "\"${PARAM_AGENT_PROC}\"" "${SCRIPT_DIR}/uninstall.sh"
+    editScriptFileParam "readonly AGENT_PROC" "\"${PARAM_AGENT_PROC}\"" "${SMARTAGENT_DIR}/package_dir/bin/agentctl"
+  fi
+  buildVersion
+  toConsoleInfo "-----Write release version ${AGENT_INSTALLER_VERSION} to ${SMARTAGENT_DIR}/${INSTALL_VERSION_FILE_NAME} file."
+  echo "${AGENT_INSTALLER_VERSION}">"${SMARTAGENT_DIR}/${INSTALL_VERSION_FILE_NAME}"
+
+  #== 修改uninstall.sh 版本
+  editScriptFileParam "readonly AGENT_INSTALLER_VERSION" "${AGENT_INSTALLER_VERSION}" "${SCRIPT_DIR}/uninstall.sh"
+
+  deleteParentTmpFile
+
+  #== 压缩 并生成安装脚本
+  tarSmartAgent
+}
+
+main "$@"

+ 1111 - 0
dist/scripts/uninstall.sh

@@ -0,0 +1,1111 @@
+#!/bin/bash
+
+#==脚本执行中遇到不存在的变量就报错
+set -o nounset
+#==脚本执行中报错即刻退出
+set -o errexit
+#==脚本执行只要一个子命令失
+set -o pipefail
+
+readonly BRAND_FORMAL_NAME="Cloudwise"
+readonly BRAND_PRODUCT_NAME="euspace"
+#== 根路径
+readonly BRAND_PARENT_PRODUCT_NAME="omniagent"
+readonly BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME}"
+readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
+readonly BRAND_PRODUCT_NAME_LOWER="euspace"
+readonly BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME="cloudwise"
+
+#== **********************************************************
+#==  配置重要文件名称
+#== **********************************************************
+#== smartagentd
+readonly AGENT_PROC="euspace"
+readonly AGENT_INSTALLER_VERSION=0.0.2
+#== **********************************************************
+#==  配置目录
+#== **********************************************************
+readonly INSTALL_BASE=/opt
+#== /opt/cloudwise/omniagent/agents 目录
+readonly BASE_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents
+#== cloudwise/omniagent/agents/agent
+readonly INSTALL_FOLDER=${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents/${BRAND_PRODUCT_NAME_LOWER}
+#== cloudwise/omniagent/agents/agent/version
+readonly INSTALL_VERSION_FOLDER=${INSTALL_FOLDER}/${AGENT_INSTALLER_VERSION}
+#== /opt/cloudwise/omniagent/agents/agent/version
+readonly INSTALL_DIR=${INSTALL_BASE}/${INSTALL_VERSION_FOLDER}
+#== /opt/cloudwise/omniagent/agents/agent
+readonly AGENT_BASE_DIR=${INSTALL_BASE}/${INSTALL_FOLDER}
+
+readonly CURRENT_VERSION_LINK=${AGENT_BASE_DIR}/current
+
+#== /opt/cloudwise/omniagent/conf
+readonly AGENT_CONF_DIR="${INSTALL_DIR}/conf"
+
+readonly AGENT_LIBS_DIR="${INSTALL_DIR}/libs"
+
+#== agent 初始化脚本目录 /opt/cloudwise/omniagent/scripts
+readonly AGENT_SCRIPTS_DIR="${INSTALL_DIR}/scripts"
+
+#== agent 初始化脚本目录 /opt/cloudwise/omniagent/bin
+readonly AGENT_BIN_DIR="${INSTALL_DIR}/bin"
+
+
+readonly PERL_FILE="/usr/bin/perl"
+
+readonly SAFE_RM_FILE="safe-rm"
+
+readonly SAFE_RM_BIN="${AGENT_BIN_DIR}/${SAFE_RM_FILE}"
+
+if [ -f ${PERL_FILE} ] && [ -f "${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/bin/${SAFE_RM_FILE}" ]; then
+    readonly SAFE_RM_EXEC="${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/bin/${SAFE_RM_FILE}"
+else
+    readonly SAFE_RM_EXEC="rm"
+fi
+
+readonly LOG_DIR_NAME=logs
+#== /opt/cloudwise/omniagent/logs
+readonly LOG_DIR=${INSTALL_DIR}/${LOG_DIR_NAME}
+
+#== /var/log/cloudwise/omniagent/
+readonly INSTALLER_LOG_DIR="/var/log/${INSTALL_FOLDER}"
+
+#readonly INSTALLER_LOG_SUBDIR="installer"
+##== /opt/cloudwise/smartagent/log/installer
+#readonly INSTALLER_LOG_DIR=${LOG_DIR}/${INSTALLER_LOG_SUBDIR}
+
+readonly AGENT_PID_FILE="${INSTALL_DIR}/bin/${AGENT_PROC}.pid"
+
+
+#== 【0】=
+readonly INSTALLER_LOCK_FILE="/tmp/${BRAND_FORMAL_NAME_LOWER}_${BRAND_PRODUCT_NAME_LOWER}.lock"
+
+readonly INIT_SYSTEM_SYSV="SysV"
+#== 系统 systemd
+readonly INIT_SYSTEM_SYSTEMD="systemd"
+#== cw-serveragent
+readonly SERVICE_SCRIPT_FILE="cw-oneagent"
+#== cw-serveragent.service
+readonly SYSTEMD_UNIT_FILE_AGENT="${SERVICE_SCRIPT_FILE}.service"
+
+#== 系统文件目录
+readonly SYSTEMD_UNIT_FILES_DIR="/etc/systemd/system/"
+
+readonly EXIT_CODE_OK=0
+readonly EXIT_CODE_GENERIC_ERROR=1
+
+readonly EXIT_CODE_SIGNAL_RECEIVED=12
+readonly EXIT_CODE_ANOTHER_INSTALLER_RUNNING=13
+readonly EXIT_CODE_AGENT_CONTAINER_RUNNING=14
+readonly EXIT_CODE_GLIBC_VERSION_TOO_LOW=15
+readonly EXIT_CODE_MISCONFIGURED_ENVIRONMENT=17
+
+
+readonly USER="root"
+readonly GROUP="root"
+#== 非root模式
+readonly PARAM_NON_ROOT_MODE=false
+
+readonly CONTAINER_DEPLOYMENT_STATE_ENTRY=false
+#== 是否有 SELinux
+readonly ARCH_HAS_SELINUX=true
+################################################################################
+#	Logs
+################################################################################
+toLog() {
+  if [ -e "${LOG_FILE}" ]; then
+    printf '%s UTC %s\n' "$(date -u +"%Y-%m-%d %H:%M:%S")" "$*" >>"${LOG_FILE}" 2>/dev/null
+  fi
+}
+
+toConsoleOnly() {
+  printf '%s %s\n' "$(date +"%H:%M:%S")" "$*" 2>/dev/null
+}
+
+toLogInfo() {
+  toLog "[INFO]" "$@"
+}
+
+toLogWarning() {
+  toLog "[WARN]" "$@"
+}
+
+toLogError() {
+  toLog "[ERROR]" "$@"
+}
+
+toLogAdaptive() {
+  local success="${1}"
+  shift
+  if [ "${success}" -eq 0 ]; then
+    toLogInfo "$@"
+  else
+    toLogError "$@"
+  fi
+}
+
+toConsoleInfo() {
+  toConsoleOnly "$@"
+  toLogInfo "$@"
+}
+
+toConsoleWarning() {
+  toConsoleOnly "Warning:" "$@"
+  toLogWarning "$@"
+} >&2
+
+toConsoleError() {
+  toConsoleOnly "Error:" "$@"
+  toLogError "$@"
+} >&2
+
+createDirIfNotExistAndSetRights() {
+  local dir="${1}"
+  local rights="${2}"
+
+  if [ ! -d "${dir}" ]; then
+    toLogInfo "Creating directory ${dir} with rights ${rights}"
+    local message
+    if ! message="$(mkdir -p "${dir}" 2>&1)"; then
+      toConsoleWarning "Cannot create ${dir} directory."
+      toLogWarning "Error message: ${message}"
+      return 1
+    fi
+  fi
+
+  if ! message="$(chmod "${rights}" "${dir}" 2>&1)"; then
+    toConsoleWarning "Cannot change permisions of ${dir} directory to ${rights}."
+    toLogWarning "Error message: ${message}"
+    return 1
+  fi
+
+  return 0
+}
+
+#== 创建日志目录及日志文件installation_$$.log
+createLogDirsIfMissing() {
+  createDirIfNotExistAndSetRights "${INSTALL_BASE}" u+rwx,g+rx,o+rx
+  createDirIfNotExistAndSetRights "${BASE_INSTALL_DIR}" u+rwx,g+rx,o+rx
+  createDirIfNotExistAndSetRights "${INSTALL_DIR}" 1775
+  createDirIfNotExistAndSetRights "${LOG_DIR}" 1777
+  createDirIfNotExistAndSetRights "${INSTALLER_LOG_DIR}" 1777
+
+#  for agentLog in ${PLUGIN_LOG_NAMES}; do
+#    createDirIfNotExistAndSetRights "${LOG_DIR}/${agentLog}" 1777
+#  done
+
+  touch "${LOG_FILE}"
+  setRightsForFiles "${LOG_FILE}" 766
+}
+
+
+################################################################################
+#	Platform characteristics detection
+################################################################################
+
+#== 设置文件可执行权限
+setRightsForFiles() {
+  local file="${1}"
+  local perms="${2}"
+
+  if [ -f "${file}" ]; then
+    chmod "${perms}" "${file}"
+  elif [ -d "${file}" ]; then
+    chmod -R "${perms}" "${file}"
+  fi
+}
+
+#== 安装类型(f:文件,d:目录)递归设置权限
+chmod4FilesRecursively() {
+  local dir="${1}"
+  local type="${2}"
+  local mask="${3}"
+  find "${dir}" -type "${type}" -exec chmod "${mask}" {} \;
+}
+
+detectLinuxDistribution() {
+  if [ -f /etc/redhat-release ]; then
+    cat /etc/redhat-release
+  elif [ -f /etc/os-release ]; then
+    (
+      . /etc/os-release
+      local distrib="${NAME}"
+      if [ -z "${distrib}" ]; then
+        distrib="${ID}"
+      fi
+
+      local version="${VERSION_ID}"
+      if printf "%s" "${distrib}" | grep -iq "debian"; then
+        version="$(cat /etc/debian_version)"
+      fi
+
+      printf "%s %s" "${distrib}" "${version}"
+    )
+  elif [ -f /etc/SuSE-release ]; then
+    head -1 /etc/SuSE-release
+  elif [ -f /etc/lsb-release ]; then
+    (
+      . /etc/lsb-release
+      printf "%s %s" "${DISTRIB_ID}" "${DISTRIB_RELEASE}"
+    )
+  elif ls /etc/*release* >/dev/null 2>&1; then
+    # Generic fallback
+    cat /etc/*release*
+  else
+    printf "AIX %s" "$(oslevel -s 2>&1)"
+  fi
+}
+
+checkInitSystem() {
+  local version
+  if version="$(systemctl --version 2>&1)"; then
+    if [ -d "${SYSTEMD_UNIT_FILES_DIR}" ]; then
+      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSTEMD}
+    else
+      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
+      toLogWarning "${INIT_SYSTEM_SYSTEMD} was detected but ${SYSTEMD_UNIT_FILES_DIR} does not exist, using ${INIT_SYSTEM_SYSV} handling as a fallback"
+    fi
+  else
+    readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
+    if ! version="$(init --version 2>&1)"; then
+      if ! version="$(chkconfig --version 2>&1)"; then
+        version="$(head -n1 /etc/inittab 2>&1)"
+      fi
+    fi
+  fi
+
+  readonly INIT_SYSTEM_VERSION="$(printf '%s' "${version}" 2>/dev/null | head -n1)"
+}
+
+setLocationOfInitScripts() {
+  toLogInfo "Determining location of init scripts..."
+
+  if [ "${INIT_SYSTEM}" = "${INIT_SYSTEM_SYSTEMD}" ] || [ "${ARCH_ARCH}" = "AIX" ]; then
+    readonly INIT_DIR="${AGENT_SCRIPTS_DIR}"
+  else
+    if [ -d "/etc/init.d" ]; then
+      readonly INIT_DIR="/etc/init.d"
+    elif [ -d "/sbin/init.d" ]; then
+      readonly INIT_DIR="/sbin/init.d"
+    elif [ -d "/etc/rc.d" ]; then
+      readonly INIT_DIR="/etc/rc.d"
+    else
+      return 1
+    fi
+  fi
+
+  toLogInfo "Location of init scripts ${INIT_DIR}"
+  return 0
+}
+
+detectArchitecture() {
+  local detected_arch=
+  if isAvailable arch; then
+    detected_arch="$(arch | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  if [ -z "${detected_arch}" ]; then
+    detected_arch="$(uname -m | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  printf '%s' "${detected_arch}"
+}
+
+################################################################################
+#	Misc functions
+################################################################################
+
+getAgentCtlBinPath() {
+  printf "%s" "${AGENT_TOOLS_DIR}/lib64/${AGENT_CTL_BIN}"
+}
+
+getOsConfigBinPath() {
+  printf "%s" "${AGENT_INSTALL_DIR}/lib64/${AGENT_OS_CONFIG_BIN}"
+}
+
+commandErrorWrapper() {
+  set +e
+  local command="${*}"
+  local errorFile="/tmp/smartagent_commanderror_$$"
+
+  ${command} 2>"${errorFile}"
+  local returnCode=$?
+  if [ ${returnCode} -ne 0 ]; then
+    toLogWarning "Command '${command}' failed, return code: ${returnCode}, message: $(cat "${errorFile}")"
+  fi
+
+  ${SAFE_RM_EXEC} -f "${errorFile}"
+  set -e
+  return ${returnCode}
+}
+
+checkRootAccess() {
+  toConsoleInfo "Checking root privileges..."
+
+  if [ "$(id -u)" != "0" ]; then
+    toConsoleError "NOT OK"
+    return 1
+  fi
+
+  toConsoleInfo "OK"
+  return 0
+}
+
+removeIfExists() {
+  local pathToRemove="${1}"
+  toLogInfo "${SAFE_RM_EXEC} -rf ${pathToRemove}"
+  if [ ! -e "${pathToRemove}" ]; then
+    toLogInfo "${pathToRemove} does not exist, skipping removal"
+    return
+  fi
+
+  local output
+  if ! output="$(${SAFE_RM_EXEC} -rf "${pathToRemove}" 2>&1)"; then
+    toLogWarning "Failed to remove ${pathToRemove}: ${output}"
+  fi
+}
+
+################################################################################
+#	SELinux related functions
+################################################################################
+
+executeUsingOsConfigBin() {
+  local command="${1}"
+  local unit="${2}"
+  if [ "${unit}" ]; then
+    command="${command}-${unit}"
+    unit=""
+  fi
+
+  local output=
+  output="$("$(getOsConfigBinPath)" "${command}" 2>&1)"
+  local exitCode=$?
+  toLogAdaptive ${exitCode} "Executed $(getOsConfigBinPath) ${command} ${unit}, exitCode = ${exitCode}, output: ${output}"
+  return ${exitCode}
+}
+
+executeSystemctlCommand() {
+  local command="${1}"
+  local unit="${2}"
+
+  if [ "$(id -u)" != 0 ]; then
+    executeUsingOsConfigBin "${command}" "${unit}"
+    return $?
+  fi
+
+  local output=
+  #== shellcheck disable=SC2086
+  output="$(systemctl "${command}" ${unit} 2>&1)"
+  local exitCode=$?
+
+  if [ ${exitCode} -eq 0 ]; then
+    toLogInfo "Successfully executed: systemctl ${command} ${unit}"
+  else
+    toLogError "Failed to execute: systemctl ${command} ${unit}"
+    toLogError "Command output: ${output}"
+    if [ -n "${unit}" ]; then
+      local reachBackNumSeconds=360
+      toLogError "journalctl output: $(journalctl -u "${unit}" --since=-${reachBackNumSeconds} 2>&1)"
+    fi
+  fi
+
+  return ${exitCode}
+} 2>>"${LOG_FILE}"
+
+executeInitScriptCommand() {
+  local command=
+  local parameters="$*"
+  local output=
+  local exitCode=
+
+  if isAvailable service; then
+    command="service"
+    parameters="${SERVICE_SCRIPT_FILE} ${parameters}"
+  else
+    command="${INIT_DIR}/${SERVICE_SCRIPT_FILE}"
+  fi
+  #shellcheck disable=SC2086
+  output="$("${command}" ${parameters} 2>&1)"
+  exitCode=$?
+  toLogAdaptive ${exitCode} "Executed ${command} ${parameters}, exitCode = ${exitCode}, output: ${output}"
+  return ${exitCode}
+}
+
+signalHandler() {
+	end_exit_code=$?
+	if [ ${end_exit_code} -eq ${EXIT_CODE_SIGNAL_RECEIVED} ]; then
+		return
+	fi
+	local signal="${1}"
+	local callback="${2}"
+	if [ ${end_exit_code} -ne 0 ]; then
+		toLogWarning "Process Received [Signal: ${signal}] [Code : ${end_exit_code}]"
+	fi
+	${callback} ${end_exit_code}
+
+	if [ ${end_exit_code} -eq ${EXIT_CODE_OK} ]; then
+		exit ${EXIT_CODE_OK}
+	fi
+
+	exit ${EXIT_CODE_SIGNAL_RECEIVED}
+}
+
+configureSignalHandling() {
+  local callback="${1}"
+  for signal in HUP INT QUIT ABRT ALRM TERM EXIT; do
+    # shellcheck disable=SC2064
+    trap "signalHandler '${signal}' '${callback}'" ${signal}
+  done
+
+  trap "" PIPE
+}
+
+removeSecretsFromString() {
+  printf "%s" "$*" | sed 's#\(Api-Token=\)[[:alnum:]_-]\{21\}#\1***#' |
+    sed 's#\(TENANT_TOKEN=\)[[:alnum:]]\{16\}#\1***#' |
+    sed 's#\(latest/\)[[:alnum:]]\{16\}#\1***#' |
+    sed 's#\(PROXY=\)[^[:space:]]*#\1***#'
+}
+
+#== waitTime must be divisible by 10
+sendSignalToProcessAndWaitForStop() {
+  local pidCheckingFunction="${1}"
+  local signal="${2}"
+  local action="${3}"
+  local waitTime="${4}"
+  local pidToStop="${5}"
+
+  if ! ${pidCheckingFunction} "${pidToStop}"; then
+    toLogInfo "Process with pid ${pidToStop} doesn't exist"
+    return
+  fi
+
+  toConsoleInfo "Waiting ${waitTime} seconds for process with pid ${pidToStop} to ${action}."
+  while [ "${waitTime}" -gt 0 ]; do
+    if [ "$((waitTime % 10))" -eq 0 ]; then
+      toLogInfo "Sending signal: ${signal} to ${pidToStop}"
+      kill -s "${signal}" "${pidToStop}" 2>>"${LOG_FILE}"
+    fi
+
+    if ! ${pidCheckingFunction} "${pidToStop}"; then
+      return 0
+    fi
+    sleep 1
+    waitTime=$((waitTime - 1))
+  done
+  return 1
+}
+
+testWriteAccessToDir() {
+  local errorFile="/tmp/smartagent_commandError_$$"
+  local dir="${1}"
+  local tmpfilename
+  if [ "${ARCH_ARCH}" = "AIX" ]; then
+    tmpfilename="${dir}/.tmp_${BRAND_PRODUCT_NAME_LOWER}.$$${RANDOM}"
+    touch "${tmpfilename}" 2>"${errorFile}"
+  else
+    tmpfilename="$(mktemp -p "${dir}" ".tmp_${BRAND_PRODUCT_NAME_LOWER}.XXXXXXXXXXXXXX" 2>"${errorFile}")"
+  fi
+
+  #shellcheck disable=SC2181
+  if [ $? -ne 0 ]; then
+    toLogInfo "$(cat "${errorFile}")"
+    ${SAFE_RM_EXEC} -f "${errorFile}"
+    return 1
+  fi
+
+  ${SAFE_RM_EXEC} -f "${tmpfilename}" "${errorFile}"
+  return 0
+}
+
+setPATH() {
+  local prependToPATH="/usr/sbin:/usr/bin:/sbin:/bin"
+  if [ "${PATH}" ]; then
+    PATH=${prependToPATH}:${PATH}
+  else
+    PATH=${prependToPATH}
+  fi
+}
+
+systemLibDirSanityCheck() {
+  local dir="${1}"
+  if [ ! -d "${dir}" ]; then
+    toLogWarning "Directory: ${dir} does not exist"
+    printf ""
+    return
+  fi
+
+  if ! testWriteAccessToDir "${dir}"; then
+    toLogWarning "Detected that ${dir} is not writable"
+    dir="${PA_FALLBACK_INSTALLATION_DIR}${dir}"
+    createDirIfNotExistAndSetRights "${dir}" 755
+  fi
+
+  printf "%s" "${dir}"
+}
+
+isNonRootModeEnabled() {
+  printf '%s' "${PARAM_NON_ROOT_MODE}" | grep -qE "(true|no_ambient)"
+}
+
+runAutostartAddingTool() {
+  local prefix="${1}"
+  local file="${2}"
+  local suffix="${3}"
+  local output
+
+  if isNonRootModeEnabled && printf '%s' "${prefix}" | grep -q "chkconfig"; then
+    local action="chkconfig-add"
+    if printf '%s' "${prefix}" | grep -q -- "--del"; then
+      action="chkconfig-del"
+    fi
+    toLogInfo "Using ${AGENT_OS_CONFIG_BIN} ${action}-${file} to modify autostart"
+    output="$("$(getOsConfigBinPath)" "${action}-${file}" 2>&1)"
+  else
+    local command="${prefix}${file} ${suffix}"
+    toLogInfo "Executing ${command}"
+    output="$(${command} 2>&1)"
+  fi
+  local status=$?
+
+  if [ "${output}" ]; then
+    toLogAdaptive ${status} "${output}"
+  fi
+
+  return ${status}
+}
+
+readLinkFromLs() {
+  local path="${1}"
+  local result="${path}"
+  local lsOutput="$(ls -dl "${path}" 2>/dev/null)"
+  if printf '%s' "${lsOutput}" | grep -q " -> "; then
+    local parsedLinkTarget="$(printf '%s' "${lsOutput}" | sed 's|^.* -> ||')"
+    if [ "${parsedLinkTarget}" ]; then
+      result="${parsedLinkTarget}"
+    else
+      toLogWarning "Failed to parse ls output '${lsOutput}'"
+    fi
+  fi
+  printf '%s' "${result}"
+}
+
+readLink() {
+  local args=-e
+  local path="${1}"
+  if [ "${2}" ]; then
+    args="${1}"
+    path="${2}"
+  fi
+
+  (
+    if [ "${ARCH_ARCH}" = "AIX" ]; then
+      PATH="${PATH}:/opt/freeware/bin"
+    fi
+
+    if isAvailable readlink; then
+      #shellcheck disable=SC2086
+      readlink ${args} "${path}"
+    else
+      toLogInfo "readlink command not found, falling back to parsing ls output"
+      readLinkFromLs "${path}"
+    fi
+  )
+}
+
+isAvailable() {
+  command -v "${1}" >/dev/null 2>&1
+}
+
+getValueFromConfigFile() {
+  local key="${1}"
+  local separator="${2}"
+  local configFile="${3}"
+  local defaultValue="${4}"
+
+  local value="$(sed -n "s|${key}${separator}||p" "${configFile}" 2>/dev/null)"
+
+  if [ "${value}" ]; then
+    printf '%s' "${value}"
+  else
+    printf '%s' "${defaultValue}"
+  fi
+}
+
+isAnotherInstallerRunning() {
+  if [ ! -f "${INSTALLER_LOCK_FILE}" ]; then
+    return 1
+  fi
+
+  local pidFromLockFile="$(head -n 1 "${INSTALLER_LOCK_FILE}")"
+  local nameFromLockFile="$(tail -n 1 "${INSTALLER_LOCK_FILE}")"
+  if [ "$(wc -l <"${INSTALLER_LOCK_FILE}")" -ne 2 ] || [ -z "${pidFromLockFile}" ] || [ -z "${nameFromLockFile}" ]; then
+    toConsoleWarning "Installer lock file ${INSTALLER_LOCK_FILE} is damaged, skipping uniqueness check."
+    toConsoleWarning "Lock file contents: '$(cat ${INSTALLER_LOCK_FILE})'"
+    return 1
+  fi
+
+  #shellcheck disable=SC2009
+  local foundProcesses="$(ps -e -o "pid,args" 2>&1 | grep -w "${nameFromLockFile}" | grep -v " grep ")"
+  if printf '%s' "${foundProcesses}" | awk '{ print $1 }' | grep -wq "${pidFromLockFile}"; then
+    local errorMessage="Another ${BRAND_PRODUCT_NAME} installer or uninstaller is already running"
+    if printf '%s' "${foundProcesses}" | grep -q "${DOWNLOADS_DIRECTORY}"; then
+      errorMessage="${errorMessage} (AutoUpdate is in progress)"
+    fi
+
+    toConsoleError "${errorMessage}, PID ${pidFromLockFile}. Exiting."
+    return 0
+  fi
+
+  toConsoleInfo "Lock file exists but corresponding installer process does not run, contents of lock file: ${pidFromLockFile}, ${nameFromLockFile}."
+  return 0
+}
+
+createInstallerLockFile() {
+  printf '%s\n%s\n' "$$" "$0" >"${INSTALLER_LOCK_FILE}" 2>/dev/null
+}
+
+removeInstallerLockFile() {
+  toLogInfo "Removing installer lock file."
+  ${SAFE_RM_EXEC} -f "${INSTALLER_LOCK_FILE}"
+}
+
+logBasicStartupInformation() {
+  toLogInfo "Command line: $(removeSecretsFromString "${@}")"
+  toLogInfo "Shell options: $-"
+  toLogInfo "Working dir: $(pwd)"
+  toLogInfo "PID: $$"
+  toLogInfo "Parent process: $(
+    printf '\n'
+    ps -o user,pid,ppid,comm -p ${PPID} 2>&1
+  )"
+  toLogInfo "User id: $(id -u)"
+}
+
+mapPidsToName() {
+  local pids="${1}"
+  local output
+  for pid in ${pids}; do
+    local name="$(grep 'Name:' "/proc/${pid}/status" 2>/dev/null | awk '{print $2}')"
+    output="${output}, ${pid} (${name})"
+  done
+
+  printf '%s' "${output}" | cut -c 3-
+}
+
+readonly ARCH_ARCH="X86"
+readonly ARCH_VERSIONED_LIB_DIR_PREFIX="linux-x86"
+
+arch_checkArchitectureCompatibility() {
+  local arch="$(detectArchitecture)"
+  if [ "${arch}" = "X86_64" ] || [ "${arch}" = "IA64" ]; then
+    arch="X86_64"
+  else
+    arch="$(uname -m | sed -e 's/i.86/x86/' | sed -e 's/i86pc/x86/' | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  printf '%s' "${arch}"
+  [ "${arch}" = "X86_64" ]
+}
+
+arch_local_getLibraryPathFromLdd() {
+  local binary="${1}"
+  ldd "${binary}" 2>/dev/null | grep libc.so | awk '{ print $3 }'
+}
+
+arch_local_detectProcessAgentInstallerDirectories() {
+  local useLddOutput="false"
+
+  local systemLib32Prefix
+  systemLib32Prefix="$(arch_local_getSystemLibraryPath 32)"
+  local exitCode=$?
+
+  if [ ! "${systemLib32Prefix}" ]; then
+    if [ "${exitCode}" -eq 0 ]; then
+      toLogWarning "This is a 64-bit platform with 32-bit libraries installed, but ${AGENT_INSTALL_ACTION_BIN} failed to determine their location"
+      useLddOutput="true"
+    else
+      toLogInfo "This is a 64-bit platform and 32-bit libraries were not detected"
+    fi
+  else
+    systemLib32Prefix="$(systemLibDirSanityCheck "/${systemLib32Prefix}")"
+    if [ ! "${systemLib32Prefix}" ]; then
+      useLddOutput="true"
+    fi
+  fi
+
+  local systemLib64Prefix="$(arch_local_getSystemLibraryPath 64)"
+  if [ ! "${systemLib64Prefix}" ]; then
+    toLogWarning "This is a 64-bit platform, but ${AGENT_INSTALL_ACTION_BIN} failed to determine location of 64-bit libraries"
+    useLddOutput="true"
+  else
+    systemLib64Prefix="$(systemLibDirSanityCheck "/${systemLib64Prefix}")"
+    if [ ! "${systemLib64Prefix}" ]; then
+      useLddOutput="true"
+    fi
+  fi
+
+  if [ "${useLddOutput}" = "true" ]; then
+    arch_local_detectProcessAgentDirectoriesBasedOnLdd
+  else
+    readonly SYSTEM_LIB32="${systemLib32Prefix}"
+    readonly SYSTEM_LIB64="${systemLib64Prefix}"
+  fi
+} 2>>"${LOG_FILE}"
+
+arch_detectProcessAgentInstallerDirectories() {
+  if [ "${CONTAINER_DEPLOYMENT_STATE_ENTRY}"x = "true"x ]; then
+    readonly SYSTEM_LIB32="${PA_FALLBACK_INSTALLATION_DIR}/lib32"
+    readonly SYSTEM_LIB64="${PA_FALLBACK_INSTALLATION_DIR}/lib64"
+    createDirIfNotExistAndSetRights "${SYSTEM_LIB32}" 755
+    createDirIfNotExistAndSetRights "${SYSTEM_LIB64}" 755
+    return 0
+  fi
+
+  arch_local_detectProcessAgentInstallerDirectories
+}
+
+arch_getLibMacro() {
+  local libMacro=""
+  if [ "${SYSTEM_LIB32}" ]; then
+    #shellcheck disable=SC2016
+    libMacro='/$LIB'
+  fi
+  printf "%s" "${libMacro}"
+}
+
+arch_checkGlibc() {
+  local glibcVersion="$(ldd --version | awk 'NR==1{ print $NF }')"
+
+  toLogInfo "Detected glibc version: ${glibcVersion}"
+
+  if [ "$(format_version "${glibcVersion}")" -gt "$(format_version "${GLIBC_SUPPORTED_VERSION}")" ]; then
+    return
+  elif [ "$(format_version "${glibcVersion}")" -lt "$(format_version "${GLIBC_SUPPORTED_VERSION}")" ]; then
+    toConsoleError "We can't continue setup. The glibc version: ${glibcVersion} detected on your system isn't supported."
+    toConsoleError "To install ${BRAND_AGENT_PRODUCT_NAME} you need at least glibc ${GLIBC_SUPPORTED_VERSION}."
+    toConsoleError "Stopping installer process..."
+    finishInstaller "${EXIT_CODE_GLIBC_VERSION_TOO_LOW}"
+  fi
+
+  if ! isAvailable rpm; then
+    toLogInfo "RPM not detected - skipping glibc patch version check."
+    return
+  fi
+
+  local glibcFullVersion="$(arch_runCommandWithTimeout 60 "rpm" "-q" "glibc" | tail -n 1)"
+  if [ ! "${glibcFullVersion}" ]; then
+    toConsoleError "Could not determine exact glibc version using RPM."
+    finishInstaller "${EXIT_CODE_GLIBC_VERSION_TOO_LOW}"
+  fi
+
+  # fix for RedHat 5.x with unsufficient glibc 2.5 patch version
+  local glibcPatchVersion="$(format_patch "${glibcFullVersion}")"
+  if [ "$(format_version "${glibcVersion}")" -eq "$(format_version "${GLIBC_SUPPORTED_VERSION}")" ] && [ "${glibcPatchVersion}" -lt "${GLIBC_SUPPORTED_VERSION_MINIMAL_PATCH}" ]; then
+    toConsoleError "glibc patch version: ${glibcPatchVersion} detected on your system is not supported, setup won't continue."
+    toConsoleError "To install ${BRAND_AGENT_PRODUCT_NAME} you need at least glibc ${GLIBC_SUPPORTED_VERSION}-${GLIBC_SUPPORTED_VERSION_MINIMAL_PATCH}."
+    finishInstaller "${EXIT_CODE_GLIBC_VERSION_TOO_LOW}"
+  fi
+}
+
+# 'timeout' requires gnu-coreutils8, i.e. it is not available on RHEL5, that's why we need this utility function
+arch_runCommandWithTimeout() {
+  local commandTimeout="${1}"
+  shift
+  local resultFile="/tmp/smartagent_commandResult_$$"
+  local errorFile="/tmp/smartagent_commandError_$$"
+  local loopErrorsFile="/tmp/smartagent_loopErrors_$$"
+
+  toLogInfo "Executing $* with timeout ${commandTimeout} seconds"
+  (
+    "$@" >${resultFile} 2>${errorFile} &
+    child=$!
+    while [ "${commandTimeout}" -gt 0 ]; do
+      toLogInfo "Time left: ${commandTimeout} s,  pid: ${child}"
+      sleep 1
+      kill -0 "${child}" 2>/dev/null || exit 0
+      commandTimeout=$((commandTimeout - 1))
+    done
+
+    # Be nice, post SIGTERM first.
+    # The 'exit 0' below will be executed if any preceeding command fails.
+    toLogInfo "Killing ${child} with SIGTERM"
+    kill -s 15 "${child}" && kill -0 "${child}" || exit 0
+    sleep 1
+    toLogWarning "Killing ${child} with SIGKILL"
+    kill -s 9 "${child}"
+  ) 2>"${loopErrorsFile}"
+
+  local errorOutput="$(cat "${errorFile}")"
+  local commandOutput="$(cat "${resultFile}")"
+  local loopErrors="$(cat "${loopErrorsFile}")"
+  ${SAFE_RM_EXEC} -f "${errorFile}" "${resultFile}" "${loopErrorsFile}"
+
+  if [ -n "${errorOutput}" ]; then
+    toLogInfo "Failed to execute command, error output: ${errorOutput}"
+    if [ "${loopErrors}" ]; then
+      toLogInfo "Loop errors: ${loopErrors}"
+    fi
+    printf ""
+    return 1
+  else
+    toLogInfo "Command executed successfully, output: ${commandOutput}"
+    if [ "${loopErrors}" ]; then
+      toLogInfo "Loop errors: ${loopErrors}"
+    fi
+    printf '%s' "${commandOutput}"
+    return 0
+  fi
+}
+
+arch_getAccessRights() {
+  stat --format='%A' "${1}"
+}
+
+arch_preloadTest() {
+  if [ "${SYSTEM_LIB32}" ]; then
+    runSanityCheckCommand "performLdPreloadSanityCheck" "32"
+    local returnCode=$?
+    if [ ${returnCode} -ne 0 ]; then
+      return ${returnCode}
+    fi
+  fi
+
+  if [ "${SYSTEM_LIB64}" ]; then
+    runSanityCheckCommand "performLdPreloadSanityCheck" "64"
+  fi
+}
+
+arch_checkEnvironmentConfiguration() {
+  if ! stat --format='%t,%T' /dev/null | grep -q "1,3"; then
+    toLogInfo "$(stat /dev/null)"
+    toConsoleError "Installer detected corruption of '/dev/null': Not a character device"
+    return "${EXIT_CODE_MISCONFIGURED_ENVIRONMENT}"
+  fi
+
+  return 0
+}
+
+arch_moveReplaceTarget() {
+  local source="${1}"
+  local target="${2}"
+  mv -fT "${source}" "${target}"
+}
+
+initLog() {
+  if [ "${CONTAINER_DEPLOYMENT_STATE_ENTRY}"x != "true"x ]; then
+    createLogDirsIfMissing
+  fi
+
+  toLogInfo "Uninstaller started."
+  toLogInfo "Distribution: $(detectLinuxDistribution)"
+  logBasicStartupInformation "${@}"
+}
+
+finishUninstaller() {
+  removeInstallerLockFile
+  if [ -z "$(ls -A ${INSTALL_DIR})" ];then
+    ${SAFE_RM_EXEC} -df ${INSTALL_DIR}
+    ${SAFE_RM_EXEC} -f ${CURRENT_VERSION_LINK}
+  fi
+  if [ $1 -eq 0 ]; then
+    toConsoleInfo "Uninstaller finished successfully"
+  else
+    toConsoleInfo "Uninstaller finished failed"
+  fi
+  exit "${EXIT_CODE_OK}"
+}
+
+setLogFilePath() {
+    #== - 反安装日志: uninstallation-{timestamp}.log 如: uninstallation-20220601110912.log
+    readonly LOG_FILE="${INSTALLER_LOG_DIR}/uninstallation-"$(date -u +"%Y%m%d%H%M%S")".log"
+}
+
+removeScriptsFromAutostart() {
+  local prefix="${1}"
+  local suffix="${2}"
+
+  if ! runAutostartAddingTool "${prefix}" "${SERVICE_SCRIPT_FILE}" "${suffix}"; then
+    toConsoleError "Failed to remove ${SERVICE_SCRIPT_FILE} from autostart. For details, see ${LOG_FILE}"
+  fi
+}
+
+removeUnitFromSystemd() {
+  local unit=${1}
+  if [ ! -e "${SYSTEMD_UNIT_FILES_DIR}/${unit}" ]; then
+    toLogInfo "${unit} does not exist and will not be removed from ${INIT_SYSTEM_SYSTEMD} autostart"
+    return
+  fi
+
+  executeSystemctlCommand disable "${unit}"
+  ${SAFE_RM_EXEC} -f "${SYSTEMD_UNIT_FILES_DIR}/${unit}"
+}
+
+removeSystemvAutostart() {
+  toConsoleInfo "Removing ${BRAND_AGENT_PRODUCT_NAME} from autostart"
+
+  #Order of checking is important
+  if [ -x /usr/bin/update-rc.d ]; then #Ubuntu
+    removeScriptsFromAutostart "/usr/bin/update-rc.d -f " "remove"
+  elif [ -x /usr/sbin/update-rc.d ]; then #Ubuntu
+    removeScriptsFromAutostart "/usr/sbin/update-rc.d -f " "remove"
+  elif [ -x /sbin/chkconfig ]; then #RedHat
+    removeScriptsFromAutostart "/sbin/chkconfig --del "
+  elif [ -x /usr/lib/lsb/install_initd ]; then #Suse
+    removeScriptsFromAutostart "/usr/lib/lsb/install_initd ${INIT_DIR}/"
+  elif [ "${ARCH_ARCH}" = "AIX" ]; then
+    #== 不存在
+    arch_removeAutostart
+  else
+    toConsoleError "Couldn't remove ${BRAND_AGENT_PRODUCT_NAME} from autostart."
+  fi
+}
+
+removeSystemdAutostart() {
+  removeUnitFromSystemd "${SYSTEMD_UNIT_FILE_AGENT}"
+  executeSystemctlCommand daemon-reload ""
+}
+
+stopAgentService() {
+  if [ -s "${AGENT_PID_FILE}" ]; then
+    toLogInfo "Stop ${AGENT_PROC}..."
+    commandErrorWrapper ${AGENT_BIN_DIR}/agentctl stop
+  fi
+}
+
+removeSystemvInitScripts() {
+  if [ "${ARCH_ARCH}" = "AIX" ]; then
+    return
+  fi
+
+  toConsoleInfo "Removing ${SERVICE_SCRIPT_FILE} from ${INIT_DIR}"
+  ${SAFE_RM_EXEC} -f "${INIT_DIR}/${SERVICE_SCRIPT_FILE}"
+}
+
+removeAutostartScripts() {
+  if [ "${INIT_SYSTEM}" = "${INIT_SYSTEM_SYSV}" ]; then
+    removeSystemvAutostart
+    removeSystemvInitScripts
+  else
+    removeSystemdAutostart
+  fi
+}
+
+createUninstallInfoFile() {
+  local reason
+  if [ "${IS_UPGRADING}"x = "true"x ]; then
+    reason="upgrade"
+  else
+    reason="uninstall"
+  fi
+
+  toLogInfo "Creating uninstall.info file: ${reason}"
+}
+
+initializeUninstaller() {
+  local installerPid=
+  local skipRootPrivilegesCheck=
+
+  if [ $# -eq 1 ]; then
+    installerPid="${1}"
+  fi
+  if [ $# -eq 2 ]; then
+     installerPid="${1}"
+     skipRootPrivilegesCheck="${2}"
+  fi
+
+  setPATH
+
+  #setLogFilePath "${installerPid}"
+  setLogFilePath
+
+  if [ "${installerPid}" ] && [ "${installerPid}"x != "online"x ]; then
+    IS_UPGRADING="true"
+  else
+    IS_UPGRADING="false"
+  fi
+  toConsoleInfo "IS_UPGRADING :${IS_UPGRADING}"
+
+  if [ "${skipRootPrivilegesCheck}"x = "true"x ] && [ "${IS_UPGRADING}"x = "true"x ]; then
+    toConsoleInfo "Skipping root privileges check"
+  else
+    if ! checkRootAccess; then
+      toConsoleError "You must run uninstaller with root privileges"
+      exit "${EXIT_CODE_GENERIC_ERROR}"
+    fi
+  fi
+
+  initLog "${@}"
+
+  toLogInfo "Launched during upgrade: ${IS_UPGRADING}"
+  createUninstallInfoFile
+
+  configureSignalHandling "finishUninstaller"
+
+  if [ "${IS_UPGRADING}"x = "false"x ] && [ "${CONTAINER_DEPLOYMENT_STATE_ENTRY}"x != "true"x ]; then
+    if isAnotherInstallerRunning; then
+      exit "${EXIT_CODE_ANOTHER_INSTALLER_RUNNING}"
+    fi
+    createInstallerLockFile
+  fi
+
+  if [ ! -f ${PERL_FILE} ]; then
+    toConsoleInfo "/usr/bin/perl file not found so ${SAFE_RM_EXEC} is used."
+  fi
+
+  toConsoleInfo "Using ${SAFE_RM_EXEC} to uninstall"
+}
+
+getVolumeStorageAgentInstallDir() {
+  readLink "$(dirname "${0}")"
+}
+
+cleanUpSELinux() {
+  if [ ! "${ARCH_HAS_SELINUX}" ] || ! isAvailable semodule; then
+    return
+  fi
+
+  toConsoleInfo "Removing ${BRAND_FORMAL_NAME} SELinux policies, this may take a while..."
+}
+
+cleanUpFilesDuringUninstall() {
+	toLogInfo "This is a full uninstaller, cleaning up rest of the files"
+
+  local fileNames="${AGENT_CONF_DIR} ${AGENT_BIN_DIR} ${LOG_DIR} ${AGENT_SCRIPTS_DIR} ${AGENT_LIBS_DIR}"
+  for fileName in ${fileNames}; do
+	  removeIfExists "${fileName}"
+  done
+
+  fileNames="uninstall.sh installer.version"
+  for fileName in ${fileNames}; do
+	  removeIfExists "${INSTALL_DIR}/${fileName}"
+  done
+
+}
+
+handleRegularUninstaller() {
+
+  checkInitSystem
+  toLogInfo "Detected init system: ${INIT_SYSTEM}, version: ${INIT_SYSTEM_VERSION}"
+  if ! setLocationOfInitScripts; then
+    toLogWarning "Cannot determine location of init scripts."
+  fi
+
+  stopAgentService
+
+  cleanUpFilesDuringUninstall
+
+}
+
+main() {
+  initializeUninstaller "$@"
+  handleRegularUninstaller "$@"
+#  finishUninstaller
+}
+
+################################################################################
+#
+# Script start
+#
+################################################################################
+main "$@"

BIN
dist/scripts/xzdec


+ 1 - 1
ebpftracer/init.go

@@ -4,7 +4,7 @@ import (
 	"strings"
 	"strings"
 
 
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/coroot-node-agent/proc"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 type file struct {
 type file struct {

+ 6 - 8
ebpftracer/jvm.go

@@ -4,6 +4,7 @@ import (
 	"errors"
 	"errors"
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer/inject"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer/inject"
+	klog "github.com/sirupsen/logrus"
 	"io/ioutil"
 	"io/ioutil"
 	"runtime"
 	"runtime"
 	"strings"
 	"strings"
@@ -36,15 +37,13 @@ func (t *Tracer) AttachJavaNioReadUprobes(pid uint32, codeType common.CodeType)
 		bpath = proc.Path(uint32(pid), "exe")
 		bpath = proc.Path(uint32(pid), "exe")
 	} else {
 	} else {
 		version := UsePIDToGetJDKVersion(pid)
 		version := UsePIDToGetJDKVersion(pid)
-		fmt.Println("java version is ", version)
-
+		klog.Infof("[attach] java version is %s", version)
 		bpath = getSoPath(pid, "libnio.so")
 		bpath = getSoPath(pid, "libnio.so")
 		if bpath == "" {
 		if bpath == "" {
 			return nil, errors.New("can not find nio.so")
 			return nil, errors.New("can not find nio.so")
 		}
 		}
 	}
 	}
-
-	fmt.Println("find the nio.so path is ", bpath)
+	klog.Infof("[attach] find the nio.so path is  %s", bpath)
 	ex, err := link.OpenExecutable(bpath)
 	ex, err := link.OpenExecutable(bpath)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -102,7 +101,7 @@ func (t *Tracer) AttachJavaNioReadUprobes(pid uint32, codeType common.CodeType)
 				break
 				break
 			}
 			}
 		}
 		}
-		fmt.Println("s.Name-----:", s.Name)
+		klog.Infof("[attach] java symbol name is  %s", s.Name)
 		switch s.Name {
 		switch s.Name {
 		case symbolsocketRead0:
 		case symbolsocketRead0:
 			sStart := s.Value - textSection.Addr
 			sStart := s.Value - textSection.Addr
@@ -128,7 +127,7 @@ func (t *Tracer) AttachJavaNioReadUprobes(pid uint32, codeType common.CodeType)
 	if len(links) == 0 {
 	if len(links) == 0 {
 		return nil, fmt.Errorf("no links found for %s", bpath)
 		return nil, fmt.Errorf("no links found for %s", bpath)
 	}
 	}
-	fmt.Println("[jvm] libnio attached, pid is ", pid)
+	klog.WithField("pid", pid).Infof("[attach] libnio attached!")
 	return links, nil
 	return links, nil
 }
 }
 
 
@@ -179,7 +178,6 @@ func (t *Tracer) AttachJavaNetWriteUprobes(pid uint32) ([]link.Link, error) {
 	}
 	}
 
 
 	err := inject.JvmInject(jvmInjector)
 	err := inject.JvmInject(jvmInjector)
-	fmt.Println("AttachJavaNetWriteUprobes:", err)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -203,7 +201,7 @@ func (t *Tracer) AttachJavaNetWriteUprobes(pid uint32) ([]link.Link, error) {
 	if len(links) == 0 {
 	if len(links) == 0 {
 		return nil, errors.New("can not find uprobe_Java_net_SocketOutputStream_socketWrite0")
 		return nil, errors.New("can not find uprobe_Java_net_SocketOutputStream_socketWrite0")
 	}
 	}
-	fmt.Println("[jvm] libnet attached", pid)
+	klog.WithField("pid", pid).Infoln("[jvm] libnet attached")
 	return links, nil
 	return links, nil
 }
 }
 
 

+ 4 - 5
ebpftracer/stack.go

@@ -1,11 +1,10 @@
 package ebpftracer
 package ebpftracer
 
 
 import (
 import (
-	"fmt"
-
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf/link"
 	"github.com/cilium/ebpf/link"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 func (t *Tracer) AttachStackUprobes(path string, uprobes []tracer.Uprobe) []link.Link {
 func (t *Tracer) AttachStackUprobes(path string, uprobes []tracer.Uprobe) []link.Link {
@@ -15,10 +14,10 @@ func (t *Tracer) AttachStackUprobes(path string, uprobes []tracer.Uprobe) []link
 	if err != nil {
 	if err != nil {
 		return nil
 		return nil
 	}
 	}
-	fmt.Println("Attach Start", path)
+	klog.Infoln("[stack] Attach Start", path)
 
 
 	for i, up := range uprobes {
 	for i, up := range uprobes {
-		fmt.Printf("attaching %d -> %d -> %s -> 0x%x -> 0x%x -> 0x%x\n", i, len(uprobes), up.Funcname, up.AbsOffset, up.Address, up.AbsOffset+up.Address)
+		klog.Debugf("[stack] attaching %d -> %d -> %s -> 0x%x -> 0x%x -> 0x%x", i, len(uprobes), up.Funcname, up.AbsOffset, up.Address, up.AbsOffset+up.Address)
 		var prog *ebpf.Program
 		var prog *ebpf.Program
 		switch up.Location {
 		switch up.Location {
 		case tracer.AtEntry:
 		case tracer.AtEntry:
@@ -30,7 +29,7 @@ func (t *Tracer) AttachStackUprobes(path string, uprobes []tracer.Uprobe) []link
 		}
 		}
 		uplink, err := ex.Uprobe(up.Funcname, prog, &link.UprobeOptions{Address: up.Address, Offset: up.AbsOffset})
 		uplink, err := ex.Uprobe(up.Funcname, prog, &link.UprobeOptions{Address: up.Address, Offset: up.AbsOffset})
 		if err != nil {
 		if err != nil {
-			fmt.Printf("attachingERROR:%v, %v, %v\n", err, up, uplink)
+			klog.Errorf("[stack] attachingERROR:%v, %v, %v", err, up, uplink)
 			// return nil
 			// return nil
 		} else {
 		} else {
 			links = append(links, uplink)
 			links = append(links, uplink)

+ 22 - 23
ebpftracer/tls.go

@@ -16,10 +16,10 @@ import (
 
 
 	"github.com/cilium/ebpf/link"
 	"github.com/cilium/ebpf/link"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/coroot-node-agent/proc"
+	klog "github.com/sirupsen/logrus"
 	"golang.org/x/arch/arm64/arm64asm"
 	"golang.org/x/arch/arm64/arm64asm"
 	"golang.org/x/arch/x86/x86asm"
 	"golang.org/x/arch/x86/x86asm"
 	"golang.org/x/mod/semver"
 	"golang.org/x/mod/semver"
-	"k8s.io/klog/v2"
 )
 )
 
 
 const (
 const (
@@ -52,10 +52,10 @@ func (t *Tracer) AttachOpenSslUprobes(pid uint32) ([]link.Link, error) {
 					return
 					return
 				}
 				}
 			}
 			}
-			klog.ErrorfDepth(1, "pid=%d libssl_version=%s: %s: %s", pid, version, msg, err)
+			klog.Errorf("pid=%d libssl_version=%s: %s: %s", pid, version, msg, err)
 			return
 			return
 		}
 		}
-		klog.InfofDepth(1, "pid=%d libssl_version=%s: %s", pid, version, msg)
+		klog.Infof("pid=%d libssl_version=%s: %s", pid, version, msg)
 	}
 	}
 
 
 	exe, err := link.OpenExecutable(libPath)
 	exe, err := link.OpenExecutable(libPath)
@@ -131,10 +131,10 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 					return
 					return
 				}
 				}
 			}
 			}
-			klog.ErrorfDepth(1, "pid=%d golang_app=%s golang_version=%s: %s: %s", pid, name, version, msg, err)
+			klog.Errorf("pid=%d golang_app=%s golang_version=%s: %s: %s", pid, name, version, msg, err)
 			return
 			return
 		}
 		}
-		klog.InfofDepth(1, "pid=%d golang_app=%s golang_version=%s: %s", pid, name, version, msg)
+		klog.Infof("pid=%d golang_app=%s golang_version=%s: %s", pid, name, version, msg)
 	}
 	}
 
 
 	bi, err := buildinfo.ReadFile(path)
 	bi, err := buildinfo.ReadFile(path)
@@ -316,13 +316,10 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 		case goServeHTTP:
 		case goServeHTTP:
 			l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_HandlerFunc_ServeHTTP"], &link.UprobeOptions{Address: address})
 			l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_HandlerFunc_ServeHTTP"], &link.UprobeOptions{Address: address})
 			if err != nil {
 			if err != nil {
-				log("failed to attach write_enter uprobe", err)
-				fmt.Println("net/http.serverHandler.ServeHTTP no")
-				fmt.Println(err)
+				klog.WithError(err).Errorln("failed to attach uprobe_HandlerFunc_ServeHTTP uprobe")
 				continue
 				continue
-			} else {
-				fmt.Println("net/http.serverHandler.ServeHTTP ok")
 			}
 			}
+			klog.Infoln("net/http.serverHandler.ServeHTTP ok")
 			links = append(links, l)
 			links = append(links, l)
 			sStart := s.Value - textSection.Addr
 			sStart := s.Value - textSection.Addr
 			sEnd := sStart + s.Size
 			sEnd := sStart + s.Size
@@ -332,13 +329,14 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 			sBytes := textSectionData[sStart:sEnd]
 			sBytes := textSectionData[sStart:sEnd]
 			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
 			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
 			if len(returnOffsets) == 0 {
 			if len(returnOffsets) == 0 {
-				log("failed to attach uprobe_HandlerFunc_ServeHTTP uprobe", fmt.Errorf("no return offsets found"))
+				err = fmt.Errorf("failed to attach uprobe_HandlerFunc_ServeHTTP no return offsets found")
+				klog.Errorln(err)
 				return nil, err
 				return nil, err
 			}
 			}
 			for _, offset := range returnOffsets {
 			for _, offset := range returnOffsets {
 				l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_HandlerFunc_ServeHTTP_Returns"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
 				l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_HandlerFunc_ServeHTTP_Returns"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
 				if err != nil {
 				if err != nil {
-					log("failed to attach exit_runtime_newproc1 uprobe", err)
+					klog.WithError(err).Errorln(fmt.Errorf("failed to attach exit_runtime_newproc1 uprobe"))
 					return nil, err
 					return nil, err
 				}
 				}
 				links = append(links, l)
 				links = append(links, l)
@@ -350,13 +348,12 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 			}
 			}
 			l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_Transport_roundTrip"], &link.UprobeOptions{Address: address})
 			l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_Transport_roundTrip"], &link.UprobeOptions{Address: address})
 			if err != nil {
 			if err != nil {
-				log("failed to attach write_enter uprobe", err)
-				fmt.Println("net/http.uprobe_Transport_roundTrip no")
-				fmt.Println(err)
+				klog.WithError(err).Errorln(fmt.Errorf("failed to attach write_enter uprobe"))
 				continue
 				continue
 			} else {
 			} else {
-				fmt.Println("net/http.uprobe_Transport_roundTrip ok")
 			}
 			}
+			klog.Infoln("net/http.uprobe_Transport_roundTrip ok")
+
 			links = append(links, l)
 			links = append(links, l)
 			sStart := s.Value - textSection.Addr
 			sStart := s.Value - textSection.Addr
 			sEnd := sStart + s.Size
 			sEnd := sStart + s.Size
@@ -366,13 +363,14 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 			sBytes := textSectionData[sStart:sEnd]
 			sBytes := textSectionData[sStart:sEnd]
 			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
 			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
 			if len(returnOffsets) == 0 {
 			if len(returnOffsets) == 0 {
-				log("failed to attach uprobe_Transport_roundTrip uprobe", fmt.Errorf("no return offsets found"))
+				err = fmt.Errorf("failed to attach uprobe_Transport_roundTrip uprobe no return offsets found")
+				klog.Errorln(err)
 				return nil, err
 				return nil, err
 			}
 			}
 			for _, offset := range returnOffsets {
 			for _, offset := range returnOffsets {
 				l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_Transport_roundTrip_Returns"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
 				l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_Transport_roundTrip_Returns"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
 				if err != nil {
 				if err != nil {
-					log("failed to attach exit_runtime_newproc1 uprobe", err)
+					klog.WithError(err).Errorln("failed to attach exit_runtime_newproc1 uprobe")
 					return nil, err
 					return nil, err
 				}
 				}
 				links = append(links, l)
 				links = append(links, l)
@@ -381,14 +379,14 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 		case goTlsWriteSymbol:
 		case goTlsWriteSymbol:
 			l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_write_enter"], &link.UprobeOptions{Address: address})
 			l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_write_enter"], &link.UprobeOptions{Address: address})
 			if err != nil {
 			if err != nil {
-				log("failed to attach write_enter uprobe", err)
+				klog.WithError(err).Errorln("failed to attach write_enter uprobe")
 				return nil, err
 				return nil, err
 			}
 			}
 			links = append(links, l)
 			links = append(links, l)
 		case goTlsReadSymbol:
 		case goTlsReadSymbol:
 			l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_read_enter"], &link.UprobeOptions{Address: address})
 			l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_read_enter"], &link.UprobeOptions{Address: address})
 			if err != nil {
 			if err != nil {
-				log("failed to attach read_enter uprobe", err)
+				klog.WithError(err).Errorln("failed to attach read_enter uprobe")
 				return nil, err
 				return nil, err
 			}
 			}
 			links = append(links, l)
 			links = append(links, l)
@@ -400,13 +398,14 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 			sBytes := textSectionData[sStart:sEnd]
 			sBytes := textSectionData[sStart:sEnd]
 			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
 			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
 			if len(returnOffsets) == 0 {
 			if len(returnOffsets) == 0 {
-				log("failed to attach read_exit uprobe", fmt.Errorf("no return offsets found"))
+				err = fmt.Errorf("failed to attach read_exit uprobe no return offsets found")
+				klog.Errorln(err)
 				return nil, err
 				return nil, err
 			}
 			}
 			for _, offset := range returnOffsets {
 			for _, offset := range returnOffsets {
 				l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_read_exit"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
 				l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_read_exit"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
 				if err != nil {
 				if err != nil {
-					log("failed to attach read_exit uprobe", err)
+					klog.WithError(err).Errorln("failed to attach read_exit uprobe")
 					return nil, err
 					return nil, err
 				}
 				}
 				links = append(links, l)
 				links = append(links, l)
@@ -416,7 +415,7 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 	if len(links) == 0 {
 	if len(links) == 0 {
 		return nil, err
 		return nil, err
 	}
 	}
-	log("crypto/tls uprobes attached", nil)
+	klog.Infoln("crypto/tls uprobes attached")
 	return links, nil
 	return links, nil
 }
 }
 
 

+ 11 - 14
ebpftracer/tracer.go

@@ -20,9 +20,9 @@ import (
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/coroot-node-agent/utils"
 	"github.com/coroot/coroot-node-agent/utils"
+	klog "github.com/sirupsen/logrus"
 	"golang.org/x/sys/unix"
 	"golang.org/x/sys/unix"
 	"inet.af/netaddr"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 )
 
 
 /*
 /*
@@ -230,7 +230,7 @@ type perfMap struct {
 func (t *Tracer) ebpf(ch chan<- Event) error {
 func (t *Tracer) ebpf(ch chan<- Event) error {
 	kv := "v" + common.KernelMajorMinor(t.kernelVersion)
 	kv := "v" + common.KernelMajorMinor(t.kernelVersion)
 	path, prg, err := EbpfCode(kv)
 	path, prg, err := EbpfCode(kv)
-	klog.Infof("kv is %s, kernel version: %s path: %s\n", kv, t.kernelVersion, path)
+	klog.Infof("kv is %s, kernel version: %s path: %s", kv, t.kernelVersion, path)
 	if len(prg) == 0 || err != nil {
 	if len(prg) == 0 || err != nil {
 		return fmt.Errorf("kv is %s, unsupported kernel version: %s path: %s err:<%v>", kv, t.kernelVersion, path, err)
 		return fmt.Errorf("kv is %s, unsupported kernel version: %s path: %s err:<%v>", kv, t.kernelVersion, path, err)
 	}
 	}
@@ -248,9 +248,11 @@ func (t *Tracer) ebpf(ch chan<- Event) error {
 	_ = unix.Setrlimit(unix.RLIMIT_MEMLOCK, &unix.Rlimit{Cur: unix.RLIM_INFINITY, Max: unix.RLIM_INFINITY})
 	_ = unix.Setrlimit(unix.RLIMIT_MEMLOCK, &unix.Rlimit{Cur: unix.RLIM_INFINITY, Max: unix.RLIM_INFINITY})
 	tracer.PidFilter(collectionSpec)
 	tracer.PidFilter(collectionSpec)
 	opts := &ebpf.CollectionOptions{MapReplacements: make(map[string]*ebpf.Map)}
 	opts := &ebpf.CollectionOptions{MapReplacements: make(map[string]*ebpf.Map)}
+	klog.Infof("[start] Look eBPF .maps")
 	for _, spec := range collectionSpec.Maps {
 	for _, spec := range collectionSpec.Maps {
-		fmt.Println("maps:", spec.Name)
+		klog.Infoln(spec.Name)
 	}
 	}
+	klog.Infof("[end] Look eBPF .maps")
 	tracer.MapInit(collectionSpec, opts)
 	tracer.MapInit(collectionSpec, opts)
 	// TODO 多进程
 	// TODO 多进程
 	// tracer.SetConstants(collectionSpec)
 	// tracer.SetConstants(collectionSpec)
@@ -274,16 +276,14 @@ func (t *Tracer) ebpf(ch chan<- Event) error {
 		{name: "file_events", typ: perfMapTypeFileEvents, perCPUBufferSizePages: 4},
 		{name: "file_events", typ: perfMapTypeFileEvents, perCPUBufferSizePages: 4},
 		{name: "event_queue", typ: perfMapTypeEventQueue, perCPUBufferSizePages: 32},
 		{name: "event_queue", typ: perfMapTypeEventQueue, perCPUBufferSizePages: 32},
 	}
 	}
-	fmt.Println(len(collectionSpec.Programs))
-	fmt.Println(len(c.Programs))
 	tracer.MapInsert(c)
 	tracer.MapInsert(c)
 	if !t.DisableL7Tracing() {
 	if !t.DisableL7Tracing() {
 		perfMaps = append(perfMaps, perfMap{name: "l7_events", typ: perfMapTypeL7Events, perCPUBufferSizePages: 32})
 		perfMaps = append(perfMaps, perfMap{name: "l7_events", typ: perfMapTypeL7Events, perCPUBufferSizePages: 32})
 	}
 	}
 	perfMaps = append(perfMaps, perfMap{name: tracer.MAP_PERF_SOCKET_DATA_NAME, typ: perfMapTypeSocketEvents, perCPUBufferSizePages: 64})
 	perfMaps = append(perfMaps, perfMap{name: tracer.MAP_PERF_SOCKET_DATA_NAME, typ: perfMapTypeSocketEvents, perCPUBufferSizePages: 64})
-	fmt.Println("perfMaps start --")
+	klog.Infof("[start] Look eBPF perf_maps")
 	for _, pm := range perfMaps {
 	for _, pm := range perfMaps {
-		fmt.Println("pm.namepm.name: ", pm.name)
+		klog.Infoln(pm.name)
 		m, ok := t.collection.Maps[pm.name]
 		m, ok := t.collection.Maps[pm.name]
 		if ok {
 		if ok {
 			r, err := perf.NewReader(m, pm.perCPUBufferSizePages*os.Getpagesize())
 			r, err := perf.NewReader(m, pm.perCPUBufferSizePages*os.Getpagesize())
@@ -296,11 +296,12 @@ func (t *Tracer) ebpf(ch chan<- Event) error {
 			go runEventsReader(pm.name, r, ch, pm.typ)
 			go runEventsReader(pm.name, r, ch, pm.typ)
 		}
 		}
 	}
 	}
-	fmt.Println("perfMaps end --")
+	klog.Infof("[end] Look eBPF perf_maps")
 
 
+	klog.Infof("[start] Look eBPF specPrograms")
 	for _, programSpec := range collectionSpec.Programs {
 	for _, programSpec := range collectionSpec.Programs {
 		program := t.collection.Programs[programSpec.Name]
 		program := t.collection.Programs[programSpec.Name]
-		fmt.Println("programSpecprogramSpec:--:", programSpec.Name, programSpec.SectionName, programSpec.Type)
+		klog.Infof("%s:[%s]", programSpec.SectionName, programSpec.Name)
 		if t.DisableL7Tracing() {
 		if t.DisableL7Tracing() {
 			switch programSpec.Name {
 			switch programSpec.Name {
 			case "sys_enter_writev", "sys_enter_write", "sys_enter_sendto", "sys_enter_sendmsg", "sys_enter_sendmmsg":
 			case "sys_enter_writev", "sys_enter_write", "sys_enter_sendto", "sys_enter_sendmsg", "sys_enter_sendmmsg":
@@ -321,11 +322,6 @@ func (t *Tracer) ebpf(ch chan<- Event) error {
 			l, err = link.Tracepoint(parts[0], parts[1], program, nil)
 			l, err = link.Tracepoint(parts[0], parts[1], program, nil)
 		case ebpf.Kprobe:
 		case ebpf.Kprobe:
 			if strings.HasPrefix(programSpec.SectionName, "uprobe/") {
 			if strings.HasPrefix(programSpec.SectionName, "uprobe/") {
-
-				fmt.Println("==============uprobe s")
-				fmt.Println(programSpec.Name, programSpec.SectionName, programSpec.Type)
-				fmt.Println("==============uprobe e")
-
 				t.uprobes[programSpec.Name] = program
 				t.uprobes[programSpec.Name] = program
 				continue
 				continue
 			}
 			}
@@ -337,6 +333,7 @@ func (t *Tracer) ebpf(ch chan<- Event) error {
 		}
 		}
 		t.links = append(t.links, l)
 		t.links = append(t.links, l)
 	}
 	}
+	klog.Infof("[end] Look eBPF specPrograms")
 
 
 	return nil
 	return nil
 }
 }

+ 1 - 1
ebpftracer/tracer/allocate.go

@@ -4,7 +4,7 @@ import (
 	"fmt"
 	"fmt"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer/ptrace"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer/ptrace"
 	"github.com/coroot/coroot-node-agent/utils"
 	"github.com/coroot/coroot-node-agent/utils"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 	"math"
 	"math"
 	"os"
 	"os"
 	"runtime"
 	"runtime"

+ 2 - 2
ebpftracer/tracer/filter.go

@@ -1,7 +1,6 @@
 package tracer
 package tracer
 
 
 import (
 import (
-	"fmt"
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf/asm"
 	"github.com/cilium/ebpf/asm"
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/common"
@@ -15,7 +14,8 @@ func PidFilter(collectionSpec *ebpf.CollectionSpec) {
 			ReferenceOffsets map[string][]int
 			ReferenceOffsets map[string][]int
 		}
 		}
 		for _, prog := range collectionSpec.Programs {
 		for _, prog := range collectionSpec.Programs {
-			fmt.Println("collectionSpec.Program:", prog.Name, prog.SectionName, prog.Type)
+			//klog.Infof("[%v] [%v] [%v]", prog.Type, prog.Name, prog.SectionName)
+			//fmt.Println("collectionSpec.Program:", prog.Name, prog.SectionName, prog.Type)
 			insns := &prog.Instructions
 			insns := &prog.Instructions
 			refs := insns.ReferenceOffsets()
 			refs := insns.ReferenceOffsets()
 
 

+ 8 - 7
ebpftracer/tracer/inject/inject_linux_amd64.go

@@ -13,6 +13,7 @@ import (
 	"bufio"
 	"bufio"
 	"debug/elf"
 	"debug/elf"
 	"fmt"
 	"fmt"
+	klog "github.com/sirupsen/logrus"
 	"golang.org/x/arch/x86/x86asm"
 	"golang.org/x/arch/x86/x86asm"
 	"log"
 	"log"
 	"os"
 	"os"
@@ -107,7 +108,7 @@ func (j *JvmInjector) findReleaseAddressInfoFromMem() error {
 		if pc == 0 && inst.Op == x86asm.JMP {
 		if pc == 0 && inst.Op == x86asm.JMP {
 			// 已经被修改过的首指令
 			// 已经被修改过的首指令
 			j.PreCheck.EbpfCanInjection = true
 			j.PreCheck.EbpfCanInjection = true
-			fmt.Printf("Inst already modified. <%s>\n", x86asm.IntelSyntax(inst, 0, nil))
+			klog.Infof("[inject] Inst already modified. <%s>", x86asm.IntelSyntax(inst, 0, nil))
 			return nil
 			return nil
 		}
 		}
 		if pc == 0 {
 		if pc == 0 {
@@ -464,7 +465,7 @@ func (j *JvmInjector) findLibBaseFromProcMaps(libName string) (uint64, string, e
 			if len(fields) > 5 {
 			if len(fields) > 5 {
 				path := fields[5]
 				path := fields[5]
 				if strings.HasSuffix(path, ".so") {
 				if strings.HasSuffix(path, ".so") {
-					fmt.Printf("Found library %s\n", path)
+					klog.Infof("[inject] found library %s", path)
 					return start, path, nil
 					return start, path, nil
 				}
 				}
 			}
 			}
@@ -520,7 +521,7 @@ func (j *JvmInjector) findReleaseFuncContextFromLibPath() error {
 		log.Fatalf("Error finding base addresses: %v", err)
 		log.Fatalf("Error finding base addresses: %v", err)
 		return err
 		return err
 	}
 	}
-	fmt.Printf("Base address of (%s)%s: %x\n", "", libName, baseAddress)
+	klog.Infof("[inject] Base address of %s: %x", libName, baseAddress)
 
 
 	// 获取函数的偏移量
 	// 获取函数的偏移量
 	functionSym, err := j.getFunctionOffset(libPath, functionName)
 	functionSym, err := j.getFunctionOffset(libPath, functionName)
@@ -529,10 +530,10 @@ func (j *JvmInjector) findReleaseFuncContextFromLibPath() error {
 	j.ReleaseLibNetInfo.FuncSymbol.SymAddr = baseAddress + functionSym.Value
 	j.ReleaseLibNetInfo.FuncSymbol.SymAddr = baseAddress + functionSym.Value
 	j.ReleaseLibNetInfo.FuncSymbol.SymSize = functionSym.Size
 	j.ReleaseLibNetInfo.FuncSymbol.SymSize = functionSym.Size
 	if err != nil {
 	if err != nil {
-		log.Fatalf("Error getting function offset: %v", err)
+		klog.WithError(err).Errorf("Error getting function offset")
 		return err
 		return err
 	}
 	}
-	fmt.Printf("Actual memory address of %s at base 0x%x: 0x%x\n", functionName, baseAddress, j.ReleaseLibNetInfo.FuncSymbol.SymAddr)
+	klog.Infof("[inject] Actual memory address of %s at base 0x%x: 0x%x", functionName, baseAddress, j.ReleaseLibNetInfo.FuncSymbol.SymAddr)
 	err = j.findReleaseAddressInfoFromMem()
 	err = j.findReleaseAddressInfoFromMem()
 
 
 	if err != nil {
 	if err != nil {
@@ -752,12 +753,12 @@ func JvmInject(jvmInjector *JvmInjector) error {
 	// Debug版本无需修改寄存器
 	// Debug版本无需修改寄存器
 	// 已经加载so并指令修改正确的
 	// 已经加载so并指令修改正确的
 	if jvmInjector.PreCheck.EbpfCanInjection {
 	if jvmInjector.PreCheck.EbpfCanInjection {
-		fmt.Println("eBPF can injection.")
+		klog.Infoln("[inject] eBPF can injection.")
 		return nil
 		return nil
 	}
 	}
 
 
 	if err != nil {
 	if err != nil {
-		log.Fatalf("Error message during release phase: %v", err)
+		klog.WithError(err).Errorf("[inject] Error message during release phase.")
 		return err
 		return err
 	}
 	}
 
 

+ 9 - 8
ebpftracer/tracer/offset.go

@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"fmt"
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf"
 	"io"
 	"io"
+	"k8s.io/klog/v2"
 	"net"
 	"net"
 	"os"
 	"os"
 	"strings"
 	"strings"
@@ -33,7 +34,7 @@ type ID struct {
 
 
 func kernelOffsetInferServer(listener net.Listener) error {
 func kernelOffsetInferServer(listener net.Listener) error {
 
 
-	fmt.Println("[eBPF Kernel Adapt] kernel_offset_infer_server started.")
+	klog.Info("[eBPF Kernel Adapt] kernel_offset_infer_server started.")
 
 
 	//cpuOnlineCount := runtime.NumCPU()
 	//cpuOnlineCount := runtime.NumCPU()
 
 
@@ -41,7 +42,7 @@ func kernelOffsetInferServer(listener net.Listener) error {
 		for {
 		for {
 			conn, err := listener.Accept()
 			conn, err := listener.Accept()
 			if err != nil {
 			if err != nil {
-				fmt.Printf("[eBPF Kernel Adapt] Fail to accept client request: %v\n", err)
+				klog.Errorf("[eBPF Kernel Adapt] Fail to accept client request: %v", err)
 				return
 				return
 			}
 			}
 			go handleConnection(conn)
 			go handleConnection(conn)
@@ -59,10 +60,10 @@ func handleConnection(conn net.Conn) {
 		n, err := conn.Read(buffer)
 		n, err := conn.Read(buffer)
 		if err != nil {
 		if err != nil {
 			if err == io.EOF {
 			if err == io.EOF {
-				fmt.Printf("[eBPF Kernel Adapt] client connection closed: %v\n", err)
+				klog.Errorf("[eBPF Kernel Adapt] client connection closed: %v", err)
 				return
 				return
 			}
 			}
-			fmt.Printf("[eBPF Kernel Adapt] Error reading from connection: %v\n", err)
+			klog.Errorf("[eBPF Kernel Adapt] Error reading from connection: %v", err)
 			return
 			return
 		}
 		}
 		//if n == 0 {
 		//if n == 0 {
@@ -75,7 +76,7 @@ func handleConnection(conn net.Conn) {
 		if request == "hello" {
 		if request == "hello" {
 			_, err := conn.Write([]byte("OK"))
 			_, err := conn.Write([]byte("OK"))
 			if err != nil {
 			if err != nil {
-				fmt.Printf("[eBPF Kernel Adapt] Error writing response: %v\n", err)
+				klog.Errorf("[eBPF Kernel Adapt] Error writing response: %v", err)
 				break
 				break
 			}
 			}
 		}
 		}
@@ -89,7 +90,7 @@ func kernelOffsetInferClient() error {
 	}
 	}
 	defer conn.Close()
 	defer conn.Close()
 
 
-	fmt.Println("[eBPF Kernel Adapt] kernel_offset_infer_client started.")
+	klog.Infoln("[eBPF Kernel Adapt] kernel_offset_infer_client started.")
 
 
 	request := "hello"
 	request := "hello"
 	_, err = conn.Write([]byte(request))
 	_, err = conn.Write([]byte(request))
@@ -109,14 +110,14 @@ func kernelOffsetInferClient() error {
 		}
 		}
 
 
 		response := strings.TrimSpace(string(buffer[:n]))
 		response := strings.TrimSpace(string(buffer[:n]))
-		fmt.Println(response)
+		klog.Infoln(response)
 
 
 		if response == "OK" {
 		if response == "OK" {
 			break
 			break
 		}
 		}
 	}
 	}
 
 
-	fmt.Println("[eBPF Kernel Adapt] kernel_offset_infer_client finished.")
+	klog.Infoln("[eBPF Kernel Adapt] kernel_offset_infer_client finished.")
 	return nil
 	return nil
 }
 }
 
 

+ 2 - 2
ebpftracer/tracer/ptrace/ptrace_linux.go

@@ -17,7 +17,7 @@ package ptrace
 import (
 import (
 	"fmt"
 	"fmt"
 	"github.com/coroot/coroot-node-agent/utils"
 	"github.com/coroot/coroot-node-agent/utils"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 	"os"
 	"os"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
@@ -133,7 +133,7 @@ func NewTracedProgram(pid int) (*TracedProgram, error) {
 				return nil, errors.WithStack(err)
 				return nil, errors.WithStack(err)
 			}
 			}
 
 
-			klog.Info("attach successfully", "tid", tid)
+			klog.Infof("attach successfully %d", tid)
 			tids[tid] = true
 			tids[tid] = true
 			tidMap[tid] = true
 			tidMap[tid] = true
 		}
 		}

+ 29 - 30
ebpftracer/tracer/socket.go

@@ -5,7 +5,7 @@ import (
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf/btf"
 	"github.com/cilium/ebpf/btf"
 	"github.com/coroot/coroot-node-agent/utils"
 	"github.com/coroot/coroot-node-agent/utils"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 	"net"
 	"net"
 	"os"
 	"os"
 	"runtime"
 	"runtime"
@@ -134,12 +134,12 @@ func Offset() {
 func set_offset_map(collectionSpec *ebpf.CollectionSpec, opts *ebpf.CollectionOptions) {
 func set_offset_map(collectionSpec *ebpf.CollectionSpec, opts *ebpf.CollectionOptions) {
 	// 解析BTF数据
 	// 解析BTF数据
 	if update_offset_map_from_btf_vmlinux(collectionSpec, opts) != ETR_OK {
 	if update_offset_map_from_btf_vmlinux(collectionSpec, opts) != ETR_OK {
-		klog.Infof("[eBPF Kernel Adapt] Set offsets map from btf_vmlinux, not support.\n")
+		klog.Infof("[eBPF Kernel Adapt] Set offsets map from btf_vmlinux, not support.")
 		if update_offset_map_default(collectionSpec, opts) != ETR_OK {
 		if update_offset_map_default(collectionSpec, opts) != ETR_OK {
-			klog.Infof("Fatal error, failed to update default offset\n")
+			klog.Infof("Fatal error, failed to update default offset")
 		}
 		}
 	} else {
 	} else {
-		klog.Infof("[eBPF Kernel Adapt] Set offsets map from btf_vmlinux, success.\n")
+		klog.Infof("[eBPF Kernel Adapt] Set offsets map from btf_vmlinux, success.")
 	}
 	}
 }
 }
 
 
@@ -151,9 +151,8 @@ func set_offset_map(collectionSpec *ebpf.CollectionSpec, opts *ebpf.CollectionOp
 // __u64 io_event_collect_mode;
 // __u64 io_event_collect_mode;
 // __u64 io_event_minimal_duration;
 // __u64 io_event_minimal_duration;
 func set_conf_map_default(collectionSpec *ebpf.CollectionSpec, opts *ebpf.CollectionOptions) {
 func set_conf_map_default(collectionSpec *ebpf.CollectionSpec, opts *ebpf.CollectionOptions) {
-	fmt.Println("GetHostID")
 	_, charHostID := utils.GetHostID()
 	_, charHostID := utils.GetHostID()
-	_, charAppID := utils.GetAppID()
+	//_, charAppID := utils.GetAppID()
 
 
 	uidBase := uint64(time.Now().UnixNano()/int64(time.Millisecond)) & 0xffffffffffffff
 	uidBase := uint64(time.Now().UnixNano()/int64(time.Millisecond)) & 0xffffffffffffff
 	numCPU := runtime.NumCPU()
 	numCPU := runtime.NumCPU()
@@ -170,15 +169,15 @@ func set_conf_map_default(collectionSpec *ebpf.CollectionSpec, opts *ebpf.Collec
 			IOEventCollectMode:     1,
 			IOEventCollectMode:     1,
 			IOEventMinimalDuartion: 1000000,
 			IOEventMinimalDuartion: 1000000,
 			HostID:                 charHostID,
 			HostID:                 charHostID,
-			APPID:                  charAppID,
-			TotalCpus:              uint64(nCPU),
+			//APPID:                  charAppID,
+			TotalCpus: uint64(nCPU),
 		}
 		}
 		tConf[i] = tracerConf
 		tConf[i] = tracerConf
 	}
 	}
 	if bpf_table_pre_set_value(collectionSpec, opts, MAP_TRACE_CONF_NAME, tConf) != ETR_OK {
 	if bpf_table_pre_set_value(collectionSpec, opts, MAP_TRACE_CONF_NAME, tConf) != ETR_OK {
-		klog.Infof("[eBPF Kernel Adapt] Set config map from btf_vmlinux, not support.\n")
+		klog.Infof("[eBPF Kernel Adapt] Set config map from btf_vmlinux, not support.")
 	} else {
 	} else {
-		klog.Infof("[eBPF Kernel Adapt] Set config map from btf_vmlinux, success.\n")
+		klog.Infof("[eBPF Kernel Adapt] Set config map from btf_vmlinux, success.")
 	}
 	}
 }
 }
 
 
@@ -231,26 +230,26 @@ func update_offset_map_from_btf_vmlinux(collectionSpec *ebpf.CollectionSpec, opt
 	struct_sock_skc_state_offset := kernel_struct_field_offset(btfSpec, "sock_common", "skc_state")
 	struct_sock_skc_state_offset := kernel_struct_field_offset(btfSpec, "sock_common", "skc_state")
 	struct_sock_common_ipv6only_offset := kernel_struct_field_offset(btfSpec, "sock_common", "skc_flags")
 	struct_sock_common_ipv6only_offset := kernel_struct_field_offset(btfSpec, "sock_common", "skc_flags")
 
 
-	klog.Infof("Offsets from BTF vmlinux:\n")
-	klog.Infof("    copied_seq_offs: 0x%x\n", copied_seq_offs)
-	klog.Infof("    write_seq_offs: 0x%x\n", write_seq_offs)
-	klog.Infof("    files_offs: 0x%x\n", files_offs)
-	klog.Infof("    sk_flags_offs: 0x%x\n", sk_flags_offs)
-	klog.Infof("    struct_files_struct_fdt_offset: 0x%x\n", struct_files_struct_fdt_offset)
-	klog.Infof("    struct_files_private_data_offset: 0x%x\n", struct_files_private_data_offset)
-	klog.Infof("    struct_file_f_inode_offset: 0x%x\n", struct_file_f_inode_offset)
-	klog.Infof("    struct_inode_i_mode_offset: 0x%x\n", struct_inode_i_mode_offset)
-	klog.Infof("    struct_file_dentry_offset: 0x%x\n", struct_file_dentry_offset)
-	klog.Infof("    struct_dentry_name_offset: 0x%x\n", struct_dentry_name_offset)
-	klog.Infof("    struct_sock_family_offset: 0x%x\n", struct_sock_family_offset)
-	klog.Infof("    struct_sock_saddr_offset: 0x%x\n", struct_sock_saddr_offset)
-	klog.Infof("    struct_sock_daddr_offset: 0x%x\n", struct_sock_daddr_offset)
-	klog.Infof("    struct_sock_ip6saddr_offset: 0x%x\n", struct_sock_ip6saddr_offset)
-	klog.Infof("    struct_sock_ip6daddr_offset: 0x%x\n", struct_sock_ip6daddr_offset)
-	klog.Infof("    struct_sock_dport_offset: 0x%x\n", struct_sock_dport_offset)
-	klog.Infof("    struct_sock_sport_offset: 0x%x\n", struct_sock_sport_offset)
-	klog.Infof("    struct_sock_skc_state_offset: 0x%x\n", struct_sock_skc_state_offset)
-	klog.Infof("    struct_sock_common_ipv6only_offset: 0x%x\n", struct_sock_common_ipv6only_offset)
+	klog.Infof("Offsets from BTF vmlinux:")
+	klog.Infof("    copied_seq_offs: 0x%x", copied_seq_offs)
+	klog.Infof("    write_seq_offs: 0x%x", write_seq_offs)
+	klog.Infof("    files_offs: 0x%x", files_offs)
+	klog.Infof("    sk_flags_offs: 0x%x", sk_flags_offs)
+	klog.Infof("    struct_files_struct_fdt_offset: 0x%x", struct_files_struct_fdt_offset)
+	klog.Infof("    struct_files_private_data_offset: 0x%x", struct_files_private_data_offset)
+	klog.Infof("    struct_file_f_inode_offset: 0x%x", struct_file_f_inode_offset)
+	klog.Infof("    struct_inode_i_mode_offset: 0x%x", struct_inode_i_mode_offset)
+	klog.Infof("    struct_file_dentry_offset: 0x%x", struct_file_dentry_offset)
+	klog.Infof("    struct_dentry_name_offset: 0x%x", struct_dentry_name_offset)
+	klog.Infof("    struct_sock_family_offset: 0x%x", struct_sock_family_offset)
+	klog.Infof("    struct_sock_saddr_offset: 0x%x", struct_sock_saddr_offset)
+	klog.Infof("    struct_sock_daddr_offset: 0x%x", struct_sock_daddr_offset)
+	klog.Infof("    struct_sock_ip6saddr_offset: 0x%x", struct_sock_ip6saddr_offset)
+	klog.Infof("    struct_sock_ip6daddr_offset: 0x%x", struct_sock_ip6daddr_offset)
+	klog.Infof("    struct_sock_dport_offset: 0x%x", struct_sock_dport_offset)
+	klog.Infof("    struct_sock_sport_offset: 0x%x", struct_sock_sport_offset)
+	klog.Infof("    struct_sock_skc_state_offset: 0x%x", struct_sock_skc_state_offset)
+	klog.Infof("    struct_sock_common_ipv6only_offset: 0x%x", struct_sock_common_ipv6only_offset)
 
 
 	if copied_seq_offs < 0 || write_seq_offs < 0 || files_offs < 0 ||
 	if copied_seq_offs < 0 || write_seq_offs < 0 || files_offs < 0 ||
 		sk_flags_offs < 0 || struct_files_struct_fdt_offset < 0 ||
 		sk_flags_offs < 0 || struct_files_struct_fdt_offset < 0 ||

+ 2 - 0
go.mod

@@ -21,6 +21,7 @@ require (
 	github.com/hashicorp/go-version v1.6.0
 	github.com/hashicorp/go-version v1.6.0
 	github.com/jpillora/backoff v1.0.0
 	github.com/jpillora/backoff v1.0.0
 	github.com/mdlayher/taskstats v0.0.0-20230712191918-387b3d561d14
 	github.com/mdlayher/taskstats v0.0.0-20230712191918-387b3d561d14
+	github.com/natefinch/lumberjack v0.0.0-00010101000000-000000000000
 	github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
 	github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
 	github.com/pkg/errors v0.9.1
 	github.com/pkg/errors v0.9.1
 	github.com/prometheus/client_golang v1.18.0
 	github.com/prometheus/client_golang v1.18.0
@@ -189,6 +190,7 @@ require (
 )
 )
 
 
 replace (
 replace (
+	github.com/natefinch/lumberjack => ./pkg/lumberjack
 	github.com/optiopay/kafka => github.com/cilium/kafka v0.0.0-20180809090225-01ce283b732b
 	github.com/optiopay/kafka => github.com/cilium/kafka v0.0.0-20180809090225-01ce283b732b
 	github.com/pyroscope-io/dotnetdiag => github.com/coroot/dotnetdiag v1.2.2
 	github.com/pyroscope-io/dotnetdiag => github.com/coroot/dotnetdiag v1.2.2
 	go.opentelemetry.io/otel => ./pkg/go.opentelemetry.io/otel
 	go.opentelemetry.io/otel => ./pkg/go.opentelemetry.io/otel

+ 4 - 0
go.sum

@@ -71,6 +71,8 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp
 github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
 github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
 github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
 github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
+github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/Code-Hex/go-generics-cache v1.3.1 h1:i8rLwyhoyhaerr7JpjtYjJZUcCbWOdiYO3fZXLiEC4g=
 github.com/Code-Hex/go-generics-cache v1.3.1 h1:i8rLwyhoyhaerr7JpjtYjJZUcCbWOdiYO3fZXLiEC4g=
 github.com/Code-Hex/go-generics-cache v1.3.1/go.mod h1:qxcC9kRVrct9rHeiYpFWSoW1vxyillCVzX13KZG8dl4=
 github.com/Code-Hex/go-generics-cache v1.3.1/go.mod h1:qxcC9kRVrct9rHeiYpFWSoW1vxyillCVzX13KZG8dl4=
@@ -1630,6 +1632,8 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
 gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
 gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=

+ 1 - 1
logs/journald_reader.go

@@ -8,7 +8,7 @@ import (
 
 
 	"github.com/coreos/go-systemd/v22/sdjournal"
 	"github.com/coreos/go-systemd/v22/sdjournal"
 	"github.com/coroot/logparser"
 	"github.com/coroot/logparser"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 type JournaldReader struct {
 type JournaldReader struct {

+ 181 - 0
logs/log.go

@@ -0,0 +1,181 @@
+package logs
+
+import (
+	"fmt"
+	"github.com/natefinch/lumberjack"
+	"github.com/sirupsen/logrus"
+	"log"
+	"os"
+	"path"
+	"path/filepath"
+	"strings"
+	"time"
+)
+
+const (
+	defaultTimestamp    = "2006-01-02 15:04:05.999"
+	defaultLogDelimeter = "|"
+	defaultLogLevel     = logrus.ErrorLevel
+	defaultLogFileSize  = 50
+	// 日志保存天数
+	defaultMaxAge = 3
+	// 每天最多保存的日志数量
+	defaultMaxBackups = 3
+)
+
+type LogConfig struct {
+	Path       string
+	AppInfo    string
+	MaxSize    int // 日志文件最大尺寸,单位MB
+	MaxBackups int // 最多保留的旧日志文件数
+	MaxAge     int // 日志文件保留的最长时间,单位天
+	// 控制台输出
+	Console bool
+}
+
+// 定义Hook结构体
+type FileHook struct {
+	level     logrus.Level
+	formatter logrus.Formatter
+	logger    *log.Logger
+}
+
+func InitLog(level string, config LogConfig) error {
+	FormatterInit()
+	if config.Console {
+		logrus.SetOutput(os.Stdout)
+	} else {
+		logrus.SetOutput(&NoOut{})
+	}
+	if level == "" {
+		level = "error"
+	}
+	logrusLevel, err := logrus.ParseLevel(level)
+	if err == nil {
+		logrus.SetLevel(logrusLevel)
+	} else {
+		logrus.SetLevel(defaultLogLevel)
+		logrus.WithError(err).Error("log level parse error, use default log level error")
+	}
+	logrus.SetFormatter(NewFormatter())
+	logrus.SetReportCaller(true)
+
+	if config.Path == "" {
+		config.Path = log_config.Path
+	}
+
+	if config.Path == "" {
+		return fmt.Errorf("config.Path is nil")
+	}
+
+	log_config = config
+	RegisterLogHook(config)
+	return nil
+}
+
+// 实现Hook接口
+func (hook *FileHook) Levels() []logrus.Level {
+	return logrus.AllLevels[:hook.level+1]
+}
+
+func (hook *FileHook) Fire(entry *logrus.Entry) error {
+	// 将日志消息格式化为字符串
+	bytes, err := hook.formatter.Format(entry)
+	hook.logger.Print(string(bytes))
+	if err != nil {
+		return fmt.Errorf("failed to format log entry: %v", err)
+	}
+	if err != nil {
+		return fmt.Errorf("failed to write log entry: %v", err)
+	}
+	return nil
+}
+
+type NoOut struct {
+}
+
+func (no *NoOut) Write(p []byte) (n int, err error) {
+	return 0, nil
+}
+
+func RegisterLogHook(logConf LogConfig) {
+	hooks := make(logrus.LevelHooks)
+	logrus.StandardLogger().ReplaceHooks(hooks)
+	// 日志文件最大尺寸,不超过100MB
+	if logConf.MaxSize > defaultLogFileSize || logConf.MaxSize < 1 {
+		logConf.MaxSize = defaultLogFileSize
+	}
+	// 最多保留的旧日志文件数
+	if logConf.MaxBackups > defaultMaxBackups || logConf.MaxBackups == 0 {
+		logConf.MaxBackups = defaultMaxBackups
+	}
+	// 日志文件保留的最长时间,单位天
+	if logConf.MaxAge > defaultMaxAge || logConf.MaxAge == 0 {
+		logConf.MaxAge = defaultMaxAge
+	}
+
+	for _, val := range logrus.AllLevels {
+		if val <= logrus.GetLevel() {
+			fileName := filepath.Join(logConf.Path, logConf.AppInfo+"."+val.String()+".log")
+			archive := &lumberjack.Logger{
+				Filename:   fileName,
+				MaxSize:    logConf.MaxSize,    // 日志文件最大尺寸,单位MB
+				MaxBackups: logConf.MaxBackups, // 最多保留的旧日志文件数
+				MaxAge:     logConf.MaxAge,     // 日志文件保留的最长时间,单位天
+				LocalTime:  true,               // 使用本地时间
+				Compress:   true,
+			}
+			sysLogger := &log.Logger{}
+			sysLogger.SetOutput(archive)
+			hooker := &FileHook{
+				level:     val,
+				formatter: NewFormatter(),
+				logger:    sysLogger,
+			}
+			hooks.Add(hooker)
+		}
+	}
+}
+
+func FormatterInit() {
+	logrus.SetFormatter(NewFormatter())
+	logrus.SetReportCaller(true)
+}
+
+func NewFormatter() *MyFormatter {
+	return &MyFormatter{
+		Pid:             os.Getpid(),
+		TimestampFormat: defaultTimestamp,
+	}
+}
+
+// Custom log format definition
+type MyFormatter struct {
+	Pid             int
+	TimestampFormat string
+}
+
+func (s *MyFormatter) Format(entry *logrus.Entry) ([]byte, error) {
+	//var callerFun string
+	var fileInfo string
+	var entryDataInfo string
+
+	for key, val := range entry.Data {
+		entryDataInfo += fmt.Sprintf(" %s=\"%v\"", key, val)
+	}
+	if entry.HasCaller() {
+		//callerFun = entry.Caller.Function
+		fileInfo = fmt.Sprintf("%s:%d", path.Base(entry.Caller.File), entry.Caller.Line)
+	}
+
+	timestamp := time.Now().Local().Format(s.TimestampFormat)
+	msg := fmt.Sprintf("%s|%s|%d|%s] %s%s\n",
+		timestamp, strings.ToUpper(entry.Level.String()), s.Pid, fileInfo, entry.Message, entryDataInfo)
+	return []byte(msg), nil
+}
+
+var log_config LogConfig
+
+func GetLogConf() LogConfig {
+	return log_config
+}

+ 1 - 1
logs/otel.go

@@ -12,10 +12,10 @@ import (
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/flags"
 	"github.com/coroot/coroot-node-agent/flags"
 	"github.com/coroot/logparser"
 	"github.com/coroot/logparser"
+	klog "github.com/sirupsen/logrus"
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/sdk/resource"
 	"go.opentelemetry.io/otel/sdk/resource"
 	semconv "go.opentelemetry.io/otel/semconv/v1.18.0"
 	semconv "go.opentelemetry.io/otel/semconv/v1.18.0"
-	"k8s.io/klog/v2"
 )
 )
 
 
 var otelLogger otelLogs.Logger
 var otelLogger otelLogs.Logger

+ 1 - 1
logs/tail_reader.go

@@ -9,7 +9,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/coroot/logparser"
 	"github.com/coroot/logparser"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 var (
 var (

+ 34 - 19
logs/tail_reader_test.go

@@ -2,6 +2,9 @@ package main
 
 
 import (
 import (
 	"bytes"
 	"bytes"
+	"github.com/coroot/coroot-node-agent/utils"
+	"github.com/coroot/coroot-node-agent/utils/enums"
+	log "github.com/sirupsen/logrus"
 	"net/http"
 	"net/http"
 	_ "net/http/pprof"
 	_ "net/http/pprof"
 	"os"
 	"os"
@@ -24,7 +27,6 @@ import (
 	"golang.org/x/mod/semver"
 	"golang.org/x/mod/semver"
 	"golang.org/x/sys/unix"
 	"golang.org/x/sys/unix"
 	"golang.org/x/time/rate"
 	"golang.org/x/time/rate"
-	"k8s.io/klog/v2"
 )
 )
 
 
 var (
 var (
@@ -70,11 +72,11 @@ func machineID() string {
 	for _, p := range []string{"sys/devices/virtual/dmi/id/product_uuid", "etc/machine-id", "var/lib/dbus/machine-id"} {
 	for _, p := range []string{"sys/devices/virtual/dmi/id/product_uuid", "etc/machine-id", "var/lib/dbus/machine-id"} {
 		payload, err := os.ReadFile(path.Join("/proc/1/root", p))
 		payload, err := os.ReadFile(path.Join("/proc/1/root", p))
 		if err != nil {
 		if err != nil {
-			klog.Warningln("failed to read machine-id:", err)
+			log.Warningln("failed to read machine-id:", err)
 			continue
 			continue
 		}
 		}
 		id := strings.TrimSpace(strings.Replace(string(payload), "-", "", -1))
 		id := strings.TrimSpace(strings.Replace(string(payload), "-", "", -1))
-		klog.Infoln("machine-id: ", id)
+		log.Infoln("machine-id: ", id)
 		return id
 		return id
 	}
 	}
 	return ""
 	return ""
@@ -83,7 +85,7 @@ func machineID() string {
 func whitelistNodeExternalNetworks() {
 func whitelistNodeExternalNetworks() {
 	netdevs, err := node.NetDevices()
 	netdevs, err := node.NetDevices()
 	if err != nil {
 	if err != nil {
-		klog.Warningln("failed to get network interfaces:", err)
+		log.Warningln("failed to get network interfaces:", err)
 		return
 		return
 	}
 	}
 	for _, iface := range netdevs {
 	for _, iface := range netdevs {
@@ -108,24 +110,37 @@ type MetricData struct {
 }
 }
 
 
 func main() {
 func main() {
-	klog.LogToStderr(false)
-	klog.SetOutput(&RateLimitedLogOutput{limiter: rate.NewLimiter(rate.Limit(*flags.LogPerSecond), *flags.LogBurst)})
+	err := logs.InitLog("info", logs.LogConfig{
+		Path:       utils.GetDefaultLogPath(),
+		AppInfo:    enums.DaemonProc,
+		MaxSize:    50, // 日志文件最大尺寸,单位MB
+		MaxBackups: 3,  // 最多保留的旧日志文件数
+		MaxAge:     3,  // 日志文件保留的最长时间,单位天
+		Console:    true,
+	})
+
+	if err != nil {
+		log.WithError(err).Errorf("log init error.")
+	}
+
+	//log.LogToStderr(false)
+	//log.SetOutput(&RateLimitedLogOutput{limiter: rate.NewLimiter(rate.Limit(*flags.LogPerSecond), *flags.LogBurst)})
 
 
-	klog.Infoln("agent version:", version)
+	log.Infoln("agent version:", version)
 
 
 	hostname, kv, err := uname()
 	hostname, kv, err := uname()
 	if err != nil {
 	if err != nil {
-		klog.Exitln("failed to get uname:", err)
+		log.Fatalln("failed to get uname:", err)
 	}
 	}
-	klog.Infoln("hostname:", hostname)
-	klog.Infoln("kernel version:", kv)
+	log.Infoln("hostname:", hostname)
+	log.Infoln("kernel version:", kv)
 
 
 	ver := common.KernelMajorMinor(kv)
 	ver := common.KernelMajorMinor(kv)
 	if ver == "" {
 	if ver == "" {
-		klog.Exitln("invalid kernel version:", kv)
+		log.Fatalln("invalid kernel version:", kv)
 	}
 	}
 	if semver.Compare("v"+ver, "v"+minSupportedKernelVersion) == -1 {
 	if semver.Compare("v"+ver, "v"+minSupportedKernelVersion) == -1 {
-		klog.Exitf("the minimum Linux kernel version required is %s or later", minSupportedKernelVersion)
+		log.Fatalf("the minimum Linux kernel version required is %s or later", minSupportedKernelVersion)
 	}
 	}
 
 
 	whitelistNodeExternalNetworks()
 	whitelistNodeExternalNetworks()
@@ -140,7 +155,7 @@ func main() {
 	registerer.MustRegister(info("node_agent_info", version))
 	registerer.MustRegister(info("node_agent_info", version))
 
 
 	if err := registerer.Register(node.NewCollector(hostname, kv)); err != nil {
 	if err := registerer.Register(node.NewCollector(hostname, kv)); err != nil {
-		klog.Exitln(err)
+		log.Fatalln(err)
 	}
 	}
 
 
 	//processInfoCh := profiling.Init(machineId, hostname)
 	//processInfoCh := profiling.Init(machineId, hostname)
@@ -148,10 +163,10 @@ func main() {
 	cr, err := containers.NewRegistry(registerer, kv, nil)
 	cr, err := containers.NewRegistry(registerer, kv, nil)
 
 
 	if err != nil {
 	if err != nil {
-		klog.Exitln(err)
+		log.Fatalln(err)
 	}
 	}
 	defer cr.Close()
 	defer cr.Close()
-	klog.Infoln("START_TRACE")
+	log.Infoln("START_TRACE")
 
 
 	//profiling.Start()
 	//profiling.Start()
 	//defer profiling.Stop()
 	//defer profiling.Stop()
@@ -225,13 +240,13 @@ func main() {
 	}
 	}
 
 
 	if err := prom.StartAgent(machineId); err != nil {
 	if err := prom.StartAgent(machineId); err != nil {
-		klog.Exitln(err)
+		log.Fatalln(err)
 	}
 	}
 
 
 	http.Handle("/metrics", promhttp.HandlerFor(registry, promhttp.HandlerOpts{ErrorLog: logger{}, Registry: registerer}))
 	http.Handle("/metrics", promhttp.HandlerFor(registry, promhttp.HandlerOpts{ErrorLog: logger{}, Registry: registerer}))
 	http.HandleFunc("/metrics2", metricsHandler)
 	http.HandleFunc("/metrics2", metricsHandler)
-	klog.Infoln("listening on:", *flags.ListenAddress)
-	klog.Errorln(http.ListenAndServe(*flags.ListenAddress, nil))
+	log.Infoln("listening on:", *flags.ListenAddress)
+	log.Errorln(http.ListenAndServe(*flags.ListenAddress, nil))
 }
 }
 
 
 func info(name, version string) prometheus.Collector {
 func info(name, version string) prometheus.Collector {
@@ -246,7 +261,7 @@ func info(name, version string) prometheus.Collector {
 type logger struct{}
 type logger struct{}
 
 
 func (l logger) Println(v ...interface{}) {
 func (l logger) Println(v ...interface{}) {
-	klog.Errorln(v...)
+	log.Errorln(v...)
 }
 }
 
 
 type RateLimitedLogOutput struct {
 type RateLimitedLogOutput struct {

+ 1 - 1
node/collector.go

@@ -5,7 +5,7 @@ import (
 	"github.com/coroot/coroot-node-agent/flags"
 	"github.com/coroot/coroot-node-agent/flags"
 	"github.com/coroot/coroot-node-agent/node/metadata"
 	"github.com/coroot/coroot-node-agent/node/metadata"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 var (
 var (

+ 1 - 1
node/disk.go

@@ -7,7 +7,7 @@ import (
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 
 
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 var blockDevice = regexp.MustCompile(`^(dm-\d+|(s|h|xv|v)d[a-z]|md\d+|nvme\d+n\d+|rbd\d+)`)
 var blockDevice = regexp.MustCompile(`^(dm-\d+|(s|h|xv|v)d[a-z]|md\d+|nvme\d+n\d+|rbd\d+)`)

+ 1 - 1
node/memory.go

@@ -6,7 +6,7 @@ import (
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 
 
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 func memoryInfo(procRoot string) (MemoryStat, error) {
 func memoryInfo(procRoot string) (MemoryStat, error) {

+ 1 - 1
node/metadata/aws.go

@@ -9,7 +9,7 @@ import (
 	"net/http"
 	"net/http"
 
 
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/coroot-node-agent/proc"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 const awsInstanceMetadataURL = "http://169.254.169.254/latest"
 const awsInstanceMetadataURL = "http://169.254.169.254/latest"

+ 1 - 1
node/metadata/azure.go

@@ -4,7 +4,7 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"net/http"
 	"net/http"
 
 
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 const (
 const (

+ 1 - 1
node/metadata/digital_ocean.go

@@ -4,7 +4,7 @@ import (
 	"io"
 	"io"
 	"net/http"
 	"net/http"
 
 
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 const doInstanceMetadataURL = "http://169.254.169.254/metadata/v1/"
 const doInstanceMetadataURL = "http://169.254.169.254/metadata/v1/"

+ 1 - 1
node/metadata/gcp.go

@@ -4,7 +4,7 @@ import (
 	"strings"
 	"strings"
 
 
 	gcp "cloud.google.com/go/compute/metadata"
 	gcp "cloud.google.com/go/compute/metadata"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 func getGcpMetadata() *CloudMetadata {
 func getGcpMetadata() *CloudMetadata {

+ 1 - 1
node/metadata/hetzner.go

@@ -3,8 +3,8 @@ package metadata
 import (
 import (
 	"net/http"
 	"net/http"
 
 
+	klog "github.com/sirupsen/logrus"
 	"gopkg.in/yaml.v2"
 	"gopkg.in/yaml.v2"
-	"k8s.io/klog/v2"
 )
 )
 
 
 const hetznerInstanceMetadataURL = "http://169.254.169.254/hetzner/v1/metadata"
 const hetznerInstanceMetadataURL = "http://169.254.169.254/hetzner/v1/metadata"

+ 1 - 1
node/metadata/metadata.go

@@ -7,7 +7,7 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 const metadataServiceTimeout = 5 * time.Second
 const metadataServiceTimeout = 5 * time.Second

+ 1 - 1
pinger/pinger.go

@@ -12,12 +12,12 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/coroot-node-agent/proc"
+	klog "github.com/sirupsen/logrus"
 	"github.com/vishvananda/netns"
 	"github.com/vishvananda/netns"
 	"golang.org/x/net/icmp"
 	"golang.org/x/net/icmp"
 	"golang.org/x/net/ipv4"
 	"golang.org/x/net/ipv4"
 	"golang.org/x/sys/unix"
 	"golang.org/x/sys/unix"
 	"inet.af/netaddr"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 )
 
 
 const (
 const (

+ 5 - 5
pkg/go.opentelemetry.io/otel/example/otel-collector/go.mod

@@ -14,7 +14,7 @@ require (
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0
 	go.opentelemetry.io/otel/sdk v1.22.0
 	go.opentelemetry.io/otel/sdk v1.22.0
 	go.opentelemetry.io/otel/trace v1.22.0
 	go.opentelemetry.io/otel/trace v1.22.0
-	google.golang.org/grpc v1.58.2
+	google.golang.org/grpc v1.61.0
 )
 )
 
 
 require (
 require (
@@ -26,11 +26,11 @@ require (
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
 	go.opentelemetry.io/otel/metric v1.22.0 // indirect
 	go.opentelemetry.io/otel/metric v1.22.0 // indirect
 	go.opentelemetry.io/proto/otlp v1.0.0 // indirect
 	go.opentelemetry.io/proto/otlp v1.0.0 // indirect
-	golang.org/x/net v0.12.0 // indirect
+	golang.org/x/net v0.22.0 // indirect
 	golang.org/x/sys v0.18.0 // indirect
 	golang.org/x/sys v0.18.0 // indirect
-	golang.org/x/text v0.11.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
+	golang.org/x/text v0.14.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect
 	google.golang.org/protobuf v1.32.0 // indirect
 	google.golang.org/protobuf v1.32.0 // indirect
 )
 )
 
 

+ 5 - 0
pkg/go.opentelemetry.io/otel/example/otel-collector/go.sum

@@ -22,19 +22,24 @@ go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v8
 go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
 go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
 golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
 golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
 golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
 golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
+golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
 golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
 golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
 golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
 golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
 golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g=
 google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g=
 google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
 google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
 google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
 google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
+google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
 google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
 google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
 google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
 google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
+google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
 google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=

+ 24 - 3
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/go.mod

@@ -16,22 +16,41 @@ require (
 )
 )
 
 
 require (
 require (
+	github.com/BurntSushi/toml v1.4.0 // indirect
+	github.com/agoda-com/opentelemetry-logs-go v0.4.1 // indirect
 	github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
 	github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
 	github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect
 	github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect
+	github.com/cenkalti/backoff/v4 v4.2.1 // indirect
+	github.com/cilium/ebpf v0.11.0 // indirect
+	github.com/coreos/go-systemd/v22 v22.5.0 // indirect
+	github.com/coroot/logparser v1.1.2 // indirect
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 	github.com/go-logr/logr v1.4.1 // indirect
 	github.com/go-logr/logr v1.4.1 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
+	github.com/golang/protobuf v1.5.3 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
 	github.com/hashicorp/go-version v1.6.0 // indirect
 	github.com/hashicorp/go-version v1.6.0 // indirect
-	github.com/kr/pretty v0.3.1 // indirect
+	github.com/natefinch/lumberjack v0.0.0-00010101000000-000000000000 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
-	github.com/rogpeppe/go-internal v1.10.0 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
+	github.com/vishvananda/netlink v1.2.1-beta.2.0.20220608195807-1a118fe229fc // indirect
+	github.com/vishvananda/netns v0.0.4 // indirect
+	go.mongodb.org/mongo-driver v1.13.1 // indirect
 	go.opentelemetry.io/otel/metric v1.22.0 // indirect
 	go.opentelemetry.io/otel/metric v1.22.0 // indirect
 	go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
 	go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
 	go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect
 	go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect
+	golang.org/x/arch v0.4.0 // indirect
+	golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
+	golang.org/x/mod v0.16.0 // indirect
+	golang.org/x/net v0.22.0 // indirect
 	golang.org/x/sys v0.18.0 // indirect
 	golang.org/x/sys v0.18.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect
+	google.golang.org/grpc v1.61.0 // indirect
 	gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
 	gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
-	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
+	gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a // indirect
 	inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a // indirect
 	k8s.io/klog/v2 v2.120.1 // indirect
 	k8s.io/klog/v2 v2.120.1 // indirect
@@ -45,4 +64,6 @@ replace go.opentelemetry.io/otel/trace => ../../../trace
 
 
 replace go.opentelemetry.io/otel/metric => ../../../metric
 replace go.opentelemetry.io/otel/metric => ../../../metric
 
 
+replace github.com/natefinch/lumberjack => ../../../../../../pkg/lumberjack
+
 replace github.com/coroot/coroot-node-agent => ../../../../../../
 replace github.com/coroot/coroot-node-agent => ../../../../../../

+ 101 - 8
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/go.sum

@@ -1,36 +1,69 @@
+github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
+github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
+github.com/agoda-com/opentelemetry-logs-go v0.4.1 h1:PWGqIxkEEg4HIjnHsHmNa+yGu0lhxHz4XPGKeT4o6T0=
+github.com/agoda-com/opentelemetry-logs-go v0.4.1/go.mod h1:CeDuVaK9yCWN+8UjOW8AciYJE0rl7K/mw4ejBntGYkc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs=
 github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs=
 github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
 github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
+github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
+github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs=
+github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA=
+github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA=
+github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coroot/logparser v1.1.2 h1:9aH4zIBle14xMHq07YHqVFE2t68k3LE10X2yKHXtJG8=
+github.com/coroot/logparser v1.1.2/go.mod h1:YfYxn9FYBm5GYHHUB4zI22irFAWVDe2bcbOWDHKSmEo=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
+github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
 github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
+github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
+github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
 github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
 github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
+github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
+github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
 github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
 github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
 github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
-github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc=
+github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
-github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
-github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
+github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
+github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
 github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -38,38 +71,94 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/vishvananda/netlink v1.2.1-beta.2.0.20220608195807-1a118fe229fc h1:2wzJ1cBcM23GetRJs2y6ETXrFMvp6HefTbFWtqviHZQ=
+github.com/vishvananda/netlink v1.2.1-beta.2.0.20220608195807-1a118fe229fc/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
+github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
+github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
+github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
+github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk=
+go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
 go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
 go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
 go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
 go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
 go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=
 go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=
 go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
 go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
 go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
 go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
 go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 h1:WJhcL4p+YeDxmZWg141nRm7XC8IDmhz7lk5GpadO1Sg=
 go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 h1:WJhcL4p+YeDxmZWg141nRm7XC8IDmhz7lk5GpadO1Sg=
 go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
 go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
+golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc=
+golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
+golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
+golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
+golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
 golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg=
+google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0=
+google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac h1:OZkkudMUu9LVQMCoRUbI/1p5VCo9BOrlvkqMvWtqa6s=
+google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
+google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
+google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
 google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
 google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
@@ -77,7 +166,11 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 5 - 5
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/go.mod

@@ -13,8 +13,8 @@ require (
 	go.opentelemetry.io/otel/trace v1.22.0
 	go.opentelemetry.io/otel/trace v1.22.0
 	go.opentelemetry.io/proto/otlp v1.0.0
 	go.opentelemetry.io/proto/otlp v1.0.0
 	go.uber.org/goleak v1.2.1
 	go.uber.org/goleak v1.2.1
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98
-	google.golang.org/grpc v1.58.2
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac
+	google.golang.org/grpc v1.61.0
 	google.golang.org/protobuf v1.32.0
 	google.golang.org/protobuf v1.32.0
 )
 )
 
 
@@ -27,10 +27,10 @@ require (
 	github.com/kr/text v0.2.0 // indirect
 	github.com/kr/text v0.2.0 // indirect
 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
 	go.opentelemetry.io/otel/metric v1.22.0 // indirect
 	go.opentelemetry.io/otel/metric v1.22.0 // indirect
-	golang.org/x/net v0.12.0 // indirect
+	golang.org/x/net v0.22.0 // indirect
 	golang.org/x/sys v0.18.0 // indirect
 	golang.org/x/sys v0.18.0 // indirect
-	golang.org/x/text v0.11.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
+	golang.org/x/text v0.14.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
 )
 
 

+ 5 - 0
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/go.sum

@@ -33,19 +33,24 @@ go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
 go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
 go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
 golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
 golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
 golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
 golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
+golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
 golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
 golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
 golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
 golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
 golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g=
 google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g=
 google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
 google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
 google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
 google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
+google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
 google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
 google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
 google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
 google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
+google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
 google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=

+ 23 - 0
pkg/lumberjack/.gitignore

@@ -0,0 +1,23 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test

+ 6 - 0
pkg/lumberjack/.travis.yml

@@ -0,0 +1,6 @@
+language: go
+
+go:
+  - 1.8
+  - 1.7
+  - 1.6

+ 21 - 0
pkg/lumberjack/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Nate Finch 
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 179 - 0
pkg/lumberjack/README.md

@@ -0,0 +1,179 @@
+# lumberjack  [![GoDoc](https://godoc.org/gopkg.in/natefinch/lumberjack.v2?status.png)](https://godoc.org/gopkg.in/natefinch/lumberjack.v2) [![Build Status](https://travis-ci.org/natefinch/lumberjack.svg?branch=v2.0)](https://travis-ci.org/natefinch/lumberjack) [![Build status](https://ci.appveyor.com/api/projects/status/00gchpxtg4gkrt5d)](https://ci.appveyor.com/project/natefinch/lumberjack) [![Coverage Status](https://coveralls.io/repos/natefinch/lumberjack/badge.svg?branch=v2.0)](https://coveralls.io/r/natefinch/lumberjack?branch=v2.0)
+
+### Lumberjack is a Go package for writing logs to rolling files.
+
+Package lumberjack provides a rolling logger.
+
+Note that this is v2.0 of lumberjack, and should be imported using gopkg.in
+thusly:
+
+    import "gopkg.in/natefinch/lumberjack.v2"
+
+The package name remains simply lumberjack, and the code resides at
+https://github.com/natefinch/lumberjack under the v2.0 branch.
+
+Lumberjack is intended to be one part of a logging infrastructure.
+It is not an all-in-one solution, but instead is a pluggable
+component at the bottom of the logging stack that simply controls the files
+to which logs are written.
+
+Lumberjack plays well with any logging package that can write to an
+io.Writer, including the standard library's log package.
+
+Lumberjack assumes that only one process is writing to the output files.
+Using the same lumberjack configuration from multiple processes on the same
+machine will result in improper behavior.
+
+
+**Example**
+
+To use lumberjack with the standard library's log package, just pass it into the SetOutput function when your application starts.
+
+Code:
+
+```go
+log.SetOutput(&lumberjack.Logger{
+    Filename:   "/var/log/myapp/foo.log",
+    MaxSize:    500, // megabytes
+    MaxBackups: 3,
+    MaxAge:     28, //days
+    Compress:   true, // disabled by default
+})
+```
+
+
+
+## type Logger
+``` go
+type Logger struct {
+    // Filename is the file to write logs to.  Backup log files will be retained
+    // in the same directory.  It uses <processname>-lumberjack.log in
+    // os.TempDir() if empty.
+    Filename string `json:"filename" yaml:"filename"`
+
+    // MaxSize is the maximum size in megabytes of the log file before it gets
+    // rotated. It defaults to 100 megabytes.
+    MaxSize int `json:"maxsize" yaml:"maxsize"`
+
+    // MaxAge is the maximum number of days to retain old log files based on the
+    // timestamp encoded in their filename.  Note that a day is defined as 24
+    // hours and may not exactly correspond to calendar days due to daylight
+    // savings, leap seconds, etc. The default is not to remove old log files
+    // based on age.
+    MaxAge int `json:"maxage" yaml:"maxage"`
+
+    // MaxBackups is the maximum number of old log files to retain.  The default
+    // is to retain all old log files (though MaxAge may still cause them to get
+    // deleted.)
+    MaxBackups int `json:"maxbackups" yaml:"maxbackups"`
+
+    // LocalTime determines if the time used for formatting the timestamps in
+    // backup files is the computer's local time.  The default is to use UTC
+    // time.
+    LocalTime bool `json:"localtime" yaml:"localtime"`
+
+    // Compress determines if the rotated log files should be compressed
+    // using gzip. The default is not to perform compression.
+    Compress bool `json:"compress" yaml:"compress"`
+    // contains filtered or unexported fields
+}
+```
+Logger is an io.WriteCloser that writes to the specified filename.
+
+Logger opens or creates the logfile on first Write.  If the file exists and
+is less than MaxSize megabytes, lumberjack will open and append to that file.
+If the file exists and its size is >= MaxSize megabytes, the file is renamed
+by putting the current time in a timestamp in the name immediately before the
+file's extension (or the end of the filename if there's no extension). A new
+log file is then created using original filename.
+
+Whenever a write would cause the current log file exceed MaxSize megabytes,
+the current file is closed, renamed, and a new log file created with the
+original name. Thus, the filename you give Logger is always the "current" log
+file.
+
+Backups use the log file name given to Logger, in the form `name-timestamp.ext`
+where name is the filename without the extension, timestamp is the time at which
+the log was rotated formatted with the time.Time format of
+`2006-01-02T15-04-05.000` and the extension is the original extension.  For
+example, if your Logger.Filename is `/var/log/foo/server.log`, a backup created
+at 6:30pm on Nov 11 2016 would use the filename
+`/var/log/foo/server-2016-11-04T18-30-00.000.log`
+
+### Cleaning Up Old Log Files
+Whenever a new logfile gets created, old log files may be deleted.  The most
+recent files according to the encoded timestamp will be retained, up to a
+number equal to MaxBackups (or all of them if MaxBackups is 0).  Any files
+with an encoded timestamp older than MaxAge days are deleted, regardless of
+MaxBackups.  Note that the time encoded in the timestamp is the rotation
+time, which may differ from the last time that file was written to.
+
+If MaxBackups and MaxAge are both 0, no old log files will be deleted.
+
+
+
+
+
+
+
+
+
+
+
+### func (\*Logger) Close
+``` go
+func (l *Logger) Close() error
+```
+Close implements io.Closer, and closes the current logfile.
+
+
+
+### func (\*Logger) Rotate
+``` go
+func (l *Logger) Rotate() error
+```
+Rotate causes Logger to close the existing log file and immediately create a
+new one.  This is a helper function for applications that want to initiate
+rotations outside of the normal rotation rules, such as in response to
+SIGHUP.  After rotating, this initiates a cleanup of old log files according
+to the normal rules.
+
+**Example**
+
+Example of how to rotate in response to SIGHUP.
+
+Code:
+
+```go
+l := &lumberjack.Logger{}
+log.SetOutput(l)
+c := make(chan os.Signal, 1)
+signal.Notify(c, syscall.SIGHUP)
+
+go func() {
+    for {
+        <-c
+        l.Rotate()
+    }
+}()
+```
+
+### func (\*Logger) Write
+``` go
+func (l *Logger) Write(p []byte) (n int, err error)
+```
+Write implements io.Writer.  If a write would cause the log file to be larger
+than MaxSize, the file is closed, renamed to include a timestamp of the
+current time, and a new log file is created using the original log file name.
+If the length of the write is greater than MaxSize, an error is returned.
+
+
+
+
+
+
+
+
+
+- - -
+Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md)

+ 11 - 0
pkg/lumberjack/chown.go

@@ -0,0 +1,11 @@
+// +build !linux
+
+package lumberjack
+
+import (
+	"os"
+)
+
+func chown(_ string, _ os.FileInfo) error {
+	return nil
+}

+ 19 - 0
pkg/lumberjack/chown_linux.go

@@ -0,0 +1,19 @@
+package lumberjack
+
+import (
+	"os"
+	"syscall"
+)
+
+// os_Chown is a var so we can mock it out during tests.
+var os_Chown = os.Chown
+
+func chown(name string, info os.FileInfo) error {
+	f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, info.Mode())
+	if err != nil {
+		return err
+	}
+	f.Close()
+	stat := info.Sys().(*syscall.Stat_t)
+	return os_Chown(name, int(stat.Uid), int(stat.Gid))
+}

+ 19 - 0
pkg/lumberjack/example_test.go

@@ -0,0 +1,19 @@
+package lumberjack_test
+
+import (
+	"log"
+
+	"gopkg.in/natefinch/lumberjack.v2"
+)
+
+// To use lumberjack with the standard library's log package, just pass it into
+// the SetOutput function when your application starts.
+func Example() {
+	log.SetOutput(&lumberjack.Logger{
+		Filename:   "/var/log/myapp/foo.log",
+		MaxSize:    500, // megabytes
+		MaxBackups: 3,
+		MaxAge:     28, // days
+		Compress:   true, // disabled by default
+	})
+}

+ 9 - 0
pkg/lumberjack/go.mod

@@ -0,0 +1,9 @@
+module github.com/natefinch/lumberjack
+
+go 1.12
+
+require (
+	github.com/BurntSushi/toml v1.4.0
+	gopkg.in/natefinch/lumberjack.v2 v2.2.1
+	gopkg.in/yaml.v2 v2.4.0
+)

+ 205 - 0
pkg/lumberjack/linux_test.go

@@ -0,0 +1,205 @@
+// +build linux
+
+package lumberjack
+
+import (
+	"os"
+	"syscall"
+	"testing"
+	"time"
+)
+
+func TestMaintainMode(t *testing.T) {
+	currentTime = fakeTime
+	dir := makeTempDir("TestMaintainMode", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+
+	mode := os.FileMode(0600)
+	f, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, mode)
+	isNil(err, t)
+	f.Close()
+
+	l := &Logger{
+		Filename:   filename,
+		MaxBackups: 1,
+		MaxSize:    100, // megabytes
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	newFakeTime()
+
+	err = l.Rotate()
+	isNil(err, t)
+
+	filename2 := backupFile(dir)
+	info, err := os.Stat(filename)
+	isNil(err, t)
+	info2, err := os.Stat(filename2)
+	isNil(err, t)
+	equals(mode, info.Mode(), t)
+	equals(mode, info2.Mode(), t)
+}
+
+func TestMaintainOwner(t *testing.T) {
+	fakeFS := newFakeFS()
+	os_Chown = fakeFS.Chown
+	os_Stat = fakeFS.Stat
+	defer func() {
+		os_Chown = os.Chown
+		os_Stat = os.Stat
+	}()
+	currentTime = fakeTime
+	dir := makeTempDir("TestMaintainOwner", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+
+	f, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, 0644)
+	isNil(err, t)
+	f.Close()
+
+	l := &Logger{
+		Filename:   filename,
+		MaxBackups: 1,
+		MaxSize:    100, // megabytes
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	newFakeTime()
+
+	err = l.Rotate()
+	isNil(err, t)
+
+	equals(555, fakeFS.files[filename].uid, t)
+	equals(666, fakeFS.files[filename].gid, t)
+}
+
+func TestCompressMaintainMode(t *testing.T) {
+	currentTime = fakeTime
+
+	dir := makeTempDir("TestCompressMaintainMode", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+
+	mode := os.FileMode(0600)
+	f, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, mode)
+	isNil(err, t)
+	f.Close()
+
+	l := &Logger{
+		Compress: true,
+		Filename:   filename,
+		MaxBackups: 1,
+		MaxSize:    100, // megabytes
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	newFakeTime()
+
+	err = l.Rotate()
+	isNil(err, t)
+
+	// we need to wait a little bit since the files get compressed on a different
+	// goroutine.
+	<-time.After(10 * time.Millisecond)
+
+	// a compressed version of the log file should now exist with the correct
+	// mode.
+	filename2 := backupFile(dir)
+	info, err := os.Stat(filename)
+	isNil(err, t)
+	info2, err := os.Stat(filename2+compressSuffix)
+	isNil(err, t)
+	equals(mode, info.Mode(), t)
+	equals(mode, info2.Mode(), t)
+}
+
+func TestCompressMaintainOwner(t *testing.T) {
+	fakeFS := newFakeFS()
+	os_Chown = fakeFS.Chown
+	os_Stat = fakeFS.Stat
+	defer func() {
+		os_Chown = os.Chown
+		os_Stat = os.Stat
+	}()
+	currentTime = fakeTime
+	dir := makeTempDir("TestCompressMaintainOwner", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+
+	f, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, 0644)
+	isNil(err, t)
+	f.Close()
+
+	l := &Logger{
+		Compress:   true,
+		Filename:   filename,
+		MaxBackups: 1,
+		MaxSize:    100, // megabytes
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	newFakeTime()
+
+	err = l.Rotate()
+	isNil(err, t)
+
+	// we need to wait a little bit since the files get compressed on a different
+	// goroutine.
+	<-time.After(10 * time.Millisecond)
+
+	// a compressed version of the log file should now exist with the correct
+	// owner.
+	filename2 := backupFile(dir)
+	equals(555, fakeFS.files[filename2+compressSuffix].uid, t)
+	equals(666, fakeFS.files[filename2+compressSuffix].gid, t)
+}
+
+type fakeFile struct {
+	uid int
+	gid int
+}
+
+type fakeFS struct {
+	files map[string]fakeFile
+}
+
+func newFakeFS() *fakeFS {
+	return &fakeFS{files: make(map[string]fakeFile)}
+}
+
+func (fs *fakeFS) Chown(name string, uid, gid int) error {
+	fs.files[name] = fakeFile{uid: uid, gid: gid}
+	return nil
+}
+
+func (fs *fakeFS) Stat(name string) (os.FileInfo, error) {
+	info, err := os.Stat(name)
+	if err != nil {
+		return nil, err
+	}
+	stat := info.Sys().(*syscall.Stat_t)
+	stat.Uid = 555
+	stat.Gid = 666
+	return info, nil
+}

+ 562 - 0
pkg/lumberjack/lumberjack.go

@@ -0,0 +1,562 @@
+// Package lumberjack provides a rolling logger.
+//
+// Note that this is v2.0 of lumberjack, and should be imported using gopkg.in
+// thusly:
+//
+//   import "gopkg.in/natefinch/lumberjack.v2"
+//
+// The package name remains simply lumberjack, and the code resides at
+// https://github.com/natefinch/lumberjack under the v2.0 branch.
+//
+// Lumberjack is intended to be one part of a logging infrastructure.
+// It is not an all-in-one solution, but instead is a pluggable
+// component at the bottom of the logging stack that simply controls the files
+// to which logs are written.
+//
+// Lumberjack plays well with any logging package that can write to an
+// io.Writer, including the standard library's log package.
+//
+// Lumberjack assumes that only one process is writing to the output files.
+// Using the same lumberjack configuration from multiple processes on the same
+// machine will result in improper behavior.
+package lumberjack
+
+import (
+	"compress/gzip"
+	"errors"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+	"sync"
+	"time"
+)
+
+const (
+	backupTimeFormat = "2006-01-02T15-04-05.000"
+	compressSuffix   = ".gz"
+	defaultMaxSize   = 100
+)
+
+// ensure we always implement io.WriteCloser
+var _ io.WriteCloser = (*Logger)(nil)
+
+// Logger is an io.WriteCloser that writes to the specified filename.
+//
+// Logger opens or creates the logfile on first Write.  If the file exists and
+// is less than MaxSize megabytes, lumberjack will open and append to that file.
+// If the file exists and its size is >= MaxSize megabytes, the file is renamed
+// by putting the current time in a timestamp in the name immediately before the
+// file's extension (or the end of the filename if there's no extension). A new
+// log file is then created using original filename.
+//
+// Whenever a write would cause the current log file exceed MaxSize megabytes,
+// the current file is closed, renamed, and a new log file created with the
+// original name. Thus, the filename you give Logger is always the "current" log
+// file.
+//
+// Backups use the log file name given to Logger, in the form
+// `name-timestamp.ext` where name is the filename without the extension,
+// timestamp is the time at which the log was rotated formatted with the
+// time.Time format of `2006-01-02T15-04-05.000` and the extension is the
+// original extension.  For example, if your Logger.Filename is
+// `/var/log/foo/server.log`, a backup created at 6:30pm on Nov 11 2016 would
+// use the filename `/var/log/foo/server-2016-11-04T18-30-00.000.log`
+//
+// Cleaning Up Old Log Files
+//
+// Whenever a new logfile gets created, old log files may be deleted.  The most
+// recent files according to the encoded timestamp will be retained, up to a
+// number equal to MaxBackups (or all of them if MaxBackups is 0).  Any files
+// with an encoded timestamp older than MaxAge days are deleted, regardless of
+// MaxBackups.  Note that the time encoded in the timestamp is the rotation
+// time, which may differ from the last time that file was written to.
+//
+// If MaxBackups and MaxAge are both 0, no old log files will be deleted.
+type Logger struct {
+	// Filename is the file to write logs to.  Backup log files will be retained
+	// in the same directory.  It uses <processname>-lumberjack.log in
+	// os.TempDir() if empty.
+	Filename string `json:"filename" yaml:"filename"`
+
+	// MaxSize is the maximum size in megabytes of the log file before it gets
+	// rotated. It defaults to 100 megabytes.
+	MaxSize int `json:"maxsize" yaml:"maxsize"`
+
+	// MaxAge is the maximum number of days to retain old log files based on the
+	// timestamp encoded in their filename.  Note that a day is defined as 24
+	// hours and may not exactly correspond to calendar days due to daylight
+	// savings, leap seconds, etc. The default is not to remove old log files
+	// based on age.
+	MaxAge int `json:"maxage" yaml:"maxage"`
+
+	// MaxBackups is the maximum number of old log files to retain.  The default
+	// is to retain all old log files (though MaxAge may still cause them to get
+	// deleted.)
+	MaxBackups int `json:"maxbackups" yaml:"maxbackups"`
+
+	// LocalTime determines if the time used for formatting the timestamps in
+	// backup files is the computer's local time.  The default is to use UTC
+	// time.
+	LocalTime bool `json:"localtime" yaml:"localtime"`
+
+	// Compress determines if the rotated log files should be compressed
+	// using gzip. The default is not to perform compression.
+	Compress bool `json:"compress" yaml:"compress"`
+
+	size int64
+	file *os.File
+	mu   sync.Mutex
+
+	millCh    chan bool
+	startMill sync.Once
+}
+
+var (
+	// currentTime exists so it can be mocked out by tests.
+	currentTime = time.Now
+
+	// os_Stat exists so it can be mocked out by tests.
+	os_Stat = os.Stat
+
+	// megabyte is the conversion factor between MaxSize and bytes.  It is a
+	// variable so tests can mock it out and not need to write megabytes of data
+	// to disk.
+	megabyte = 1024 * 1024
+)
+
+// Write implements io.Writer.  If a write would cause the log file to be larger
+// than MaxSize, the file is closed, renamed to include a timestamp of the
+// current time, and a new log file is created using the original log file name.
+// If the length of the write is greater than MaxSize, an error is returned.
+func (l *Logger) Write(p []byte) (n int, err error) {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+
+	writeLen := int64(len(p))
+	if writeLen > l.max() {
+		return 0, fmt.Errorf(
+			"write length %d exceeds maximum file size %d", writeLen, l.max(),
+		)
+	}
+
+	if l.file == nil {
+		if err = l.openExistingOrNew(len(p)); err != nil {
+			return 0, err
+		}
+	}
+
+	if l.size+writeLen > l.max() {
+		if err := l.rotate(); err != nil {
+			return 0, err
+		}
+	}
+
+	n, err = l.file.Write(p)
+	l.size += int64(n)
+
+	return n, err
+}
+
+// Close implements io.Closer, and closes the current logfile.
+func (l *Logger) Close() error {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	return l.close()
+}
+
+// close closes the file if it is open.
+func (l *Logger) close() error {
+	if l.file == nil {
+		return nil
+	}
+	err := l.file.Close()
+	l.file = nil
+	return err
+}
+
+// Rotate causes Logger to close the existing log file and immediately create a
+// new one.  This is a helper function for applications that want to initiate
+// rotations outside of the normal rotation rules, such as in response to
+// SIGHUP.  After rotating, this initiates compression and removal of old log
+// files according to the configuration.
+func (l *Logger) Rotate() error {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	return l.rotate()
+}
+
+// rotate closes the current file, moves it aside with a timestamp in the name,
+// (if it exists), opens a new file with the original filename, and then runs
+// post-rotation processing and removal.
+func (l *Logger) rotate() error {
+	if err := l.close(); err != nil {
+		return err
+	}
+	if err := l.openNew(); err != nil {
+		return err
+	}
+	l.mill()
+	return nil
+}
+
+// openNew opens a new log file for writing, moving any old log file out of the
+// way.  This methods assumes the file has already been closed.
+func (l *Logger) openNew() error {
+	err := os.MkdirAll(l.dir(), 0744)
+	if err != nil {
+		return fmt.Errorf("can't make directories for new logfile: %s", err)
+	}
+
+	name := l.filename()
+	mode := os.FileMode(0644)
+	info, err := os_Stat(name)
+	if err == nil {
+		// Copy the mode off the old logfile.
+		mode = info.Mode()
+		// move the existing file
+		newname := backupName(name, l.LocalTime)
+		if err := os.Rename(name, newname); err != nil {
+			return fmt.Errorf("can't rename log file: %s", err)
+		}
+
+		// this is a no-op anywhere but linux
+		if err := chown(name, info); err != nil {
+			return err
+		}
+	}
+
+	// we use truncate here because this should only get called when we've moved
+	// the file ourselves. if someone else creates the file in the meantime,
+	// just wipe out the contents.
+	f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode)
+	if err != nil {
+		return fmt.Errorf("can't open new logfile: %s", err)
+	}
+	l.file = f
+	l.size = 0
+	return nil
+}
+
+// backupName creates a new filename from the given name, inserting a timestamp
+// between the filename and the extension, using the local time if requested
+// (otherwise UTC).
+func backupName(name string, local bool) string {
+	dir := filepath.Dir(name)
+	filename := filepath.Base(name)
+	ext := filepath.Ext(filename)
+	prefix := filename[:len(filename)-len(ext)]
+	t := currentTime()
+	if !local {
+		t = t.UTC()
+	}
+
+	timestamp := t.Format(backupTimeFormat)
+	return filepath.Join(dir, fmt.Sprintf("%s-%s%s", prefix, timestamp, ext))
+}
+
+// openExistingOrNew opens the logfile if it exists and if the current write
+// would not put it over MaxSize.  If there is no such file or the write would
+// put it over the MaxSize, a new file is created.
+func (l *Logger) openExistingOrNew(writeLen int) error {
+	l.mill()
+
+	filename := l.filename()
+	info, err := os_Stat(filename)
+	if os.IsNotExist(err) {
+		return l.openNew()
+	}
+	if err != nil {
+		return fmt.Errorf("error getting log file info: %s", err)
+	}
+
+	if info.Size()+int64(writeLen) >= l.max() {
+		return l.rotate()
+	}
+
+	file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0644)
+	if err != nil {
+		// if we fail to open the old log file for some reason, just ignore
+		// it and open a new log file.
+		return l.openNew()
+	}
+	l.file = file
+	l.size = info.Size()
+	return nil
+}
+
+// genFilename generates the name of the logfile from the current time.
+func (l *Logger) filename() string {
+	if l.Filename != "" {
+		return l.Filename
+	}
+	name := filepath.Base(os.Args[0]) + "-lumberjack.log"
+	return filepath.Join(os.TempDir(), name)
+}
+
+func isToday(date time.Time) bool {
+	now := time.Now()
+	return now.Year() == date.Year() && now.Month() == date.Month() && now.Day() == date.Day()
+}
+
+// millRunOnce performs compression and removal of stale log files.
+// Log files are compressed if enabled via configuration and old log
+// files are removed, keeping at most l.MaxBackups files, as long as
+// none of them are older than MaxAge.
+func (l *Logger) millRunOnce() error {
+	if l.MaxBackups == 0 && l.MaxAge == 0 && !l.Compress {
+		return nil
+	}
+
+	files, err := l.oldLogFiles()
+	if err != nil {
+		return err
+	}
+
+	var compress, remove, todyFiles []logInfo
+
+	for _, f := range files {
+		if isToday(f.timestamp) {
+			todyFiles = append(todyFiles, f)
+		}
+	}
+
+	if l.MaxBackups > 0 && l.MaxBackups < len(todyFiles) {
+		preserved := make(map[string]bool)
+		var remaining []logInfo
+		for _, f := range files {
+			// Only count the uncompressed log file or the
+			// compressed log file, not both.
+			fn := f.Name()
+			if strings.HasSuffix(fn, compressSuffix) {
+				fn = fn[:len(fn)-len(compressSuffix)]
+			}
+			preserved[fn] = true
+
+			if len(preserved) > l.MaxBackups && isToday(f.timestamp) {
+				remove = append(remove, f)
+			} else {
+				remaining = append(remaining, f)
+			}
+		}
+		files = remaining
+	}
+	if l.MaxAge > 0 {
+		diff := time.Duration(int64(24*time.Hour) * int64(l.MaxAge))
+		cutoff := currentTime().Add(-1 * diff)
+
+		var remaining []logInfo
+		for _, f := range files {
+			if l.LocalTime {
+				f.timestamp = f.timestamp.Local()
+			} else {
+				f.timestamp = f.timestamp.UTC()
+			}
+			if f.timestamp.Before(cutoff) {
+				remove = append(remove, f)
+			} else {
+				remaining = append(remaining, f)
+			}
+		}
+		files = remaining
+	}
+
+	if l.Compress {
+		for _, f := range files {
+			if !strings.HasSuffix(f.Name(), compressSuffix) {
+				compress = append(compress, f)
+			}
+		}
+	}
+
+	for _, f := range remove {
+		errRemove := os.Remove(filepath.Join(l.dir(), f.Name()))
+		if err == nil && errRemove != nil {
+			err = errRemove
+		}
+	}
+	for _, f := range compress {
+		fn := filepath.Join(l.dir(), f.Name())
+		errCompress := compressLogFile(fn, fn+compressSuffix)
+		if err == nil && errCompress != nil {
+			err = errCompress
+		}
+	}
+
+	return err
+}
+
+// millRun runs in a goroutine to manage post-rotation compression and removal
+// of old log files.
+func (l *Logger) millRun() {
+	for _ = range l.millCh {
+		// what am I going to do, log this?
+		_ = l.millRunOnce()
+	}
+}
+
+// mill performs post-rotation compression and removal of stale log files,
+// starting the mill goroutine if necessary.
+func (l *Logger) mill() {
+	l.startMill.Do(func() {
+		l.millCh = make(chan bool, 1)
+		go l.millRun()
+	})
+	select {
+	case l.millCh <- true:
+	default:
+	}
+}
+
+// oldLogFiles returns the list of backup log files stored in the same
+// directory as the current log file, sorted by ModTime
+func (l *Logger) oldLogFiles() ([]logInfo, error) {
+	files, err := ioutil.ReadDir(l.dir())
+	if err != nil {
+		return nil, fmt.Errorf("can't read log file directory: %s", err)
+	}
+	logFiles := []logInfo{}
+
+	prefix, ext := l.prefixAndExt()
+
+	for _, f := range files {
+		if f.IsDir() {
+			continue
+		}
+		if t, err := l.timeFromName(f.Name(), prefix, ext); err == nil {
+			logFiles = append(logFiles, logInfo{t, f})
+			continue
+		}
+		if t, err := l.timeFromName(f.Name(), prefix, ext+compressSuffix); err == nil {
+			logFiles = append(logFiles, logInfo{t, f})
+			continue
+		}
+		// error parsing means that the suffix at the end was not generated
+		// by lumberjack, and therefore it's not a backup file.
+	}
+
+	sort.Sort(byFormatTime(logFiles))
+
+	return logFiles, nil
+}
+
+// timeFromName extracts the formatted time from the filename by stripping off
+// the filename's prefix and extension. This prevents someone's filename from
+// confusing time.parse.
+func (l *Logger) timeFromName(filename, prefix, ext string) (time.Time, error) {
+	if !strings.HasPrefix(filename, prefix) {
+		return time.Time{}, errors.New("mismatched prefix")
+	}
+	if !strings.HasSuffix(filename, ext) {
+		return time.Time{}, errors.New("mismatched extension")
+	}
+	ts := filename[len(prefix) : len(filename)-len(ext)]
+	if l.LocalTime {
+		loc, _ := time.LoadLocation("Local")
+		return time.ParseInLocation(backupTimeFormat, ts, loc)
+	} else {
+		return time.Parse(backupTimeFormat, ts)
+	}
+}
+
+// max returns the maximum size in bytes of log files before rolling.
+func (l *Logger) max() int64 {
+	if l.MaxSize == 0 {
+		return int64(defaultMaxSize * megabyte)
+	}
+	return int64(l.MaxSize) * int64(megabyte)
+}
+
+// dir returns the directory for the current filename.
+func (l *Logger) dir() string {
+	return filepath.Dir(l.filename())
+}
+
+// prefixAndExt returns the filename part and extension part from the Logger's
+// filename.
+func (l *Logger) prefixAndExt() (prefix, ext string) {
+	filename := filepath.Base(l.filename())
+	ext = filepath.Ext(filename)
+	prefix = filename[:len(filename)-len(ext)] + "-"
+	return prefix, ext
+}
+
+// compressLogFile compresses the given log file, removing the
+// uncompressed log file if successful.
+func compressLogFile(src, dst string) (err error) {
+	f, err := os.Open(src)
+	if err != nil {
+		return fmt.Errorf("failed to open log file: %v", err)
+	}
+	defer f.Close()
+
+	fi, err := os_Stat(src)
+	if err != nil {
+		return fmt.Errorf("failed to stat log file: %v", err)
+	}
+
+	if err := chown(dst, fi); err != nil {
+		return fmt.Errorf("failed to chown compressed log file: %v", err)
+	}
+
+	// If this file already exists, we presume it was created by
+	// a previous attempt to compress the log file.
+	gzf, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode())
+	if err != nil {
+		return fmt.Errorf("failed to open compressed log file: %v", err)
+	}
+	defer gzf.Close()
+
+	gz := gzip.NewWriter(gzf)
+
+	defer func() {
+		if err != nil {
+			os.Remove(dst)
+			err = fmt.Errorf("failed to compress log file: %v", err)
+		}
+	}()
+
+	if _, err := io.Copy(gz, f); err != nil {
+		return err
+	}
+	if err := gz.Close(); err != nil {
+		return err
+	}
+	if err := gzf.Close(); err != nil {
+		return err
+	}
+
+	if err := f.Close(); err != nil {
+		return err
+	}
+	if err := os.Remove(src); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// logInfo is a convenience struct to return the filename and its embedded
+// timestamp.
+type logInfo struct {
+	timestamp time.Time
+	os.FileInfo
+}
+
+// byFormatTime sorts by newest time formatted in the name.
+type byFormatTime []logInfo
+
+func (b byFormatTime) Less(i, j int) bool {
+	return b[i].timestamp.After(b[j].timestamp)
+}
+
+func (b byFormatTime) Swap(i, j int) {
+	b[i], b[j] = b[j], b[i]
+}
+
+func (b byFormatTime) Len() int {
+	return len(b)
+}

+ 816 - 0
pkg/lumberjack/lumberjack_test.go

@@ -0,0 +1,816 @@
+package lumberjack
+
+import (
+	"bytes"
+	"compress/gzip"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
+	"time"
+
+	"github.com/BurntSushi/toml"
+	"gopkg.in/yaml.v2"
+)
+
+// !!!NOTE!!!
+//
+// Running these tests in parallel will almost certainly cause sporadic (or even
+// regular) failures, because they're all messing with the same global variable
+// that controls the logic's mocked time.Now.  So... don't do that.
+
+// Since all the tests uses the time to determine filenames etc, we need to
+// control the wall clock as much as possible, which means having a wall clock
+// that doesn't change unless we want it to.
+var fakeCurrentTime = time.Now()
+
+func fakeTime() time.Time {
+	return fakeCurrentTime
+}
+
+func TestNewFile(t *testing.T) {
+	currentTime = fakeTime
+
+	dir := makeTempDir("TestNewFile", t)
+	defer os.RemoveAll(dir)
+	l := &Logger{
+		Filename: logFile(dir),
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+	existsWithContent(logFile(dir), b, t)
+	fileCount(dir, 1, t)
+}
+
+func TestOpenExisting(t *testing.T) {
+	currentTime = fakeTime
+	dir := makeTempDir("TestOpenExisting", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+	data := []byte("foo!")
+	err := ioutil.WriteFile(filename, data, 0644)
+	isNil(err, t)
+	existsWithContent(filename, data, t)
+
+	l := &Logger{
+		Filename: filename,
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	// make sure the file got appended
+	existsWithContent(filename, append(data, b...), t)
+
+	// make sure no other files were created
+	fileCount(dir, 1, t)
+}
+
+func TestWriteTooLong(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+	dir := makeTempDir("TestWriteTooLong", t)
+	defer os.RemoveAll(dir)
+	l := &Logger{
+		Filename: logFile(dir),
+		MaxSize:  5,
+	}
+	defer l.Close()
+	b := []byte("booooooooooooooo!")
+	n, err := l.Write(b)
+	notNil(err, t)
+	equals(0, n, t)
+	equals(err.Error(),
+		fmt.Sprintf("write length %d exceeds maximum file size %d", len(b), l.MaxSize), t)
+	_, err = os.Stat(logFile(dir))
+	assert(os.IsNotExist(err), t, "File exists, but should not have been created")
+}
+
+func TestMakeLogDir(t *testing.T) {
+	currentTime = fakeTime
+	dir := time.Now().Format("TestMakeLogDir" + backupTimeFormat)
+	dir = filepath.Join(os.TempDir(), dir)
+	defer os.RemoveAll(dir)
+	filename := logFile(dir)
+	l := &Logger{
+		Filename: filename,
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+	existsWithContent(logFile(dir), b, t)
+	fileCount(dir, 1, t)
+}
+
+func TestDefaultFilename(t *testing.T) {
+	currentTime = fakeTime
+	dir := os.TempDir()
+	filename := filepath.Join(dir, filepath.Base(os.Args[0])+"-lumberjack.log")
+	defer os.Remove(filename)
+	l := &Logger{}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+
+	isNil(err, t)
+	equals(len(b), n, t)
+	existsWithContent(filename, b, t)
+}
+
+func TestAutoRotate(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+
+	dir := makeTempDir("TestAutoRotate", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+	l := &Logger{
+		Filename: filename,
+		MaxSize:  10,
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	existsWithContent(filename, b, t)
+	fileCount(dir, 1, t)
+
+	newFakeTime()
+
+	b2 := []byte("foooooo!")
+	n, err = l.Write(b2)
+	isNil(err, t)
+	equals(len(b2), n, t)
+
+	// the old logfile should be moved aside and the main logfile should have
+	// only the last write in it.
+	existsWithContent(filename, b2, t)
+
+	// the backup file will use the current fake time and have the old contents.
+	existsWithContent(backupFile(dir), b, t)
+
+	fileCount(dir, 2, t)
+}
+
+func TestFirstWriteRotate(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+	dir := makeTempDir("TestFirstWriteRotate", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+	l := &Logger{
+		Filename: filename,
+		MaxSize:  10,
+	}
+	defer l.Close()
+
+	start := []byte("boooooo!")
+	err := ioutil.WriteFile(filename, start, 0600)
+	isNil(err, t)
+
+	newFakeTime()
+
+	// this would make us rotate
+	b := []byte("fooo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	existsWithContent(filename, b, t)
+	existsWithContent(backupFile(dir), start, t)
+
+	fileCount(dir, 2, t)
+}
+
+func TestMaxBackups(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+	dir := makeTempDir("TestMaxBackups", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+	l := &Logger{
+		Filename:   filename,
+		MaxSize:    10,
+		MaxBackups: 1,
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	existsWithContent(filename, b, t)
+	fileCount(dir, 1, t)
+
+	newFakeTime()
+
+	// this will put us over the max
+	b2 := []byte("foooooo!")
+	n, err = l.Write(b2)
+	isNil(err, t)
+	equals(len(b2), n, t)
+
+	// this will use the new fake time
+	secondFilename := backupFile(dir)
+	existsWithContent(secondFilename, b, t)
+
+	// make sure the old file still exists with the same content.
+	existsWithContent(filename, b2, t)
+
+	fileCount(dir, 2, t)
+
+	newFakeTime()
+
+	// this will make us rotate again
+	b3 := []byte("baaaaaar!")
+	n, err = l.Write(b3)
+	isNil(err, t)
+	equals(len(b3), n, t)
+
+	// this will use the new fake time
+	thirdFilename := backupFile(dir)
+	existsWithContent(thirdFilename, b2, t)
+
+	existsWithContent(filename, b3, t)
+
+	// we need to wait a little bit since the files get deleted on a different
+	// goroutine.
+	<-time.After(time.Millisecond * 10)
+
+	// should only have two files in the dir still
+	fileCount(dir, 2, t)
+
+	// second file name should still exist
+	existsWithContent(thirdFilename, b2, t)
+
+	// should have deleted the first backup
+	notExist(secondFilename, t)
+
+	// now test that we don't delete directories or non-logfile files
+
+	newFakeTime()
+
+	// create a file that is close to but different from the logfile name.
+	// It shouldn't get caught by our deletion filters.
+	notlogfile := logFile(dir) + ".foo"
+	err = ioutil.WriteFile(notlogfile, []byte("data"), 0644)
+	isNil(err, t)
+
+	// Make a directory that exactly matches our log file filters... it still
+	// shouldn't get caught by the deletion filter since it's a directory.
+	notlogfiledir := backupFile(dir)
+	err = os.Mkdir(notlogfiledir, 0700)
+	isNil(err, t)
+
+	newFakeTime()
+
+	// this will use the new fake time
+	fourthFilename := backupFile(dir)
+
+	// Create a log file that is/was being compressed - this should
+	// not be counted since both the compressed and the uncompressed
+	// log files still exist.
+	compLogFile := fourthFilename + compressSuffix
+	err = ioutil.WriteFile(compLogFile, []byte("compress"), 0644)
+	isNil(err, t)
+
+	// this will make us rotate again
+	b4 := []byte("baaaaaaz!")
+	n, err = l.Write(b4)
+	isNil(err, t)
+	equals(len(b4), n, t)
+
+	existsWithContent(fourthFilename, b3, t)
+	existsWithContent(fourthFilename+compressSuffix, []byte("compress"), t)
+
+	// we need to wait a little bit since the files get deleted on a different
+	// goroutine.
+	<-time.After(time.Millisecond * 10)
+
+	// We should have four things in the directory now - the 2 log files, the
+	// not log file, and the directory
+	fileCount(dir, 5, t)
+
+	// third file name should still exist
+	existsWithContent(filename, b4, t)
+
+	existsWithContent(fourthFilename, b3, t)
+
+	// should have deleted the first filename
+	notExist(thirdFilename, t)
+
+	// the not-a-logfile should still exist
+	exists(notlogfile, t)
+
+	// the directory
+	exists(notlogfiledir, t)
+}
+
+func TestCleanupExistingBackups(t *testing.T) {
+	// test that if we start with more backup files than we're supposed to have
+	// in total, that extra ones get cleaned up when we rotate.
+
+	currentTime = fakeTime
+	megabyte = 1
+
+	dir := makeTempDir("TestCleanupExistingBackups", t)
+	defer os.RemoveAll(dir)
+
+	// make 3 backup files
+
+	data := []byte("data")
+	backup := backupFile(dir)
+	err := ioutil.WriteFile(backup, data, 0644)
+	isNil(err, t)
+
+	newFakeTime()
+
+	backup = backupFile(dir)
+	err = ioutil.WriteFile(backup+compressSuffix, data, 0644)
+	isNil(err, t)
+
+	newFakeTime()
+
+	backup = backupFile(dir)
+	err = ioutil.WriteFile(backup, data, 0644)
+	isNil(err, t)
+
+	// now create a primary log file with some data
+	filename := logFile(dir)
+	err = ioutil.WriteFile(filename, data, 0644)
+	isNil(err, t)
+
+	l := &Logger{
+		Filename:   filename,
+		MaxSize:    10,
+		MaxBackups: 1,
+	}
+	defer l.Close()
+
+	newFakeTime()
+
+	b2 := []byte("foooooo!")
+	n, err := l.Write(b2)
+	isNil(err, t)
+	equals(len(b2), n, t)
+
+	// we need to wait a little bit since the files get deleted on a different
+	// goroutine.
+	<-time.After(time.Millisecond * 10)
+
+	// now we should only have 2 files left - the primary and one backup
+	fileCount(dir, 2, t)
+}
+
+func TestMaxAge(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+
+	dir := makeTempDir("TestMaxAge", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+	l := &Logger{
+		Filename: filename,
+		MaxSize:  10,
+		MaxAge:   1,
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	existsWithContent(filename, b, t)
+	fileCount(dir, 1, t)
+
+	// two days later
+	newFakeTime()
+
+	b2 := []byte("foooooo!")
+	n, err = l.Write(b2)
+	isNil(err, t)
+	equals(len(b2), n, t)
+	existsWithContent(backupFile(dir), b, t)
+
+	// we need to wait a little bit since the files get deleted on a different
+	// goroutine.
+	<-time.After(10 * time.Millisecond)
+
+	// We should still have 2 log files, since the most recent backup was just
+	// created.
+	fileCount(dir, 2, t)
+
+	existsWithContent(filename, b2, t)
+
+	// we should have deleted the old file due to being too old
+	existsWithContent(backupFile(dir), b, t)
+
+	// two days later
+	newFakeTime()
+
+	b3 := []byte("baaaaar!")
+	n, err = l.Write(b3)
+	isNil(err, t)
+	equals(len(b3), n, t)
+	existsWithContent(backupFile(dir), b2, t)
+
+	// we need to wait a little bit since the files get deleted on a different
+	// goroutine.
+	<-time.After(10 * time.Millisecond)
+
+	// We should have 2 log files - the main log file, and the most recent
+	// backup.  The earlier backup is past the cutoff and should be gone.
+	fileCount(dir, 2, t)
+
+	existsWithContent(filename, b3, t)
+
+	// we should have deleted the old file due to being too old
+	existsWithContent(backupFile(dir), b2, t)
+}
+
+func TestOldLogFiles(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+
+	dir := makeTempDir("TestOldLogFiles", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+	data := []byte("data")
+	err := ioutil.WriteFile(filename, data, 07)
+	isNil(err, t)
+
+	// This gives us a time with the same precision as the time we get from the
+	// timestamp in the name.
+	t1, err := time.Parse(backupTimeFormat, fakeTime().UTC().Format(backupTimeFormat))
+	isNil(err, t)
+
+	backup := backupFile(dir)
+	err = ioutil.WriteFile(backup, data, 07)
+	isNil(err, t)
+
+	newFakeTime()
+
+	t2, err := time.Parse(backupTimeFormat, fakeTime().UTC().Format(backupTimeFormat))
+	isNil(err, t)
+
+	backup2 := backupFile(dir)
+	err = ioutil.WriteFile(backup2, data, 07)
+	isNil(err, t)
+
+	l := &Logger{Filename: filename}
+	files, err := l.oldLogFiles()
+	isNil(err, t)
+	equals(2, len(files), t)
+
+	// should be sorted by newest file first, which would be t2
+	equals(t2, files[0].timestamp, t)
+	equals(t1, files[1].timestamp, t)
+}
+
+func TestTimeFromName(t *testing.T) {
+	l := &Logger{Filename: "/var/log/myfoo/foo.log"}
+	prefix, ext := l.prefixAndExt()
+
+	tests := []struct {
+		filename string
+		want     time.Time
+		wantErr  bool
+	}{
+		{"foo-2014-05-04T14-44-33.555.log", time.Date(2014, 5, 4, 14, 44, 33, 555000000, time.UTC), false},
+		{"foo-2014-05-04T14-44-33.555", time.Time{}, true},
+		{"2014-05-04T14-44-33.555.log", time.Time{}, true},
+		{"foo.log", time.Time{}, true},
+	}
+
+	for _, test := range tests {
+		got, err := l.timeFromName(test.filename, prefix, ext)
+		equals(got, test.want, t)
+		equals(err != nil, test.wantErr, t)
+	}
+}
+
+func TestLocalTime(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+
+	dir := makeTempDir("TestLocalTime", t)
+	defer os.RemoveAll(dir)
+
+	l := &Logger{
+		Filename:  logFile(dir),
+		MaxSize:   10,
+		LocalTime: true,
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	b2 := []byte("fooooooo!")
+	n2, err := l.Write(b2)
+	isNil(err, t)
+	equals(len(b2), n2, t)
+
+	existsWithContent(logFile(dir), b2, t)
+	existsWithContent(backupFileLocal(dir), b, t)
+}
+
+func TestRotate(t *testing.T) {
+	currentTime = fakeTime
+	dir := makeTempDir("TestRotate", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+
+	l := &Logger{
+		Filename:   filename,
+		MaxBackups: 1,
+		MaxSize:    100, // megabytes
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	existsWithContent(filename, b, t)
+	fileCount(dir, 1, t)
+
+	newFakeTime()
+
+	err = l.Rotate()
+	isNil(err, t)
+
+	// we need to wait a little bit since the files get deleted on a different
+	// goroutine.
+	<-time.After(10 * time.Millisecond)
+
+	filename2 := backupFile(dir)
+	existsWithContent(filename2, b, t)
+	existsWithContent(filename, []byte{}, t)
+	fileCount(dir, 2, t)
+	newFakeTime()
+
+	err = l.Rotate()
+	isNil(err, t)
+
+	// we need to wait a little bit since the files get deleted on a different
+	// goroutine.
+	<-time.After(10 * time.Millisecond)
+
+	filename3 := backupFile(dir)
+	existsWithContent(filename3, []byte{}, t)
+	existsWithContent(filename, []byte{}, t)
+	fileCount(dir, 2, t)
+
+	b2 := []byte("foooooo!")
+	n, err = l.Write(b2)
+	isNil(err, t)
+	equals(len(b2), n, t)
+
+	// this will use the new fake time
+	existsWithContent(filename, b2, t)
+}
+
+func TestCompressOnRotate(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+
+	dir := makeTempDir("TestCompressOnRotate", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+	l := &Logger{
+		Compress: true,
+		Filename: filename,
+		MaxSize:  10,
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	existsWithContent(filename, b, t)
+	fileCount(dir, 1, t)
+
+	newFakeTime()
+
+	err = l.Rotate()
+	isNil(err, t)
+
+	// the old logfile should be moved aside and the main logfile should have
+	// nothing in it.
+	existsWithContent(filename, []byte{}, t)
+
+	// we need to wait a little bit since the files get compressed on a different
+	// goroutine.
+	<-time.After(300 * time.Millisecond)
+
+	// a compressed version of the log file should now exist and the original
+	// should have been removed.
+	bc := new(bytes.Buffer)
+	gz := gzip.NewWriter(bc)
+	_, err = gz.Write(b)
+	isNil(err, t)
+	err = gz.Close()
+	isNil(err, t)
+	existsWithContent(backupFile(dir)+compressSuffix, bc.Bytes(), t)
+	notExist(backupFile(dir), t)
+
+	fileCount(dir, 2, t)
+}
+
+func TestCompressOnResume(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+
+	dir := makeTempDir("TestCompressOnResume", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+	l := &Logger{
+		Compress: true,
+		Filename: filename,
+		MaxSize:  10,
+	}
+	defer l.Close()
+
+	// Create a backup file and empty "compressed" file.
+	filename2 := backupFile(dir)
+	b := []byte("foo!")
+	err := ioutil.WriteFile(filename2, b, 0644)
+	isNil(err, t)
+	err = ioutil.WriteFile(filename2+compressSuffix, []byte{}, 0644)
+	isNil(err, t)
+
+	newFakeTime()
+
+	b2 := []byte("boo!")
+	n, err := l.Write(b2)
+	isNil(err, t)
+	equals(len(b2), n, t)
+	existsWithContent(filename, b2, t)
+
+	// we need to wait a little bit since the files get compressed on a different
+	// goroutine.
+	<-time.After(300 * time.Millisecond)
+
+	// The write should have started the compression - a compressed version of
+	// the log file should now exist and the original should have been removed.
+	bc := new(bytes.Buffer)
+	gz := gzip.NewWriter(bc)
+	_, err = gz.Write(b)
+	isNil(err, t)
+	err = gz.Close()
+	isNil(err, t)
+	existsWithContent(filename2+compressSuffix, bc.Bytes(), t)
+	notExist(filename2, t)
+
+	fileCount(dir, 2, t)
+}
+
+func TestJson(t *testing.T) {
+	data := []byte(`
+{
+	"filename": "foo",
+	"maxsize": 5,
+	"maxage": 10,
+	"maxbackups": 3,
+	"localtime": true,
+	"compress": true
+}`[1:])
+
+	l := Logger{}
+	err := json.Unmarshal(data, &l)
+	isNil(err, t)
+	equals("foo", l.Filename, t)
+	equals(5, l.MaxSize, t)
+	equals(10, l.MaxAge, t)
+	equals(3, l.MaxBackups, t)
+	equals(true, l.LocalTime, t)
+	equals(true, l.Compress, t)
+}
+
+func TestYaml(t *testing.T) {
+	data := []byte(`
+filename: foo
+maxsize: 5
+maxage: 10
+maxbackups: 3
+localtime: true
+compress: true`[1:])
+
+	l := Logger{}
+	err := yaml.Unmarshal(data, &l)
+	isNil(err, t)
+	equals("foo", l.Filename, t)
+	equals(5, l.MaxSize, t)
+	equals(10, l.MaxAge, t)
+	equals(3, l.MaxBackups, t)
+	equals(true, l.LocalTime, t)
+	equals(true, l.Compress, t)
+}
+
+func TestToml(t *testing.T) {
+	data := `
+filename = "foo"
+maxsize = 5
+maxage = 10
+maxbackups = 3
+localtime = true
+compress = true`[1:]
+
+	l := Logger{}
+	md, err := toml.Decode(data, &l)
+	isNil(err, t)
+	equals("foo", l.Filename, t)
+	equals(5, l.MaxSize, t)
+	equals(10, l.MaxAge, t)
+	equals(3, l.MaxBackups, t)
+	equals(true, l.LocalTime, t)
+	equals(true, l.Compress, t)
+	equals(0, len(md.Undecoded()), t)
+}
+
+// makeTempDir creates a file with a semi-unique name in the OS temp directory.
+// It should be based on the name of the test, to keep parallel tests from
+// colliding, and must be cleaned up after the test is finished.
+func makeTempDir(name string, t testing.TB) string {
+	dir := time.Now().Format(name + backupTimeFormat)
+	dir = filepath.Join(os.TempDir(), dir)
+	isNilUp(os.Mkdir(dir, 0700), t, 1)
+	return dir
+}
+
+// existsWithContent checks that the given file exists and has the correct content.
+func existsWithContent(path string, content []byte, t testing.TB) {
+	info, err := os.Stat(path)
+	isNilUp(err, t, 1)
+	equalsUp(int64(len(content)), info.Size(), t, 1)
+
+	b, err := ioutil.ReadFile(path)
+	isNilUp(err, t, 1)
+	equalsUp(content, b, t, 1)
+}
+
+// logFile returns the log file name in the given directory for the current fake
+// time.
+func logFile(dir string) string {
+	return filepath.Join(dir, "foobar.log")
+}
+
+func backupFile(dir string) string {
+	return filepath.Join(dir, "foobar-"+fakeTime().UTC().Format(backupTimeFormat)+".log")
+}
+
+func backupFileLocal(dir string) string {
+	return filepath.Join(dir, "foobar-"+fakeTime().Format(backupTimeFormat)+".log")
+}
+
+// logFileLocal returns the log file name in the given directory for the current
+// fake time using the local timezone.
+func logFileLocal(dir string) string {
+	return filepath.Join(dir, fakeTime().Format(backupTimeFormat))
+}
+
+// fileCount checks that the number of files in the directory is exp.
+func fileCount(dir string, exp int, t testing.TB) {
+	files, err := ioutil.ReadDir(dir)
+	isNilUp(err, t, 1)
+	// Make sure no other files were created.
+	equalsUp(exp, len(files), t, 1)
+}
+
+// newFakeTime sets the fake "current time" to two days later.
+func newFakeTime() {
+	fakeCurrentTime = fakeCurrentTime.Add(time.Hour * 24 * 2)
+}
+
+func notExist(path string, t testing.TB) {
+	_, err := os.Stat(path)
+	assertUp(os.IsNotExist(err), t, 1, "expected to get os.IsNotExist, but instead got %v", err)
+}
+
+func exists(path string, t testing.TB) {
+	_, err := os.Stat(path)
+	assertUp(err == nil, t, 1, "expected file to exist, but got error from os.Stat: %v", err)
+}

+ 27 - 0
pkg/lumberjack/rotate_test.go

@@ -0,0 +1,27 @@
+// +build linux
+
+package lumberjack_test
+
+import (
+	"log"
+	"os"
+	"os/signal"
+	"syscall"
+
+	"gopkg.in/natefinch/lumberjack.v2"
+)
+
+// Example of how to rotate in response to SIGHUP.
+func ExampleLogger_Rotate() {
+	l := &lumberjack.Logger{}
+	log.SetOutput(l)
+	c := make(chan os.Signal, 1)
+	signal.Notify(c, syscall.SIGHUP)
+
+	go func() {
+		for {
+			<-c
+			l.Rotate()
+		}
+	}()
+}

+ 91 - 0
pkg/lumberjack/testing_test.go

@@ -0,0 +1,91 @@
+package lumberjack
+
+import (
+	"fmt"
+	"path/filepath"
+	"reflect"
+	"runtime"
+	"testing"
+)
+
+// assert will log the given message if condition is false.
+func assert(condition bool, t testing.TB, msg string, v ...interface{}) {
+	assertUp(condition, t, 1, msg, v...)
+}
+
+// assertUp is like assert, but used inside helper functions, to ensure that
+// the file and line number reported by failures corresponds to one or more
+// levels up the stack.
+func assertUp(condition bool, t testing.TB, caller int, msg string, v ...interface{}) {
+	if !condition {
+		_, file, line, _ := runtime.Caller(caller + 1)
+		v = append([]interface{}{filepath.Base(file), line}, v...)
+		fmt.Printf("%s:%d: "+msg+"\n", v...)
+		t.FailNow()
+	}
+}
+
+// equals tests that the two values are equal according to reflect.DeepEqual.
+func equals(exp, act interface{}, t testing.TB) {
+	equalsUp(exp, act, t, 1)
+}
+
+// equalsUp is like equals, but used inside helper functions, to ensure that the
+// file and line number reported by failures corresponds to one or more levels
+// up the stack.
+func equalsUp(exp, act interface{}, t testing.TB, caller int) {
+	if !reflect.DeepEqual(exp, act) {
+		_, file, line, _ := runtime.Caller(caller + 1)
+		fmt.Printf("%s:%d: exp: %v (%T), got: %v (%T)\n",
+			filepath.Base(file), line, exp, exp, act, act)
+		t.FailNow()
+	}
+}
+
+// isNil reports a failure if the given value is not nil.  Note that values
+// which cannot be nil will always fail this check.
+func isNil(obtained interface{}, t testing.TB) {
+	isNilUp(obtained, t, 1)
+}
+
+// isNilUp is like isNil, but used inside helper functions, to ensure that the
+// file and line number reported by failures corresponds to one or more levels
+// up the stack.
+func isNilUp(obtained interface{}, t testing.TB, caller int) {
+	if !_isNil(obtained) {
+		_, file, line, _ := runtime.Caller(caller + 1)
+		fmt.Printf("%s:%d: expected nil, got: %v\n", filepath.Base(file), line, obtained)
+		t.FailNow()
+	}
+}
+
+// notNil reports a failure if the given value is nil.
+func notNil(obtained interface{}, t testing.TB) {
+	notNilUp(obtained, t, 1)
+}
+
+// notNilUp is like notNil, but used inside helper functions, to ensure that the
+// file and line number reported by failures corresponds to one or more levels
+// up the stack.
+func notNilUp(obtained interface{}, t testing.TB, caller int) {
+	if _isNil(obtained) {
+		_, file, line, _ := runtime.Caller(caller + 1)
+		fmt.Printf("%s:%d: expected non-nil, got: %v\n", filepath.Base(file), line, obtained)
+		t.FailNow()
+	}
+}
+
+// _isNil is a helper function for isNil and notNil, and should not be used
+// directly.
+func _isNil(obtained interface{}) bool {
+	if obtained == nil {
+		return true
+	}
+
+	switch v := reflect.ValueOf(obtained); v.Kind() {
+	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
+		return v.IsNil()
+	}
+
+	return false
+}

+ 1 - 1
profiling/profiling.go

@@ -22,7 +22,7 @@ import (
 	"github.com/grafana/pyroscope/ebpf/symtab/elf"
 	"github.com/grafana/pyroscope/ebpf/symtab/elf"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/prometheus/model/labels"
 	"github.com/prometheus/prometheus/model/labels"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 const (
 const (

+ 1 - 1
prom/agent.go

@@ -16,7 +16,7 @@ import (
 	"github.com/prometheus/prometheus/storage/remote"
 	"github.com/prometheus/prometheus/storage/remote"
 	"github.com/prometheus/prometheus/tsdb"
 	"github.com/prometheus/prometheus/tsdb"
 	"github.com/prometheus/prometheus/tsdb/agent"
 	"github.com/prometheus/prometheus/tsdb/agent"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 )
 
 
 const (
 const (

+ 1 - 1
prom/wrappers.go

@@ -7,7 +7,7 @@ import (
 	"math"
 	"math"
 	"sync"
 	"sync"
 
 
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 
 
 	"github.com/prometheus/prometheus/config"
 	"github.com/prometheus/prometheus/config"
 	"github.com/prometheus/prometheus/model/exemplar"
 	"github.com/prometheus/prometheus/model/exemplar"

+ 2 - 1
tracing/apm_tracing.go

@@ -12,6 +12,7 @@ import (
 	semconv "go.opentelemetry.io/otel/semconv/v1.18.0"
 	semconv "go.opentelemetry.io/otel/semconv/v1.18.0"
 	"go.opentelemetry.io/otel/trace"
 	"go.opentelemetry.io/otel/trace"
 	"inet.af/netaddr"
 	"inet.af/netaddr"
+	"k8s.io/klog/v2"
 	"strconv"
 	"strconv"
 	"sync/atomic"
 	"sync/atomic"
 	"time"
 	"time"
@@ -120,7 +121,7 @@ func (t *Trace) endReadyEvent(needCount uint32) {
 }
 }
 
 
 func (t *Trace) AllEventReady(traceID uint64) bool {
 func (t *Trace) AllEventReady(traceID uint64) bool {
-	fmt.Printf("[AllEventReady (current/need)|start|end|traceid](%d/%d)%v|%v|%d\n", *t.currenEventCount, t.needEventCount, t.startEventReady, t.endEventReady, traceID)
+	klog.Infof("[AllEventReady (current/need)|start|end|traceid](%d/%d)%v|%v|%d", *t.currenEventCount, t.needEventCount, t.startEventReady, t.endEventReady, traceID)
 	return t.startEventReady && t.endEventReady && *t.currenEventCount >= t.needEventCount
 	return t.startEventReady && t.endEventReady && *t.currenEventCount >= t.needEventCount
 }
 }
 
 

+ 3 - 3
tracing/tracing.go

@@ -11,6 +11,7 @@ import (
 	"github.com/coroot/coroot-node-agent/ebpftracer"
 	"github.com/coroot/coroot-node-agent/ebpftracer"
 	"github.com/coroot/coroot-node-agent/ebpftracer/l7"
 	"github.com/coroot/coroot-node-agent/ebpftracer/l7"
 	"github.com/coroot/coroot-node-agent/flags"
 	"github.com/coroot/coroot-node-agent/flags"
+	klog "github.com/sirupsen/logrus"
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/codes"
 	"go.opentelemetry.io/otel/codes"
 	"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
 	"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
@@ -20,7 +21,6 @@ import (
 	semconv "go.opentelemetry.io/otel/semconv/v1.18.0"
 	semconv "go.opentelemetry.io/otel/semconv/v1.18.0"
 	"go.opentelemetry.io/otel/trace"
 	"go.opentelemetry.io/otel/trace"
 	"inet.af/netaddr"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 )
 
 
 const (
 const (
@@ -53,7 +53,7 @@ func Init(machineId, hostname, version string) {
 	client := otlptracehttp.NewClient(opts...)
 	client := otlptracehttp.NewClient(opts...)
 	exporter, err := otlptrace.New(context.Background(), client)
 	exporter, err := otlptrace.New(context.Background(), client)
 	if err != nil {
 	if err != nil {
-		klog.Exitln(err)
+		klog.Fatalln(err)
 	}
 	}
 
 
 	batcher := sdktrace.WithBatcher(exporter)
 	batcher := sdktrace.WithBatcher(exporter)
@@ -80,7 +80,7 @@ type Trace struct {
 	ctx              context.Context
 	ctx              context.Context
 	span             trace.Span
 	span             trace.Span
 	lock             sync.RWMutex
 	lock             sync.RWMutex
-	stack       []ebpftracer.StackFunEvent
+	stack            []ebpftracer.StackFunEvent
 	currenEventCount *uint32
 	currenEventCount *uint32
 	needEventCount   uint32
 	needEventCount   uint32
 	startEventReady  bool
 	startEventReady  bool

+ 27 - 0
utils/enums/enums.go

@@ -0,0 +1,27 @@
+package enums
+
+import "time"
+
+const (
+	DaemonProc        = "euspace"
+	DaemonCtlProc     = "omniagent-ctl"
+	ServerAgentProc   = "omniagent-os"
+	NpmAgentProc      = "omniagent-net"
+	DaemonServiceFile = "omniagent"
+	CpuLimit          = "omniagent-cpulimit"
+
+	// windows
+	DaemonCtlProcEXE   = "omniagent-ctl.exe"
+	ServerAgentProcEXE = "omniagent-os.exe"
+	NpmAgentProcEXE    = "omniagent-net.exe"
+
+	DefaultInterval        = 30
+	DefaultFuseInterval    = 10
+	RootUser               = 0
+	DefaultTimeOut         = time.Duration(10) * time.Minute
+	DefaultLocalProxy      = "http://127.0.0.1"
+	DefaultLocalConfigPort = "18084"
+	DefaultLocalDataPort   = "18085"
+	DefaultLocalFilePort   = "18086"
+	DefaultVpcKey          = "identification"
+)

+ 0 - 4
utils/id.go

@@ -81,7 +81,6 @@ func BuildInt64ID(str string) ID_STRING {
 	code := fmt.Sprintf("%x", srcCode)
 	code := fmt.Sprintf("%x", srcCode)
 
 
 	id_string := md5ToDec(code)
 	id_string := md5ToDec(code)
-	fmt.Println(id_string)
 	return ID_STRING(id_string)
 	return ID_STRING(id_string)
 	//
 	//
 	//id, err := strconv.ParseInt(id_string, 10, 64)
 	//id, err := strconv.ParseInt(id_string, 10, 64)
@@ -120,13 +119,11 @@ func GetHostID() (int64, HashByte) {
 	srcCode := md5.Sum([]byte(str))
 	srcCode := md5.Sum([]byte(str))
 	code := fmt.Sprintf("%x", srcCode)
 	code := fmt.Sprintf("%x", srcCode)
 	host_id_string := md5ToDec(code)
 	host_id_string := md5ToDec(code)
-	fmt.Println(host_id_string)
 	id, err := strconv.ParseInt(host_id_string, 10, 64)
 	id, err := strconv.ParseInt(host_id_string, 10, 64)
 	if err != nil {
 	if err != nil {
 		return 0, HashByte{}
 		return 0, HashByte{}
 	}
 	}
 
 
-	fmt.Println(host_id_string)
 	HOST_ID_INT64 = id
 	HOST_ID_INT64 = id
 
 
 	var charArray HashByte
 	var charArray HashByte
@@ -155,7 +152,6 @@ func GetAppID() (int64, HashByte) {
 	} else {
 	} else {
 		id_string = "5410049101545798"
 		id_string = "5410049101545798"
 	}
 	}
-	fmt.Println("APPID:", id_string)
 	id, err := strconv.ParseInt(id_string, 10, 64)
 	id, err := strconv.ParseInt(id_string, 10, 64)
 	if err != nil {
 	if err != nil {
 		return 0, HashByte{}
 		return 0, HashByte{}

+ 2 - 1
utils/kernel.go

@@ -18,6 +18,7 @@ import (
 	"bufio"
 	"bufio"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	klog "github.com/sirupsen/logrus"
 	"golang.org/x/sys/unix"
 	"golang.org/x/sys/unix"
 	"os"
 	"os"
 	"strconv"
 	"strconv"
@@ -194,7 +195,6 @@ func GetBootTime() (time.Time, error) {
 	if !bootTime.IsZero() {
 	if !bootTime.IsZero() {
 		return bootTime, nil
 		return bootTime, nil
 	}
 	}
-	fmt.Println("GetBootTime")
 	currentTime := time.Now()
 	currentTime := time.Now()
 	var info unix.Sysinfo_t
 	var info unix.Sysinfo_t
 	if err := unix.Sysinfo(&info); err != nil {
 	if err := unix.Sysinfo(&info); err != nil {
@@ -207,6 +207,7 @@ func GetBootTime() (time.Time, error) {
 func KtimeToTimestamp(t uint64) int64 {
 func KtimeToTimestamp(t uint64) int64 {
 	bT, err := GetBootTime()
 	bT, err := GetBootTime()
 	if err != nil {
 	if err != nil {
+		klog.Errorf("error getting boot time: %v", err)
 		return int64(0)
 		return int64(0)
 	}
 	}
 	return bT.Add(time.Duration(t) * time.Nanosecond).UnixNano()
 	return bT.Add(time.Duration(t) * time.Nanosecond).UnixNano()

+ 14 - 2
utils/modelse/models.go

@@ -117,11 +117,23 @@ type RegisterAppReq struct {
 }
 }
 
 
 func (r *RegisterAppReq) Print() {
 func (r *RegisterAppReq) Print() {
+	println(r.String())
+}
+
+func (r *RegisterAppReq) String() string {
 	a, err := json.Marshal(r)
 	a, err := json.Marshal(r)
 	if err != nil {
 	if err != nil {
-		log.Error(err)
+		return err.Error()
+	}
+	return string(a)
+}
+
+func (w *WhiteListReq) String() string {
+	a, err := json.Marshal(w)
+	if err != nil {
+		return err.Error()
 	}
 	}
-	println(string(a))
+	return string(a)
 }
 }
 
 
 type WhiteListReq struct {
 type WhiteListReq struct {

+ 893 - 1
utils/util.go

@@ -1,12 +1,904 @@
 package utils
 package utils
 
 
 import (
 import (
+	"archive/tar"
+	"archive/zip"
+	"bufio"
+	"bytes"
+	"compress/gzip"
+	"crypto/md5"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
 	"fmt"
 	"fmt"
-	log "github.com/sirupsen/logrus"
+	"github.com/coroot/coroot-node-agent/logs"
+	"github.com/coroot/coroot-node-agent/utils/enums"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"regexp"
+	"runtime"
 	"runtime/debug"
 	"runtime/debug"
+	"strconv"
+	"strings"
+	. "strings"
+	"sync"
+	"time"
+
+	log "github.com/sirupsen/logrus"
+)
+
+var (
+	agentdaemon_home = ""
+	AgentsPath       = "agents"
+	BinPath          = "bin"
+	ConfPath         = "conf"
+	LogsPath         = "logs"
+	ConfName         = "daemon.conf"
+	ComIniFile       = "common.ini"
+	LogName          = "daemon.log"
+	ScriptPath       = "scripts"
+	RuntimePath      = "runtime"
+	MetaPath         = "meta"
 )
 )
 
 
+func init() {
+	logs.FormatterInit()
+}
+
+func init() {
+	// TODO: 后面校验所有的路径,都要以agentdaemon_home开头.... @jay
+	// 初始化时获取omniagent的工作目录
+	if ex, err := os.Executable(); err != nil {
+		agentdaemon_home = ""
+		// TODO: 不应该exit吗?@jay
+	} else {
+		agentdaemon_home = ex
+	}
+
+	tmp_dir, _ := filepath.EvalSymlinks(os.TempDir())
+	if tmp_dir != "" && strings.Contains(agentdaemon_home, tmp_dir) {
+		_, filename, _, ok := runtime.Caller(0)
+		if ok {
+			agentdaemon_home = filepath.Dir(filename)
+		}
+	}
+	agentdaemon_home = filepath.Dir(filepath.Dir(agentdaemon_home))
+	log.Infof("omniagent work path %s", agentdaemon_home)
+
+	_, filename := filepath.Split(agentdaemon_home)
+	if !strings.EqualFold(filename, "omniagent") && !strings.EqualFold(filename, "doccagent") && !strings.EqualFold(filename, "cloudwise") {
+		// 考虑后续支持自定义目录安装的情况 卸载逻辑在uninstall.sh中判断
+		//panic(errors.New(fmt.Sprintf("get exec path[%s] is not doccagent work path panic error", agentdaemon_home)))
+	}
+	log.Infof("omniagent exec path %s", agentdaemon_home)
+	//initCntrPidConvert()
+}
+
+func MD5(src string) (dest string) {
+	dest = fmt.Sprintf("%x", md5.Sum([]byte(src)))
+	return dest
+}
+
+// 检查url是否正确
+func CheckUrl(url string) error {
+	if url == "" {
+		err := errors.New("url is empty")
+		return err
+	}
+	// 对接软负载后直接返回uri即可
+	//if !strings.Contains(url, "http") {
+	//	err := errors.New("URL is incorrect,url is " + url)
+	//	return err
+	//}
+	return nil
+}
+
+// DecompressTarGzip 将 TAR.GZ 格式的文件解包到指定目录并返回指定文件所在的解包后的目录,
+// 如果存在多个同名指定文件,则返回第一个找到的目录
+func decompress(srcPath, dstPath string) (targetDir string, _ error) {
+	linkMap := make(map[string]string)
+	if err := os.MkdirAll(dstPath, os.ModePerm); err != nil {
+		return "", err
+	}
+	if strings.HasSuffix(srcPath, ".zip") {
+
+		zr, err := zip.OpenReader(srcPath)
+		if err != nil {
+			return "", err
+		}
+		defer zr.Close()
+
+		for _, k := range zr.File {
+			info := k.FileInfo()
+			fpath := filepath.Join(dstPath, k.Name)
+			if k.FileInfo().IsDir() {
+				err := os.MkdirAll(fpath, info.Mode())
+				if err != nil {
+					return "", err
+				}
+				continue
+			}
+			w, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, info.Mode())
+			if err != nil {
+				return "", err
+			}
+
+			r, err := k.Open()
+			if err != nil {
+				return "", err
+			}
+			io.Copy(w, r)
+			w.Close()
+		}
+		return dstPath, nil
+	}
+	srcFile, err := os.Open(srcPath)
+	if err != nil {
+		return "", err
+	}
+	defer srcFile.Close()
+	gr, err := gzip.NewReader(srcFile)
+	if err != nil {
+		return "", err
+	}
+	defer gr.Close()
+
+	tr := tar.NewReader(gr)
+	count := 1
+	for {
+		header, err := tr.Next()
+		if err == io.EOF {
+			break
+		} else if err != nil {
+			return "", err
+		}
+		if strings.HasPrefix(header.Name, ".") {
+			continue
+		}
+		if header.Linkname != "" {
+			hnd := filepath.Dir(header.Name)
+			hld := filepath.Join(hnd, header.Linkname)
+			linkMap[filepath.Join(dstPath, hld)] = filepath.Join(dstPath, header.Name)
+			continue
+		}
+		fpath := filepath.Join(dstPath, header.Name)
+		if count == 1 {
+			fmt.Println(fpath, header.Name)
+			targetDir = fpath
+		}
+		count++
+
+		info := header.FileInfo()
+		if info.IsDir() {
+			if err = os.MkdirAll(fpath, info.Mode()); err != nil {
+				return "", err
+			}
+			continue
+		}
+
+		if err = extractTarFileToPath(tr, info, fpath); err != nil {
+			return "", err
+		}
+	}
+	linkMapHandle(linkMap)
+	linkMap = nil
+	return targetDir, nil
+}
+
+func decompressWithTar(srcPath, dstPath string) (targetDir string, _ error) {
+	if err := os.MkdirAll(dstPath, os.ModePerm); err != nil {
+		return "", err
+	}
+	if strings.HasSuffix(srcPath, ".zip") {
+		return "", errors.New("unsupported zip file")
+	}
+
+	compressPath, err := makeCompressFolderAndMvTarFile2(srcPath)
+	if err != nil {
+		return "", err
+	}
+	folderName, err := decompressCjeInCurrentDirectory(compressPath)
+	if err != nil {
+		os.Rename(compressPath, srcPath)
+		return "", err
+	}
+	return filepath.Join(filepath.Dir(srcPath), "compress", folderName), nil
+}
+
+func makeCompressFolderAndMvTarFile2(path string) (string, error) {
+	baseName := filepath.Base(path)
+	folder := filepath.Dir(path)
+	compressPath := filepath.Join(folder, "compress")
+	if _, err := os.Stat(compressPath); os.IsNotExist(err) {
+		os.Mkdir(compressPath, os.ModePerm)
+	}
+	moveToPath := filepath.Join(compressPath, baseName)
+	if err := os.Rename(path, moveToPath); err != nil {
+		return "", err
+	}
+	return moveToPath, nil
+}
+
+func decompressCjeInCurrentDirectory(gzipPath string) (string, error) {
+	folder := filepath.Dir(gzipPath)
+	dir, err := ioutil.ReadDir(folder)
+	for _, d := range dir {
+		if d.Name() == filepath.Base(gzipPath) {
+			continue
+		} else {
+			err = RemovePath(filepath.Join(folder, d.Name()), "")
+			if err != nil {
+				log.Errorf("remove dir error %s", err)
+			}
+		}
+	}
+	cmd := exec.Command("tar", "-vzxf", gzipPath)
+	cmd.Dir = folder
+	err = cmd.Run()
+	if err != nil {
+		return "", err
+	}
+	fileInfoList, err := ioutil.ReadDir(folder)
+	if err != nil {
+		return "", err
+	}
+	for _, file := range fileInfoList {
+		if file.Name() == filepath.Base(gzipPath) {
+			continue
+		}
+		return file.Name(), nil
+	}
+	return "", fmt.Errorf("uncompress faild unknow error")
+}
+
+// extractTarFileToPath 写出 *tar.Reader 对象的内容到指定路径
+func extractTarFileToPath(tr *tar.Reader, info os.FileInfo, path string) error {
+	w, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, info.Mode())
+	if err != nil {
+		return err
+	}
+	defer w.Close()
+
+	_, err = io.Copy(w, tr)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func linkMapHandle(m map[string]string) {
+	for s, s2 := range m {
+		err := os.Link(s, s2)
+		if err != nil {
+			log.Errorf("linkMapHandle err %s => %s, err is %v", s, s2, err)
+		}
+	}
+}
+
+var CPUNum = float64(runtime.NumCPU())
+
+func FormatPath(s string) string {
+	switch runtime.GOOS {
+	case "windows":
+		return Replace(s, "/", "\\", -1)
+	case "darwin", "linux":
+		return Replace(s, "\\", "/", -1)
+	default:
+		log.Infof("only support linux,windows,darwin, but os is " + runtime.GOOS)
+		return s
+	}
+}
+
+func CopyDir(src string, dest string) {
+	src = FormatPath(src)
+	dest = FormatPath(dest)
+
+	var cmd *exec.Cmd
+
+	switch runtime.GOOS {
+	case "windows":
+		cmd = exec.Command("xcopy", src, dest, "/I", "/E")
+	case "darwin", "linux":
+		cmd = exec.Command("cp", "-R", src, dest)
+	}
+
+	outPut, err := cmd.Output()
+	if err != nil {
+		log.Infof("copyDir %s => %s ,err is => %v", src, dest, err)
+		return
+	}
+	log.Infof("copyDir %s => %s output is => %s", src, dest, string(outPut))
+}
+
+func WriteConf(path string, new map[string]interface{}) error {
+	info, err := os.Stat(path)
+	if err != nil {
+		log.Errorf("write config[%s] get file info error:%s", path, err)
+
+		return err
+	}
+	file, err := os.OpenFile(path, os.O_WRONLY, info.Mode())
+	if err != nil {
+		log.Errorf("write config[%s] open file error:%s", path, err)
+		return err
+	}
+	file.Truncate(0)
+	defer file.Close()
+	data, err := json.MarshalIndent(&new, "", "\t")
+	if err != nil {
+		log.Errorf("write config[%s] write json data error:%s", path, err)
+		return err
+	}
+	file.Write(data)
+	return nil
+}
+
+func HTTPConnect(method, url, token string, data []byte) ([]byte, error) {
+	req, err := http.NewRequest(method, url, bytes.NewBuffer(data))
+	if err != nil {
+		log.Errorf("create request error:%s.", err)
+		return nil, err
+	}
+	req.Header.Set("Content-Type", "application/json")
+	req.Header.Add("token", token)
+	client := &http.Client{
+		Timeout: 60 * time.Second,
+	}
+	resp, err := client.Do(req)
+	if err != nil {
+		log.Error(err)
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	result, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		log.Errorf("http read body error:%s", err)
+		return nil, err
+	}
+
+	return result, nil
+}
+
+func ReadConfig(data []byte) (map[string]interface{}, error) {
+	result := map[string]interface{}{}
+	err := json.Unmarshal(data, &result)
+	if err != nil {
+		return result, err
+	}
+	return result, nil
+}
+
+func updateConfig(new map[string]interface{}, old map[string]interface{}) map[string]interface{} {
+	for k, v := range new {
+		if value, ok := old[k]; ok {
+			newconf, newok := v.(map[string]interface{})
+			oldconf, oldok := value.(map[string]interface{})
+			if newok && oldok {
+				new[k] = updateConfig(newconf, oldconf)
+				continue
+			}
+			new[k] = value
+		}
+	}
+	return new
+}
+
+func UpdateConfig(oldPath, newPath string) error {
+	oldConfByte, err := ioutil.ReadFile(oldPath)
+	if err != nil {
+		return err
+	}
+	oldConf, err := ReadConfig(oldConfByte)
+	if err != nil {
+		return err
+	}
+	newConfByte, err := ioutil.ReadFile(newPath)
+	if err != nil {
+		return err
+	}
+	newConf, err := ReadConfig(newConfByte)
+	if err != nil {
+		return err
+	}
+	new := updateConfig(newConf, oldConf)
+	err = WriteConf(oldPath, new)
+	if err != nil {
+		WriteConf(oldPath, oldConf)
+		return err
+	}
+	return nil
+}
+
+func CopyFile(dstName, srcName string) (written int64, err error) {
+	src, err := os.Open(srcName)
+	if err != nil {
+		return
+	}
+	defer src.Close()
+	dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0777)
+	if err != nil {
+		return
+	}
+	defer dst.Close()
+	return io.Copy(dst, src)
+}
+
+func CheckPath(path string) (err error) {
+	path, err = filepath.Abs(path)
+	if err != nil {
+		log.Errorf("check path[%s] get abs path error %s", path, err)
+		return err
+	}
+	// 通过比较路径的长短来判断是否为子路径
+	if len(path) < len(agentdaemon_home) {
+		log.Errorf("remove path[%s] check path length error", path)
+		return errors.New(fmt.Sprintf("remove path[%s] check path length error", path))
+	}
+	// 通过比较父路径是否相同来判断是否为子路径
+	if agentdaemon_home != path[:len(agentdaemon_home)] {
+		log.Errorf("remove path[%s] check path is not subdirectory[%s]", path, agentdaemon_home)
+		return errors.New(fmt.Sprintf("remove path[%s] check path is not subdirectory[%s]", path, agentdaemon_home))
+	}
+	return nil
+}
+
+// 校验删除的目录是否为doccagent工作目录的子目录
+func RemovePath(path, filename string) (err error) {
+	// 校验删除的文件名
+	_, real_filename := filepath.Split(path)
+	if filename != "" && real_filename != filename {
+		log.Errorf("remove path[%s] check filename is not %s", path, filename)
+		return errors.New(fmt.Sprintf("remove path[%s] check filename is not %s", path, filename))
+	}
+	stat, err := os.Stat(path)
+	if os.IsNotExist(err) {
+		return nil
+	}
+	if err != nil {
+		log.Errorf("remove path[%s] get file stat error %s", path, err)
+		return err
+	}
+	if stat.IsDir() {
+		err = os.RemoveAll(path)
+	} else {
+		err = os.Remove(path)
+	}
+	if err != nil {
+		log.Errorf("remove path[%s] error %s", path, err)
+	}
+	return err
+}
+
+func GetRootPath() string {
+	return agentdaemon_home
+}
+
+func GetDefaultPath(path_name ...string) string {
+	return filepath.Join(agentdaemon_home, filepath.Join(path_name...))
+}
+
+func GetDefaultConfigPath() string {
+	return GetDefaultPath(ConfPath, ConfName)
+}
+
+func GetDefaultLogPath() string {
+	return GetDefaultPath(LogsPath)
+}
+
+func GetDefaultBasePath() string {
+	return filepath.Join(GetDefaultPath(ScriptPath), "base")
+}
+
+func GetControlProc() string {
+	return filepath.Join(GetDefaultPath(ScriptPath), enums.DaemonCtlProc)
+}
+
+func GetCommonIni() string {
+	return GetDefaultPath(ConfPath, ComIniFile)
+}
+
+func GetPluginInstallParamsPath(pluginId string) string {
+	if pluginId != "" {
+		pluginId += ".conf"
+	}
+	return GetDefaultPath(ConfPath, "agents-installation", pluginId)
+}
+
 func CatchFn(err interface{}) {
 func CatchFn(err interface{}) {
 	log.Errorf(fmt.Sprintf("panic : %s\n", err))
 	log.Errorf(fmt.Sprintf("panic : %s\n", err))
 	log.Errorf(fmt.Sprint(string(debug.Stack())))
 	log.Errorf(fmt.Sprint(string(debug.Stack())))
 }
 }
+
+func FileExist(path string) bool {
+	_, err := os.Stat(path) //os.Stat获取文件信息
+	if os.IsNotExist(err) {
+		return false
+	}
+	return true
+}
+
+/**
+ * 从token中解析accountid/userid
+ */
+func DecodeAccountUser(token string) (string, string) {
+	var account_id string
+	var user_id string
+	account_and_user_byte, err := base64.StdEncoding.DecodeString(token)
+	if err != nil {
+		return account_id, user_id
+	}
+	account_and_user_string := strings.Split(string(account_and_user_byte), "@")
+	if len(account_and_user_string) > 0 {
+		account_id = account_and_user_string[0]
+	}
+	return account_id, user_id
+}
+
+/**
+ * 读取原配置项
+ */
+func GetOriginConfig() (map[string]interface{}, error) {
+	configMap := make(map[string]interface{})
+	content, err := ioutil.ReadFile(GetDefaultConfigPath())
+	if err != nil {
+		return nil, err
+	}
+	err = json.Unmarshal(content, &configMap)
+	return configMap, err
+}
+
+func Capitalize(str string) string {
+	if len(str) == 0 {
+		return str
+	}
+	upperStr := strings.ToUpper(string(str[0]))
+	if len(str) > 1 {
+		return upperStr + str[1:]
+	}
+	return upperStr
+}
+
+func CreatePidFile() error {
+	pid := os.Getpid()
+	pidFile := GetDefaultPath(RuntimePath, enums.DaemonProc+".pid")
+	// 如果pid文件存在,覆盖写入
+	if !FileExist(pidFile) {
+		_, err := os.Create(pidFile)
+		if err != nil {
+			return err
+		}
+	}
+	err := ioutil.WriteFile(pidFile, []byte(strconv.Itoa(pid)), 0644)
+	return err
+}
+
+// back up file
+func BackUpFile(src string, dst string, list []string) error {
+	// backup file
+	backupDir := filepath.Join(dst, "backup")
+	for _, v := range list {
+		srcPath := filepath.Join(src, v)
+		dstPath := filepath.Join(backupDir, v)
+		// rename file
+		err := os.Rename(srcPath, dstPath)
+		if err != nil {
+			// log.Errorf("backup file %s err : %v", v, err)
+			return err
+		}
+	}
+	return nil
+}
+
+func CopyFiles(srcDir string, dstDir string, list []string) error {
+	// if srcDir not exist ,return
+	if _, err := os.Stat(srcDir); os.IsNotExist(err) {
+		log.Debugf("srcDir is not exist %s err : %v", srcDir, err)
+		return err
+	}
+
+	// if dstdir not exist ,create it
+	if _, err := os.Stat(dstDir); os.IsNotExist(err) {
+		err := os.MkdirAll(dstDir, os.ModePerm)
+		if err != nil {
+			log.Errorf("mkdir dst dir %s err : %v", dstDir, err)
+			return err
+
+		}
+	}
+
+	// Copy list dir or file to dstDir
+	for _, v := range list {
+		srcPath := filepath.Join(srcDir, v)
+		dstPath := filepath.Join(dstDir, v)
+
+		// src is dir
+		fsInfo, err := os.Stat(srcPath)
+		if err != nil {
+			log.Debugf("srcPath is %s, err is %v", srcPath, err)
+			continue
+		}
+		// log err
+		log.Debugf("srcPath is %s, err is %v", srcPath, err)
+		if fsInfo.IsDir() {
+			// mkdir dst dir
+			err := os.MkdirAll(filepath.Dir(dstPath), os.ModePerm)
+			if err != nil {
+				log.Errorf("mkdir dst dir %s err : %v", dstPath, err)
+				return err
+			}
+			CopyDir(srcPath, dstPath)
+		} else {
+			// copy file
+			err = os.MkdirAll(filepath.Dir(dstPath), os.ModePerm)
+			if err != nil {
+				log.Errorf("mkdir dst dir %s err : %v", dstPath, err)
+				return err
+			}
+			_, err := CopyFile(dstPath, srcPath)
+			if err != nil {
+				log.Errorf("copy file %s err : %v", v, err)
+				return err
+			}
+		}
+
+	}
+	return nil
+}
+
+// // CopyDir 拷贝整个目录
+// func CopyDir(src string, dst string, overwrite bool) error {
+//     // 创建目标目录
+//     err := os.MkdirAll(dst, 0755)
+//     if err != nil {
+//         return err
+//     }
+
+//     // 打开源目录
+//     entries, err := os.ReadDir(src)
+//     if err != nil {
+//         return err
+//     }
+
+//     // 遍历源目录中的条目
+//     for _, entry := range entries {
+//         sourcePath := filepath.Join(src, entry.Name())
+//         destinationPath := filepath.Join(dst, entry.Name())
+
+//         // 如果是目录,则递归拷贝
+//         if entry.IsDir() {
+//             err = CopyDir(sourcePath, destinationPath, overwrite)
+//             if err != nil {
+//                 return err
+//             }
+//         } else {
+//             // 如果是文件,则拷贝文件
+//             err = CopyFile(sourcePath, destinationPath, overwrite)
+//             if err != nil {
+//                 return err
+//             }
+//         }
+//     }
+
+//     return nil
+// }
+
+func NewInstallPathParams(installPath string) []string {
+	// check dir is prefix of "/D="
+	if !strings.HasPrefix(installPath, "/D=") {
+		installPath = "/D=" + installPath
+	}
+
+	paths := strings.Split(installPath, " ")
+	return paths
+}
+
+func GetHostPid(pid int) (int, error) {
+	if os.Getenv("CW_CONTAINER") == "true" {
+		if cntrPidConvert != nil {
+			hostPid, err := cntrPidConvert.getHostPidByHostProc(pid)
+			if err != nil {
+				log.Errorf("getHostPidByHostProc %d error:%v", pid, err)
+			} else {
+				log.Infof("getHostPidByHostProc %d hostPid:%d", pid, hostPid)
+			}
+			return hostPid, err
+		}
+		hostPid, err := getHostPidBySched(pid)
+		if err != nil {
+			log.Errorf("getHostPidBySched %d error:%v", pid, err)
+		} else {
+			log.Infof("getHostPidBySched %d hostPid:%d", pid, hostPid)
+		}
+		return hostPid, err
+	} else {
+		return pid, nil
+	}
+}
+
+func getHostPidBySched(pid int) (int, error) {
+	hostPid := 0
+	schedFile := fmt.Sprintf("/proc/%d/sched", pid)
+	file, err := os.Open(schedFile)
+	if err != nil {
+		return 0, err
+	}
+	defer file.Close()
+	scanner := bufio.NewScanner(file)
+	if scanner.Scan() {
+		line := scanner.Text()
+		re := regexp.MustCompile(`\d+`)
+		match := re.FindString(line)
+		if match != "" {
+			hostPid, err = strconv.Atoi(match)
+			if err != nil {
+				return 0, err
+			}
+		}
+	}
+	return hostPid, nil
+}
+
+type cntrPidConvertMgr struct {
+	cntrPidToHostPid  map[int]int
+	mtxCntrPidConvert *sync.RWMutex
+	daemonid          []byte
+}
+
+var cntrPidConvert *cntrPidConvertMgr
+
+func initCntrPidConvert() {
+	if os.Getenv("CW_CONTAINER") == "true" && os.Getenv("CW_HOST_PID_ENABLE") != "true" {
+		if hostPid, err := getHostPidBySched(1); err == nil && hostPid > 1 {
+			log.Infof("not need convert cntrPid to hostPid")
+			return
+		}
+
+		host_root := os.Getenv("HOST_ROOT")
+		host_proc := os.Getenv("HOST_PROC")
+		log.Infof("host_root %s, host_proc %s", host_root, host_proc)
+		if len(host_proc) <= len(host_root) || host_proc[:len(host_root)] != host_root {
+			log.Errorf("HOST_ROOT %s is not prefix of HOST_PROC %s", host_root, host_proc)
+			return
+		}
+		//若host_proc目录不存在,直接返回
+		if _, err := os.Stat(host_proc); os.IsNotExist(err) {
+			log.Errorf("host_proc %s not exist", host_proc)
+			return
+		}
+
+		daemonidPath := filepath.Join(GetDefaultPath(MetaPath, ".daemonid"))
+		daemonid, err := ioutil.ReadFile(daemonidPath)
+		if len(daemonid) == 0 {
+			log.Errorf("read daemonid from %s fail: %v", daemonidPath, err)
+			return
+		}
+		log.Infof("daemonid %s", daemonid)
+		cntrPidConvert = &cntrPidConvertMgr{
+			cntrPidToHostPid:  make(map[int]int),
+			mtxCntrPidConvert: &sync.RWMutex{},
+			daemonid:          daemonid,
+		}
+	}
+}
+
+func (c *cntrPidConvertMgr) getHostPidByHostProc(pid int) (int, error) {
+	c.mtxCntrPidConvert.RLock()
+	hostPid, ok := c.cntrPidToHostPid[pid]
+	c.mtxCntrPidConvert.RUnlock()
+	if ok {
+		return hostPid, nil
+	}
+	var err error
+	c.mtxCntrPidConvert.Lock()
+	defer c.mtxCntrPidConvert.Unlock()
+	hostPid, ok = c.cntrPidToHostPid[pid]
+	if ok {
+		return hostPid, nil
+	}
+	timeStart := time.Now()
+	c.cntrPidToHostPid, err = parseProcDirectory(os.Getenv("OS_HOST_PROC"), c.daemonid)
+	log.Infof("cntrPidToHostPid %#v, time_%s: %#v", c.cntrPidToHostPid, time.Since(timeStart).String(), err)
+	if err != nil {
+		return 0, err
+	}
+	hostPid, ok = c.cntrPidToHostPid[pid]
+	if ok {
+		return hostPid, nil
+	} else {
+		return 0, fmt.Errorf("can not find pid %d in container", pid)
+	}
+}
+
+func parseProcDirectory(dirPath string, daemonid []byte) (pidMap map[int]int, err error) {
+	pidMap = make(map[int]int)
+
+	daemonidPath := filepath.Join(GetDefaultPath(MetaPath, ".daemonid"))
+	fileInfo, err := os.Stat(daemonidPath)
+	if err != nil {
+		return
+	}
+	daemonidFileSize := fileInfo.Size()
+
+	// 遍历指定目录下的PID目录
+	files, err := ioutil.ReadDir(dirPath)
+	if err != nil {
+		return
+	}
+
+	for _, file := range files {
+		if file.IsDir() {
+			pid := file.Name()
+			pidInt, err := strconv.Atoi(pid)
+			if err != nil {
+				continue
+			}
+			daemonidFilePath := filepath.Join(dirPath, pid, "root/opt/cloudwise/omniagent/meta/.daemonid")
+
+			// 检查是否存在指定文件并size等于指定大小
+			fileInfo, err = os.Stat(daemonidFilePath)
+			if err != nil {
+				continue
+			}
+			fileSize := fileInfo.Size()
+			if fileSize != daemonidFileSize {
+				log.Errorf("%s file size %d not equal %d", daemonidFilePath, fileSize, daemonidFileSize)
+				continue
+			}
+
+			// 检查是否存在指定文件并内容等于指定内容
+			daemonidContentBytes, err := ioutil.ReadFile(daemonidFilePath)
+			if err == nil && bytes.Compare(daemonidContentBytes, daemonid) == 0 {
+				statusFilePath := filepath.Join(dirPath, pid, "status")
+
+				// 解析status文件获取NSpid的第二个字符串
+				nspidValue, err := getNSpidValue(statusFilePath)
+				if err == nil {
+					pidMap[nspidValue] = pidInt
+				} else {
+					log.Errorf("getNSpidValue %s error:%v", statusFilePath, err)
+				}
+			}
+		}
+	}
+
+	return pidMap, nil
+}
+
+func getNSpidValue(filePath string) (int, error) {
+	content, err := ioutil.ReadFile(filePath)
+	if err != nil {
+		return 0, err
+	}
+
+	lines := strings.Split(string(content), "\n")
+
+	for _, line := range lines {
+		if strings.HasPrefix(line, "NSpid:") {
+			fields := strings.Fields(line)
+			if len(fields) >= 3 {
+				valueStr := fields[2]
+				value, err := strconv.Atoi(valueStr)
+				if err != nil {
+					return 0, fmt.Errorf("failed to convert NSpid value %s to integer: %v", valueStr, err)
+				}
+				return value, nil
+			} else if len(fields) == 2 {
+				valueStr := fields[1]
+				value, err := strconv.Atoi(valueStr)
+				if err != nil {
+					return 0, fmt.Errorf("failed to convert NSpid value %s to integer: %v", valueStr, err)
+				}
+				return value, nil
+			} else {
+				log.Errorf("invalid NSpid line format in %s: %s", filePath, line)
+			}
+		}
+	}
+
+	return 0, errors.New("NSpid value not found!")
+}

+ 4 - 6
utils/worker/serverWorker.go

@@ -45,28 +45,26 @@ type ServerWorker interface {
 }
 }
 
 
 func (w *ServerHTTPWorker) RegisterApp(request RegisterAppReq) error {
 func (w *ServerHTTPWorker) RegisterApp(request RegisterAppReq) error {
-	log.Infof("RegisterApp request:%v.", request)
+	log.Infof("Register App request:%v.", request.String())
 	_, err := w.requestServer("/v2/app/create", request)
 	_, err := w.requestServer("/v2/app/create", request)
 	if err != nil {
 	if err != nil {
+		log.WithError(err).Errorf("Failed RegisterApp request:%v.", request.String())
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
 func (w *ServerHTTPWorker) WhiteList(request WhiteListReq) (WhiteData, error) {
 func (w *ServerHTTPWorker) WhiteList(request WhiteListReq) (WhiteData, error) {
-	log.Infof("WhiteList request:%v.", request)
+	log.Infof("WhiteList request:%v.", request.String())
 	response := WhiteData{}
 	response := WhiteData{}
-
-	//log.Infof("WhiteList request:%s.", string(data))
 	result, err := w.requestServer("/api/v1/agent/whitelist", request)
 	result, err := w.requestServer("/api/v1/agent/whitelist", request)
 	if err != nil {
 	if err != nil {
 		return response, err
 		return response, err
 	}
 	}
 
 
 	err = json.Unmarshal(result, &response)
 	err = json.Unmarshal(result, &response)
-	//fmt.Println(string(result))
 	if err != nil {
 	if err != nil {
-		log.Errorf("WhiteList unmarshal response error:%s.", err)
+		log.WithError(err).Errorf("Failed RegisterApp request:%v.", request.String())
 		return response, err
 		return response, err
 	}
 	}
 	return response, nil
 	return response, nil