gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/tools/tracereplay/save.go (about) 1 // Copyright 2022 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package tracereplay 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "os" 21 "path/filepath" 22 23 "gvisor.dev/gvisor/pkg/atomicbitops" 24 "gvisor.dev/gvisor/pkg/sentry/seccheck/sinks/remote/server" 25 "gvisor.dev/gvisor/pkg/sentry/seccheck/sinks/remote/wire" 26 ) 27 28 // Save implements the functionality required for the "save" command. 29 type Save struct { 30 server.CommonServer 31 dir string 32 prefix string 33 clientCount atomicbitops.Uint64 34 } 35 36 var _ server.ClientHandler = (*Save)(nil) 37 38 // NewSave creates a new Save instance. 39 func NewSave(endpoint, dir, prefix string) *Save { 40 s := &Save{dir: dir, prefix: prefix} 41 s.CommonServer.Init(endpoint, s) 42 return s 43 } 44 45 // Start starts the server. 46 func (s *Save) Start() error { 47 if err := os.MkdirAll(s.dir, 0755); err != nil { 48 return err 49 } 50 return s.CommonServer.Start() 51 } 52 53 // NewClient creates a new file for the client and writes messages to it. 54 // 55 // The file format starts with a string signature to make it easy to check that 56 // it's a trace file. The signature is followed by a JSON configuration that 57 // contains information required to process the file. Next, there are a sequence 58 // of messages. Both JSON and messages are prefixed by an uint64 with their 59 // size. 60 // 61 // Ex: signature <size>Config JSON [<size>message]* 62 func (s *Save) NewClient() (server.MessageHandler, error) { 63 seq := s.clientCount.Add(1) 64 filename := filepath.Join(s.dir, fmt.Sprintf("%s%04d", s.prefix, seq)) 65 fmt.Printf("New client connected, writing to: %q\n", filename) 66 67 out, err := os.Create(filename) 68 if err != nil { 69 return nil, err 70 } 71 if _, err := out.Write([]byte(signature)); err != nil { 72 return nil, err 73 } 74 75 handler := &msgHandler{out: out} 76 77 cfg, err := json.Marshal(Config{Version: handler.Version()}) 78 if err != nil { 79 return nil, err 80 } 81 if err := writeWithSize(out, cfg); err != nil { 82 return nil, err 83 } 84 85 return handler, nil 86 } 87 88 type msgHandler struct { 89 out *os.File 90 messageCount atomicbitops.Uint64 91 } 92 93 var _ server.MessageHandler = (*msgHandler)(nil) 94 95 // Version implements server.MessageHandler. 96 func (m *msgHandler) Version() uint32 { 97 return wire.CurrentVersion 98 } 99 100 // Message saves the message to the client file. 101 func (m *msgHandler) Message(raw []byte, _ wire.Header, _ []byte) error { 102 m.messageCount.Add(1) 103 return writeWithSize(m.out, raw) 104 } 105 106 // Close closes the client file. 107 func (m *msgHandler) Close() { 108 fmt.Printf("Closing client, wrote %d messages to %q\n", m.messageCount.Load(), m.out.Name()) 109 _ = m.out.Close() 110 }