libevent: raise event on file change - c

I have the following code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <event.h>
void fd_cb(int fd,short event,void *arg){
printf("changed\n");
}
int main(int argc, const char* argv[]){
struct event eoh_ev;
FILE *fp;
int fd;
fp=fopen("/var/log/syslog","rw");
fd=fileno(fp);
event_init();
event_set(&eoh_ev,fd,EV_READ|EV_WRITE,fd_cb,NULL);
event_add(&eoh_ev,NULL);
event_dispatch();
return 0;
}
As you can see, I'm trying to call fd_cb(...) when something is written to /var/log/syslog.
The problem is, "changed" never gets printed!
I'm running the code as root.
Many thanks in advance,

Libevent is designed to work on the same file descriptors that poll or select support. Those system calls are not designed to check for file change events. They are designed to return when a file descriptor can be read or written without blocking, something that isn't very meaningful for regular files (reads and writes to regular files either never block or can always block, depending on how you look at it). In other words - libevent on file descriptors other than sockets, pipes and fifo:s will not work.
There are other mechanisms for checking if a file has changed, but those are not portable.

Related

Error in the systemcalls C11 for do file i/o

Hi I'm on Linux WSL Debian and I've the following code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char *argv[]) {
int fd = open("file.dat", O_RDONLY | O_WRONLY);
write(fd, "ciao", strlen("ciao"));
close(fd);
}
Why don't works?
Many bugs:
Must include unistd.h for the declaration of write.
O_RDONLY and O_WRONLY (and O_RDWR) are mutually exclusive. If you want to open a file for read and write you have to use O_RDWR. Since you are not actually reading from the file, you should use O_WRONLY alone.
It is very rare to want to use O_WRONLY without O_CREAT and one (exactly one; these three are also mutually exclusive) of O_APPEND, O_EXCL, and O_TRUNC. Assuming that you do want O_CREAT, you must also supply a third argument, which should be the octal constant 0666 unless you have a specific reason to use some other value.
write(fd, "ciao", strlen("ciao")) should be write(fd, "ciao\n", strlen("ciao\n")) unless you have a specific reason to be creating a text file with an incomplete last line.
Because you are writing to a file, you need to check for errors on all three of the open, write, and close calls. (The fact that close can fail is a bug in its specification, but one that we are permanently stuck with. It's safe to ignore errors in close for files that were opened for reading, but not writing.)
Also some style corrections:
The code as shown only needs unistd.h, fcntl.h, and string.h; it should also be including stdio.h, because it should also be making calls to perror. None of the other headers should be included (unless this is cut down from a much larger program).
Declare main as int main(void) unless you are actually going to use argc and argv.
Don't use the C99 license to fall off the end of main; the last line of main should be return 0;.
For historical reasons, in C, the opening curly brace of a function definition should always be placed on a line by itself, even if all other opening curly braces are "cuddled" with their parent control flow construct.
You need the header <unistd.h> to get the declarations of write() and close(). The only other header you need is <fcntl.h> for open().
I've also kept <stdio.h> so I can use perror() if open() fails.
Since you're only writing to the file, you don't need O_RDONLY in the open modes. If you want to read and write, you should use O_RDWR instead. These three flags are mutually exclusive.
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
int fd=open("file.dat",O_WRONLY);
if (fd < 0) {
perror("open failed");
return 1;
}
write(fd,"ciao",strlen("ciao"));
close(fd);
}
Note that this will not create the file if it doesn't already exist. If you want that, you need to add the O_CREAT flag and another argument with the permissions that should be given to the file if it's created.

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.

What's wrong with my code? (Printing File names inside an Archive File)

I'm trying to write a program to open an archive file from Unix, read the files in and print what files are inside the archive and just the filename. Below is my code -- it compiles, but I got some weird output in the terminal -- e.g. ?;?U?. It should just display 2 txt file names. Can someone take a look at my code and give me some guidance on what I'm missing? Thank you!
EDIT: I made some changes to my code, but it's still not working. Any suggestion or help is greatly appreciated.
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/utsname.h>
#include <ctype.h>
#include <string.h>
#include <ar.h>
int main (int argc, char **argv)
{
int input_fd;
//char buffer[25];
struct ar_hdr my_ar;
if (argc != 2) {
printf("Error", argv[0]);
exit(EXIT_FAILURE);
}
//open the archive file (e.g., hw.a)
input_fd = open(argv[1], O_RDONLY);
if (input_fd == -1)
{
perror("Can't open input file\n");
exit(-1);
}
while (read(input_fd, my_ar.ar_name, sizeof(buffer)) > 0) {
printf("%s\n", my_ar.ar_name);
}
close(input_fd);
return 0;
}
I don't see anywhere that you are defining what a my_hdr is, yet you are printing one of its supposed members here:
printf("%s\n", my_ar.ar_name);
You also never set ar_name to anything either.
I see you read some data into a variable named buffer, but you never actually copy that buffer into ar_name which I am assuming is your intent.
while (read(input_fd, buffer, sizeof(buffer)) > 0) {
printf("%s\n", my_ar.ar_name);
}
The POSIX documentation explicitly states that the archive file format is not described, on the grounds that several incompatible such formats already exist, so you'll definitely need to study the documentation provided by your OS if you're going to use <ar.h> instead of the ar command-line utility.
Here are some details that might help, based on the Solaris implementation:
An archive file starts with a magic cookie string which you are neither verifying nor skipping,
Each header object is followed by the content of the actual object in the archive, which you are not stepping over,
The first object in the archive is an unnamed object containing the archive's symbol table,
If an object's name is more than fifteen bytes long, it will be stored in the archive's string table, which appears to be part of the symbol table.
Note that these points, or details thereof, may vary on your OS. As I said at the beginning, study the documentation provided by your OS.

Resources