github.com/LanceLRQ/deer-common@v0.0.9-0.20210319081233-e8222ac018a8/utils/binary.go (about)

     1  package utils
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/binary"
     7  	"github.com/LanceLRQ/deer-common/constants"
     8  	"github.com/LanceLRQ/deer-common/structs"
     9  	"github.com/pkg/errors"
    10  	"os"
    11  	"os/exec"
    12  	"path"
    13  	"path/filepath"
    14  	"runtime"
    15  	"strings"
    16  	"syscall"
    17  )
    18  
    19  func IsExecutableFile(filePath string) (bool, error) {
    20  	fp, err := os.OpenFile(filePath, os.O_RDONLY|syscall.O_NONBLOCK, 0)
    21  	if err != nil {
    22  		return false, errors.Errorf("open file error")
    23  	}
    24  	defer fp.Close()
    25  
    26  	var magic uint32 = 0
    27  	err = binary.Read(fp, binary.BigEndian, &magic)
    28  	if err != nil {
    29  		return false, err
    30  	}
    31  
    32  	isExec := false
    33  	if runtime.GOOS == "darwin" {
    34  		isExec = magic == 0xCFFAEDFE || magic == 0xCEFAEDFE || magic == 0xFEEDFACF || magic == 0xFEEDFACE
    35  	} else if runtime.GOOS == "linux" {
    36  		isExec = magic == 0x7F454C46
    37  	}
    38  	return isExec, nil
    39  }
    40  
    41  func GetCompiledBinaryFileName(typeName, moduleName string) string {
    42  	prefix, ok := constants.TestlibBinaryPrefixs[typeName]
    43  	if !ok {
    44  		prefix = ""
    45  	}
    46  	return prefix + moduleName
    47  }
    48  
    49  // 根据配置文件将对应预编译文件转换成绝对路径
    50  func GetCompiledBinaryFileAbsPath(typeName, moduleName, configDir string) (string, error) {
    51  	targetName := GetCompiledBinaryFileName(typeName, moduleName)
    52  	return filepath.Abs(path.Join(path.Join(configDir, "bin"), targetName))
    53  }
    54  
    55  // 解析generator脚本
    56  func ParseGeneratorScript(script string) (string, []string, error) {
    57  	vals := strings.Split(script, " ")
    58  	if len(vals) <= 1 {
    59  		return "", nil, errors.Errorf("generator calling script error")
    60  	}
    61  	return vals[0], vals[1:], nil
    62  }
    63  
    64  // 运行UnixShell,支持context
    65  func RunUnixShell(options *structs.ShellOptions) (*structs.ShellResult, error) {
    66  	fpath, err := exec.LookPath(options.Name)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  	result := structs.ShellResult{}
    71  	proc := exec.CommandContext(options.Context, fpath, options.Args...)
    72  	var stderr, stdout bytes.Buffer
    73  
    74  	if options.StdWriter != nil && options.StdWriter.Output != nil {
    75  		proc.Stdout = options.StdWriter.Output
    76  	} else {
    77  		proc.Stdout = &stdout
    78  	}
    79  	if options.StdWriter != nil && options.StdWriter.Error != nil {
    80  		proc.Stderr = options.StdWriter.Error
    81  	} else {
    82  		proc.Stderr = &stderr
    83  	}
    84  
    85  	if options.StdWriter != nil && options.StdWriter.Input != nil {
    86  		proc.Stdin = options.StdWriter.Input
    87  	} else {
    88  		stdin, err := proc.StdinPipe()
    89  		if err != nil {
    90  			return nil, err
    91  		}
    92  		if options.OnStart != nil {
    93  			err = options.OnStart(stdin)
    94  			if err != nil {
    95  				return nil, err
    96  			}
    97  		}
    98  		_ = stdin.Close()
    99  	}
   100  
   101  	//err = proc.Run()
   102  	if err := proc.Start(); err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	err = proc.Wait()
   107  
   108  	if options.StdWriter == nil || options.StdWriter.Output == nil {
   109  		result.Stdout = stdout.String()
   110  	}
   111  	if options.StdWriter == nil || options.StdWriter.Error == nil {
   112  		result.Stderr = stderr.String()
   113  	}
   114  	result.ExitCode = proc.ProcessState.ExitCode()
   115  	result.Signal = int(proc.ProcessState.Sys().(syscall.WaitStatus).Signal())
   116  	if err != nil {
   117  		result.Success = false
   118  		result.ErrorMessage = err.Error()
   119  		if serr := result.Stderr; serr == "" {
   120  			result.Stderr += err.Error()
   121  		}
   122  		return &result, nil
   123  	}
   124  	result.Success = true
   125  	return &result, nil
   126  }
   127  
   128  func CallGenerator(ctx context.Context, tc *structs.TestCase, configDir string) ([]byte, error) {
   129  	name, args, err := ParseGeneratorScript(tc.Generator)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	gBin, err := GetCompiledBinaryFileAbsPath("generator", name, configDir)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	rel, err := RunUnixShell(&structs.ShellOptions{
   138  		Context:   ctx,
   139  		Name:      gBin,
   140  		Args:      args,
   141  		StdWriter: nil,
   142  		OnStart:   nil,
   143  	})
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  	if rel.Success {
   148  		return []byte(rel.Stdout), nil
   149  	} else {
   150  		return nil, errors.Errorf("generator error")
   151  	}
   152  }
   153  
   154  // 判断是否是题目包
   155  func IsZipFile(filePath string) (bool, error) {
   156  	fp, err := os.OpenFile(filePath, os.O_RDONLY|syscall.O_NONBLOCK, 0)
   157  	if err != nil {
   158  		return false, errors.Errorf("open file error")
   159  	}
   160  	defer fp.Close()
   161  
   162  	var magic uint32 = 0
   163  	err = binary.Read(fp, binary.BigEndian, &magic)
   164  	if err != nil {
   165  		return false, err
   166  	}
   167  	return magic == constants.ZipArchiveMagicCode, nil
   168  }
   169  
   170  // 判断是否是题目包
   171  func IsProblemPackage(filePath string) (bool, error) {
   172  	fp, err := os.OpenFile(filePath, os.O_RDONLY|syscall.O_NONBLOCK, 0)
   173  	if err != nil {
   174  		return false, errors.Errorf("open file error")
   175  	}
   176  	defer fp.Close()
   177  
   178  	var magic uint16 = 0
   179  	err = binary.Read(fp, binary.BigEndian, &magic)
   180  	if err != nil {
   181  		return false, err
   182  	}
   183  
   184  	return magic == constants.ProblemPackageMagicCode, nil
   185  }