package utils

import (
	"archive/tar"
	"archive/zip"
	"bufio"
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/coroot/coroot-node-agent/utils/enums"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"runtime/debug"
	"strconv"
	"strings"
	. "strings"
	"sync"
	"time"

	log "github.com/sirupsen/logrus"
)

var (
	agentdaemon_home = ""
	AgentsPath       = "agents"
	BinPath          = "bin"
	ConfPath         = "conf"
	LogsPath         = "logs"
	LibsPath         = "libs"
	ConfName         = "daemon.conf"
	ComIniFile       = "common.ini"
	LogName          = "daemon.log"
	ScriptPath       = "scripts"
	RuntimePath      = "runtime"
	MetaPath         = "meta"
)

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(srcName, dstName 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 GetDefaultRuntimePath() string {
	return GetDefaultPath(RuntimePath)
}

func GetDefaultScriptsPath(path_name ...string) string {
	return GetDefaultPath(ScriptPath, filepath.Join(path_name...))
}

func GetDefaultLibsPath(path_name ...string) string {
	return GetDefaultPath(LibsPath, runtime.GOARCH, filepath.Join(path_name...))
}

func GetDefaultAgentsPath(path_name ...string) string {
	return GetDefaultPath(AgentsPath, filepath.Join(path_name...))
}

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{}) {
	log.Errorf(fmt.Sprintf("panic : %s\n", err))
	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!")
}

func ToString(v interface{}) string {
	b, err := json.Marshal(v)
	if err != nil {
		return err.Error()
	}
	return string(b)
}

func GetSoPath(pid uint32, soname string, rootfs string) (string, error) {
	mapsFile := fmt.Sprintf("/proc/%d/maps", pid)
	file, err := os.Open(mapsFile)
	if err != nil {
		return "", err
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.Contains(line, soname) {
			fields := strings.Fields(line)
			if len(fields) > 5 {
				path := fields[5]
				if strings.HasSuffix(path, ".so") {
					return filepath.Join(rootfs, path), nil
				}
			}
		}
	}
	return "", fmt.Errorf("library %s not found in process.", soname)
}

func StopTimerWithDrainChan(timer *time.Timer) {
	if !timer.Stop() {
		select {
		case <-timer.C:
		default:
		}
	}
}

func ResetTimerWithDrainChan(timer *time.Timer, d time.Duration) {
	if !timer.Stop() {
		select {
		case <-timer.C:
		default:
		}
	}
	timer.Reset(d)
}
