Linux pipe bad behavior - c

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).

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 when I press CTRL+C the program reads zero bytes? (C-Posix)

My program has to do this:
The user has to pass N absolute pathname for files by command line. Then the i-th thread, with 0<=i<= N, has to write in the i-th file a string passed by user with scanf (or fgets). If CTRL+C is pressed, the program has to print all strings that user has passed with scanf.
When I run this and I insert a string for 1 of the N files and I press CTRL+C, in the function onPress the function read returns 0 (I think that in this case not indicates that the file pointer is in the end of file) and it prints only the string "Strings:"
Code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <signal.h>
pthread_mutex_t mutex;
int fdGlobal;
void* writer (int* arg) {
int fd_in = *(arg);
char buffer[100];
pthread_mutex_lock(&mutex);
printf("Write the string that you want to insert in the file\n");
scanf("%s", &buffer);
write(fd_in, &buffer, strlen(buffer));
write(fdGlobal, &buffer, strlen(buffer));
printf("Finished\n");
pthread_mutex_unlock(&mutex);
}
void onPress(int sig) {
char buff[100];
printf("I'm going to print all strings passed in files...\n");
int rd = read(fdGlobal, &buff, sizeof(buff));
if (rd == -1) perror("Error in the read of global file\n");
printf("I read %d bytes\n", rd);
printf("Strings: %s\n", buff);
exit(0);
}
void main (int argc, char* argv[]) {
int fds[argc-1];
pthread_t tid[argc-1];
int i, mu;
if (argc<=1) {
printf("Insert a number >=1 of pathname/s\n");
}
for ( i = 1 ; i<argc; i++) {
if (argv[i][0] != '/') {
printf("Insert a pathname\n");
}
}
signal(SIGINT, onPress);
fdGlobal = open("globalFile.txt", O_CREAT|O_RDWR, 0666);
if (fdGlobal == -1) perror("Error in the open of global file\n");
mu = pthread_mutex_init(&mutex, NULL);
if (mu < 0) perror("Error in the creation of mutex\n");
for (i=0; i<argc-1; i++) {
fds[i] = open(argv[i+1], O_CREAT|O_WRONLY, 0666);
if (fds[i] < 0 ) perror("Error in the open of the file\n");
pthread_create ( &tid[i], NULL, (void*) writer, &(fds[i]) );
}
for (i=0; i<argc-1; i++) {
pthread_join(tid[i], NULL);
}
}
Your code has numerous problems revolving around async-signal-safety, buffer sizes, and (non-)concurrency, but by far the most likely reason for the symptom you describe:
the function read returns 0
is that your belief that the file pointer is not at the end of the file is misplaced.
Indeed, read() returning 0 is a positive indicator that the file offset is currently at (or past) the end of the file. If the file was newly created then I don't see any reason to think that the offset would be anywhere else. Even when the file already exists, you need to move the file offset back to the beginning to read the data written in the current run of the program. You could do this with an appropriate call to lseek(), for example.

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;
}

Sending characters using pipes in C

I am trying to send string from parent process to child process using pipes in C programming language, it works almost correctly, but I receive incomplete string without first character. What is I wrong in my code? Thank you.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
int main(int argc, char** args)
{
int pid, p[2];
int status = -100;
char input[100];
char inputBuffer[100];
if(pipe(p) == -1)
{
return -1;
}
if((pid = fork())<0)
{
printf("error\n");
}
else
{
if(pid==0)
{
close(p[1]);
while(1)
{
if(!read(p[0],&inputBuffer,1))
{
close(p[0]);
break;
}
else
{
read(p[0],&inputBuffer,100);
}
}
printf("received: %s\n",inputBuffer);
exit(0);
}
else
{
printf("Enter String\n");
scanf("%s", &input);
printf("String Entered: %s\n",input);
close(p[0]);
write(p[1], input, strlen(input)+1);
close(p[1]);
wait(&status);
}
}
return 0;
}
The problem is that you first read 1 byte to see if it isn't a nul terminator but then that information is gone because you overwrite it in the latter call! So you need to increment the pointer on the latter call.
if(!read(p[0],&inputBuffer,1))
{
close(p[0]);
break;
}
else
{
char *pointer = &inputBuffer;
read(p[0],pointer+1,99);
}

Confused on the use of exec(family) in Unix

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.

Resources