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> 22*5f757f3fSDimitry Andric #include <thread> 230b57cec5SDimitry Andric 240b57cec5SDimitry Andric namespace fuzzer { 250b57cec5SDimitry Andric 260b57cec5SDimitry Andric class Command final { 270b57cec5SDimitry Andric public: 280b57cec5SDimitry Andric // This command line flag is used to indicate that the remaining command line 290b57cec5SDimitry Andric // is immutable, meaning this flag effectively marks the end of the mutable 300b57cec5SDimitry Andric // argument list. ignoreRemainingArgs()310b57cec5SDimitry Andric static inline const char *ignoreRemainingArgs() { 320b57cec5SDimitry Andric return "-ignore_remaining_args=1"; 330b57cec5SDimitry Andric } 340b57cec5SDimitry Andric Command()350b57cec5SDimitry Andric Command() : CombinedOutAndErr(false) {} 360b57cec5SDimitry Andric Command(const std::vector<std::string> & ArgsToAdd)37349cc55cSDimitry Andric explicit Command(const std::vector<std::string> &ArgsToAdd) 380b57cec5SDimitry Andric : Args(ArgsToAdd), CombinedOutAndErr(false) {} 390b57cec5SDimitry Andric Command(const Command & Other)400b57cec5SDimitry Andric explicit Command(const Command &Other) 410b57cec5SDimitry Andric : Args(Other.Args), CombinedOutAndErr(Other.CombinedOutAndErr), 420b57cec5SDimitry Andric OutputFile(Other.OutputFile) {} 430b57cec5SDimitry Andric 440b57cec5SDimitry Andric Command &operator=(const Command &Other) { 450b57cec5SDimitry Andric Args = Other.Args; 460b57cec5SDimitry Andric CombinedOutAndErr = Other.CombinedOutAndErr; 470b57cec5SDimitry Andric OutputFile = Other.OutputFile; 480b57cec5SDimitry Andric return *this; 490b57cec5SDimitry Andric } 500b57cec5SDimitry Andric ~Command()510b57cec5SDimitry Andric ~Command() {} 520b57cec5SDimitry Andric 530b57cec5SDimitry Andric // Returns true if the given Arg is present in Args. Only checks up to 540b57cec5SDimitry Andric // "-ignore_remaining_args=1". hasArgument(const std::string & Arg)550b57cec5SDimitry Andric bool hasArgument(const std::string &Arg) const { 560b57cec5SDimitry Andric auto i = endMutableArgs(); 570b57cec5SDimitry Andric return std::find(Args.begin(), i, Arg) != i; 580b57cec5SDimitry Andric } 590b57cec5SDimitry Andric 600b57cec5SDimitry Andric // Gets all of the current command line arguments, **including** those after 610b57cec5SDimitry Andric // "-ignore-remaining-args=1". getArguments()62349cc55cSDimitry Andric const std::vector<std::string> &getArguments() const { return Args; } 630b57cec5SDimitry Andric 640b57cec5SDimitry Andric // Adds the given argument before "-ignore_remaining_args=1", or at the end 650b57cec5SDimitry Andric // if that flag isn't present. addArgument(const std::string & Arg)660b57cec5SDimitry Andric void addArgument(const std::string &Arg) { 670b57cec5SDimitry Andric Args.insert(endMutableArgs(), Arg); 680b57cec5SDimitry Andric } 690b57cec5SDimitry Andric 700b57cec5SDimitry Andric // Adds all given arguments before "-ignore_remaining_args=1", or at the end 710b57cec5SDimitry Andric // if that flag isn't present. addArguments(const std::vector<std::string> & ArgsToAdd)72349cc55cSDimitry Andric void addArguments(const std::vector<std::string> &ArgsToAdd) { 730b57cec5SDimitry Andric Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end()); 740b57cec5SDimitry Andric } 750b57cec5SDimitry Andric 760b57cec5SDimitry Andric // Removes the given argument from the command argument list. Ignores any 770b57cec5SDimitry Andric // occurrences after "-ignore_remaining_args=1", if present. removeArgument(const std::string & Arg)780b57cec5SDimitry Andric void removeArgument(const std::string &Arg) { 790b57cec5SDimitry Andric auto i = endMutableArgs(); 800b57cec5SDimitry Andric Args.erase(std::remove(Args.begin(), i, Arg), i); 810b57cec5SDimitry Andric } 820b57cec5SDimitry Andric 830b57cec5SDimitry Andric // Like hasArgument, but checks for "-[Flag]=...". hasFlag(const std::string & Flag)840b57cec5SDimitry Andric bool hasFlag(const std::string &Flag) const { 850b57cec5SDimitry Andric std::string Arg("-" + Flag + "="); 860b57cec5SDimitry Andric auto IsMatch = [&](const std::string &Other) { 870b57cec5SDimitry Andric return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; 880b57cec5SDimitry Andric }; 890b57cec5SDimitry Andric return std::any_of(Args.begin(), endMutableArgs(), IsMatch); 900b57cec5SDimitry Andric } 910b57cec5SDimitry Andric 920b57cec5SDimitry Andric // Returns the value of the first instance of a given flag, or an empty string 930b57cec5SDimitry Andric // if the flag isn't present. Ignores any occurrences after 940b57cec5SDimitry Andric // "-ignore_remaining_args=1", if present. getFlagValue(const std::string & Flag)950b57cec5SDimitry Andric std::string getFlagValue(const std::string &Flag) const { 960b57cec5SDimitry Andric std::string Arg("-" + Flag + "="); 970b57cec5SDimitry Andric auto IsMatch = [&](const std::string &Other) { 980b57cec5SDimitry Andric return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; 990b57cec5SDimitry Andric }; 1000b57cec5SDimitry Andric auto i = endMutableArgs(); 1010b57cec5SDimitry Andric auto j = std::find_if(Args.begin(), i, IsMatch); 1020b57cec5SDimitry Andric std::string result; 1030b57cec5SDimitry Andric if (j != i) { 1040b57cec5SDimitry Andric result = j->substr(Arg.length()); 1050b57cec5SDimitry Andric } 1060b57cec5SDimitry Andric return result; 1070b57cec5SDimitry Andric } 1080b57cec5SDimitry Andric 1090b57cec5SDimitry Andric // Like AddArgument, but adds "-[Flag]=[Value]". addFlag(const std::string & Flag,const std::string & Value)1100b57cec5SDimitry Andric void addFlag(const std::string &Flag, const std::string &Value) { 1110b57cec5SDimitry Andric addArgument("-" + Flag + "=" + Value); 1120b57cec5SDimitry Andric } 1130b57cec5SDimitry Andric 1140b57cec5SDimitry Andric // Like RemoveArgument, but removes "-[Flag]=...". removeFlag(const std::string & Flag)1150b57cec5SDimitry Andric void removeFlag(const std::string &Flag) { 1160b57cec5SDimitry Andric std::string Arg("-" + Flag + "="); 1170b57cec5SDimitry Andric auto IsMatch = [&](const std::string &Other) { 1180b57cec5SDimitry Andric return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; 1190b57cec5SDimitry Andric }; 1200b57cec5SDimitry Andric auto i = endMutableArgs(); 1210b57cec5SDimitry Andric Args.erase(std::remove_if(Args.begin(), i, IsMatch), i); 1220b57cec5SDimitry Andric } 1230b57cec5SDimitry Andric 1240b57cec5SDimitry Andric // Returns whether the command's stdout is being written to an output file. hasOutputFile()1250b57cec5SDimitry Andric bool hasOutputFile() const { return !OutputFile.empty(); } 1260b57cec5SDimitry Andric 1270b57cec5SDimitry Andric // Returns the currently set output file. getOutputFile()1280b57cec5SDimitry Andric const std::string &getOutputFile() const { return OutputFile; } 1290b57cec5SDimitry Andric 1300b57cec5SDimitry Andric // Configures the command to redirect its output to the name file. setOutputFile(const std::string & FileName)1310b57cec5SDimitry Andric void setOutputFile(const std::string &FileName) { OutputFile = FileName; } 1320b57cec5SDimitry Andric 1330b57cec5SDimitry Andric // Returns whether the command's stderr is redirected to stdout. isOutAndErrCombined()1340b57cec5SDimitry Andric bool isOutAndErrCombined() const { return CombinedOutAndErr; } 1350b57cec5SDimitry Andric 1360b57cec5SDimitry Andric // Sets whether to redirect the command's stderr to its stdout. 1370b57cec5SDimitry Andric void combineOutAndErr(bool combine = true) { CombinedOutAndErr = combine; } 1380b57cec5SDimitry Andric 1390b57cec5SDimitry Andric // Returns a string representation of the command. On many systems this will 1400b57cec5SDimitry Andric // be the equivalent command line. toString()1410b57cec5SDimitry Andric std::string toString() const { 1420b57cec5SDimitry Andric std::stringstream SS; 14306c3fb27SDimitry Andric for (const auto &arg : getArguments()) 1440b57cec5SDimitry Andric SS << arg << " "; 1450b57cec5SDimitry Andric if (hasOutputFile()) 1460b57cec5SDimitry Andric SS << ">" << getOutputFile() << " "; 1470b57cec5SDimitry Andric if (isOutAndErrCombined()) 1480b57cec5SDimitry Andric SS << "2>&1 "; 1490b57cec5SDimitry Andric std::string result = SS.str(); 1500b57cec5SDimitry Andric if (!result.empty()) 1510b57cec5SDimitry Andric result = result.substr(0, result.length() - 1); 1520b57cec5SDimitry Andric return result; 1530b57cec5SDimitry Andric } 1540b57cec5SDimitry Andric 1550b57cec5SDimitry Andric private: 1560b57cec5SDimitry Andric Command(Command &&Other) = delete; 1570b57cec5SDimitry Andric Command &operator=(Command &&Other) = delete; 1580b57cec5SDimitry Andric endMutableArgs()159349cc55cSDimitry Andric std::vector<std::string>::iterator endMutableArgs() { 1600b57cec5SDimitry Andric return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); 1610b57cec5SDimitry Andric } 1620b57cec5SDimitry Andric endMutableArgs()163349cc55cSDimitry Andric std::vector<std::string>::const_iterator endMutableArgs() const { 1640b57cec5SDimitry Andric return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); 1650b57cec5SDimitry Andric } 1660b57cec5SDimitry Andric 1670b57cec5SDimitry Andric // The command arguments. Args[0] is the command name. 168349cc55cSDimitry Andric std::vector<std::string> Args; 1690b57cec5SDimitry Andric 1700b57cec5SDimitry Andric // True indicates stderr is redirected to stdout. 1710b57cec5SDimitry Andric bool CombinedOutAndErr; 1720b57cec5SDimitry Andric 1730b57cec5SDimitry Andric // If not empty, stdout is redirected to the named file. 1740b57cec5SDimitry Andric std::string OutputFile; 1750b57cec5SDimitry Andric }; 1760b57cec5SDimitry Andric 1770b57cec5SDimitry Andric } // namespace fuzzer 1780b57cec5SDimitry Andric 1790b57cec5SDimitry Andric #endif // LLVM_FUZZER_COMMAND_H 180