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