Simple shell with fork and exec - c

I am trying to make a simple shell running any command from PATH, lets say ls or pwd du gedit etc. I am having trouble with exec.I require that if i enter space nothing happens and if i type exit it terminates.Any help is appreciated
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#define BUFFER 1024
int main() {
char line[BUFFER];
char* args[100];
char* path = "";
char program[20];
while(1){
printf("$ ");
if(!fgets(line, BUFFER, stdin))
break;
size_t length = strlen(line);
if (line[length - 1] == '\n')
line[length - 1] = '\0';
if(strcmp(line, "exit")==0) break;
strcpy(program,path);
strcat(program,line);
int pid= fork(); //fork child
if(pid==0){ //Child
execlp(program,line,(char *)NULL);
}else{ //Parent
wait(NULL);
}
}
}

You have 2 fgets() call. Remove the first one fgets(line, BUFFER, stdin);.
fgets() will read in the newline if there's space in buffer. You need to remove it because when you input exit, you'll actually input exit\n and there's no command as /bin/exit\n.
The below code demonstrates removing newline character:
if(!fgets(line, BUFFER, stdin))
break;
char *p = strchr(line, '\n');
if (p) *p = 0;
Your usage is wrong. Check the manual of execl. You need to pass the arguments: execl(program, line, (char *)NULL);
Notice the cast of last argument of NULL. In case, NULL is defined as 0 then the cast becomes necessary because execl is a variadic function.
A modified example using execvp:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#define BUFFER 1024
int main(void) {
char line[BUFFER];
while(1) {
printf("$ ");
if(!fgets(line, BUFFER, stdin)) break;
char *p = strchr(line, '\n');
if (p) *p = 0;
if(strcmp(line, "exit")==0) break;
char *args[] = {line, (char*)0};
int pid= fork(); //fork child
if(pid==0) { //Child
execvp(line, args);
perror("exec");
exit(1);
} else { //Parent
wait(NULL);
}
}
return 0;
}

Hi See my correction below and see my comment as well.
int main() {
char line[BUFFER];
char* args[100];
char* path = "/bin/";
char program[20];
char command[50];
while(1){
printf("$ ");
if(!fgets(line, BUFFER, stdin))
break;
memset(command,0,sizeof(command));
if(strncmp(line, "exit", (strlen(line)-1))==0) break;
strncpy(command,line,(strlen(line)-1));
strcpy(program, path);
strcat(program,command);
int pid= fork(); //fork child
if(pid==0){ //Child
execl(program,command,NULL);
exit(0);// you must exit from the child because now you are inside while loop of child. Otherwise you have to type exit twice to exit from the application. Because your while loop also became the part of every child and from the child again it will call fork and create a child again
}else{
wait(NULL);
}
}
}
Also to support all command execution see the function execl how you need to pass the parameters in it. Accordingly you need to split your command and create the parameter list properly for execl.

Related

Use read() to get the name of a file from a pipe and open() it in c

This is all done on a linux machine.
I have a pipe, fp, sending from the parent to the child the name of a file using a buffer.
The buffer is:
char buf[20];
the child has the following code:
{
//we are in the child
close(fp[1]);
int fd;
read(fp[0],buf,20);
if((fd=(open(buf, O_RDONLY)))==-1) exit(1);
else exit(0);
close(fp[0]);
}
Even if I type in the name of a file that exists, I'm getting the exit status of 1. So...
this unfortunately doesn't work. The issue is that the buff itself not only does '\n', but also also plenty of '\0', all of which don't actually exist in the name of real file. I've tried replacing the '\n' with a '\0' but that also doesn't work. How can I solve this?
Here's the whole code.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
int main(){
int fp[2];
if (pipe(fp) < 0){
printf("error creating pipe\n");
exit(-1);
}
int id;
char buf[20];
id=fork();
//father process here --------------------------------
if (id!=0){
close(fp[0]); //closing read
printf("program name: ");
fflush(stdout);
read(STDIN_FILENO,buf,20);
write(fp[1],buf,20);
int waitstatus, exitcode;
wait(&waitstatus);
//check if exited correctly
if (WIFEXITED(waitstatus))
exitcode = WEXITSTATUS(waitstatus);
else
{
printf("Bad exit\n");
return 0;
}
if (exitcode==1) printf("error, file doesn't exist\n");
else printf("file does exist\n");
close(fp[1]);
}
//child process here --------------------
else{
close(fp[1]); //closing write
int fd;
read(fp[0],buf,20);
//write(STDOUT_FILENO, buf, 20);
if((fd=(open(buf, O_RDONLY)))==-1) exit(1);
exit(0);
close(fp[0]);
}
}
You send the full buf which contains a newline and other indeterminate values. You need to remove the newline and I suggest that you only send what you need on the receiving end.
printf("program name: ");
fflush(stdout);
if(fgets(buf, sizeof buf, stdin)==NULL) return 1;
size_t len = strlen(buf);
buf[len - 1] = '\0'; // remove the newline
write(fp[1], buf, len); // only send what you actually need

Why does the execlp in child not return anything into stdout?

I have a program that reads in line by line from a text file. Each line has the layout
command arg1 arg2 arg3
and I have read it in so that I have 2 arrays, 1 which contains the string and another which points to each string value. eg
char read_in_line[128]
char* command[100]
and so:
command[0] = command arg1 arg2 arg3
command[1] = command arg1
etc.
I then have this command array as an input to a function that uses fork and pipes. The following is a snippet of this function and note it is in a while loop which will continue while *cmd != NULL
void piping(char* cmd[100]{
else if(pid == 0){
//child does not need to read
close(thepipe[0]);
dup2(thepipe[1],1);
close(thepipe[1]);
execlp(*cmd,*cmd,NULL);
However, this does not return anything. My C program compiles without showing any errors, however in my stdout I can not see the execution of any of the commands i sent into the function.
EDIT:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define BUFFERSIZE 128
#define oops(m,x) {perror(m); exit(x);}
void piping(char **cmd[BUFFERSIZE]){
pid_t pid;
int thepipe[2];
int in = 0;
//while there are still commands
while (*cmd != NULL){
pipe(thepipe);
//fork error case
if((pid = fork()) < 0)
oops("cannot fork",1);
//child
if(pid == 0){
//child does not need to read
close(thepipe[0]);
if(dup2(thepipe[1],1)== -1)
oops("Error redirecting stdout",2);
//duplication succesful can now close thepipe[1]
close(thepipe[1]);
//execute the command
execvp(*cmd[0], *cmd);
exit(-1);
}
else{
//parent does not write to pipe
close(thepipe[1]);
//setting up parent input to read from the pipe
dup2(thepipe[0],0);
close(thepipe[0]);
//wait until child finishes
wait(NULL);
cmd++;
}
}
}
int main(int argc, char* argv[]){
char **command[BUFFERSIZE];
char read_in_line[BUFFERSIZE];
int i = 0;
int counter =0;
int counter2 =0;
//reading in line by line until end of file is reached
FILE* fp = fopen("test.txt","r");
while( fgets(read_in_line, BUFFERSIZE, fp) != NULL ){
int j = 0;
//setting up memory for arguments given that we know there is a max
//of 10 arguments per line
char **arguments = (char**) calloc(16, sizeof(char*));
command[i] = arguments;
//Will break up the line read in when a newline is argument resulting in one
//string containing the commands and arguments
//this string will then be broken up every time a space is met so that
//commands and arguments can be seperated, and saved to command[i][j]
char *t = strtok(read_in_line, "\n");
char *argument = strtok(t, " ");
command[i][j] = strdup(argument);
while(argument != NULL){
argument =strtok(NULL, " ");
if(argument != NULL){
command[i][++j] = strdup(argument);
}
}
i++;
}
piping(command);
return (0);
}
The program below works as expected:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
int main(void)
{
int rc;
rc=execlp("/bin/date", "deet", (char*) NULL);
printf("Rc=%d,%d(%s)\n", rc, errno, strerror(errno));
return 0;
}
Next step: add some arguments. (next step: fix the plumbing)
rc=execlp("/bin/ls", "ls", "-li", (char*) NULL);

Infinite loop caused by perror scanf through a pipe

I have been writing a c program which pipes data from one file to the next however it has been infinite looping.
What i have discovered so far. The infinite loop is caused on file c1.c where perror (or stderr) skips the scanf. If scanf does work. The program infinite loops further down the track printing out the perror even though it is past that section!
My code is below
controller.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>
int main(int ac, char**av)
{
int pipez[2];
int piped[2];
int status;
pid_t pid;
if (pipe (pipez) == -1){
perror("could not make pipe");
return 1;
}
if ((pid = fork()) == -1){
perror("fork");
return 1;
}
if(pid == 0){
close(pipez[1]);
dup2(pipez[0],0);
close(pipez[0]);
execvp("./c1",av);
perror("demo);
_exit(1);
}
else{
close(pipez[0]);
dup2(pipez[1],1);
close(pipez[1]);
execvp("./c2",av);
perror("demo");
exit(1);
}
waitpid(pid,&status,0);
if(WIFEXITED(status)){
printf("[%d] TERMINATED (Status: %d)\n", pid, WEXITSTATUS(status));
}
if(pipe (piped) == -1){
perror("could not make pipe");
return 1;
}
if((pid = fork()) == -1){
perror("fork");
return 1;
}
if (pid == 0){
close(piped[1]);
dup2(piped[0],0);
close(piped[0]);
execvp("./c2", av);
perror("demo");
_exit(1);
}
else{
close(piped[0]);
dup2(piped(piped[1],1);
close(piped[1]);
execvp("./c3",av);
perror("demo");
exit(1);
}
waitpid(pid, &status, 0);
if (WIFEXITED(status)){
printf("[%d] TERMINATED (Status: %d)\n", pid, WEXITSTATUS(status));
}
return 0;
}
c1.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 256
//This program is causing infinite loop, tried fflush and fgets and scanf
// It will run independently but will loop via the pipe
int main(int a, char**av){
char store[BUFSIZE];
memset(store, '\0', sizeof(store));
while(strcmp(store, "exit!") != 0){
perror("Please enter next line of input (type 'exit!' to stop) \n"); //This repeats itself infinitely
fgets(store, BUFSIZE, stdin);
printf("%s",store); // This also repeats itself dependant on where i put
//fflush or another printf. Repeated outputs occur in blocks
}
return 0;
}
c2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 256
char *strlwr(char *str);
int main(int ac, char**av){
char store[BUFSIZE];
while(strcmp(store, "exit!") != 0){
scanf("%s", store);
printf("%s", strlwr(store));
}
return 0;
}
char *strlwr(char *str){
unsigned char *p = (unsigned char *)str;
while(*p){
*p = tolower((unsigned char)*p);
p++;
}
return str;
}
c3.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 256
int main(int ac, char**av){
char store[BUFSIZE];
int n = 0;
while(strcmp(store, "exit!") != 0{
scanf("%s",store);
printf("Line %d: %s\n",n,store);
n++;
}
return 0;
}
fgets leaves '\n' char in the store buffer so, easiest fix is to add '\n' to the compared string:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 256
//This program is causing infinite loop, tried fflush and fgets and scanf
// It will run independently but will loop via the pipe
int main(int a, char**av)
{
char store[BUFSIZE];
memset(store, '\0', sizeof(store));
while (strcmp(store, "exit!\n") != 0)
{
perror("Please enter next line of input (type 'exit!' to stop) \n"); //This repeats itself infinitely
fgets(store, BUFSIZE, stdin);
printf("%s", store); // This also repeats itself dependant on where i put
// fflush or another printf. Repeated outputs occur in blocks
}
return 0;
}
Moreover, as you can see, init value '/0' is not acceptable; it must be '\0'. Your compiler is probably telling you something like
test.c: In function ‘main’:
test.c:242:16: warning: multi-character character constant [-Wmultichar]
memset(store, '/0', sizeof(store));
^
BTW memset, in this case is useless because fgets does all the job, but anyway, if you want you buffer set to nul, just declare:
char store[BUFSIZE] = {0};
Best solution using fgets is (obviously) always manage the newline left:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 256
//This program is causing infinite loop, tried fflush and fgets and scanf
// It will run independently but will loop via the pipe
int main(void)
{
char store[BUFSIZE] = {0};
char *pos;
while (strcmp(store, "exit!") != 0)
{
perror("Please enter next line of input (type 'exit!' to stop) \n"); //This repeats itself infinitely
fgets(store, BUFSIZE, stdin);
if ((pos = strchr(store, '\n')) != NULL)
*pos = '\0';
else
fprintf(stderr, "String too long!\n");
printf("%s", store); // This also repeats itself dependant on where i put
// fflush or another printf. Repeated outputs occur in blocks
}
return 0;
}

execv waiting for input input instead of executing program

i am have a program named "test" that executes another program called "hello". After receiving the name of the desired program, my program seems to wait for more input in order to display the "hello" program code. A snippet of example code
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h> /* for fork */
#include <sys/wait.h> /* for wait */
int main() {
char *temp[] = {NULL,NULL,NULL,NULL};
char buf[BUFSIZ];
char s[256];
while(1) {
printf("type r at next input\n");
fgets(buf, sizeof(buf), stdin);
strtok(buf, "\n");
if((strcmp(buf,"r")) == 0) { //if r typed
printf("run what file : ");
scanf("%s ",s);
pid_t i = fork();
if (i == 0) //we are in child process
{
execv(s,temp);
_exit(1);
}
if (i != 0) { //parent
wait(NULL);
}
}
else
exit(0);
}
}
the "hello" program is...
#include<stdio.h>
main(int argc, char* argv[])
{
printf("\nHello World\n");
}
and an example run from the shell on linux is...* means my input, // are comments
issac#issac-ThinkPad-T440s ~$ ./a.out
type r at next input
*r*
run what file : *hello*
*r* //i cant proceed unless i type a character so i input *r*?
Hello World //after ?additional? input it finally displays the "hello" program code
type r at next input //loops back but program skips my input?
run what file ex ? hello :
i realize this may be a simple mistake but i cant figure out whats wrong with this. i realize the last part with the skipped input is probably due to the input buffer having the newline in there but more confusing is why after executing "hello" my program waits for more input to display result.
To answer your first question: drop a trailing whitespace in scanf() so it looks like this: scanf("%s",s);.
To answer your second question: your problem is that you mix scanf() and fgets(). A newline is not consumed be scanf() and is passed as a new input to the next (non-first) fgets. The easiest solution is to use fgets in both places:
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h> /* for fork */
#include <sys/wait.h> /* for wait */
#include <string.h>
int main() {
char *temp[] = {NULL,NULL,NULL,NULL};
char buf[BUFSIZ];
char s[256];
while(1) {
printf("type r at next input\n");
fgets(buf, sizeof(buf), stdin);
strtok(buf, "\n");
if((strcmp(buf,"r")) == 0) { //if r typed
printf("run what file : ");
fgets(s, sizeof(s), stdin);
strtok(s, "\n");
pid_t i = fork();
if (i == 0) //we are in child process
{
execv(s,temp);
_exit(1);
}
if (i != 0) { //parent
wait(NULL);
}
}
else
exit(0);
}
}

C - WHILE Loop with fork() / pipe() inside

I have a problem where I must implement a key logger into a shell we have made in class. I am having trouble getting the flow of the program within a while loop to continue looping after a child process is created and it has ran execlp().
Here is a simple program I have made to work on the part I am having trouble with.. My main program, pipe.c, includes the parent/child process with a while loop that "should" continue getting an input from the user with fgets(), create a child process, use dup2(), write to stdout, then the child process invoke the receive.c executable which will get the input from stdin and display it..
/* file: pipe.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
int key_logger_on = 0;
int p[2];
pid_t pid;
char str[256];
char input[1024];
int status;
char * file = "test.txt";
printf("Input :: ");
while(fgets(input, sizeof(input), stdin)) {
if (pipe(p)==-1) {
perror("Pipe create error");
exit(1);
}
if ((pid=fork())==-1) {
perror("Fork create error");
exit(1);
}
if (pid==0) {
close(p[1]); // Close write
dup2(p[0],0);
close(p[0]);
execlp("receive",file,NULL);
}
else {
close(p[0]); // Close read
fflush(stdout);
dup2(p[1],1);
close(p[1]);
write(1, input, strlen(input)+1);
waitpid(pid, NULL, 0);
}
printf("Input :: ");
}
}
Here is the simple receive.c that gets the stdin of the input and displays it. The file is just a test of passing a parameter.
/* file: receive.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
char input[256];
fgets(input, sizeof(input), stdin);
printf("FILE: %s RECEIVE: %s", argv[0],input);
return 0;
}
Right now, all this does for me is when ran the first time, it gets the input, sends it to stdout, child calls receive, prints out the input, and then the whole parent program exits, the while loop is ignored, everything just ends. I'm very new to forks and pipes so this is very frustrating to deal with! Even made me post a question on here for the first time! Thank you very much in advance.
Did it today as repetition task for me . CHeck this code . I tested it with your receive too :
#define PREAD 0
#define PWRITE 1
/*
*
*/
int main(int argc, char** argv) {
int key_logger_on = 0;
int pIn[2];
int pOut[2];
pid_t pid;
char str[256];
char input[1024] = "";
int status;
char file[] = "test.txt";
char buf;
printf("Input :: ");
while (fgets(input,sizeof(input),stdin)) {
char nChar;
int nResult;
if (pipe(pIn) < 0) {
perror("allocating pipe for child input redirect");
return -1;
}
if (pipe(pOut) < 0) {
close(pIn[PREAD]);
close(pIn[PWRITE]);
perror("allocating pipe for child output redirect");
return -1;
}
pid = fork();
if ( pid==0) {
// child continues here
// redirect stdin
if (dup2(pIn[PREAD], 0) == -1) {
perror("stdin");
return -1;
}
// redirect stdout
if (dup2(pOut[PWRITE], 1) == -1) {
perror("stdout");
return -1;
}
// redirect stderr
if (dup2(pOut[PWRITE], 2) == -1) {
perror("stderr");
return -1;
}
// all these are for use by parent only
close(pIn[PREAD]);
close(pIn[PWRITE]);
close(pOut[PREAD]);
close(pOut[PWRITE]);
// run child process image
nResult = execl("receive",file,NULL);
exit(nResult);
} else if (pid > 0) {
// parent continues here
// close unused file descriptors, these are for child only
close(pIn[PREAD]);
close(pOut[PWRITE]);
write(pIn[PWRITE], input, strlen(input));
// char by char reading
while (read(pOut[PREAD], &nChar, 1) == 1) {
write(STDOUT_FILENO, &nChar, 1);
}
// close we done
close(pIn[PWRITE]);
close(pOut[PREAD]);
}
printf("Input :: ");
}
}

Resources