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