Basic C Shell Implementation: Executing Commands - c

I'm stuck on a piece of work which requires me to write a basic shell in C to be run on minix, following the pseudo code below (1). This is my first time using C and at the minute its all a little over my head. Ive just about managed to write the first part of the program (prompting user for input, then parsing the input into tokens to break down into commands and arguments). But i am struggling with then executing the command.
I'm not looking for someone to just do the work for me and then give me the answer, what I really need is help to understand what it is that I need to do to complete the task (i.e. a general outline, resources I can use, etc.)
Any help would be greatly appreciated!
*Following suggestions that the question is too broad, I would like to point out that the main thing i'm having trouble with here is understanding the 'execve' function (even after reading the man page)
(1)
#define TRUE 1
/* declare cmd, params, envp, stat, prompt, readcmd */
while (TRUE) { /* repeat forever */
prompt(); /* display prompt */
readcmd(cmd, params); /* read input from terminal*/
if (fork() != 0) { /* fork child process */
/* parent code */
waitpid(-1, &stat, 0); /* wait for child */
} else {
/* child code */
execve(cmd, params, envp); /* execute command */
}
}
}

As mentionned in the comments below your question, if this is the first time you're using C, you should be learning how to write a basic Hello World program and not how to code a unix shell from scratch !
But since you asked, I'm going to give you some guidelines with the fragment of code you already have :-)
First of all, it is very bad practice to make blind calls to fork() and execve() without even checking if your input is a valid unix command.
You need to check first if the input is a command that can actually be executed. For this, you will have to locate (if it exists) the binary of your input command. For example, the ls command is actually a binary located in /bin/ls. Luckily for Unix users, a variable named PATH and stored in the environnement will inform the shell where to look for binaries.
$> env will print you all the variables stored in the environnement. By doing this, you will see a line that pretty much looks like this depending on each operating system:
PATH=/usr/lib/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
This line contains several paths concatenated with a colon ":" These paths correspond to the folders where common system binaries are stored.
So if the command you want to execute exist, it's binary should be located in one of those folder !
As a third argument of your main function, you can retrieve the char **env in your C program. Iterate over it until you find the PATH line.
Split this line into a list of paths
Iterate over this list and for each path, append your input command at the end. Then, pass it to the very handy stat() function that will give you several usefull informations (if the file exists, if it has execution rights...). Reading man 2 stat would also help you a lot in this exercise by the way
Over your iteration, if one the created paths exists and has execution rights, save this path and execute it later with execve(). Otherwise, if none of the created paths exists (stat() returning -1) or doesn't have execution right, the input is probably not a command. You should also learn about builtins http://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html#Bourne-Shell-Builtins
The $> which <command> is very usefull to locate existing commands or aliases
That should help you through the process but then again, I would also advise you to do several tutorials on fork() and execve().
Best of luck !

Related

No such file or directory: Running Ruby as an interpreter script for execv()

I'm writing a C program that passes data to a Ruby script. The snippet of code in main.c is below. Whenever I run, I receive an error from execv() that there is no such file or directory. I guessed that I didn't add my file to the PATH but I did and I still ended up with the same outcome.
int temp = execv("#!/usr/bin/ruby xmlReader.rb", list);
if (spec == -1)
printf("%s\n", strerror(errno));
The exec*() family of system calls are very low level, they replace the executing program with the program mentioned as the first argument, so there's no return value unless there's an error. Upon success, your program is entirely gone, overlaid with the new program. It's a one-way program-execution chain.
It doesn't know how to run command lines, and exec() is usually usually is done after a fork() operation. You almost certainly don't want to do this.
What you probably want is int temp = system("ruby xmlreader.rb"); which will run the command line given in a subprocess (ultimately using fork and exec, though this aspect is invisible to you), wait for it to complete, then return the exit value to you.

How can I handle _popen() errors in C?

Good morning;
Right now, I'm writing a program which makes a Montecarlo simulation of a physical process and then pipes the data generated to gnuplot to plot a graphical representation. The simulation and plotting work just fine; but I'm interested in printing an error message which informs the user that gnuplot is not installed. In order to manage this, I've tried the following code:
#include <stdio.h>
#include <stdlib.h>
FILE *pipe_gnuplot;
int main()
{
pipe_gnuplot = _popen("gnuplot -persist", "w");
if (pipe_gnuplot==NULL)
{
printf("ERROR. INSTALL gnuplot FIRST!\n");
exit (1);
}
return 0;
}
But, instead of printing my error message, "gnuplot is not recognized as an internal or external command, operable program or batch file" appears (the program runs on Windows). I don't understand what I'm doing wrong. According to _popen documentation, NULL should be returned if the pipe opening fails. Can you help me managing this issue? Thanks in advance and sorry if the question is very basic.
Error handling of popen (or _popen) is difficult.
popen creates a pipe and a process. If this fails, you will get a NULL result, but this occurs only in rare cases. (no more system resources to create a pipe or process or wrong second argument)
popen passes your command line to a shell (UNIX) or to the command processor (Windows). I'm not sure if you would get a NULL result if the system cannot execute the shell or command processor respectively.
The command line will be parsed by the shell or command processor and errors are handled as if you entered the command manually, e.g. resulting in an error message and/or a non-zero exit code.
A successful popen means nothing more than that the system could successfully start the shell or command processor. There is no direct way to check for errors executing the command or to get the exit code of the command.
Generally I would avoid using popen if possible.
If you want to program specifically for Windows, check if you can get better error handling from Windows API functions like CreateProcess.
Otherwise you could wrap your command in a script that checks the result and prints specific messages you can read and parse to distinguish between success and error. (I don't recommend this approach.)
Just to piggy-back on #Bodo's answer, on a POSIX-compatible system you can use wait() to wait for a single child process to return, and obtain its exit status (which would typically be 127 if the command was not found).
Since you are on Windows you have _cwait(), but this does not appear to be compatible with how _popen is implemented, as it requires a handle to the child process, which _popen does not return or give any obvious access to.
Therefore, it seems the best thing to do is to essentially manually re-implemented popen() by creating a pipe manually and spawning the process with one of the spawn[lv][p][e] functions. In fact the docs for _pipe() give an example of how one might do this (although in your case you want to redirect the child process's stdin to the write end of your pipe).
I have not tried writing an example though.

Best way in C to test whether `foo` is executable at the command line?

In C, what is the best way to find out if foo is available at the command line to execute on the host? If I were at the bash command line, I'd run type foo. In C, I could do type foo as a system call and check the exit status. But it's a good idea to avoid system calls when possible, right? Is there a better way?
My program may have to do a system command that eventually would run foo inside a shell script. (It's someone else's world and they supposedly use foo.) But if it can tell at the start that foo would be unavailable, it can avoid doing a lot of unnecessary computation because no foo means failure is certain.
type (and its cousin, which) are commands provided by most Unix-like OS's and also implemented by most shells as an intrinsic.
You could simply invoke those commands by starting a child process from your program, and then read their output. If you want to rely on the behavior of a shell's version of the command, then you must start a child process that launches that shell, then command it to run type or which. If you'd rather not use child processes, then you must re-implement their logic in your program.
Using the FreeBSD implementation of which as a guide, we can see the basic steps for doing this:
Read the value of the $PATH environment variable.
Split apart $PATH into the various directories that it contains.
Test to see if the target file/program exists at one of the various sub directories in path.
And in code:
Read $PATH from the env:
if ((p = getenv("PATH")) == NULL)
exit(EXIT_FAILURE);
Call print_matches with one of the arguments to which, providing the entire value of the $PATH:
while (argc > 0) {
memcpy(path, p, pathlen);
if (strlen(*argv) >= FILENAME_MAX ||
print_matches(path, *argv) == -1)
status = EXIT_FAILURE;
...
In print_matches, break apart the $PATH variable by splitting on the : character:
while ((d = strsep(&path, ":")) != NULL) {
And for each directory, concatenate the target program to the directory (checking to make sure the string doesn't become too big):
if (snprintf(candidate, sizeof(candidate), "%s/%s", d,
filename) >= (int)sizeof(candidate))
continue;
And then test to see if that file exists:
if (is_there(candidate)) {

Asynchronous communication between C and the Unix Environment

The Program:
The goal of my program is to make use of another program to carry out its calculations. The problem is communicating with that program asynchronously. The following is a section of my code used to call that other program. This is done via a script for distributed computing.
while(c < Images)
{
system(CommandArray[c]);
k = chdir(ImageArray[c]);
if(k != 0)
{
printf("Directory Change Failed!");
}
else
{
system("qsub SubScript.sh");
printf("Job Submitted for Image: %d \n",c+1);
sleep(1);
}
c++;
k = chdir("../");
}
c = 0;
printf("Starting 'Checking' Script");
system("./CheckingScript.sh 3");
Meaning of Variables:
ImageArray[c]: Contains the names of the sub-directories (Image_1, Image_2, etc.)
SubScript.sh: This is the script used for submitting the files generated by the rest of this program (code not shown) to the head node of the server and sending those jobs to be executed by the other program on the other nodes.
CheckingScript.sh: A simple script that checks for the existence of the "Status" file generated by SubScript. If the file exists the script waits in that directory until that file no longer exists. Then moves to the next directory. This was meant to be a way to tell if the job had been completed by the other program.
The Problem: No matter what I do I cannot get the C program to wait for the results to come back without causing those directories in which the other program is working to suddenly be emptied (the files are all still there but their contents are deleted). I have tried checking up on the directory directly from the C program but that causes the aforementioned problem. So as a last resort I have attempted to write a script to do the checking for me. And it works! (well not really...) I can use the script from the command line and it works as intended, but when I submit it via system() it doesn't echo back any of its built in statements to show that it is running. This means that system() executes and then the program ends without collecting the results from the other program that it is supposed to be waiting for.
Thanks for reading through this, I really appreciate the help! I know that the system() command is kinda thuggish so any suggestions regarding alternative options would be greatly appreciated. For more information about my program please see my blog: Link to Blog
Well, I guess the function popen() can help you collecting the results.
The function, popen() executes a command and connect a pipe between a calling program and executed command. I recommend that you refer to the man page for the function for detail.
Try the following code and use fgets() function to read from pipe.
FILE *fp = popen("./CheckingScript.sh 3", "r");
Don't forget to close fp by using pclose() instead of fclose().

UNIX run program within another program

I am trying to execute a program from within a C program (inside UNIX).
I have been given an executable ( the program requires a string input during execution and writes that input to another file called sample ) called exec and I want to execute it in program.c, but giving the string input through indirection.
For that I created a file as follows:
% vim input
I wrote the following inside the input file
content
Now in program.c,
#include<unistd.h>
int main()
{
const char* command = "./exec < input";
execvp(command, NULL);
return 0;
}
When I run the program, the content is not entered into the sample file.
But when I run it without indirection, i.e.
const char* command = "./exec";
then it works, and input entered in saved in sample file.
Can someone please tell what am I doing wrong in the indirection syntax.
Thanks.
The syntax you are using is supposed to be interpreted by a shell like bash, csh, ksh, etc.
The system call execvp only expects the path to the executable and a number of arguments, the shell is not invoked there.
To perform redirection in this manner, you'll have to use the dup2(2) system call before calling execvp:
int fd = open("input", O_RDONLY);
/* redirect standard input to the opened file */
dup2(fd, 0);
execvp("/path/to/exec", ...);
Of course, you'll need some additional error checking in a real-world program.
You can't do redirection like that with execvp. Use system() or start getting friendly with dup() and friends. You might google 'implementing redirection'.. you'll likely turn up plenty of examples of how shells (for example) handle this problem.
The exec(3) family of functions does not know anything about input redirection or parsing command lines: it tries to execute exactly the executable you give it. It's trying to search for an executable file with the name "./exec < input", which unsurprisingly does not exist.
One solution would be to use the system(3) function instead of exec. system invokes the user's shell (such as /bin/bash), which is capable of parsing the command line and doing appropriate redirections. But, system() is not as versatile as exec, so it may or may not be suitable for your needs.
The better solution is to do the input redirection yourself. What you need to do us use open(3) to open the file and dup2(3) to duplicate the file descriptor onto file descriptor 0 (standard input), and then exec the executable.

Resources