I am trying to write a simple shell program. I take a user input via getline(), and tokenize that string - I keep the arguments in a char* arg[]; execvp, however, never works and always returns a neg. number.
CODE:
char* arg[100];
int i = 0;
for (i; i<100; i++){
arg[i] = malloc(11); //each one has 11 bytes
}
tokenize();
i = 0;
for (i; i<100; i++){
printf("Arg %d: %s\n", i, arg[i]);
}
pid_t child = -5;
child = fork();
if (child == 0){
if (execvp(arg[0], arg) < 0){
printf("Failed.\n");
fflush(stdout);
}
}
RUNNING:
ls
OUTPUT:
Arg 0: ls
Arg 1:
Arg 2:
....
Failed.
I'm not sure why this is happening. Any tips would be greatly appreciated.
Related
My program should use fork and exec system calls. The exec should change the child process such that it takes another command as an argument and executes
that command. For example, to display the message of the day:
./myexec cat /etc/motd
This is my current code
extern char **environ; /* environment info */
main(int argc, char **argv) {
/* argc -- number of arguments */
/* argv -- an array of strings */
char *argvNew[argc + 1];
int pid;
for(int i=0; i<argc; i++){
argvNew[i] = argv[i];
}
argvNew[argc + 1] = NULL;
printf("After For: %s\n",argvNew[0]);
printf("After For: %s\n",argvNew[1]);
printf("After For: %s\n",argvNew[2]);
printf("After For: %s\n",argvNew[3]);
if ((pid = fork()) < 0) {
fprintf(stderr, "Fork error%sstrerror\n", strerror(errno));
exit(1);
}
else if (pid == 0) {
/* child process */
if (execve(argvNew[0], argvNew, environ) < 0) {
fprintf(stderr, "Execve error%d %s\n",errno,strerror(errno));
exit(1);
}
}
else {
/* parent */
wait(0); /* wait for the child to finish */
}
}
After running ./myexecv cat etc/motd nothing happens; just the print statements. Any advice going forward?
There are multiple bugs in the shown code.
for(int i=0; i<argc; i++){
argvNew[i] = argv[i];
}
argvNew[argc+1] = NULL;
On it's face value, the NULL assignment is wrong, and will result in undefined behavior because argvNew is declared as
char *argvNew[argc + 1];
So the array contains values argvNew[0] through argvNew[argc], and argvNew[argc+1]=NULL; runs off past the end of the array, that results in undefined behavior. This should obviously be
argvNew[argc] = NULL;
But even that would also be wrong, because:
execve(argvNew[0], argvNew, environ);
argvNew[0] is copied from argv[0], which is the name of this program being executed. This is going to fork and run the same program, in the child process.
You will end up forkbombing yourself. If this is a shared server, you will make the system administrator very very mad.
You need to remove argv[0] from the equation, and copy over only argv[1], and on. The correct loop, and copy, is:
int pid;
char *argvNew[argc];
for(int i=1; i<argc; i++){
argvNew[i-1] = argv[i];
}
argvNew[argc-1] = NULL;
The calling parameters of execve() require the first parameter to be the filename to execute. Unfortunately you pass it argvNew[0] which is the same value as argv[0]. This means that you call and call again your own program and never the script. You need to shift the parameters by one:
...
for(int i=1; i<argc; i++){
argvNew[i-1] = argv[i];
}
argvNew[argc-1] = NULL;
...
cmds is a list of commands to call. In my case, I'm tring to call ls | grep c. When I run the program, nothing gets printed. It seems grep is waiting for something?
Note: If I only use ls (via execPipe(cmds,1)), everything works.
What is wrong?
int execPipe(char*** cmds,int len){
int i;
int pipefd[100][2];
for(i = 0; i < len; i++)
pipe(pipefd[i]);
i = 0;
for(i = 0; i < len; i++){
if (fork() == 0){
printf("executing #%d %s\n",i,cmds[i][0]);
//i=0: in=sdtin, out=1
//i=1: in=1,out=3
//i=2: in=3,out=5
//i=len in=len*2-1, out=sdtout
close(pipefd[i][0]);
if(i != 0){
dup2(pipefd[i-1][1],0); //read becomes the write of last one
}
if(i != len-1){
dup2(pipefd[i][1],1); //write becomes pipefd[i][1]
}
execvp(cmds[i][0],cmds[i]);
return EXIT_SUCCESS;
}
close(pipefd[i][0]);
close(pipefd[i][1]);
wait(NULL);
}
return 0;
}
int main(){
char*** cmds = malloc(2*sizeof(char**));
cmds[0] = malloc(2*sizeof(char**));
cmds[0][0] = "ls";
cmds[0][1] = NULL;
cmds[1] = malloc(3*sizeof(char**));
cmds[1][0] = "grep";
cmds[1][1] = "c";
cmds[1][2] = NULL;
execPipe(cmds,2);
return 0;
}
This code works:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static
int execPipe(char ***cmds, int len)
{
int i;
int pids[len];
int pipefd[len][2];
for (i = 0; i < len - 1; i++)
pipe(pipefd[i]);
for (i = 0; i < len; i++)
{
int pid;
if ((pid = fork()) == 0)
{
printf("PID %d: executing #%d %s\n", (int)getpid(), i, cmds[i][0]);
if (i != 0)
{
dup2(pipefd[i - 1][0], 0); // JL: Fix
}
if (i != len - 1)
{
dup2(pipefd[i][1], 1); // write becomes pipefd[i][1]
}
for (int j = 0; j < len - 1; j++) // JL: Fix
{
close(pipefd[j][0]);
close(pipefd[j][1]);
}
execvp(cmds[i][0], cmds[i]);
fprintf(stderr, "Failed to execute command %s\n", cmds[i][0]);
return EXIT_FAILURE;
}
else if (pid < 0)
{
fprintf(stderr, "failed to fork for %s\n", cmds[i][0]);
exit(1);
}
else
pids[i] = pid;
}
for (i = 0; i < len - 1; i++) // JL: Fix
{
close(pipefd[i][0]);
close(pipefd[i][1]);
}
int corpse;
int status;
int kids = len;
while (kids > 0 && (corpse = wait(&status)) > 0)
{
printf("PID %d died with status 0x%.4X\n", corpse, status);
for (i = 0; i < kids; i++)
{
if (pids[i] == corpse)
{
pids[i] = pids[kids-1];
kids--;
break;
}
}
}
return 0;
}
int main(void)
{
char ***cmds = malloc(2 * sizeof(char **));
cmds[0] = malloc(2 * sizeof(char **));
cmds[0][0] = "ls";
cmds[0][1] = NULL;
cmds[1] = malloc(3 * sizeof(char **));
cmds[1][0] = "grep";
cmds[1][1] = "c";
cmds[1][2] = NULL;
execPipe(cmds, 2);
return 0;
}
Comments:
Notice how many close operations there are.
It would be reasonable to factor the close loop into a function that gets called where needed.
You could get away without the first child closing the pipes, but it is silly to break the symmetry.
It is crucial that the parent close the pipes — but only after the pipes are finished with (that is, after all the children are created).
The wait() loop deals with the situation where the parent process had children that it didn't know about that terminate before the children it launches — a rather unusual but far from impossible circumstance. It would be possible simply to wait until all children die, but maybe one of the previously created children isn't going to terminate. The loop waits until all the known children have died and then exits.
A more complex mechanism would only have two pipes open at any one time, even in a 50 process pipeline, rather than opening all 49 pipes at once, but that's a refinement for later.
You should extend this to a 3-process or longer pipeline and check that it works. Possible pipelines include:
who | awk '{print $1}' | sort
who | awk '{print $1}' | sort | uniq -c
who | awk '{print $1}' | sort | uniq -c | sort -n
Beware: the shell removes single quotes.
I have a troubles with the following. Let's say, I have a two programs, one is "input.c" and second is "output.c". Output is a simple one and looks like this (I will paste only the most important passage).
outputbin.c
//
char buffer[512];
strncpy(buffer, argv[1], sizeof(buffer));
printf("Your output is: %s\n", buffer);
//
And this is the main passage from my input.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
pid_t pid;
char *charchar = "\x41";
int status;
char *outputbin;
int i, j, iterations;
if(argc < 2)
{
fprintf(stderr, "Usage: %s <iterations> <outputbin>\n", argv[0]);
exit(0);
}
iterations = atoi(argv[1]);
outputbin = argv[2];
pid = fork();
if(pid != 0)
{
waitpid(-1, &status, 0);
}
if(pid == 0)
{
for(i=0; i < iterations; ++i)
{
for(j = 0; j <= i; ++j)
{
printf("%s", charchar);
//execl(outputbin, outputbin, charchar, NULL);
}
printf("\n");
}
}
return 0;
}
When I compile this program using gcc and do this (without argv[2]):
./input 10
I get this:
A
AA
AAA
AAAA
AAAAA
AAAAAA
AAAAAAA
AAAAAAAA
AAAAAAAAA
AAAAAAAAAA
It's okay, but only till I remove this piece of code - "printf("%s", input);" and uncomment "execl", so:
for(j = 0; j <= i; ++j)
{
execl(output, output, input, NULL);
}
And run: ./a.out 10 ./outputbin
I get only this - Your output is: A
Only first char and that's all. How to let is execute whole "half pyramid" of strings line by line? No matter what I tried, everything end with the same result.
Assuming it executes successfully, execl does not return - it replaces the program running in the process with another program. So when calling:
for(j = 0; j <= i; ++j)
{
execl(output, output, input, NULL);
}
The loop will only iterate one time - after execl is called, outputbin will start executing. If you want to keep the loop logic, you should move it to outputbin.c so that it'll be executed there.
I'm working on a shell written in C for a school project, though this question is much less intensive than you might think. The issue is that when I type a command, for example ls, the output is displayed after the next shell> prompt that shows at the beginning of the overall shell loop. The shell looks like:
shell>ls
shell>Debug shell.c
My code:
int main(){
char line[255];
while (1){
printf("shell>");
fgets(line, 255, stdin);
if (strcmp(line, "exit\n") == 0){
break;
}
else{
char* args[255];
size_t n = 0;
for (char* ptr = strtok(line, " \n"); ptr; ptr = strtok(NULL, " \n")){
if (n >= 255){
break;
}
args[n++] = ptr;
}
args[n++] = NULL;
for (size_t i = 0; i != n; ++i){
//printf("Token %zu is '%s'.\n", i, args[i]);
}
int pid = fork();
if (pid == 0){
//child process code
int flag = execvp(args[0], args);
if (flag < 0){
perror("execvp failed");
}
}
else{
//parent process code
pid_t wait_pid(pid);
}
}
}
return 0;
}
All other errors aside, what's causing the output to be displayed this way?
pid_t wait_pid(pid); is wrong, it does not call wait_pid. When calling function, you don't specify the return type. Instead, try:
pid_t result_pid = wait_pid(pid);
// add error handling etc here, check man page for return value details
And since you don't actually wait for the child process, the parent proceeds to print the prompt immediately, giving you the output you see.
I am so close to figuring out a program I have been writing for linux shell written in C. I have been wanting to get this working for a while now, and I decided to pick it up again and have been tinkering with it for the past few weeks.
For the following code, keep in mind that the array called arrayOfCommands is dynamically filled. My code fills the arrayOfCommands with the current command being run. For the sake of my example, we will be running the command ls -l | wc and arrayOfCommands is filled with the following, depending on which time through the loop it is:
//Pass #1
arrayOfCommands[]= ("ls", "-l", NULL)
//Pass #2
arrayOfCommands[]= ("wc", NULL)
Here is what I have so far:
//PIPING
int do_command(char **args, int pipes) {
// pipes is the number of pipes in the command
// (In our example, one)
// The number of commands is one more than the
// number of pipes (In our example, two)
const int commands = pipes + 1; //Ex: 2
int i = 0;
// Set up the pipes
int pipefds[2*pipes];
for(i = 0; i < pipes; i++){
if(pipe(pipefds + i*2) < 0) {
perror("Couldn't Pipe");
exit(EXIT_FAILURE);
}
}
// Variables
int pid;
int status;
char *str_ptr;
int j = 0;
for (i = 0; i < commands; ++i) {
// A magic function that updates arrayOfCommands with
// the current command goes here. It doesn't make
// sense in the context of the code, so just believe me! :)
// Ex: The contents will be "ls -l" or "wc" depending on
// which time through the loop we are
pid = fork();
if(pid == 0) {
//if not last command
if(i < commands){
if(dup2(pipefds[j + 1], 1) < 0){
perror("dup2");
exit(EXIT_FAILURE);
}
}
//if not first command&& j!= 2*pipes
if(j != 0 ){
if(dup2(pipefds[j-2], 0) < 0){
perror("dup2");
exit(EXIT_FAILURE);
}
}
for(i = 0; i < 2*pipes; i++){
close(pipefds[i]);
}
// Should any of the below inputs be *arrayOfCommands or
// **arrayOfCommands or &arrayOfCommands?
// I'm REALLY bad with pointers
if( execvp(arrayOfCommands, arrayOfCommands) < 0 ){
perror(arrayOfCommands);
exit(EXIT_FAILURE);
}
}
else if(pid < 0){
perror("error");
exit(EXIT_FAILURE);
}
j+=2;
}
for(i = 0; i < 2 * pipes; i++){
close(pipefds[i]);
}
for(i = 0; i < pipes + 1; i++){
}
wait(&status);
}
When I run this, I get a couple of errors:
dup2: Bad file descriptor
ls: |: No such file or directory
ls: wc: No such file or directory
Could someone help me figure out the following two things:
Why am I getting these errors?
In the execvp function, what kinds if pointers am I looking for? arrayOfCommands was initialized as a char *arrayOfArgs[]
First thing:
//if not last command
if(i < commands)
should be
if(i < commands -1)
since igoes from 0 to commands -1
that should solve dup2: Bad file descriptor
ls: |: No such file or directory
ls: wc: No such file or directory
are caused by malformed arrayOfCommands. It has to be initialized by
char * arrayOfCommands[] = {"ls", "-l", NULL};
and
char * arrayOfCommands[] = {"wc", NULL};
respectively and invoked via execvp(arrayOfCommands[0], arrayOfCommands)
essentially arrayOfCommands has to be of the same format that your argument vector (typically argv) of an int main(int argc, char** argv).