diff options
Diffstat (limited to 'inotify-info.cpp')
-rw-r--r-- | inotify-info.cpp | 867 |
1 files changed, 393 insertions, 474 deletions
diff --git a/inotify-info.cpp b/inotify-info.cpp index 71ed3a1..ae5e61e 100644 --- a/inotify-info.cpp +++ b/inotify-info.cpp @@ -24,34 +24,38 @@ #define _GNU_SOURCE 1 -#include <limits.h> #include <dirent.h> #include <fcntl.h> #include <getopt.h> +#include <libgen.h> +#include <limits.h> +#include <locale.h> #include <pthread.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <libgen.h> -#include <locale.h> #include <sys/stat.h> +#include <sys/statfs.h> +#include <sys/sysmacros.h> #include <sys/types.h> #include <syscall.h> #include <unistd.h> -#include <sys/sysmacros.h> -#include <sys/statfs.h> +#include <algorithm> #include <cstdint> #include <string> -#include <vector> -#include <algorithm> -#include <unordered_set> #include <unordered_map> +#include <unordered_set> +#include <vector> #include "inotify-info.h" #include "lfqueue/lfqueue.h" +#ifndef INOTIFYINFO_VERSION +#error INOTIFYINFO_VERSION must be set +#endif + /* * TODO * - Comments @@ -72,23 +76,21 @@ static int g_kernel_provides_watches_info = 0; static char thousands_sep = ','; -static std::vector< std::string > ignore_dirs; +static std::vector<std::string> ignore_dirs; /* * filename info */ -struct filename_info_t -{ - ino64_t inode; // Inode number - dev_t dev; // Device ID containing file +struct filename_info_t { + ino64_t inode; // Inode number + dev_t dev; // Device ID containing file std::string filename; }; /* * inotify process info */ -struct procinfo_t -{ +struct procinfo_t { pid_t pid = 0; // uid @@ -108,26 +110,24 @@ struct procinfo_t std::string appname; // Inotify fdset filenames - std::vector< std::string > fdset_filenames; + std::vector<std::string> fdset_filenames; // Device id map -> set of inodes for that device id - std::unordered_map< dev_t, std::unordered_set< ino64_t > > dev_map; + std::unordered_map<dev_t, std::unordered_set<ino64_t>> dev_map; }; -class lfqueue_wrapper_t -{ +class lfqueue_wrapper_t { public: - lfqueue_wrapper_t() { lfqueue_init( &queue ); } - ~lfqueue_wrapper_t() { lfqueue_destroy( &queue ); } + lfqueue_wrapper_t() { lfqueue_init(&queue); } + ~lfqueue_wrapper_t() { lfqueue_destroy(&queue); } - void queue_directory( char *path ) { lfqueue_enq( &queue, path ); } - char *dequeue_directory() { return ( char * )lfqueue_deq( &queue ); } + void queue_directory(char* path) { lfqueue_enq(&queue, path); } + char* dequeue_directory() { return (char*)lfqueue_deq(&queue); } public: typedef long long my_m256i __attribute__((__vector_size__(32), __aligned__(32))); - union - { + union { lfqueue_t queue; my_m256i align_buf[4]; // Align to 128 bytes }; @@ -136,141 +136,135 @@ public: /* * shared thread data */ -class thread_shared_data_t -{ +class thread_shared_data_t { public: - bool init( uint32_t numthreads, const std::vector< procinfo_t > &inotify_proclist ); + bool init(uint32_t numthreads, const std::vector<procinfo_t>& inotify_proclist); public: // Array of queues - one per thread - std::vector< lfqueue_wrapper_t > dirqueues; + std::vector<lfqueue_wrapper_t> dirqueues; // Map of all inotify inodes watched to the set of devices they are on - std::unordered_map< ino64_t, std::unordered_set< dev_t > > inode_set; + std::unordered_map<ino64_t, std::unordered_set<dev_t>> inode_set; }; /* * thread info */ -class thread_info_t -{ +class thread_info_t { public: - thread_info_t( thread_shared_data_t &tdata_in ) : tdata( tdata_in ) {} - ~thread_info_t() {} + thread_info_t(thread_shared_data_t& tdata_in) + : tdata(tdata_in) + { + } + ~thread_info_t() { } - void queue_directory( char *path ); - char *dequeue_directory(); + void queue_directory(char* path); + char* dequeue_directory(); // Returns -1: queue empty, 0: open error, > 0 success int parse_dirqueue_entry(); - void add_filename( ino64_t inode, const char *path, const char *d_name, bool is_dir ); + void add_filename(ino64_t inode, const char* path, const char* d_name, bool is_dir); public: uint32_t idx = 0; pthread_t pthread_id = 0; - thread_shared_data_t &tdata; + thread_shared_data_t& tdata; // Total dirs scanned by this thread uint32_t scanned_dirs = 0; // Files found by this thread - std::vector< filename_info_t > found_files; + std::vector<filename_info_t> found_files; }; /* * getdents64 syscall */ -GCC_DIAG_PUSH_OFF( pedantic ) -struct linux_dirent64 -{ - uint64_t d_ino; // Inode number - int64_t d_off; // Offset to next linux_dirent +GCC_DIAG_PUSH_OFF(pedantic) +struct linux_dirent64 { + uint64_t d_ino; // Inode number + int64_t d_off; // Offset to next linux_dirent unsigned short d_reclen; // Length of this linux_dirent - unsigned char d_type; // File type - char d_name[]; // Filename (null-terminated) + unsigned char d_type; // File type + char d_name[]; // Filename (null-terminated) }; GCC_DIAG_POP() -int sys_getdents64( int fd, char *dirp, int count ) +int sys_getdents64(int fd, char* dirp, int count) { - return syscall( SYS_getdents64, fd, dirp, count ); + return syscall(SYS_getdents64, fd, dirp, count); } static double gettime() { struct timespec ts; - clock_gettime( CLOCK_MONOTONIC, &ts ); - return ( double )ts.tv_sec + ( double )ts.tv_nsec / 1e9; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9; } -std::string string_formatv( const char *fmt, va_list ap ) +std::string string_formatv(const char* fmt, va_list ap) { std::string str; int size = 512; - for ( ;; ) - { - str.resize( size ); - int n = vsnprintf( ( char * )str.c_str(), size, fmt, ap ); + for (;;) { + str.resize(size); + int n = vsnprintf((char*)str.c_str(), size, fmt, ap); - if ( ( n > -1 ) && ( n < size ) ) - { - str.resize( n ); + if ((n > -1) && (n < size)) { + str.resize(n); return str; } - size = ( n > -1 ) ? ( n + 1 ) : ( size * 2 ); + size = (n > -1) ? (n + 1) : (size * 2); } } -std::string string_format( const char *fmt, ... ) +std::string string_format(const char* fmt, ...) { va_list ap; std::string str; - va_start( ap, fmt ); - str = string_formatv( fmt, ap ); - va_end( ap ); + va_start(ap, fmt); + str = string_formatv(fmt, ap); + va_end(ap); return str; } -static std::string get_link_name( const char *pathname ) +static std::string get_link_name(const char* pathname) { std::string Result; - char filename[ PATH_MAX + 1 ]; + char filename[PATH_MAX + 1]; - ssize_t ret = readlink( pathname, filename, sizeof( filename ) ); - if ( ( ret > 0 ) && ( ret < ( ssize_t )sizeof( filename ) ) ) - { - filename[ ret ] = 0; + ssize_t ret = readlink(pathname, filename, sizeof(filename)); + if ((ret > 0) && (ret < (ssize_t)sizeof(filename))) { + filename[ret] = 0; Result = filename; } return Result; } -static uid_t get_uid(const char *pathname) +static uid_t get_uid(const char* pathname) { - int fd = open( pathname, O_RDONLY, 0 ); + int fd = open(pathname, O_RDONLY, 0); - if ( fd >= 0 ) - { - char buf[ 16 * 1024 ]; + if (fd >= 0) { + char buf[16 * 1024]; - ssize_t len = read( fd, buf, sizeof( buf ) ); + ssize_t len = read(fd, buf, sizeof(buf)); - close( fd ); + close(fd); fd = -1; - if ( len > 0 ) - { - buf[ len - 1 ] = 0; + if (len > 0) { + buf[len - 1] = 0; - const char *uidstr = strstr( buf, "\nUid:" ); - if ( uidstr ) - { - return atoll( uidstr + 5 ); + const char* uidstr = strstr(buf, "\nUid:"); + if (uidstr) { + return atoll(uidstr + 5); } } } @@ -278,27 +272,25 @@ static uid_t get_uid(const char *pathname) return -1; } -static uint64_t get_token_val( const char *line, const char *token ) +static uint64_t get_token_val(const char* line, const char* token) { - const char *str = strstr( line, token ); + const char* str = strstr(line, token); - return str ? strtoull( str + strlen( token ), nullptr, 16 ) : 0; + return str ? strtoull(str + strlen(token), nullptr, 16) : 0; } -static uint32_t inotify_parse_fdinfo_file( procinfo_t &procinfo, const char *fdset_name ) +static uint32_t inotify_parse_fdinfo_file(procinfo_t& procinfo, const char* fdset_name) { uint32_t watch_count = 0; - FILE *fp = fopen( fdset_name, "r" ); - if ( fp ) - { - char line_buf[ 256 ]; + FILE* fp = fopen(fdset_name, "r"); + if (fp) { + char line_buf[256]; - procinfo.fdset_filenames.push_back( fdset_name ); + procinfo.fdset_filenames.push_back(fdset_name); - for ( ;; ) - { - if ( !fgets( line_buf, sizeof( line_buf ), fp ) ) + for (;;) { + if (!fgets(line_buf, sizeof(line_buf), fp)) break; /* sample fdinfo; inotify line added in linux 3.8, available if @@ -309,15 +301,13 @@ static uint32_t inotify_parse_fdinfo_file( procinfo_t &procinfo, const char *fds * ino: 5865 * inotify wd:1 ino:80001 sdev:800011 mask:100 ignored_mask:0 fhandle-bytes:8 fhandle-type:1 f_handle:01000800bc1b8c7c */ - if ( !strncmp( line_buf, "inotify ", 8 ) ) - { + if (!strncmp(line_buf, "inotify ", 8)) { watch_count++; - uint64_t inode_val = get_token_val( line_buf, "ino:" ); - uint64_t sdev_val = get_token_val( line_buf, "sdev:" ); + uint64_t inode_val = get_token_val(line_buf, "ino:"); + uint64_t sdev_val = get_token_val(line_buf, "sdev:"); - if ( inode_val ) - { + if (inode_val) { // https://unix.stackexchange.com/questions/645937/listing-the-files-that-are-being-watched-by-inotify-instances // Assuming that the sdev field is encoded according to Linux's so-called "huge // encoding", which uses 20 bits (instead of 8) for minor numbers, in bitwise @@ -326,42 +316,39 @@ static uint32_t inotify_parse_fdinfo_file( procinfo_t &procinfo, const char *fds unsigned int minor = sdev_val & 0xfffff; // Add inode to this device map - procinfo.dev_map[ makedev( major, minor ) ].insert( inode_val ); + procinfo.dev_map[makedev(major, minor)].insert(inode_val); } } } - fclose( fp ); + fclose(fp); } return watch_count; } -static void inotify_parse_fddir( procinfo_t &procinfo ) +static void inotify_parse_fddir(procinfo_t& procinfo) { - std::string filename = string_format( "/proc/%d/fd", procinfo.pid ); + std::string filename = string_format("/proc/%d/fd", procinfo.pid); - DIR *dir_fd = opendir( filename.c_str() ); - if ( !dir_fd ) + DIR* dir_fd = opendir(filename.c_str()); + if (!dir_fd) return; - for ( ;; ) - { - struct dirent *dp_fd = readdir( dir_fd ); - if ( !dp_fd ) + for (;;) { + struct dirent* dp_fd = readdir(dir_fd); + if (!dp_fd) break; - if ( ( dp_fd->d_type == DT_LNK ) && isdigit( dp_fd->d_name[ 0 ] ) ) - { - filename = string_format( "/proc/%d/fd/%s", procinfo.pid, dp_fd->d_name ); - filename = get_link_name( filename.c_str() ); + if ((dp_fd->d_type == DT_LNK) && isdigit(dp_fd->d_name[0])) { + filename = string_format("/proc/%d/fd/%s", procinfo.pid, dp_fd->d_name); + filename = get_link_name(filename.c_str()); - if ( filename == "anon_inode:inotify" || filename == "inotify" ) - { - filename = string_format( "/proc/%d/fdinfo/%s", procinfo.pid, dp_fd->d_name ); + if (filename == "anon_inode:inotify" || filename == "inotify") { + filename = string_format("/proc/%d/fdinfo/%s", procinfo.pid, dp_fd->d_name); procinfo.instances++; - procinfo.watches += inotify_parse_fdinfo_file( procinfo, filename.c_str() ); + procinfo.watches += inotify_parse_fdinfo_file(procinfo, filename.c_str()); /* If any watches have been found, enable the stats display */ g_kernel_provides_watches_info |= !!procinfo.watches; @@ -369,25 +356,23 @@ static void inotify_parse_fddir( procinfo_t &procinfo ) } } - closedir( dir_fd ); + closedir(dir_fd); } -void thread_info_t::queue_directory( char *path ) +void thread_info_t::queue_directory(char* path) { - tdata.dirqueues[ idx ].queue_directory( path ); + tdata.dirqueues[idx].queue_directory(path); } -char *thread_info_t::dequeue_directory() +char* thread_info_t::dequeue_directory() { - char *path = tdata.dirqueues[ idx ].dequeue_directory(); + char* path = tdata.dirqueues[idx].dequeue_directory(); - if ( !path ) - { + if (!path) { // Nothing on our queue, check queues on other threads - for ( lfqueue_wrapper_t &dirq : tdata.dirqueues ) - { + for (lfqueue_wrapper_t& dirq : tdata.dirqueues) { path = dirq.dequeue_directory(); - if ( path ) + if (path) break; } } @@ -396,59 +381,56 @@ char *thread_info_t::dequeue_directory() } // statx() was added to Linux in kernel 4.11; library support was added in glibc 2.28. -#if defined( __linux__ ) && ( ( __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 28 ) || ( __GLIBC__ > 2 ) ) +#if defined(__linux__) && ((__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 28) || (__GLIBC__ > 2)) -struct statx mystatx( const char *filename, unsigned int mask = 0 ) +struct statx mystatx(const char* filename, unsigned int mask = 0) { struct statx statxbuf; int flags = AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW | AT_STATX_DONT_SYNC; - if ( statx( 0, filename, flags, mask, &statxbuf ) == -1 ) - { - printf( "ERROR: statx-ino( %s ) failed. Errno: %d\n", filename, errno ); - memset( &statxbuf, 0, sizeof( statxbuf ) ); + if (statx(0, filename, flags, mask, &statxbuf) == -1) { + printf("ERROR: statx-ino( %s ) failed. Errno: %d\n", filename, errno); + memset(&statxbuf, 0, sizeof(statxbuf)); } return statxbuf; } -static dev_t stat_get_dev_t( const char *filename ) +static dev_t stat_get_dev_t(const char* filename) { - struct statx statxbuf = mystatx( filename ); + struct statx statxbuf = mystatx(filename); - return makedev( statxbuf.stx_dev_major, statxbuf.stx_dev_minor ); + return makedev(statxbuf.stx_dev_major, statxbuf.stx_dev_minor); } -static uint64_t stat_get_ino( const char *filename ) +static uint64_t stat_get_ino(const char* filename) { - return mystatx( filename, STATX_INO ).stx_ino; + return mystatx(filename, STATX_INO).stx_ino; } #else // Fall back to using stat() functions. Should work but be slower than using statx(). -static dev_t stat_get_dev_t( const char *filename ) +static dev_t stat_get_dev_t(const char* filename) { struct stat statbuf; - int ret = stat( filename, &statbuf ); - if ( ret == -1 ) - { - printf( "ERROR: stat-dev_t( %s ) failed. Errno: %d\n", filename, errno ); + int ret = stat(filename, &statbuf); + if (ret == -1) { + printf("ERROR: stat-dev_t( %s ) failed. Errno: %d\n", filename, errno); return 0; } return statbuf.st_dev; } -static uint64_t stat_get_ino( const char *filename ) +static uint64_t stat_get_ino(const char* filename) { struct stat statbuf; - int ret = stat( filename, &statbuf ); - if ( ret == -1 ) - { - printf( "ERROR: stat-ino( %s ) failed. Errno: %d\n", filename, errno ); + int ret = stat(filename, &statbuf); + if (ret == -1) { + printf("ERROR: stat-ino( %s ) failed. Errno: %d\n", filename, errno); return 0; } @@ -457,39 +439,36 @@ static uint64_t stat_get_ino( const char *filename ) #endif -void thread_info_t::add_filename( ino64_t inode, const char *path, const char *d_name, bool is_dir ) +void thread_info_t::add_filename(ino64_t inode, const char* path, const char* d_name, bool is_dir) { - auto it = tdata.inode_set.find( inode ); + auto it = tdata.inode_set.find(inode); - if ( it != tdata.inode_set.end() ) - { - const std::unordered_set< dev_t > &dev_set = it->second; + if (it != tdata.inode_set.end()) { + const std::unordered_set<dev_t>& dev_set = it->second; - std::string filename = std::string( path ) + d_name; - dev_t dev = stat_get_dev_t( filename.c_str() ); + std::string filename = std::string(path) + d_name; + dev_t dev = stat_get_dev_t(filename.c_str()); // Make sure the inode AND device ID match before adding. - if ( dev_set.find( dev ) != dev_set.end() ) - { + if (dev_set.find(dev) != dev_set.end()) { filename_info_t fname; fname.filename = is_dir ? filename + "/" : filename; fname.inode = inode; fname.dev = dev; - found_files.push_back( fname ); + found_files.push_back(fname); } } } -static bool is_dot_dir( const char *dname ) +static bool is_dot_dir(const char* dname) { - if ( dname[ 0 ] == '.' ) - { - if ( !dname[ 1 ] ) + if (dname[0] == '.') { + if (!dname[1]) return true; - if ( ( dname[ 1 ] == '.' ) && !dname[ 2 ] ) + if ((dname[1] == '.') && !dname[2]) return true; } @@ -497,24 +476,22 @@ static bool is_dot_dir( const char *dname ) } // From "linux/magic.h" -#define PROC_SUPER_MAGIC 0x9fa0 -#define SMB_SUPER_MAGIC 0x517B -#define CIFS_SUPER_MAGIC 0xFF534D42 /* the first four bytes of SMB PDUs */ -#define SMB2_SUPER_MAGIC 0xFE534D42 -#define FUSE_SUPER_MAGIC 0x65735546 +#define PROC_SUPER_MAGIC 0x9fa0 +#define SMB_SUPER_MAGIC 0x517B +#define CIFS_SUPER_MAGIC 0xFF534D42 /* the first four bytes of SMB PDUs */ +#define SMB2_SUPER_MAGIC 0xFE534D42 +#define FUSE_SUPER_MAGIC 0x65735546 // Detect proc and fuse directories and skip them. // https://github.com/mikesart/inotify-info/issues/6 // Could use setmntent("/proc/mounts", "r") + getmntent if speed is an issue? -static bool is_proc_dir( const char *path, const char *d_name ) +static bool is_proc_dir(const char* path, const char* d_name) { struct statfs s; - std::string filename = std::string( path ) + d_name; + std::string filename = std::string(path) + d_name; - if ( statfs(filename.c_str(), &s ) == 0 ) - { - switch ( s.f_type ) - { + if (statfs(filename.c_str(), &s) == 0) { + switch (s.f_type) { case PROC_SUPER_MAGIC: case FUSE_SUPER_MAGIC: return true; @@ -527,65 +504,55 @@ static bool is_proc_dir( const char *path, const char *d_name ) // Returns -1: queue empty, 0: open error, > 0 success int thread_info_t::parse_dirqueue_entry() { - char __attribute__( ( aligned( 16 ) ) ) buf[ 1024 ]; + char __attribute__((aligned(16))) buf[1024]; - char *path = dequeue_directory(); - if ( !path ) - { + char* path = dequeue_directory(); + if (!path) { return -1; } - for ( std::string &dname : ignore_dirs ) - { - if ( dname == path ) - { - if ( g_verbose > 1 ) - { - printf( "Ignoring '%s'\n", path ); + for (std::string& dname : ignore_dirs) { + if (dname == path) { + if (g_verbose > 1) { + printf("Ignoring '%s'\n", path); } return 0; } } - int fd = open( path, O_RDONLY | O_DIRECTORY, 0 ); - if ( fd < 0 ) - { - free( path ); + int fd = open(path, O_RDONLY | O_DIRECTORY, 0); + if (fd < 0) { + free(path); return 0; } scanned_dirs++; - size_t pathlen = strlen( path ); + size_t pathlen = strlen(path); - for ( ;; ) - { - int ret = sys_getdents64( fd, buf, sizeof( buf ) ); + for (;;) { + int ret = sys_getdents64(fd, buf, sizeof(buf)); - if ( ret < 0 ) - { + if (ret < 0) { bool spew_error = true; - if ( ( errno == 5 ) && !strncmp( path, "/sys/kernel/", 12 ) ) - { + if ((errno == 5) && !strncmp(path, "/sys/kernel/", 12)) { // In docker container we can get permission denied errors in /sys/kernel. Ignore them. // https://github.com/mikesart/inotify-info/issues/16 spew_error = false; } - if ( spew_error ) - { - printf( "ERROR: sys_getdents64 failed on '%s': %d errno:%d\n", path, ret, errno ); + if (spew_error) { + printf("ERROR: sys_getdents64 failed on '%s': %d errno:%d\n", path, ret, errno); } break; } - if ( ret == 0 ) + if (ret == 0) break; - for ( int bpos = 0; bpos < ret; ) - { - struct linux_dirent64 *dirp = ( struct linux_dirent64 * )( buf + bpos ); - const char *d_name = dirp->d_name; + for (int bpos = 0; bpos < ret;) { + struct linux_dirent64* dirp = (struct linux_dirent64*)(buf + bpos); + const char* d_name = dirp->d_name; // DT_BLK This is a block device. // DT_CHR This is a character device. @@ -595,28 +562,24 @@ int thread_info_t::parse_dirqueue_entry() // DT_REG This is a regular file. // DT_LNK This is a symbolic link. - if ( dirp->d_type == DT_REG || dirp->d_type == DT_LNK ) - { - add_filename( dirp->d_ino, path, d_name, false ); + if (dirp->d_type == DT_REG || dirp->d_type == DT_LNK) { + add_filename(dirp->d_ino, path, d_name, false); } // DT_DIR This is a directory. - else if ( dirp->d_type == DT_DIR ) - { - if ( !is_dot_dir( d_name ) && !is_proc_dir( path, d_name ) ) - { - add_filename( dirp->d_ino, path, d_name, true ); - - size_t len = strlen( d_name ); - char *newpath = ( char * )malloc( pathlen + len + 2 ); - - if ( newpath ) - { - strcpy( newpath, path ); - strcpy( newpath + pathlen, d_name ); - newpath[ pathlen + len ] = '/'; - newpath[ pathlen + len + 1 ] = 0; - - queue_directory( newpath ); + else if (dirp->d_type == DT_DIR) { + if (!is_dot_dir(d_name) && !is_proc_dir(path, d_name)) { + add_filename(dirp->d_ino, path, d_name, true); + + size_t len = strlen(d_name); + char* newpath = (char*)malloc(pathlen + len + 2); + + if (newpath) { + strcpy(newpath, path); + strcpy(newpath + pathlen, d_name); + newpath[pathlen + len] = '/'; + newpath[pathlen + len + 1] = 0; + + queue_directory(newpath); } } } @@ -625,107 +588,97 @@ int thread_info_t::parse_dirqueue_entry() } } - close( fd ); - free( path ); + close(fd); + free(path); return 1; } -static void *parse_dirqueue_threadproc( void *arg ) +static void* parse_dirqueue_threadproc(void* arg) { - thread_info_t *pthread_info = ( thread_info_t * )arg; + thread_info_t* pthread_info = (thread_info_t*)arg; - for ( ;; ) - { + for (;;) { // Loop until all the dequeue(s) fail - if ( pthread_info->parse_dirqueue_entry() == -1 ) + if (pthread_info->parse_dirqueue_entry() == -1) break; } return nullptr; } -static bool is_proc_in_cmdline_applist( const procinfo_t &procinfo, std::vector< std::string > &cmdline_applist ) +static bool is_proc_in_cmdline_applist(const procinfo_t& procinfo, std::vector<std::string>& cmdline_applist) { - for ( const std::string &str : cmdline_applist ) - { + for (const std::string& str : cmdline_applist) { // Check if our command line string is a subset of this appname - if ( strstr( procinfo.appname.c_str(), str.c_str() ) ) + if (strstr(procinfo.appname.c_str(), str.c_str())) return true; // Check if the PIDs match - if ( atoll( str.c_str() ) == procinfo.pid ) + if (atoll(str.c_str()) == procinfo.pid) return true; } return false; } -static bool watch_count_is_greater ( procinfo_t elem1, procinfo_t elem2 ) +static bool watch_count_is_greater(procinfo_t elem1, procinfo_t elem2) { - return elem1.watches > elem2.watches; + return elem1.watches > elem2.watches; } - -static bool init_inotify_proclist( std::vector< procinfo_t > &inotify_proclist ) +static bool init_inotify_proclist(std::vector<procinfo_t>& inotify_proclist) { - DIR *dir_proc = opendir( "/proc" ); + DIR* dir_proc = opendir("/proc"); - if ( !dir_proc ) - { - printf( "ERROR: opendir /proc failed: %d\n", errno ); + if (!dir_proc) { + printf("ERROR: opendir /proc failed: %d\n", errno); return false; } - for ( ;; ) - { - struct dirent *dp_proc = readdir( dir_proc ); - if ( !dp_proc ) + for (;;) { + struct dirent* dp_proc = readdir(dir_proc); + if (!dp_proc) break; - if ( ( dp_proc->d_type == DT_DIR ) && isdigit( dp_proc->d_name[ 0 ] ) ) - { + if ((dp_proc->d_type == DT_DIR) && isdigit(dp_proc->d_name[0])) { procinfo_t procinfo; - procinfo.pid = atoll( dp_proc->d_name ); + procinfo.pid = atoll(dp_proc->d_name); - std::string executable = string_format( "/proc/%d/exe", procinfo.pid ); - std::string status = string_format( "/proc/%d/status", procinfo.pid ); - procinfo.uid = get_uid( status.c_str() ); - procinfo.executable = get_link_name( executable.c_str() ); - if ( !procinfo.executable.empty() ) - { - procinfo.appname = basename( (char*)procinfo.executable.c_str() ); + std::string executable = string_format("/proc/%d/exe", procinfo.pid); + std::string status = string_format("/proc/%d/status", procinfo.pid); + procinfo.uid = get_uid(status.c_str()); + procinfo.executable = get_link_name(executable.c_str()); + if (!procinfo.executable.empty()) { + procinfo.appname = basename((char*)procinfo.executable.c_str()); - inotify_parse_fddir( procinfo ); + inotify_parse_fddir(procinfo); - if ( procinfo.instances ) - { - inotify_proclist.push_back( procinfo ); + if (procinfo.instances) { + inotify_proclist.push_back(procinfo); } } } } std::sort(inotify_proclist.begin(), inotify_proclist.end(), watch_count_is_greater); - closedir( dir_proc ); + closedir(dir_proc); return true; } // From: // https://stackoverflow.com/questions/1449805/how-to-format-a-number-using-comma-as-thousands-separator-in-c -size_t str_format_uint32( char dst[16], uint32_t num ) +size_t str_format_uint32(char dst[16], uint32_t num) { - if ( thousands_sep ) - { + if (thousands_sep) { char src[16]; - char *p_src = src; - char *p_dst = dst; + char* p_src = src; + char* p_dst = dst; int num_len, commas; num_len = sprintf(src, "%u", num); - for (commas = 2 - num_len % 3; *p_src; commas = (commas + 1) % 3) - { + for (commas = 2 - num_len % 3; *p_src; commas = (commas + 1) % 3) { *p_dst++ = *p_src++; if (commas == 1) { *p_dst++ = thousands_sep; @@ -739,7 +692,7 @@ size_t str_format_uint32( char dst[16], uint32_t num ) return sprintf(dst, "%u", num); } -static void print_inotify_proclist( std::vector< procinfo_t > &inotify_proclist ) +static void print_inotify_proclist(std::vector<procinfo_t>& inotify_proclist) { #if 0 // test data @@ -775,192 +728,170 @@ static void print_inotify_proclist( std::vector< procinfo_t > &inotify_proclist int lenWatches = 8; int lenInstances = 10; - for ( procinfo_t &procinfo : inotify_proclist ) - lenApp = std::max<int>( procinfo.appname.length(), lenApp ); + for (procinfo_t& procinfo : inotify_proclist) + lenApp = std::max<int>(procinfo.appname.length(), lenApp); /* If the number of watches is negative, the kernel doesn't support this info. omit the header*/ if (g_kernel_provides_watches_info) - printf( "%s%*s %-*s %-*s %*s %*s%s\n", + printf("%s%*s %-*s %-*s %*s %*s%s\n", BCYAN, lenPid, "Pid", lenUid, "Uid", lenApp, "App", lenWatches, "Watches", lenInstances, "Instances", RESET); else - printf( "%s%*s %-*s %*s %*s%s\n", + printf("%s%*s %-*s %*s %*s%s\n", BCYAN, lenPid, "Pid", lenUid, "Uid", lenApp, "App", lenInstances, "Instances", RESET); - - for ( procinfo_t &procinfo : inotify_proclist ) - { + for (procinfo_t& procinfo : inotify_proclist) { char watches_str[16]; str_format_uint32(watches_str, procinfo.watches); if (g_kernel_provides_watches_info) - printf( "%*d %-*d %s%-*s%s %*s %*u\n", + printf("%*d %-*d %s%-*s%s %*s %*u\n", lenPid, procinfo.pid, lenUid, procinfo.uid, BYELLOW, lenApp, procinfo.appname.c_str(), RESET, lenWatches, watches_str, - lenInstances, procinfo.instances ); + lenInstances, procinfo.instances); else - printf( "%*d %-*d %s%-*s%s %*u\n", + printf("%*d %-*d %s%-*s%s %*u\n", lenPid, procinfo.pid, lenUid, procinfo.uid, BYELLOW, lenApp, procinfo.appname.c_str(), RESET, - lenInstances, procinfo.instances ); + lenInstances, procinfo.instances); - if ( g_verbose > 1 ) - { - for ( std::string &fname : procinfo.fdset_filenames ) - { - printf( " %s%s%s\n", CYAN, fname.c_str(), RESET ); + if (g_verbose > 1) { + for (std::string& fname : procinfo.fdset_filenames) { + printf(" %s%s%s\n", CYAN, fname.c_str(), RESET); } } - if ( procinfo.in_cmd_line ) - { - for ( const auto &it1 : procinfo.dev_map ) - { + if (procinfo.in_cmd_line) { + for (const auto& it1 : procinfo.dev_map) { dev_t dev = it1.first; - printf( "%s[%u.%u]:%s", BGRAY, major( dev ), minor( dev ), RESET ); - for ( const auto &it2 : it1.second ) - { - std::string inode_device_str = string_format( "%lu", it2 ); + printf("%s[%u.%u]:%s", BGRAY, major(dev), minor(dev), RESET); + for (const auto& it2 : it1.second) { + std::string inode_device_str = string_format("%lu", it2); - printf( " %s%s%s", BGRAY, inode_device_str.c_str(), RESET ); + printf(" %s%s%s", BGRAY, inode_device_str.c_str(), RESET); } - printf( "\n" ); + printf("\n"); } } } } -bool thread_shared_data_t::init( uint32_t numthreads, const std::vector< procinfo_t > &inotify_proclist ) +bool thread_shared_data_t::init(uint32_t numthreads, const std::vector<procinfo_t>& inotify_proclist) { - for ( const procinfo_t &procinfo : inotify_proclist ) - { - if ( !procinfo.in_cmd_line ) + for (const procinfo_t& procinfo : inotify_proclist) { + if (!procinfo.in_cmd_line) continue; - for ( const auto &it1 : procinfo.dev_map ) - { + for (const auto& it1 : procinfo.dev_map) { dev_t dev = it1.first; - for ( const auto &inode : it1.second ) - { - inode_set[ inode ].insert( dev ); + for (const auto& inode : it1.second) { + inode_set[inode].insert(dev); } } } - if ( !inode_set.empty() ) - { - dirqueues.resize( numthreads ); + if (!inode_set.empty()) { + dirqueues.resize(numthreads); } return !inode_set.empty(); } -static uint32_t find_files_in_inode_set( const std::vector< procinfo_t > &inotify_proclist, - std::vector< filename_info_t > &all_found_files ) +static uint32_t find_files_in_inode_set(const std::vector<procinfo_t>& inotify_proclist, + std::vector<filename_info_t>& all_found_files) { thread_shared_data_t tdata; - g_numthreads = std::max< size_t >( 1, g_numthreads ); + g_numthreads = std::max<size_t>(1, g_numthreads); - if ( !tdata.init( g_numthreads, inotify_proclist ) ) + if (!tdata.init(g_numthreads, inotify_proclist)) return 0; - printf( "\n%sSearching '/' for listed inodes...%s (%lu threads)\n", BCYAN, RESET, g_numthreads ); + printf("\n%sSearching '/' for listed inodes...%s (%lu threads)\n", BCYAN, RESET, g_numthreads); // Initialize thread_info_t array - std::vector< class thread_info_t > thread_array( g_numthreads, thread_info_t( tdata ) ); + std::vector<class thread_info_t> thread_array(g_numthreads, thread_info_t(tdata)); - for ( uint32_t idx = 0; idx < thread_array.size(); idx++ ) - { - thread_info_t &thread_info = thread_array[ idx ]; + for (uint32_t idx = 0; idx < thread_array.size(); idx++) { + thread_info_t& thread_info = thread_array[idx]; thread_info.idx = idx; - if ( idx == 0 ) - { + if (idx == 0) { // Add root dir in case someone is watching it - thread_info.add_filename( stat_get_ino( "/" ), "/", "", false ); + thread_info.add_filename(stat_get_ino("/"), "/", "", false); // Add and parse root - thread_info.queue_directory( strdup( "/" ) ); + thread_info.queue_directory(strdup("/")); thread_info.parse_dirqueue_entry(); - } - else if ( pthread_create( &thread_info.pthread_id, NULL, &parse_dirqueue_threadproc, &thread_info ) ) - { - printf( "Warning: pthread_create failed. errno: %d\n", errno ); + } else if (pthread_create(&thread_info.pthread_id, NULL, &parse_dirqueue_threadproc, &thread_info)) { + printf("Warning: pthread_create failed. errno: %d\n", errno); thread_info.pthread_id = 0; } } // Put main thread to work - parse_dirqueue_threadproc( &thread_array[ 0 ] ); + parse_dirqueue_threadproc(&thread_array[0]); uint32_t total_scanned_dirs = 0; - for ( const thread_info_t &thread_info : thread_array ) - { - if ( thread_info.pthread_id ) - { - if ( g_verbose > 1 ) - { - printf( "Waiting for thread #%zu\n", thread_info.pthread_id ); + for (const thread_info_t& thread_info : thread_array) { + if (thread_info.pthread_id) { + if (g_verbose > 1) { + printf("Waiting for thread #%zu\n", thread_info.pthread_id); } - void *status = NULL; - int rc = pthread_join( thread_info.pthread_id, &status ); + void* status = NULL; + int rc = pthread_join(thread_info.pthread_id, &status); - if ( g_verbose > 1 ) - { - printf( "Thread #%zu rc=%d status=%d\n", thread_info.pthread_id, rc, ( int )( intptr_t )status ); + if (g_verbose > 1) { + printf("Thread #%zu rc=%d status=%d\n", thread_info.pthread_id, rc, (int)(intptr_t)status); } } // Snag data from this thread total_scanned_dirs += thread_info.scanned_dirs; - all_found_files.insert( all_found_files.end(), - thread_info.found_files.begin(), thread_info.found_files.end() ); + all_found_files.insert(all_found_files.end(), + thread_info.found_files.begin(), thread_info.found_files.end()); - if ( g_verbose > 1 ) - { - printf( "Thread #%zu: %u dirs, %zu files found\n", - thread_info.pthread_id, thread_info.scanned_dirs, thread_info.found_files.size() ); + if (g_verbose > 1) { + printf("Thread #%zu: %u dirs, %zu files found\n", + thread_info.pthread_id, thread_info.scanned_dirs, thread_info.found_files.size()); } } struct { - bool operator()( const filename_info_t &a, const filename_info_t &b ) const + bool operator()(const filename_info_t& a, const filename_info_t& b) const { - if ( a.dev == b.dev ) + if (a.dev == b.dev) return a.inode < b.inode; return a.dev < b.dev; } } filename_info_less_func; - std::sort( all_found_files.begin(), all_found_files.end(), filename_info_less_func ); + std::sort(all_found_files.begin(), all_found_files.end(), filename_info_less_func); return total_scanned_dirs; } -static uint32_t get_inotify_procfs_value( const std::string &fname ) +static uint32_t get_inotify_procfs_value(const std::string& fname) { - char buf[ 64 ]; + char buf[64]; uint32_t val = 0; std::string filename = "/proc/sys/fs/inotify/" + fname; - int fd = open( filename.c_str(), O_RDONLY ); - if ( fd >= 0 ) - { - if ( read( fd, buf, sizeof( buf ) ) > 0 ) - { - val = strtoul( buf, nullptr, 10 ); + int fd = open(filename.c_str(), O_RDONLY); + if (fd >= 0) { + if (read(fd, buf, sizeof(buf)) > 0) { + val = strtoul(buf, nullptr, 10); } - close( fd ); + close(fd); } return val; @@ -968,77 +899,63 @@ static uint32_t get_inotify_procfs_value( const std::string &fname ) static void print_inotify_limits() { - const std::vector< std::string > filenames = - { + const std::vector<std::string> filenames = { "max_queued_events", "max_user_instances", "max_user_watches" }; - printf( "%sINotify Limits:%s\n", BCYAN, RESET ); - for ( const std::string &fname : filenames ) - { + printf("%sINotify Limits:%s\n", BCYAN, RESET); + for (const std::string& fname : filenames) { char str[16]; - uint32_t val = get_inotify_procfs_value( fname ); + uint32_t val = get_inotify_procfs_value(fname); str_format_uint32(str, val); - printf( " %-20s %s%s%s\n", fname.c_str(), BGREEN, str, RESET ); + printf(" %-20s %s%s%s\n", fname.c_str(), BGREEN, str, RESET); } } -static uint32_t parse_config_file( const char *config_file ) +static uint32_t parse_config_file(const char* config_file) { uint32_t dir_count = 0; - FILE *fp = fopen( config_file, "r" ); - if ( fp ) - { - char line_buf[ 8192 ]; + FILE* fp = fopen(config_file, "r"); + if (fp) { + char line_buf[8192]; bool in_ignore_dirs_section = false; - for ( ;; ) - { - if ( !fgets( line_buf, sizeof( line_buf ) - 1, fp ) ) + for (;;) { + if (!fgets(line_buf, sizeof(line_buf) - 1, fp)) break; - if ( line_buf[0] == '#' ) - { + if (line_buf[0] == '#') { // comment - } - else if ( !in_ignore_dirs_section ) - { - size_t len = strcspn( line_buf, "\r\n" ); + } else if (!in_ignore_dirs_section) { + size_t len = strcspn(line_buf, "\r\n"); - if ( ( len == 12 ) && !strncmp( "[ignoredirs]", line_buf, 12 ) ) - { + if ((len == 12) && !strncmp("[ignoredirs]", line_buf, 12)) { in_ignore_dirs_section = true; } - } - else if ( line_buf[ 0 ] == '[' ) - { + } else if (line_buf[0] == '[') { in_ignore_dirs_section = false; - } - else if ( in_ignore_dirs_section && ( line_buf[ 0 ] == '/' ) ) - { - size_t len = strcspn( line_buf, "\r\n" ); - - if ( len > 1 ) - { - line_buf[ len ] = 0; - if ( line_buf[ len - 1 ] != '/' ) - { - line_buf[ len ] = '/'; - line_buf[ len + 1 ] = '\0'; + } else if (in_ignore_dirs_section && (line_buf[0] == '/')) { + size_t len = strcspn(line_buf, "\r\n"); + + if (len > 1) { + line_buf[len] = 0; + if (line_buf[len - 1] != '/') { + line_buf[len] = '/'; + line_buf[len + 1] = '\0'; } - ignore_dirs.push_back( line_buf ); + ignore_dirs.push_back(line_buf); dir_count++; } } } - fclose( fp ); + fclose(fp); } return dir_count; @@ -1048,74 +965,81 @@ static bool parse_ignore_dirs_file() { const std::string filename = "inotify-info.config"; - const char *xdg_config_dir = getenv( "XDG_CONFIG_HOME" ); - if ( xdg_config_dir ) - { - std::string config_file = std::string( xdg_config_dir ) + "/" + filename; - if ( parse_config_file( config_file.c_str() ) ) + const char* xdg_config_dir = getenv("XDG_CONFIG_HOME"); + if (xdg_config_dir) { + std::string config_file = std::string(xdg_config_dir) + "/" + filename; + if (parse_config_file(config_file.c_str())) return true; - config_file = std::string( xdg_config_dir) + "/.config/" + filename; - if ( parse_config_file( config_file.c_str() ) ) + config_file = std::string(xdg_config_dir) + "/.config/" + filename; + if (parse_config_file(config_file.c_str())) return true; } - const char *home_dir = getenv( "HOME" ); - if ( home_dir ) - { - std::string config_file = std::string( home_dir ) + "/" + filename; - if ( parse_config_file( config_file.c_str() ) ) + const char* home_dir = getenv("HOME"); + if (home_dir) { + std::string config_file = std::string(home_dir) + "/" + filename; + if (parse_config_file(config_file.c_str())) return true; } std::string config_file = "/etc/" + filename; - if ( parse_config_file( config_file.c_str() ) ) + if (parse_config_file(config_file.c_str())) return true; return false; } -static void print_usage( const char *appname ) +static void print_version() { - printf( "Usage: %s [--threads=##] [appname | pid...]\n", appname ); - printf( " [-vv]\n" ); - printf( " [-?|-h|--help]\n" ); + printf("%s\n", INOTIFYINFO_VERSION); +} - exit( -1 ); +static void print_usage(const char* appname) +{ + printf("Usage: %s [--threads=##] [appname | pid...]\n", appname); + printf(" [-vv]\n"); + printf(" [--version]\n"); + printf(" [-?|-h|--help]\n"); } -static void parse_cmdline( int argc, char **argv, std::vector< std::string > &cmdline_applist ) +static void parse_cmdline(int argc, char** argv, std::vector<std::string>& cmdline_applist) { - static struct option long_opts[] = - { + static struct option long_opts[] = { { "verbose", no_argument, 0, 0 }, { "threads", required_argument, 0, 0 }, { "ignoredir", required_argument, 0, 0 }, + { "version", no_argument, 0, 0 }, + { "help", no_argument, 0, 0 }, { 0, 0, 0, 0 } }; // Let's pick the number of processors online (with a max of 32) for a default. - g_numthreads = std::min< uint32_t >( g_numthreads, sysconf( _SC_NPROCESSORS_ONLN ) ); + g_numthreads = std::min<uint32_t>(g_numthreads, sysconf(_SC_NPROCESSORS_ONLN)); int c; int opt_ind = 0; - while ( ( c = getopt_long( argc, argv, "m:s:?hv", long_opts, &opt_ind ) ) != -1 ) - { - switch ( c ) - { + while ((c = getopt_long(argc, argv, "m:s:?hv", long_opts, &opt_ind)) != -1) { + switch (c) { case 0: - if ( !strcasecmp( "verbose", long_opts[ opt_ind ].name ) ) + if (!strcasecmp("help", long_opts[opt_ind].name)) { + print_usage(argv[0]); + exit(0); + }; + if (!strcasecmp("version", long_opts[opt_ind].name)) { + print_version(); + exit(0); + } + if (!strcasecmp("verbose", long_opts[opt_ind].name)) g_verbose++; - else if ( !strcasecmp( "threads", long_opts[ opt_ind ].name ) ) - g_numthreads = atoi( optarg ); - else if ( !strcasecmp( "ignoredir", long_opts[ opt_ind ].name ) ) - { + else if (!strcasecmp("threads", long_opts[opt_ind].name)) + g_numthreads = atoi(optarg); + else if (!strcasecmp("ignoredir", long_opts[opt_ind].name)) { std::string dirname = optarg; - if ( dirname.size() > 1 ) - { - if ( optarg[ dirname.size() - 1 ] != '/' ) + if (dirname.size() > 1) { + if (optarg[dirname.size() - 1] != '/') dirname += "/"; - ignore_dirs.push_back( dirname ); + ignore_dirs.push_back(dirname); } } break; @@ -1124,93 +1048,88 @@ static void parse_cmdline( int argc, char **argv, std::vector< std::string > &cm break; case 'h': case '?': + print_usage(argv[0]); + exit(0); default: - print_usage( argv[ 0 ] ); + print_usage(argv[0]); + exit(-1); break; } } - for ( ; optind < argc; optind++ ) - { - cmdline_applist.push_back( argv[ optind ] ); + for (; optind < argc; optind++) { + cmdline_applist.push_back(argv[optind]); } parse_ignore_dirs_file(); - if ( g_verbose > 1 ) - { - printf( "%lu ignore_dirs:\n", ignore_dirs.size() ); + if (g_verbose > 1) { + printf("%lu ignore_dirs:\n", ignore_dirs.size()); - for ( std::string &dname : ignore_dirs ) - { - printf( " '%s'\n", dname.c_str() ); + for (std::string& dname : ignore_dirs) { + printf(" '%s'\n", dname.c_str()); } } } static void print_separator() { - printf( "%s%s%s\n", YELLOW, std::string( 78, '-' ).c_str(), RESET ); + printf("%s%s%s\n", YELLOW, std::string(78, '-').c_str(), RESET); } -int main( int argc, char *argv[] ) +int main(int argc, char* argv[]) { - std::vector< std::string > cmdline_applist; - std::vector< procinfo_t > inotify_proclist; + std::vector<std::string> cmdline_applist; + std::vector<procinfo_t> inotify_proclist; - struct lconv *env = localeconv(); - if (env && env->thousands_sep && env->thousands_sep[0]) - { + struct lconv* env = localeconv(); + if (env && env->thousands_sep && env->thousands_sep[0]) { thousands_sep = env->thousands_sep[0]; } - parse_cmdline( argc, argv, cmdline_applist ); + parse_cmdline(argc, argv, cmdline_applist); print_separator(); print_inotify_limits(); print_separator(); - if ( init_inotify_proclist( inotify_proclist ) ) - { + if (init_inotify_proclist(inotify_proclist)) { uint32_t total_watches = 0; uint32_t total_instances = 0; - std::vector< filename_info_t > all_found_files; + std::vector<filename_info_t> all_found_files; - for ( procinfo_t &procinfo : inotify_proclist ) - { - procinfo.in_cmd_line = is_proc_in_cmdline_applist( procinfo, cmdline_applist ); + for (procinfo_t& procinfo : inotify_proclist) { + procinfo.in_cmd_line = is_proc_in_cmdline_applist(procinfo, cmdline_applist); total_watches += procinfo.watches; total_instances += procinfo.instances; } if (inotify_proclist.size()) { - print_inotify_proclist( inotify_proclist ); + print_inotify_proclist(inotify_proclist); print_separator(); } if (g_kernel_provides_watches_info) - printf( "Total inotify Watches: %s%u%s\n", BGREEN, total_watches, RESET ); - printf( "Total inotify Instances: %s%u%s\n", BGREEN, total_instances, RESET ); + printf("Total inotify Watches: %s%u%s\n", BGREEN, total_watches, RESET); + printf("Total inotify Instances: %s%u%s\n", BGREEN, total_instances, RESET); print_separator(); double search_time = gettime(); - uint32_t total_scanned_dirs = find_files_in_inode_set( inotify_proclist, all_found_files ); - if ( total_scanned_dirs ) - { + uint32_t total_scanned_dirs = find_files_in_inode_set(inotify_proclist, all_found_files); + if (total_scanned_dirs) { search_time = gettime() - search_time; - for ( const filename_info_t &fname_info : all_found_files ) - { - printf( "%s%9lu%s [%u:%u] %s\n", BGREEN, fname_info.inode, RESET, - major( fname_info.dev ), minor( fname_info.dev ), - fname_info.filename.c_str() ); + for (const filename_info_t& fname_info : all_found_files) { + printf("%s%9lu%s [%u:%u] %s\n", BGREEN, fname_info.inode, RESET, + major(fname_info.dev), minor(fname_info.dev), + fname_info.filename.c_str()); } - setlocale( LC_NUMERIC, "" ); -GCC_DIAG_PUSH_OFF( format ) - printf( "\n%'u dirs scanned (%.2f seconds)\n", total_scanned_dirs, search_time ); -GCC_DIAG_POP() + setlocale(LC_NUMERIC, ""); + GCC_DIAG_PUSH_OFF(format) + printf("\n%'u dirs scanned (%.2f seconds)\n", total_scanned_dirs, search_time); + GCC_DIAG_POP() } } |