inotify watcher stops working after ls or watch commands - c++

I took the code below from the many examples on the internet about how to use inotify.
I then tried the following experiment:
1) run the watcher below
2) in a separate shell, cd into '/mypath' create some files to the folder you are watching. For example, 'date > output.txt' one ore more times.
3) you will see notifications from the watcher.
4) type 'ls /mypath' (or even 'watch -n 1 /mypath')
5) try 'date > output.txt' in /mypath. You will no longer see notifications from the watcher. Or at least, this is what happened when I tested with Ubuntu 12/13.
Any ideas about how to fix it?
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <limits.h>
#include <unistd.h>
#define MAX_EVENTS 1024 /*Max. number of events to process at one go*/
#define LEN_NAME 16 /*Assuming that the length of the filename won't exceed 16 bytes*/
#define EVENT_SIZE ( sizeof (struct inotify_event) ) /*size of one event*/
#define BUF_LEN ( MAX_EVENTS * ( EVENT_SIZE + LEN_NAME )) /*buffer to store the data of events*/
int main()
{
int length, i = 0, wd;
int fd;
char buffer[BUF_LEN];
/* Initialize Inotify*/
fd = inotify_init();
if ( fd < 0 ) {
perror( "Couldn't initialize inotify");
}
/* add watch to starting directory */
wd = inotify_add_watch(fd, "/mypath", IN_CLOSE_WRITE | IN_CLOSE_NOWRITE);
if (wd == -1)
{
printf("Couldn't add watch to %s\n","/mypath");
}
else
{
printf("Watching:: %s\n","/mypath");
}
/* do it forever*/
while(1)
{
i = 0;
length = read( fd, buffer, BUF_LEN );
if ( length < 0 ) {
perror( "read" );
}
while ( i < length ) {
struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];
if ( event->len ) {
if ( event->mask & IN_CLOSE_WRITE) {
if (event->mask & IN_ISDIR)
printf( "The directory %s was Created.\n", event->name );
else
printf( "The file %s was closed (write) with WD %d\n", event->name, event->wd );
}
if ( event->mask & IN_CLOSE_NOWRITE) {
if (event->mask & IN_ISDIR)
printf( "The directory %s was Created.\n", event->name );
else
printf( "The file %s was closed (nowrite) with WD %d\n", event->name, event->wd );
}
i += EVENT_SIZE + event->len;
}
}
}
/* Clean up*/
inotify_rm_watch( fd, wd );
close( fd );
return 0;
}

You should not put i += EVENT_SIZE + event->len; inside the if ( event->len ) block. If an event has a zero-length name, then the pointer should still be incremented by EVENT_SIZE (which is what will happen if you put that statement outside the block). I think you might be seeing an infinite loop in your inotify program, kicked off by the first event which happens to have a zero-length name. (Which is exactly what happens with the ls: The directory is being opened, not its files, so there's nothing in the name field.)

You get into an ever-ending loop since you do not change i when event->len == 0
Add this:
else
i += EVENT_SIZE ;
in case if ( event->len == 0 )

It turns out that when your program stops working it eats all the CPU. I made a few changes and it seems to work now. Here are the details:
Declare BUF_LEN to handle 16 events (you can increase that value):
#define BUF_LEN (16 * (sizeof(struct inotify_event) + NAME_MAX + 1))
Change your while (i < length) loop that processes the events to the following for loop:
for ( p = buffer; p < buffer + length; ) {
struct inotify_event *event = ( struct inotify_event * ) p;
p += sizeof(struct inotify_event) + event->len;
if ( event->len ) {
/* SNIP */
}
}
The p variable should be declared as char * and you can remove the i which is not used anymore.

Related

C++ program opens file corectly on Linux but not on Windows

I compiled a Linux program on Windows via Mingw but the output is wrong.
Error description:
The output of the program looks different on Windows than on Linux. This is how it looks on Windows:
>tig_2
CAATCTTCAGAGTCCAGAGTGGGAGGCACAGACTACAGAAAATGAGCAGCGGGGCTGGTA
>cluster_1001_conTTGGTGAAGAGAATTTGGACATGGATGAAGGCTTGGGCTTGACCATGCGAAGG
Expected output:
>cluster_1001_contig2
CAATCTTCAGAGTCCAGAGTGGGAGGCACAGACTACAGAAAATGAGCAGCGGGGCTGGTA
>cluster_1001_contig1
TTGGTGAAGAGAATTTGGACATGGATGAAGGCTTGGGCTTGACCATGCGAAGG
(Note: the output is very large to paste it here so the examples above are pseudo-real).
Possible cause:
I have observed that if I convert the enter characters the input file from Linux (LF) to Windows (CRLF) it almost works: the first character (>) in file is missing. The same code works perfectly on Linux without any enter conversion. So, the problem must be in the function that is parsing the input not in the one that writes the output:
seq_db.Read( db_in.c_str(), options );
Source code:
This is the piece that is parsing the input file. Anyway, I might me wrong. The fault might be in other place. In case it is needed, the FULL source code is here :)
void SequenceDB::Read( const char *file, const Options & options )
{
Sequence one;
Sequence dummy;
Sequence des;
Sequence *last = NULL;
FILE *swap = NULL;
FILE *fin = fopen( file, "r" );
char *buffer = NULL;
char *res = NULL;
size_t swap_size = 0;
int option_l = options.min_length;
if( fin == NULL ) bomb_error( "Failed to open the database file" );
if( options.store_disk ) swap = OpenTempFile( temp_dir );
Clear();
dummy.swap = swap;
buffer = new char[ MAX_LINE_SIZE+1 ];
while (not feof( fin ) || one.size) { /* do not break when the last sequence is not handled */
buffer[0] = '>';
if ( (res=fgets( buffer, MAX_LINE_SIZE, fin )) == NULL && one.size == 0) break;
if( buffer[0] == '+' ){
int len = strlen( buffer );
int len2 = len;
while( len2 && buffer[len2-1] != '\n' ){
if ( (res=fgets( buffer, MAX_LINE_SIZE, fin )) == NULL ) break;
len2 = strlen( buffer );
len += len2;
}
one.des_length2 = len;
dummy.des_length2 = len;
fseek( fin, one.size, SEEK_CUR );
}else if (buffer[0] == '>' || buffer[0] == '#' || (res==NULL && one.size)) {
if ( one.size ) { // write previous record
one.dat_length = dummy.dat_length = one.size;
if( one.identifier == NULL || one.Format() ){
printf( "Warning: from file \"%s\",\n", file );
printf( "Discarding invalid sequence or sequence without identifier and description!\n\n" );
if( one.identifier ) printf( "%s\n", one.identifier );
printf( "%s\n", one.data );
one.size = 0;
}
one.index = dummy.index = sequences.size();
if( one.size > option_l ) {
if ( swap ) {
swap_size += one.size;
// so that size of file < MAX_BIN_SWAP about 2GB
if ( swap_size >= MAX_BIN_SWAP) {
dummy.swap = swap = OpenTempFile( temp_dir );
swap_size = one.size;
}
dummy.size = one.size;
dummy.offset = ftell( swap );
dummy.des_length = one.des_length;
sequences.Append( new Sequence( dummy ) );
one.ConvertBases();
fwrite( one.data, 1, one.size, swap );
}else{
//printf( "==================\n" );
sequences.Append( new Sequence( one ) );
//printf( "------------------\n" );
//if( sequences.size() > 10 ) break;
}
//if( sequences.size() >= 10000 ) break;
}
}
one.size = 0;
one.des_length2 = 0;
int len = strlen( buffer );
int len2 = len;
des.size = 0;
des += buffer;
while( len2 && buffer[len2-1] != '\n' ){
if ( (res=fgets( buffer, MAX_LINE_SIZE, fin )) == NULL ) break;
des += buffer;
len2 = strlen( buffer );
len += len2;
}
size_t offset = ftell( fin );
one.des_begin = dummy.des_begin = offset - len;
one.des_length = dummy.des_length = len;
int i = 0;
if( des.data[i] == '>' || des.data[i] == '#' || des.data[i] == '+' ) i += 1;
if( des.data[i] == ' ' or des.data[i] == '\t' ) i += 1;
if( options.des_len and options.des_len < des.size ) des.size = options.des_len;
while( i < des.size and ( des.data[i] != '\n') ) i += 1;
des.data[i] = 0;
one.identifier = dummy.identifier = des.data;
} else {
one += buffer;
}
}
#if 0
int i, n = 0;
for(i=0; i<sequences.size(); i++) n += sequences[i].bufsize + 4;
cout<<n<<"\t"<<sequences.capacity() * sizeof(Sequence)<<endl;
int i;
scanf( "%i", & i );
#endif
one.identifier = dummy.identifier = NULL;
delete[] buffer;
fclose( fin );
}
The format of the input file is like this:
> comment
ACGTACGTACGTACGTACGTACGTACGTACGT
> comment
ACGTACGTACGTACGTACGTACGTACGTACGT
> comment
ACGTACGTACGTACGTACGTACGTACGTACGT
etc
The issue is more than likely you need to open the file using the "rb" switch in the call to fopen. The "rb" opens the file in binary mode, as opposed to "r", which opens a file in "text" mode.
Since you're going back and forth between Linux and Windows, the end-of-line characters will be different. If you open the file as "text" in Windows, but the file was formatted for Linux, you're lying to Windows that it is a text file. So the runtime will do CR/LF conversion all wrong.
Therefore you should open the file as binary, "rb" so that the CR/LF translation isn't done.

How to get changed content of inotify in C++?

I want to get the changed content of a textfile. Detecting file change using inotify in C++ works fine, but is there a way to get the changed content of a text file? The problem is that the event of inotify is consumed before the content is written so ifstream gives the previous output.
I am using inotify as follows (simplified):
fd = inotify_init();
wd = inotify_add_watch(fd, "myDir", IN_MODIFY);
while(1)
{
i = 0;
length = read( fd, buffer, BUF_LEN );
while ( i < length ) {
struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];
if ( event->len && (event->mask & IN_MODIFY) && !(event->mask & IN_ISDIR)) {
getNewContent();//-->if I use ifstream herre, I get the previous content
cout<<event->name<<", WD"<<event->wd<<endl;
i += EVENT_SIZE + event->len;
}
}
}
void getNewContent(){
string temp;
ifstream in("myDir/test.txt");
while(getline(in, temp,';')){
cout<<temp.c_str()<<endl;
}
in.close();
}

How to obtain (almost) unique system identifier in a cross platform way?

I'm looking for a way to get a number which will almost surely change when running the code on different machines and almost surely stay the same between two runs on the same machine.
If I were doing this as a shell script in Linux, I would use something like this:
{ uname -n ; cat /proc/meminfo | head -n1 ; cat /proc/cpuinfo ; } | md5sum
But I need this in C++ (with boost) and at least on Windows, Linux and Mac.
To generate a mostly unique machine id, you can get a few serial numbers from various pieces of hardware on the system. Most processors will have a CPU serial number, the hard disks each have a number, and each network card will have a unique MAC address.
You can get these and build a fingerprint for the machine. You might want to allow some of these numbers to change before declaring it a new machine. ( e.g. if the 2 out of three are the same, then the machine is the same ). So you can deal somewhat gracefully from having a component upgraded.
I've clipped some code from one of my projects that gets these numbers.
Windows:
#include "machine_id.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <intrin.h>
#include <iphlpapi.h>
#ifndef _MSC_VER
#include <cpuid.h>
#else
#include <intrin.h>
#endif
// we just need this for purposes of unique machine id. So any one or two mac's is
// fine.
u16 hashMacAddress( PIP_ADAPTER_INFO info )
{
u16 hash = 0;
for ( u32 i = 0; i < info->AddressLength; i++ )
{
hash += ( info->Address[i] << (( i & 1 ) * 8 ));
}
return hash;
}
void getMacHash( u16& mac1, u16& mac2 )
{
IP_ADAPTER_INFO AdapterInfo[32];
DWORD dwBufLen = sizeof( AdapterInfo );
DWORD dwStatus = GetAdaptersInfo( AdapterInfo, &dwBufLen );
if ( dwStatus != ERROR_SUCCESS )
return; // no adapters.
PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;
mac1 = hashMacAddress( pAdapterInfo );
if ( pAdapterInfo->Next )
mac2 = hashMacAddress( pAdapterInfo->Next );
// sort the mac addresses. We don't want to invalidate
// both macs if they just change order.
if ( mac1 > mac2 )
{
u16 tmp = mac2;
mac2 = mac1;
mac1 = tmp;
}
}
u16 getVolumeHash()
{
DWORD serialNum = 0;
// Determine if this volume uses an NTFS file system.
GetVolumeInformation( "c:\\", NULL, 0, &serialNum, NULL, NULL, NULL, 0 );
u16 hash = (u16)(( serialNum + ( serialNum >> 16 )) & 0xFFFF );
return hash;
}
u16 getCpuHash()
{
int cpuinfo[4] = { 0, 0, 0, 0 };
__cpuid( cpuinfo, 0 );
u16 hash = 0;
u16* ptr = (u16*)(&cpuinfo[0]);
for ( u32 i = 0; i < 8; i++ )
hash += ptr[i];
return hash;
}
const char* getMachineName()
{
static char computerName[1024];
DWORD size = 1024;
GetComputerName( computerName, &size );
return &(computerName[0]);
}
Linux and OsX:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/utsname.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <assert.h>
#ifdef DARWIN
#include <net/if_dl.h>
#include <ifaddrs.h>
#include <net/if_types.h>
#else //!DARWIN
// #include <linux/if.h>
// #include <linux/sockios.h>
#endif //!DARWIN
const char* getMachineName()
{
static struct utsname u;
if ( uname( &u ) < 0 )
{
assert(0);
return "unknown";
}
return u.nodename;
}
//---------------------------------get MAC addresses ------------------------------------unsigned short-unsigned short----------
// we just need this for purposes of unique machine id. So any one or two mac's is fine.
unsigned short hashMacAddress( unsigned char* mac )
{
unsigned short hash = 0;
for ( unsigned int i = 0; i < 6; i++ )
{
hash += ( mac[i] << (( i & 1 ) * 8 ));
}
return hash;
}
void getMacHash( unsigned short& mac1, unsigned short& mac2 )
{
mac1 = 0;
mac2 = 0;
#ifdef DARWIN
struct ifaddrs* ifaphead;
if ( getifaddrs( &ifaphead ) != 0 )
return;
// iterate over the net interfaces
bool foundMac1 = false;
struct ifaddrs* ifap;
for ( ifap = ifaphead; ifap; ifap = ifap->ifa_next )
{
struct sockaddr_dl* sdl = (struct sockaddr_dl*)ifap->ifa_addr;
if ( sdl && ( sdl->sdl_family == AF_LINK ) && ( sdl->sdl_type == IFT_ETHER ))
{
if ( !foundMac1 )
{
foundMac1 = true;
mac1 = hashMacAddress( (unsigned char*)(LLADDR(sdl))); //sdl->sdl_data) + sdl->sdl_nlen) );
} else {
mac2 = hashMacAddress( (unsigned char*)(LLADDR(sdl))); //sdl->sdl_data) + sdl->sdl_nlen) );
break;
}
}
}
freeifaddrs( ifaphead );
#else // !DARWIN
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP );
if ( sock < 0 ) return;
// enumerate all IP addresses of the system
struct ifconf conf;
char ifconfbuf[ 128 * sizeof(struct ifreq) ];
memset( ifconfbuf, 0, sizeof( ifconfbuf ));
conf.ifc_buf = ifconfbuf;
conf.ifc_len = sizeof( ifconfbuf );
if ( ioctl( sock, SIOCGIFCONF, &conf ))
{
assert(0);
return;
}
// get MAC address
bool foundMac1 = false;
struct ifreq* ifr;
for ( ifr = conf.ifc_req; (char*)ifr < (char*)conf.ifc_req + conf.ifc_len; ifr++ )
{
if ( ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data )
continue; // duplicate, skip it
if ( ioctl( sock, SIOCGIFFLAGS, ifr ))
continue; // failed to get flags, skip it
if ( ioctl( sock, SIOCGIFHWADDR, ifr ) == 0 )
{
if ( !foundMac1 )
{
foundMac1 = true;
mac1 = hashMacAddress( (unsigned char*)&(ifr->ifr_addr.sa_data));
} else {
mac2 = hashMacAddress( (unsigned char*)&(ifr->ifr_addr.sa_data));
break;
}
}
}
close( sock );
#endif // !DARWIN
// sort the mac addresses. We don't want to invalidate
// both macs if they just change order.
if ( mac1 > mac2 )
{
unsigned short tmp = mac2;
mac2 = mac1;
mac1 = tmp;
}
}
unsigned short getVolumeHash()
{
// we don't have a 'volume serial number' like on windows. Lets hash the system name instead.
unsigned char* sysname = (unsigned char*)getMachineName();
unsigned short hash = 0;
for ( unsigned int i = 0; sysname[i]; i++ )
hash += ( sysname[i] << (( i & 1 ) * 8 ));
return hash;
}
#ifdef DARWIN
#include <mach-o/arch.h>
unsigned short getCpuHash()
{
const NXArchInfo* info = NXGetLocalArchInfo();
unsigned short val = 0;
val += (unsigned short)info->cputype;
val += (unsigned short)info->cpusubtype;
return val;
}
#else // !DARWIN
static void getCpuid( unsigned int* p, unsigned int ax )
{
__asm __volatile
( "movl %%ebx, %%esi\n\t"
"cpuid\n\t"
"xchgl %%ebx, %%esi"
: "=a" (p[0]), "=S" (p[1]),
"=c" (p[2]), "=d" (p[3])
: "0" (ax)
);
}
unsigned short getCpuHash()
{
unsigned int cpuinfo[4] = { 0, 0, 0, 0 };
getCpuid( cpuinfo, 0 );
unsigned short hash = 0;
unsigned int* ptr = (&cpuinfo[0]);
for ( unsigned int i = 0; i < 4; i++ )
hash += (ptr[i] & 0xFFFF) + ( ptr[i] >> 16 );
return hash;
}
#endif // !DARWIN
int main()
{
printf("Machine: %s\n", getMachineName());
printf("CPU: %d\n", getCpuHash());
printf("Volume: %d\n", getVolumeHash());
return 0;
}
I know, the question is bit too old to be answered. But I have on many occasions faced this issue. I like the accept solution, but if you have tried the code then you will know that it has issues.
firstly the CPU id is the product ID- it is not the serial. So if you have same CPU in another Server then it is just not going to work. also the MAC Address can be changed with ease.
If you are only trying to get this done on Linux- you could try like hal services. ie.
hal-get-property --udi /org/freedesktop/Hal/devices/computer --key system.hardware.uuid
But best thing probably to do is if you can enforce root access and if you want to get your hands dirty- is to look at the code for dmidecode. It will allow you to extract UUID of Chasis, Bios, Video and System. You cannot beat that :) and with a few tweaking you can convert it to a class.
Maybe you can generate almost unique id from unique hardware ids -
MAC is universally unique, you can also use cpu model
In my opinion you should pick only those things which may not be changed frequently like cpu or LAN/WLAN cards.
One quite portable solution would be to use modification time of a current executable. stat function is available on unix and windows, although API is different so you would need to use some IFDEFs.
A binary is unlikely to be deployed at the exactly same time to different machines, so the ids should be unique. The drawback is that the binary update will change the ids.

inotify recursively how to do it?

i need to print events on a folder with multiple subfolders. how to do it recursivly? Please print a c++ code. I am stucked!! Every time the evet is poped i need to open the subfolder, take the file and copy it into another directory. I don't want to list all the subfolders in every 2 seconds and find the files if there are any. Is not efficient. I need to use a monitor folder. Please help
The director that i want to monitor has multiple subfolders. Each subfolder has another subfolder that could contain in a moment of time a file. MainFolder->Subfolders->each subfolder-> subfolder -> file.
Here is the code I have for he moment:
/*
*/
#include <pthread.h>
#include <unistd.h>
#include <iostream>
#include <sys/inotify.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <vector>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
using namespace std;
vector<string> SS;
void *print_message_function( void *ptr );
int main(int argc, char **argv ){
pthread_t t1;
int fd,fd1,wd,wd1,i=0,i1=0,len=0,len1=0;
int length;
char pathname[100],buf[1024],buf1[1024];
int data;
struct inotify_event *event;
char *message1 = "Thread 1";
FILE *fr;
// fd=inotify_init1(IN_NONBLOCK);//--rewrite
fd = inotify_init();
/* watch /test directory for any activity and report it back to me */
wd=inotify_add_watch(fd,"/home/MainFoder/",IN_ALL_EVENTS);
// int flag=0;
// char*ev="";
//wd=inotifytools_watch_recursively_with_exclude("/home/MainFolder/",IN_ALL_EVENTS);
while(1)
{
//sleep(30);
//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];
/* watch /test directory for any activity and report it back to me */
/* check for changes */
{
if((event->mask & IN_OPEN) ||(event->mask & IN_CREATE))
{
printf("\n %s :was opened\n",event->name);
SS.push_back(event->name);
}
}
/* update index to start of next event */
i+=sizeof(struct inotify_event)+event->len;
}
vector<string>::const_iterator cii;
for(cii=SS.begin(); cii!=SS.end(); cii++)
{
wd1 = watch_from_filename(*ci);
}
/*
vector<string>::const_iterator cii;
for(cii=SS.begin(); cii!=SS.end(); cii++)
{
cout <<"HERE:"<< *cii << endl;
}
*/
int iret1, iret2;
/* Create independent threads each of which will execute function */
iret1 = pthread_create( &t1, NULL, print_message_function, (void*) message1);
}
}
void *print_message_function( void *ptr )
{
vector<string>::const_iterator cii;
for(cii=SS.begin(); cii!=SS.end(); cii++)
{
cout <<"HERE:"<< *cii << endl;
std::string path=exec
}
}
This working sample on Github does what you're looking for: inotify-example.cpp
On CREATE events, the current wd (watch descriptor), plus the inotify_event wd and name components, are added to a Watch object (see sample).
The class includes methods to lookup wd and names in several ways.
This snippet shows how CREATE/DELETE events are handled:
if ( event->mask & IN_CREATE ) {
current_dir = watch.get(event->wd);
if ( event->mask & IN_ISDIR ) {
new_dir = current_dir + "/" + event->name;
wd = inotify_add_watch( fd, new_dir.c_str(), WATCH_FLAGS );
watch.insert( event->wd, event->name, wd );
total_dir_events++;
printf( "New directory %s created.\n", new_dir.c_str() );
} else {
total_file_events++;
printf( "New file %s/%s created.\n", current_dir.c_str(), event->name );
}
} else if ( event->mask & IN_DELETE ) {
if ( event->mask & IN_ISDIR ) {
new_dir = watch.erase( event->wd, event->name, &wd );
inotify_rm_watch( fd, wd );
total_dir_events--;
printf( "Directory %s deleted.\n", new_dir.c_str() );
} else {
current_dir = watch.get(event->wd);
total_file_events--;
printf( "File %s/%s deleted.\n", current_dir.c_str(), event->name );
}
}
You can do it in two steps:
Detect all the changes you're interested in on the root directory, plus (if not already included) creations (IN_CREATE).
If the creation is a directory, do the whole algorithm on it.
I have written the code for you. Now, you have to do only one change in this code. Just give path of your directory in main function.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <limits.h>
#include<sys/stat.h>
#include<dirent.h>
#include<time.h>
#include<string.h>
#include<unistd.h>
#define MAX_EVENTS 1024 /*Max. number of events to process at one go*/
#define LEN_NAME 16 /*Assuming that the length of the filename won't exceed 16 bytes*/
#define EVENT_SIZE ( sizeof (struct inotify_event) ) /*size of one event*/
#define BUF_LEN ( MAX_EVENTS * ( EVENT_SIZE + LEN_NAME )) /*buffer to store the data of events*/
void monitor(char *);
int evnt_mon(char *);
void main()
{
if(fork()==0)
evnt_mon("./usssb");// give path of your directory which you want to monitor
monitor("./usssb");// give path of your directory which you want to monitor
while(1);
}
void monitor(char * rt_dir)
{
struct stat st;
DIR *dirp;
struct dirent *dp;
char str[100][100]={ };
char temp[100];
char str1[500]=" ";
int i=0,j=0,src_ret=9,src_ret1=9;
strcpy(str1,rt_dir);
dirp=opendir(str1);
if(dirp==NULL)
{
perror("opendir");
return;
}
while(1)
{
dp=readdir(dirp);
if(dp==NULL)
break;
if((strcmp(dp->d_name,".\0")==0) || (strcmp(dp->d_name,"..")==0))
continue;
if((dp->d_type==DT_DIR)&&((strcmp(dp->d_name,".")!=0)&&(strcmp(dp->d_name,"..")!=0)))
{
strcat(str[i],str1);
strcat(str[i],"/");
strcat(str[i],dp->d_name);
if(fork()==0)
{
evnt_mon(str[i]);
}
i++;
}
}
closedir(dirp);
if(i>0)
{
for(j=0;j<i;j++)
{
monitor(str[j]);
}
}
}
int evnt_mon(char *argv)
{
int length, i = 0, wd;
int fd;
char buffer[BUF_LEN];
/* Initialize Inotify*/
fd = inotify_init();
if ( fd < 0 )
{
perror( "Couldn't initialize inotify");
}
/* add watch to starting directory */
wd = inotify_add_watch(fd, argv, IN_CREATE | IN_MODIFY | IN_DELETE);
if (wd == -1)
{
printf("Couldn't add watch to %s\n",argv);
}
else
{
printf("Watching:: %s\n",argv);
}
/* do it forever*/
while(1)
{
i = 0;
length = read( fd, buffer, BUF_LEN );
if ( length < 0 )
{
perror( "read" );
}
while ( i < length )
{
struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];
if ( event->len )
{
if ( event->mask & IN_CREATE)
{
if (event->mask & IN_ISDIR)
{
printf( "The directory %s was Created in %s.\n", event->name,argv );
if(fork()==0)
{
char p[100]=" ";
strcpy(p,argv);
strcat(p,"/");
strcat(p,event->name);
evnt_mon(p);
}
}
else
printf( "The file %s was Created with WD %d\n", event->name, event->wd );
}
if ( event->mask & IN_MODIFY)
{
if (event->mask & IN_ISDIR)
printf( "The directory %s was modified.\n", event->name );
else
printf( "The file %s was modified with WD %d\n", event->name, event->wd );
}
if ( event->mask & IN_DELETE)
{
if (event->mask & IN_ISDIR)
printf( "The directory %s was deleted from %s.\n", event->name,argv );
else
printf( "The file %s was deleted with WD %d\n", event->name, event->wd );
}
i += EVENT_SIZE + event->len;
}
}
}
/* Clean up*/
inotify_rm_watch( fd, wd );
close( fd );
return 0;
}
You might use the fanotify API. It allows you to monitor a complete mount. The only drawback is that you need to be root.
To address the problem stated by ribram (the 'hole':)). one possible solution i can think of is that we can do a combination of 'polling the directory' and 'using inotify'... i.e. Each time a directory is detected (directory only, don't do it for files):
add a watchpoint for the newly detected directory to inotify
'poll' (or 'scan') the newly detected directory (man readdir()) to see if there're already items (files, directories) created. Those are possibly the ones that are missing.
Note that to build an 'air-tight' case, the above steps' order is important. you need to add the watchpoint first than scan ... This will guarantee that an item is picked up by either 'scan' or inotify or both. In that case you may also need to aware of the dups. i.e. the same item can be both yielded by the scan and the inotify

C++ Socket Connection Error

EDIT
I've made changes to what I saw below and this is what I have
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string>
#include <vector>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <errno.h>
using namespace std;
string buffer;
vector<string> ex;
int s;
void recvline ( int s, string* buf ) {
char in, t;
while ( 1 ) {
recv ( s, &in, 1, 0 );
*buf += in;
if ( in == 10 ) {
t = 1; }
if ( t && in == 13 ) {
break; }
}
}
void push ( int s, string msg ) {
string o = msg + "\r\n";
cout << "SENT:", o;
send ( s, o.c_str(), o.size(), 0 );
}
int main ( int argc, char *argv[] ) {
if ( argc < 3 ) {
cout << "Insufficient Arguments" << endl;
exit ( 7 ); }
s = socket ( AF_INET, SOCK_STREAM, IPPROTO_TCP );
if ( s < 0 )
exit ( 1 );
struct hostent h = *gethostbyname ( argv[1] );
struct sockaddr_in c;
c.sin_family = AF_INET;
c.sin_port = htons(atoi(argv[2]));
c.sin_addr.s_addr = inet_addr ( h.h_addr_list[0] );
if ( connect ( s, (struct sockaddr*)&c, sizeof c ) != 0 ) {
cout << "Unable to connect to network" << endl;
cout << strerror(errno) << endl;
exit ( 2 );
}
push ( s, "USER LOLwat Lw lol.wat :LOLwat" );
push ( s, "NICK LOLwat" );
while ( true ) {
recvline ( s, &buffer );
cout << buffer;
if ( buffer.substr(0,4).c_str() == "PING" )
push ( s, "PONG " + buffer.substr(6,-2) );
}
}
And this is the result:
[dbdii407#xpcd Desktop]$ g++ ?.cpp -o 4096 -
[dbdii407#xpcd Desktop]$ ./4096 irc.scrapirc.com 6667 - Unable to connect to network - Network is unreachable
I think the problem is that this line:
c.sin_port = htons(*argv[2]);
Is not doing what you think it's doing. argv[2] is a string, *argv[2] is the first character of the string. So if you passed "4567" as the second command-line argument, then *argv[2] will be '4' which has ASCII value 52. That means you'll be attempting to connect to port 52, not "4567" as you would expect.
Change the line to:
c.sin_port = htons(atoi(argv[2]));
The atoi function takes a string and converts it to an integer. So "4567" would become 4567.
Also, in general, you should check the value of errno when a function call like that fails (it'll usually tell you in the documentation whether errno is set and the possible values it can be set to). That should help to give you some clue in the future.
Edit
As others have noted, make sure you pay attention to your braces. It's usually easier if you just always use braces around if, while, and so on. That is, this:
if ( connect ( s, (struct sockaddr*)&c, sizeof c ) != 0 )
cout << "Unable to connect to network" << endl;
exit ( 2 );
Is completely different to this:
if ( connect ( s, (struct sockaddr*)&c, sizeof c ) != 0 ) {
cout << "Unable to connect to network" << endl;
exit ( 2 );
}
I decided to completely redo my answer, in part due to the following comment in the gethostbyname manpage:
The gethostbyname*() and
gethostbyaddr*() functions are
obsolete. Applications should use
getaddrinfo(3) and getnameinfo(3)
instead.
Here is the reworked program ( cleaned up a bit with bcpp ) based on using getaddrinfo. I would strongly suggest always compiling with the following options:
g++ -Wall -Wextra irc.cpp -o irc
This showed up the following bugs in your code:
irc.cpp: In function ‘void push(int, std::string)’:
irc.cpp:40: warning: right-hand operand of comma has no effect
irc.cpp: In function ‘int main(int, char**)’:
irc.cpp:87: warning: comparison with string literal results in unspecified behaviour
I went ahead and fixed the errors. Also, try and eliminate global variables as much as possible.
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string>
#include <vector>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <errno.h>
using namespace std;
string buffer;
vector<string> ex;
void recvline ( int s, string* buf )
{
char in, t;
while ( 1 )
{
recv ( s, &in, 1, 0 );
*buf += in;
if ( in == 10 )
{
t = 1;
}
if ( t && in == 13 )
{
break;
}
}
}
void push ( int s, string msg )
{
string o = msg + "\r\n";
cout << "SENT:" << o;
send ( s, o.c_str(), o.size(), 0 );
}
int main ( int argc, char *argv[] )
{
if ( argc < 3 )
{
cout << "Insufficient Arguments" << endl;
exit ( 7 );
}
int s, sfd;
struct addrinfo *result, *rp;
s = getaddrinfo(argv[1], argv[2], NULL, &result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
for (rp = result; rp != NULL; rp = rp->ai_next) {
sfd = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (sfd == -1)
continue;
if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
break; /* Success */
close(sfd);
}
if (rp == NULL) { /* No address succeeded */
fprintf(stderr, "Could not connect\n");
exit(EXIT_FAILURE);
}
freeaddrinfo(result); /* No longer needed */
push ( sfd, "USER LOLwat Lw lol.wat :LOLwat" );
push ( sfd, "NICK LOLwat" );
while ( true )
{
recvline ( sfd, &buffer );
cout << buffer;
if ( buffer.substr(0,4) == "PING" )
push ( sfd, "PONG " + buffer.substr(6,-2) );
}
}