FreeNOS
Shell.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2009 Niek Linnenbank
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <TerminalCodes.h>
19 #include "Shell.h"
20 #include "ChangeDirCommand.h"
21 #include "ExitCommand.h"
22 #include "StdioCommand.h"
23 #include "WriteCommand.h"
24 #include "HelpCommand.h"
25 #include "TimeCommand.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <dirent.h>
31 #include <sys/utsname.h>
32 #include <sys/wait.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 
38 #define MAX_ARGV 16
39 
40 Shell::Shell(int argc, char **argv)
41  : POSIXApplication(argc, argv)
42 {
43  parser().setDescription("System command shell interpreter");
44  parser().registerPositional("FILE", "File(s) containing shell commands to execute", 0);
45 
50  registerCommand(new HelpCommand(this));
51  registerCommand(new TimeCommand(this));
52 }
53 
55 {
57  {
58  delete i.current();
59  }
60 }
61 
63 {
64  const Vector<Argument *> & positionals = arguments().getPositionals();
65  FILE *fp;
66  struct stat st;
67  char *contents;
68 
69  // Check if shell script was given as argument
70  if (positionals.count() > 0)
71  {
72  // Execute commands in each file
73  for (Size i = 0; i < positionals.count(); i++)
74  {
75  const char *file = *(positionals[i]->getValue());
76 
77  // Query the file size
78  if (stat(file, &st) != 0)
79  {
80  ERROR("failed to stat() `" << file << "': " << strerror(errno));
81  continue;
82  }
83 
84  // Open file
85  if ((fp = fopen(file, "r")) == NULL)
86  {
87  ERROR("failed to fopen() `" << file << "': " << strerror(errno));
88  continue;
89  }
90 
91  // Allocate buffer storage
92  contents = new char[st.st_size + 1];
93 
94  // Read the entire file into memory
95  if (fread(contents, st.st_size, 1, fp) != (size_t) 1U)
96  {
97  ERROR("failed to fread() `" << file << "': " << strerror(errno));
98  fclose(fp);
99  continue;
100  }
101  // Null terminate
102  contents[st.st_size] = 0;
103 
104  // Parse it into lines
105  String contentString(contents);
106  List<String> lines = contentString.split('\n');
107 
108  // Execute each command
109  for (ListIterator<String> i(lines); i.hasCurrent(); i++)
110  {
111  executeInput((char *) *i.current());
112  }
113 
114  // Cleanup
115  delete[] contents;
116  fclose(fp);
117  }
118  }
119  // Run an interactive Shell
120  else
121  {
122  // Show the user where to get help
123  printf( "\r\n"
124  "Entering interactive Shell. Type 'help' for the command list.\r\n"
125  "\r\n");
126 
127  // Begin loop
128  return runInteractive();
129  }
130 
131  // Done
132  return Success;
133 }
134 
136 {
137  char *cmdStr;
138 
139  // Read commands
140  while (true)
141  {
142  // Print the prompt
143  prompt();
144 
145  // Wait for a command string
146  cmdStr = getInput();
147 
148  // Enough input?
149  if (!cmdStr || strlen(cmdStr) == 0)
150  {
151  continue;
152  }
153  // Execute the command
154  executeInput(cmdStr);
155  }
156 
157  // Done (never reached)
158  return Success;
159 }
160 
161 int Shell::executeInput(const Size argc, const char **argv, const bool background)
162 {
163  char tmp[128];
164  ShellCommand *cmd;
165  int pid, status;
166 
167  for (Size i = 0; i < argc; i++)
168  {
169  DEBUG("argv[" << i << "] = " << argv[i]);
170  }
171 
172  // Ignore comments
173  if (argv[0][0] == '#')
174  return EXIT_SUCCESS;
175 
176  // Do we have a matching ShellCommand?
177  if (!(cmd = getCommand(argv[0])))
178  {
179  // If not, try to execute it as a file directly
180  if ((pid = runProgram(argv[0], argv)) != -1)
181  {
182  if (!background)
183  {
184  waitpid(pid, &status, 0);
185  return status;
186  } else
187  return EXIT_SUCCESS;
188  }
189 
190  // Try to find it on the filesystem. (temporary hardcoded PATH)
191  else if (argv[0][0] != '/' && snprintf(tmp, sizeof(tmp), "/bin/%s", argv[0]) &&
192  (pid = runProgram(tmp, argv)) != -1)
193  {
194  if (!background)
195  {
196  waitpid(pid, &status, 0);
197  return status;
198  } else
199  return EXIT_SUCCESS;
200  }
201  else
202  {
203  ERROR("exec `" << argv[0] << "' failed: " << strerror(errno));
204  }
205  }
206  // Enough arguments given?
207  else if (argc - 1 < cmd->getMinimumParams())
208  {
209  ERROR(cmd->getName() << ": not enough arguments (" << cmd->getMinimumParams() << " required)");
210  }
211  // Execute it
212  else
213  {
214  return cmd->execute(argc - 1, argv + 1);
215  }
216  // Not successful
217  return EXIT_FAILURE;
218 }
219 
220 int Shell::executeInput(char *command)
221 {
222  char *argv[MAX_ARGV];
223  Size argc;
224  bool background;
225 
226  // Valid argument?
227  if (!strlen(command))
228  {
229  return EXIT_SUCCESS;
230  }
231 
232  // Attempt to extract arguments
233  argc = parse(command, argv, MAX_ARGV, &background);
234 
235  // Run command
236  return executeInput(argc, (const char **)argv, background);
237 }
238 
239 char * Shell::getInput() const
240 {
241  static char line[1024];
242  Size total = 0;
243 #ifdef __HOST__
244  const bool echo = false;
245 #else
246  const bool echo = true;
247 #endif /* __HOST__ */
248 
249  // Read a line
250  while (total < sizeof(line) - 1)
251  {
252  // Read a character
253  const ssize_t result = read(0, line + total, 1);
254  if (result == -1)
255  {
256  return (char *) NULL;
257  }
258 
259  // Process character
260  switch (line[total])
261  {
262  // Enter
263  case '\r':
264  case '\n':
265  if (echo) printf("\r\n");
266  line[total] = ZERO;
267  return line;
268 
269  // Backspace
270  case '\b':
271  case 0x7F:
272  if (total > 0)
273  {
274  total--;
275  if (echo) printf("\b \b");
276  }
277  break;
278 
279  default:
280  if (echo) printf("%c", line[total]);
281  total++;
282  break;
283  }
284  }
285  line[total] = ZERO;
286  return line;
287 }
288 
289 void Shell::prompt() const
290 {
291  char host[128], cwd[128];
292 
293  // Retrieve current hostname
294  gethostname(host, sizeof(host));
295 
296  // Retrieve current working directory
297  if (getcwd(cwd, sizeof(cwd)) == NULL)
298  {
299  cwd[0] = '/';
300  cwd[1] = 0;
301  }
302 
303  // Print out the prompt
304  printf(WHITE "(" GREEN "%s" WHITE ") " BLUE "%s" WHITE " # ",
305  host, cwd);
306 
307 #ifdef __HOST__
308  fflush(stdout);
309 #endif /* __HOST__ */
310 }
311 
313 {
314  return m_commands;
315 }
316 
317 ShellCommand * Shell::getCommand(const char *name)
318 {
319  return m_commands.get(name) ? *m_commands.get(name) : ZERO;
320 }
321 
323 {
324  m_commands.insert(command->getName(), command);
325 }
326 
327 Size Shell::parse(char *cmdline, char **argv, Size maxArgv, bool *background)
328 {
329  Size argc;
330 
331  *background = false;
332 
333  for (argc = 0; argc < maxArgv && *cmdline; argc++)
334  {
335  while (*cmdline && *cmdline == ' ')
336  cmdline++;
337 
338  if (*cmdline == '&')
339  {
340  *background = true;
341  break;
342  }
343 
344  argv[argc] = cmdline;
345 
346  while (*cmdline && *cmdline != ' ')
347  cmdline++;
348 
349  if (*cmdline) *cmdline++ = ZERO;
350  }
351  argv[argc] = ZERO;
352  return argc;
353 }
gethostname
int gethostname(char *name, size_t namelen)
Get name of current host.
Definition: gethostname.cpp:21
Shell::~Shell
virtual ~Shell()
Destructor.
Definition: Shell.cpp:54
EXIT_FAILURE
#define EXIT_FAILURE
Unsuccessful termination.
Definition: stdlib.h:36
stat::st_size
off_t st_size
For regular files, the file size in bytes.
Definition: stat.h:226
Shell::exec
virtual Result exec()
Execute the application.
Definition: Shell.cpp:62
Shell::m_commands
HashTable< String, ShellCommand * > m_commands
All known ShellCommands.
Definition: Shell.h:131
stat
The <sys/stat.h> header shall define the stat structure.
Definition: stat.h:176
HashTable< String, ShellCommand * >
TimeCommand.h
TimeCommand
Measure the execution time of a program.
Definition: TimeCommand.h:37
GREEN
@ GREEN
Definition: VGA.h:68
fcntl.h
HelpCommand.h
strlen
size_t strlen(const char *str)
Calculate the length of a string.
Definition: strlen.cpp:21
errno
C int errno
The lvalue errno is used by many functions to return error values.
fopen
FILE * fopen(const char *filename, const char *mode)
Open a stream.
Definition: fopen.cpp:24
string.h
TerminalCodes.h
Shell::prompt
void prompt() const
Output a prompt.
Definition: Shell.cpp:289
getcwd
char * getcwd(char *buf, size_t size)
Get the pathname of the current working directory.
Definition: getcwd.cpp:24
ShellCommand
Builtin command for the Shell.
Definition: ShellCommand.h:38
String
Abstraction of strings.
Definition: String.h:41
HashIterator
Iterate through a HashTable.
Definition: HashIterator.h:39
ExitCommand.h
POSIXApplication
POSIX-compatible application.
Definition: POSIXApplication.h:35
Application::Success
@ Success
Definition: Application.h:55
HelpCommand
Prints the help info for all known ShellCommands.
Definition: HelpCommand.h:37
Application::arguments
const ArgumentContainer & arguments() const
Get program arguments.
Definition: Application.cpp:112
ArgumentParser::setDescription
void setDescription(const String &desc)
Set program description.
Definition: ArgumentParser.cpp:95
ShellCommand::getName
const char * getName() const
Get command name.
Definition: ShellCommand.cpp:31
Shell::executeInput
int executeInput(const Size argc, const char **argv, const bool background)
Executes the given input.
Definition: Shell.cpp:161
read
ssize_t read(int fildes, void *buf, size_t nbyte)
Read from a file.
Definition: read.cpp:22
wait.h
dirent.h
Shell::registerCommand
void registerCommand(ShellCommand *command)
Register a new ShellCommand.
Definition: Shell.cpp:322
String::split
List< String > split(const char delimiter) const
Split the String into parts separated by a delimiter.
Definition: String.cpp:408
WriteCommand.h
StdioCommand.h
DEBUG
#define DEBUG(msg)
Output a debug message to standard output.
Definition: Log.h:89
printf
int printf(const char *format,...)
Output a formatted string to standard output.
Definition: printf.cpp:22
WHITE
@ WHITE
Definition: VGA.h:81
ListIterator::hasCurrent
virtual bool hasCurrent() const
Check if there is a current item on the List.
Definition: ListIterator.h:104
Shell::Shell
Shell(int argc, char **argv)
Constructor.
Definition: Shell.cpp:40
HashTable::get
virtual const V * get(const K &key) const
Returns the first value for the given key.
Definition: HashTable.h:287
BLUE
@ BLUE
Definition: VGA.h:67
POSIXApplication::runProgram
int runProgram(const char *path, const char **argv)
Runs an external program.
Definition: POSIXApplication.cpp:39
Vector::count
virtual Size count() const
Returns the number of items inside the Vector.
Definition: Vector.h:204
stdio.h
strerror
char * strerror(int errnum)
The strerror function maps the number in errnum to a message string.
Definition: strerror.cpp:20
Shell::getCommands
HashTable< String, ShellCommand * > & getCommands()
Get all shell commands.
Definition: Shell.cpp:312
NULL
#define NULL
NULL means zero.
Definition: Macros.h:39
Size
unsigned int Size
Any sane size indicator cannot go negative.
Definition: Types.h:128
Shell::parse
Size parse(char *cmdline, char **argv, Size maxArgv, bool *background)
Parses an input string into separate pieces.
Definition: Shell.cpp:327
Application::Result
Result
Result codes.
Definition: Application.h:53
ChangeDirCommand
Change the current working directory.
Definition: ChangeDirCommand.h:35
Shell::runInteractive
Result runInteractive()
Executes the Shell by entering an infinite loop.
Definition: Shell.cpp:135
Shell::getInput
char * getInput() const
Fetch a command text from standard input.
Definition: Shell.cpp:239
stat
int stat(const char *path, struct stat *buf)
Get file status.
Definition: stat.cpp:25
ArgumentParser::registerPositional
Result registerPositional(const char *name, const char *description, Size count=1)
Register a positional argument.
Definition: ArgumentParser.cpp:119
stat.h
MAX_ARGV
#define MAX_ARGV
Maximum number of supported command arguments.
Definition: Shell.cpp:38
EXIT_SUCCESS
#define EXIT_SUCCESS
Successful termination.
Definition: stdlib.h:33
unistd.h
Application::parser
ArgumentParser & parser()
Get program arguments parser.
Definition: Application.cpp:102
ERROR
#define ERROR(msg)
Output an error message.
Definition: Log.h:61
Shell::getCommand
ShellCommand * getCommand(const char *name)
Get shell command.
Definition: Shell.cpp:317
ShellCommand::getMinimumParams
Size getMinimumParams() const
Get the minimum number of parameters required.
Definition: ShellCommand.cpp:41
ShellCommand::execute
virtual int execute(const Size nparams, const char **params)=0
Executes the command.
ssize_t
slong ssize_t
Used for a count of bytes or an error indication.
Definition: types.h:38
WriteCommand
Write data to a file.
Definition: WriteCommand.h:35
HashIterator::hasCurrent
virtual bool hasCurrent() const
Check if there is a current item.
Definition: HashIterator.h:83
fread
size_t fread(void *ptr, size_t size, size_t nitems, FILE *stream)
Binary input.
Definition: fread.cpp:24
List< String >
FILE
A structure containing information about a file.
Definition: stdio.h:60
stdlib.h
waitpid
pid_t waitpid(pid_t pid, int *stat_loc, int options)
Wait for a child process to stop or terminate.
Definition: waitpid.cpp:23
fclose
int fclose(FILE *stream)
Close a stream.
Definition: fclose.cpp:23
utsname.h
Vector< Argument * >
Shell.h
ZERO
#define ZERO
Zero value.
Definition: Macros.h:43
ChangeDirCommand.h
snprintf
int snprintf(char *buffer, unsigned int size, const char *fmt,...)
Write a formatted string into a buffer.
Definition: snprintf.cpp:22
HashTable::insert
virtual bool insert(const K &key, const V &value)
Inserts the given item to the HashTable.
Definition: HashTable.h:133
errno.h
StdioCommand
Change the standard Input/Output of the shell.
Definition: StdioCommand.h:35
ExitCommand
Exit the Shell.
Definition: ExitCommand.h:35
ArgumentContainer::getPositionals
const Vector< Argument * > & getPositionals() const
Get positional arguments.
Definition: ArgumentContainer.cpp:39
ListIterator
Iterate through a List.
Definition: ListIterator.h:37