runtime.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. // SiYuan - Build Your Eternal Digital Garden
  2. // Copyright (c) 2020-present, b3log.org
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. package util
  17. import (
  18. "fmt"
  19. "math/rand"
  20. "os"
  21. "path/filepath"
  22. "reflect"
  23. "runtime"
  24. "runtime/debug"
  25. "strings"
  26. "sync"
  27. "time"
  28. "github.com/88250/gulu"
  29. "github.com/denisbrodbeck/machineid"
  30. "github.com/siyuan-note/httpclient"
  31. "github.com/siyuan-note/logging"
  32. )
  33. const DatabaseVer = "20220501" // 修改表结构的话需要修改这里
  34. const (
  35. ExitCodeReadOnlyDatabase = 20 // 数据库文件被锁
  36. ExitCodeUnavailablePort = 21 // 端口不可用
  37. ExitCodeCreateConfDirErr = 22 // 创建配置目录失败
  38. ExitCodeBlockTreeErr = 23 // 无法读写 blocktree.msgpack 文件
  39. ExitCodeWorkspaceLocked = 24 // 工作空间已被锁定
  40. ExitCodeCreateWorkspaceDirErr = 25 // 创建工作空间失败
  41. ExitCodeFileSysInconsistent = 26 // 文件系统不一致
  42. ExitCodeOk = 0 // 正常退出
  43. ExitCodeFatal = 1 // 致命错误
  44. )
  45. // IsExiting 是否正在退出程序。
  46. var IsExiting = false
  47. // MobileOSVer 移动端操作系统版本。
  48. var MobileOSVer string
  49. func logBootInfo() {
  50. plat, platVer := GetOSPlatform()
  51. osInfo := plat
  52. if "" != platVer {
  53. osInfo += " " + platVer
  54. }
  55. logging.LogInfof("kernel is booting:\n"+
  56. " * ver [%s]\n"+
  57. " * arch [%s]\n"+
  58. " * os [%s]\n"+
  59. " * pid [%d]\n"+
  60. " * runtime mode [%s]\n"+
  61. " * working directory [%s]\n"+
  62. " * read only [%v]\n"+
  63. " * container [%s]\n"+
  64. " * database [ver=%s]\n"+
  65. " * workspace directory [%s]",
  66. Ver, runtime.GOARCH, osInfo, os.Getpid(), Mode, WorkingDir, ReadOnly, Container, DatabaseVer, WorkspaceDir)
  67. }
  68. func IsMutexLocked(m *sync.Mutex) bool {
  69. state := reflect.ValueOf(m).Elem().FieldByName("state")
  70. return state.Int()&1 == 1
  71. }
  72. func RandomSleep(minMills, maxMills int) {
  73. r := gulu.Rand.Int(minMills, maxMills)
  74. time.Sleep(time.Duration(r) * time.Millisecond)
  75. }
  76. func GetDeviceID() string {
  77. if ContainerStd == Container {
  78. machineID, err := machineid.ID()
  79. if nil != err {
  80. return gulu.Rand.String(12)
  81. }
  82. return machineID
  83. }
  84. return gulu.Rand.String(12)
  85. }
  86. func SetNetworkProxy(proxyURL string) {
  87. if err := os.Setenv("HTTPS_PROXY", proxyURL); nil != err {
  88. logging.LogErrorf("set env [HTTPS_PROXY] failed: %s", err)
  89. }
  90. if err := os.Setenv("HTTP_PROXY", proxyURL); nil != err {
  91. logging.LogErrorf("set env [HTTP_PROXY] failed: %s", err)
  92. }
  93. if "" != proxyURL {
  94. logging.LogInfof("use network proxy [%s]", proxyURL)
  95. } else {
  96. logging.LogInfof("use network proxy [system]")
  97. }
  98. httpclient.CloseIdleConnections()
  99. }
  100. const (
  101. // FrontendQueueInterval 为前端请求队列轮询间隔。
  102. FrontendQueueInterval = 512 * time.Millisecond
  103. // SQLFlushInterval 为数据库事务队列写入间隔。
  104. SQLFlushInterval = 3000 * time.Millisecond
  105. )
  106. var (
  107. Langs = map[string]map[int]string{}
  108. TimeLangs = map[string]map[string]interface{}{}
  109. TaskActionLangs = map[string]map[string]interface{}{}
  110. )
  111. var (
  112. thirdPartySyncCheckTicker = time.NewTicker(time.Minute * 30)
  113. firstThirdPartySyncCheck = true
  114. )
  115. func CheckFileSysStatus() {
  116. if ContainerStd != Container {
  117. return
  118. }
  119. if firstThirdPartySyncCheck {
  120. firstThirdPartySyncCheck = false
  121. time.Sleep(time.Second * 10)
  122. }
  123. reportFileSysFatalError := func(err error) {
  124. stack := debug.Stack()
  125. logging.LogErrorf("check file system status failed: %s, %s", err, stack)
  126. os.Exit(ExitCodeFileSysInconsistent)
  127. }
  128. const fileSysStatusCheckFile = "filesys_status_check"
  129. for {
  130. workspaceDirLower := strings.ToLower(WorkspaceDir)
  131. if strings.Contains(workspaceDirLower, "onedrive") || strings.Contains(workspaceDirLower, "dropbox") ||
  132. strings.Contains(workspaceDirLower, "google drive") || strings.Contains(workspaceDirLower, "pcloud") {
  133. reportFileSysFatalError(fmt.Errorf("workspace dir [%s] is in third party sync dir", WorkspaceDir))
  134. continue
  135. }
  136. dir := filepath.Join(DataDir, fileSysStatusCheckFile)
  137. if err := os.RemoveAll(dir); nil != err {
  138. reportFileSysFatalError(err)
  139. continue
  140. }
  141. if err := os.MkdirAll(dir, 0755); nil != err {
  142. reportFileSysFatalError(err)
  143. continue
  144. }
  145. for i := 0; i < 16; i++ {
  146. tmp := filepath.Join(dir, "check_"+gulu.Rand.String(7))
  147. data := make([]byte, 1024*4)
  148. _, err := rand.Read(data)
  149. if nil != err {
  150. reportFileSysFatalError(err)
  151. break
  152. }
  153. if err = os.WriteFile(tmp, data, 0644); nil != err {
  154. reportFileSysFatalError(err)
  155. break
  156. }
  157. time.Sleep(time.Second)
  158. for j := 0; j < 64; j++ {
  159. f, err := os.Open(tmp)
  160. if nil != err {
  161. reportFileSysFatalError(err)
  162. break
  163. }
  164. if err = f.Close(); nil != err {
  165. reportFileSysFatalError(err)
  166. break
  167. }
  168. time.Sleep(100 * time.Millisecond)
  169. if err = os.Rename(tmp, tmp+"_1"); nil != err {
  170. reportFileSysFatalError(err)
  171. break
  172. }
  173. time.Sleep(100 * time.Millisecond)
  174. if err = os.Rename(tmp+"_1", tmp); nil != err {
  175. reportFileSysFatalError(err)
  176. break
  177. }
  178. }
  179. entries, err := os.ReadDir(dir)
  180. if nil != err {
  181. reportFileSysFatalError(err)
  182. break
  183. }
  184. count := 0
  185. for _, entry := range entries {
  186. if !entry.IsDir() && strings.Contains(entry.Name(), "check_") {
  187. count++
  188. }
  189. }
  190. if 1 < count {
  191. reportFileSysFatalError(fmt.Errorf("dir [%s] has more than 1 file", dir))
  192. break
  193. }
  194. if err = os.RemoveAll(tmp); nil != err {
  195. reportFileSysFatalError(err)
  196. break
  197. }
  198. <-thirdPartySyncCheckTicker.C
  199. }
  200. }
  201. }