github.com/nya3jp/tast@v0.0.0-20230601000426-85c8e4d83a9b/src/go.chromium.org/tast/core/ssh/linuxssh/fileutil.go (about) 1 // Copyright 2023 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Package linuxssh provides Linux specific operations conducted via SSH 6 // TODO(oka): now that this file is not used from framework, simplify the code. 7 package linuxssh 8 9 import ( 10 "bytes" 11 "context" 12 "fmt" 13 "os" 14 "strconv" 15 "strings" 16 17 "go.chromium.org/tast/core/errors" 18 "go.chromium.org/tast/core/internal/linuxssh" 19 "go.chromium.org/tast/core/ssh" 20 ) 21 22 // SymlinkPolicy describes how symbolic links should be handled by PutFiles. 23 type SymlinkPolicy = linuxssh.SymlinkPolicy 24 25 const ( 26 // PreserveSymlinks indicates that symlinks should be preserved during the copy. 27 PreserveSymlinks = linuxssh.PreserveSymlinks 28 // DereferenceSymlinks indicates that symlinks should be dereferenced and turned into normal files. 29 DereferenceSymlinks = linuxssh.DereferenceSymlinks 30 ) 31 32 // WordCountInfo describes the result from the unix command wc. 33 type WordCountInfo struct { 34 Lines int64 35 Words int64 36 Bytes int64 37 } 38 39 // GetFile copies a file or directory from the host to the local machine. 40 // dst is the full destination name for the file or directory being copied, not 41 // a destination directory into which it will be copied. dst will be replaced 42 // if it already exists. 43 func GetFile(ctx context.Context, s *ssh.Conn, src, dst string, symlinkPolicy SymlinkPolicy) error { 44 return linuxssh.GetFile(ctx, s, src, dst, symlinkPolicy) 45 } 46 47 // PutFiles copies files on the local machine to the host. files describes 48 // a mapping from a local file path to a remote file path. For example, the call: 49 // 50 // PutFiles(ctx, conn, map[string]string{"/src/from": "/dst/to"}) 51 // 52 // will copy the local file or directory /src/from to /dst/to on the remote host. 53 // Local file paths can be absolute or relative. Remote file paths must be absolute. 54 // SHA1 hashes of remote files are checked in advance to send updated files only. 55 // bytes is the amount of data sent over the wire (possibly after compression). 56 func PutFiles(ctx context.Context, s *ssh.Conn, files map[string]string, 57 symlinkPolicy SymlinkPolicy) (bytes int64, err error) { 58 return linuxssh.PutFiles(ctx, s, files, symlinkPolicy) 59 } 60 61 // ReadFile reads the file on the path and returns the contents. 62 func ReadFile(ctx context.Context, conn *ssh.Conn, path string) ([]byte, error) { 63 return conn.CommandContext(ctx, "cat", path).Output(ssh.DumpLogOnError) 64 } 65 66 // GetFileTail reads the file on the path and returns the file truncate by command tail 67 // with the Max System Message Log Size and destination. 68 func GetFileTail(ctx context.Context, conn *ssh.Conn, src, dst string, startLine, maxSize int64) error { 69 return linuxssh.GetFileTail(ctx, conn, src, dst, startLine, maxSize) 70 } 71 72 // WriteFile writes data to the file on the path. If the file does not exist, 73 // WriteFile creates it with permissions perm; otherwise WriteFile truncates it 74 // before writing, without changing permissions. 75 // Unlike ioutil.WriteFile, it doesn't apply umask on perm. 76 func WriteFile(ctx context.Context, conn *ssh.Conn, path string, data []byte, perm os.FileMode) error { 77 cmd := conn.CommandContext(ctx, "sh", "-c", `test -e "$0"; r=$?; cat > "$0"; if [ $r = 1 ]; then chmod "$1" "$0"; fi`, path, fmt.Sprintf("%o", perm&os.ModePerm)) 78 cmd.Stdin = bytes.NewBuffer(data) 79 return cmd.Run(ssh.DumpLogOnError) 80 } 81 82 // WordCount get the line, word and byte counts of a remote text file. 83 func WordCount(ctx context.Context, conn *ssh.Conn, path string) (*WordCountInfo, error) { 84 cmd := conn.CommandContext(ctx, "wc", path) 85 output, err := cmd.Output() 86 if err != nil { 87 return nil, errors.Wrapf(err, "failed to call wc for %s", path) 88 } 89 // Output example: " 68201 1105834 14679551 /var/log/messages". 90 strList := strings.Split(string(output), " ") 91 var strs []string 92 for _, s := range strList { 93 if s != "" { 94 strs = append(strs, s) 95 } 96 } 97 if len(strs) < 3 { 98 return nil, errors.Errorf("wc information is not available for %s", path) 99 } 100 lc, err := strconv.ParseInt(strs[0], 10, 64) 101 if err != nil { 102 return nil, errors.Wrapf(err, "failed to parse line count from string %s", string(output)) 103 } 104 wc, err := strconv.ParseInt(strs[1], 10, 64) 105 if err != nil { 106 return nil, errors.Wrapf(err, "failed to parse word count from string %s", string(output)) 107 } 108 bc, err := strconv.ParseInt(strs[2], 10, 64) 109 if err != nil { 110 return nil, errors.Wrapf(err, "failed to parse bytes count from string %s", string(output)) 111 } 112 return &WordCountInfo{Lines: lc, Words: wc, Bytes: bc}, nil 113 }