10b57cec5SDimitry Andric //===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // FuzzerCommand represents a command to run in a subprocess. It allows callers 90b57cec5SDimitry Andric // to manage command line arguments and output and error streams. 100b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 110b57cec5SDimitry Andric 120b57cec5SDimitry Andric #ifndef LLVM_FUZZER_COMMAND_H 130b57cec5SDimitry Andric #define LLVM_FUZZER_COMMAND_H 140b57cec5SDimitry Andric 150b57cec5SDimitry Andric #include "FuzzerDefs.h" 160b57cec5SDimitry Andric #include "FuzzerIO.h" 170b57cec5SDimitry Andric 180b57cec5SDimitry Andric #include <algorithm> 190b57cec5SDimitry Andric #include <sstream> 200b57cec5SDimitry Andric #include <string> 210b57cec5SDimitry Andric #include <vector> 220b57cec5SDimitry Andric 230b57cec5SDimitry Andric namespace fuzzer { 240b57cec5SDimitry Andric 250b57cec5SDimitry Andric class Command final { 260b57cec5SDimitry Andric public: 270b57cec5SDimitry Andric // This command line flag is used to indicate that the remaining command line 280b57cec5SDimitry Andric // is immutable, meaning this flag effectively marks the end of the mutable 290b57cec5SDimitry Andric // argument list. 300b57cec5SDimitry Andric static inline const char *ignoreRemainingArgs() { 310b57cec5SDimitry Andric return "-ignore_remaining_args=1"; 320b57cec5SDimitry Andric } 330b57cec5SDimitry Andric 340b57cec5SDimitry Andric Command() : CombinedOutAndErr(false) {} 350b57cec5SDimitry Andric 36349cc55cSDimitry Andric explicit Command(const std::vector<std::string> &ArgsToAdd) 370b57cec5SDimitry Andric : Args(ArgsToAdd), CombinedOutAndErr(false) {} 380b57cec5SDimitry Andric 390b57cec5SDimitry Andric explicit Command(const Command &Other) 400b57cec5SDimitry Andric : Args(Other.Args), CombinedOutAndErr(Other.CombinedOutAndErr), 410b57cec5SDimitry Andric OutputFile(Other.OutputFile) {} 420b57cec5SDimitry Andric 430b57cec5SDimitry Andric Command &operator=(const Command &Other) { 440b57cec5SDimitry Andric Args = Other.Args; 450b57cec5SDimitry Andric CombinedOutAndErr = Other.CombinedOutAndErr; 460b57cec5SDimitry Andric OutputFile = Other.OutputFile; 470b57cec5SDimitry Andric return *this; 480b57cec5SDimitry Andric } 490b57cec5SDimitry Andric 500b57cec5SDimitry Andric ~Command() {} 510b57cec5SDimitry Andric 520b57cec5SDimitry Andric // Returns true if the given Arg is present in Args. Only checks up to 530b57cec5SDimitry Andric // "-ignore_remaining_args=1". 540b57cec5SDimitry Andric bool hasArgument(const std::string &Arg) const { 550b57cec5SDimitry Andric auto i = endMutableArgs(); 560b57cec5SDimitry Andric return std::find(Args.begin(), i, Arg) != i; 570b57cec5SDimitry Andric } 580b57cec5SDimitry Andric 590b57cec5SDimitry Andric // Gets all of the current command line arguments, **including** those after 600b57cec5SDimitry Andric // "-ignore-remaining-args=1". 61349cc55cSDimitry Andric const std::vector<std::string> &getArguments() const { return Args; } 620b57cec5SDimitry Andric 630b57cec5SDimitry Andric // Adds the given argument before "-ignore_remaining_args=1", or at the end 640b57cec5SDimitry Andric // if that flag isn't present. 650b57cec5SDimitry Andric void addArgument(const std::string &Arg) { 660b57cec5SDimitry Andric Args.insert(endMutableArgs(), Arg); 670b57cec5SDimitry Andric } 680b57cec5SDimitry Andric 690b57cec5SDimitry Andric // Adds all given arguments before "-ignore_remaining_args=1", or at the end 700b57cec5SDimitry Andric // if that flag isn't present. 71349cc55cSDimitry Andric void addArguments(const std::vector<std::string> &ArgsToAdd) { 720b57cec5SDimitry Andric Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end()); 730b57cec5SDimitry Andric } 740b57cec5SDimitry Andric 750b57cec5SDimitry Andric // Removes the given argument from the command argument list. Ignores any 760b57cec5SDimitry Andric // occurrences after "-ignore_remaining_args=1", if present. 770b57cec5SDimitry Andric void removeArgument(const std::string &Arg) { 780b57cec5SDimitry Andric auto i = endMutableArgs(); 790b57cec5SDimitry Andric Args.erase(std::remove(Args.begin(), i, Arg), i); 800b57cec5SDimitry Andric } 810b57cec5SDimitry Andric 820b57cec5SDimitry Andric // Like hasArgument, but checks for "-[Flag]=...". 830b57cec5SDimitry Andric bool hasFlag(const std::string &Flag) const { 840b57cec5SDimitry Andric std::string Arg("-" + Flag + "="); 850b57cec5SDimitry Andric auto IsMatch = [&](const std::string &Other) { 860b57cec5SDimitry Andric return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; 870b57cec5SDimitry Andric }; 880b57cec5SDimitry Andric return std::any_of(Args.begin(), endMutableArgs(), IsMatch); 890b57cec5SDimitry Andric } 900b57cec5SDimitry Andric 910b57cec5SDimitry Andric // Returns the value of the first instance of a given flag, or an empty string 920b57cec5SDimitry Andric // if the flag isn't present. Ignores any occurrences after 930b57cec5SDimitry Andric // "-ignore_remaining_args=1", if present. 940b57cec5SDimitry Andric std::string getFlagValue(const std::string &Flag) const { 950b57cec5SDimitry Andric std::string Arg("-" + Flag + "="); 960b57cec5SDimitry Andric auto IsMatch = [&](const std::string &Other) { 970b57cec5SDimitry Andric return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; 980b57cec5SDimitry Andric }; 990b57cec5SDimitry Andric auto i = endMutableArgs(); 1000b57cec5SDimitry Andric auto j = std::find_if(Args.begin(), i, IsMatch); 1010b57cec5SDimitry Andric std::string result; 1020b57cec5SDimitry Andric if (j != i) { 1030b57cec5SDimitry Andric result = j->substr(Arg.length()); 1040b57cec5SDimitry Andric } 1050b57cec5SDimitry Andric return result; 1060b57cec5SDimitry Andric } 1070b57cec5SDimitry Andric 1080b57cec5SDimitry Andric // Like AddArgument, but adds "-[Flag]=[Value]". 1090b57cec5SDimitry Andric void addFlag(const std::string &Flag, const std::string &Value) { 1100b57cec5SDimitry Andric addArgument("-" + Flag + "=" + Value); 1110b57cec5SDimitry Andric } 1120b57cec5SDimitry Andric 1130b57cec5SDimitry Andric // Like RemoveArgument, but removes "-[Flag]=...". 1140b57cec5SDimitry Andric void removeFlag(const std::string &Flag) { 1150b57cec5SDimitry Andric std::string Arg("-" + Flag + "="); 1160b57cec5SDimitry Andric auto IsMatch = [&](const std::string &Other) { 1170b57cec5SDimitry Andric return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; 1180b57cec5SDimitry Andric }; 1190b57cec5SDimitry Andric auto i = endMutableArgs(); 1200b57cec5SDimitry Andric Args.erase(std::remove_if(Args.begin(), i, IsMatch), i); 1210b57cec5SDimitry Andric } 1220b57cec5SDimitry Andric 1230b57cec5SDimitry Andric // Returns whether the command's stdout is being written to an output file. 1240b57cec5SDimitry Andric bool hasOutputFile() const { return !OutputFile.empty(); } 1250b57cec5SDimitry Andric 1260b57cec5SDimitry Andric // Returns the currently set output file. 1270b57cec5SDimitry Andric const std::string &getOutputFile() const { return OutputFile; } 1280b57cec5SDimitry Andric 1290b57cec5SDimitry Andric // Configures the command to redirect its output to the name file. 1300b57cec5SDimitry Andric void setOutputFile(const std::string &FileName) { OutputFile = FileName; } 1310b57cec5SDimitry Andric 1320b57cec5SDimitry Andric // Returns whether the command's stderr is redirected to stdout. 1330b57cec5SDimitry Andric bool isOutAndErrCombined() const { return CombinedOutAndErr; } 1340b57cec5SDimitry Andric 1350b57cec5SDimitry Andric // Sets whether to redirect the command's stderr to its stdout. 1360b57cec5SDimitry Andric void combineOutAndErr(bool combine = true) { CombinedOutAndErr = combine; } 1370b57cec5SDimitry Andric 1380b57cec5SDimitry Andric // Returns a string representation of the command. On many systems this will 1390b57cec5SDimitry Andric // be the equivalent command line. 1400b57cec5SDimitry Andric std::string toString() const { 1410b57cec5SDimitry Andric std::stringstream SS; 142*06c3fb27SDimitry Andric for (const auto &arg : getArguments()) 1430b57cec5SDimitry Andric SS << arg << " "; 1440b57cec5SDimitry Andric if (hasOutputFile()) 1450b57cec5SDimitry Andric SS << ">" << getOutputFile() << " "; 1460b57cec5SDimitry Andric if (isOutAndErrCombined()) 1470b57cec5SDimitry Andric SS << "2>&1 "; 1480b57cec5SDimitry Andric std::string result = SS.str(); 1490b57cec5SDimitry Andric if (!result.empty()) 1500b57cec5SDimitry Andric result = result.substr(0, result.length() - 1); 1510b57cec5SDimitry Andric return result; 1520b57cec5SDimitry Andric } 1530b57cec5SDimitry Andric 1540b57cec5SDimitry Andric private: 1550b57cec5SDimitry Andric Command(Command &&Other) = delete; 1560b57cec5SDimitry Andric Command &operator=(Command &&Other) = delete; 1570b57cec5SDimitry Andric 158349cc55cSDimitry Andric std::vector<std::string>::iterator endMutableArgs() { 1590b57cec5SDimitry Andric return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); 1600b57cec5SDimitry Andric } 1610b57cec5SDimitry Andric 162349cc55cSDimitry Andric std::vector<std::string>::const_iterator endMutableArgs() const { 1630b57cec5SDimitry Andric return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); 1640b57cec5SDimitry Andric } 1650b57cec5SDimitry Andric 1660b57cec5SDimitry Andric // The command arguments. Args[0] is the command name. 167349cc55cSDimitry Andric std::vector<std::string> Args; 1680b57cec5SDimitry Andric 1690b57cec5SDimitry Andric // True indicates stderr is redirected to stdout. 1700b57cec5SDimitry Andric bool CombinedOutAndErr; 1710b57cec5SDimitry Andric 1720b57cec5SDimitry Andric // If not empty, stdout is redirected to the named file. 1730b57cec5SDimitry Andric std::string OutputFile; 1740b57cec5SDimitry Andric }; 1750b57cec5SDimitry Andric 1760b57cec5SDimitry Andric } // namespace fuzzer 1770b57cec5SDimitry Andric 1780b57cec5SDimitry Andric #endif // LLVM_FUZZER_COMMAND_H 179