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