package service import ( "bufio" "encoding/json" "errors" "fmt" "io/ioutil" "os" "os/exec" "path" "strconv" "strings" "syscall" "time" "vamicro/config" "vamicro/extend/util" "vamicro/system-service/models" versionControlS "vamicro/version-control/service" "basic.com/fileServer/WeedFSClient.git" "basic.com/valib/bhomeclient.git" "basic.com/valib/bhomedbapi.git" "basic.com/valib/logger.git" uuid "github.com/satori/go.uuid" ) var ( AuthInfo util.AuthorizationInfo ) type SysService struct { } type FileChunkCheckVo struct { UserId string ChunkNumber int //当前分片下标,从1开始 ChunkSize int //每一块的大小 CurrentChunkSize int //当前分块的大小 FileName string //文件名称 Identifier string //整个文件唯一标识,md5 RelativePath string //文件客户端路径 TotalSize int64 //文件总大小 TotalChunks int //总分片数量 } type FileUploadVo struct { Id string UserId string ChunkNumber int //当前分片下标,从1开始 ChunkSize int //每一块的大小 CurrentChunkSize int //当前分块的大小 FileName string //文件名称 Identifier string //整个文件唯一标识,md5 RelativePath string //文件客户端路径 TotalSize int64 //文件总大小 TotalChunks int //总分片数量 File *bhomeclient.FileArg //当前分片的文件内容 } func (sv SysService) CheckUpdateFile(arg *FileChunkCheckVo) bool { configPatchPath := "" if config.Server.PatchPath != "" { configPatchPath = config.Server.PatchPath } else { configPatchPath = "/opt/vasystem/patch" } fileTmpPath := configPatchPath + "/" + arg.Identifier if !util.Exists(fileTmpPath) { return false } //判断合成的文件是否存在 index := strings.LastIndex(arg.FileName, ".") subfix := "" if index > -1 { //有后缀 subfix = arg.FileName[index:] } mergedFilePath := fileTmpPath + subfix if util.Exists(mergedFilePath) { return true } //判断分块文件是否存在 chunkAlignNum := util.FormatNum(arg.TotalChunks, arg.ChunkNumber) chunkFilePath := fileTmpPath + "/" + arg.Identifier + "_" + chunkAlignNum if !util.Exists(chunkFilePath) { return false } if arg.ChunkNumber == arg.TotalChunks { dirFiles, _ := ioutil.ReadDir(fileTmpPath) if dirFiles != nil && len(dirFiles) == arg.TotalChunks { //表示所有分块都上传了,需要merge if b, _ := MergeChunks(fileTmpPath, mergedFilePath); !b { return false } } } return true } func (sv SysService) PatchUpload(arg *FileUploadVo) (bool, bool, string) { configPatchPath := "" if config.Server.PatchPath != "" { configPatchPath = config.Server.PatchPath } else { configPatchPath = "/opt/vasystem/patch" } if !util.CreateDirectory(configPatchPath) { return false, false, "创建文件夹失败" } filenameWithSuffix := path.Base(arg.FileName) subfix := path.Ext(filenameWithSuffix) MD5Str := arg.Identifier logger.Debug("Identifier:", MD5Str) fileTmpPath := configPatchPath + "/" + MD5Str if !util.Exists(fileTmpPath) { if !util.CreateDirectory(fileTmpPath) { return false, false, "创建补丁文件夹失败" } } chunkAlignNum := util.FormatNum(arg.TotalChunks, arg.ChunkNumber) fileSavePath := fileTmpPath + "/" + MD5Str + "_" + chunkAlignNum if util.Exists(fileSavePath) { rmErr := os.Remove(fileSavePath) if rmErr != nil { logger.Debug("rmErr:", rmErr) return false, false, rmErr.Error() } } file, e := os.Create(fileSavePath) if e != nil { logger.Debug("os.Create err:", e, "fileSavePath:", fileSavePath) return false, false, e.Error() } defer file.Close() writer := bufio.NewWriter(file) nn, err2 := writer.Write(arg.File.Bytes) if nn == 0 || err2 != nil { logger.Debug("write chunkData err:", err2, "nn:", nn) return false, false, "写入补丁包失败" } if err2 = writer.Flush(); err2 != nil { logger.Debug("write flush err:", err2) return false, false, err2.Error() } isComplete := false dirFiles, _ := ioutil.ReadDir(fileTmpPath) if dirFiles != nil && len(dirFiles) == arg.TotalChunks { isComplete = true } if isComplete { if mergeB, mergeE := MergeChunks(fileTmpPath, fileTmpPath+subfix); mergeB { logger.Debug("merge all chunks success,identifier:", MD5Str, "fileName:", arg.FileName) unPackB, unPackErr := unPackPatchPackage(MD5Str, subfix) logger.Debug("unPackB:", unPackB, "unPackErr:", unPackErr) if unPackB { return true, isComplete, "解压补丁包失败,错误的补丁包格式" } } else { return false, isComplete, mergeE.Error() } } return true, isComplete, "" } // upgrade func (sv SysService) Upgrade(identifier string, filename string) (bool, error) { if !bakBeforeUpgrade() { return false, errors.New("更新前备份失败") } configPatchPath := "" if config.Server.PatchPath != "" { configPatchPath = config.Server.PatchPath } else { configPatchPath = "/opt/vasystem/patch" } filenameWithSuffix := path.Base(filename) ext := path.Ext(filenameWithSuffix) zipFilePath := configPatchPath + "/" + identifier + ext if util.Exists(zipFilePath) { //校验md5 strMd5, e := util.FileMd5(zipFilePath) if e != nil || strMd5 == "" { return false, errors.New("获取升级压缩包md5失败") } if strMd5 == identifier { if !updatePatch(identifier, ext) { return false, errors.New("执行升级过程异常,请确定上传的补丁是tar.gz格式") } return true, nil } else { logger.Debug("strMd5 is", strMd5, "identifier is", identifier, "not equal") return false, errors.New("校验升级文件失败") } } else { return false, errors.New("升级文件已丢失,请重新上传") } } func bakBeforeUpgrade() bool { configBakPath := "" if config.Server.BakPath != "" { configBakPath = config.Server.BakPath } else { configBakPath = "/opt/vasystem/bak" } if util.Exists(configBakPath) { //只保留最新的版本 if err := os.RemoveAll(configBakPath); err != nil { return false } } if !util.CreateDirectory(configBakPath) { return false } b, err := ExecCmd("cp -r /opt/vasystem/bin /opt/vasystem/bak") if err != nil { logger.Debug("bakBeforeUpgrade result:", string(b), "err:", err) return false } return true } // 更新系统程序 func updatePatch(identifier string, ext string) bool { configPatchPath := "" if config.Server.PatchPath != "" { configPatchPath = config.Server.PatchPath } else { configPatchPath = "/opt/vasystem/patch" } //1.解压缩更新包 unPackPath := configPatchPath + "/" + identifier + "_basic/" if util.Exists(unPackPath) { //此版本已经更新过 rmErr := os.RemoveAll(unPackPath) if rmErr != nil { return false } } if !util.CreateDirectory(unPackPath) { return false } unPackFilePath := configPatchPath + "/" + identifier + ext utzOut, err := util.UnTarGzByCmd(unPackFilePath, unPackPath) if err != nil { logger.Debug("UnPack err:", err, "unPackFile:", unPackFilePath, " out:", utzOut) return false } //如果通用脚本有更新,则更新通用脚本 if util.Exists(unPackPath + "updatePatch.sh") { cpStr := fmt.Sprintf("cp %s /opt/vasystem/bin", unPackPath+"updatePatch.sh") b, err := ExecCmd(cpStr) if err != nil { logger.Debug("cp updatePatch.sh to bin err:", err, "result:", string(b)) return false } } //判断更新包里是否有补丁脚本,如果有则执行,否则执行updatePatch.sh updateCmd := fmt.Sprintf("./updatePatch.sh %s %s %s &", unPackPath, unPackFilePath, configPatchPath+"/"+identifier) if util.Exists(unPackPath + "upgrade.sh") { updateCmd = fmt.Sprintf("%supgrade.sh %s %s %s &", unPackPath, unPackPath, unPackFilePath, configPatchPath+"/"+identifier) } //2.更新系统 b, err := ExecCmd(updateCmd) if err != nil { logger.Debug("upgrade err:", err, "result:", string(b), "cmd:", updateCmd) return false } else { logger.Debug("upgrade result:", string(b), "cmd:", updateCmd) } return true } func unPackPatchPackage(identifier string, ext string) (bool, error) { if ext != ".gz" { return false, errors.New("非法的安装包文件,安装包为.tar.gz文件") } configPatchPath := "" if config.Server.PatchPath != "" { configPatchPath = config.Server.PatchPath } else { configPatchPath = "../patch" } //1.解压缩更新包 unPackTargetPath := configPatchPath + "/" + identifier + "_basic/" unPackFilePath := configPatchPath + "/" + identifier + ext if util.Exists(unPackTargetPath) { //此版本已经更新过 rmErr := os.RemoveAll(unPackTargetPath) if rmErr != nil { return false, rmErr } } if !util.CreateDirectory(unPackTargetPath) { return false, errors.New("创建压缩文件夹失败") } logger.Debug("unPackFilePath:", unPackFilePath, "unPackPath:", unPackTargetPath) _, err := util.UnTarGzByCmd(unPackFilePath, unPackTargetPath) if err != nil { logger.Debug("UnPack err:", err, "unPackFile:", unPackFilePath) return false, err } return true, nil } func MergeChunks(chunkPath string, storePath string) (bool, error) { var cmd *exec.Cmd cmd = exec.Command("/bin/sh", "-c", fmt.Sprintf(` filepath=%s filestore=%s echo "filepath: " $filepath echo "filestorepath: " $filestore if [ ! -f $filestore ]; then echo "$filestore not exist" else rm -f $filestore fi for item in $(ls $filepath | sort -n) do $(cat ${filepath}/${item} >> ${filestore}) echo "merge ${filepath}/${item} to $filestore ok" done echo "file store ok"`, chunkPath, storePath)) if b, err := cmd.Output(); err != nil { logger.Debug("mergeChunks err:", err, "result:", string(b)) return false, errors.New("合成压缩包失败") } else { logger.Debug("mergeChunks result:", string(b)) return true, nil } } func ExecCmd(cmdStr string) ([]byte, error) { var cmd *exec.Cmd cmd = exec.Command("/bin/sh", "-c", cmdStr) return cmd.Output() } // 上传声音文件 func (sv SysService) UploadVoice(fileBytes []byte, filename string) (string, error) { fileExt := path.Ext(filename) fileExt = strings.ToLower(fileExt) if fileExt != ".mp3" && fileExt != ".wma" && fileExt != ".wav" { return "", errors.New("audio format error") } var sApi bhomedbapi.SysSetApi flag, localConf := sApi.GetServerInfo() if !flag || localConf.AlarmIp == "" || localConf.AlarmPort == 0 { logger.Debug("localConf err") return "", errors.New("localConf err") } var weedfsUri = "http://" + localConf.WebPicIp + ":" + strconv.Itoa(int(localConf.WebPicPort)) + "/submit?collection=voice" logger.Debug("weedfsUri:", weedfsUri) weedFilePath, err := WeedFSClient.UploadFile(weedfsUri, filename, fileBytes, 30*time.Second) if err != nil { logger.Debug("WeedFSClient.UploadFile err:", err, filename) return "", errors.New("WeedFSClient.UploadFile err") } v := models.Voice{ Name: filename, Path: weedFilePath, } v.Id = uuid.NewV4().String() b, err := v.Insert() if nil == err && b { return weedFilePath, nil } return weedFilePath, err } // 获取所有配置 func (sv SysService) GetAllSetting() (settings []models.SysSetting, err error) { settingModel := models.SysSetting{} return settingModel.GetAllSetting() } // 更新配置 func (sv SysService) SaveSetting(setting models.SysSetting) (err error) { return setting.SaveSetting(true) } // 获取硬盘剩余空间 func (sv SysService) DiskInfo(dev string) (uint64, uint64) { var stat syscall.Statfs_t err := syscall.Statfs(dev, &stat) if err != nil { return 0, 0 } All := stat.Blocks * uint64(stat.Bsize) Free := stat.Bfree * uint64(stat.Bsize) return All, Free } // 同步更新设置 func PersistentWrapper(topic string, payloads []byte) { if versionControlS.AuthorizationUpdateTopic == topic { if err := json.Unmarshal(payloads, &AuthInfo); nil != err { logger.Error("handleSubMsg failed to persistent:", topic, string(payloads)) } } if "sync-proc-message-to-serf" == topic { logger.Debug("handleSubMsg sync-proc-message-to-serf") ClusterSyncProcMessage(payloads) } } // 获取授权方式和授权密码 func (sv SysService) ShowAuthInfo() (interface{}, error) { local_configs := models.LocalConfig{} // 获取表中的第一条记录(只有一条) err := local_configs.Select() if err == nil && local_configs.Id != "" { ret := map[string]interface{}{ "id": local_configs.Id, "need_auth_pwd": local_configs.NeedAuthPwd, "auth_pwd": local_configs.AuthPwd, } return ret, nil } else { return nil, err } } // 设置授权方式和授权密码 func (sv SysService) SetAuthInfo(req *models.LocalConfig) error { err := req.UpdateAuth() if err == nil { return nil } else { return err } }