wa_init_vm.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. package openngvfs
  2. import (
  3. "context"
  4. "fmt"
  5. "git.swzry.com/zry/GoHiedaLogger/hiedalog"
  6. "git.swzry.com/zry/pathutils"
  7. "github.com/tetratelabs/wazero"
  8. wa_api "github.com/tetratelabs/wazero/api"
  9. "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
  10. "os"
  11. "path"
  12. )
  13. type InitWAVM struct {
  14. logger *hiedalog.HiedaLogger
  15. ctx context.Context
  16. runtime wazero.Runtime
  17. mainModCompiled wazero.CompiledModule
  18. mainModCfg wazero.ModuleConfig
  19. mainMod wa_api.Module
  20. hostMod wa_api.Module
  21. pStdout *IOWriterAdapter
  22. pStderr *IOWriterAdapter
  23. hmAdapter *InitWAVMHostModuleAdapter
  24. fsTab *RawFsTab
  25. appName string
  26. }
  27. func NewInitWAVM(ctx context.Context, api *InitWAVMHostAPI, ngRoot string, appName string, logger *hiedalog.HiedaLogger) (*InitWAVM, error) {
  28. ok, err := pathutils.PathExists(ngRoot)
  29. if err != nil {
  30. return nil, fmt.Errorf("failed check if NAGAE_ROV_ROOT is exists: %w", err)
  31. }
  32. if !ok {
  33. return nil, fmt.Errorf("NAGAE_ROV_ROOT '%s' not exists", ngRoot)
  34. }
  35. pcache := path.Join(ngRoot, "cache", "wasm", "ngvfs", "app", appName)
  36. err = pathutils.MkDirIfNotExist(pcache, 0755)
  37. if !ok {
  38. return nil, fmt.Errorf("failed create dir '%s' for wasm cache: %w", pcache, err)
  39. }
  40. pwar := path.Join(ngRoot, "misc", "wasm", "common", "osfs")
  41. err = pathutils.MkDirIfNotExist(pwar, 0755)
  42. if !ok {
  43. return nil, fmt.Errorf("failed create dir '%s' for wasm common osfs: %w", pcache, err)
  44. }
  45. fscfgRoot := path.Join(ngRoot, "syscfg", "ngvfs")
  46. fstabPath := path.Join(fscfgRoot, "fstab.toml")
  47. waRoot := path.Join(fscfgRoot, "wasm")
  48. fsTab, err := loadFsTab(fstabPath)
  49. if err != nil {
  50. return nil, fmt.Errorf("failed load fstab: %w", err)
  51. }
  52. wasmBin, err := getWasmFromFstab(waRoot, fsTab, appName, logger)
  53. if err != nil {
  54. return nil, fmt.Errorf("failed load init wasm: %w", err)
  55. }
  56. cc, err := wazero.NewCompilationCacheWithDir(pcache)
  57. if !ok {
  58. return nil, fmt.Errorf("failed create dir '%s' for wasm cache: %w", pcache, err)
  59. }
  60. rtcfg := wazero.NewRuntimeConfig().WithCompilationCache(cc)
  61. rt := wazero.NewRuntimeWithConfig(ctx, rtcfg)
  62. _, err = wasi_snapshot_preview1.Instantiate(ctx, rt)
  63. if !ok {
  64. return nil, fmt.Errorf("failed instantiate wasi: %w", err)
  65. }
  66. wafscfg := wazero.NewFSConfig().
  67. WithReadOnlyDirMount(pwar, "/osfs")
  68. pStdout := NewIOWriterAdapter(logger, "ngvfs-vm-stdio", hiedalog.DLN_INFO)
  69. pStderr := NewIOWriterAdapter(logger, "ngvfs-vm-stdio", hiedalog.DLN_WARN)
  70. mcfg := wazero.NewModuleConfig().
  71. WithFSConfig(wafscfg).
  72. WithStderr(pStderr).
  73. WithStdout(pStdout).
  74. WithEnv("NAGAE_ROV_ROOT", ngRoot).
  75. WithEnv("NAGAE_APP_NAME", appName)
  76. mainModCompiled, err := rt.CompileModule(ctx, wasmBin)
  77. hostModuleBuilder := rt.NewHostModuleBuilder("ngvfs_init")
  78. apiAdapter := NewInitWAVMHostModuleAdapter(hostModuleBuilder, api, fsTab, logger, appName)
  79. if err != nil {
  80. return nil, fmt.Errorf("failed compile wasm code: %w", err)
  81. }
  82. vm := &InitWAVM{
  83. ctx: ctx,
  84. runtime: rt,
  85. mainModCompiled: mainModCompiled,
  86. mainModCfg: mcfg,
  87. logger: logger,
  88. pStderr: pStderr,
  89. pStdout: pStdout,
  90. hmAdapter: apiAdapter,
  91. fsTab: fsTab,
  92. appName: appName,
  93. }
  94. return vm, nil
  95. }
  96. func (vm *InitWAVM) Init() error {
  97. if vm.pStderr == nil {
  98. return fmt.Errorf("internal error: pStderr is nil")
  99. }
  100. if vm.pStdout == nil {
  101. return fmt.Errorf("internal error: pStdout is nil")
  102. }
  103. go func() {
  104. vm.pStdout.Run()
  105. vm.logger.LogComplex("ngvfs-vm", hiedalog.DLN_INFO, map[string]string{
  106. "type": "stdio_routine_end",
  107. "msg": "stdout routine end",
  108. })
  109. }()
  110. go func() {
  111. vm.pStderr.Run()
  112. vm.logger.LogComplex("ngvfs-vm", hiedalog.DLN_INFO, map[string]string{
  113. "type": "stdio_routine_end",
  114. "msg": "stderr routine end",
  115. })
  116. }()
  117. hm, err := vm.hmAdapter.Instantiate(vm.ctx)
  118. if err != nil {
  119. return fmt.Errorf("failed instantiate host module: %w", err)
  120. }
  121. vm.hostMod = hm
  122. mainMod, err := vm.runtime.InstantiateModule(vm.ctx, vm.mainModCompiled, vm.mainModCfg)
  123. if err != nil {
  124. return fmt.Errorf("failed instantiate WASM code: %w", err)
  125. }
  126. vm.mainMod = mainMod
  127. return nil
  128. }
  129. func (vm *InitWAVM) AutoMount() error {
  130. if vm.mainMod == nil {
  131. return fmt.Errorf("call AutoMount before initialized")
  132. }
  133. amfn := vm.mainMod.ExportedFunction("auto_mount")
  134. if amfn == nil {
  135. return fmt.Errorf("exported function 'auto_mount' not found")
  136. }
  137. ret, err := amfn.Call(vm.ctx)
  138. if err != nil {
  139. return fmt.Errorf("failed call 'auto_mount': %w", err)
  140. }
  141. if len(ret) != 1 {
  142. return fmt.Errorf("unexpected number of return values: %d", len(ret))
  143. }
  144. retval := ret[0]
  145. if retval != 0 {
  146. vm.logger.LogComplex("ngvfs-vm", hiedalog.DLN_ERROR, map[string]string{
  147. "type": "auto_mount_fail",
  148. "msg": fmt.Sprintf("auto mount returned %d", ret[0]),
  149. })
  150. return fmt.Errorf("error when calling `auto_mount`: returned non-zero vaule: %d", retval)
  151. }
  152. vm.logger.LogComplex("ngvfs-vm", hiedalog.DLN_INFO, map[string]string{
  153. "type": "auto_mount_ok",
  154. "msg": fmt.Sprintf("auto mount ok"),
  155. })
  156. return nil
  157. }
  158. func (vm *InitWAVM) DeInit() error {
  159. if vm.pStdout == nil {
  160. vm.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
  161. "type": "deinit_warn",
  162. "msg": "stdout is not initialized",
  163. })
  164. } else {
  165. err := vm.pStdout.Close()
  166. if err != nil {
  167. vm.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
  168. "type": "deinit_warn",
  169. "msg": "close stdout error",
  170. "err": err.Error(),
  171. })
  172. }
  173. }
  174. if vm.pStderr == nil {
  175. vm.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
  176. "type": "deinit_warn",
  177. "msg": "stderr is not initialized",
  178. })
  179. } else {
  180. err := vm.pStderr.Close()
  181. if err != nil {
  182. vm.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
  183. "type": "deinit_warn",
  184. "msg": "close stderr error",
  185. "err": err.Error(),
  186. })
  187. }
  188. }
  189. return nil
  190. }
  191. func getWasmFromFstab(waRoot string, fstab *RawFsTab, appName string, logger *hiedalog.HiedaLogger) (wasmBin []byte, err error) {
  192. var ok bool
  193. var waName string
  194. waName, ok = fstab.Wasm.App[appName]
  195. if !ok {
  196. waName = fstab.Wasm.Default
  197. }
  198. waFileName := path.Join(waRoot, waName+".wasm")
  199. logger.LogComplex("ngvfs", hiedalog.DLN_INFO, map[string]string{
  200. "type": "will_init_by_wasm",
  201. "wasm_name": waName,
  202. "wasm_file": waFileName,
  203. })
  204. wasmBin, err = os.ReadFile(waFileName)
  205. if err != nil {
  206. return nil, fmt.Errorf("failed read wasm file: %w", err)
  207. }
  208. return wasmBin, nil
  209. }