Storage Manager

Fix the app store classification problem
Fix the application market classification problem
This commit is contained in:
link 2022-01-20 18:38:59 +08:00
parent 9675eff69e
commit fcb906aa85
15 changed files with 482 additions and 120 deletions

View File

@ -21,6 +21,7 @@ type LSBLKModel struct {
Health string `json:"health"` Health string `json:"health"`
HotPlug bool `json:"hotplug"` HotPlug bool `json:"hotplug"`
FSUsed string `json:"fsused"` FSUsed string `json:"fsused"`
Temperature int `json:"temperature"`
Tran string `json:"tran"` Tran string `json:"tran"`
MinIO uint64 `json:"min-io"` MinIO uint64 `json:"min-io"`
UsedPercent float64 `json:"used_percent"` UsedPercent float64 `json:"used_percent"`
@ -28,5 +29,7 @@ type LSBLKModel struct {
Children []LSBLKModel `json:"children"` Children []LSBLKModel `json:"children"`
//详情特有 //详情特有
StartSector uint64 `json:"start_sector,omitempty"` StartSector uint64 `json:"start_sector,omitempty"`
Rota bool `json:"rota"` //true(hhd) false(ssd)
DiskType string `json:"disk_type"`
EndSector uint64 `json:"end_sector,omitempty"` EndSector uint64 `json:"end_sector,omitempty"`
} }

69
model/smartctl_model.go Normal file
View File

@ -0,0 +1,69 @@
package model
//
type SmartctlA struct {
Smartctl struct {
Version []int `json:"version"`
SvnRevision string `json:"svn_revision"`
PlatformInfo string `json:"platform_info"`
BuildInfo string `json:"build_info"`
Argv []string `json:"argv"`
ExitStatus int `json:"exit_status"`
} `json:"smartctl"`
Device struct {
Name string `json:"name"`
InfoName string `json:"info_name"`
Type string `json:"type"`
Protocol string `json:"protocol"`
} `json:"device"`
ModelName string `json:"model_name"`
SerialNumber string `json:"serial_number"`
FirmwareVersion string `json:"firmware_version"`
UserCapacity struct {
Blocks int `json:"blocks"`
Bytes int64 `json:"bytes"`
} `json:"user_capacity"`
SmartStatus struct {
Passed bool `json:"passed"`
} `json:"smart_status"`
AtaSmartData struct {
OfflineDataCollection struct {
Status struct {
Value int `json:"value"`
String string `json:"string"`
} `json:"status"`
CompletionSeconds int `json:"completion_seconds"`
} `json:"offline_data_collection"`
SelfTest struct {
Status struct {
Value int `json:"value"`
String string `json:"string"`
Passed bool `json:"passed"`
} `json:"status"`
PollingMinutes struct {
Short int `json:"short"`
Extended int `json:"extended"`
Conveyance int `json:"conveyance"`
} `json:"polling_minutes"`
} `json:"self_test"`
Capabilities struct {
Values []int `json:"values"`
ExecOfflineImmediateSupported bool `json:"exec_offline_immediate_supported"`
OfflineIsAbortedUponNewCmd bool `json:"offline_is_aborted_upon_new_cmd"`
OfflineSurfaceScanSupported bool `json:"offline_surface_scan_supported"`
SelfTestsSupported bool `json:"self_tests_supported"`
ConveyanceSelfTestSupported bool `json:"conveyance_self_test_supported"`
SelectiveSelfTestSupported bool `json:"selective_self_test_supported"`
AttributeAutosaveEnabled bool `json:"attribute_autosave_enabled"`
ErrorLoggingSupported bool `json:"error_logging_supported"`
GpLoggingSupported bool `json:"gp_logging_supported"`
} `json:"capabilities"`
} `json:"ata_smart_data"`
PowerOnTime struct {
Hours int `json:"hours"`
} `json:"power_on_time"`
PowerCycleCount int `json:"power_cycle_count"`
Temperature struct {
Current int `json:"current"`
} `json:"temperature"`
}

View File

@ -2,9 +2,11 @@ package command
import ( import (
"bufio" "bufio"
"context"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os/exec" "os/exec"
"time"
) )
func OnlyExec(cmdStr string) { func OnlyExec(cmdStr string) {
@ -85,7 +87,26 @@ func ExecLSBLK() []byte {
func ExecLSBLKByPath(path string) []byte { func ExecLSBLKByPath(path string) []byte {
output, err := exec.Command("lsblk", path, "-O", "-J", "-b").Output() output, err := exec.Command("lsblk", path, "-O", "-J", "-b").Output()
if err != nil { if err != nil {
fmt.Println("lsblk", err)
return nil return nil
} }
return output return output
} }
//exec smart
func ExecSmartCTLByPath(path string) []byte {
timeout := 3
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
defer cancel()
output, err := exec.CommandContext(ctx, "smartctl", "-a", path, "-j").Output()
if err != nil {
fmt.Println("smartctl", err)
return nil
}
return output
}
func ExecEnabledSMART(path string) {
exec.Command("smartctl", "-s on", path).Output()
}

View File

@ -21,6 +21,13 @@ const (
//zerotier //zerotier
GET_TOKEN_ERROR = 30001 GET_TOKEN_ERROR = 30001
//disk
NAME_NOT_AVAILABLE = 40001
DISK_NEEDS_FORMAT = 40002
DISK_BUSYING = 40003
REMOVE_MOUNT_POINT_ERROR = 40004
FORMAT_ERROR = 40005
//app //app
UNINSTALL_APP_ERROR = 50001 UNINSTALL_APP_ERROR = 50001
PULL_IMAGE_ERROR = 50002 PULL_IMAGE_ERROR = 50002
@ -37,34 +44,41 @@ const (
var MsgFlags = map[int]string{ var MsgFlags = map[int]string{
SUCCESS: "ok", SUCCESS: "ok",
ERROR: "fail", ERROR: "fail",
INVALID_PARAMS: "Invalid params", INVALID_PARAMS: "Parameters Error",
ERROR_AUTH_TOKEN: "error auth token", ERROR_AUTH_TOKEN: "Error auth token",
//user //user
PWD_INVALID: "Password invalid", PWD_INVALID: "Invalid password",
PWD_IS_EMPTY: "Password is empty", PWD_IS_EMPTY: "Password is empty",
PWD_INVALID_OLD: "Old Password invalid", PWD_INVALID_OLD: "Invalid old password",
ACCOUNT_LOCK: "Account Lock", ACCOUNT_LOCK: "Account is locked",
//system //system
DIR_ALREADY_EXISTS: "Directory already exists", DIR_ALREADY_EXISTS: "Folder already exists",
FILE_ALREADY_EXISTS: "File already exists", FILE_ALREADY_EXISTS: "File already exists",
FILE_OR_DIR_EXISTS: "File or directory already exists", FILE_OR_DIR_EXISTS: "File or folder already exists",
PORT_IS_OCCUPIED: "Port is occupied", PORT_IS_OCCUPIED: "Port is occupied",
//zerotier //zerotier
GET_TOKEN_ERROR: "Get token error,Please log in to zerotier's official website to confirm whether the account is available", GET_TOKEN_ERROR: "Get token error,Please log in to zerotier's official website to confirm whether the account is available",
//app //app
UNINSTALL_APP_ERROR: "uninstall app error", UNINSTALL_APP_ERROR: "Error uninstalling app",
PULL_IMAGE_ERROR: "pull image error", PULL_IMAGE_ERROR: "Error pulling image",
DEVICE_NOT_EXIST: "device not exist", DEVICE_NOT_EXIST: "Device does not exist",
//disk
NAME_NOT_AVAILABLE: "Name not available",
DISK_NEEDS_FORMAT: "Drive needs to be formatted",
REMOVE_MOUNT_POINT_ERROR: "Failed to remove mount point",
DISK_BUSYING: "Drive is busy",
FORMAT_ERROR: "Formatting failed, please check if the directory is occupied",
// //
FILE_DOES_NOT_EXIST: "file does not exist", FILE_DOES_NOT_EXIST: "File does not exist",
FILE_READ_ERROR: "file read error", FILE_READ_ERROR: "File read error",
SHORTCUTS_URL_ERROR: "url error", SHORTCUTS_URL_ERROR: "URL error",
} }
//获取错误信息 //获取错误信息

View File

@ -3,6 +3,7 @@ package route
import ( import (
"encoding/json" "encoding/json"
"encoding/xml" "encoding/xml"
"fmt"
"strconv" "strconv"
"time" "time"
@ -34,10 +35,9 @@ func installSyncthing(appId string) {
m := model.CustomizationPostData{} m := model.CustomizationPostData{}
var dockerImage string var dockerImage string
var dockerImageVersion string var dockerImageVersion string
appInfo = service.MyService.OAPI().GetServerAppInfo(appId, "system")
appInfo = service.MyService.OAPI().GetServerAppInfo(appId)
dockerImage = appInfo.Image dockerImage = appInfo.Image
dockerImageVersion = appInfo.ImageVersion
if len(appInfo.ImageVersion) == 0 { if len(appInfo.ImageVersion) == 0 {
dockerImageVersion = "latest" dockerImageVersion = "latest"
@ -84,9 +84,9 @@ func installSyncthing(appId string) {
err := service.MyService.Docker().DockerPullImage(dockerImage+":"+dockerImageVersion, installLog) err := service.MyService.Docker().DockerPullImage(dockerImage+":"+dockerImageVersion, installLog)
if err != nil { if err != nil {
//pull image error //pull image error
fmt.Println("pull image error", err, dockerImage, dockerImageVersion)
return return
} }
for !service.MyService.Docker().IsExistImage(dockerImage + ":" + dockerImageVersion) { for !service.MyService.Docker().IsExistImage(dockerImage + ":" + dockerImageVersion) {
time.Sleep(time.Second) time.Sleep(time.Second)
} }
@ -101,8 +101,8 @@ func installSyncthing(appId string) {
m.Volumes = appInfo.Volumes m.Volumes = appInfo.Volumes
containerId, err := service.MyService.Docker().DockerContainerCreate(dockerImage+":"+dockerImageVersion, id, m, appInfo.NetworkModel) containerId, err := service.MyService.Docker().DockerContainerCreate(dockerImage+":"+dockerImageVersion, id, m, appInfo.NetworkModel)
if err != nil { if err != nil {
fmt.Println("container create error", err)
// create container error // create container error
return return
} }
@ -170,7 +170,7 @@ func checkSystemApp() {
path := "" path := ""
for _, i := range paths { for _, i := range paths {
if i.ContainerPath == "/config" { if i.ContainerPath == "/config" {
path = docker.GetDir(v.CustomId, i.Path) + "config.xml" path = docker.GetDir(v.CustomId, i.Path) + "/config.xml"
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
if file.CheckNotExist(path) { if file.CheckNotExist(path) {
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
@ -197,21 +197,37 @@ func CheckSerialDiskMount() {
list := service.MyService.Disk().LSBLK() list := service.MyService.Disk().LSBLK()
mountPoint := make(map[string]string, len(dbList)) mountPoint := make(map[string]string, len(dbList))
//remount
for _, v := range dbList {
mountPoint[v.Path] = v.MountPoint
}
for _, v := range list { for _, v := range list {
command.ExecEnabledSMART(v.Path)
if v.Children != nil { if v.Children != nil {
for _, h := range v.Children { for _, h := range v.Children {
mountPoint[h.MountPoint] = "1" if len(h.MountPoint) == 0 && len(v.Children) == 1 && h.FsType == "ext4" {
if m, ok := mountPoint[h.Path]; ok {
//mount point check
volume := m
if !file.CheckNotExist(m) {
for i := 0; file.CheckNotExist(volume); i++ {
volume = m + strconv.Itoa(i+1)
}
}
service.MyService.Disk().MountDisk(h.Path, volume)
if volume != m {
ms := model2.SerialDisk{}
ms.Serial = v.Serial
service.MyService.Disk().UpdateMountPoint(ms)
}
}
}
} }
} }
} }
service.MyService.Disk().RemoveLSBLKCache()
//remount command.OnlyExec("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;AutoRemoveUnuseDir")
for _, item := range dbList {
if _, ok := mountPoint[item.MountPoint]; !ok {
service.MyService.Disk().MountDisk(item.Path, item.MountPoint)
}
}
} }
func Update2_3() { func Update2_3() {

View File

@ -218,17 +218,23 @@ func InitRouter() *gin.Engine {
v1DiskGroup.Use() v1DiskGroup.Use()
{ {
v1DiskGroup.GET("/check", v1.GetDiskCheck) v1DiskGroup.GET("/check", v1.GetDiskCheck)
//获取磁盘列表
v1DiskGroup.GET("/list", v1.GetPlugInDisk) v1DiskGroup.GET("/list", v1.GetDiskList)
//获取磁盘详情 //获取磁盘详情
v1DiskGroup.GET("/info", v1.GetDiskInfo) v1DiskGroup.GET("/info", v1.GetDiskInfo)
//格式化磁盘 //format storage
v1DiskGroup.POST("/format", v1.FormatDisk) v1DiskGroup.POST("/format", v1.FormatDisk)
//添加分区 // add storage
v1DiskGroup.POST("/part", v1.AddPartition) v1DiskGroup.POST("/storage", v1.AddPartition)
//mount SATA disk
v1DiskGroup.POST("/mount", v1.PostMountDisk)
//umount sata disk
v1DiskGroup.POST("/umount", v1.PostDiskUmount)
//获取可以格式化的内容 //获取可以格式化的内容
v1DiskGroup.GET("/type", v1.FormatDiskType) v1DiskGroup.GET("/type", v1.FormatDiskType)
@ -236,12 +242,6 @@ func InitRouter() *gin.Engine {
//删除分区 //删除分区
v1DiskGroup.DELETE("/delpart", v1.RemovePartition) v1DiskGroup.DELETE("/delpart", v1.RemovePartition)
//mount SATA disk
v1DiskGroup.POST("/mount", v1.PostMountDisk)
//umount SATA disk
v1DiskGroup.POST("/umount", v1.PostDiskUmount)
v1DiskGroup.DELETE("/remove/:id", v1.DeleteDisk)
} }
v1ShareGroup := v1Group.Group("/share") v1ShareGroup := v1Group.Group("/share")
v1ShareGroup.Use() v1ShareGroup.Use()

View File

@ -36,24 +36,24 @@ func AppList(c *gin.Context) {
categoryId := c.DefaultQuery("category_id", "0") categoryId := c.DefaultQuery("category_id", "0")
key := c.DefaultQuery("key", "") key := c.DefaultQuery("key", "")
recommend, list, community := service.MyService.OAPI().GetServerList(index, size, t, categoryId, key) recommend, list, community := service.MyService.OAPI().GetServerList(index, size, t, categoryId, key)
for i := 0; i < len(recommend); i++ { // for i := 0; i < len(recommend); i++ {
ct, _ := service.MyService.Docker().DockerListByImage(recommend[i].Image, recommend[i].ImageVersion) // ct, _ := service.MyService.Docker().DockerListByImage(recommend[i].Image, recommend[i].ImageVersion)
if ct != nil { // if ct != nil {
list[i].State = ct.State // recommend[i].State = ct.State
} // }
} // }
for i := 0; i < len(list); i++ { // for i := 0; i < len(list); i++ {
ct, _ := service.MyService.Docker().DockerListByImage(list[i].Image, list[i].ImageVersion) // ct, _ := service.MyService.Docker().DockerListByImage(list[i].Image, list[i].ImageVersion)
if ct != nil { // if ct != nil {
list[i].State = ct.State // list[i].State = ct.State
} // }
} // }
for i := 0; i < len(community); i++ { // for i := 0; i < len(community); i++ {
ct, _ := service.MyService.Docker().DockerListByImage(community[i].Image, community[i].ImageVersion) // ct, _ := service.MyService.Docker().DockerListByImage(community[i].Image, community[i].ImageVersion)
if ct != nil { // if ct != nil {
list[i].State = ct.State // community[i].State = ct.State
} // }
} // }
data := make(map[string]interface{}, 3) data := make(map[string]interface{}, 3)
data["recommend"] = recommend data["recommend"] = recommend
data["list"] = list data["list"] = list
@ -137,7 +137,7 @@ func AppUsageList(c *gin.Context) {
func AppInfo(c *gin.Context) { func AppInfo(c *gin.Context) {
id := c.Param("id") id := c.Param("id")
info := service.MyService.OAPI().GetServerAppInfo(id) info := service.MyService.OAPI().GetServerAppInfo(id, "")
if info.NetworkModel != "host" { if info.NetworkModel != "host" {
for i := 0; i < len(info.Ports); i++ { for i := 0; i < len(info.Ports); i++ {
if p, _ := strconv.Atoi(info.Ports[i].ContainerPort); port2.IsPortAvailable(p, info.Ports[i].Protocol) { if p, _ := strconv.Atoi(info.Ports[i].ContainerPort); port2.IsPortAvailable(p, info.Ports[i].Protocol) {

View File

@ -2,9 +2,13 @@ package v1
import ( import (
"net/http" "net/http"
"reflect"
"strconv" "strconv"
"strings"
"github.com/IceWhaleTech/CasaOS/model" "github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err" "github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
"github.com/IceWhaleTech/CasaOS/service" "github.com/IceWhaleTech/CasaOS/service"
model2 "github.com/IceWhaleTech/CasaOS/service/model" model2 "github.com/IceWhaleTech/CasaOS/service/model"
@ -12,18 +16,53 @@ import (
"github.com/shirou/gopsutil/v3/disk" "github.com/shirou/gopsutil/v3/disk"
) )
// @Summary 获取磁盘列表 var diskMap = make(map[string]string)
// @Summary disk list
// @Produce application/json // @Produce application/json
// @Accept application/json // @Accept application/json
// @Tags disk // @Tags disk
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Success 200 {string} string "ok" // @Success 200 {string} string "ok"
// @Router /disk/list [get] // @Router /disk/list [get]
func GetPlugInDisk(c *gin.Context) { func GetDiskList(c *gin.Context) {
list := service.MyService.Disk().LSBLK() list := service.MyService.Disk().LSBLK()
newList := []model.LSBLKModel{}
for i := len(list) - 1; i >= 0; i-- {
if list[i].Rota {
list[i].DiskType = "HDD"
} else {
list[i].DiskType = "SSD"
}
if list[i].Tran == "sata" {
temp := service.MyService.Disk().SmartCTL(list[i].Path)
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS), Data: list}) if reflect.DeepEqual(temp, model.SmartctlA{}) {
continue
}
if len(list[i].Children) == 1 && len(list[i].Children[0].MountPoint) > 0 {
pathArr := strings.Split(list[i].Children[0].MountPoint, "/")
if len(pathArr) == 3 {
list[i].Children[0].Name = pathArr[2]
}
}
list[i].Temperature = temp.Temperature.Current
list[i].Health = strconv.FormatBool(temp.SmartStatus.Passed)
newList = append(newList, list[i])
} else if len(list[i].Children) > 0 && list[i].Children[0].MountPoint == "/" {
//system
list[i].Children[0].Name = "System"
list[i].Model = "System"
list[i].DiskType = "EMMC"
list[i].Health = "true"
newList = append(newList, list[i])
}
}
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS), Data: newList})
} }
// @Summary get disk list // @Summary get disk list
@ -60,25 +99,46 @@ func GetDiskInfo(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS), Data: m}) c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS), Data: m})
} }
// @Summary format disk // @Summary format storage
// @Produce application/json // @Produce application/json
// @Accept multipart/form-data // @Accept multipart/form-data
// @Tags disk // @Tags disk
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Param path formData string true "for example /dev/sda1" // @Param path formData string true "e.g. /dev/sda1"
// @Param pwd formData string true "user password"
// @Param volume formData string true "mount point"
// @Success 200 {string} string "ok" // @Success 200 {string} string "ok"
// @Router /disk/format [post] // @Router /disk/format [post]
func FormatDisk(c *gin.Context) { func FormatDisk(c *gin.Context) {
path := c.PostForm("path") path := c.PostForm("path")
t := "ext4"
pwd := c.PostForm("pwd")
volume := c.PostForm("volume")
t := c.PostForm("type") if pwd != config.UserInfo.PWD {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.PWD_INVALID, Message: oasis_err.GetMsg(oasis_err.PWD_INVALID)})
return
}
if len(path) == 0 || len(t) == 0 { if len(path) == 0 || len(t) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.INVALID_PARAMS, Message: oasis_err.GetMsg(oasis_err.INVALID_PARAMS)}) c.JSON(http.StatusOK, model.Result{Success: oasis_err.INVALID_PARAMS, Message: oasis_err.GetMsg(oasis_err.INVALID_PARAMS)})
return return
} }
service.MyService.Disk().FormatDisk(path, t) if _, ok := diskMap[path]; ok {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.DISK_BUSYING, Message: oasis_err.GetMsg(oasis_err.DISK_BUSYING)})
return
}
diskMap[path] = "busying"
service.MyService.Disk().UmountPointAndRemoveDir(path)
format := service.MyService.Disk().FormatDisk(path, t)
if len(format) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.FORMAT_ERROR, Message: oasis_err.GetMsg(oasis_err.FORMAT_ERROR)})
delete(diskMap, path)
return
}
service.MyService.Disk().MountDisk(path, volume)
service.MyService.Disk().RemoveLSBLKCache()
delete(diskMap, path)
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)}) c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)})
} }
@ -115,23 +175,64 @@ func RemovePartition(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)}) c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)})
} }
// @Summary serial number // @Summary add storage
// @Produce application/json // @Produce application/json
// @Accept multipart/form-data // @Accept multipart/form-data
// @Tags disk // @Tags disk
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Param path formData string true "磁盘路径 例如/dev/sda" // @Param path formData string true "disk path e.g. /dev/sda"
// @Param serial formData string true "serial" // @Param serial formData string true "serial"
// @Param name formData string true "name"
// @Param format formData bool true "need format(true)"
// @Success 200 {string} string "ok" // @Success 200 {string} string "ok"
// @Router /disk/addpart [post] // @Router /disk/storage [post]
func AddPartition(c *gin.Context) { func AddPartition(c *gin.Context) {
name := c.PostForm("name")
path := c.PostForm("path") path := c.PostForm("path")
serial := c.PostForm("serial") serial := c.PostForm("serial")
if len(path) == 0 || len(serial) == 0 { format, _ := strconv.ParseBool(c.PostForm("format"))
if len(name) == 0 || len(path) == 0 || len(serial) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.INVALID_PARAMS, Message: oasis_err.GetMsg(oasis_err.INVALID_PARAMS)}) c.JSON(http.StatusOK, model.Result{Success: oasis_err.INVALID_PARAMS, Message: oasis_err.GetMsg(oasis_err.INVALID_PARAMS)})
return return
} }
service.MyService.Disk().AddPartition(path) if _, ok := diskMap[serial]; ok {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.DISK_BUSYING, Message: oasis_err.GetMsg(oasis_err.DISK_BUSYING)})
return
}
if !file.CheckNotExist("/mnt/" + name) {
// /mnt/name exist
c.JSON(http.StatusOK, model.Result{Success: oasis_err.NAME_NOT_AVAILABLE, Message: oasis_err.GetMsg(oasis_err.NAME_NOT_AVAILABLE)})
return
}
diskMap[serial] = "busying"
currentDisk := service.MyService.Disk().GetDiskInfo(path)
if !format {
if len(currentDisk.Children) != 1 || !(len(currentDisk.Children) > 0 && currentDisk.Children[0].FsType == "ext4") {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.DISK_NEEDS_FORMAT, Message: oasis_err.GetMsg(oasis_err.DISK_NEEDS_FORMAT)})
delete(diskMap, serial)
return
}
} else {
service.MyService.Disk().AddPartition(path)
}
mountPath := "/mnt/" + name
service.MyService.Disk().MountDisk(path, mountPath)
m := model2.SerialDisk{}
m.MountPoint = mountPath
m.Path = path + "1"
m.Serial = serial
m.State = 0
service.MyService.Disk().SaveMountPoint(m)
//mount dir
service.MyService.Disk().MountDisk(path+"1", mountPath)
service.MyService.Disk().RemoveLSBLKCache()
delete(diskMap, serial)
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)}) c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)})
} }
@ -165,13 +266,13 @@ func PostMountDisk(c *gin.Context) {
//mount dir //mount dir
service.MyService.Disk().MountDisk(path, mountPath) service.MyService.Disk().MountDisk(path, mountPath)
//save to data
m := model2.SerialDisk{} m := model2.SerialDisk{}
m.MountPoint = mountPath m.MountPoint = mountPath
m.Path = path m.Path = path
m.Serial = serial m.Serial = serial
m.State = 0 m.State = 0
service.MyService.Disk().SaveMountPoint(m) //service.MyService.Disk().SaveMountPoint(m)
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)}) c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)})
} }
@ -180,19 +281,35 @@ func PostMountDisk(c *gin.Context) {
// @Accept multipart/form-data // @Accept multipart/form-data
// @Tags disk // @Tags disk
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Param path formData string true "for example: /dev/sda1" // @Param path formData string true "e.g. /dev/sda1"
// @Param mount_point formData string true "for example: /mnt/volume1" // @Param mount_point formData string true "e.g. /mnt/volume1"
// @Param pwd formData string true "user password"
// @Success 200 {string} string "ok" // @Success 200 {string} string "ok"
// @Router /disk/umount [post] // @Router /disk/umount [post]
func PostDiskUmount(c *gin.Context) { func PostDiskUmount(c *gin.Context) {
//
path := c.PostForm("path") path := c.PostForm("path")
mountPoint := c.PostForm("mount_point") mountPoint := c.PostForm("volume")
service.MyService.Disk().UmountPointAndRemoveDir(path) pwd := c.PostForm("pwd")
if len(path) == 0 || len(mountPoint) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.INVALID_PARAMS, Message: oasis_err.GetMsg(oasis_err.INVALID_PARAMS)})
return
}
if pwd != config.UserInfo.PWD {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.PWD_INVALID, Message: oasis_err.GetMsg(oasis_err.PWD_INVALID)})
return
}
if _, ok := diskMap[path]; ok {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.DISK_BUSYING, Message: oasis_err.GetMsg(oasis_err.DISK_BUSYING)})
return
}
service.MyService.Disk().UmountPointAndRemoveDir(path)
//delete data //delete data
service.MyService.Disk().DeleteMountPoint(path, mountPoint) service.MyService.Disk().DeleteMountPoint(path, mountPoint)
service.MyService.Disk().RemoveLSBLKCache()
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)}) c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)})
} }

View File

@ -174,7 +174,7 @@ func InstallApp(c *gin.Context) {
dockerImageVersion = "latest" dockerImageVersion = "latest"
} }
if m.Origin != "custom" { if m.Origin != "custom" {
appInfo = service.MyService.OAPI().GetServerAppInfo(appId) appInfo = service.MyService.OAPI().GetServerAppInfo(appId, "")
} else { } else {

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"os" "os"
"reflect"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -107,9 +108,33 @@ func GetSystemConfigDebug(c *gin.Context) {
array := service.MyService.System().GetSystemConfigDebug() array := service.MyService.System().GetSystemConfigDebug()
disk := service.MyService.ZiMa().GetDiskInfo() disk := service.MyService.ZiMa().GetDiskInfo()
array = append(array, fmt.Sprintf("disk,total:%v,used:%v,UsedPercent:%v", disk.Total>>20, disk.Used>>20, disk.UsedPercent)) sys := service.MyService.ZiMa().GetSysInfo()
//todo 准备sync需要显示的数据(镜像,容器)
var systemAppStatus string
images := service.MyService.Docker().IsExistImage("linuxserver/syncthing")
systemAppStatus += "Sync img: " + strconv.FormatBool(images) + "\n\t"
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS), Data: array}) list := service.MyService.App().GetSystemAppList()
for _, v := range *list {
systemAppStatus += v.Image + ",\n\t"
}
systemAppStatus += "Sync Key: " + config.SystemConfigInfo.SyncKey + "\n\t"
var bugContent string = fmt.Sprintf(`
**Desktop (please complete the following information):**
- OS: %s
- CasaOS Version: %s
- Disk Total: %v
- Disk Used: %v
- Sync State: %s
- System Info: %s
- Browser: $Browser$
- Version: $Version$
`, sys.OS, types.CURRENTVERSION, disk.Total>>20, disk.Used>>20, systemAppStatus, array)
// array = append(array, fmt.Sprintf("disk,total:%v,used:%v,UsedPercent:%v", disk.Total>>20, disk.Used>>20, disk.UsedPercent))
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS), Data: bugContent})
} }
func Sys(c *gin.Context) { func Sys(c *gin.Context) {
service.DockerPull() service.DockerPull()
@ -237,7 +262,42 @@ func Info(c *gin.Context) {
var data = make(map[string]interface{}, 5) var data = make(map[string]interface{}, 5)
list := service.MyService.Disk().LSBLK() list := service.MyService.Disk().LSBLK()
data["disk"] = list
newList := []model.LSBLKModel{}
for i := len(list) - 1; i >= 0; i-- {
if list[i].Rota {
list[i].DiskType = "HDD"
} else {
list[i].DiskType = "SSD"
}
if list[i].Tran == "sata" {
temp := service.MyService.Disk().SmartCTL(list[i].Path)
if reflect.DeepEqual(temp, model.SmartctlA{}) {
continue
}
if len(list[i].Children) == 1 && len(list[i].Children[0].MountPoint) > 0 {
pathArr := strings.Split(list[i].Children[0].MountPoint, "/")
if len(pathArr) == 3 {
list[i].Children[0].Name = pathArr[2]
}
}
list[i].Temperature = temp.Temperature.Current
list[i].Health = strconv.FormatBool(temp.SmartStatus.Passed)
newList = append(newList, list[i])
} else if len(list[i].Children) > 0 && list[i].Children[0].MountPoint == "/" {
//system
list[i].Children[0].Name = "System"
list[i].Model = "System"
list[i].DiskType = "EMMC"
list[i].Health = "true"
newList = append(newList, list[i])
}
}
data["disk"] = newList
cpu := service.MyService.ZiMa().GetCpuPercent() cpu := service.MyService.ZiMa().GetCpuPercent()
num := service.MyService.ZiMa().GetCpuCoreNum() num := service.MyService.ZiMa().GetCpuCoreNum()
cpuData := make(map[string]interface{}) cpuData := make(map[string]interface{})

View File

@ -16,7 +16,7 @@ type CasaService interface {
GetServerList(index, size, tp, categoryId, key string) (recommend, list, community []model.ServerAppList) GetServerList(index, size, tp, categoryId, key string) (recommend, list, community []model.ServerAppList)
GetServerCategoryList() []model.ServerCategoryList GetServerCategoryList() []model.ServerCategoryList
GetTaskList(size int) []model2.TaskDBModel GetTaskList(size int) []model2.TaskDBModel
GetServerAppInfo(id string) model.ServerAppList GetServerAppInfo(id, t string) model.ServerAppList
ShareAppFile(body []byte) string ShareAppFile(body []byte) string
} }
@ -88,13 +88,12 @@ func (o *casaService) GetServerCategoryList() []model.ServerCategoryList {
return list return list
} }
func (o *casaService) GetServerAppInfo(id string) model.ServerAppList { func (o *casaService) GetServerAppInfo(id, t string) model.ServerAppList {
head := make(map[string]string) head := make(map[string]string)
head["Authorization"] = GetToken() head["Authorization"] = GetToken()
infoS := httper2.Get(config.ServerInfo.ServerApi+"/v2/app/info/"+id+"?t="+t, head)
infoS := httper2.Get(config.ServerInfo.ServerApi+"/v2/app/info/"+id, head)
info := model.ServerAppList{} info := model.ServerAppList{}
json2.Unmarshal([]byte(gjson.Get(infoS, "data").String()), &info) json2.Unmarshal([]byte(gjson.Get(infoS, "data").String()), &info)

View File

@ -3,6 +3,7 @@ package service
import ( import (
json2 "encoding/json" json2 "encoding/json"
"fmt" "fmt"
"reflect"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -20,8 +21,9 @@ import (
type DiskService interface { type DiskService interface {
GetPlugInDisk() []string GetPlugInDisk() []string
LSBLK() []model.LSBLKModel LSBLK() []model.LSBLKModel
FormatDisk(path, format string) string SmartCTL(path string) model.SmartctlA
UmountPointAndRemoveDir(path string) string FormatDisk(path, format string) []string
UmountPointAndRemoveDir(path string) []string
GetDiskInfo(path string) model.LSBLKModel GetDiskInfo(path string) model.LSBLKModel
DelPartition(path, num string) string DelPartition(path, num string) string
AddPartition(path string) string AddPartition(path string) string
@ -31,30 +33,60 @@ type DiskService interface {
SaveMountPoint(m model2.SerialDisk) SaveMountPoint(m model2.SerialDisk)
DeleteMountPoint(path, mountPoint string) DeleteMountPoint(path, mountPoint string)
DeleteMount(id string) DeleteMount(id string)
UpdateMountPoint(m model2.SerialDisk)
RemoveLSBLKCache()
} }
type diskService struct { type diskService struct {
log loger2.OLog log loger2.OLog
db *gorm.DB db *gorm.DB
} }
func (d *diskService) RemoveLSBLKCache() {
key := "system_lsblk"
Cache.Delete(key)
}
func (d *diskService) SmartCTL(path string) model.SmartctlA {
key := "system_smart_" + path
if result, ok := Cache.Get(key); ok {
res, ok := result.(model.SmartctlA)
if ok {
return res
}
}
var m model.SmartctlA
str := command2.ExecSmartCTLByPath(path)
if str == nil {
d.log.Error("smartctl exec error,smartctl")
return m
}
err := json2.Unmarshal([]byte(str), &m)
if err != nil {
d.log.Error("json ummarshal error", err)
}
if !reflect.DeepEqual(m, model.SmartctlA{}) {
Cache.Add(key, m, time.Second*10)
}
return m
}
//通过脚本获取外挂磁盘 //通过脚本获取外挂磁盘
func (d *diskService) GetPlugInDisk() []string { func (d *diskService) GetPlugInDisk() []string {
return command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetPlugInDisk") return command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetPlugInDisk")
} }
//格式化硬盘 //格式化硬盘
func (d *diskService) FormatDisk(path, format string) string { func (d *diskService) FormatDisk(path, format string) []string {
r := command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;FormatDisk " + path + " " + format) r := command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;FormatDisk " + path + " " + format)
fmt.Println(r) return r
return ""
} }
//移除挂载点,删除目录 //移除挂载点,删除目录
func (d *diskService) UmountPointAndRemoveDir(path string) string { func (d *diskService) UmountPointAndRemoveDir(path string) []string {
r := command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;UMountPorintAndRemoveDir " + path) r := command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;UMountPorintAndRemoveDir " + path)
fmt.Println(r) return r
return ""
} }
//删除分区 //删除分区
@ -66,8 +98,7 @@ func (d *diskService) DelPartition(path, num string) string {
//part //part
func (d *diskService) AddPartition(path string) string { func (d *diskService) AddPartition(path string) string {
r := command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;AddPartition " + path) command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;AddPartition " + path)
fmt.Println(r)
return "" return ""
} }
@ -78,11 +109,10 @@ func (d *diskService) AddAllPartition(path string) {
//获取硬盘详情 //获取硬盘详情
func (d *diskService) GetDiskInfoByPath(path string) *disk.UsageStat { func (d *diskService) GetDiskInfoByPath(path string) *disk.UsageStat {
diskInfo, err := disk.Usage(path + "1") diskInfo, err := disk.Usage(path + "1")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
fmt.Println(path)
fmt.Println(diskInfo)
diskInfo.UsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", diskInfo.UsedPercent), 64) diskInfo.UsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", diskInfo.UsedPercent), 64)
diskInfo.InodesUsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", diskInfo.InodesUsedPercent), 64) diskInfo.InodesUsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", diskInfo.InodesUsedPercent), 64)
return diskInfo return diskInfo
@ -91,7 +121,6 @@ func (d *diskService) GetDiskInfoByPath(path string) *disk.UsageStat {
//get disk details //get disk details
func (d *diskService) LSBLK() []model.LSBLKModel { func (d *diskService) LSBLK() []model.LSBLKModel {
key := "system_lsblk" key := "system_lsblk"
var n []model.LSBLKModel var n []model.LSBLKModel
if result, ok := Cache.Get(key); ok { if result, ok := Cache.Get(key); ok {
@ -151,7 +180,7 @@ func (d *diskService) LSBLK() []model.LSBLKModel {
} }
} }
if len(n) > 0 { if len(n) > 0 {
Cache.Add(key, n, time.Second*10) Cache.Add(key, n, time.Second*100)
} }
return n return n
} }
@ -162,6 +191,7 @@ func (d *diskService) GetDiskInfo(path string) model.LSBLKModel {
d.log.Error("lsblk exec error,str") d.log.Error("lsblk exec error,str")
return model.LSBLKModel{} return model.LSBLKModel{}
} }
var ml []model.LSBLKModel var ml []model.LSBLKModel
err := json2.Unmarshal([]byte(gjson.Get(string(str), "blockdevices").String()), &ml) err := json2.Unmarshal([]byte(gjson.Get(string(str), "blockdevices").String()), &ml)
if err != nil { if err != nil {
@ -169,9 +199,13 @@ func (d *diskService) GetDiskInfo(path string) model.LSBLKModel {
d.log.Error("json ummarshal error", err) d.log.Error("json ummarshal error", err)
return model.LSBLKModel{} return model.LSBLKModel{}
} }
//todo 需要判断长度
m := ml[0] m := model.LSBLKModel{}
//声明数组 if len(ml) > 0 {
m = ml[0]
}
return m
// 下面为计算是否可以继续分区的部分,暂时不需要
chiArr := make(map[string]string) chiArr := make(map[string]string)
chiList := command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetPartitionSectors " + m.Path) chiList := command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetPartitionSectors " + m.Path)
if len(chiList) == 0 { if len(chiList) == 0 {
@ -182,7 +216,6 @@ func (d *diskService) GetDiskInfo(path string) model.LSBLKModel {
tempArr := strings.Split(chiList[i], ",") tempArr := strings.Split(chiList[i], ",")
chiArr[tempArr[0]] = chiList[i] chiArr[tempArr[0]] = chiList[i]
} }
var maxSector uint64 = 0 var maxSector uint64 = 0
for i := 0; i < len(m.Children); i++ { for i := 0; i < len(m.Children); i++ {
tempArr := strings.Split(chiArr[m.Children[i].Path], ",") tempArr := strings.Split(chiArr[m.Children[i].Path], ",")
@ -191,13 +224,13 @@ func (d *diskService) GetDiskInfo(path string) model.LSBLKModel {
if m.Children[i].EndSector > maxSector { if m.Children[i].EndSector > maxSector {
maxSector = m.Children[i].EndSector maxSector = m.Children[i].EndSector
} }
} }
diskEndSector := command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetDiskSizeAndSectors " + m.Path) diskEndSector := command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetDiskSizeAndSectors " + m.Path)
if len(diskEndSector) < 2 { if len(diskEndSector) < 2 {
d.log.Error("diskEndSector length error") d.log.Error("diskEndSector length error")
} }
diskEndSectorInt, _ := strconv.ParseUint(diskEndSector[len(diskEndSector)-1], 10, 64) diskEndSectorInt, _ := strconv.ParseUint(diskEndSector[len(diskEndSector)-1], 10, 64)
if (diskEndSectorInt-maxSector)*m.MinIO/1024/1024 > 100 { if (diskEndSectorInt-maxSector)*m.MinIO/1024/1024 > 100 {
//添加可以分区情况 //添加可以分区情况
@ -214,9 +247,14 @@ func (d *diskService) MountDisk(path, volume string) {
} }
func (d *diskService) SaveMountPoint(m model2.SerialDisk) { func (d *diskService) SaveMountPoint(m model2.SerialDisk) {
d.db.Where("serial = ?", m.Serial).Delete(&model2.SerialDisk{})
d.db.Create(&m) d.db.Create(&m)
} }
func (d *diskService) UpdateMountPoint(m model2.SerialDisk) {
d.db.Model(&model2.SerialDisk{}).Where("serial = ?", m.Serial).Update("mount_point", m.MountPoint)
}
func (d *diskService) DeleteMount(id string) { func (d *diskService) DeleteMount(id string) {
d.db.Delete(&model2.SerialDisk{}).Where("id = ?", id) d.db.Delete(&model2.SerialDisk{}).Where("id = ?", id)
@ -224,7 +262,7 @@ func (d *diskService) DeleteMount(id string) {
func (d *diskService) DeleteMountPoint(path, mountPoint string) { func (d *diskService) DeleteMountPoint(path, mountPoint string) {
d.db.Delete(&model2.SerialDisk{}).Where("path= ? && mount_point = ?", path, mountPoint) d.db.Where("path = ? AND mount_point = ?", path, mountPoint).Delete(&model2.SerialDisk{})
command2.OnlyExec("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;do_umount " + path) command2.OnlyExec("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;do_umount " + path)
} }

View File

@ -9,4 +9,33 @@ version_0_2_3() {
} }
# add in v0.2.5
readonly CASA_DEPANDS="curl smartmontools"
version_0_2_5{
install_depends "$CASA_DEPANDS"
}
#Install Depends
install_depends() {
((EUID)) && sudo_cmd="sudo"
if [[ ! -x "$(command -v '$1')" ]]; then
show 2 "Install the necessary dependencies: $1"
packagesNeeded=$1
if [ -x "$(command -v apk)" ]; then
$sudo_cmd apk add --no-cache $packagesNeeded
elif [ -x "$(command -v apt-get)" ]; then
$sudo_cmd apt-get -y -q install $packagesNeeded
elif [ -x "$(command -v dnf)" ]; then
$sudo_cmd dnf install $packagesNeeded
elif [ -x "$(command -v zypper)" ]; then
$sudo_cmd zypper install $packagesNeeded
else
show 1 "Package manager not found. You must manually install: $packagesNeeded"
fi
fi
}
version_0_2_3 version_0_2_3
version_0_2_5

View File

@ -73,10 +73,8 @@ UMountPorintAndRemoveDir() {
if [[ -z ${MOUNT_POINT} ]]; then if [[ -z ${MOUNT_POINT} ]]; then
${log} "Warning: ${DEVICE} is not mounted" ${log} "Warning: ${DEVICE} is not mounted"
else else
umount -l ${DEVICE} umount -lf ${DEVICE}
${log} "Unmounted ${DEVICE} from ${MOUNT_POINT}"
/bin/rmdir "${MOUNT_POINT}" /bin/rmdir "${MOUNT_POINT}"
sed -i.bak "\@${MOUNT_POINT}@d" /var/log/usb-mount.track
fi fi
} }
@ -89,11 +87,11 @@ FormatDisk() {
elif [ "$2" == "ntfs" ]; then elif [ "$2" == "ntfs" ]; then
mkfs.ntfs $1 mkfs.ntfs $1
elif [ "$2" == "ext4" ]; then elif [ "$2" == "ext4" ]; then
mkfs.ext4 -F $1 mkfs.ext4 -m 1 -F $1
elif [ "$2" == "exfat" ]; then elif [ "$2" == "exfat" ]; then
mkfs.exfat $1 mkfs.exfat $1
else else
mkfs.ext4 -F $1 mkfs.ext4 -m 1 -F $1
fi fi
} }
@ -118,12 +116,10 @@ AddPartition() {
parted -s $1 mkpart primary ext4 0 100% parted -s $1 mkpart primary ext4 0 100%
mkfs.ext4 $11 mkfs.ext4 -m 1 $11
partprobe $1 partprobe $1
# mount $11 $2
} }
#磁盘类型 #磁盘类型
@ -148,14 +144,14 @@ GetDiskHealthState() {
#result bytes #result bytes
#result sectors #result sectors
GetDiskSizeAndSectors() { GetDiskSizeAndSectors() {
fdisk $1 -l | grep "/dev/sda:" | awk -F, 'BEGIN {OFS="\n"}{print $2,$3}' | awk '{print $1}' fdisk $1 -l | grep "$1:" | awk -F, 'BEGIN {OFS="\n"}{print $2,$3}' | awk '{print $1}'
} }
#获取磁盘分区数据扇区 #获取磁盘分区数据扇区
#param 磁盘路径 /dev/sda #param 磁盘路径 /dev/sda
#result start,end,sectors #result start,end,sectors
GetPartitionSectors() { GetPartitionSectors() {
fdisk $1 -l | grep "/dev/sda[1-9]" | awk 'BEGIN{OFS=","}{print $1,$2,$3,$4}' fdisk $1 -l | grep "$1[1-9]" | awk 'BEGIN{OFS=","}{print $1,$2,$3,$4}'
} }
#检查没有使用的挂载点删除文件夹 #检查没有使用的挂载点删除文件夹

View File

@ -1,4 +1,4 @@
package types package types
const CURRENTVERSION = "0.2.4" const CURRENTVERSION = "0.2.5"
const BODY = "<li>New App Store</li><li>Fix minor bugs</li>" const BODY = "<li>Storage Manager</li><li>File synchronization issues</li><li>Fix the app store classification problem</li>"