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);
}
}
Related
So, I'm playing with pipes in c, and I have an exercise where I call a program through command line as this: "./self 1" which then calls itself with execlp but with an argument 2: "./self 2" which further on calls itself with argument 3: "./self 3". The point of these processes is this: process1 takes a line from keyboard and puts it in pipe1, then process2 gets the line from pipe1 and puts it in pipe2, then process3 gets it from pipe2 and counts the number of space characters. This code never works if I dont print a newline character on the screen before taking inputs with fprintf(stdout,"\n"); . Why is that?
Here is the code:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
if (strcmp(argv[1], "1") == 0) {
int fdpipe1[2];
if (pipe(fdpipe1)) {
printf("Error pipe1\n");
return 0;
}
pid_t p;
p = fork();
if (p == 0) {
close(fdpipe1[1]);
dup2(fdpipe1[0], 0);
execlp("./self", "./self", "2", NULL);
} else {
close(fdpipe1[0]);
fprintf(stdout, "\n");
dup2(fdpipe1[1], 1);
char input[100];
gets(input);
puts(input);
wait(NULL);
}
}
else if (strcmp(argv[1], "2") == 0) {
int fdpipe2[2];
if (pipe(fdpipe2)) {
printf("Error pipe2\n");
return 0;
}
pid_t p;
p = fork();
if (p == 0) {
close(fdpipe2[1]);
dup2(fdpipe2[0], 0);
execlp("./self", "./self", "3", NULL);
} else {
close(fdpipe2[0]);
fprintf(stdout, "\n");
dup2(fdpipe2[1], 1);
char input[100];
gets(input);
puts(input);
wait(NULL);
}
}
else if (strcmp(argv[1], "3") == 0) {
char input[100];
gets(input);
int i = 0, counter = 0;
while (input[i] != '\0') {
if (input[i++] == ' ') counter++;
}
printf("%d\n", counter);
}
return;
}
In this kind of construct, when you connect stdout from a process to stdin of another process via unnamed pipe, a newline character is added usually to ensure the stream is sent, i.e. the stdout buffer is flushed, as a parallel example, when you use scanf, only when you hit enter (a newline is added to stdin) is the stream read, a similar principle applies here.
I would suggest you use STDIN_FILENO and STDOUT_FILENO
built in macros instead of the hard coded file descriptors, if not for anything else, it makes the code more readable for someone who is unfamiliar with the matter.
Please avoid using gets, this is a dangerous function, it does not check the bounds of the destination buffer, it can cause all kinds of trouble, so much so it was deprecated and later removed from the standard, though it still can be used with some compilers, for legacy reasons I would imagine, check this fantastic answer on a post about this topic:
Why is the gets function so dangerous that it should not be used?
The advice is to use fgets instead.
This question already has answers here:
Why is printf before exevp not running?
(1 answer)
Prints before execl is not visible in output
(1 answer)
Closed 4 years ago.
This program does two things:
1) Duplicates the action of shell
2) Record user input to a tmp.log file
The problem here is that in my child process, printf("ABC"); does nothing.
Outputting the log file works fine, but it just doesn't print.
Why does this behave like this?
I know execvp is supposed to replace the current process but that doesn't explain why it would execute the output but not the print.
I saw the below link but this doesn't answer my question.
exevp skips over all code until wait call in c
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <wordexp.h>
void execute(char *user_input)
{
pid_t pid;
int state_loc;
if( (pid = fork()) == -1){
printf("fork failed\n");
exit(1);
}
else if(pid == 0){
FILE *f;
//open the file and append. Create if not there.
f = fopen("tmp.log", "a+");
if (f == NULL) { printf("Something is wrong");}
struct tm *p;
struct tm buf;
char timestring[100];
time_t ltime = time(NULL);
if (NULL != (p=localtime_r(<ime, &buf))){
strftime(timestring, sizeof(timestring),"** %c: ", p);
fprintf(f, "%s %s \n", timestring, user_input);
}
fclose(f);
char* separator = " ";
char* argv[64];
int argc = 0;
char* tmp;
argv[argc] = strtok_r(user_input, separator, &tmp);
while( argv[argc] != NULL){
argc+=1;
argv[argc] = strtok_r(NULL, separator, &tmp);
}
printf("ABC"); //why doesn't this print??
execvp(argv[0],argv);
}
else{
wait(&state_loc);
}
}
int main ()
{
while(1)
{
char user_input[1024];
printf("recsh>> ");
//empty the buffer right scanf
scanf("%[^\n]", user_input);
//calls each character in the user input, repeat until it reaches the terminating \n
while( getchar() != '\n');
if(strcmp(user_input, "exit") == 0){
printf("Exiting\n");
break;
}
else{
execute(user_input);
}
}
return 0;
}
That call to printf is executed in the child just before it calls execvp.
Since stdout is line-buffered by default and the text printed does not constitute a line (since there is no new line character), it is left in the output buffer. That output buffer vanishes along with the rest of the original executable when the image is replaced by execvp.
Moral: always terminate your output with a newline character (\n).
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.
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;
}
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 :: ");
}
}