15 #include "../../config.h"
17 #include "inotifytools_p.h"
25 #include <sys/select.h>
26 #include <sys/types.h>
28 #include <sys/ioctl.h>
35 #include "inotifytools/inotify.h"
123 #define MAX_EVENTS 4096
124 #define MAX_STRLEN 4096
125 #define INOTIFY_PROCDIR "/proc/sys/fs/inotify/"
126 #define WATCHES_SIZE_PATH INOTIFY_PROCDIR "max_user_watches"
127 #define QUEUE_SIZE_PATH INOTIFY_PROCDIR "max_queued_watches"
128 #define INSTANCES_PATH INOTIFY_PROCDIR "max_user_instances"
130 static int inotify_fd;
131 static unsigned num_access;
132 static unsigned num_modify;
133 static unsigned num_attrib;
134 static unsigned num_close_nowrite;
135 static unsigned num_close_write;
136 static unsigned num_open;
137 static unsigned num_move_self;
138 static unsigned num_moved_to;
139 static unsigned num_moved_from;
140 static unsigned num_create;
141 static unsigned num_delete;
142 static unsigned num_delete_self;
143 static unsigned num_unmount;
144 static unsigned num_total;
145 static int collect_stats = 0;
147 struct rbtree *tree_wd = 0;
148 struct rbtree *tree_filename = 0;
149 static int error = 0;
151 static char* timefmt = 0;
152 static regex_t* regex = 0;
154 static int invert_regexp = 0;
156 static int isdir(
char const * path );
157 void record_stats(
struct inotify_event
const * event );
158 int onestr_to_event(
char const * event);
177 #define niceassert(cond,mesg) _niceassert((long)cond, __LINE__, __FILE__, \
180 #define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory")
199 static void _niceassert(
long cond,
int line,
char const * file,
200 char const * condstr,
char const * mesg ) {
204 fprintf(stderr,
"%s:%d assertion ( %s ) failed: %s\n", file, line,
208 fprintf(stderr,
"%s:%d assertion ( %s ) failed.\n", file, line, condstr);
221 char * chrtostr(
char ch) {
222 static char str[2] = {
'\0',
'\0' };
230 int read_num_from_file(
char * filename,
int * num ) {
231 FILE * file = fopen( filename,
"r" );
237 if ( EOF == fscanf( file,
"%d", num ) ) {
242 niceassert( 0 == fclose( file ), 0 );
247 int wd_compare(
const void *d1,
const void *d2,
const void *config) {
248 if (!d1 || !d2)
return d1 - d2;
249 return ((watch*)d1)->wd - ((watch*)d2)->wd;
252 int filename_compare(
const void *d1,
const void *d2,
const void *config) {
253 if (!d1 || !d2)
return d1 - d2;
254 return strcmp(((watch*)d1)->filename, ((watch*)d2)->filename);
260 watch *watch_from_wd(
int wd ) {
263 return (watch*)rbfind(&w, tree_wd);
269 watch *watch_from_filename(
char const *filename ) {
271 w.filename = (
char*)filename;
272 return (watch*)rbfind(&w, tree_filename);
289 inotify_fd = inotify_init();
290 if (inotify_fd < 0) {
297 tree_wd = rbinit(wd_compare, 0);
298 tree_filename = rbinit(filename_compare, 0);
307 void destroy_watch(watch *w) {
308 if (w->filename) free(w->filename);
315 void cleanup_tree(
const void *nodep,
317 const int depth,
void* arg) {
318 if (which != endorder && which != leaf)
return;
319 watch *w = (watch*)nodep;
344 rbwalk(tree_wd, cleanup_tree, 0);
345 rbdestroy(tree_wd); tree_wd = 0;
346 rbdestroy(tree_filename); tree_filename = 0;
352 void empty_stats(
const void *nodep,
354 const int depth,
void *arg) {
355 if (which != endorder && which != leaf)
return;
356 watch *w = (watch*)nodep;
360 w->hit_close_nowrite = 0;
361 w->hit_close_write = 0;
363 w->hit_move_self = 0;
364 w->hit_moved_from = 0;
368 w->hit_delete_self = 0;
376 struct replace_filename_data {
377 char const *old_name;
378 char const *new_name;
385 void replace_filename(
const void *nodep,
const VISIT which,
const int depth,
386 const struct replace_filename_data *data) {
387 if (which != endorder && which != leaf)
return;
388 watch *w = (watch*)nodep;
390 if ( 0 == strncmp( data->old_name, w->filename, data->old_len ) ) {
391 nasprintf( &name,
"%s%s", data->new_name, &(w->filename[data->old_len]) );
392 if (!strcmp( w->filename, data->new_name )) {
395 rbdelete(w, tree_filename);
398 rbsearch(w, tree_filename);
406 void get_num(
const void *nodep,
408 const int depth,
void *arg) {
409 if (which != endorder && which != leaf)
return;
427 niceassert( init,
"inotifytools_initialize not called yet" );
431 rbwalk(tree_wd, empty_stats, 0);
437 num_close_nowrite = 0;
480 if ( strchr(
"_" "abcdefghijklmnopqrstuvwxyz"
481 "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
486 char * event1, * event2;
487 static const size_t eventstr_size = 4096;
488 char eventstr[eventstr_size];
491 if ( !event || !event[0] )
return 0;
493 event1 = (
char *)event;
494 event2 = strchr( event1, sep );
495 while ( event1 && event1[0] ) {
497 len = event2 - event1;
498 niceassert(len < eventstr_size,
499 "malformed event string (very long)");
502 len = strlen(event1);
504 if (len > eventstr_size - 1)
505 len = eventstr_size - 1;
507 if (event2 || len == eventstr_size - 1) {
508 strncpy(eventstr, event1, len);
510 strcpy(eventstr, event1);
515 ret1 = onestr_to_event( eventstr );
516 if ( 0 == ret1 || -1 == ret1 ) {
523 if ( event1 && event1[0] ) {
527 if ( !event1[0] )
return 0;
528 event2 = strchr( event1, sep );
573 int onestr_to_event(
char const * event)
578 if ( !event || !event[0] )
580 else if ( 0 == strcasecmp(event,
"ACCESS") )
582 else if ( 0 == strcasecmp(event,
"MODIFY") )
584 else if ( 0 == strcasecmp(event,
"ATTRIB") )
586 else if ( 0 == strcasecmp(event,
"CLOSE_WRITE") )
587 ret = IN_CLOSE_WRITE;
588 else if ( 0 == strcasecmp(event,
"CLOSE_NOWRITE") )
589 ret = IN_CLOSE_NOWRITE;
590 else if ( 0 == strcasecmp(event,
"OPEN") )
592 else if ( 0 == strcasecmp(event,
"MOVED_FROM") )
594 else if ( 0 == strcasecmp(event,
"MOVED_TO") )
596 else if ( 0 == strcasecmp(event,
"CREATE") )
598 else if ( 0 == strcasecmp(event,
"DELETE") )
600 else if ( 0 == strcasecmp(event,
"DELETE_SELF") )
601 ret = IN_DELETE_SELF;
602 else if ( 0 == strcasecmp(event,
"UNMOUNT") )
604 else if ( 0 == strcasecmp(event,
"Q_OVERFLOW") )
606 else if ( 0 == strcasecmp(event,
"IGNORED") )
608 else if ( 0 == strcasecmp(event,
"CLOSE") )
610 else if ( 0 == strcasecmp(event,
"MOVE_SELF") )
612 else if ( 0 == strcasecmp(event,
"MOVE") )
614 else if ( 0 == strcasecmp(event,
"ISDIR") )
616 else if ( 0 == strcasecmp(event,
"ONESHOT") )
618 else if ( 0 == strcasecmp(event,
"ALL_EVENTS") )
675 static char ret[1024];
679 if ( IN_ACCESS & events ) {
680 strcat( ret, chrtostr(sep) );
681 strcat( ret,
"ACCESS" );
683 if ( IN_MODIFY & events ) {
684 strcat( ret, chrtostr(sep) );
685 strcat( ret,
"MODIFY" );
687 if ( IN_ATTRIB & events ) {
688 strcat( ret, chrtostr(sep) );
689 strcat( ret,
"ATTRIB" );
691 if ( IN_CLOSE_WRITE & events ) {
692 strcat( ret, chrtostr(sep) );
693 strcat( ret,
"CLOSE_WRITE" );
695 if ( IN_CLOSE_NOWRITE & events ) {
696 strcat( ret, chrtostr(sep) );
697 strcat( ret,
"CLOSE_NOWRITE" );
699 if ( IN_OPEN & events ) {
700 strcat( ret, chrtostr(sep) );
701 strcat( ret,
"OPEN" );
703 if ( IN_MOVED_FROM & events ) {
704 strcat( ret, chrtostr(sep) );
705 strcat( ret,
"MOVED_FROM" );
707 if ( IN_MOVED_TO & events ) {
708 strcat( ret, chrtostr(sep) );
709 strcat( ret,
"MOVED_TO" );
711 if ( IN_CREATE & events ) {
712 strcat( ret, chrtostr(sep) );
713 strcat( ret,
"CREATE" );
715 if ( IN_DELETE & events ) {
716 strcat( ret, chrtostr(sep) );
717 strcat( ret,
"DELETE" );
719 if ( IN_DELETE_SELF & events ) {
720 strcat( ret, chrtostr(sep) );
721 strcat( ret,
"DELETE_SELF" );
723 if ( IN_UNMOUNT & events ) {
724 strcat( ret, chrtostr(sep) );
725 strcat( ret,
"UNMOUNT" );
727 if ( IN_Q_OVERFLOW & events ) {
728 strcat( ret, chrtostr(sep) );
729 strcat( ret,
"Q_OVERFLOW" );
731 if ( IN_IGNORED & events ) {
732 strcat( ret, chrtostr(sep) );
733 strcat( ret,
"IGNORED" );
735 if ( IN_CLOSE & events ) {
736 strcat( ret, chrtostr(sep) );
737 strcat( ret,
"CLOSE" );
739 if ( IN_MOVE_SELF & events ) {
740 strcat( ret, chrtostr(sep) );
741 strcat( ret,
"MOVE_SELF" );
743 if ( IN_ISDIR & events ) {
744 strcat( ret, chrtostr(sep) );
745 strcat( ret,
"ISDIR" );
747 if ( IN_ONESHOT & events ) {
748 strcat( ret, chrtostr(sep) );
749 strcat( ret,
"ONESHOT" );
753 if (ret[0] ==
'\0') {
754 niceassert( -1 != sprintf( ret,
"%c0x%08x", sep, events ), 0 );
781 niceassert( init,
"inotifytools_initialize not called yet" );
782 watch *w = watch_from_wd(wd);
804 niceassert( init,
"inotifytools_initialize not called yet" );
805 watch *w = watch_from_filename(filename);
825 niceassert( init,
"inotifytools_initialize not called yet" );
826 watch *w = watch_from_wd(wd);
828 if (w->filename) free(w->filename);
829 w->filename = strdup(filename);
847 char const * newname ) {
848 watch *w = watch_from_filename(oldname);
850 if (w->filename) free(w->filename);
851 w->filename = strdup(newname);
877 char const * newname ) {
878 if ( !oldname || !newname )
return;
879 struct replace_filename_data data;
880 data.old_name = oldname;
881 data.new_name = newname;
882 data.old_len = strlen(oldname);
883 rbwalk(tree_filename, (
void *)replace_filename, (
void *)&data);
889 int remove_inotify_watch(watch *w) {
891 int status = inotify_rm_watch( inotify_fd, w->wd );
893 fprintf(stderr,
"Failed to remove watch on %s: %s\n", w->filename,
904 watch *create_watch(
int wd,
char *filename) {
905 if ( wd <= 0 || !filename)
return 0;
907 watch *w = (watch*)calloc(1,
sizeof(watch));
909 w->filename = strdup(filename);
910 rbsearch(w, tree_wd);
911 rbsearch(w, tree_filename);
928 niceassert( init,
"inotifytools_initialize not called yet" );
929 watch *w = watch_from_wd(wd);
932 if (!remove_inotify_watch(w))
return 0;
933 rbdelete(w, tree_wd);
934 rbdelete(w, tree_filename);
951 niceassert( init,
"inotifytools_initialize not called yet" );
952 watch *w = watch_from_filename(filename);
955 if (!remove_inotify_watch(w))
return 0;
956 rbdelete(w, tree_wd);
957 rbdelete(w, tree_filename);
974 static char const * filenames[2];
975 filenames[0] = filename;
996 niceassert( init,
"inotifytools_initialize not called yet" );
1000 for ( i = 0; filenames[i]; ++i ) {
1002 wd = inotify_add_watch( inotify_fd, filenames[i], events );
1009 fprintf( stderr,
"Failed to watch %s: returned wd was %d "
1010 "(expected -1 or >0 )", filenames[i], wd );
1018 if ( !isdir(filenames[i])
1019 || filenames[i][strlen(filenames[i])-1] ==
'/') {
1020 filename = strdup(filenames[i]);
1023 nasprintf( &filename,
"%s/", filenames[i] );
1025 create_watch(wd, filename);
1112 niceassert( init,
"inotifytools_initialize not called yet" );
1113 niceassert( num_events <= MAX_EVENTS,
"too many events requested" );
1115 if ( num_events < 1 )
return NULL;
1117 static struct inotify_event event[MAX_EVENTS];
1118 static struct inotify_event * ret;
1119 static int first_byte = 0;
1120 static ssize_t bytes;
1122 static char match_name[MAX_STRLEN];
1124 #define RETURN(A) {\
1126 inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\
1127 if (0 == regexec(regex, match_name, 0, 0, 0)) {\
1128 if (!invert_regexp)\
1135 if ( collect_stats ) {\
1146 if ( first_byte != 0
1147 && first_byte <= (
int)(bytes -
sizeof(
struct inotify_event)) ) {
1149 ret = (
struct inotify_event *)((
char *)&
event[0] + first_byte);
1150 first_byte +=
sizeof(
struct inotify_event) + ret->len;
1154 if ( first_byte == bytes ) {
1157 else if ( first_byte > bytes ) {
1164 niceassert( (
long)((
char *)&event[0] +
1165 sizeof(
struct inotify_event) +
1166 event[0].len) <= (
long)ret,
1167 "extremely unlucky user, death imminent" );
1169 bytes = (
char *)&event[0] + bytes - (
char *)ret;
1170 memcpy( &event[0], ret, bytes );
1177 else if ( first_byte == 0 ) {
1182 static ssize_t this_bytes;
1183 static unsigned int bytes_to_read;
1185 static fd_set read_fds;
1187 static struct timeval read_timeout;
1188 read_timeout.tv_sec = timeout;
1189 read_timeout.tv_usec = 0;
1190 static struct timeval * read_timeout_ptr;
1191 read_timeout_ptr = ( timeout < 0 ? NULL : &read_timeout );
1194 FD_SET(inotify_fd, &read_fds);
1195 rc = select(inotify_fd + 1, &read_fds,
1196 NULL, NULL, read_timeout_ptr);
1202 else if ( rc == 0 ) {
1209 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
1211 bytes_to_read <
sizeof(
struct inotify_event)*num_events );
1218 this_bytes = read(inotify_fd, &event[0] + bytes,
1219 sizeof(
struct inotify_event)*MAX_EVENTS - bytes);
1220 if ( this_bytes < 0 ) {
1224 if ( this_bytes == 0 ) {
1225 fprintf(stderr,
"Inotify reported end-of-file. Possibly too many "
1226 "events occurred at once.\n");
1229 bytes += this_bytes;
1232 first_byte =
sizeof(
struct inotify_event) + ret->len;
1233 niceassert( first_byte <= bytes,
"ridiculously long filename, things will "
1234 "almost certainly screw up." );
1235 if ( first_byte == bytes ) {
1306 char const ** exclude_list ) {
1307 niceassert( init,
"inotifytools_initialize not called yet" );
1312 dir = opendir( path );
1315 if ( errno == ENOTDIR ) {
1324 if ( path[strlen(path)-1] !=
'/' ) {
1325 nasprintf( &my_path,
"%s/", path );
1328 my_path = (
char *)path;
1331 static struct dirent * ent;
1333 static struct stat64 my_stat;
1334 ent = readdir( dir );
1337 if ( (0 != strcmp( ent->d_name,
"." )) &&
1338 (0 != strcmp( ent->d_name,
".." )) ) {
1339 nasprintf(&next_file,
"%s%s", my_path, ent->d_name);
1340 if ( -1 == lstat64( next_file, &my_stat ) ) {
1343 if ( errno != EACCES ) {
1345 if ( my_path != path ) free( my_path );
1350 else if ( S_ISDIR( my_stat.st_mode ) &&
1351 !S_ISLNK( my_stat.st_mode )) {
1353 nasprintf(&next_file,
"%s%s/", my_path, ent->d_name);
1354 static unsigned int no_watch;
1355 static char const ** exclude_entry;
1358 for (exclude_entry = exclude_list;
1359 exclude_entry && *exclude_entry && !no_watch;
1361 static int exclude_length;
1363 exclude_length = strlen(*exclude_entry);
1364 if ((*exclude_entry)[exclude_length-1] ==
'/') {
1367 if ( strlen(next_file) == (
unsigned)(exclude_length + 1) &&
1368 !strncmp(*exclude_entry, next_file, exclude_length)) {
1380 if ( !status && (EACCES != error) && (ENOENT != error) &&
1381 (ELOOP != error) ) {
1383 if ( my_path != path ) free( my_path );
1394 ent = readdir( dir );
1401 if ( my_path != path ) free( my_path );
1408 void record_stats(
struct inotify_event
const * event ) {
1410 watch *w = watch_from_wd(event->wd);
1412 if ( IN_ACCESS & event->mask ) {
1416 if ( IN_MODIFY & event->mask ) {
1420 if ( IN_ATTRIB & event->mask ) {
1424 if ( IN_CLOSE_WRITE & event->mask ) {
1425 ++w->hit_close_write;
1428 if ( IN_CLOSE_NOWRITE & event->mask ) {
1429 ++w->hit_close_nowrite;
1430 ++num_close_nowrite;
1432 if ( IN_OPEN & event->mask ) {
1436 if ( IN_MOVED_FROM & event->mask ) {
1437 ++w->hit_moved_from;
1440 if ( IN_MOVED_TO & event->mask ) {
1444 if ( IN_CREATE & event->mask ) {
1448 if ( IN_DELETE & event->mask ) {
1452 if ( IN_DELETE_SELF & event->mask ) {
1453 ++w->hit_delete_self;
1456 if ( IN_UNMOUNT & event->mask ) {
1460 if ( IN_MOVE_SELF & event->mask ) {
1470 unsigned int *stat_ptr(watch *w,
int event)
1472 if ( IN_ACCESS == event )
1473 return &w->hit_access;
1474 if ( IN_MODIFY == event )
1475 return &w->hit_modify;
1476 if ( IN_ATTRIB == event )
1477 return &w->hit_attrib;
1478 if ( IN_CLOSE_WRITE == event )
1479 return &w->hit_close_write;
1480 if ( IN_CLOSE_NOWRITE == event )
1481 return &w->hit_close_nowrite;
1482 if ( IN_OPEN == event )
1483 return &w->hit_open;
1484 if ( IN_MOVED_FROM == event )
1485 return &w->hit_moved_from;
1486 if ( IN_MOVED_TO == event )
1487 return &w->hit_moved_to;
1488 if ( IN_CREATE == event )
1489 return &w->hit_create;
1490 if ( IN_DELETE == event )
1491 return &w->hit_delete;
1492 if ( IN_DELETE_SELF == event )
1493 return &w->hit_delete_self;
1494 if ( IN_UNMOUNT == event )
1495 return &w->hit_unmount;
1496 if ( IN_MOVE_SELF == event )
1497 return &w->hit_move_self;
1499 return &w->hit_total;
1519 if (!collect_stats)
return -1;
1521 watch *w = watch_from_wd(wd);
1523 unsigned int *i = stat_ptr(w, event);
1542 if (!collect_stats)
return -1;
1543 if ( IN_ACCESS == event )
1545 if ( IN_MODIFY == event )
1547 if ( IN_ATTRIB == event )
1549 if ( IN_CLOSE_WRITE == event )
1550 return num_close_write;
1551 if ( IN_CLOSE_NOWRITE == event )
1552 return num_close_nowrite;
1553 if ( IN_OPEN == event )
1555 if ( IN_MOVED_FROM == event )
1556 return num_moved_from;
1557 if ( IN_MOVED_TO == event )
1558 return num_moved_to;
1559 if ( IN_CREATE == event )
1561 if ( IN_DELETE == event )
1563 if ( IN_DELETE_SELF == event )
1564 return num_delete_self;
1565 if ( IN_UNMOUNT == event )
1567 if ( IN_MOVE_SELF == event )
1568 return num_move_self;
1598 filename ), event );
1618 static int isdir(
char const * path ) {
1619 static struct stat64 my_stat;
1621 if ( -1 == lstat64( path, &my_stat ) ) {
1622 if (errno == ENOENT)
return 0;
1623 fprintf(stderr,
"Stat failed on %s: %s\n", path, strerror(errno));
1627 return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
1639 rbwalk(tree_filename, get_num, (
void*)&ret);
1729 static char out[MAX_STRLEN+1];
1732 if ( -1 != ret ) fprintf( file,
"%s", out );
1838 struct inotify_event* event,
char* fmt ) {
1839 static char * filename, * eventname, * eventstr;
1840 static unsigned int i, ind;
1842 static char timestr[MAX_STRLEN];
1846 if ( event->len > 0 ) {
1847 eventname =
event->name;
1856 if ( !fmt || 0 == strlen(fmt) ) {
1860 if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
1866 for ( i = 0; i < strlen(fmt) &&
1867 (int)ind < size - 1; ++i ) {
1868 if ( fmt[i] !=
'%' ) {
1869 out[ind++] = fmt[i];
1873 if ( i == strlen(fmt) - 1 ) {
1889 strncpy( &out[ind], filename, size - ind );
1890 ind += strlen(filename);
1898 strncpy( &out[ind], eventname, size - ind );
1899 ind += strlen(eventname);
1907 strncpy( &out[ind], eventstr, size - ind );
1908 ind += strlen(eventstr);
1918 if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
1919 localtime( &now ) ) ) {
1930 strncpy( &out[ind], timestr, size - ind );
1931 ind += strlen(timestr);
1937 if ( i < strlen(fmt) - 2 && fmt[i+2] ==
'e' ) {
1939 strncpy( &out[ind], eventstr, size - ind );
1940 ind += strlen(eventstr);
1946 if ( ind < MAX_STRLEN ) out[ind++] =
'%';
1947 if ( ind < MAX_STRLEN ) out[ind++] = ch1;
1978 if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) )
return -1;
1993 if ( !read_num_from_file( INSTANCES_PATH, &ret ) )
return -1;
2008 if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) )
return -1;
2025 static int do_ignore_events_by_regex(
char const *pattern,
int flags,
int invert ) {
2035 if (regex) { regfree(regex); }
2036 else { regex = (regex_t *)malloc(
sizeof(regex_t)); }
2038 invert_regexp = invert;
2039 int ret = regcomp(regex, pattern, flags | REG_NOSUB);
2040 if (0 == ret)
return 1;
2061 return do_ignore_events_by_regex(pattern, flags, 0);
2076 return do_ignore_events_by_regex(pattern, flags, 1);
2079 int event_compare(
const void *p1,
const void *p2,
const void *config)
2081 if (!p1 || !p2)
return p1 - p2;
2083 long sort_event = (long)config;
2084 if (sort_event == -1) {
2087 }
else if (sort_event < 0) {
2088 sort_event = -sort_event;
2091 unsigned int *i1 = stat_ptr((watch*)p1, sort_event);
2092 unsigned int *i2 = stat_ptr((watch*)p2, sort_event);
2093 if (0 == *i1 - *i2) {
2094 return ((watch*)p1)->wd - ((watch*)p2)->wd;
2102 struct rbtree *inotifytools_wd_sorted_by_event(
int sort_event)
2104 struct rbtree *ret = rbinit(event_compare, (
void*)(uintptr_t)sort_event);
2105 RBLIST *all = rbopenlist(tree_wd);
2106 void const *p = rbreadlist(all);
2108 void const *r = rbsearch(p, ret);
2109 niceassert((
int)(r == p),
"Couldn't insert watch into new tree");
2110 p = rbreadlist(all);