Limit buffer in read() function - c

I have to create a program that asks from standard input a string and write in standard error the string previously written.
This is my program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main() {
char *buffer = malloc(sizeof(char)*20);
int len = 0;
do {
len = read(STDIN_FILENO, buffer, 20);
if(len == -1)
write(STDERR_FILENO, "Error read\n", 10);
else
write(STDERR_FILENO, buffer, len);
} while(strncmp(buffer,"fine\n", 5));
free(buffer);
return 0;
}
The code works but I'm not satisfied..there is one problem:
The buffer is a 20char but I can insert more than 20 char...why? How I can limit the buffer to only 20 char?

The code works but I'm not satisfied..there is one problem: The buffer is a 20char but I can insert more than 20 char...why?
Because your program can't stop someone inputting more than 20 chars; all it can do is limit that it doesn't overflow the buffer which it already does - read() doesn't read more than the requested bytes. It only appears as if a read() call is reading more than size (20) but acutally read() reads only (upto) 20 chars and the rest is read in the next iteration.
No matter what method you use to read input and/or increase buffer size, this problem of "extra input" is always going to be there.
What you can do instead is check if if len is 20 and buffer[19] is not \n:
else {
write(STDERR_FILENO, buffer, len);
/* Read out the left over chars. */
if (len == 20 && buffer[19] != '\n') {
char c;
do {
read(STDIN_FILENO, &c, 1); /* left out the error checking */
} while (c != '\n');
}
Or increase the buffer size, say, to 512 bytes and then only look at the first 20 bytes that you're interested in.
Note: Add error checking for all read() and write() calls.

You're not allocating enough memory for your buffer. You always need 1 more to store the NUL terminating character. And you also need to remember to add that NUL character to the end of the string read in by read as it won't do it for you.
When you get an error, you should exit the loop.
#define BUF_SIZE (20)
int main() {
char *buffer = malloc(sizeof(char)*(BUF_SIZE+1));
int len = 0;
do {
len = read(STDIN_FILENO, buffer, BUF_SIZE);
if(len == -1) {
write(STDERR_FILENO, "Error read\n", 10);
break;
} else {
buffer[len]='\0';
write(STDERR_FILENO, buffer, len);
}
} while(strncmp(buffer,"fine\n", 5));
free(buffer);
return 0;
}
You'll probably also find that the strncmp(buffer,"fine\n", 5) isn't going to work as you'd need to process the read in string to handle lines of input as read will happily read in multiple lines at a time (assuming they all fit in the buffer size).

Related

Is it necessary to use a while loop when using the read and write system calls?

I am practicing the read and write system call, the below code is working fine with a while loop and also without them. could you please tell me what is the use of while loop here, is it necessary to add it while using read and write system calls. I am a beginner. Thanks.
#include <unistd.h>
#define BUF_SIZE 256
int main(int argc, char *argv[])
{
char buf[BUF_SIZE];
ssize_t rlen;
int i;
char from;
char to;
from = 'e';
to = 'a';
while (1) {
rlen = read(0, buf, sizeof(buf));
if (rlen == 0)
return 0;
for (i = 0; i < rlen; i++) {
if (buf[i] == from)
buf[i] = to;
}
write(1, buf, rlen);
}
return 0;
}
You usually need to use while loops (or some kind of loop in general) with read and write, because, as you should know from the manual page (man 2 read):
RETURN VALUE
On success, the number of bytes read is returned (zero indicates end
of file), and the file position is advanced by this number. It is
not an error if this number is smaller than the number of bytes
requested; this may happen for example because fewer bytes are
actually available right now (maybe because we were close to end-of-
file, or because we are reading from a pipe, or from a terminal), or
because read() was interrupted by a signal. See also NOTES.
Therefore, if you ever want to read more than 1 byte, you need to do this in a loop, because read can always process less than the requested amount.
Similarly, write can also process less than the requested size (see man 2 write):
RETURN VALUE
On success, the number of bytes written is returned (zero indicates nothing was written). It is not an error if this
number is smaller than the number of bytes requested; this may happen for example because the disk device was filled.
See also NOTES.
On error, -1 is returned, and errno is set appropriately.
The only difference here is that when write returns 0 it's not an error or an end of file indicator, you should just retry writing.
Your code is almost correct, in that it uses a loop to keep reading until there are no more bytes left to read (when read returns 0), but there are two problems:
You should check for errors after read (rlen < 0).
When you use write you should also add a loop there too, because as I just said, even write could process less than the requested amount of bytes.
A correct version of your code would be:
#include <stdio.h>
#include <unistd.h>
#define BUF_SIZE 256
int main(int argc, char *argv[])
{
char buf[BUF_SIZE];
ssize_t rlen, wlen, written;
char from, to;
int i;
from = 'e';
to = 'a';
while (1) {
rlen = read(0, buf, sizeof(buf));
if (rlen < 0) {
perror("read failed");
return 1;
} else if (rlen == 0) {
return 0;
}
for (i = 0; i < rlen; i++) {
if (buf[i] == from)
buf[i] = to;
}
for (written = 0; written < rlen; written += wlen) {
wlen = write(1, buf + written, rlen - written);
if (wlen < 0) {
perror("write failed");
return 1;
}
}
}
return 0;
}

C read file and print buffer

I am learning C and I have been trying to read a file and print what I just read. I open the file and need to call another function to read and return the sentence that was just read.
My function will return 1 if everything went fine or 0 otherwise.
I have been trying to make it work for a while but I really dont get why I cant manage to give line its value. In the main, it always prints (null).
The structure of the project has to stay the same, and I absolutely have to use open and read. Not fopen, or anything else...
If someone can explain it to me that would be awesome.
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define BUFF_SIZE 50
int read_buff_size(int const fd, char **line)
{
char buf[BUFF_SIZE];
int a;
a = read(fd, buf, BUFF_SIZE);
buf[a] = '\0';
*line = strdup(buf);
return (1);
}
int main(int ac, char **av)
{
char *line;
int fd;
if (ac != 2)
{
printf("error");
return (0);
}
else
{
if((fd = open(av[1], O_RDONLY)) == -1)
{
printf("error");
return (0);
}
else
{
if (read_buff_size(fd, &line))
printf("%s\n", line);
}
close(fd);
}
}
Here:
char buf[BUFF_SIZE];
int a;
a = read(fd, buf, BUFF_SIZE);
buf[a] = '\0';
if there are more characters than BUFF_SIZE available to be read, then you will fill your array entirely, and buf[a] will be past the end of your array. You should either increase the size of buf by one character:
char buf[BUFF_SIZE + 1];
or, more logically given your macro name, read one fewer characters:
a = read(fd, buf, BUFF_SIZE - 1);
You should also check the returns from strdup() and read() for errors, as they can both fail.
read(fd, buf, BUFF_SIZE); //UB if string is same or longer as BUFF_SIZE
u need +1 byte to store 0, so use BUFF_SIZE - 1 on reading or +1 on array allocation...also you should check all returned values and if something failed - return 0
Keep it simple and take a look at:
https://github.com/mantovani/apue/blob/c47b4b1539d098c153edde8ff6400b8272acb709/mycat/mycat.c
(Archive form straight from the source: http://www.kohala.com/start/apue.tar.Z)
#define BUFFSIZE 8192
int main(void){
int n;
char buf[BUFFSIZE];
while ( (n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
if (write(STDOUT_FILENO, buf, n) != n)
err_sys("write error");
if (n < 0)
err_sys("read error");
exit(0);
}
No need to use the heap (strdup). Just write your buffer to STDOUT_FILENO (=1) for as long as read returns a value that's greater than 0. If you end with read returning 0, the whole file has been read.

C: sockets: can't read the whole server response

I'm programming in C an IRC chat client. everything it's working well except I can't read the whole answer sent by the server. here's the code:
char buffer[2048];
write_on_screen(current_page(), "LOG COMMAND", command);
write(sockfd, command, strlen(command)); //write to socket
bzero(buffer, sizeof(buffer));
read(sockfd, buffer, sizeof(buffer));
write_on_screen(current_page(), "RESPONSE", buffer);
return buffer;
most of the time buffer will contain just a piece of the response (which is shorter than 2048 bytes) and other times it contains nothing. in both cases if I do another read() after the first one, it returns me the rest of the answer or another small piece (and then I've to do another read() again). if I put a sleep(1) between write() and read() I get the whole answer, but I'm sure this not a good pratice.
Is there some way I can avoid this?
thank you in advance
You're making the usual mistakes. It is impossible to write correct network code without storing the result of read() or recv() into a variable. You have to:
Check it for -1, and if so look at errno to see whether was fatal, which it almost always is except for EAGAIN/EWOULDBLOCK, and if fatal close the socket and abandon the process.
Check it for zero, which means the peer disconnected. Again you must close the socket and abandon the process.
Use it as the count of bytes actually received. These functions are not obliged nor guaranteed to fill the buffer. Their contract in blocking mode is that they block until an error, end of stream, or at least one byte is transferred. If you're expecting more than one byte, you normally have to loop until you get it.
According to RFC-1459, a single line of text in IRC can contain up to 512 characters and is terminated by a CRLF (\r\n) pair. However:
You're not guaranteed to receive exactly 512 bytes each time. For example, you might receive a comparatively short message from someone else one in the channel: Hi!
Related to the above: A group of 512 bytes might represent more than one message. For example, the buffer might contain a whole line, plus part of the next line: PRIVMSG <msgtarget> <message>\r\nPRIVMS
Given that you could have zero-or-more complete lines plus zero-or-one incomplete lines in your buffer[] at any time, you could try doing something along the lines of:
char buffer[2048];
while(keep_going)
{
char **lines;
int i, num_lines;
// Receive data from the internet.
receiveData(buffer);
// Create an array of all COMPLETE lines in the buffer (split on \r\n).
lines = getCompleteLines(buffer, &num_lines);
removeCompleteLinesFromBuffer(buffer);
// Handle each COMPLETE line in the array.
for (i = 0; i < num_lines; ++i) { handle_line(lines[i]); }
freeLines(lines);
}
This would allow you to handle zero or more complete lines in one go, with any incomplete line (i.e anything after the final \r\n pair) being kept around until the next call to receiveData().
You need to loop around read() until a CRLF had been detected.
A possible way to do this would be:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
ssize_t read_until_crlf(int sd, char * p, size_t s, int break_on_interupt)
{
ssize_t bytes_read = 0;
ssize_t result = 0;
int read_cr = 0;
int read_crlf = 0;
while (bytes_read < s)
{
result = read(sd, p + bytes_read, 1);
if (-1 == result)
{
if ((EAGAIN == errno) || (EWOULDBLOCK == errno))
{
continue;
}
else if (EINTR == errno)
{
if (break_on_interupt)
{
break;
}
continue;
}
else
{
perror("read() failed");
break;
}
}
else if (0 == result)
{
break; /* peer disconnected */
}
if ('\r' == p[bytes_read])
{
read_cr = 1;
}
else if (('\n' == p[bytes_read]) && read_cr)
{
read_crlf = 1;
break; /* CRLF detected */
}
else
{
read_cr = 0;
}
++bytes_read;
}
if (!read_crlf)
{
result = -1; /* Buffer full without having read a CRLF. */
errno = ENOSPC; /* ... or whatever might suite. */
}
return (0 >= result) ?result :bytes_read;
}
Call it like this:
#include <stdio.h>
ssize_t read_until_crlf(int sd, char * p, size_t s, int break_on_interupt);
int main(void)
{
int sd = -1;
/* init sd here */
{
char line[2048] = "";
ssize_t result = read_until_crlf(sd, line, sizeof line, 0);
if (-1 == result)
{
perror("read_until_newline() failed");
}
printf("read '%s'\n", line);
}
return 0;
}

fgets() blocking when buffer too large

I'm currently using select() to tell if there is data to be read in a file descriptor because I don't want fgets to block. This works 99% of the time, however, if select() detects data in fp, but the data doesn't have a newline and the data is smaller than my buffer, it will block. Is there any way to tell how many bytes are ready to be read?
//See if there is data in fp, waiting up to 5 seconds
select_rv = checkForData(fp, 5);
//If there is data in fp...
if (select_rv > 0)
{
//Blocks if data doesn't have a newline and the data in fp is smaller than the size of command_out!!
if (fgets(command_out, sizeof(command_out)-1, fp) != NULL)
{
printf("WGET: %s", command_out);
}
}
else if (select_rv == 0)
{
printf("select() timed out... No output from command process!\n");
}
I guess what I really want is a way to know if a full line is ready to be read before calling fgets.
As MBlanc mentions, implementing your own buffering using read() is the way to go here.
Here's a program that demonstrates the general method. I don't recommend doing exactly this, since:
The function presented here uses static variables, and will only work for one single file, and will be unusable once that's over. In reality, you'd want to set up a separate struct for each file and store the state for each file in there, passing it into your function each time.
This maintains the buffer by simply memmove()ing the remaining data after some is removed from the buffer. In reality, implementing a circular queue would probably be a better approach, although the basic usage will be the same.
If the output buffer here is larger than the internal buffer, it'll never use that extra space. In reality, if you get into this situation, you'd either resize the internal buffer, or copy the internal buffer into the output string, and go back and try for a second read() call before returning.
but implementing all this would add too much complexity to an example program, and the general approach here will show how to accomplish the task.
To simulate delays in receiving input, the main program will pipe the output from the following program, which just outputs a few times, sometimes with newlines, sometimes without, and sleep()s in between outputs:
delayed_output.c:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("Here is some input...");
fflush(stdout);
sleep(3);
printf("and here is some more.\n");
printf("Yet more output is here...");
fflush(stdout);
sleep(3);
printf("and here's the end of it.\n");
printf("Here's some more, not ending with a newline. ");
printf("There are some more words here, to exceed our buffer.");
fflush(stdout);
return 0;
}
The main program:
buffer.c:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/select.h>
#define INTBUFFERSIZE 1024
#define BUFFERSIZE 60
#define GET_LINE_DEBUG true
/* Prints a debug message if debugging is on */
void get_line_debug_msg(const char * msg, ...)
{
va_list ap;
va_start(ap, msg);
if ( GET_LINE_DEBUG ) {
vfprintf(stderr, msg, ap);
}
va_end(ap);
}
/*
* Gets a line from a file if one is available.
*
* Returns:
* 1 if a line was successfully gotten
* 0 if a line is not yet available
* -1 on end-of-file (no more input available)
*
* NOTE: This function can be used only with one file, and will
* be unusable once that file has reached the end.
*/
int get_line_if_ready(int fd, char * out_buffer, const size_t size)
{
static char int_buffer[INTBUFFERSIZE + 1] = {0}; /* Internal buffer */
static char * back = int_buffer; /* Next available space in buffer */
static bool end_of_file = false;
if ( !end_of_file ) {
/* Check if input is available */
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
struct timeval tv = {0, 0};
int status;
if ( (status = select(fd + 1, &fds, NULL, NULL, &tv)) == -1 ) {
perror("error calling select()");
exit(EXIT_FAILURE);
}
else if ( status == 0 ) {
/* Return zero if no input available */
return 0;
}
/* Get as much available input as will fit in buffer */
const size_t bufferspace = INTBUFFERSIZE - (back - int_buffer) - 1;
const ssize_t numread = read(fd, back, bufferspace);
if ( numread == -1 ) {
perror("error calling read()");
exit(EXIT_FAILURE);
}
else if ( numread == 0 ) {
end_of_file = true;
}
else {
const char * oldback = back;
back += numread;
*back = 0;
get_line_debug_msg("(In function, just read [%s])\n"
"(Internal buffer is [%s])\n",
oldback, int_buffer);
}
}
/* Write line to output buffer if a full line is available,
* or if we have reached the end of the file. */
char * endptr;
const size_t bufferspace = INTBUFFERSIZE - (back - int_buffer) - 1;
if ( (endptr = strchr(int_buffer, '\n')) ||
bufferspace == 0 ||
end_of_file ) {
const size_t buf_len = back - int_buffer;
if ( end_of_file && buf_len == 0 ) {
/* Buffer empty, we're done */
return -1;
}
endptr = (end_of_file || bufferspace == 0) ? back : endptr + 1;
const size_t line_len = endptr - int_buffer;
const size_t numcopy = line_len > (size - 1) ? (size - 1) : line_len;
strncpy(out_buffer, int_buffer, numcopy);
out_buffer[numcopy] = 0;
memmove(int_buffer, int_buffer + numcopy, INTBUFFERSIZE - numcopy);
back -= numcopy;
return 1;
}
/* No full line available, and
* at end of file, so return 0. */
return 0;
}
int main(void)
{
char buffer[BUFFERSIZE];
FILE * fp = popen("./delayed_output", "r");
if ( !fp ) {
perror("error calling popen()");
return EXIT_FAILURE;
}
sleep(1); /* Give child process some time to write output */
int n = 0;
while ( n != -1 ) {
/* Loop until we get a line */
while ( !(n = get_line_if_ready(fileno(fp), buffer, BUFFERSIZE)) ) {
/* Here's where you could do other stuff if no line
* is available. Here, we'll just sleep for a while. */
printf("Line is not ready. Sleeping for five seconds.\n");
sleep(5);
}
/* Output it if we're not at end of file */
if ( n != -1 ) {
const size_t len = strlen(buffer);
if ( buffer[len - 1] == '\n' ) {
buffer[len - 1] = 0;
}
printf("Got line: %s\n", buffer);
}
}
if ( pclose(fp) == -1 ) {
perror("error calling pclose()");
return EXIT_FAILURE;
}
return 0;
}
and the output:
paul#thoth:~/src/sandbox/buffer$ ./buffer
(In function, just read [Here is some input...])
(Internal buffer is [Here is some input...])
Line is not ready. Sleeping for five seconds.
(In function, just read [and here is some more.
Yet more output is here...])
(Internal buffer is [Here is some input...and here is some more.
Yet more output is here...])
Got line: Here is some input...and here is some more.
Line is not ready. Sleeping for five seconds.
(In function, just read [and here's the end of it.
Here's some more, not ending with a newline. There are some more words here, to exceed our buffer.])
(Internal buffer is [Yet more output is here...and here's the end of it.
Here's some more, not ending with a newline. There are some more words here, to exceed our buffer.])
Got line: Yet more output is here...and here's the end of it.
Got line: Here's some more, not ending with a newline. There are some
Got line: more words here, to exceed our buffer.
paul#thoth:~/src/sandbox/buffer$
Is there any way to tell how many bytes are ready to be read?
Not that I'm aware of in C99/POSIX. I'm guessing this functionality wasn't deemed useful since files have a fixed size (most of the time, anyways). Unfortunately select() is very rudimentary as you have already seen.
I guess what I really want is a way to know if a full line is ready to be read before calling fgets.
fgets() buffers in a loop until a '\n' is reached. This action consumes the input from the underlying file descriptor, so you'll need to implement a non-blocking version yourself.

How to prevent garbage being read from read end of PIPE in Linux

I have written a small code using pipe and fork. The child process calls child function which writes to the pipe. The parent process calls parent function which reads from the pipe.
The problem comes when the first call of the program after fork() goes to parent function. Here the write end is closed. Now the problem is that the read call is reading some garbage into buf and nread is giving value > 0 . How can this be prevented.
Using Linux 2.6.32-30-generic and gcc 4.4.3. Below is the code::
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#define MSGSIZE 16
void parent(int* p);
void child(int* p);
char* msg1 = "hello";
char* msg2 = "bye";
int main()
{
int pfd[2];
if(pipe(pfd) == -1)
{
printf("Unable to create pipe\n");
exit(1);
}
fcntl(pfd[0],F_SETFL,O_NDELAY);
if(fork() == 0)
child(pfd);
else
parent(pfd);
return 0;
}
void parent(int p[2])
{
int nread;
char buf[MSGSIZE];
buf[0] = '\0';
close(p[1]);
for(;;)
{
nread = read(p[0] , buf , MSGSIZE);
if(nread == 0)
{
printf("pipe Empty/n");
sleep(1);
}
else
{
if(strcmp(buf,msg2) == 0)
{
printf("End of conversation\n");
exit(0);
}
else
printf("MSG=%s\n" , buf);
}
}
}
void child(int p[2])
{
int count;
close(p[0]);
for(count = 0 ; count < 3 ; count++)
{
write(p[1],msg1 , MSGSIZE);
sleep(3);
}
write(p[1],msg2,MSGSIZE);
exit(0);
}
One problem is this:
char buf[MSGSIZE];
buf[0] = '\0';
this only sets the first character in buf to the null terminator: the remaining characters in buf are unitialized. The read() is attempting to read 16 bytes meaning the characters in buf will not be null terminated and printf("%s", buf) requires that buf is null terminated. Even if buf had been initialized correctly it would still not be sufficient due to its size being 16 but the read() reading 16 also, leaving no room for the null terminator.
A possible fix would be:
char buf[MSGSIZE + 1] = ""; /* +1 added to store the null terminator and
all characters set to 0 (null terminator). */
Another problem is the write()s (as commented by Joachim Pileborg):
write(p[1],msg1 , MSGSIZE);
write(p[1],msg2 , MSGSIZE);
msg1 and msg2 are not 16 bytes long. Change to:
write(p[1],msg1 , strlen(msg1));
write(p[1],msg2 , strlen(msg2));
Also, read() returns -1 on failure so the following is not sufficient:
nread = read(p[0] , buf , MSGSIZE);
if(nread == 0)
{
...
}
Check also for -1:
else if(nread == -1)
{
fprintf(stderr, "read() failed: %s\n", strerror(errno));
}
else
{
...
}
EDIT:
See answer from nos regarding blocking/non-blocking configuration issues.
Your real problem is this line:
fcntl(pfd[0],F_SETFL,O_NDELAY);
This sets the read end pipe to non-blocking. So every read() call will return as much data as there is in the buffer, or return -1 and set errno to EWOULDBLOCK if there's no data to be read at this particular time.
However, your code does not handle that case, it only checks if(nread == 0) and prints out the buffer even if you didn't read anything. So remove that line.
If you do not want to send fixed size messages, or want to keep the read end non-blocking, things becomes more tricky, as you have to account for at least these cases:
read() returning -1 and sets errno to EWOULDBLOCK (just try read() again).
read() reads the first 4 bytes of your "message", and the next read returns the rest of the message.
read() reads the first message, and half of the subsequent message.
i.e. you need some form of framing/delimiters on your messages that you need to handle, unless you just need to stream the content of the pipe further on.
Read does not nul-terminate the input.
To print a character buffer that isn't nul-terminated, do like this:
printf("MSQ=%.*s\n", nread, buf);
If you want to nul-terminate the read buffer you need to make 2 changes.
1 . Increase the size of the buffer to MSGSIZE+1:
char buf[MSGSIZE + 1];
2 . nul-terminate buf after each read.
buf[nread > 0 ? nread : 0] = 0; // read returns -1 on error
Also
msg1 and msg2 are string literals which are smaller than MSGSIZE.
The thing about garbage is this principle called GIGO: garbage in, garbage out.
Don't want garbage in the pipe? Use a drain trap on your kitchen sink.

Resources