entangler 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+