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 std::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 std::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 std::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   std::vector<std::string>::iterator endMutableArgs() {
159     return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
160   }
161 
162   std::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   std::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