Instant write buffer to stdout - c

Is possible to write a large block of text into stdout all at once.
For instance, I get a 50kb text file and put it into story.txt. I am curious if I can dump the contents of this file into stdout without the user noticing any text slowly coming in. One moment there is no text, next the whole buffer is flushed into stdout.
I was trying to do it with the following code but no matter what buffering mode I set it didn't manage to write the file all at once, only in parts.
/* dumps a lot of text at once */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
char story[100000];
char buffer[100000];
int main(int argc, char **argv)
{
FILE *handle = fopen("coolstory.txt", "r");
size_t n = fread(&story[0], 1, 100000, handle);
fclose(handle);
/* try to flush all at once... */
fclose(stdout);
freopen("/dev/tty", "w", stdout);
setvbuf(stdout, &buffer[0], _IOFBF, 100000);
fwrite(story, n, 1, stdout);
fflush(stdout);
printf("\nread %u bytes.\n", n);
return 0;
}
The reopen part was me wondering if setvbuf/flush would behave differently if I called them right after the stdout was opened. Unfortunately it did nothing.
I just want to know whether it is possible, and if not, why.

I'm on ubuntu linux 14.04.
Note: it is usually a bad idea to #include header files that are not used.
I ran this version of the code:
/* dumps a lot of text at once */
//#include <unistd.h>
#include <stdio.h>
//#include <stdlib.h>
//#include <fcntl.h>
//#include <string.h>
char story[100000];
int main( void )
{
FILE *handle = fopen("value_chainLength.txt", "r");
size_t n = fread(story, 1, 100000, handle);
fclose(handle);
fwrite(story, n, 1, stdout);
fflush(stdout);
printf("\nread %lu bytes.\n", (long unsigned)n);
return 0;
}
on a 46550749 byte text file
The output was done on a terminal almost as fast as I could press and release the 'enter' key.
the last line output was:
read 100000 bytes.
I did notice ever so slight a hesitation before printing the last line, all the lines before that point were practically instantaneous.

Related

Again a question about reading a directory file with C

How we can use C functions open/read/close properly in order to read a linux directory?. I notice serveral others asked this question before, about on reading directories in C, also that several ones suggest the use of readdir/opendir/closedir functions, I know, but RnK book (the C programming language) in fact define or introduces those readdir/opendir/closedir/ functions, the problem is that read() function "not read" properly directories; it returns -1 instead of the number of bytes readed. Is there any change into the actual read() C function that produces this or it is necesary modifications to read()?
Here is my example code:
#include <sys/types.h>
#include <unistd.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
int main(){
int fd;
size_t nbytes;
ssize_t bytes_read;
char buf[20];
fd=open(".",O_RDONLY,0);
nbytes = sizeof(buf);
bytes_read = read(fd, buf, nbytes);
printf("Buf size: %ld file descriptor: %d bytes readed:
%ld\n",nbytes,fd,bytes_read);
}
Compiling above code in ubuntu linux read gives bytes_read = -1.
Thanks in advance

Program running mkfifo doesn't work

I'm trying to make a named pipe on c under linux using the mkfifo command. But when I run the program, I either get a "no such file or directory" error or absolutely nothing (console doesn't display anything)
Here is my code :
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define MAX_LINE 80
int main(int argc, char** argv) {
int create;
//mkfifo("/tmp/myfifo", 0666);
create = mkfifo("tmp/myfifo", 0666);
if (create==-1)
{
printf("error%s", strerror(errno));
}
char line[MAX_LINE];
int pipe;
pipe = open("/tmp/myfifo", O_WRONLY);
if (pipe==-1)
{printf("error");
}
printf("Enter line: ");
fgets(line, MAX_LINE, stdin);
write(pipe, line, strlen(line));
sleep (100);
close(pipe);
return 0;
}
I am still learning, and I don't understand what i'm doing wrong. Thanks for your help.
For a named pipe to be useful, somebody has to read it and somebody has to write it. Usually this will be 2 separate programs. Your program is the writer. Where is the reader?
If there is no reader, it is normal for the program to block on the O_WRONLY open. So when your program appears to be doing nothing, it's really just doing this:
pipe = open("/tmp/myfifo", O_WRONLY);
which waits for a reader to show up.
In another terminal, run cat /tmp/myfifo. The presence of a reader will allow the writer to make progress. Your program will wake up and move on to the Enter line prompt, and what you enter will be read by the cat and written to the second terminal.
The other problem is an inconsistency in your filenames. In one place you wrote "tmp/myfifo" without a leading slash, so you are trying to create the named pipe in a tmp directory that is inside the current working directory. If that tmp directory doesn't exist, No such file or directory will be the result.

Setting Immutable Flag using ioctl() in C

I have attempted to make a script that creates a file and then sets it as immutable similar to the chattr +i command for linux. The script compiles (with gcc), runs and the file is created. However the file itself is not immutable and can be removed with a simple rm -f. I have attempted to stacktrace where chattr is called and I found a function called ioctl. I then used what little information I could gather and came up with what I have below. I narrowed it down from ext2_fs.h but it just doesn't seem to work. I've clearly overlooked something.
Updates to previous entry: Compiles but returns -1 on ioctl() function. Bad address shown with perror().
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
int main()
{
FILE *fp;
char shovel[16] = "I have a shovel!";
fp = fopen("/shovel.txt", "w+");
fwrite(shovel, sizeof(shovel[0]), sizeof(shovel)/sizeof(shovel[0]), fp);
ioctl(fileno(fp), FS_IOC_SETFLAGS, 0x00000010);
fclose(fp);
}
Any help appreciated.
You are using the right ioctl command, but you're passing it the wrong arguments.
The manpage for ioctl_list(2) shows that FS_IOC_SETFLAGS expects to receive a pointer to int (an int *), yet you're passing it an integer literal (hence the Bad Address error).
The fact that you don't to any error checking whatsoever is also not helping.
The correct flag to pass to FS_IOC_SETFLAGS is a pointer holding the value EXT2_IMMUTABLE_FL, which is defined in ext2fs/ext2_fs.h (some older / different Linux distributions seem to have it under linux/ext2_fs.h), so you'll need to #include <ext2fs/etx2_fs.h>. Make sure to install e2fslibs-dev (and probably you'll need linux-headers too).
This code is working:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <ext2fs/ext2_fs.h>
int main()
{
FILE *fp;
char shovel[16] = "I have a shovel!";
if ((fp = fopen("shovel.txt", "w+")) == NULL) {
perror("fopen(3) error");
exit(EXIT_FAILURE);
}
fwrite(shovel, sizeof(shovel[0]), sizeof(shovel)/sizeof(shovel[0]), fp);
int val = EXT2_IMMUTABLE_FL;
if (ioctl(fileno(fp), FS_IOC_SETFLAGS, &val) < 0)
perror("ioctl(2) error");
fclose(fp);
return 0;
}
Remember to run this as root.
UPDATE:
As Giuseppe Guerrini suggests in his answer, you might want to use FS_IMMUTABLE_FL instead, and you won't need to include ext2_fs.h:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
int main()
{
FILE *fp;
char shovel[16] = "I have a shovel!";
if ((fp = fopen("shovel.txt", "w+")) == NULL) {
perror("fopen(3) error");
exit(EXIT_FAILURE);
}
fwrite(shovel, sizeof(shovel[0]), sizeof(shovel)/sizeof(shovel[0]), fp);
int val = FS_IMMUTABLE_FL;
if (ioctl(fileno(fp), FS_IOC_SETFLAGS, &val) < 0)
perror("ioctl(2) error");
fclose(fp);
return 0;
}
The main problem is that the ioctl wants a pointer to the mask, not a direct constant. You have to define a int variable, store the mask (0x10) in it and pass its address as third argument of ioctl.
Also, I'd add some hints:
other programs to change attributes are used to use low-level I/O directly (open, close...). Also, the file is usually opened with O_RDONLY.
Use FS_IMMUTABLE_FL istead the raw constant.
Get the current attribute mask first (FS_IOC_SETFLAGS) and mask it with the new flag, so other settings are not lost by the service.

Can't open 2 FILE *

I was working on a program in C for Raspberry PI development, and I've been getting this weird bug.
I honestly have no clue regarding its origins. The program is very simple so far.
#include <bcm2835.h>
#include <time.h>
#include <sys/time.h>
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
int main(int argc, char *argv[])
{
FILE *file;
FILE *file2;
FILE *peak1;
FILE *peak2;
file = fopen("input0.txt", "a+");
file2 = fopen("input1.txt", "a+");
peak1=fopen("peak1.txt", "a+");
peak2=fopen("peak2.txt", "a+");
fprintf(file, "%s\n", "HELLO!");
fprintf(peak1, "%s\n", "HELLO!");
}
Bug:-
When I run the program and check the outputs to the files, Only 'input0.txt' has "HELLO!" written where as 'peak1.txt' has nothing.
I can write to the first two files file and file2, but cannot write to the second two files peak1 and peak2.
I have tried writing multiple things but to no avail. What could be the problem?
Thanks!
You forgot to call fclose(FILE *) at the end. Calling int fclose(FILE *fp); will ensure the file descriptor is properly disposed of and output buffers flushed so the data written to the file will be present in the file on disk.
From: IEEE Std 1003.1, 2004 Edition:
int fclose(FILE *stream);
The fclose() function shall cause the stream pointed to by stream to
be flushed and the associated file to be closed. Any unwritten
buffered data for the stream shall be written to the file; any unread
buffered data shall be discarded. Whether or not the call succeeds,
the stream shall be disassociated from the file and any buffer set by
the setbuf() or setvbuf() function shall be disassociated from the
stream. If the associated buffer was automatically allocated, it shall
be deallocated.
You need to call fclose(FILE *) at the end of your code.
The C library function int fclose(FILE *stream) closes the stream. All buffers are flushed.

Unix fifo client to server

I want to use a pair of Unix FIFOs in such manner that:
a client sends to a server a file name and
the server returns to the client: the number of words, lines and bytes from the given file.
Could you please help?
client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int nr,s2c,c2s,c,d,e;
char a[20];
c2s=open("fifo1",O_WRONLY);
s2c=open("fifo2",O_RDONLY);
printf("give file name \n");
scanf("%s",a);
nr=strlen(a);
write(c2s,&nr,sizeof(int));
write(c2s,&a,sizeof(nr));
read(s2c,&c,sizeof(int));
read(s2c,&d,sizeof(int));
read(s2c,&e,sizeof(int));
close(c2s);
close(s2c);
return 0;
}
server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int nr,s2c,c2s,c,d,e;
char a[20];
FILE* f;
c2s=open("fifo1",O_RDONLY);
s2c=open("fifo2",O_WRONLY);
read(c2s,&nr,sizeof(int));
read(c2s,&a,sizeof(nr));
f=fopen(a,"r");
if(fork()==0)
{
printf("result is: \n");
execl("/usr/bin/wc","wc",c,d,e,NULL);
}
wait(0);
write(s2c,&c,sizeof(int));
write(s2c,&d,sizeof(int));
write(s2c,&e,sizeof(int));
close(c2s);
close(s2c);
printf("\n FINISH \n");
return 0;
}
I have done some improvements but still it doesn't work properly.
In the fork'ed part of the server, redirect the standard input and output of wc with
dup2(c2s, STDIN_FILENO);
dup2(s2c, STDOUT_FILENO);
Then exec it with
execl("/usr/bin/wc", "wc", NULL);
Don't pass the file descriptors as arguments to execl. It expects strings (char const*), not int.
See dup2 in the POSIX standard to understand how this works.
Note that wc writes strings of characters to its output. You are trying to read them as if they are binary numbers. This will lead to confusion - especially as you do not check that the read calls worked correctly.
Actually, general comment - you should check many more of your system calls.
You also have to ensure that your processes do not block when opening the FIFOs. You should be OK; you have the processes open 'fifo1' for reading and writing, and then 'fifo2'. I think that forces a correct order on things.
You only write 4-letter file names correctly on the pipe.

Resources