// agent-mounter:资料库挂载服务(docs/design/21-knowledge-sources.md §4)。 // 以部署用户运行,把本机目录(以后还有远程存储)只读挂到 knowledge/mnt/,隐藏以 . 开头的文件。 package main import ( "flag" "log" "net" "os" "os/signal" "path/filepath" "syscall" "golang.org/x/sys/unix" ) const version = "1.0.0" func main() { dir := flag.String("dir", "/srv/agent-server/mounter", "挂载服务目录(socket、密钥、state.json)") mnt := flag.String("mnt", "/srv/agent-server/knowledge/mnt", "挂载点所在目录") showVersion := flag.Bool("version", false, "显示版本") flag.Parse() if *showVersion { println(version) return } log.SetFlags(0) log.SetOutput(os.Stderr) var st unix.Stat_t if err := unix.Stat(*dir, &st); err != nil { log.Fatalf("挂载服务目录不存在:%s(先执行 deploy/deploy.sh)", *dir) } gid := int(st.Gid) if err := unix.Stat(*mnt, &st); err != nil { log.Fatalf("挂载点目录不存在:%s", *mnt) } unix.Umask(0o027) mgr := newManager(realSystem{}, allowedRoots, filepath.Clean(*mnt), filepath.Join(*dir, "state.json")) mgr.cleanupStale() if specs := mgr.loadState(); len(specs) > 0 { if _, err := mgr.sync(specs, false); err != nil { log.Printf("按 state.json 恢复挂载失败:%v", err) } else { log.Printf("已按 state.json 恢复 %d 个挂载", len(specs)) } } stop := make(chan struct{}) go mgr.watchMountinfo(stop) srv := &server{token: newToken(), mgr: mgr, version: version} sock := filepath.Join(*dir, "mounter.sock") _ = os.Remove(sock) l, err := net.Listen("unix", sock) if err != nil { log.Fatalf("监听 %s 失败:%v", sock, err) } if err := os.Chown(sock, -1, gid); err != nil { log.Fatalf("设置 socket 的组失败:%v", err) } if err := os.Chmod(sock, 0o660); err != nil { log.Fatalf("设置 socket 权限失败:%v", err) } if err := writeGroupFile(filepath.Join(*dir, "token"), []byte(srv.token+"\n"), 0o640, gid); err != nil { log.Fatalf("写入密钥失败:%v", err) } go srv.serve(l) log.Printf("agent-mounter %s 已启动:%s", version, sock) sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT) <-sig close(stop) l.Close() mgr.unmountAll() log.Printf("已停止") }