system.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. package service
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "io/ioutil"
  7. net2 "net"
  8. "os"
  9. "os/exec"
  10. "path/filepath"
  11. "runtime"
  12. "strconv"
  13. "strings"
  14. "time"
  15. "github.com/IceWhaleTech/CasaOS-Common/utils/file"
  16. "github.com/IceWhaleTech/CasaOS-Common/utils/logger"
  17. "github.com/IceWhaleTech/CasaOS/common"
  18. "github.com/IceWhaleTech/CasaOS/model"
  19. "github.com/IceWhaleTech/CasaOS/pkg/config"
  20. command2 "github.com/IceWhaleTech/CasaOS/pkg/utils/command"
  21. "github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
  22. "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
  23. "github.com/IceWhaleTech/CasaOS/pkg/utils/ip_helper"
  24. "github.com/tidwall/gjson"
  25. "go.uber.org/zap"
  26. "github.com/shirou/gopsutil/v3/cpu"
  27. "github.com/shirou/gopsutil/v3/disk"
  28. "github.com/shirou/gopsutil/v3/host"
  29. "github.com/shirou/gopsutil/v3/mem"
  30. "github.com/shirou/gopsutil/v3/net"
  31. )
  32. type SystemService interface {
  33. UpdateSystemVersion(version string)
  34. GetSystemConfigDebug() []string
  35. GetCasaOSLogs(lineNumber int) string
  36. UpdateAssist()
  37. UpSystemPort(port string)
  38. GetTimeZone() string
  39. UpAppOrderFile(str, id string)
  40. GetAppOrderFile(id string) []byte
  41. GetNet(physics bool) []string
  42. GetNetInfo() []net.IOCountersStat
  43. GetCpuCoreNum() int
  44. GetCpuPercent() float64
  45. GetMemInfo() map[string]interface{}
  46. GetCpuInfo() []cpu.InfoStat
  47. GetDirPath(path string) ([]model.Path, error)
  48. GetDirPathOne(path string) (m model.Path)
  49. GetNetState(name string) string
  50. GetDiskInfo() *disk.UsageStat
  51. GetSysInfo() host.InfoStat
  52. GetDeviceTree() string
  53. GetDeviceInfo() model.DeviceInfo
  54. CreateFile(path string) (int, error)
  55. RenameFile(oldF, newF string) (int, error)
  56. MkdirAll(path string) (int, error)
  57. GetCPUTemperature() int
  58. GetCPUPower() map[string]string
  59. GetMacAddress() (string, error)
  60. SystemReboot() error
  61. SystemShutdown() error
  62. GetSystemEntry() string
  63. GenreateSystemEntry()
  64. }
  65. type systemService struct{}
  66. func (c *systemService) GetDeviceInfo() model.DeviceInfo {
  67. m := model.DeviceInfo{}
  68. m.OS_Version = common.VERSION
  69. err, portStr := MyService.Gateway().GetPort()
  70. if err != nil {
  71. m.Port = 80
  72. } else {
  73. port := gjson.Get(portStr, "data")
  74. if len(port.Raw) == 0 {
  75. m.Port = 80
  76. } else {
  77. p, err := strconv.Atoi(port.Raw)
  78. if err != nil {
  79. m.Port = 80
  80. } else {
  81. m.Port = p
  82. }
  83. }
  84. }
  85. allIpv4 := ip_helper.GetDeviceAllIPv4()
  86. ip := []string{}
  87. nets := MyService.System().GetNet(true)
  88. for _, n := range nets {
  89. if v, ok := allIpv4[n]; ok {
  90. {
  91. ip = append(ip, v)
  92. }
  93. }
  94. }
  95. m.LanIpv4 = ip
  96. h, err := host.Info() /* */
  97. if err == nil {
  98. m.DeviceName = h.Hostname
  99. }
  100. mb := model.BaseInfo{}
  101. err = json.Unmarshal(file.ReadFullFile(config.AppInfo.DBPath+"/baseinfo.conf"), &mb)
  102. if err == nil {
  103. m.Hash = mb.Hash
  104. }
  105. osRelease, _ := file.ReadOSRelease()
  106. m.DeviceModel = osRelease["MODEL"]
  107. m.DeviceSN = osRelease["SN"]
  108. res := httper.Get("http://127.0.0.1:"+strconv.Itoa(m.Port)+"/v1/users/status", nil)
  109. init := gjson.Get(res, "data.initialized")
  110. m.Initialized, _ = strconv.ParseBool(init.Raw)
  111. return m
  112. }
  113. func (c *systemService) GenreateSystemEntry() {
  114. modelsPath := "/var/lib/casaos/www/modules"
  115. entryFileName := "entry.json"
  116. entryFilePath := filepath.Join(config.AppInfo.DBPath, "db", entryFileName)
  117. file.IsNotExistCreateFile(entryFilePath)
  118. dir, err := os.ReadDir(modelsPath)
  119. if err != nil {
  120. logger.Error("read dir error", zap.Error(err))
  121. return
  122. }
  123. json := "["
  124. for _, v := range dir {
  125. data, err := os.ReadFile(filepath.Join(modelsPath, v.Name(), entryFileName))
  126. if err != nil {
  127. logger.Error("read entry file error", zap.Error(err))
  128. continue
  129. }
  130. json += string(data) + ","
  131. }
  132. json = strings.TrimRight(json, ",")
  133. json += "]"
  134. err = os.WriteFile(entryFilePath, []byte(json), 0666)
  135. if err != nil {
  136. logger.Error("write entry file error", zap.Error(err))
  137. return
  138. }
  139. }
  140. func (c *systemService) GetSystemEntry() string {
  141. modelsPath := "/var/lib/casaos/www/modules"
  142. entryFileName := "entry.json"
  143. dir, err := os.ReadDir(modelsPath)
  144. if err != nil {
  145. logger.Error("read dir error", zap.Error(err))
  146. return ""
  147. }
  148. json := "["
  149. for _, v := range dir {
  150. data, err := os.ReadFile(filepath.Join(modelsPath, v.Name(), entryFileName))
  151. if err != nil {
  152. logger.Error("read entry file error", zap.Error(err))
  153. continue
  154. }
  155. json += string(data) + ","
  156. }
  157. json = strings.TrimRight(json, ",")
  158. json += "]"
  159. if err != nil {
  160. logger.Error("write entry file error", zap.Error(err))
  161. return ""
  162. }
  163. return json
  164. }
  165. func (c *systemService) GetMacAddress() (string, error) {
  166. interfaces, err := net.Interfaces()
  167. if err != nil {
  168. return "", err
  169. }
  170. nets := MyService.System().GetNet(true)
  171. for _, v := range interfaces {
  172. for _, n := range nets {
  173. if v.Name == n {
  174. return v.HardwareAddr, nil
  175. }
  176. }
  177. }
  178. return "", errors.New("not found")
  179. }
  180. func (c *systemService) MkdirAll(path string) (int, error) {
  181. _, err := os.Stat(path)
  182. if err == nil {
  183. return common_err.DIR_ALREADY_EXISTS, nil
  184. } else {
  185. if os.IsNotExist(err) {
  186. os.MkdirAll(path, os.ModePerm)
  187. return common_err.SUCCESS, nil
  188. } else if strings.Contains(err.Error(), ": not a directory") {
  189. return common_err.FILE_OR_DIR_EXISTS, err
  190. }
  191. }
  192. return common_err.SERVICE_ERROR, err
  193. }
  194. func (c *systemService) RenameFile(oldF, newF string) (int, error) {
  195. _, err := os.Stat(newF)
  196. if err == nil {
  197. return common_err.DIR_ALREADY_EXISTS, nil
  198. } else {
  199. if os.IsNotExist(err) {
  200. err := os.Rename(oldF, newF)
  201. if err != nil {
  202. return common_err.SERVICE_ERROR, err
  203. }
  204. return common_err.SUCCESS, nil
  205. }
  206. }
  207. return common_err.SERVICE_ERROR, err
  208. }
  209. func (c *systemService) CreateFile(path string) (int, error) {
  210. _, err := os.Stat(path)
  211. if err == nil {
  212. return common_err.FILE_OR_DIR_EXISTS, nil
  213. } else {
  214. if os.IsNotExist(err) {
  215. file.CreateFile(path)
  216. return common_err.SUCCESS, nil
  217. }
  218. }
  219. return common_err.SERVICE_ERROR, err
  220. }
  221. func (c *systemService) GetDeviceTree() string {
  222. return command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;GetDeviceTree")
  223. }
  224. func (c *systemService) GetSysInfo() host.InfoStat {
  225. info, _ := host.Info()
  226. return *info
  227. }
  228. func (c *systemService) GetDiskInfo() *disk.UsageStat {
  229. path := "/"
  230. if runtime.GOOS == "windows" {
  231. path = "C:"
  232. }
  233. diskInfo, _ := disk.Usage(path)
  234. diskInfo.UsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", diskInfo.UsedPercent), 64)
  235. diskInfo.InodesUsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", diskInfo.InodesUsedPercent), 64)
  236. return diskInfo
  237. }
  238. func (c *systemService) GetNetState(name string) string {
  239. return command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;CatNetCardState " + name)
  240. }
  241. func (c *systemService) GetDirPathOne(path string) (m model.Path) {
  242. f, err := os.Stat(path)
  243. if err != nil {
  244. return
  245. }
  246. m.IsDir = f.IsDir()
  247. m.Name = f.Name()
  248. m.Path = path
  249. m.Size = f.Size()
  250. m.Date = f.ModTime()
  251. return
  252. }
  253. func (c *systemService) GetDirPath(path string) ([]model.Path, error) {
  254. if path == "/DATA" {
  255. sysType := runtime.GOOS
  256. if sysType == "windows" {
  257. path = "C:\\CasaOS\\DATA"
  258. }
  259. if sysType == "darwin" {
  260. path = "./CasaOS/DATA"
  261. }
  262. }
  263. ls, err := os.ReadDir(path)
  264. if err != nil {
  265. logger.Error("when read dir", zap.Error(err))
  266. return []model.Path{}, err
  267. }
  268. dirs := []model.Path{}
  269. if len(path) > 0 {
  270. for _, l := range ls {
  271. filePath := filepath.Join(path, l.Name())
  272. link, err := filepath.EvalSymlinks(filePath)
  273. if err != nil {
  274. link = filePath
  275. }
  276. tempFile, err := l.Info()
  277. if err != nil {
  278. logger.Error("when read dir", zap.Error(err))
  279. return []model.Path{}, err
  280. }
  281. temp := model.Path{Name: l.Name(), Path: filePath, IsDir: l.IsDir(), Date: tempFile.ModTime(), Size: tempFile.Size()}
  282. if filePath != link {
  283. file, _ := os.Stat(link)
  284. temp.IsDir = file.IsDir()
  285. }
  286. dirs = append(dirs, temp)
  287. }
  288. } else {
  289. dirs = append(dirs, model.Path{Name: "DATA", Path: "/DATA/", IsDir: true, Date: time.Now()})
  290. }
  291. return dirs, nil
  292. }
  293. func (c *systemService) GetCpuInfo() []cpu.InfoStat {
  294. info, _ := cpu.Info()
  295. return info
  296. }
  297. func (c *systemService) GetMemInfo() map[string]interface{} {
  298. memInfo, _ := mem.VirtualMemory()
  299. memInfo.UsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", memInfo.UsedPercent), 64)
  300. memData := make(map[string]interface{})
  301. memData["total"] = memInfo.Total
  302. memData["available"] = memInfo.Available
  303. memData["used"] = memInfo.Used
  304. memData["free"] = memInfo.Free
  305. memData["usedPercent"] = memInfo.UsedPercent
  306. return memData
  307. }
  308. func (c *systemService) GetCpuPercent() float64 {
  309. percent, _ := cpu.Percent(0, false)
  310. value, _ := strconv.ParseFloat(fmt.Sprintf("%.1f", percent[0]), 64)
  311. return value
  312. }
  313. func (c *systemService) GetCpuCoreNum() int {
  314. count, _ := cpu.Counts(false)
  315. return count
  316. }
  317. func (c *systemService) GetNetInfo() []net.IOCountersStat {
  318. parts, _ := net.IOCounters(true)
  319. return parts
  320. }
  321. func (c *systemService) GetNet(physics bool) []string {
  322. t := "1"
  323. if physics {
  324. t = "2"
  325. }
  326. return command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/helper.sh ;GetNetCard " + t)
  327. }
  328. func (s *systemService) UpdateSystemVersion(version string) {
  329. keyName := "casa_version"
  330. Cache.Delete(keyName)
  331. if file.Exists(config.AppInfo.LogPath + "/upgrade.log") {
  332. os.Remove(config.AppInfo.LogPath + "/upgrade.log")
  333. }
  334. file.CreateFile(config.AppInfo.LogPath + "/upgrade.log")
  335. // go command2.OnlyExec("curl -fsSL https://raw.githubusercontent.com/LinkLeong/casaos-alpha/main/update.sh | bash")
  336. if len(config.ServerInfo.UpdateUrl) > 0 {
  337. go command2.OnlyExec("curl -fsSL " + config.ServerInfo.UpdateUrl + " | bash")
  338. } else {
  339. osRelease, _ := file.ReadOSRelease()
  340. go command2.OnlyExec("curl -fsSL https://get.casaos.io/update?t=" + osRelease["MANUFACTURER"] + " | bash")
  341. }
  342. // s.log.Error(config.AppInfo.ProjectPath + "/shell/tool.sh -r " + version)
  343. // s.log.Error(command2.ExecResultStr(config.AppInfo.ProjectPath + "/shell/tool.sh -r " + version))
  344. }
  345. func (s *systemService) UpdateAssist() {
  346. command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/assist.sh")
  347. }
  348. func (s *systemService) GetTimeZone() string {
  349. return command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;GetTimeZone")
  350. }
  351. func (s *systemService) GetSystemConfigDebug() []string {
  352. return command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/helper.sh ;GetSysInfo")
  353. }
  354. func (s *systemService) UpAppOrderFile(str, id string) {
  355. file.WriteToPath([]byte(str), config.AppInfo.DBPath+"/"+id, "app_order.json")
  356. }
  357. func (s *systemService) GetAppOrderFile(id string) []byte {
  358. return file.ReadFullFile(config.AppInfo.UserDataPath + "/" + id + "/app_order.json")
  359. }
  360. func (s *systemService) UpSystemPort(port string) {
  361. if len(port) > 0 && port != config.ServerInfo.HttpPort {
  362. config.Cfg.Section("server").Key("HttpPort").SetValue(port)
  363. config.ServerInfo.HttpPort = port
  364. }
  365. config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
  366. }
  367. func (s *systemService) GetCasaOSLogs(lineNumber int) string {
  368. file, err := os.Open(filepath.Join(config.AppInfo.LogPath, fmt.Sprintf("%s.%s",
  369. config.AppInfo.LogSaveName,
  370. config.AppInfo.LogFileExt,
  371. )))
  372. if err != nil {
  373. return err.Error()
  374. }
  375. defer file.Close()
  376. content, err := ioutil.ReadAll(file)
  377. if err != nil {
  378. return err.Error()
  379. }
  380. return string(content)
  381. }
  382. func GetDeviceAllIP() []string {
  383. var address []string
  384. addrs, err := net2.InterfaceAddrs()
  385. if err != nil {
  386. return address
  387. }
  388. for _, a := range addrs {
  389. if ipNet, ok := a.(*net2.IPNet); ok && !ipNet.IP.IsLoopback() {
  390. if ipNet.IP.To16() != nil {
  391. address = append(address, ipNet.IP.String())
  392. }
  393. }
  394. }
  395. return address
  396. }
  397. // find thermal_zone of cpu.
  398. // assertions:
  399. // - thermal_zone "type" and "temp" are required fields
  400. // (https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-thermal)
  401. func GetCPUThermalZone() string {
  402. keyName := "cpu_thermal_zone"
  403. var path string
  404. if result, ok := Cache.Get(keyName); ok {
  405. path, ok = result.(string)
  406. if ok {
  407. return path
  408. }
  409. }
  410. var name string
  411. cpu_types := []string{"x86_pkg_temp", "cpu", "CPU", "soc"}
  412. stub := "/sys/devices/virtual/thermal/thermal_zone"
  413. for i := 0; i < 100; i++ {
  414. path = stub + strconv.Itoa(i)
  415. if _, err := os.Stat(path); !os.IsNotExist(err) {
  416. name = strings.TrimSuffix(string(file.ReadFullFile(path+"/type")), "\n")
  417. for _, s := range cpu_types {
  418. if strings.HasPrefix(name, s) {
  419. logger.Info(fmt.Sprintf("CPU thermal zone found: %s, path: %s.", name, path))
  420. Cache.SetDefault(keyName, path)
  421. return path
  422. }
  423. }
  424. } else {
  425. if len(name) > 0 { // proves at least one zone
  426. path = stub + "0"
  427. } else {
  428. path = ""
  429. }
  430. break
  431. }
  432. }
  433. Cache.SetDefault(keyName, path)
  434. return path
  435. }
  436. func (s *systemService) GetCPUTemperature() int {
  437. outPut := ""
  438. path := GetCPUThermalZone()
  439. if len(path) > 0 {
  440. outPut = string(file.ReadFullFile(path + "/temp"))
  441. } else {
  442. outPut = "0"
  443. }
  444. celsius, _ := strconv.Atoi(strings.TrimSpace(outPut))
  445. if celsius > 1000 {
  446. celsius = celsius / 1000
  447. }
  448. return celsius
  449. }
  450. func (s *systemService) GetCPUPower() map[string]string {
  451. data := make(map[string]string, 2)
  452. data["timestamp"] = strconv.FormatInt(time.Now().Unix(), 10)
  453. if file.Exists("/sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj") {
  454. data["value"] = strings.TrimSpace(string(file.ReadFullFile("/sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj")))
  455. } else {
  456. data["value"] = "0"
  457. }
  458. return data
  459. }
  460. func (s *systemService) SystemReboot() error {
  461. // cmd := exec.Command("/bin/bash", "-c", "reboot")
  462. arg := []string{"6"}
  463. cmd := exec.Command("init", arg...)
  464. _, err := cmd.CombinedOutput()
  465. if err != nil {
  466. return err
  467. }
  468. return nil
  469. }
  470. func (s *systemService) SystemShutdown() error {
  471. arg := []string{"0"}
  472. cmd := exec.Command("init", arg...)
  473. _, err := cmd.CombinedOutput()
  474. if err != nil {
  475. return err
  476. }
  477. return nil
  478. }
  479. func NewSystemService() SystemService {
  480. return &systemService{}
  481. }