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  }