1 //===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // FuzzerCommand represents a command to run in a subprocess. It allows callers 10 // to manage command line arguments and output and error streams. 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LLVM_FUZZER_COMMAND_H 14 #define LLVM_FUZZER_COMMAND_H 15 16 #include "FuzzerDefs.h" 17 #include "FuzzerIO.h" 18 19 #include <algorithm> 20 #include <sstream> 21 #include <string> 22 #include <vector> 23 24 namespace fuzzer { 25 26 class Command final { 27 public: 28 // This command line flag is used to indicate that the remaining command line 29 // is immutable, meaning this flag effectively marks the end of the mutable 30 // argument list. ignoreRemainingArgs()31 static inline const char *ignoreRemainingArgs() { 32 return "-ignore_remaining_args=1"; 33 } 34 Command()35 Command() : CombinedOutAndErr(false) {} 36 Command(const Vector<std::string> & ArgsToAdd)37 explicit Command(const Vector<std::string> &ArgsToAdd) 38 : Args(ArgsToAdd), CombinedOutAndErr(false) {} 39 Command(const Command & Other)40 explicit Command(const Command &Other) 41 : Args(Other.Args), CombinedOutAndErr(Other.CombinedOutAndErr), 42 OutputFile(Other.OutputFile) {} 43 44 Command &operator=(const Command &Other) { 45 Args = Other.Args; 46 CombinedOutAndErr = Other.CombinedOutAndErr; 47 OutputFile = Other.OutputFile; 48 return *this; 49 } 50 ~Command()51 ~Command() {} 52 53 // Returns true if the given Arg is present in Args. Only checks up to 54 // "-ignore_remaining_args=1". hasArgument(const std::string & Arg)55 bool hasArgument(const std::string &Arg) const { 56 auto i = endMutableArgs(); 57 return std::find(Args.begin(), i, Arg) != i; 58 } 59 60 // Gets all of the current command line arguments, **including** those after 61 // "-ignore-remaining-args=1". getArguments()62 const Vector<std::string> &getArguments() const { return Args; } 63 64 // Adds the given argument before "-ignore_remaining_args=1", or at the end 65 // if that flag isn't present. addArgument(const std::string & Arg)66 void addArgument(const std::string &Arg) { 67 Args.insert(endMutableArgs(), Arg); 68 } 69 70 // Adds all given arguments before "-ignore_remaining_args=1", or at the end 71 // if that flag isn't present. addArguments(const Vector<std::string> & ArgsToAdd)72 void addArguments(const Vector<std::string> &ArgsToAdd) { 73 Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end()); 74 } 75 76 // Removes the given argument from the command argument list. Ignores any 77 // occurrences after "-ignore_remaining_args=1", if present. removeArgument(const std::string & Arg)78 void removeArgument(const std::string &Arg) { 79 auto i = endMutableArgs(); 80 Args.erase(std::remove(Args.begin(), i, Arg), i); 81 } 82 83 // Like hasArgument, but checks for "-[Flag]=...". hasFlag(const std::string & Flag)84 bool hasFlag(const std::string &Flag) const { 85 std::string Arg("-" + Flag + "="); 86 auto IsMatch = [&](const std::string &Other) { 87 return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; 88 }; 89 return std::any_of(Args.begin(), endMutableArgs(), IsMatch); 90 } 91 92 // Returns the value of the first instance of a given flag, or an empty string 93 // if the flag isn't present. Ignores any occurrences after 94 // "-ignore_remaining_args=1", if present. getFlagValue(const std::string & Flag)95 std::string getFlagValue(const std::string &Flag) const { 96 std::string Arg("-" + Flag + "="); 97 auto IsMatch = [&](const std::string &Other) { 98 return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; 99 }; 100 auto i = endMutableArgs(); 101 auto j = std::find_if(Args.begin(), i, IsMatch); 102 std::string result; 103 if (j != i) { 104 result = j->substr(Arg.length()); 105 } 106 return result; 107 } 108 109 // Like AddArgument, but adds "-[Flag]=[Value]". addFlag(const std::string & Flag,const std::string & Value)110 void addFlag(const std::string &Flag, const std::string &Value) { 111 addArgument("-" + Flag + "=" + Value); 112 } 113 114 // Like RemoveArgument, but removes "-[Flag]=...". removeFlag(const std::string & Flag)115 void removeFlag(const std::string &Flag) { 116 std::string Arg("-" + Flag + "="); 117 auto IsMatch = [&](const std::string &Other) { 118 return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; 119 }; 120 auto i = endMutableArgs(); 121 Args.erase(std::remove_if(Args.begin(), i, IsMatch), i); 122 } 123 124 // Returns whether the command's stdout is being written to an output file. hasOutputFile()125 bool hasOutputFile() const { return !OutputFile.empty(); } 126 127 // Returns the currently set output file. getOutputFile()128 const std::string &getOutputFile() const { return OutputFile; } 129 130 // Configures the command to redirect its output to the name file. setOutputFile(const std::string & FileName)131 void setOutputFile(const std::string &FileName) { OutputFile = FileName; } 132 133 // Returns whether the command's stderr is redirected to stdout. isOutAndErrCombined()134 bool isOutAndErrCombined() const { return CombinedOutAndErr; } 135 136 // Sets whether to redirect the command's stderr to its stdout. 137 void combineOutAndErr(bool combine = true) { CombinedOutAndErr = combine; } 138 139 // Returns a string representation of the command. On many systems this will 140 // be the equivalent command line. toString()141 std::string toString() const { 142 std::stringstream SS; 143 for (auto arg : getArguments()) 144 SS << arg << " "; 145 if (hasOutputFile()) 146 SS << ">" << getOutputFile() << " "; 147 if (isOutAndErrCombined()) 148 SS << "2>&1 "; 149 std::string result = SS.str(); 150 if (!result.empty()) 151 result = result.substr(0, result.length() - 1); 152 return result; 153 } 154 155 private: 156 Command(Command &&Other) = delete; 157 Command &operator=(Command &&Other) = delete; 158 endMutableArgs()159 Vector<std::string>::iterator endMutableArgs() { 160 return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); 161 } 162 endMutableArgs()163 Vector<std::string>::const_iterator endMutableArgs() const { 164 return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); 165 } 166 167 // The command arguments. Args[0] is the command name. 168 Vector<std::string> Args; 169 170 // True indicates stderr is redirected to stdout. 171 bool CombinedOutAndErr; 172 173 // If not empty, stdout is redirected to the named file. 174 std::string OutputFile; 175 }; 176 177 } // namespace fuzzer 178 179 #endif // LLVM_FUZZER_COMMAND_H 180