I'm writing a C++ program to run on my raspberry pi 3b+ that monitors the temperature of the CPU in real-time.In order to avoid polling, I'm using the sys/inotify library to watch /sys/class/thermal/thermal_zone0/temp for file updates.
However, this doesn't seem to pick up changes to the file.
To test this:
I polled the file repeatedly (using cat) while running main, and I saw that the value in the file did change, but the change was not detected by main.
I tried tail -f /sys/class/thermal/thermal_zone0/temp, but this did not detect any changes either.
I created a script to periodically write to another file, and had my program watch this file, and it detected changes there.
Is it possible that this file is being updated without propagating an event that is detectable by inotify? I am trying to avoid having to implement a periodic polling of this file to monitor for changes at all cost.
temperatureMonitor.cpp
#include <iostream>
#include <unistd.h>
#include <sys/inotify.h>
#include <sys/types.h>
#include "./temperatureMonitor.hpp"
#define EVENT_SIZE ( sizeof (struct inotify_event) )
#define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
namespace Performance {
void TemperatureMonitor::monitor_temperature(void(*callback)(double)){
};
void TemperatureMonitor::monitor_temperature_file(){
int length, i = 0;
int fd;
int wd;
char buffer[BUF_LEN];
fd = inotify_init();
wd = inotify_add_watch(fd,"/sys/class/thermal/thermal_zone0/temp" , IN_MODIFY|IN_CREATE|IN_DELETE);
while (true){
length = read(fd, buffer, BUF_LEN);
std::cout << "detected file change\n";
};
};
};
main.cpp
#include "Performance/temperatureMonitor.hpp"
#define EVENT_SIZE ( sizeof (struct inotify_event) )
#define BUF_LEN ( 1024 ∗ ( EVENT_SIZE + 16 ) )
int main(){
Performance::TemperatureMonitor tm = Performance::TemperatureMonitor();
tm.monitor_temperature_file();
return 0;
};
fwriter.go
package main
import (
"os"
"time"
)
func main() {
f, err := os.Create("foo.txt")
if err != nil {
panic(err)
}
for {
f.Write([]byte("test\n"))
time.Sleep(time.Second)
}
}
It's not a real file, whenever you read it, the driver is asked to produce the data in it. There is no way to get notified when its contents would change. Polling is the answer.
https://www.kernel.org/doc/html/latest/filesystems/sysfs.html
On read(2), the show() method should fill the entire buffer. Recall
that an attribute should only be exporting one value, or an array of
similar values, so this shouldn’t be that expensive.
This allows userspace to do partial reads and forward seeks
arbitrarily over the entire file at will. If userspace seeks back to
zero or does a pread(2) with an offset of ‘0’ the show() method will
be called again, rearmed, to fill the buffer.
You don't have to open and close it every time, seeking to the beginning should refresh it. Although this will probably not save much.
Related
I have a program whiuch utilizes inotify.
What it does is it start watching the directory for the file being created. When it happens the program reads the content then deletes the file.
Then the user initiates an action which will create the same file again. However, inotify does not see that the file has been created a second time and the file is not processed.
Code is as follows:
fileCreated = false;
m_wd1 = inotify_add_watch( m_fd, "/tmp", IN_CREATE );
if( m_wd1 == -1 )
{
}
else
{
while( true )
{
poll_num = poll( &fds, nfds, -1 );
if( poll_num == -1 )
{
if( errno == EINTR )
continue;
syslog( LOG_ERR, "Fail to run poll" );
result = 1;
}
else if( poll_num > 0 && ( fds.revents & POLLIN ) )
{
syslog( LOG_DEBUG, "Polling is successful" );
for( ;; )
{
len = read( m_fd, buf, sizeof( buf ) );
if( len == -1 && errno != EAGAIN )
{
syslog( LOG_ERR, "Failure to read the inotify event" );
result = 1;
break;
}
for( ptr = buf; ptr < buf + len; ptr += sizeof( struct inotify_event ) + event->len )
{
event = (const struct inotify_event *) ptr;
if( event->mask & IN_CREATE )
{
std::string name( event->name );
if( name == "scan_results" )
{
fileCreated = true;
break;
}
}
}
if( fileCreated || result )
break;
}
}
if( fileCreated )
{
std::ifstream log( "scan_results" );
if( log.rdstate() & std::ifstream::failbit ) != 0 )
{
}
else
{
}
log.close();
if( remove( "scan_results" ) != 0 )
{
syslog( LOG_ERR, "Failed to remove the file" );
}
else
{
syslog( LOG_DEBUG, "File deleted successfully" );
}
}
fileCreated = false;
The while() loop runs only once. When the action happens second time I see a message "Polling is successful".
Should I add IN_MODIFY as a mask for inotify?
If it matters - this code is running inside std::thread.
Turns out that polling plus reading inotify is not thread-safe.
So to overcome this I had to add inotify_init() to every thread. So now every thread have its own inotify file descriptor. And now it looks like program works.
Thank you everybody for reading and trying to help.
Well, I have an answer, it's not a short one and it's directly from the man 7 inotify page in linked above in the comments. Your two pending questions from the narrowing of your questions in the comments are:
if I call remove() on the file - does this mean that the watch in the directory for file creation will be removed?; and
if it is true can you give me some pseudocode for the workaround?
The answer to (1) is Yes, but the watch may be recycled causing the next file/dir to which it is assigned to potentially read the pending requests for the file/dir that was closed/deleted:
When a watch descriptor is removed by calling inotify_rm_watch(2) (or
because a watch file is deleted or the filesystem that contains it is
unmounted), ...
(caveat to be aware of)
... any pending unread events for that watch descriptor
remain available to read. As watch descriptors are subsequently
allocated with inotify_add_watch(2), the kernel cycles through the
range of possible watch descriptors (0 to INT_MAX) incrementally.
When allocating a free watch descriptor, no check is made to see
whether that watch descriptor number has any pending unread events in
the inotify queue. Thus, it can happen that a watch descriptor is
reallocated even when pending unread events exist for a previous
incarnation of that watch descriptor number, with the result that the
application might then read those events and interpret them as
belonging to the file associated with the newly recycled watch
descriptor. In practice, the likelihood of hitting this bug may be
extremely low, since it requires that an application cycle through
INT_MAX watch descriptors, release a watch descriptor while leaving
unread events for that watch descriptor in the queue, and then
recycle that watch descriptor. For this reason, and because there
have been no reports of the bug occurring in real-world applications,
as of Linux 3.15, no kernel changes have yet been made to eliminate
this possible bug.
(I doubt you will ever have that many watches open)
In answer to the second question, the pseudo code would simply be to check the mask for:
IN_DELETE_SELF
Watched file/directory was itself deleted. (This event
also occurs if an object is moved to another filesystem,
since mv(1) in effect copies the file to the other
filesystem and then deletes it from the original filesys‐
tem.) In addition, an IN_IGNORED event will subsequently
be generated for the watch descriptor.
So you can check with either the mask IN_DELETE_SELF or IN_IGNORED if the file/directory being watched is deleted.
I can't see exactly what is wrong with your code since you haven't provided an mcve, but watching a directory should give you all of the subscribed events for the directory until you stop reading them.
Here's an example of using inotify to watch a directory...
#include <iostream>
#include <unistd.h>
#include <sys/inotify.h>
int main(int argc, char *argv[]) {
const int ifd = inotify_init();
const int iwd = inotify_add_watch(ifd, "/tmp/inotify", IN_CREATE);
char buf[4096];
while (read(ifd, buf, sizeof(buf)) > 0) {
const struct inotify_event* ie = (const struct inotify_event*) buf;
std::cout << ie->name << std::endl;
}
return 0;
}
(jason#pi) [4868] ~/tmp touch /tmp/inotify/foo
(jason#pi) [4880] ~/tmp touch /tmp/inotify/bar
(jason#pi) [4881] ~/tmp rm /tmp/inotify/foo
(jason#pi) [4882] ~/tmp touch /tmp/inotify/foo
(jason#pi) [4879] ~/tmp ./so
foo
bar
foo
This is the first time I'm communicating with Arduino using my computer. I use Ubuntu 14.04. This is the C program for writing to the file. The Arduino shows up ttyACM0.
While compiling using gcc the compiler shows an error saying:
Segmentation fault(core dumped)
How do I rectify this error.
#include<unistd.h>
#include<stdio.h>
int main() {
char data[] = {'f','b','r'}; //Random data we want to send
FILE *file;
file = fopen("/dev/ttyACM0","w"); //Opening device file
int i = 0;
for(i = 0 ; i < 3 ; i++) {
fprintf(file,"%c",data[i]); //Writing to the file
fprintf(file,"%c",','); //To separate digits
sleep(1);
}
fclose(file);
}
Pardon my ignorance. I tried researching on it. Couldn't make it work. Thanks in advance for your help.
You're getting a NULL return from the fopen() that NULL is being passed to fprintf() which is expecting a valid FILE* and messing up causing the SEGV.
If you use fopen you should check what it returns so you can give the user a something more useful than "segmentation fault".
The probable cause of the fopen() failure is you don't have permission to play with the serial port.
Normally you need the group dialout to be able to access the serial port.
As root do:
usermod -a -G dialoutyourusername
Then log out and back in so you get the new group.
Consider using minicom or microcom (on any of the several other serial terminal programs) to access the serial port instead of writing your own.
I also suggest you have the Arduino send a hello message when it boots up so you can be sure you have the right baud rate etc...
You did not put any success check on the return value of fopen("/dev/ttyACM0","w");. In case fopen() fails, using file further is undefined behavior, causing segmentation fault. Do something like
file = fopen("/dev/ttyACM0","w"); //Opening device file
if (file)
{
//do something with file
}
else
return 0;
Also, add a return 0 before ending main().
// the following code:
// compiles cleanly
// performs appropriate error checking
// has proper return statement
#include <unistd.h> // sleep()
#include <stdio.h> // fopen(), fclose(), fprintf(), perror()
#include <stdlib.h> // exit() and EXIT_FAILURE
int main()
{
char data[] = {'f','b','r'}; //Random data we want to send
FILE *file;
if( NULL == (file = fopen("/dev/ttyACM0","w") ) ) //Opening device file
{ // then fopen failed
perror("fopen failed for ttyACM0" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
int i = 0;
for(i = 0 ; i < 3 ; i++)
{
if( 0 >= fprintf(file,"%c",data[i]) ) //Writing to the file
{ // fprintf failed
perror("fprintf data failed" );
exit( EXIT_FAILURE );
}
// implied else, fprintf successful for data
if( 0 >= fprintf(file,"%c",',') ) //To separate digits
{ // then, fprintf failed
perror( "fprintf for comma failed");
exit( EXIT_FAILURE );
}
// implied else, fprintf successful for comma
sleep(1);
} // end for
fclose(file);
return(0);
} // end function: main
On failure fopen returns NULL, so you are potentially dereferencing a NULL pointer, the correct way of doing that, is checking the result of fopen. I would however suggest low level IO for this kind of thing something like
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
int main()
{
char data[] = {'f','b','r'}; //Random data we want to send
int fd;
int i;
fd = open("/dev/ttyACM0", O_WRONLY); //Opening device file
if (fd == -1)
{
perror("cannot open /dev/ttyACM0");
return -1;
}
for(i = 0 ; i < 3 ; i++)
{
write(fd, &(data[i]), 1);
write(fd, ",", 1);
sleep(1);
}
close(fd);
return 0;
}
on error open returns a special value -1 so you should abort writing to it.
I'm pretty sure in your case there will be a permission denied error, since normally the /dev/tty* belong to group dialout and they have group write permission by default, but since probably your user doesn't belong to that group you don't have write access to /dev/ttyACM0.
I am writing a program where one process A reads the data appended to file by another process B.I am using ReadDirectoryChangesW for the notification.The problem is that the notification is not being generated until I close the handle in B although I am flushing contents to file using fflush.The code is a given below
File Writer:
int _tmain(int argc, _TCHAR* argv[])
{
FILE *fp;
fp=_fsopen("log.txt", "a", _SH_DENYNO);
char str[4096];
for(int i=1;i<4096;i++)
str[i]=i;
while(true){
fwrite(str,1,4096,fp);
fflush(fp);
Sleep(2000);
}
return 0;
}
File Reader:
#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
#include <conio.h>
#include <assert.h>
#include <share.h>
void _tmain(int argc, TCHAR *argv[])
{
FILE *fp;
fp=_fsopen("C:\\Users\\dell\\Documents\\Visual Studio 2012\\Projects\\FileWriter\\FileWriter\\log.txt", "r", _SH_DENYNO);
int last_size=0,new_size=0;
if(fp==NULL)
return ;
HANDLE m_hMonitoredDir = CreateFile(TEXT("C:\\Users\\dell\\Documents\\Visual Studio 2012\\Projects\\FileWriter\\FileWriter"), FILE_LIST_DIRECTORY,
FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, NULL );
if ( m_hMonitoredDir == INVALID_HANDLE_VALUE )
{
DWORD dwErr = GetLastError();
printf("error");
return;
}
char szBuf[ MAX_PATH ];
DWORD dwBytesRead = 0;
int flag=0;
char *buffer;
while ( ReadDirectoryChangesW( m_hMonitoredDir, szBuf, MAX_PATH, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE,&dwBytesRead, NULL, NULL ))
{
PFILE_NOTIFY_INFORMATION pstFileNotif = (PFILE_NOTIFY_INFORMATION)( szBuf );
if ( pstFileNotif->Action == FILE_ACTION_MODIFIED )
{
char szNotifFilename[ MAX_PATH ] = { 0 };
if ( int iNotifFilenameLen = WideCharToMultiByte( CP_OEMCP, NULL,
pstFileNotif->FileName,
pstFileNotif->FileNameLength / sizeof( WCHAR ),
szNotifFilename, sizeof( szNotifFilename ) / sizeof( char ),
NULL, NULL ) )
{
if ( strcmp("log.txt", szNotifFilename ) == 0 )
{
fseek(fp, 0, SEEK_END);
new_size = ftell(fp);
fseek(fp,last_size,SEEK_SET);
int size=new_size-last_size;
buffer=new char[size+1];
fread(buffer,1,size,fp);
buffer[size]='\0';
printf("%s",buffer);
free(buffer);
}
}
}
}
}
Can anyone help me get notifications as soon as I use fflush in B ?
I don't think this is possible. According to the documentation on FILE_NOTIFY_CHANGE_LAST_WRITE (emphasis mine):
Any change to the last write-time of files in the watched directory or subtree causes a change notification wait operation to return. The operating system detects a change to the last write-time only when the file is written to the disk. For operating systems that use extensive caching, detection occurs only when the cache is sufficiently flushed.
fflush() ensures that the file data is passed back to the operating system, but it does not guarantee that the data gets written to the disk, since typically a lot of caching is involved:
Buffers are normally maintained by the operating system, which determines the optimal time to write the data automatically to disk: when a buffer is full, when a stream is closed, or when a program terminates normally without closing the stream. The commit-to-disk feature of the run-time library lets you ensure that critical data is written directly to disk rather than to the operating-system buffers.
As others have said in the comments, you may be better of using named pipes for your goals, since you're only dealing with a single known file.
You can force a commit-to-disk by calling _flushall ( http://msdn.microsoft.com/en-us/library/s9xk9ehd.aspx )
or see this article ( http://support.microsoft.com/kb/148505 ) on how to force a commit-to-disk. You need to link with commode.obj to force fflush to commit-to-disk automatically.
The alternative might be to fclose the file each time, and reopen the file in append mode, if you are only doing it every 2 seconds (the overhead is small).
See here: https://jeffpar.github.io/kbarchive/kb/066/Q66052/
For us, linking with commode.obj didn't work.
However, this approach did:
When we opened our file using fopen, we included the "c" mode option as the LAST OPTION:
fopen( path, "wc") // w - write mode, c - allow immediate commit to disk
Then when you want to force a flush to disk, call
_flushall()
We made this call before calling
fclose()
We experienced the exact issue you described and this approach fixed it.
From that above site:
"Microsoft C/C++ version 7.0 introduces the "c" mode option for the fopen()
function. When an application opens a file and specifies the "c" mode, the
run-time library writes the contents of the file buffer to disk when the
application calls the fflush() or _flushall() function. "
Using inotify to monitor a directory for any new file created in the directory by adding a watch on the directory by
fd = inotify_init();
wd = inotify_add_watch(fd, "filename_with_path", IN_CLOSE_WRITE);
inotify_add_watch(fd, directory_name, IN_CLOSE_WRITE);
const int event_size = sizeof(struct inotify_event);
const int buf_len = 1024 * (event_size + FILENAME_MAX);
while(true) {
char buf[buf_len];
int no_of_events, count = 0;
no_of_events = read(fd, buf, buf_len);
while(count < no_of_events) {
struct inotify_event *event = (struct inotify_event *) &buf[count];
if (event->len) {
if (event->mask & IN_CLOSE_WRITE) {
if (!(event->mask & IN_ISDIR)) {
//It's here multiple times
}
}
}
count += event_size + event->len;
}
When I scp a file to the directory, this loops infinitely. What is the problem with this code ? It shows the same event name and event mask too. So , it shows that the event for the same, infinite times.
There are no break statements. If I find an event, I just print it and carry on waiting for another event on read(), which should be a blocking call. Instead, it starts looping infinitely. This means, read doesn't block it but returns the same value for one file infinitely.
This entire operation runs on a separate boost::thread.
EDIT:
Sorry all. The error I was getting was not because of the inotify but because of sqlite which was tricky to detect at first. I think I jumped the gun here. With further investigation, I did find that the inotify works perfectly well. But the error actually came from the sqlite command : ATTACH
That command was not a ready-only command as it was supposed to. It was writing some meta data to the file. So inotify gets notification again and again. Since they were happening so fast, it screwed up the application.I finally had to breakup the code to understand why.
Thanks everyone.
I don't see anything wrong with your code...I'm running basically the same thing and it's working fine. I'm wondering if there's a problem with the test, or some part of the code that's omitted. If you don't mind, let's see if we can remove any ambiguity.
Can you try this out (I know it's almost the same thing, but just humor me) and let me know the results of the exact test?
1) Put the following code into test.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/inotify.h>
int main (int argc, char *argv[])
{
char target[FILENAME_MAX];
int result;
int fd;
int wd; /* watch descriptor */
const int event_size = sizeof(struct inotify_event);
const int buf_len = 1024 * (event_size + FILENAME_MAX);
strcpy (target, ".");
fd = inotify_init();
if (fd < 0) {
printf ("Error: %s\n", strerror(errno));
return 1;
}
wd = inotify_add_watch (fd, target, IN_CLOSE_WRITE);
if (wd < 0) {
printf ("Error: %s\n", strerror(errno));
return 1;
}
while (1) {
char buff[buf_len];
int no_of_events, count = 0;
no_of_events = read (fd, buff, buf_len);
while (count < no_of_events) {
struct inotify_event *event = (struct inotify_event *)&buff[count];
if (event->len){
if (event->mask & IN_CLOSE_WRITE)
if(!(event->mask & IN_ISDIR)){
printf("%s opened for writing was closed\n", target);
fflush(stdout);
}
}
count += event_size + event->len;
}
}
return 0;
}
2) Compile it with gcc:
gcc test.c
3) kick it off in one window:
./a.out
4) in a second window from the same directory try this:
echo "hi" > blah.txt
Let me know if that works correctly to show output every time the file is written to and does not loop as your code does. If so, there's something important your omiting from your code. If not, then there's some difference in the systems.
Sorry for putting this in the "answer" section, but too much for a comment.
My guess is that read is returning -1 and since you dont ever try to fix the error, you get another error on the next call to read which also returns -1.
I want to monitor a folder.
Every time a notification pops up i want to run a system command line.
Problems when using system command. Each new event pops up 3 times though it should pop up one time.
EDIT:
Thx for you replays. I found the bug. The system executed a folder that was inside the monitored foder. this is why each time i dropped a foder in the monitored folder, the event was printed 3 times.
code-----------
/*
Simple example for inotify in Linux.
*/
#include <sys/inotify.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
int fd,wd,wd1,i=0,len=0;
char pathname[100],buf[1024];
struct inotify_event *event;
fd=inotify_init1(IN_NONBLOCK);
/* watch /test directory for any activity and report it back to me */
`wd=inotify_add_watch(fd,"/home/folder",IN_ALL_EVENTS`);
while(1){
//read 1024 bytes of events from fd into buf
i=0;
len=read(fd,buf,1024);
while(i<len){
event=(struct inotify_event *) &buf[i];
/* check for changes */
if(event->mask & IN_OPEN)
{ // printf("\n %s :was opened\n",event->name);
char*path="/home/folder/";
char*file=event->name;
int n=sizeof(path)+sizeof(file);
char *result=(char *)malloc(512);
strcpy(result,path); // copy string one into the result.
strcat(result,file); // append string two to the result
puts (result);
//printf("RESUULT:");
int pp=sizeof(result);
char *run="/home/test/./userr ";
int l=sizeof(run);
char *cmd=(char *)malloc(1000);
strcpy(cmd,run);
strcat(cmd,result);
puts (cmd);
system(cmd);
printf("\n %s :was opened\n",event->name);
//break;
}
if(event->mask & IN_MODIFY)
printf("%s : modified\n",event->name);
if(event->mask & IN_ATTRIB)
printf("%s :meta data changed\n",event->name);
if(event->mask & IN_ACCESS)
printf("%s :was read\n",event->name);
if(event->mask & IN_CLOSE_WRITE)
printf("%s :file opened for writing was closed\n",event->name);
// if(event->mask & IN_DELETE)
// printf("%s :deleted\n",event->name);
/* update index to start of next event */
i+=sizeof(struct inotify_event)+event->len;
}
}
}
EDIT:
HOW CAN I PUT TO SLEEP THE WHILE(1) FOR 1 MINUTE?
SPEEP(60); pops the inotify void:was opened instead of folder1:was opened when i dropp a foder in the monitored folder
./exec
:was opened
/home/folder/
:file opened not for writing was closed
Without sleep inside while (the code posted) i have:
1002_copy :was opened
/home/folder/1002_copy
1002_copy :file opened not for writing was closed
You are running system() in the if condition which is called every time a open file is seen.
So, it will run infinitely whenever you open a file. You have to find a way to get out of the loop.
When execution reaches the line break, it breaks out of the inner while but what about the outer while(1)?
AS REQUESTED (working code):
#include <sys/inotify.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
int fd,wd,wd1,i=0,len=0;
char pathname[100],buf[1024];
struct inotify_event *event;
fd=inotify_init1(IN_NONBLOCK);
wd=inotify_add_watch(fd,"/tmp/temp",IN_ALL_EVENTS);
while(1){
//read 1024 bytes of events from fd into buf
i=0;
len=read(fd,buf,1024);
while(i<len){
event=(struct inotify_event *) &buf[i];
/* check for changes */
if(event->mask & IN_CREATE){
system("/bin/date");
}
i+=sizeof(struct inotify_event)+event->len;
}
}
}
The above code executes $date command each time a file is added to /tmp/temp. Modify it to suit your needs.
What is your system command doing? Is it opening the file you're changing? If so, wouldn't that cause your code to run again and put you into an infinite loop (which is apparently what you're seeing)?
Edit - There's an article in Linux Journal which is doing something very similar to you. I noticed they're using a larger buffer and the comments mention something about structure alignment so is it possible you're getting a bad event length which is causing a buffer overrun and forcing your code to loop for longer than expected.
Ether way, I'd check (with a bit of printf debug logic) that you're looping as expected.