diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..fbcb06f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.swp
+*.swo
+*.o
+cv
diff --git a/Makefile b/Makefile
index aa2158b..5dce11c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,13 +1,12 @@
OBJ=cv
-CFLAGS=-g -Wall -D_FILE_OFFSET_BITS=64
-
+CFLAGS = -g -Wall -D_FILE_OFFSET_BITS=64 -std=gnu99
+CFLAGS += -DBUILD_DAEMON=1
+CFLAGS += $(shell pkg-config --cflags --libs libnotify glib)
PREFIX = $(DESTDIR)/usr/local
BINDIR = $(PREFIX)/bin
-$(OBJ) : cv.o sizes.o
- gcc -Wall $^ -o $@
-%.o : %.c
- gcc $(CFLAGS) -c $^
+$(OBJ) : cv.c sizes.c
+ gcc $(CFLAGS) $^ -o $@
clean :
rm -f *.o $(OBJ)
install : $(OBJ)
diff --git a/cv.c b/cv.c
index 8ac82ae..751be49 100644
--- a/cv.c
+++ b/cv.c
@@ -13,7 +13,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
-*/
+ */
#include
#include
@@ -31,450 +31,419 @@
#include
-// for the BLKGETSIZE64 code section
+/* for O_PATH */
+#define __USE_GNU
+/* for the BLKGETSIZE64 code section */
#include
#include
+#include
#include
+#ifdef BUILD_DAEMON
+#include
+#endif
+
#include "cv.h"
#include "sizes.h"
char *proc_names[] = {"cp", "mv", "dd", "tar", "gzip", "gunzip", "cat", "grep", "cut", "sort", NULL};
-char *proc_specifiq = NULL;
-signed char flag_quiet = 0;
-signed char flag_throughput = 0;
+char *proc_specific = NULL;
+int flag_quiet = 0;
+int flag_throughput = 0;
double throughput_wait_secs = 1;
+int flag_daemon = 0;
-signed char is_numeric(char *str)
-{
-while(*str) {
- if(!isdigit(*str))
- return 0;
- str++;
-}
-return 1;
-}
-
-int find_pids_by_binary_name(char *bin_name, pidinfo_t *pid_list, int max_pids)
-{
-DIR *proc;
-struct dirent *direntp;
-struct stat stat_buf;
-char fullpath_dir[MAXPATHLEN + 1];
-char fullpath_exe[MAXPATHLEN + 1];
-char exe[MAXPATHLEN + 1];
-ssize_t len;
-int pid_count=0;
-
-proc=opendir(PROC_PATH);
-if(!proc) {
- perror("opendir");
- fprintf(stderr,"Can't open %s\n",PROC_PATH);
- exit(EXIT_FAILURE);
-}
-
-while((direntp = readdir(proc)) != NULL) {
- snprintf(fullpath_dir, MAXPATHLEN, "%s/%s", PROC_PATH, direntp->d_name);
-
- if(stat(fullpath_dir, &stat_buf) == -1) {
- if (!flag_quiet)
- perror("stat (find_pids_by_binary_name)");
- continue;
- }
-
- if((S_ISDIR(stat_buf.st_mode) && is_numeric(direntp->d_name))) {
- snprintf(fullpath_exe, MAXPATHLEN, "%s/exe", fullpath_dir);
- len=readlink(fullpath_exe, exe, MAXPATHLEN);
- if(len != -1)
- exe[len] = 0;
- else {
- // Will be mostly "Permission denied"
- //~ perror("readlink");
- continue;
- }
-
- if(!strcmp(basename(exe), bin_name)) {
- pid_list[pid_count].pid=atol(direntp->d_name);
- strcpy(pid_list[pid_count].name, bin_name);
- pid_count++;
- if(pid_count==max_pids)
- break;
- }
- }
-}
-
-closedir(proc);
-return pid_count;
-}
-
-int find_fd_for_pid(pid_t pid, int *fd_list, int max_fd)
-{
-DIR *proc;
-struct dirent *direntp;
-char path_dir[MAXPATHLEN + 1];
-char fullpath[MAXPATHLEN + 1];
-char link_dest[MAXPATHLEN + 1];
-struct stat stat_buf;
-int count = 0;
-ssize_t len;
-
-snprintf(path_dir, MAXPATHLEN, "%s/%d/fd", PROC_PATH, pid);
-
-proc=opendir(path_dir);
-if(!proc) {
- perror("opendir");
- fprintf(stderr,"Can't open %s\n",path_dir);
- return 0;
-}
-
-while((direntp = readdir(proc)) != NULL) {
- snprintf(fullpath, MAXPATHLEN, "%s/%s", path_dir, direntp->d_name);
- if(stat(fullpath, &stat_buf) == -1) {
- if (!flag_quiet)
- perror("stat (find_fd_for_pid)");
- continue;
- }
-
- // if not a regular file or a block device
- if(!S_ISREG(stat_buf.st_mode) && !S_ISBLK(stat_buf.st_mode))
- continue;
-
- // try to read link ...
- len=readlink(fullpath, link_dest, MAXPATHLEN);
- if(len != -1)
- link_dest[len] = 0;
- else
- continue;
-
- // try to stat link target (invalid link ?)
- if(stat(link_dest, &stat_buf) == -1)
- continue;
-
- // OK, we've found a potential interesting file.
-
- fd_list[count++] = atoi(direntp->d_name);
- //~ printf("[debug] %s\n",fullpath);
- if(count == max_fd)
- break;
-}
-
-closedir(proc);
-return count;
-}
+ssize_t size_for_stat(struct stat *fd_target, int fdfd, char *name){
+ if(!S_ISBLK(fd_target->st_mode))
+ return fd_target->st_size;
+ int fd = openat(fdfd, name, O_RDONLY);
+ if(fd < 0)
+ return -1;
-signed char get_fdinfo(pid_t pid, int fdnum, fdinfo_t *fd_info)
-{
-struct stat stat_buf;
-char fdpath[MAXPATHLEN + 1];
-char line[LINE_LEN];
-ssize_t len;
-FILE *fp;
-struct timezone tz;
-
-fd_info->num = fdnum;
-
-snprintf(fdpath, MAXPATHLEN, "%s/%d/fd/%d", PROC_PATH, pid, fdnum);
-
-len=readlink(fdpath, fd_info->name, MAXPATHLEN);
-if(len != -1)
- fd_info->name[len] = 0;
-else {
- //~ perror("readlink");
- return 0;
-}
-
-if(stat(fd_info->name, &stat_buf) == -1) {
- //~ printf("[debug] %i - %s\n",pid,fd_info->name);
- if (!flag_quiet)
- perror("stat (get_fdinfo)");
- return 0;
+ ssize_t rv = 0;
+ if(ioctl(fd, BLKGETSIZE64, &rv) < 0)
+ rv = -1;
+ close(fd);
+ return rv;
}
-if(S_ISBLK(stat_buf.st_mode)) {
- int fd;
-
- fd = open(fd_info->name, O_RDONLY);
-
- if (fd < 0) {
- if (!flag_quiet)
- perror("open (get_fdinfo)");
- return 0;
- }
-
- if (ioctl(fd, BLKGETSIZE64, &fd_info->size) < 0) {
- if (!flag_quiet)
- perror("ioctl (get_fdinfo)");
- return 0;
- }
-} else {
- fd_info->size = stat_buf.st_size;
+int diropen(int entryfd, char *name, int *fdout, DIR** dirout){
+ int fd = openat(entryfd, name, O_RDONLY | O_DIRECTORY);
+ if(fd < 0) /* Likely permission denied */
+ return 1;
+ *fdout = fd;
+
+ DIR *adir = fdopendir(fd);
+ if(!adir)
+ return 2;
+ *dirout = adir;
+ return 0;
}
-fd_info->pos = 0;
-
-snprintf(fdpath, MAXPATHLEN, "%s/%d/fdinfo/%d", PROC_PATH, pid, fdnum);
-fp = fopen(fdpath, "rt");
-gettimeofday(&fd_info->tv, &tz);
-
-if(!fp) {
- if (!flag_quiet)
- perror("fopen (get_fdinfo)");
- return 0;
+int biggest_file_for_entry(int entryfd, fdinfo_t *res){
+ int rc = 0;
+ /* /proc/2342/fd */
+ int fdfd = 0;
+ DIR *fddir = NULL;
+ /* /proc/2342/fdinfo/23 */
+ int infofd = 0;
+
+ if(diropen(entryfd, "fd", &fdfd, &fddir)){
+ rc = 1;
+ goto cleanup;
+ }
+
+ size_t max_size = 0;
+ char max_size_info[sizeof("fdinfo") + sizeof("2147483648")];
+ strcpy(max_size_info, "fdinfo/");
+ ino_t max_size_inode = 0;
+
+ struct dirent *fdent;
+ while((fdent = readdir(fddir))){
+ if(fdent->d_type != DT_LNK)
+ continue;
+
+ struct stat fd_target;
+ if(fstatat(fdfd, fdent->d_name, &fd_target, 0))
+ continue;
+
+ size_t size_tmp = size_for_stat(&fd_target, fdfd, fdent->d_name);
+ if(size_tmp < max_size)
+ continue;
+
+ max_size = size_tmp;
+ strncpy(max_size_info + sizeof("fdinfo"), fdent->d_name, sizeof("2147483648"));
+ max_size_inode = fd_target.st_ino;
+ }
+
+ infofd = openat(entryfd, max_size_info, O_RDONLY);
+ if(infofd < 0){
+ rc = 1;
+ goto cleanup;
+ }
+
+ /* According to seq_show in fs/proc/fd.c in the linux kernel sources, the fdinfo file will always start with the
+ * "pos" line. The pos field is printfed as a long long, i.e. 64 bit and thus never larger/smaller than ±2**63 */
+ char buf[32];
+ if((read(infofd, buf, 5) != 5) || strncmp(buf, "pos:", 4)) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ ssize_t len = read(infofd, buf, sizeof(buf)-1);
+ buf[len] = 0;
+
+ res->pos = atoll(buf);
+ res->size = max_size;
+ res->inode= max_size_inode;
+ res->num = atoi(max_size_info+sizeof("fdinfo"));
+ gettimeofday(&res->tv, NULL);
+
+ char *fbuf = malloc(MAXPATHLEN);
+ if(!fbuf) {
+ rc = 2;
+ goto cleanup;
+ }
+
+ if(readlinkat(fdfd, max_size_info+sizeof("fdinfo"), fbuf, MAXPATHLEN) < 0) {
+ free(fbuf);
+ rc = 1;
+ goto cleanup;
+ }
+ res->filename = fbuf;
+
+cleanup:
+ if(infofd > 0)
+ close(infofd);
+ if(fddir)
+ closedir(fddir);
+ if(fdfd > 0)
+ close(fdfd);
+
+ return rc;
}
-while(fgets(line, LINE_LEN - 1, fp) != NULL) {
- line[4]=0;
- if(!strcmp(line, "pos:")) {
- fd_info->pos = atoll(line + 5);
- break;
- }
+int is_numeric(char *str) {
+ while(isdigit(*str++))
+ if(!*str)
+ return 1;
+ return 0;
}
-return 1;
+int inlist(char *str, char **list) {
+ while(*list)
+ if(!strcmp(str, *list++))
+ return 1;
+ return 0;
}
-void print_bar(float perc, int char_available)
-{
-int i;
-int num;
-
-num = (char_available / 100.0) * perc;
-
-for(i = 0 ; i < num-1 ; i++) {
- putchar('=');
-}
-putchar('>');
-i++;
-
-for( ; i < char_available ; i++)
- putchar(' ');
-
-}
-
-
void parse_options(int argc, char *argv[])
{
-static struct option long_options[] = {
- {"version", no_argument, 0, 'v'},
- {"quiet", no_argument, 0, 'q'},
- {"wait", no_argument, 0, 'w'},
- {"wait-delay", required_argument, 0, 'W'},
- {"help", no_argument, 0, 'h'},
- {"command", required_argument, 0, 'c'},
- {0, 0, 0, 0}
-};
-
-static char *options_string = "vqwhc:W:";
-int c,i;
-int option_index = 0;
-
-while(1) {
- c = getopt_long (argc, argv, options_string, long_options, &option_index);
-
- // no more options
- if (c == -1)
- break;
-
- switch(c) {
- case 'v':
- printf("cv version %s\n",CV_VERSION);
- exit(EXIT_SUCCESS);
- break;
-
- case 'h':
- printf("cv - Coreutils Viewer\n");
- printf("---------------------\n");
- printf("Shows running coreutils basic commands and displays stats.\n");
- printf("Supported commands: ");
- for(i = 0 ; proc_names[i] ; i++)
- printf("%s ", proc_names[i]);
- printf("\n");
- printf("Usage: %s [-vqwh] [-W] [-c command]\n",argv[0]);
- printf(" -v --version show version\n");
- printf(" -q --quiet hides some warning/error messages\n");
- printf(" -w --wait estimate I/O throughput (slower display)\n");
- printf(" -W --wait-delay secs wait 'secs' seconds for I/O estimation (implies -w, default=%.1f)\n", throughput_wait_secs);
- printf(" -h --help this message\n");
- printf(" -c --command cmd monitor only this command name (ex: firefox)\n");
-
- exit(EXIT_SUCCESS);
- break;
-
- case 'q':
- flag_quiet = 1;
- break;
-
- case 'c':
- proc_specifiq = strdup(optarg);
- break;
-
- case 'w':
- flag_throughput = 1;
- break;
-
- case 'W':
- flag_throughput = 1;
- throughput_wait_secs = atof(optarg);
- break;
-
- case '?':
- default:
- exit(EXIT_FAILURE);
- }
-}
-
-if (optind < argc) {
- fprintf(stderr,"Invalid arguments.\n");
- exit(EXIT_FAILURE);
-}
+ static struct option long_options[] = {
+ {"version", no_argument, 0, 'v'},
+ {"quiet", no_argument, 0, 'q'},
+ {"wait", no_argument, 0, 'w'},
+ {"wait-delay", required_argument, 0, 'W'},
+#ifdef BUILD_DAEMON
+ {"daemonize", no_argument, 0, 'd'},
+#endif
+ {"help", no_argument, 0, 'h'},
+ {"command", required_argument, 0, 'c'},
+ {0, 0, 0, 0}
+ };
+
+#ifdef BUILD_DAEMON
+ static char *options_string = "vqwdhc:W:";
+#else
+ static char *options_string = "vqwhc:W:";
+#endif
+ int c,i;
+ int option_index = 0;
+
+ while(1) {
+ c = getopt_long (argc, argv, options_string, long_options, &option_index);
+
+ // no more options
+ if (c == -1)
+ break;
+
+ switch(c) {
+ case 'v':
+ printf("cv version %s\n",CV_VERSION);
+ exit(EXIT_SUCCESS);
+ break;
+
+ case 'h':
+ printf("cv - Coreutils Viewer\n");
+ printf("---------------------\n");
+ printf("Shows running coreutils basic commands and displays stats.\n");
+ printf("Supported commands: ");
+ for(i = 0 ; proc_names[i] ; i++)
+ printf("%s ", proc_names[i]);
+ printf("\n");
+ printf("Usage: %s [-vqwh] [-W] [-c command]\n",argv[0]);
+ printf(" -v --version show version\n");
+ printf(" -q --quiet hides some warning/error messages\n");
+ printf(" -w --wait estimate I/O throughput (slower display)\n");
+ printf(" -W --wait-delay secs wait 'secs' seconds for I/O estimation (implies -w, default=%.1f)\n", throughput_wait_secs);
+#ifdef BUILD_DAEMON
+ printf(" -d --daemonize Daemonize and show results using libnotify (implies -w)\n");
+#endif
+ printf(" -h --help this message\n");
+ printf(" -c --command cmd monitor only this command name (ex: firefox)\n");
+
+ exit(EXIT_SUCCESS);
+ break;
+ case 'q':
+ flag_quiet = 1;
+ break;
+ case 'c':
+ proc_specific = strdup(optarg);
+ break;
+ case 'w':
+ flag_throughput = 1;
+ break;
+ case 'W':
+ flag_throughput = 1;
+ throughput_wait_secs = atof(optarg);
+ break;
+ case 'd':
+ flag_daemon = 1;
+ break;
+ case '?':
+ default:
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,"Invalid arguments.\n");
+ exit(EXIT_FAILURE);
+ }
}
// TODO: deal with --help
-int main(int argc, char *argv[])
-{
-int pid_count, fd_count, result_count;
-int i,j;
-pidinfo_t pidinfo_list[MAX_PIDS];
-fdinfo_t fdinfo;
-fdinfo_t biggest_fd;
-int fdnum_list[MAX_FD_PER_PID];
-off_t max_size;
-char fsize[64];
-char fpos[64];
-char ftroughput[64];
-struct winsize ws;
-float perc;
-result_t results[MAX_RESULTS];
-signed char still_there;
-
-parse_options(argc,argv);
-
-// ws.ws_row, ws.ws_col
-ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
-
-pid_count = 0;
-
-if(!proc_specifiq) {
- for(i = 0 ; proc_names[i] ; i++) {
- pid_count += find_pids_by_binary_name(proc_names[i],
- pidinfo_list + pid_count,
- MAX_PIDS - pid_count);
- if(pid_count >= MAX_PIDS) {
- fprintf(stderr, "Found too much procs (max = %d)\n",MAX_PIDS);
- break;
- }
- }
-} else {
- pid_count += find_pids_by_binary_name(proc_specifiq,
- pidinfo_list + pid_count,
- MAX_PIDS - pid_count);
-}
-
-
-if(!pid_count) {
- if(flag_quiet)
- return 0;
-
- fprintf(stderr,"No command currently running: ");
- for(i = 0 ; proc_names[i] ; i++) {
- fprintf(stderr,"%s, ", proc_names[i]);
- }
- fprintf(stderr,"exiting.\n");
- return 0;
-}
-
-result_count = 0;
-
-for(i = 0 ; i < pid_count ; i++) {
- fd_count = find_fd_for_pid(pidinfo_list[i].pid, fdnum_list, MAX_FD_PER_PID);
-
- max_size = 0;
-
- // let's find the biggest opened file
- for(j = 0 ; j < fd_count ; j++) {
- get_fdinfo(pidinfo_list[i].pid, fdnum_list[j], &fdinfo);
-
- if(fdinfo.size > max_size) {
- biggest_fd = fdinfo;
- max_size = fdinfo.size;
- }
- }
-
- if(!max_size) { // nothing found
- printf("[%5d] %s inactive/flushing/streaming/...\n",
- pidinfo_list[i].pid,
- pidinfo_list[i].name);
- continue;
- }
-
- // We've our biggest_fd now, let's store the result
- results[result_count].pid = pidinfo_list[i];
- results[result_count].fd = biggest_fd;
-
- result_count++;
-}
-
-// wait a bit, so we can estimate the throughput
-if (flag_throughput)
- usleep(1000000 * throughput_wait_secs);
-
-for (i = 0 ; i < result_count ; i++) {
-
- if (flag_throughput) {
- still_there = get_fdinfo(results[i].pid.pid, results[i].fd.num, &fdinfo);
- if (still_there && strcmp(results[i].fd.name, fdinfo.name))
- still_there = 0; // still there, but it's not the same file !
- } else
- still_there = 0;
-
- if (!still_there) {
- // pid is no more here (or no throughput was asked), use initial info
- format_size(results[i].fd.pos, fpos);
- format_size(results[i].fd.size, fsize);
- perc = ((double)100 / (double)results[i].fd.size) * (double)results[i].fd.pos;
- } else {
- // use the newest info
- format_size(fdinfo.pos, fpos);
- format_size(fdinfo.size, fsize);
- perc = ((double)100 / (double)fdinfo.size) * (double)fdinfo.pos;
-
- }
-
- printf("[%5d] %s %s %.1f%% (%s / %s)",
- results[i].pid.pid,
- results[i].pid.name,
- results[i].fd.name,
- perc,
- fpos,
- fsize);
-
- if (flag_throughput && still_there) {
- // results[i] vs fdinfo
- long long usec_diff;
- off_t byte_diff;
- off_t bytes_per_sec;
-
- usec_diff = (fdinfo.tv.tv_sec - results[i].fd.tv.tv_sec) * 1000000L
- + (fdinfo.tv.tv_usec - results[i].fd.tv.tv_usec);
- byte_diff = fdinfo.pos - results[i].fd.pos;
- bytes_per_sec = byte_diff / (usec_diff / 1000000.0);
-
- format_size(bytes_per_sec, ftroughput);
- printf(" %s/s", ftroughput);
- }
-
-
- printf("\n");
-
- // Need to work on window width when using screen/watch/...
- //~ printf(" [");
- //~ print_bar(perc, ws.ws_col-6);
- //~ printf("]\n");
-
+int compare_results(const void *a, const void *b){
+ return ((fdinfo_t*)b)->pid - ((fdinfo_t*)a)->pid;
}
-return 0;
+int main(int argc, char *argv[]) {
+ int rc = 0;
+ fdinfo_t results1[MAX_RESULTS];
+ fdinfo_t results2[MAX_RESULTS];
+ fdinfo_t *new_results = results1;
+ fdinfo_t *old_results = results2;
+ size_t old_result_count = 0;
+
+ parse_options(argc,argv);
+
+#ifdef BUILD_DAEMON
+ notify_init("cv");
+#endif
+
+ int procfd = 0;
+ DIR* procdir = NULL;
+ if(diropen(AT_FDCWD, "/proc", &procfd, &procdir)) {
+ fprintf(stderr, "Can't open /proc: %s (%d)\n", strerror(errno), errno);
+ rc = 1;
+ goto cleanup;
+ }
+
+ char *proc_specific_list[] = {proc_specific, NULL};
+ char **name_list = proc_specific ? proc_specific_list : proc_names;
+
+ while(1) {
+ /* Search /proc */
+ size_t new_result_count = 0;
+ int thisfd = 0;
+ struct dirent *procent;
+ while((procent = readdir(procdir)) && new_result_count < MAX_RESULTS) {
+ if(thisfd)
+ close(thisfd);
+
+ if(procent->d_type != DT_DIR || !is_numeric(procent->d_name))
+ continue;
+
+ thisfd = openat(procfd, procent->d_name, O_RDONLY | O_DIRECTORY);
+ if(thisfd < 0) /* Will be mostly "Permission denied" */
+ continue;
+
+ char path_buf[MAXPATHLEN];
+ ssize_t len = readlinkat(thisfd, "exe", path_buf, sizeof(path_buf));
+ if(len < 0) /* Will be mostly "Permission denied" */
+ continue;
+ path_buf[len] = 0;
+
+ if(!inlist(basename(path_buf), name_list))
+ continue;
+
+ fdinfo_t *result = new_results+new_result_count;
+ result->pid = atol(procent->d_name);
+ result->procname = strdup(basename(path_buf));
+ if(biggest_file_for_entry(thisfd, new_results+new_result_count)) {
+ if(!flag_quiet)
+ fprintf(stderr, "Found no large open files for %s [%5d]\n", result->procname, result->pid);
+ continue;
+ }
+
+ new_result_count++;
+ }
+ if(thisfd)
+ close(thisfd);
+ rewinddir(procdir);
+
+ /* Depending on the order in which the kernel returns the entries of /proc, this might not be necessary. */
+ qsort(new_results, sizeof(new_results)/sizeof(fdinfo_t), sizeof(fdinfo_t), compare_results);
+
+ /* Print results, merging old and new lists on the way */
+ fdinfo_t *old_result = old_results;
+ fdinfo_t *new_result = new_results;
+#ifdef BUILD_DAEMON
+ if(new_result_count == 0) {
+ while(old_result < old_results+old_result_count){ /* process terminated */
+ notify_notification_close(old_result->notification, NULL);
+ g_object_unref(G_OBJECT(old_result->notification));
+ old_result++;
+ }
+ }
+#endif
+ while(new_result < new_results+new_result_count) {
+ off_t throughput = -1;
+#ifdef BUILD_DAEMON
+ new_result->notification = NULL;
+#endif
+ if(old_result < old_results+old_result_count) {
+ while(new_result->pid > old_result->pid) { /* process terminated */
+#ifdef BUILD_DAEMON
+ notify_notification_close(old_result->notification, NULL);
+ g_object_unref(G_OBJECT(old_result->notification));
+#endif
+ old_result++;
+ }
+
+ if(new_result->pid == old_result->pid) {
+ if(new_result->inode == old_result->inode){
+ uint64_t time_delta = (new_result->tv.tv_sec - old_result->tv.tv_sec)
+ + (new_result->tv.tv_usec - old_result->tv.tv_usec)/1000000L;
+ throughput = (new_result->pos - old_result->pos)/time_delta;
+#ifdef BUILD_DAEMON
+ new_result->notification = old_result->notification;
+#endif
+ }
+
+ if(old_result < old_results+(old_result_count-1))
+ old_result++;
+ }
+ }
+
+ char fsize[32];
+ char fpos[32];
+ format_size(new_result->pos, fpos, sizeof(fpos));
+ format_size(new_result->size, fsize, sizeof(fsize));
+ float ratio = (float)new_result->pos/new_result->size;
+
+ char strbuf[MAXPATHLEN];
+ int pos = 0;
+ pos += snprintf(strbuf, sizeof(strbuf)-pos, "[%5d] %s %s %.1f%% (%s/%s",
+ new_result->pid,
+ new_result->procname,
+ new_result->filename,
+ ratio*100,
+ fpos,
+ fsize);
+
+ free(new_result->filename);
+
+ char fthroughput[32];
+ if(throughput < 0) {
+ pos += snprintf(strbuf+pos, sizeof(strbuf)-pos, ")");
+ } else {
+ format_size(throughput, fthroughput, sizeof(fthroughput));
+ pos += snprintf(strbuf+pos, sizeof(strbuf)-pos, " @ %s/s)", fthroughput);
+ }
+
+ if(!flag_daemon) {
+ printf("%s\n", strbuf);
+#ifdef BUILD_DAEMON
+ } else {
+ NotifyNotification *notf = new_result->notification;
+ if(!notf) {
+ notf = notify_notification_new("cv", strbuf, NULL);
+ notify_notification_set_hint(notf, "synchronous", g_variant_new_string("volume"));
+ }else{
+ notify_notification_update(notf, "cv", strbuf, NULL);
+ }
+ notify_notification_set_hint(notf, "value", g_variant_new_int32(ratio*100));
+ notify_notification_show(notf, NULL);
+ new_result->notification = notf;
+#endif
+ }
+
+ new_result++;
+ }
+
+ /* Exchange pointers */
+ fdinfo_t *tmpr = new_results;
+ new_results = old_results;
+ old_results = tmpr;
+ old_result_count = new_result_count;
+
+ if(!flag_daemon && !flag_throughput)
+ break;
+ flag_throughput = !flag_throughput;
+ usleep(1000000 * throughput_wait_secs);
+ }
+
+cleanup:
+#ifdef BUILD_DAEMON
+ notify_uninit();
+#endif
+ if(procfd > 0)
+ close(procfd);
+ if(procdir)
+ closedir(procdir);
+ return rc;
}
diff --git a/cv.h b/cv.h
index 78905f7..a72310a 100644
--- a/cv.h
+++ b/cv.h
@@ -6,31 +6,27 @@
#include
#include
+#ifdef BUILD_DAEMON
+#include
+#endif
+
#define CV_VERSION "0.3"
-#define PROC_PATH "/proc"
-#define MAX_PIDS 32
#define MAX_RESULTS 32
-#define MAX_FD_PER_PID 512
-#define LINE_LEN 256
//~ #define MINMUM_SIZE 8192
typedef struct fdinfo_t {
+ int pid;
int num;
off_t size;
off_t pos;
- char name[MAXPATHLEN + 1];
+ char *filename;
+ char *procname;
+ ino_t inode;
struct timeval tv;
+#ifdef BUILD_DAEMON
+ NotifyNotification *notification;
+#endif
} fdinfo_t;
-typedef struct pidinfo_t {
- pid_t pid;
- char name[MAXPATHLEN + 1];
-} pidinfo_t;
-
-typedef struct result_t {
- pidinfo_t pid;
- fdinfo_t fd;
-} result_t;
-
#endif
diff --git a/sizes.c b/sizes.c
index a6cd696..775944e 100644
--- a/sizes.c
+++ b/sizes.c
@@ -4,27 +4,18 @@
#define DIM(x) (sizeof(x)/sizeof(*(x)))
-static const char *sizes[] = { "EiB", "PiB", "TiB", "GiB", "MiB", "KiB", "B" };
-static const uint64_t exbibytes = 1024ULL * 1024ULL * 1024ULL *
- 1024ULL * 1024ULL * 1024ULL;
+static const char *sizes[] = {"B", "kiB", "MiB", "GiB", "TiB", "PiB", "EiB", NULL};
-void format_size(uint64_t size, char *result)
-{
-uint64_t multiplier;
-int i;
-
-multiplier = exbibytes;
-
-for (i = 0 ; i < DIM(sizes) ; i++, multiplier /= 1024) {
- if (size < multiplier)
- continue;
- if (size % multiplier == 0)
- sprintf(result, "%" PRIu64 " %s", size / multiplier, sizes[i]);
- else
- sprintf(result, "%.1f %s", (float) size / multiplier, sizes[i]);
- return;
-}
-
-strcpy(result, "0");
-return;
+void format_size(uint64_t size, char *result, size_t result_len) {
+ const char **str = sizes;
+ uint64_t omult, mult = 1;
+ do {
+ omult = mult;
+ mult <<= 10;
+ if(size < mult){
+ snprintf(result, result_len, "%.1f%s", (float)size/omult, *str);
+ return;
+ }
+ } while(*++str);
+ snprintf(result, result_len, "%.1f%s", (float)size/omult, str[-1]);
}
diff --git a/sizes.h b/sizes.h
index d274ce8..ac36f83 100644
--- a/sizes.h
+++ b/sizes.h
@@ -6,6 +6,6 @@
#include
#include
-void format_size(uint64_t size, char *result);
+void format_size(uint64_t size, char *result, size_t result_len);
#endif