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#include <thread>
23
24namespace fuzzer {
25
26class Command final {
27public:
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.
31 static inline const char *ignoreRemainingArgs() {
32 return "-ignore_remaining_args=1";
33 }
34
35 Command() : CombinedOutAndErr(false) {}
36
37 explicit Command(const std::vector<std::string> &ArgsToAdd)
38 : Args(ArgsToAdd), CombinedOutAndErr(false) {}
39
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
51 ~Command() {}
52
53 // Returns true if the given Arg is present in Args. Only checks up to
54 // "-ignore_remaining_args=1".
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".
62 const std::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.
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.
72 void addArguments(const std::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.
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]=...".
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.
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]".
110 void addFlag(const std::string &Flag, const std::string &Value) {
111 addArgument("-" + Flag + "=" + Value);
112 }
113
114 // Like RemoveArgument, but removes "-[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.
125 bool hasOutputFile() const { return !OutputFile.empty(); }
126
127 // Returns the currently set output file.
128 const std::string &getOutputFile() const { return OutputFile; }
129
130 // Configures the command to redirect its output to the name file.
131 void setOutputFile(const std::string &FileName) { OutputFile = FileName; }
132
133 // Returns whether the command's stderr is redirected to stdout.
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.
141 std::string toString() const {
142 std::stringstream SS;
143 for (const 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
155private:
156 Command(Command &&Other) = delete;
157 Command &operator=(Command &&Other) = delete;
158
159 std::vector<std::string>::iterator endMutableArgs() {
160 return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
161 }
162
163 std::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 std::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