123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- package openngvfs
- import (
- "context"
- "fmt"
- "git.swzry.com/zry/GoHiedaLogger/hiedalog"
- "git.swzry.com/zry/pathutils"
- "github.com/tetratelabs/wazero"
- wa_api "github.com/tetratelabs/wazero/api"
- "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
- "os"
- "path"
- )
- type InitWAVM struct {
- logger *hiedalog.HiedaLogger
- ctx context.Context
- runtime wazero.Runtime
- mainModCompiled wazero.CompiledModule
- mainModCfg wazero.ModuleConfig
- mainMod wa_api.Module
- hostMod wa_api.Module
- pStdout *IOWriterAdapter
- pStderr *IOWriterAdapter
- hmAdapter *InitWAVMHostModuleAdapter
- fsTab *RawFsTab
- appName string
- }
- func NewInitWAVM(ctx context.Context, api *InitWAVMHostAPI, ngRoot string, appName string, logger *hiedalog.HiedaLogger) (*InitWAVM, error) {
- ok, err := pathutils.PathExists(ngRoot)
- if err != nil {
- return nil, fmt.Errorf("failed check if NAGAE_ROV_ROOT is exists: %w", err)
- }
- if !ok {
- return nil, fmt.Errorf("NAGAE_ROV_ROOT '%s' not exists", ngRoot)
- }
- pcache := path.Join(ngRoot, "cache", "wasm", "ngvfs", "app", appName)
- err = pathutils.MkDirIfNotExist(pcache, 0755)
- if !ok {
- return nil, fmt.Errorf("failed create dir '%s' for wasm cache: %w", pcache, err)
- }
- pwar := path.Join(ngRoot, "misc", "wasm", "common", "osfs")
- err = pathutils.MkDirIfNotExist(pwar, 0755)
- if !ok {
- return nil, fmt.Errorf("failed create dir '%s' for wasm common osfs: %w", pcache, err)
- }
- fscfgRoot := path.Join(ngRoot, "syscfg", "ngvfs")
- fstabPath := path.Join(fscfgRoot, "fstab.toml")
- waRoot := path.Join(fscfgRoot, "wasm")
- fsTab, err := loadFsTab(fstabPath)
- if err != nil {
- return nil, fmt.Errorf("failed load fstab: %w", err)
- }
- wasmBin, err := getWasmFromFstab(waRoot, fsTab, appName, logger)
- if err != nil {
- return nil, fmt.Errorf("failed load init wasm: %w", err)
- }
- cc, err := wazero.NewCompilationCacheWithDir(pcache)
- if !ok {
- return nil, fmt.Errorf("failed create dir '%s' for wasm cache: %w", pcache, err)
- }
- rtcfg := wazero.NewRuntimeConfig().WithCompilationCache(cc)
- rt := wazero.NewRuntimeWithConfig(ctx, rtcfg)
- _, err = wasi_snapshot_preview1.Instantiate(ctx, rt)
- if !ok {
- return nil, fmt.Errorf("failed instantiate wasi: %w", err)
- }
- wafscfg := wazero.NewFSConfig().
- WithReadOnlyDirMount(pwar, "/osfs")
- pStdout := NewIOWriterAdapter(logger, "ngvfs-vm-stdio", hiedalog.DLN_INFO)
- pStderr := NewIOWriterAdapter(logger, "ngvfs-vm-stdio", hiedalog.DLN_WARN)
- mcfg := wazero.NewModuleConfig().
- WithFSConfig(wafscfg).
- WithStderr(pStderr).
- WithStdout(pStdout).
- WithEnv("NAGAE_ROV_ROOT", ngRoot).
- WithEnv("NAGAE_APP_NAME", appName)
- mainModCompiled, err := rt.CompileModule(ctx, wasmBin)
- hostModuleBuilder := rt.NewHostModuleBuilder("ngvfs_init")
- apiAdapter := NewInitWAVMHostModuleAdapter(hostModuleBuilder, api, fsTab, logger, appName)
- if err != nil {
- return nil, fmt.Errorf("failed compile wasm code: %w", err)
- }
- vm := &InitWAVM{
- ctx: ctx,
- runtime: rt,
- mainModCompiled: mainModCompiled,
- mainModCfg: mcfg,
- logger: logger,
- pStderr: pStderr,
- pStdout: pStdout,
- hmAdapter: apiAdapter,
- fsTab: fsTab,
- appName: appName,
- }
- return vm, nil
- }
- func (vm *InitWAVM) Init() error {
- if vm.pStderr == nil {
- return fmt.Errorf("internal error: pStderr is nil")
- }
- if vm.pStdout == nil {
- return fmt.Errorf("internal error: pStdout is nil")
- }
- go func() {
- vm.pStdout.Run()
- vm.logger.LogComplex("ngvfs-vm", hiedalog.DLN_INFO, map[string]string{
- "type": "stdio_routine_end",
- "msg": "stdout routine end",
- })
- }()
- go func() {
- vm.pStderr.Run()
- vm.logger.LogComplex("ngvfs-vm", hiedalog.DLN_INFO, map[string]string{
- "type": "stdio_routine_end",
- "msg": "stderr routine end",
- })
- }()
- hm, err := vm.hmAdapter.Instantiate(vm.ctx)
- if err != nil {
- return fmt.Errorf("failed instantiate host module: %w", err)
- }
- vm.hostMod = hm
- mainMod, err := vm.runtime.InstantiateModule(vm.ctx, vm.mainModCompiled, vm.mainModCfg)
- if err != nil {
- return fmt.Errorf("failed instantiate WASM code: %w", err)
- }
- vm.mainMod = mainMod
- return nil
- }
- func (vm *InitWAVM) AutoMount() error {
- if vm.mainMod == nil {
- return fmt.Errorf("call AutoMount before initialized")
- }
- amfn := vm.mainMod.ExportedFunction("auto_mount")
- if amfn == nil {
- return fmt.Errorf("exported function 'auto_mount' not found")
- }
- ret, err := amfn.Call(vm.ctx)
- if err != nil {
- return fmt.Errorf("failed call 'auto_mount': %w", err)
- }
- if len(ret) != 1 {
- return fmt.Errorf("unexpected number of return values: %d", len(ret))
- }
- retval := ret[0]
- if retval != 0 {
- vm.logger.LogComplex("ngvfs-vm", hiedalog.DLN_ERROR, map[string]string{
- "type": "auto_mount_fail",
- "msg": fmt.Sprintf("auto mount returned %d", ret[0]),
- })
- return fmt.Errorf("error when calling `auto_mount`: returned non-zero vaule: %d", retval)
- }
- vm.logger.LogComplex("ngvfs-vm", hiedalog.DLN_INFO, map[string]string{
- "type": "auto_mount_ok",
- "msg": fmt.Sprintf("auto mount ok"),
- })
- return nil
- }
- func (vm *InitWAVM) DeInit() error {
- if vm.pStdout == nil {
- vm.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
- "type": "deinit_warn",
- "msg": "stdout is not initialized",
- })
- } else {
- err := vm.pStdout.Close()
- if err != nil {
- vm.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
- "type": "deinit_warn",
- "msg": "close stdout error",
- "err": err.Error(),
- })
- }
- }
- if vm.pStderr == nil {
- vm.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
- "type": "deinit_warn",
- "msg": "stderr is not initialized",
- })
- } else {
- err := vm.pStderr.Close()
- if err != nil {
- vm.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
- "type": "deinit_warn",
- "msg": "close stderr error",
- "err": err.Error(),
- })
- }
- }
- return nil
- }
- func getWasmFromFstab(waRoot string, fstab *RawFsTab, appName string, logger *hiedalog.HiedaLogger) (wasmBin []byte, err error) {
- var ok bool
- var waName string
- waName, ok = fstab.Wasm.App[appName]
- if !ok {
- waName = fstab.Wasm.Default
- }
- waFileName := path.Join(waRoot, waName+".wasm")
- logger.LogComplex("ngvfs", hiedalog.DLN_INFO, map[string]string{
- "type": "will_init_by_wasm",
- "wasm_name": waName,
- "wasm_file": waFileName,
- })
- wasmBin, err = os.ReadFile(waFileName)
- if err != nil {
- return nil, fmt.Errorf("failed read wasm file: %w", err)
- }
- return wasmBin, nil
- }
|