0.9.8.10
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
ClusterCommandInterpreter.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2007-2015 Hypertable, Inc.
3  *
4  * This file is part of Hypertable.
5  *
6  * Hypertable is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; version 3 of the
9  * License, or any later version.
10  *
11  * Hypertable is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301, USA.
20  */
21 
26 
27 #include <Common/Compat.h>
28 
30 
32 #include <Common/String.h>
33 #include <Common/System.h>
34 
35 #include <boost/algorithm/string.hpp>
36 
37 #include <cctype>
38 #include <cerrno>
39 #include <cstdlib>
40 #include <iostream>
41 #include <vector>
42 
43 extern "C" {
44 #include <sys/types.h>
45 #include <sys/wait.h>
46 #include <unistd.h>
47 }
48 
49 using namespace Hypertable;
50 using namespace Hypertable::ClusterDefinitionFile;
51 using namespace std;
52 
53 namespace {
54 
55  bool split_command_line(const string &line, vector<string> &argv) {
56  const char *base = line.c_str();
57  while (*base) {
58  while (*base && isspace(*base))
59  base++;
60  if (*base == 0)
61  break;
62  const char *ptr = base;
63  if (*ptr == '"' || *ptr == '\'') {
64  const char *end;
65  if (!TokenizerTools::find_end_char(ptr, &end)) {
66  cout << "Invalid command line - missing terminating quote" << endl;
67  return false;
68  }
69  ptr++;
70  argv.push_back(string(ptr, (end-ptr)-1));
71  base = end+1;
72  }
73  else {
74  while (*ptr && !isspace(*ptr))
75  ptr++;
76  argv.push_back(string(base, ptr-base));
77  base = ptr;
78  }
79  }
80  return true;
81  }
82 
83  bool extract_command_line_argument(const char *base, const char **end, string &arg) {
84  const char *ptr = base;
85  while (*ptr && isspace(*ptr))
86  ptr++;
87  if (*ptr == '"' || *ptr == '\'') {
88  if (!TokenizerTools::find_end_char(ptr, end)) {
89  cout << "Invalid command line - missing terminating quote" << endl;
90  return false;
91  }
92  arg.append(ptr, (*end)-ptr);
93  (*end)++;
94  boost::trim(arg);
95  }
96  else {
97  base = ptr++;
98  while (*ptr && !isspace(*ptr))
99  ptr++;
100  arg.append(base, ptr-base);
101  *end = ptr;
102  boost::trim(arg);
103  }
104  return true;
105  }
106 
107  const char *help_text[] = {
108  "",
109  "Shell commands or cluster definition tasks can be run remotely on any",
110  "set of machines using the following syntax. Type 'quit' or 'exit' to",
111  "exit the command interpreter.",
112  "",
113  "<command> [ <arg> ... ]",
114  "",
115  " This instructs the interpreter to run the shell command (<command>)",
116  " with optional arguments (<arg> ...) on all hosts specified in all",
117  " the roles in the cluster definition file.",
118  "",
119  " cluster> echo \"Hello, World!\"",
120  "",
121  "on <hostspec> <command> [ <arg> ... ]",
122  "",
123  " This instructs the interpreter to run the shell command (<command>)",
124  " with optional arguments (<arg> ...) on all hosts matching a host",
125  " specification (<hostspec>). The host specification is a compact",
126  " way to specify a large number of host names matching a pattern,",
127  " for example the host specification \"test[01-06] - test02\" expands",
128  " to: test01, test03, test04, test05, test06.",
129  "",
130  " cluster> on test[02-04]-test03 echo \"Hello, World!\"",
131  "",
132  "with <roles> <command> [ <arg> ... ]",
133  "",
134  " This instructs the interpreter to run the shell command (<command>)",
135  " with optional arguments (<arg> ...) on all hosts represented by the",
136  " comma-separated list of role names (<roles>) specified in the cluster",
137  " definition file.",
138  "",
139  " cluster> with master,slave echo \"Hello, World!\"",
140  "",
141  "!<task> [ on <hostspec> ] [<arg> ...]",
142  "",
143  " This instructs the interpreter to run a task (<task>) with optional",
144  " arguments (<arg> ...) using the default roles supplied in the task",
145  " definition or on a specific set of hosts that match a host",
146  " specification pattern by supplying the \"on <hostspec>\" after the",
147  " task name.",
148  "",
149  " cluster> !start_hyperspace",
150  " cluster> !start_hyperspace on test02",
151  "",
152  nullptr
153  };
154 
155 }
156 
157 
159  : m_command_script(script) {
160  return;
161 }
162 
163 
165  char **argv;
166  const char *end;
167  string program;
168  string target;
169  string command;
170  string trimmed_line(line);
171  boost::trim(trimmed_line);
172 
173  if (trimmed_line[0] == '!') {
174  vector<string> args;
175  if (!split_command_line(trimmed_line.substr(1), args) || args.empty())
176  return 2;
177  program = m_command_script;
178  argv = new char *[args.size()+2];
179  size_t i=0;
180  argv[i++] = (char *)program.c_str();
181  for (auto & arg : args)
182  argv[i++] = (char *)arg.c_str();
183  argv[i] = 0;
184  }
185  else if (!strcmp(trimmed_line.c_str(), "help")) {
186  for (size_t i=0; help_text[i]; i++)
187  cout << help_text[i] << "\n";
188  cout << flush;
189  return 0;
190  }
191  else if (!strncmp(trimmed_line.c_str(), "on ", 3)) {
192 
193  if (!extract_command_line_argument(trimmed_line.c_str() + 3, &end, target))
194  return 2;
195 
196  command.append(end);
197  if (command.empty()) {
198  cout << "Invalid command line" << endl;
199  return 2;
200  }
201 
202  boost::trim_if(target, boost::is_any_of("'\""));
203 
204  program.append(System::install_dir.c_str());
205  program.append("/bin/ht_ssh");
206 
207  argv = new char *[4];
208  argv[0] = (char *)program.c_str();
209  argv[1] = (char *)target.c_str();
210  argv[2] = (char *)command.c_str();
211  argv[3] = nullptr;
212 
213  }
214  else {
215 
216  if (!strncmp(trimmed_line.c_str(), "with ", 5)) {
217  if (!extract_command_line_argument(trimmed_line.c_str()+5, &end, target))
218  return 2;
219  command.append(end);
220  }
221  else {
222  target = "all";
223  command.append(trimmed_line);
224  }
225 
226  if (command.empty()) {
227  cout << "Invalid command line" << endl;
228  return 2;
229  }
230 
231  boost::trim_if(target, boost::is_any_of("'\""));
232 
233  program = m_command_script;
234  argv = new char *[5];
235  argv[0] = (char *)program.c_str();
236  argv[1] = (char *)"with";
237  argv[2] = (char *)target.c_str();
238  argv[3] = (char *)command.c_str();
239  argv[4] = nullptr;
240  }
241 
242  pid_t pid = vfork();
243 
244  if (pid == 0) {
245  if (execv(program.c_str(), argv) < 0) {
246  cout << "execv(" << program << ") failed - " << strerror(errno) << endl;
247  quick_exit(2);
248  }
249  }
250  else if (pid < 0) {
251  cout << "vfork() failed - " << strerror(errno) << endl;
252  quick_exit(2);
253  }
254  else {
255  int status;
256  waitpid(pid, &status, 0);
257  delete [] argv;
258  return status;
259  }
260  return 0;
261 }
262 
Retrieves system information (hardware, installation directory, etc)
std::string String
A String is simply a typedef to std::string.
Definition: String.h:44
STL namespace.
string m_command_script
Pathname of command script.
bool find_end_char(const char *base, const char **endp, size_t *linep)
Skips to end of block or quoted string in code.
bool status(ContextPtr &context, Timer &timer, Status &status)
Runs a status check on the master.
Definition: Utility.cc:408
Compatibility Macros for C/C++.
int execute_line(const string &line) override
Executes a command line.
Hypertable definitions
static String install_dir
The installation directory.
Definition: System.h:114
Declarations for TokenizerTools.
A String class based on std::string.
Declarations for ClusterCommandInterpreter.
Cluster definition file translation definitions.
Definition: Compiler.h:35
ClusterCommandInterpreter(const string &script)
Constructor.