How to use read function to read from terminal input (stdin)? - c

The main function will be in an infinite loop reading the numbers that the user puts in the terminal and storing them on a buffer. My problem is that I need to read from terminal using this function:
read(int fd, void *buf, size_t count);
How can the file descriptor point to the terminal?! (I hope I'm not saying some barbarity)
Thanks in advance!
EDIT: I already took your advice, but something is missing. This is a small program that I wrote to test:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int buffer[5], i=0, j;
int main(){
for(j=0; j<5; j++) buffer[j] = 0;
while(i<5){
read(STDIN_FILENO, buffer, 8);
printf("->%d\n", buffer[i]);
i++;
}
return 0;
}
Outup:
1
->2609
2
->0
3
->0
4
->0
5
->0
Why this doesn't print the numbers that I inserted?

Your process has three file descriptors open since it has been spawned: STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO (0, 1, 2 respectively).
These macros are defined in unistd.h.
read(STDIN_FILENO, buff, bytes)

By default the program's standard input is on file descriptor 0.
If you really mean to read from the terminal, instead of standard input, you can open() /dev/tty.

On POSIX you can use symbols like STDIN_FILENO to represents the input of your application. But beware that the standard input is not always the terminal, especially when you redirect input/output via the shell.

stdin is fd 0,
stdout is fd 1, and
stderr is fd 2.

Related

intricacies/understanding the stdio buffer and dup2

I am reading this lecture and found this following code sample which I modified to this:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
int main()
{
int fd;
char *s, *t;
off_t ret;
fd = open("file6", O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (dup2(fd, 1) < 0) { perror("dup2"); exit(1); }
printf("Standard output now goes to file6\n");
s = "before close\n";
write(1, s, strlen(s));
close(fd);
printf("It goes even after we closed file descriptor %d\n", fd);
printf("%ld\t"
"%ld\n",
(long int) lseek(fd,0,SEEK_CUR),
(long int) lseek(1,0,SEEK_CUR));
s = "And fwrite\n";
fwrite(s, sizeof(char), strlen(s), stdout);
printf("%ld\t"
"%ld\n",
(long int) lseek(fd,0,SEEK_CUR),
(long int) lseek(STDOUT_FILENO,0,SEEK_CUR));
fflush(stdout);
s = "And write\n";
write(1, s, strlen(s));
printf("after:\tAnd wri...: lseek(fd,0,SEEK_CUR)=%ld\t"
"lseek(STDOUT_FILENO,0,SEEK_CUR)=%ld\n",
(long int) lseek(fd,0,SEEK_CUR),
(long int) lseek(STDOUT_FILENO,0,SEEK_CUR));
return 0;
}
I am sharing two different outputs with the only change in the code being that the line fflush(stdout) is commented out in first and present in the second run.
Output (with fflush(stdout) commented):
before close
And write
Standard output now goes to file4
It goes even after we closed file descriptor 3
-1 13
And fwrite
-1 13
after: And wri...: lseek(fd,0,SEEK_CUR)=-1 lseek(STDOUT_FILENO,0,SEEK_CUR)=23
Output with flush(stdout) uncommented:
before close
Standard output now goes to file4
It goes even after we closed file descriptor 3
-1 13
And fwrite
-1 13
And write
after: And wri...: lseek(fd,0,SEEK_CUR)=-1 lseek(STDOUT_FILENO,0,SEEK_CUR)=127
I have two questions:
Why does "And write appears" first when fflush(stdout) is commented?
Why lseek prints -1 which I checked separately is an error message corresponding to errno ESPIPE. I am aware that lseek on terminal results in an error. But my current understanding is that since the standard output is dup2 to file6, then, this error shouldn't arise? Shouldn't it (lseek(STDOUT_FILENO, 0, SEEK_CUR)) simply return the current lseek pointer in file6, if dup2 is successful?
Why does "And write" appear first when fflush(stdout) is commented?
Because the C stdio buffers haven't filled, so nothing written using stdio APIs is actually sent to the output until the buffers fill, the stdio handle is flushed, or the program ends. Your direct write calls (e.g. for "And write") bypass stdio buffers entirely, and get written immediately, all the buffered stuff doesn't appear until the program ends (or at least, not until after "And write" has already been written).
Why lseek prints -1?
The first lseek was called on fd, which you closed shortly after dup2ing it over STDOUT_FILENO/1, so it fails. If you checked the errno properly (zeroing errno before each lseek, calling the two lseeks separately and storing or printing their errors and errnos separately, so one of them doesn't override the errno of the other before you even see it), you'd see it has a value corresponding to EBADF, not ESPIPE. The second lseek on (STDOUT_FILENO) works just fine. A mildly modified version of your code (using stderr so you can see the output for the last couple outputs even when you can't read the actual file, carefully zeroing errno each time, printing it before calling lseek again, and using strerror to show a friendly description of the errno) shows this clearly: Try it online!

How is second scanf working after closing standard input

How is the second scanf working in the below code
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
char buf[256];
int i;
write(1, "Hello World\n", strlen("Hello World\n"));
dup(0);
close(0);
scanf("%s", buf);
printf("Buffer:%s\n", buf);
dup(3);
scanf("%s", buf);
printf("Buffer:%s\n", buf);
return 0;
}
We are closing the stdin (fd:0), Then how does dup(3) have the effect of reopening the stdin?
I think when you check the return value of these two call of dup, you will find the first is 3, and the second is 0. So before the second scanf function is called, file descriptor 0 is related with terminal again.
The documentation for dup says that it uses "the lowest-numbered unused file descriptor for the new descriptor." Since you just closed FD 0, the next time you use dup, 0 will be the lowest-numbered unused file descriptor, so that's where the duplicate FD will end up. And since you put back exactly what you got rid of, scanf is happy to work again after doing so.

Communication between processes - pipe and fifo

I need to create program with 3 processes:
The first process should repeatedly read /dev/urandom and send 15 chars each cycle to the second process via a pipe.
The second process should convert received data to hex and send the result to the third process via a fifo.
The third process should print the received data.
This is what I wrote so far. Communication using the pipe is working fine, however there is some problem with the fifo - when I change n to a larger number such as 100000 or 1000000, the program doesn't start. When it's smaller, say 500 or 1000, the program works. What could be the reason behind that?
This is how I run it:
cat /dev/urandom | ./a.out
And here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#define FIFO "my_fifo"
int main(void) {
int pdesk[2];
char buf[15];
char buffer[15];
char hex[30];
char f[30];
int len;
int n;
n = 100;
umask(0);
mkfifo(FIFO, 0666);
pipe(pdesk);
if (fork() == 0) {
for (int i = 0; i < n; i++) {
read(STDIN_FILENO, buffer, 15);
write(pdesk[1], buffer, 15);
}
close(pdesk[1]);
} else {
sleep(1);
int fp;
for(int i = 0; i < n; i++) {
read(pdesk[0], buf, 15);
for(int a = 0, b = 0; b < 30; ++a, b+= 2)
sprintf(hex + b, "%02x", buf[a] & 0xff);
fp = open(FIFO, O_WRONLY);
write(fp, hex, 30);
close(fp);
usleep(10000);
}
close(pdesk[0]);
}
if (fork() == 0) {
sleep(2);
int fp;
for (int i = 0; i < n; i++) {
fp = open(FIFO, O_RDONLY);
read(fp, f, 30);
printf("Odczytano: %s\n", f);
close(fp);
usleep(10000);
}
}
}
If I understand your code correct, it will do the following:
With the first fork you start a child that reads from stdin and writes to the pipe.
Your parent process reads from the pipe and writes to the FIFO.
When your parent process has finished its loop it calls the second fork to create another child which will read from the FIFO and print the data.
When the loop count is too large you will reach the buffer limit of the FIFO and the parent will block because no process is reading from the FIFO. When the process is blocked in writing to the FIFO it will never create the child that is expected to read from the FIFO.
I think the main problem is that you should create the second child before starting the loop that reads the data from the pipe and writes to the FIFO.
Some additional remarks:
With cat /dev/urandom | ./a.out your program does not read /dev/urandom directly. It reads from a pipe which might behave differently.
You should always check the return value of read. It will tell you how many bytes it has read which may be less than you asked it to read. If you want to have exactly 15 characters you might have to read several times if you get less than 15 characters.
The same applies to write.
Thank you very much. When the process that displays data is above other child processes, it finally works.
With cat /dev/urandom | ./a.out your program does not read /dev/urandom directly. It reads from a pipe which might behave differently.
How could I change it?
The programs also needs to read files the same way it reads from /dev/urandom, for example:
cat file.txt | ./a.out
I took your advice and started to check the value of read and now it doesn't go behind the range of file. The problem is I don't know how to check which parameter was called (and hence I can't check the length of file) - if it was file.txt, /dev/urandom, none or anything else. I tried with
int main(char argc, char* argv[])
but argv is always ./a.out, no matter what I call. Is there any way to check that?

How does stuff have to be ordered when using dup2 and pipe?

I am currently trying to understand combincation of dup2 and C pipes, but not even the simplest program seems to work. Already when reading example codes I am pretty confused on when they close ends of the pipe and where the output should be printed.
Sometimes the write end is closed, even though one line later output should be generated which should go into the pipe. In other examples, the unused end is closed (which makes more sense to me).
Then, I do not understand when dup2 should be executed. I guess it should become before the output I want to redirect, but I have the feeling I also saw that differently today.
So in the end I came up with this little test with printf and fflush in each line, where nothing gets redirected through the pipe. Why's that? What am I doing wrong?
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int out_pipe[2];
char *output[101];
if (pipe(out_pipe) != 0) {
perror("pipe()");
exit(1);
}
printf("Hello");
fflush(stdout);
dup2(out_pipe[1], STDOUT_FILENO);
printf("Hello");
fflush(stdout);
close(out_pipe[1]);
printf("Hello");
fflush(stdout);
read(out_pipe[0], output, 100);
close(out_pipe[0]);
printf("PIPE: %s", output);
fflush(stdout);
return 0;
}
End your printf() messages with newlines; the fflush() is still a good idea as you're about to change where standard output goes, though it's not usually necessary if the standard output of the program is going to a terminal. If the standard output was going to a file and the fflush() was not in place, then you'd get three copies of "Hello\n" written to the pipe.
When you change standard output to the pipe, your message is indeed written to the pipe.
When you close the write file descriptor, you don't run into any issues. You then write a second Hello to the pipe. You need this fflush() to ensure that the standard I/O package has actually written its buffered data to the pipe.
You then read from the pipe into the output buffer. You should check how many bytes you read since the string is not going to be null terminated. You should get 10 bytes read (when you don't have any newlines in the messages).
You then write to the pipe again with the PIPE: prefix.
To fix, write messages to standard error.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int out_pipe[2];
char output[101];
if (pipe(out_pipe) != 0) {
perror("pipe()");
exit(1);
}
printf("Hello\n");
fflush(stdout);
dup2(out_pipe[1], STDOUT_FILENO);
printf("Hello\n");
fflush(stdout);
close(out_pipe[1]);
printf("Hello\n");
fflush(stdout);
int n = read(out_pipe[0], output, sizeof(output));
close(out_pipe[0]);
fprintf(stderr, "PIPE: %.*s\n", n, output);
return 0;
}
Note that I changed the definition of output from an array of char * to a simple array of char. With the changes, I got the output:
$ ./pipe3
Hello
PIPE: Hello
Hello
$
That's because I included newlines in the messages written to the pipe, as well as in the format string that ends up on standard error.
Is there a possibility to "reenable" stdout?
Yes; simply preserve a copy of the original file descriptor for standard output before using dup2(), and then reinstate the copy once you've done with the pipe.
I've removed the two leading fflush() calls, and the sample output demonstrates the difference between terminal and file output:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int out_pipe[2];
char output[101];
int old_stdout;
if (pipe(out_pipe) != 0) {
perror("pipe()");
exit(1);
}
printf("Hello\n");
old_stdout = dup(STDOUT_FILENO);
dup2(out_pipe[1], STDOUT_FILENO);
printf("Hello\n");
close(out_pipe[1]);
printf("Hello\n");
fflush(stdout);
int n = read(out_pipe[0], output, sizeof(output));
close(out_pipe[0]);
dup2(old_stdout, STDOUT_FILENO);
printf("PIPE: %d <<%.*s>>\n", n, n, output);
return 0;
}
Sample outputs:
$ ./pipe3Hello
PIPE: 12 <<Hello
Hello
>>
$./pipe3 > output
'pipe3' is up to date.
$ cat output
PIPE: 18 <<Hello
Hello
Hello
>>
$
If you remove the remaining fflush(), the program hangs. There is nothing in the pipe (because standard I/O hasn't flushed its buffer because it isn't full and the output isn't a terminal any more), but the pipe is open for writing, so the kernel considers that input could appear on it — if only the program that has the pipe open for writing wasn't waiting on the read end of the pipe for the input to appear. The program has deadlocked on itself.

Mechanism for linux output in terminal when using system function read() & write()

Code:
#include <stdio.h>
#include <unistd.h>
int main(void)
{
char buf[BUFSIZ];
int n;
while((n = read(0, buf, BUFSIZ)) > 0 && printf("1:%d ", n))
{
printf("2:%d ", n);
write(1, buf, n);
}
return 0;
}
pupu(my input)
pupu(output)
popopo(my input)
popopo(output)
1:5 2:5 1:7 2:7(output)
My question: How does it work?
(why buffer text output before n_read?)
The standard I/O functions (like printf) are buffered, meaning output to stdout isn't printed until its buffer is full or explicitly flushed.
On the other hand, writing directly to the output file descriptor is not buffered and is written directly.
What you have here is you mixing direct and buffered output, and the buffered output isn't actually written until the program exits and the buffer is flushed.

Resources