entangler 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,727 @@
1
+ /******************************************************************************
2
+ *******************************************************************************
3
+ *******************************************************************************
4
+
5
+
6
+ kernel-filesystem-monitor-daemon
7
+ Copyright (C) 2005 Ben Martin
8
+
9
+ This program is free software; you can redistribute it and/or modify
10
+ it under the terms of the GNU General Public License as published by
11
+ the Free Software Foundation; either version 2 of the License, or
12
+ (at your option) any later version.
13
+
14
+ This program is distributed in the hope that it will be useful,
15
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ GNU General Public License for more details.
18
+
19
+ You should have received a copy of the GNU General Public License
20
+ along with this program; if not, write to the Free Software
21
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
+
23
+ For more details see the COPYING file in the root directory of this
24
+ distribution.
25
+
26
+ $Id: kernel-filesystem-monitor-daemon.cpp,v 1.4 2008/05/25 21:30:52 ben Exp $
27
+
28
+ *******************************************************************************
29
+ *******************************************************************************
30
+ ******************************************************************************/
31
+
32
+ #include <sys/types.h>
33
+ #include <unistd.h>
34
+ #include <sys/stat.h>
35
+ #include <fcntl.h>
36
+ #include <sys/ioctl.h>
37
+ #include <dirent.h>
38
+ #include <sys/poll.h>
39
+ #include <signal.h>
40
+ #include <string.h>
41
+ #include <errno.h>
42
+ #include <syslog.h>
43
+ #include <stdlib.h>
44
+ #include <limits.h>
45
+
46
+ #include <sys/inotify.h>
47
+ //#include "inotify-syscalls.h"
48
+
49
+ #include <iostream>
50
+ #include <sstream>
51
+
52
+ #include <kernel-filesystem-monitor-daemon.hh>
53
+
54
+ static bool WantToQuit = false;
55
+ unsigned long Verbose = 0;
56
+
57
+ //#ifdef IN_CREATE_SUBDIR
58
+ //#define MY_CREATE_SUBDIR_MASK IN_CREATE_SUBDIR | IN_CREATE_FILE
59
+ //#else
60
+ #define MY_CREATE_SUBDIR_MASK IN_CREATE
61
+ //#endif
62
+
63
+ static void sig_term_cb(int sign)
64
+ {
65
+ syslog( LOG_INFO, "preparing to exit... pid:%d", getpid() );
66
+ WantToQuit = true;
67
+ }
68
+
69
+ static void sig_usr1_cb(int sign)
70
+ {
71
+ sig_term_cb(sign);
72
+ }
73
+
74
+ static void sig_usr2_cb(int sign)
75
+ {
76
+ /* ping */
77
+ if( Verbose )
78
+ {
79
+ syslog( LOG_INFO, "got a ping! pid:%d", getpid() );
80
+ }
81
+ }
82
+
83
+ struct tolowerstr : public std::unary_function< std::string , std::string >
84
+ {
85
+ /**
86
+ */
87
+ inline std::string operator()(const std::string& x) const
88
+ {
89
+ std::string ret = x;
90
+
91
+ for( std::string::iterator p = ret.begin(); p != ret.end(); ++p )
92
+ {
93
+ *p = ::tolower( *p );
94
+ }
95
+
96
+ return ret;
97
+ }
98
+ };
99
+
100
+ static bool starts_with( const string& s, const string& starting )
101
+ {
102
+ int starting_len = starting.length();
103
+ int s_len = s.length();
104
+
105
+ if( s_len < starting_len )
106
+ return false;
107
+
108
+ return !s.compare( 0, starting_len, starting );
109
+ }
110
+
111
+ string getHomeDir( const char* homedir_CSTR )
112
+ {
113
+ if( homedir_CSTR )
114
+ return homedir_CSTR;
115
+ if( const char* en = getenv( "HOME" ) )
116
+ return en;
117
+ return "";
118
+ // cerr << "Can not work out your home directory! use the --homedir argument\n";
119
+ // exit(1);
120
+ }
121
+
122
+
123
+
124
+ /********************************************************************************/
125
+ /********************************************************************************/
126
+ /********************************************************************************/
127
+
128
+ KernelFileSystemMonitorDaemon::KernelFileSystemMonitorDaemon()
129
+ :
130
+ m_runInForground( 0 ),
131
+ watch_mask( IN_ATTRIB |
132
+ IN_MOVED_FROM |
133
+ IN_MOVED_TO |
134
+ MY_CREATE_SUBDIR_MASK |
135
+
136
+ #ifdef IN_DELETE_FILE
137
+ IN_DELETE_FILE |
138
+ #endif
139
+ #ifdef IN_DELETE
140
+ IN_DELETE |
141
+ #endif
142
+ IN_CLOSE_WRITE
143
+ ),
144
+ dev_fd( 0 ),
145
+ m_inotify_queue_threshold_bytes( 8 * 1024 ),
146
+ m_nanosleep_ns( 50 * 1000 * 1000 ),
147
+ m_inotify_queue_sleep_threshold_ns( 200 * 1000 * 1000 )
148
+ {
149
+ // watch_mask = IN_ALL_EVENTS;
150
+ }
151
+ KernelFileSystemMonitorDaemon::~KernelFileSystemMonitorDaemon()
152
+ {
153
+ }
154
+
155
+
156
+ void
157
+ KernelFileSystemMonitorDaemon::background_into_daemon()
158
+ {
159
+ pid_t pid = 0;
160
+
161
+ if((pid = fork()) < 0 )
162
+ {
163
+ syslog( LOG_EMERG, "can't fork()", 0 );
164
+ exit( 1 );
165
+ }
166
+ else if( pid != 0 )
167
+ exit(0);
168
+
169
+ setsid();
170
+ chdir("/");
171
+ umask(0);
172
+ }
173
+
174
+
175
+ void
176
+ KernelFileSystemMonitorDaemon::priv_handle_event( struct inotify_event *pevent, time_t tt )
177
+ {
178
+ handle_event( pevent, tt );
179
+ }
180
+
181
+ void
182
+ KernelFileSystemMonitorDaemon::event_batch_start( time_t tt )
183
+ {
184
+ }
185
+
186
+ void
187
+ KernelFileSystemMonitorDaemon::event_batch_end( time_t tt )
188
+ {
189
+ }
190
+
191
+
192
+ void
193
+ KernelFileSystemMonitorDaemon::setupSignalHandlers()
194
+ {
195
+ struct sigaction newinth;
196
+ newinth.sa_handler = sig_term_cb;
197
+ sigemptyset(&newinth.sa_mask);
198
+ newinth.sa_flags = SA_RESTART;
199
+ if( -1 == sigaction( SIGTERM, &newinth, NULL))
200
+ {
201
+ syslog( LOG_ERR, "ERROR: can not setup signal handling. reason:%s", strerror(errno) );
202
+ exit(2);
203
+ }
204
+ if( -1 == sigaction( SIGQUIT, &newinth, NULL))
205
+ {
206
+ syslog( LOG_ERR, "ERROR: can not setup signal handling. reason:%s", strerror(errno) );
207
+ exit(2);
208
+ }
209
+
210
+ newinth.sa_handler = sig_usr1_cb;
211
+ sigemptyset(&newinth.sa_mask);
212
+ newinth.sa_flags = SA_RESTART;
213
+ if( -1 == sigaction( SIGUSR1, &newinth, NULL))
214
+ {
215
+ syslog( LOG_ERR, "ERROR: can not setup signal handling. reason:%s", strerror(errno) );
216
+ exit(2);
217
+ }
218
+
219
+ newinth.sa_handler = sig_usr2_cb;
220
+ sigemptyset(&newinth.sa_mask);
221
+ newinth.sa_flags = SA_RESTART;
222
+ if( -1 == sigaction( SIGUSR2, &newinth, NULL))
223
+ {
224
+ syslog( LOG_ERR, "ERROR: can not setup signal handling. reason:%s", strerror(errno) );
225
+ exit(2);
226
+ }
227
+ }
228
+
229
+ void
230
+ KernelFileSystemMonitorDaemon::priv_Closedown()
231
+ {
232
+ Closedown();
233
+ }
234
+
235
+ void
236
+ KernelFileSystemMonitorDaemon::print_event (struct inotify_event *event)
237
+ {
238
+ if( m_runInForground )
239
+ {
240
+ if (event->len)
241
+ {
242
+ cout << "M " << m_workingDirToURL[event->wd] << "/" << event->name << endl;
243
+ // cout << "-" << endl;
244
+ }
245
+ #if 0
246
+ cout << "event on wd:" << event->wd << " " << m_workingDirToURL[event->wd];
247
+ if (event->len)
248
+ {
249
+ cout << " filename:" << event->name;
250
+ }
251
+ cout << endl;
252
+ print_mask( cout, event->mask );
253
+ if (event->len)
254
+ {
255
+ cout << " URL:" << m_workingDirToURL[event->wd] << "/" << event->name
256
+ << endl;
257
+ }
258
+ #endif
259
+ }
260
+ else
261
+ {
262
+ stringstream maskss;
263
+ print_mask( maskss, event->mask );
264
+ syslog( LOG_DEBUG, "event on wd:%s mask:%s file:%s\n",
265
+ m_workingDirToURL[event->wd].c_str(),
266
+ maskss.str().c_str(),
267
+ event->name );
268
+ }
269
+ }
270
+
271
+
272
+ bool
273
+ KernelFileSystemMonitorDaemon::shouldAddSubObject( int wd, const std::string& fn )
274
+ {
275
+ struct stat statbuf;
276
+
277
+ if( fn == "." || fn == ".." )
278
+ return false;
279
+
280
+ int rc = lstat( fn.c_str(), &statbuf );
281
+ if( !rc )
282
+ {
283
+ candidateObject( wd, fn, statbuf );
284
+ if( S_ISDIR( statbuf.st_mode) )
285
+ {
286
+ return true;
287
+ }
288
+ }
289
+ return false;
290
+ }
291
+
292
+ void
293
+ KernelFileSystemMonitorDaemon::candidateObject( int wd, const std::string& fn, struct stat& statbuf )
294
+ {
295
+ }
296
+
297
+
298
+
299
+ void
300
+ KernelFileSystemMonitorDaemon::Closedown()
301
+ {
302
+ }
303
+
304
+
305
+ void
306
+ KernelFileSystemMonitorDaemon::setRunInForground( bool v )
307
+ {
308
+ m_runInForground = v;
309
+ }
310
+
311
+
312
+ bool
313
+ KernelFileSystemMonitorDaemon::shouldWatch( const string& earl )
314
+ {
315
+ for( m_ignorePrefixes_t::iterator ci = m_ignorePrefixes.begin();
316
+ ci != m_ignorePrefixes.end(); ++ci )
317
+ {
318
+ if( starts_with( earl, *ci ) )
319
+ return false;
320
+ }
321
+
322
+ return true;
323
+ }
324
+
325
+ bool
326
+ KernelFileSystemMonitorDaemon::handle_create_subdir_event_by_maybe_watching(
327
+ struct inotify_event *pevent, time_t tt )
328
+ {
329
+ // cerr << "pevent->mask:" << hex << pevent->mask
330
+ // << " MY_CREATE_SUBDIR_MASK:" << hex << MY_CREATE_SUBDIR_MASK
331
+ // << dec
332
+ // << endl;
333
+
334
+ if ( pevent->mask & MY_CREATE_SUBDIR_MASK )
335
+ {
336
+ string dirName = m_workingDirToURL[ pevent->wd ];
337
+ chdir( dirName.c_str() );
338
+ // cerr << "have dirname:" << dirName << endl;
339
+
340
+ if( shouldAddSubObject( pevent->wd, pevent->name ) )
341
+ {
342
+ string earl = dirName + "/" + pevent->name;
343
+ m_watchRoots.push_back( earl );
344
+ return true;
345
+ }
346
+ }
347
+ return false;
348
+ }
349
+
350
+
351
+
352
+
353
+ void
354
+ KernelFileSystemMonitorDaemon::add_watches_recursive( const string& earl )
355
+ {
356
+ if( !shouldWatch( earl ) )
357
+ return;
358
+
359
+ // struct inotify_watch_request req;
360
+
361
+ int fd = open ( earl.c_str(), O_RDONLY);
362
+
363
+ if (fd < 0)
364
+ {
365
+ syslog( LOG_WARNING, "not monitoring:%s reason:%s", earl.c_str(), strerror(errno));
366
+ return;
367
+ }
368
+
369
+ fchdir(fd);
370
+ // req.fd = fd;
371
+ // req.mask = IN_CREATE_SUBDIR;
372
+ // long wd = ioctl( dev_fd, INOTIFY_WATCH, &req );
373
+ long wd = inotify_add_watch( dev_fd, earl.c_str(), MY_CREATE_SUBDIR_MASK );
374
+ m_workingDirToURL[ wd ] = earl;
375
+ setupWorkingDirToPersistentDirIDMapping( wd, earl );
376
+
377
+ //
378
+ // Gather up the subdirectory names into dirNames
379
+ //
380
+ typedef list< string > dirNames_t;
381
+ dirNames_t dirNames;
382
+
383
+ DIR *d;
384
+ struct dirent *e;
385
+ if ((d = opendir (earl.c_str())) == NULL)
386
+ {
387
+ syslog( LOG_WARNING, "not monitoring:%s reason:%s", earl.c_str(), strerror(errno));
388
+ }
389
+ else
390
+ {
391
+ while( e = readdir(d) )
392
+ {
393
+ if( shouldAddSubObject( wd, e->d_name ) )
394
+ {
395
+ dirNames.push_back( e->d_name );
396
+ if( Verbose )
397
+ cerr << "might add monitor for:" << e->d_name << endl;
398
+ }
399
+ }
400
+ closedir (d);
401
+ }
402
+
403
+ //
404
+ // Check to see if a new directory was created while
405
+ // we were readdir()ing
406
+ //
407
+ {
408
+ // cerr << "***** BEGIN ****** checking bg info url:" << earl << endl;
409
+
410
+ const int buf_sz = 16 * 1024;
411
+ char buf[ buf_sz + 1 ];
412
+ int event_count = 0;
413
+
414
+ unsigned int nfds = 1;
415
+ struct pollfd ufds;
416
+ ufds.fd = dev_fd;
417
+ ufds.events = POLLIN;
418
+ ufds.revents = 0;
419
+
420
+ int poll_rc = poll( &ufds, nfds, 0 );
421
+ for( ; poll_rc ; poll_rc = poll( &ufds, nfds, 0 ) )
422
+ {
423
+ size_t len = read( dev_fd, buf, buf_sz);
424
+ if( !len )
425
+ break;
426
+
427
+ size_t buf_iter = 0;
428
+
429
+ // cerr << "buf_iter:" << buf_iter << " len:" << len << endl;
430
+ // cerr << "dev_fd:" << dev_fd << endl;
431
+
432
+ while (buf_iter < len)
433
+ {
434
+ /* Parse events and queue them ! */
435
+ struct inotify_event * pevent
436
+ = (struct inotify_event *)&buf[buf_iter];
437
+
438
+ handle_create_subdir_event_by_maybe_watching( pevent, 0 );
439
+
440
+ int event_size = sizeof(struct inotify_event) + pevent->len;
441
+ buf_iter += event_size;
442
+ event_count++;
443
+ }
444
+ }
445
+ // cerr << "***** END ****** checking bg info url:" << earl << endl;
446
+ }
447
+
448
+ //
449
+ // switch to monitoring all interesting things for this directory.
450
+ //
451
+ // req.fd = fd;
452
+ // req.mask = watch_mask;
453
+ // wd = ioctl( dev_fd, INOTIFY_WATCH, &req );
454
+ wd = inotify_add_watch( dev_fd, earl.c_str(), watch_mask );
455
+ close (fd);
456
+
457
+ //
458
+ // Monitor the subdirectories
459
+ //
460
+ for( dirNames_t::const_iterator di = dirNames.begin();
461
+ di != dirNames.end(); ++di )
462
+ {
463
+ add_watches_recursive( earl + "/" + *di );
464
+ }
465
+
466
+ }
467
+
468
+ void
469
+ KernelFileSystemMonitorDaemon::addIgnorePrefix( const string& s )
470
+ {
471
+ m_ignorePrefixes.push_back( s );
472
+ }
473
+
474
+ void
475
+ KernelFileSystemMonitorDaemon::ParseWatchOptions( poptContext& optCon )
476
+ {
477
+ string opcode = "";
478
+ while( const char* tmpCSTR = poptGetArg(optCon) )
479
+ {
480
+ string s = tmpCSTR;
481
+ string ls = tolowerstr()(s);
482
+
483
+ // cerr << " s:" << s << endl;
484
+
485
+ if( ls == "ignorepfx" )
486
+ {
487
+ opcode = ls;
488
+ continue;
489
+ }
490
+ if( ls == "watch" )
491
+ {
492
+ opcode = ls;
493
+ continue;
494
+ }
495
+
496
+ if( opcode == "ignorepfx" )
497
+ {
498
+ m_ignorePrefixes.push_back( s );
499
+ }
500
+ else if( opcode == "watch" )
501
+ {
502
+ if( Verbose )
503
+ cerr << "setting up watch for:" << s << endl;
504
+ m_watchRoots.push_back( s );
505
+ }
506
+ }
507
+
508
+ }
509
+
510
+
511
+ void
512
+ KernelFileSystemMonitorDaemon::setupWatches()
513
+ {
514
+ if( !dev_fd )
515
+ {
516
+ // dev_fd = open ("/dev/inotify", O_RDONLY);
517
+ dev_fd = inotify_init();
518
+ if( dev_fd < 0 )
519
+ {
520
+ syslog( LOG_ERR, "Exiting due to failure to open inotify device reason:%s", strerror(errno));
521
+ cerr << "Exiting due to failure to open /dev/inotify device reason:" << strerror(errno)
522
+ << endl;
523
+ exit( 1 );
524
+ }
525
+ }
526
+
527
+ if( m_watchRoots.empty() )
528
+ {
529
+ cerr << "No directories/files to watch have been specified!" << endl;
530
+ }
531
+ else
532
+ {
533
+ for( stringlist_t::const_iterator ci = m_watchRoots.begin();
534
+ ci != m_watchRoots.end(); ++ci )
535
+ {
536
+ add_watches_recursive( *ci );
537
+ }
538
+ }
539
+ }
540
+
541
+ void
542
+ KernelFileSystemMonitorDaemon::HandleSleepForQueueSize()
543
+ {
544
+ unsigned long long time_slept = 0;
545
+
546
+ while( !WantToQuit )
547
+ {
548
+ unsigned int bytesAvailable = 0;
549
+ int iorc = ioctl( dev_fd, FIONREAD, &bytesAvailable, 0 );
550
+ if( iorc < 0 )
551
+ {
552
+ // error
553
+ }
554
+
555
+ syslog( LOG_DEBUG,
556
+ "HandleQ() bytesAvailable:%d iorc:%d "
557
+ "queue_threshold_bytes:%d "
558
+ "queue_sleep_threshold_ns:%d time_slept:%d",
559
+ bytesAvailable, iorc,
560
+ m_inotify_queue_threshold_bytes,
561
+ m_inotify_queue_sleep_threshold_ns,
562
+ time_slept );
563
+ // cerr << "HandleQ() bytesAvailable:" << bytesAvailable
564
+ // << " iorc:" << iorc
565
+ // << " queue_threshold_bytes:" << m_inotify_queue_threshold_bytes
566
+ // << " queue_sleep_threshold_ns:" << m_inotify_queue_sleep_threshold_ns
567
+ // << " time_slept:" << time_slept
568
+ // << endl;
569
+
570
+ if( time_slept >= m_inotify_queue_sleep_threshold_ns )
571
+ {
572
+ if( !bytesAvailable )
573
+ {
574
+ time_slept = 0;
575
+
576
+ unsigned int nfds = 1;
577
+ struct pollfd ufds;
578
+ ufds.fd = dev_fd;
579
+ ufds.events = POLLIN;
580
+ ufds.revents = 0;
581
+
582
+ // if the system is idle we should really switch to
583
+ // poll() here so that we are not a burden
584
+ int poll_rc = poll( &ufds, nfds, -1 );
585
+ syslog( LOG_DEBUG, "after poll() rc:%d", poll_rc );
586
+
587
+ continue;
588
+ }
589
+
590
+ return;
591
+ }
592
+
593
+ if( bytesAvailable >= m_inotify_queue_threshold_bytes )
594
+ {
595
+ return;
596
+ }
597
+
598
+ //
599
+ // Time to go for a little kip
600
+ //
601
+ struct timespec nts;
602
+ nts.tv_sec = 0;
603
+ nts.tv_nsec = m_nanosleep_ns;
604
+ struct timespec rem;
605
+ bzero( &rem, sizeof(rem) );
606
+
607
+ while( nanosleep( &nts, &rem ) < 0 )
608
+ {
609
+ if( WantToQuit )
610
+ return;
611
+
612
+ if( errno == EINTR )
613
+ {
614
+ nts = rem;
615
+ bzero( &rem, sizeof(rem) );
616
+ continue;
617
+ }
618
+ else
619
+ {
620
+ // error
621
+ break;
622
+ }
623
+ }
624
+ time_slept += m_nanosleep_ns;
625
+ continue;
626
+ }
627
+ }
628
+
629
+
630
+ int
631
+ KernelFileSystemMonitorDaemon::run()
632
+ {
633
+ // cerr << "KernelFileSystemMonitorDaemon::run() starting" << endl;
634
+
635
+ chdir("/");
636
+
637
+ const int buf_sz = 32 * 1024;
638
+ char buf[ buf_sz + 1 ];
639
+ int event_count = 0;
640
+
641
+ while( true )
642
+ {
643
+ HandleSleepForQueueSize();
644
+ syslog( LOG_DEBUG, "After HandleSleepForQueueSize()", 0 );
645
+
646
+ if( WantToQuit )
647
+ {
648
+ Closedown();
649
+ return 0;
650
+ }
651
+
652
+ if( size_t len = read( dev_fd, buf, buf_sz) )
653
+ {
654
+ if( len > SSIZE_MAX )
655
+ continue;
656
+
657
+ time_t tt = time( 0 );
658
+
659
+ size_t buf_iter = 0;
660
+ bool have_new_subdirs_to_watch = false;
661
+
662
+ // cerr << "buf_iter:" << buf_iter << " len:" << len << endl;
663
+ // cerr << "dev_fd:" << dev_fd << endl;
664
+
665
+ event_batch_start( tt );
666
+
667
+ while (buf_iter < len)
668
+ {
669
+ /* Parse events and queue them ! */
670
+ struct inotify_event * pevent
671
+ = (struct inotify_event *)&buf[buf_iter];
672
+
673
+ have_new_subdirs_to_watch |=
674
+ handle_create_subdir_event_by_maybe_watching( pevent, tt );
675
+ handle_event( pevent, tt );
676
+
677
+ int event_size = sizeof(struct inotify_event) + pevent->len;
678
+ buf_iter += event_size;
679
+ event_count++;
680
+ }
681
+
682
+ event_batch_end( tt );
683
+
684
+ cout << "-" << endl;
685
+
686
+ if( have_new_subdirs_to_watch )
687
+ setupWatches();
688
+ }
689
+ }
690
+
691
+ // cerr << "KernelFileSystemMonitorDaemon::run() exiting" << endl;
692
+ return 0;
693
+
694
+ }
695
+
696
+
697
+ struct ::poptOption*
698
+ KernelFileSystemMonitorDaemon::getPopTable()
699
+ {
700
+ static struct poptOption optionsTable[] =
701
+ {
702
+ { "inotify-queue-threshold-bytes", 0, POPT_ARG_INT, &m_inotify_queue_threshold_bytes, 0,
703
+ "number of bytes that should be available on /dev/inotify before reading", "" },
704
+
705
+ { "inotify-queue-sleep-threshold-ns", 0, POPT_ARG_INT, &m_inotify_queue_sleep_threshold_ns, 0,
706
+ "after this time read the inotify queue anyway", "" },
707
+
708
+ { "inotify-sleep-delay-ns", 0, POPT_ARG_INT, &m_nanosleep_ns, 0,
709
+ "nanoseconds to sleep if /dev/inotify is not full enough", "" },
710
+
711
+ POPT_TABLEEND
712
+ };
713
+ return optionsTable;
714
+ }
715
+
716
+
717
+
718
+
719
+
720
+
721
+
722
+
723
+
724
+
725
+
726
+
727
+