My program(below) writes(with pwrite()) text to a file and reads(with pread()) from the file. My problems are pread function doesn't read my text from file and what's wrong with close function(last part of program)? Results is in the second part. Where is my mistake?
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int main()
{
int fd, nr, nr2, nw, nw2;
char fl_nm[]={"file_io/pwrite.txt"};
char buf_wr[]={"hello everyone this is first text\n"};
char buf_wr2[]={"Did you miss me? Don't afraid\n"};
char buf_rd[120];
char buf_rd2[120];
//open file
fd = open(fl_nm, O_RDWR|O_CREAT, 0777);
nw = pwrite(fd, &buf_wr, strlen(buf_wr), 14);
//error checking
if(fd == -1){
perror("[error in open]\n");
}
else if(nw == -1){
perror("[error in write]\n");
}
else{
/*if open and write process are okey, read first write data
* from file*/
nr = read(fd, &buf_rd, sizeof(buf_rd));
//display succeeded message about first write and open process
printf("[file is opened]\n");
printf("[succeeded write(1) process]\n");
//read process error control
if(nr == -1){
perror("[error in read]\n");
} else{
printf("[reading(1) data] from %s\n", fl_nm);
printf("[%s]\n", buf_rd);
}
}
//second write process.
nw2= pwrite(fd, &buf_wr2, strlen(buf_wr2), 30);
//write error checking
if(nw2 == -1){
perror("[error in write 2]\n");
}else{
/*if write process is correct
* second read process*/
nr2 = read(fd, &buf_rd2, sizeof(buf_rd));
printf("-----------------------------------\n");
printf("[succeeded write(2) process]\n");
printf("[reading(2) data] from %s\n", fl_nm);
printf("[%s]\n", buf_rd2);
}
//close file
close(fd);
//error checking for close process
if(close(fd) == -1){
perror("[error in close]\n");
}else{
printf("[succeeded in close]\n");
}
return 0;
}
Result:
$ gcc pwrite.c -o pwrite
$ ./pwrite
[file is opened]
[succeeded write(1) process]
[reading(1) data] from file_io/pwrite.txt
[]
-----------------------------------
[succeeded write(2) process]
[reading(2) data] from file_io/pwrite.txt
[]
[error in close]
: Bad file descriptor
1) close() fails because you are closing the file twice:
//close file
close(fd);
//error check close process
if(close(fd) == -1){
After the first call to close(fd);, fd becomes indeterminate and the second call to close(fd) fails. You just need to remove the first call to close(fd);.
2) You are printing buf_rd as if it's a C-string. read() doesn't terminate buf_rd with a null byte.
3) You are writing at random offsets (14 and 30) using pwrite(). But read() reads from current offset - which means the starting byte could a null byte and thus %s stops printing right away (i.e. prints nothing). You are reading a lot more than what you write. That means read() is going to return less than the requested number of bytes. So use the return value of read() to get the number of bytes successfully read.
Instead, print each byte using a loop:
for (size_t l = 0; l < nr; l++)
printf("%c", buf_rd[l]);
and
for (size_t l = 0; l < nr2; l++)
printf("%c", buf_rd2[l]);
You're using pointers incorrectly, accessing to address of arrays should be with their names alone, not &name.
replace &buf_wr with buf_wr, accessing to incorrect address with &buf_wr to writing there will corrupt your stack and also variables defined inside stack
Edit:
replace
nw = pwrite(fd, &buf_wr, strlen(buf_wr), 14);
to
nw = pwrite(fd, buf_wr, strlen(buf_wr), 14);
and all other instances..
Related
I installed an application and its command line can do:
command -input 1.txt
command < 1.txt
echo "hello" | command
and output something. I don't have the source code and want to implement that behaviour too.
What I've tried is:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[]){
if ((fseek(stdin, 0, SEEK_END), ftell(stdin)) > 0){
rewind(stdin);
printf("stdin has data\n");
char buffer[100];
fgets(buffer, sizeof buffer, stdin);
printf("stdin data are: %s\n", buffer);
}else{
if (argc < 2){
printf("no cmd arguments\n");
return -1;
}else{
printf("command line argument: %s\n", argv[1]);
FILE* fp = fopen(argv[1], "r");
if (fp == NULL){
printf("NULL fp pointer\n");
return -1;
}
char a[100] = {0};
fgets(a, sizeof a, fp);
printf("first line of file: %s\n", a);
}
}
return 0;
}
But the problem is that pipes are not seekable. So ((fseek(stdin, 0, SEEK_END), ftell(stdin)) > 0) doesn't fit all cases.
One solution that I think of is:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[]){
if (argc > 1){
//open file with argv[1] as filename
//read data from disk file
}else{
//read data from stdin
if(stdin is file){
//get file size
//read data from stdin
}else if(stdin is pipe){
//get pipe size
//read data from stdin
}
}
return 0;
}
I have 2 problems with this code:
Is there a ispipe() function which works like isatty(fileno(stdin))? I need to tell if stdin is a pipe.
How do I get the stdin size/length from a pipe? Apparently I can't use:
fseek(stdin, 0, SEEK_END);
long size = ftell(stdin));
As #Peter pointed out in the comment, I should not try to get the stdin size from a pipe beforehand, then how do I know it reaches the end? Could anyone gives me an minimum example about this "stream-based processing"?
You can use the fstat() syscall to tell if standard input is a pipe (Either anonymous or named), or a file (And if a file, find its size):
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main(void) {
struct stat s;
if (fstat(STDIN_FILENO, &s) < 0) {
perror("fstat");
return EXIT_FAILURE;
}
switch (s.st_mode & S_IFMT) {
case S_IFIFO:
puts("standard input is a pipe.");
break;
case S_IFREG:
printf("standard input is a file that is %ld bytes in size.\n",
(long)s.st_size);
break;
case S_IFCHR:
if (isatty(STDIN_FILENO)) {
puts("standard input is a terminal.");
} else {
puts("standard input is a character device.");
}
break;
default:
puts("standard input is something else.");
}
return 0;
}
Example:
$ gcc testpipe.c
$ cat testpipe.c | ./a.out
standard input is a pipe.
$ ./a.out < testpipe.c
standard input is a file that is 525 bytes in size.
$ ./a.out
standard input is a terminal.
The only way to be sure that you won't recieve more data from a pipe is when it is closed (SIGPIPE signal).
Thus, as stated in comments, allocating/reading the right of memory is challenging with pipes, since they can be infinite (e.g. /dev/random). You have to make hypothesis or use extra data in order to handle the pipe.
Depending on your use case, these strategies can be one of:
Sending the data length at the beginning of the message. This can be like: echo -e'\x05\x00\x00\x00Hello'|./myprog. With that strategy, it is trivial to read the pipe but it requieres that you know the total size of the input before you start sending it.
Allocating and reading a limited amount of data/time. If you recieve than PIPE_MAX_SIZE bytes or you wait more than TIMEOUT_PIPE, close the pipe and handle the possibly incomplete message.
Handle the message block by block. If your message follows a regular pattern, you can read it this way and handle blocks sequentially until you reach the end of the message. This also allows you to discard previous buffer to read unlimited amount of data that would not fit in memory.
I tried to write basic program in C which copy data from file to another with given source path, destination path and buffer size as input.
my problem is the destination file filled with junk or something because its way larger than the source (get bigger depending on buffer size) and can't be open.
How do i read and write just the bytes in the source?
i'm working in linux, and this is the actually copying part:
char buffer[buffer_size];
int readable=1;
int writeable;
while(readable != 0){
readable = read(sourcef, buffer, buffer_size);
if(readable == -1){
close(sourcef);
close(destf);
exit_with_usage("Could not read.");
}
writeable = write(destf, buffer, buffer_size);
if(writeable == -1){
close(sourcef);
close(destf);
exit_with_usage("Could not write.");
}
}
writeable = write(destf, buffer, buffer_size);
must be
writeable = write(destf, buffer, readable);
Currently you do not write the number of characters you read but all the buffer, so the output file is too large
You also manage wrongly the end of the input file
The return value of read is :
On success, the number of bytes read is returned (zero indicates end of file)
On error, -1 is returned
A proposal :
/* you already check input and output file was open with success */
char buffer[buffer_size];
for(;;){
ssize_t readable = read(sourcef, buffer, buffer_size);
if(readable <= 0){
close(sourcef);
close(destf);
if (readable != 0)
/* not EOF */
exit_with_usage("Could not read.");
/* EOF */
break;
}
if (write(destf, buffer, n) != n) {
close(sourcef);
close(destf);
exit_with_usage("Could not write.");
}
}
I suppose exit_with_usage calls exit() so does not return
Note in theory write may write less than the expected number of characters without being an error, and the write has to be done in a loop, but in that case it is useless to manage that
read function returns how many bytes were read to buffer(which has buffer_size). Its not always the case actual bytes read has same value as buffer size(consider scenario if there are not enough bytes left in source file to fully fill your buffer). So you should write to destination file not buffer_size(third argument of the write function), but how many bytes have you read - that is readable variable in your code
You should exit when readable returns an error.So
while(readable != 0){
should be
while(readable != -1){
So that loop could be terminataed when an readfile is exhausted.
You see currently after the whole readfile has been read, calling read fails but write is being called repeatedly since execution has no exit path for failure on read. Also write should only write the number of bytes read. So the code would look like this:
char buffer[buffer_size];
int readable=1;
int writeable;
while(readable != -1){
readable = read(sourcef, buffer, buffer_size);
if(readable == -1){
close(sourcef);
close(destf);
exit_with_usage("Could not read.");
}
writeable = write(destf, buffer, readable);
if(writeable == -1){
close(sourcef);
close(destf);
exit_with_usage("Could not write.");
}
}
Simple code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // For system calls write, read e close
#include <fcntl.h>
#define BUFFER_SIZE 4096
int main(int argc, char* argv[]) {
if (argc != 3) {
printf("Usage %s Src_file Dest_file\n", argv[0]);
exit(1);
}
unsigned char buffer[BUFFER_SIZE] = {0};
ssize_t ReadByte = 0;
int src_fd, dst_fd;
// open file in read mode
if ((src_fd = open(argv[1], O_RDONLY)) == -1) {
printf("Failed to open input file %s\n", argv[1]);
exit(1);
}
// open file in write mode and already exists to overwrite
if ((dst_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 644)) == -1) {
printf("Failed to create output file %s\n", argv[2]);
exit(1);
}
// loop
while (1) {
// read buffer
ReadByte = read(src_fd, buffer, sizeof(buffer));
// error with reading
if (ReadByte == -1) {
printf("Encountered an error\n");
break;
} else if (ReadByte == 0) {
// file end exit loop
printf("File copying successful.\n");
break;
}
// error with writing
if (write(dst_fd, buffer, ReadByte) == -1) {
printf("Failed to copying file\n");
break;
}
}
// Close file
close(src_fd);
close(dst_fd);
exit(0);
}
Run
./program src_file dest_file
I am trying to write a program on how to read a file 10 bytes per time using read, however, I do not know how to go about it. How should I modify this code to read 10bytes per time. Thanks!!!!
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
int main (int argc, char *argv[])
{
printf("I am here1\n");
int fd, readd = 0;
char* buf[1024];
printf("I am here2\n");
fd =open("text.txt", O_RDWR);
if (fd == -1)
{
perror("open failed");
exit(1);
}
else
{
printf("I am here3\n");
if(("text.txt",buf, 1024)<0)
printf("read error\n");
else
{
printf("I am here3\n");
/*******************************
* I suspect this should be the place I make the modification
*******************************/
if(read("text.txt",buf, 1024)<0)
printf("read error\n");
else
{
printf("I am here4\n");
printf("\nN: %c",buf);
if(write(fd,buf,readd) != readd)
printf("write error\n");
}
}
return 0;
}
The final parameter of read() is the maximum size of the data you wish to read so, to try and read ten bytes at a time, you would need:
read (fd, buf, 10)
You'll notice I've also changed the first parameter to the file descriptor rather than the file name string.
Now, you'll probably want that in a loop since you'll want to do something with the data, and you also need to check the return value since it can give you less than what you asked for.
A good example for doing this would be:
int copyTenAtATime (char *infile, char *outfile) {
// Buffer details (size and data).
int sz;
char buff[10];
// Try open input and output.
int ifd = open (infile, O_RDWR);
int ofd = open (outfile, O_WRONLY|O_CREAT);
// Do nothing unless both opened okay.
if ((ifd >= 0) && (ofd >= 0)) {
// Read chunk, stopping on error or end of file.
while ((sz = read (ifd, buff, sizeof (buff))) > 0) {
// Write chunk, flagging error if not all written.
if (write (ofd, buff, sz) != sz) {
sz = -1;
break;
}
}
}
// Finished or errored here, close files that were opened.
if (ifd >= 0) close (ifd);
if (ofd >= 0) close (ofd);
// Return zero if all okay, otherwise error indicator.
return (sz == 0) ? 0 : -1;
}
change the value in read,
read(fd,buf,10);
From man of read
ssize_t read(int fd, void *buf, size_t count);
read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.
if(read("text.txt",buf, 1024)<0)// this will give you the error.
First argument must be an file descriptor.
I'm writing a program that when run from two separate bash sessions as two separate processes, opens a named pipe between the two to allow strings to be sent from one to the other.
When the process is first executed from one terminal, it checks stat(fname, buf) == -1 to see if a file at path fname exists and if not, creates it. The process then assumes that since it was the one to make the FIFO, it is the one that will be sending messages through it and continues accordingly.
After that occurs, the program can then be run from another terminal that should determine that it will be the receiver of messages through the pipe by checking stat(fname, buf) == -1. The condition should return false now, and stat(fname, buf) itself should return 0 because there exists a file at fname now.
But for reasons I am unable to discern, when the second process is run, stat(fname, buf) still returns -1. The variable errno is set to EFAULT. The man page for stat() only decribes EFAULT as "Bad address." Any help determining why the error occurs or what is meant by "Bad address." would be greaty appreciated.
I've verified that the file is indeed created by the first process as intended. The first process waits at the line pipe = open(fname, O_WRONLY); because it can't continue until the other end of pipe is opened.
Edit: The following is a self-contained implementation of my code. I have confirmed that it compiles and experiences the problem I described here.
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define MAX_LINE 80
#define oops(m,x) { perror(m); exit(x); }
int main(int argc, char const *argv[]) {
char line[MAX_LINE];
int pipe, pitcher, catcher, initPitcher, quit;
struct stat* buf;
char* fname = "/tmp/absFIFOO";
initPitcher = catcher = pitcher = quit = 0;
while (!quit) {
if (((!pitcher && !catcher && stat(fname, buf) == -1) || pitcher) && !quit) {
// Then file does not exist
if (errno == ENOENT) {
// printf("We're in the file does not exist part\n");
if (!pitcher && !catcher) {
// Then this must be the first time we're running the program. This process will take care of the unlink().
initPitcher = 1;
int stat;
if (stat = mkfifo(fname, 0600) < 0)
oops("Cannot make FIFO", stat);
}
pitcher = 1;
// open a named pipe
pipe = open(fname, O_WRONLY);
printf("Enter line: ");
fgets(line, MAX_LINE, stdin);
if (!strcmp(line, "quit\n")) {
quit = 1;
}
// actually write out the data and close the pipe
write(pipe, line, strlen(line));
close(pipe);
}
} else if (((!pitcher && !catcher) || catcher) && !quit) {
// The first condition is just a check to see if this is the first time we've run the program. We could check if stat(...) == 0, but that would be unnecessary
catcher = 1;
pipe = open("/tmp/absFIFO", O_RDONLY);
// set the mode to blocking (note '~')
int flags;
flags &= ~O_NONBLOCK;
fcntl(pipe, F_SETFL, flags); //what does this do?
// read the data from the pipe
read(pipe, line, MAX_LINE);
if (!strcmp(line, "quit\n")) {
quit = 1;
}
printf("Received line: %s\n", line);
// close the pipe
close(pipe);
}
}
if (initPitcher)
unlink(fname);
return 0;
}
You have this piece of code:
struct stat* buf;
...
if (((!pitcher && !catcher && stat(fname, buf) == -1)
When you call stat(), buf isn't initalized and there's no telling what it points to.
You must allocate some storage for it, so stat() has a valid place to store the result.
The easiest thing is to just allocate it on the stack:
struct stat buf;
...
if (((!pitcher && !catcher && stat(fname, &buf) == -1)
You have not shown your code, but EFAULT means 'bad address'. This indicates that you have not properly allocated (or passed) your buffer for stat or the filename (fname).
buf isn't initialised anywhere. What exactly do you expect to happen?
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.