Confused on the use of exec(family) in Unix - c

I'm writing a program in which I use the system call fork() to create a child process, and then create a grandchild, as well as creating a pipe between the child and grandchild. I think my implementation is fairly good, however when I run the program, it simply skips right through the prompts in my code.
Essentially we have this:
-Process starts
Fork() create child
Child creates pipe
Fork() creates grandchild, pipe inherited.
TL;DR- Code skips through UI prompts, not sure if I'm entering data correctly, not sure if I'm reading data into processes correctly.
How do I read inputs down the pipe?
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
void main(int argc, char *argv[])
{
int p[2];
int pid, pid2;
pid = fork();
if(pid == 0)
{
pipe(p);
pid2 = fork();
switch(pid2)
{
case -1:
printf("CASE 1");
exit(-1);
case 0:
close(0);
dup(p[0]);
close(p[0]);
close(p[1]);
execl("./Sort/sort", 0);
break;
default:
close(1);
dup(p[1]);
close(p[1]);
close(p[0]);
execl("./Pre/pre", 0);
break;
}
}
else
{
wait(pid);
printf("Process Completed\n");
exit(0);
}
}
Child process for pre:
#include <stdio.h>
void main (int argc, char *argv[])
{
char n1[20];
int g1;
FILE *ofp, *ifp;
int track;
ofp = fopen("output.txt", "w");
while(track != -1)
{
printf("Please enter the student's grade and then name, ");
printf("separated by a space: ");
scanf("%3d %s", &g1, n1);
if (g1 >= 60)
{
fprintf(ofp, "%s\n", n1);
}
printf("Add another name?(-1 to quit, 0 to continue): ");
scanf("%d", &track);
}
fclose(ofp);
ifp = fopen("output.txt", "r");
printf("Students that made a 60+:\n");
while(fscanf(ifp, "%s", n1) == 1)
printf("%s\n", n1);
fclose(ifp);
Child process for sort:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int stringcmp(const void *a, const void *b)
{
const char **ia = (const char **)a;
const char **ib = (const char **)b;
return strcmp(*ia, *ib);
}
void main(int argc, char *argv[])
{
int i = 0;
int num = 0;
int j = 0;
char name[20];
printf("How many names would you like to enter? ");
scanf("%d", &num);
char **input = malloc(num * sizeof(char*));
for (i=0; i < num; i++)
{
printf("Please input a name(first only): ");
scanf("%s", name);
input[i] = strdup(name);
}
qsort(input, num, sizeof(char *), stringcmp);
printf("Names:\n");
for(j = 0; j < num; j++)
printf("%s\n", input[j]);
for( i = 0; i < num; i++ ) free(input[i]);
free(input);

Observe that pre outputs prompts to stdout. That text will end up in the pipe, which is going to end up in sort's stdin.
You should fprintf all your prompts to stderr.
pre also doesn't output text in the format expected by sort; sort expects an integer n followed by n words (first names). So, that should be the only text that pre outputs on stdout; everything else needs to go to a file or stderr.
P.S. Also, use dup2(p[0], 0) instead of close(0); dup(p[0]) as it makes your intent clearer, as well as potentially avoiding threading issues (of course only relevant if you have threads, but it's worth keeping in mind).

Here's are two examples of how to do it
http://www.gnu.org/software/libc/manual/html_node/Creating-a-Pipe.html
http://tldp.org/LDP/lpg/node11.html
Edit: I can tell that you are trying to pipe the output of "./Pre/pre" to the input of "./Sort/sort" but I'm not sure what you mean by "Code skips through UI prompts" because for case 1: and default: you don't have any prompts. What exactly is the problem? If you want something more specific please explain more of the details in your question.

Related

How do I get keyboard inputs from the user using C and linux?

I have to make a short program that, creates two child processes, each one accepting an integer from the keyboard and writes them to a pipe, from where the parent process summarizes them and displays the total on the screen.
I've written one with scanf(), but it freezes up and it doesn't give me the sum. How do I make it work with scanf or any other way if possible?
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int pipe(int pd[2]);
int main(int argc, char *argv[])
{
int pd[2], sum=0, num=0;
if(pipe(pd) == -1)
for(int i = 0; i < 2; i++)
{
if(fork() == 0)
{
scanf("%d", num);
if(write(pd[1], &num, sizeof(int)) == -1)
printf("Error: Write()");
}
}
for(int j = 0; j < 2; j++)
{
wait(NULL);
if(read(pd[0], &num, sizeof(int)) == -1)
printf("Error: Read()");
sum += num;
}
printf("Total: %d\n", sum);
}
Lots of problems here:
You have if(pipe(pd) == -1), and I assume you meant to have an error handler as the "then" clause for it, but you don't, so children will only spawn if the pipe fails, which is basically the opposite of what you want.
You have scanf("%d", num);. You need &num since num isn't already a pointer.
You need to return or exit from the child processes, or they'll fall into the next loop and consume the output.
With just those things fixed, it's enough to make it work:
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int pipe(int pd[2]);
int main(int argc, char *argv[])
{
int pd[2], sum=0, num=0;
if(pipe(pd) == -1)
{
perror("pipe");
return 1;
}
for(int i = 0; i < 2; i++)
{
if(fork() == 0)
{
scanf("%d", &num);
if(write(pd[1], &num, sizeof(int)) == -1)
printf("Error: Write()");
return 0;
}
}
for(int j = 0; j < 2; j++)
{
wait(NULL);
if(read(pd[0], &num, sizeof(int)) == -1)
printf("Error: Read()");
sum += num;
}
printf("Total: %d\n", sum);
}
There's a few other things you should fix too, but they're not complete show-stoppers. Here's what jumped out at me:
You don't need to declare your own pipe prototype. The one from unistd.h is fine.
You should handle the case where fork or scanf fail.
You should handle partial reads and writes.
You should close the read end of the pipe in the children and the write end of the pipe in the parent after forking.
You should consider using a lock to control reading input, so that it doesn't depend on TTY line buffering to work reliably.

kill() function not working in C language

I have the following code. It generates n child and then a random number between 0 and n. So that random number lets suppose is "i". The child number i must kill his brothers.
The problem is that the kill function is not killing anything since both ptree after and before are exactly the same.
I cant found a solution, the output must be the father and the i child only since all his brothers were killed by him.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <math.h>
char archSalidaAntes[] = "aprocesosAntes.txt";
char archSalidaDespues[] = "aprocesosDespues.txt";
void imprimirArreglo(int arr[], int n){
int i;
for(i=0; i<n; i++){
printf("%d\t", arr[i]);
}
printf("\n");
}
void imprimirArbolTxt(char nombreArchivo[], pid_t id){
char buff[255];
sprintf(buff, "pstree -p -c -l %d > %s", (int)id, nombreArchivo);
system(buff);
}
void communicateSon(int arrProc[], int n, int fd[]){
int i;
int data;
int writeResp;
close(fd[0]); //closing input
for(i=0; i<n; i++){
data = arrProc[i];
writeResp = write(fd[1], &data, sizeof(data));
if(!writeResp){
printf("error writing");
}
}
close(fd[1]); //closing output
}
void killOthers(int n, int fd[], int randInt){
int i;
int readResp;
int killResp;
int data;
int arrProc[n];
close(fd[1]); //closing output
i = 0;
while(1){
readResp = read(fd[0], &data, sizeof(data));
fflush(stdout);
fflush(stdin);
if(!readResp){
break;
}
arrProc[i] = data;
i++;
}
imprimirArreglo(arrProc, n);
printf("id elegido: %d\n", getpid());
for(i=0; i<n; i++){
if(i!= randInt){
printf("killing: %d\n", arrProc[i]);
killResp = kill((pid_t)arrProc[i], SIGKILL);
if(killResp < 0){
printf("error kill: %d \n", killResp);
}
int aux = kill(arrProc[i], 0);
printf("aux: %d\n", aux);
}
}
close(fd[0]); //closing input
char com[30];
sprintf(com, "pstree -p %d", getppid());
system(com);
}
int main(int argc, char **argv){
int n;
int i;
int *arrProc;
int randInt;
int fd[2];
pid_t pId;
n = atoi(argv[1]);
printf("n = %d\n", n);
srand(time(NULL));
arrProc = (int*) malloc(sizeof(int) * n);
randInt = rand() % n;
pipe(fd);
for(i=0; i<n; i++){
pId = fork();
if(pId){
arrProc[i] = (int)pId;
if(i == (n-1)){
char com[30];
sprintf(com, "pstree -p %d", getppid());
system(com);
communicateSon(arrProc, n, fd);
waitpid(arrProc[randInt], NULL, 0);
printf("termino la espera del hijo\n");
free(arrProc);
}
} else if(pId == 0){ //hijos
if(i==randInt){
killOthers(n, fd, randInt);
exit(0);
} else{
break;
}
}
}
sleep(0.5);
return 0;
}
Since main process never calls waitpid for other children all of them become zombies after getting killed.
Update: you should also close pipe ends descriptors in other child processes prior to putting them to sleep, otherwise killer child process will get stuck at waiting for more data to come from the pipe.
} else{
close(fd[0]);
close(fd[1]);
break;
}
Update: sleep takes unsigned int number of seconds, so sleep(0.5) will be equivalent to sleep(0).
Try with sleep(1).
The sleep() function wants an integer argument, so sleep(0.5) equals to zero - which is probably not "long enough" for your demo to work. The child processes may terminate before the kill signal can reach them.

Linux pipe bad behavior

I have this linux program that uses a pipe to transmit data from the parent to child, and give an answer based on the returned value;
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
int fd[2], nbytes;
pid_t childpid;
char readbuffer[80];
int log_variable;
char login[]="login";
void toLower(char str[]){//converts UPPERCASE to lowercase
int i;
for(i=0; i<strlen(str); i++)
str[i]=tolower(str[i]);
}
//end of toLower
int compareStrings(char str1[], char str2[]){//compares 2 strings
if(strlen(str1) == strlen(str2))
{
int i;
for( i=0; i<strlen(str1); i++){
if(str1[i] != str2[i])
return 0;
return 1;
}
}
else return 0;
}
int loginCommand(char argument[]){//test function so far for login, myfind etc
int fileDescr;
pipe(fd);//defines the pipe
switch(childpid=fork()){//switch statement to control parent and child
case -1:
perror("fork -1\n");
exit(0);
case 0://child
close (fd[1]);
char givenUsername[20];
//open the config file and copy the username from it, assign to a variable, and then
//compare it to readbuffer
int nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
if(strcmp(readbuffer, login) == 0){
printf("1: ");
return 1;
}
else {
printf("0: ");
return 0;
}
exit(0);
default:
//parent
close(fd[0]);
write(fd[1], argument, sizeof(argument));
wait(NULL)!=-1;
exit(0);
}
}
main(){
char input[20];
int logs;
while(logs == 0){
printf("Insert command: \n");
scanf("%s", input);
toLower(input);
logs=(loginCommand(input));
if(logs == 1) {printf("System accessed\n"); }
if(logs == 0) {printf("This username doesnt exist\n"); }
}
return 0;
}
But my biggest question is that I input the value of "login", that is the same of the login variable above, the program responds correctly, but if I change that variable to of value of "loginloginlogin" let's say, and if I input from the console the same value, the program says that the value is incorrect. My assumption is that the program doesn't read the whole input from console, but I've changed the sizes of the strings, and still has the same behavior.
Can anyone know whats going on?
The problem is here:
write(fd[1], argument, sizeof(argument));
When you pass an array to a function, it decays to a pointer to the first character. Doing sizeof on a pointer gives you the size of the pointer and not the size of what it point so.
To get a the length of a string you need to use strlen.
Oh, and don't forget to use strlen(argument) + 1 to also send the string terminator (alternatively terminate the string in the child process).

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 :: ");
}
}

reading characters line by line from a text file in c with multiprocessing

hi there i'm facing with null character problem while i'm reading and try to printing characters of a file with using fseek() function in a multiprocess programme. here is my simple code,
#include <stdio.h> /* basic I/O routines. */
#include <unistd.h> /* define fork(), etc. */
#include <sys/types.h> /* define pid_t, etc. */
#include <sys/wait.h> /* define wait(), etc. */
#include <signal.h> /* define signal(), etc. */
#include <pthread.h>
#include <time.h>
void print_screen(int i);
int counter=0;
int main(int argc, char* argv[]) {
FILE* fptr;
fptr = fopen("sample.txt","w");
int counter = atoi(argv[1]);
int i,k;
int temp;
pid_t child_pid;
int child_status;
char array[counter];
srand ( time(NULL) );
for(i=0; i<counter; i++){
temp = rand()%4;
if( temp==0 ) {
fprintf(fptr,"A\n");
array[i]='A';
}
else if( temp==1 ) {
fprintf(fptr,"C\n");
array[i]='C';
}
else if( temp==2 ) {
fprintf(fptr,"G\n");
array[i]='G';
}
else if( temp==3 ) {
fprintf(fptr,"T\n");
array[i]='T';
}
}
fclose(fptr);
for(i=1; i<=counter; i++){
child_pid = fork();
switch(child_pid) {
case -1:
printf("Error occured with fork()\n");
exit(1);
case 0:
print_screen(i); /* Child Process */
exit(0);
}
}
wait(&child_status);
execl("/usr/bin/killall","killall","tail",(char *) 0);
return 0;
}
void print_screen(int i){
char* str;
FILE* fptr;
fptr=fopen("sample.txt","r");
fseek(fptr,i,SEEK_SET);
fscanf(fptr,"%s",str);
printf("Process Number %d, Character = %s\n",i,str);
sleep(1);
fclose(fptr);
return;
}
Suppose that i enter ./sample 10 in to command line so programme will print 10 characters into sample.txt and then 10 child processes will be created, each of them try to pick a character and print onto the screen.moreover, as you can see i send i as a parameter to set the offset.but as i mentioned it prints null. this is the outlook the programme.
Process Number 7, Character = (null)
Process Number 6, Character = (null)
Process Number 5, Character = (null)
Process Number 3, Character = (null)
Process Number 8, Character = (null)
Process Number 4, Character = (null)
Process Number 9, Character = (null)
Process Number 10, Character = (null)
Process Number 2, Character = (null)
Process Number 1, Character = (null)
and the txt file was like this.
G
A
A
T
G
C
C
A
A
T
i will appreciated if you can help and thanks anyway.
edit: i realized that i compile like $ gcc sample.c -o sample -lpthread it prints out null. On the other hand, i compile it without -lpthread it prints character but not properly for example like this is the text file.
T
G
G
T
G
and terminal gives the output like this.
Process Number 1, Character = G
Process Number 2, Character = G
Process Number 3, Character = G
Process Number 4, Character = G
Process Number 5, Character = T
Have a look at this http://www.cs.nmsu.edu/~jcook/Tools/pthreads/pthreads.html
You propably know that a fork() produces a copy of the calling process and that this copy is (almost) identical to the original - they only differ in that fork() in the child = 1
Try (and tell me if its working, i could not test it :)
for(i=1; i<=counter; i++){
child_pid = fork();
switch(child_pid) {
case -1:
printf("Error occured with fork()\n");
exit(1);
case 0:
print_screen(i); /* Child Process */
exit(0);
default:
printf("...\n");
}
}
EDIT: works for me
EDIT: okay i did some testing, should rather use if instead of case (also added dirext output of file content - should remove that if file size growth ); now thats what i had as code:
#include <stdio.h> /* basic I/O routines. */
#include <unistd.h> /* define fork(), etc. */
#include <sys/types.h> /* define pid_t, etc. */
#include <sys/wait.h> /* define wait(), etc. */
#include <signal.h> /* define signal(), etc. */
#include <pthread.h>
#include <time.h>
void print_screen(int i);
int counter=0;
int main(int argc, char* argv[]) {
FILE* fptr;
fptr = fopen("sample.txt","w");
int counter = atoi(argv[1]);
int i,k;
int temp;
pid_t child_pid;
int child_status;
char array[counter];
srand ( time(NULL) );
for(i=0; i<counter; i++){
temp = rand()%4;
if( temp==0 ) {
fprintf(fptr,"A\n");
printf("A\n");
array[i]='A';
}
else if( temp==1 ) {
fprintf(fptr,"C\n");
printf("C\n");
array[i]='C';
}
else if( temp==2 ) {
fprintf(fptr,"G\n");
printf("G\n");
array[i]='G';
}
else if( temp==3 ) {
fprintf(fptr,"T\n");
printf("T\n");
array[i]='T';
}
}
fclose(fptr);
for(i=1; i<=counter; i++){
child_pid = fork();
if (child_pid == -1){
printf("Error occured with fork()\n"); exit(-1);
}
else if (child_pid == 0){
print_screen(i); exit(0);
}
else{ printf("something \n"); }
}
wait(&child_status);
execl("/usr/bin/killall","killall","tail",(char *) 0);
return 0;
}
void print_screen(int i){
char* str;
FILE* fptr;
fptr=fopen("sample.txt","r");
fseek(fptr,i,SEEK_SET);
fscanf(fptr,"%s",str);
printf("Process Number %d, Character = %s\n",i,str);
sleep(1);
fclose(fptr);
return;
}
it doesnt work correct (i'm pretty sure there is some kind of collision when the pthreads are reading from the file at the same time) but at least you will have something to continue working with.
I got this output:
A
T
A
T
something
something
something
something
Process Number 4, Character = A
Process Number 3, Character = A
Process Number 2, Character = T
Process Number 1, Character = T
tail: no process found
You aren't allocating any memory for str in print_screen. fscanf requires that.
Try a hack like:
void print_screen(int i){
char str[256]; /* plenty large enough for this example */

Resources