would install valgrind to tell me what the problem is, but unfortunately can't any new programs on this computer... Could anyone tell me if there's an obvious problem with this "echo" program? Doing this for a friend, so not sure what the layout of the client is on the other side, but I know that both reads and writes are valid socket descriptors, and I've tested that n = write(writes,"I got your message \n",20); and n = write(reads,"I got your message \n",20); both work so can confirm that it's not a case of an invalid fd. Thanks!
int
main( int argc, char** argv ) {
int reads = atoi(argv[1]) ;
int writes = atoi(argv[3]) ;
int n ;
char buffer[MAX_LINE];
memset(buffer, 0, sizeof(buffer));
int i = 0 ;
while (1) {
read(reads, buffer, sizeof(buffer));
n = write(writes,buffer,sizeof(buffer));
if (n < 0) perror("ERROR reading from socket");
}
There are a few problems, the most pressing of which is that you're likely pushing garbage data down the the write socket by using sizeof(buffer) when writing. Lets say you read data from the reads socket and it's less than MAX_LINES. When you go to write that data, you'll be writing whatever you read plus the garbage at the end of the buffer (even though you memset at the very beginning, continual use of the same buffer without reacting to different read sizes will probably generate some garbage.
Try getting the return value from read and using it in your write. If the read return indicates an error, clean up and either exit or try again, depending on how you want your program to behave.
int n, size;
while (1) {
size = read(reads, buffer, sizeof(buffer));
if (size > 0) {
n = write(writes, buffer, size);
if (n != size) {
// write error, do something
}
} else {
// Read error, do something
}
}
This, of course, assumes your writes and reads are valid file descriptors.
These two lines look very suspicious:
int reads = atoi(argv[1]) ;
int writes = atoi(argv[3]) ;
Do you really get file/socket descriptor numbers on the command line? From where?
Check the return value of your read(2) and write(2), and then the value of errno(3) - they probably tell you that your file descriptors are invalid (EBADF).
One point not made thus far: Although you know that the file descriptors are valid, you should include some sanity checking of the command line.
if (argc < 3) {
printf("usage: foo: input output\n");
exit(0);
}
Even with this sanity checking passing parameters like this on a command line can be dangerous.
The memset() is not needed, provided you change the following (which you should do nevertheless).
read() has a result, telling you how much it has actually read. This you should give to write() in order to write only what you actually have, removing the need for zeroing.
MAX_LINE should be at least 512, if not more.
There probably are some more issues, but I think I have the most important ones.
Related
I want to take all characters past location 900 from a file called WWW, and put all of these in an array:
//Keep track of all characters past position 900 in WWW.
int Seek900InWWW = lseek(WWW, 900, 0); //goes to position 900 in WWW
printf("%d \n", Seek900InWWW);
if(Seek900InWWW < 0)
printf("Error seeking to position 900 in WWW.txt");
char EverythingPast900[appropriatesize];
int NextRead;
char NextChar[1];
int i = 0;
while((NextRead = read(WWW, NextChar, sizeof(NextChar))) > 0) {
EverythingPast900[i] = NextChar[0];
printf("%c \n", NextChar[0]);
i++;
}
I try to create a char array of length 1, since the read system call requires a pointer, I cannot use a regular char. The above code does not work. In fact, it does not print any characters to the terminal as expected by the loop. I think my logic is correct, but perhaps a misunderstanding of whats going on behind the scenes is what is making this hard for me. Or maybe i missed something simple (hope not).
If you already know how many bytes to read (e.g. in appropriatesize) then just read in that many bytes at once, rather than reading in bytes one at a time.
char everythingPast900[appropriatesize];
ssize_t bytesRead = read(WWW, everythingPast900, sizeof everythingPast900);
if (bytesRead > 0 && bytesRead != appropriatesize)
{
// only everythingPast900[0] to everythingPast900[bytesRead - 1] is valid
}
I made a test version of your code and added bits you left out. Why did you leave them out?
I also made a file named www.txt that has a hundred lines of "This is a test line." in it.
And I found a potential problem, depending on how big your appropriatesize value is and how big the file is. If you write past the end of EverythingPast900 it is possible for you to kill your program and crash it before you ever produce any output to display. That might happen on Windows where stdout may not be line buffered depending on which libraries you used.
See the MSDN setvbuf page, in particular "For some systems, this provides line buffering. However, for Win32, the behavior is the same as _IOFBF - Full Buffering."
This seems to work:
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
int WWW = open("www.txt", O_RDONLY);
if(WWW < 0)
printf("Error opening www.txt\n");
//Keep track of all characters past position 900 in WWW.
int Seek900InWWW = lseek(WWW, 900, 0); //goes to position 900 in WWW
printf("%d \n", Seek900InWWW);
if(Seek900InWWW < 0)
printf("Error seeking to position 900 in WWW.txt");
int appropriatesize = 1000;
char EverythingPast900[appropriatesize];
int NextRead;
char NextChar[1];
int i = 0;
while(i < appropriatesize && (NextRead = read(WWW, NextChar, sizeof(NextChar))) > 0) {
EverythingPast900[i] = NextChar[0];
printf("%c \n", NextChar[0]);
i++;
}
return 0;
}
As stated in another answer, read more than one byte. The theory behind "buffers" is to reduce the amount of read/write operations due to how slow disk I/O (or network I/O) is compared to memory speed and CPU speed. Look at it as if it is code and consider which is faster: adding 1 to the file size N times and writing N bytes individually, or adding N to the file size once and writing N bytes at once?
Another thing worth mentioning is the fact that read may read fewer than the number of bytes you requested, even if there is more to read. The answer written by #dreamlax illustrates this fact. If you want, you can use a loop to read as many bytes as possible, filling the buffer. Note that I used a function, but you can do the same thing in your main code:
#include <sys/types.h>
/* Read from a file descriptor, filling the buffer with the requested
* number of bytes. If the end-of-file is encountered, the number of
* bytes returned may be less than the requested number of bytes.
* On error, -1 is returned. See read(2) or read(3) for possible
* values of errno.
* Otherwise, the number of bytes read is returned.
*/
ssize_t
read_fill (int fd, char *readbuf, ssize_t nrequested)
{
ssize_t nread, nsum = 0;
while (nrequested > 0
&& (nread = read (fd, readbuf, nrequested)) > 0)
{
nsum += nread;
nrequested -= nread;
readbuf += nread;
}
return nsum;
}
Note that the buffer is not null-terminated as not all data is necessarily text. You can pass buffer_size - 1 as the requested number of bytes and use the return value to add a null terminator where necessary. This is useful primarily when interacting with functions that will expect a null-terminated string:
char readbuf[4096];
ssize_t n;
int fd;
fd = open ("WWW", O_RDONLY);
if (fd == -1)
{
perror ("unable to open WWW");
exit (1);
}
n = lseek (fd, 900, SEEK_SET);
if (n == -1)
{
fprintf (stderr,
"warning: seek operation failed: %s\n"
" reading 900 bytes instead\n",
strerror (errno));
n = read_fill (fd, readbuf, 900);
if (n < 900)
{
fprintf (stderr, "error: fewer than 900 bytes in file\n");
close (fd);
exit (1);
}
}
/* Read a file, printing its contents to the screen.
*
* Caveat:
* Not safe for UTF-8 or other variable-width/multibyte
* encodings since required bytes may get cut off.
*/
while ((n = read_fill (fd, readbuf, (ssize_t) sizeof readbuf - 1)) > 0)
{
readbuf[n] = 0;
printf ("Read\n****\n%s\n****\n", readbuf);
}
if (n == -1)
{
close (fd);
perror ("error reading from WWW");
exit (1);
}
close (fd);
I could also have avoided the null termination operation and filled all 4096 bytes of the buffer, electing to use the precision part of the format specifiers of printf in this case, changing the format specification from %s to %.4096s. However, this may not be feasible with unusually large buffers (perhaps allocated by malloc to avoid stack overflow) because the buffer size may not be representable with the int type.
Also, you can use a regular char just fine:
char c;
nread = read (fd, &c, 1);
Apparently you didn't know that the unary & operator gets the address of whatever variable is its operand, creating a value of type pointer-to-{typeof var}? Either way, it takes up the same amount of memory, but reading 1 byte at a time is something that normally isn't done as I've explained.
Mixing declarations and code is a no no. Also, no, that is not a valid declaration. C should complain about it along the lines of it being variably defined.
What you want is dynamically allocating the memory for your char buffer[]. You'll have to use pointers.
http://www.ontko.com/pub/rayo/cs35/pointers.html
Then read this one.
http://www.cprogramming.com/tutorial/c/lesson6.html
Then research a function called memcpy().
Enjoy.
Read through that guide, then you should be able to solve your problem in an entirely different way.
Psuedo code.
declare a buffer of char(pointer related)
allocate memory for said buffer(dynamic memory related)
Find location of where you want to start at
point to it(pointer related)
Figure out how much you want to store(technically a part of allocating memory^^^)
Use memcpy() to store what you want in the buffer
I'm trying to make a program that would copy 512 bytes from 1 file to another using said system calls (I could make a couple buffers, memcpy() and then fwrite() but I want to practice with Unix specific low level I/O). Here is the beginning of the code:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
int main(int argc, char **argv)
{
int src, dest, bytes_read;
char tmp_buf[512];
if (argc < 3)
printf("Needs 2 arguments.");
printf("And this message I for some reason don't see.... o_O");
if ((src = open(argv[1], O_RDWR, 0)) == -1 || (dest = open(argv[2], O_CREAT, 0)) == -1)
perror("Error");
while ((bytes_read = read(src, tmp_buf, 512)) != -1)
write(dest, tmp_buf, 512);
return 0;
}
I know I didn't deal with the fact that the file read from isn't going to be a multiple of 512 in size. But first I really need to figure out 2 things:
Why isn't my message showing up? No segmentation fault either, so I end up having to just C-c out of the program
How exactly do those low level functions work? Is there a pointer which shifts with each system call, like say if we were using FILE *file with fwrite, where our *file would automatically increment, or do we have to increment the file pointer by hand? If so, how would we access it assuming that open() and etc. never specify a file pointer, rather just the file ID?
Any help would be great. Please. Thank you!
The reason you don't see the printed message is because you don't flush the buffers. The text should show up once the program is done though (which never happens, and why this is, is explained in a comment by trojanfoe and in an answer by paxdiablo). Simply add a newline at the end of the strings to see them.
And you have a serious error in the read/write loop. If you read less than the requested 512 bytes, you will still write 512 bytes.
Also, while you do check for errors when opening, you don't know which of the open calls that failed. And you still continue the program even if you get an error.
And finally, the functions are very simple: They call a function in the kernel which handles everything for you. If you read X bytes the file pointer is moved forward X bytes after the call is done.
The reason you don't see the message is because you're in line-buffered mode. It will only be flushed if it discovers a newline character.
As to why it's waiting forever, you'll only get -1 on an error.
Successfully reading to end of file will give you a 0 return value.
A better loop would be along the lines of:
int bytes_left = 512;
while ((bytes_left > 0) {
bytes_read = read(src, tmp_buf, bytes_left);
if (bytes_read < 1) break;
write(dest, tmp_buf, bytes_read);
bytes_left -= bytes_read;
}
if (bytes_left < 0)
; // error of some sort
I'm having a hard time trying to figure out why this piece of code doesn't work as it should. I am learning the basics of I/O operations and I have to come up with a C program that writes on a 'log.txt' file what is given from standard input and as the 'stop' word is entered, the program must halt.
So my code is:
#include "main.h"
#define SIZE 1024
int main(int argc, char *argv[])
{
int fd;
int readBytes;
int writBytes;
char *buffer;
if ((fd = open("log.txt", O_WRONLY|O_APPEND)) < 0)
{
perror("open");
}
buffer = (char *) calloc (SIZE, sizeof(char));
while ((readBytes = read(0, buffer, SIZE) < SIZE)&&(strncmp(buffer, "stop", 4) != 0));
if ((writBytes = write(fd, buffer, SIZE)) < 0)
{
perror("write");
}
if ((close(fd)) < 0)
{
perror("close");
}
}
If I enter:
this is just a text
stop
The output is
stop
is just a text
If I enter more than a sentence:
this is just a text
this is more text
and text again
stop
This is what is logged:
stop
ext again
xt
t
And on top of that if I try to edit the log.txt file from vim or just a text editor I can see '\00's. I guess \00 stands for all the bytes left empty from the 1024 available, right? How can I prevent that from happening?
It looks like you're expecting
readBytes = read(0, buffer, SIZE) < SIZE)
to somehow accumulate things in buffer. It doesn't. Every subsequent read will put whatever it read at the start of the buffer, overwriting what the previous read has read.
You need to put your write in the while block - one write for every read, and only write as much as you read, otherwise you'll write garbage (zeros from the calloc and/or leftovers from the previous read) in your log file.
Also note that while your technique will probably work most of the time for a line-buffered input stream, it will not do what you expect if you redirect from a file or a pipe. You should be using formatted input functions (like getline if you your implementation has that, scanf, or fgets).
Hi everyone I am running the following code on pseudo terminal /dev/pts/1 and I am tryin to read the contents from the terminal /dev/pts/2.
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
int main(){
char str[50];
int fd = open("/dev/pts/2",O_RDONLY);
str[read(fd,str,20)] = '\0';
printf("%s\n",str);
return 0;
}
anirudh#anirudh-Aspire-5920:~$ gcc test.c
anirudh#anirudh-Aspire-5920:~$ ./a.out
n
anirudh#anirudh-Aspire-5920:~$
On the terminal /dev/pts/2 I had typed "anirudh" however it showed "airudh" on that and the missing character n was displayed on the terminal /dev/pts/1.
However when I try to read from the terminal /dev/pts/1 I can read every character properly.
So I am not able to understand the behavior of this program. Please help me out. Thanks in advance. :)
First, you probably have another process reading from /dev/pts/2 and thus characters are send to it and not yours. Then the terminal is probably set in read "char per char" mode by that other process (that's what some shell do), you are reading just one character.
Wow. First, it is a general good rule: check what syscalls are returned to you. Alaways.
int main(){
char str[50];
int fd = open("/dev/pts/2",O_RDONLY);
if (fd == -1) {
perror("open");
...
}
Second, read may return fewer bytes than you request, look at the man:
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.
So even 1 byte may be returned from read. Third, read may return -1:
On error, -1 is returned, and errno is set appropriately.
So I guess it's better to write:
ssize_t nread;
if ((nread = read(fd, str, 20) > 0)) {
str[nread] = '\0';
} else if (nread == -1) {
perror("read");
...
}
printf("%s\n",str);
return 0;
}
I have got a small program that prints the contents of files using the system call - read.
unsigned char buffer[8];
size_t offset=0;
size_t bytes_read;
int i;
int fd = open(argv[1], O_RDONLY);
do{
bytes_read = read(fd, buffer, sizeof(buffer));
printf("0x%06x : ", offset);
for(i=0; i<bytes_read; ++i)
{
printf("%c ", buffer[i]);
}
printf("\n");
offset = offset + bytes_read;
}while(bytes_read == sizeof(buffer));
Now while running I give a file name that doesn't exist.
It prints some kind of data mixed with environment variables and a segmentation fault at the end.
How is this possible? What is the program printing?
Thanks,
John
It's printing rubbish because fd will invariably be set to -1 which is not a good thing to pass to read since it will, in turn do nothing other than return -1 as well. It will leave your buffer untouched meaning that it's holding whatever rubbish you had in there when you started.
You could probably put the entire do loop inside something like:
if (fd == -1) {
printf ("error here");
} else {
// do loop here
}
read is returning -1 because fd is invalid, you store that in bytes_read which is of type size_t which is unsigned, so your loop prints (size_t)-1 chars, which is a very large number, much larger than the size of buffer. So, you're printing a big chunk of your address space and then getting a segfault when you eventually reach the end and access an invalid address.
As others have mentioned (without answering your actual question), you should be checking the results of open for an error. e.g.,
int fd = open(argv[1], O_RDONLY);
if( fd < 0 ){
fprintf(stderr, "error opening %s: %s\n", argv[1], strerror(errno));
exit(1);
}
A caveat: if you do another system call, or call any routine that might do a system call (e.g., printf) before calling strerror, you must save errno and then pass the saved copy to strerror.
Another note about your program:
while(bytes_read == sizeof(buffer))
This is not a good test, because read can return less than the amount you ask for. Your loop should continue until read returns <= 0.
You should probably check that the file descriptor returned by open is valid before using it. As per these docs, you should get a non-negative response for a valid file. Reading from an invalid descriptor is likely the source of your problem.
Upon successful completion, open function shall open the file and return a non-negative integer representing the file descriptor. Otherwise, -1 shall be returned and errno set to indicate the error. So please check fd before entering the loop to perform the read.