diff --git a/src/include/pv-internal.h b/src/include/pv-internal.h index eb13826..e1997d2 100644 --- a/src/include/pv-internal.h +++ b/src/include/pv-internal.h @@ -213,8 +213,11 @@ struct pvstate_s { struct pvwatchfd_s { unsigned int watch_pid; /* PID to watch */ int watch_fd; /* fd to watch, -1 = not displayed */ +#ifdef __APPLE__ +#else char file_fdinfo[4096]; /* path to /proc fdinfo file */ char file_fd[4096]; /* path to /proc fd symlink */ +#endif char file_fdpath[4096]; /* path to file that was opened */ char display_name[512]; /* name to show on progress bar */ struct stat64 sb_fd; /* stat of fd symlink */ diff --git a/src/main/options.c b/src/main/options.c index 0d370ed..9a369f9 100644 --- a/src/main/options.c +++ b/src/main/options.c @@ -362,6 +362,7 @@ opts_t opts_parse(int argc, char **argv) return NULL; } +#ifndef __APPLE__ if (0 != access("/proc/self/fdinfo", X_OK)) { fprintf(stderr, "%s: -d: %s\n", opts->program_name, _ @@ -369,6 +370,7 @@ opts_t opts_parse(int argc, char **argv) opts_free(opts); return NULL; } +#endif } /* diff --git a/src/pv/watchpid.c b/src/pv/watchpid.c index 9dda7cd..bed053a 100644 --- a/src/pv/watchpid.c +++ b/src/pv/watchpid.c @@ -20,6 +20,97 @@ #include #include +#ifdef __APPLE__ +#include +#include +#endif + +int filesize(pvwatchfd_t info) +{ + if (S_ISBLK(info->sb_fd.st_mode)) { + int fd; + + /* + * Get the size of block devices by opening + * them and seeking to the end. + */ + fd = open64(info->file_fdpath, O_RDONLY); + if (fd >= 0) { + info->size = lseek64(fd, 0, SEEK_END); + close(fd); + } else { + info->size = 0; + } + } else if (S_ISREG(info->sb_fd.st_mode)) { + if ((info->sb_fd_link.st_mode & S_IWUSR) == 0) { + info->size = info->sb_fd.st_size; + } + } else { + return 4; + } + + return 0; +} + +#ifdef __APPLE__ +int pv_watchfd_info(pvstate_t state, pvwatchfd_t info, int automatic) +{ + struct vnode_fdinfowithpath vnodeInfo = {}; + + if (NULL == state) + return -1; + if (NULL == info) + return -1; + + if (kill(info->watch_pid, 0) != 0) { + if (!automatic) + pv_error(state, "%s %u: %s", + _("pid"), + info->watch_pid, strerror(errno)); + return 1; + } + + int32_t proc_fd = (int32_t)info->watch_fd; + int size = proc_pidfdinfo(info->watch_pid, proc_fd, PROC_PIDFDVNODEPATHINFO, &vnodeInfo, PROC_PIDFDVNODEPATHINFO_SIZE); + if (size != PROC_PIDFDVNODEPATHINFO_SIZE) { + pv_error(state, "%s %u: %s %d: %s", + _("pid"), + info->watch_pid, + _("fd"), info->watch_fd, strerror(errno)); + return 3; + } + + strlcpy(info->file_fdpath, vnodeInfo.pvip.vip_path, sizeof(info->file_fdpath)); + + info->size = 0; + + if (!(0 == stat64(info->file_fdpath, &(info->sb_fd)))) { + if (!automatic) + pv_error(state, "%s %u: %s %d: %s: %s", + _("pid"), + info->watch_pid, + _("fd"), + info->watch_fd, info->file_fdpath, + strerror(errno)); + return 3; + } + + if (filesize(info) != 0) { + if (!automatic) + pv_error(state, "%s %u: %s %d: %s: %s", + _("pid"), + info->watch_pid, + _("fd"), + info->watch_fd, + info->file_fdpath, + _("not a regular file or block device")); + return 4; + } + + return 0; +} + +#else /* * Fill in the given information structure with the file paths and stat @@ -91,25 +182,8 @@ int pv_watchfd_info(pvstate_t state, pvwatchfd_t info, int automatic) info->size = 0; - if (S_ISBLK(info->sb_fd.st_mode)) { - int fd; - - /* - * Get the size of block devices by opening - * them and seeking to the end. - */ - fd = open64(info->file_fdpath, O_RDONLY); - if (fd >= 0) { - info->size = lseek64(fd, 0, SEEK_END); - close(fd); - } else { - info->size = 0; - } - } else if (S_ISREG(info->sb_fd.st_mode)) { - if ((info->sb_fd_link.st_mode & S_IWUSR) == 0) { - info->size = info->sb_fd.st_size; - } - } else { + int ret = filesize(info); + if (ret != 0) { if (!automatic) pv_error(state, "%s %u: %s %d: %s: %s", _("pid"), @@ -118,13 +192,19 @@ int pv_watchfd_info(pvstate_t state, pvwatchfd_t info, int automatic) info->watch_fd, info->file_fdpath, _("not a regular file or block device")); - return 4; + return ret; } return 0; } +#endif - +#ifdef __APPLE__ +int pv_watchfd_changed(pvwatchfd_t info) +{ + return 1; +} +#else /* * Return nonzero if the given file descriptor has changed in some way since * we started looking at it (i.e. changed destination or permissions). @@ -147,8 +227,26 @@ int pv_watchfd_changed(pvwatchfd_t info) return 0; } +#endif +#ifdef __APPLE__ +long long pv_watchfd_position(pvwatchfd_t info) +{ + long long position; + struct vnode_fdinfowithpath vnodeInfo = {}; + int32_t proc_fd = (int32_t)info->watch_fd; + int size = proc_pidfdinfo(info->watch_pid, proc_fd, PROC_PIDFDVNODEPATHINFO, &vnodeInfo, PROC_PIDFDVNODEPATHINFO_SIZE); + if (size != PROC_PIDFDVNODEPATHINFO_SIZE) { + return -1; + } + + position = (long long)vnodeInfo.pfi.fi_offset; + + return position; +} + +#else /* * Return the current file position of the given file descriptor, or -1 if * the fd has closed or has changed in some way. @@ -170,8 +268,32 @@ long long pv_watchfd_position(pvwatchfd_t info) return position; } +#endif +#ifdef __APPLE__ +int pidfds(pvstate_t state, unsigned int pid, struct proc_fdinfo **fds, int *count) +{ + int size_needed = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, 0, 0); + if (size_needed == -1) { + pv_error(state, "%s: unable to list pid fds: %s", _("pid"), strerror(errno)); + return -1; + } + + *count = size_needed / PROC_PIDLISTFD_SIZE; + + *fds = (struct proc_fdinfo *)malloc(size_needed); + if (*fds == NULL) { + pv_error(state, "%s: alloc failed: %s", _("pid"), strerror(errno)); + return -1; + } + + proc_pidinfo(pid, PROC_PIDLISTFDS, 0, *fds, size_needed); + + return 0; +} +#endif + /* * Scan the given process and update the arrays with any new file * descriptors. @@ -185,8 +307,6 @@ int pv_watchpid_scanfds(pvstate_t state, pvstate_t pristine, pvstate_t * state_array_ptr, int *fd_to_idx) { char fd_dir[512] = { 0, }; - DIR *dptr; - struct dirent *d; int array_length = 0; struct pvwatchfd_s *info_array = NULL; struct pvstate_s *state_array = NULL; @@ -196,24 +316,48 @@ int pv_watchpid_scanfds(pvstate_t state, pvstate_t pristine, #else sprintf(fd_dir, "/proc/%u/fd", watch_pid); #endif +#ifdef __APPLE__ + struct proc_fdinfo *fd_infos = NULL; + int fd_infos_count = 0; + + if (pidfds(state, watch_pid, &fd_infos, &fd_infos_count) != 0) { + pv_error(state, "%s: pidfds failed", _("pid")); + return -1; + } + +#else + DIR *dptr; + struct dirent *d; dptr = opendir(fd_dir); if (NULL == dptr) return 1; +#endif array_length = *array_length_ptr; info_array = *info_array_ptr; state_array = *state_array_ptr; +#ifdef __APPLE__ + if (fd_infos_count < 1) { + pv_error(state, "%s: no fds found", _("pid")); + return -1; + } + for (int i = 0; i < fd_infos_count; i++) { +#else while ((d = readdir(dptr)) != NULL) { +#endif int fd, check_idx, use_idx, rc; long long position_now; fd = -1; +#ifdef __APPLE__ + fd = fd_infos[i].proc_fd; +#else if (sscanf(d->d_name, "%d", &fd) != 1) continue; if ((fd < 0) || (fd >= FD_SETSIZE)) continue; - +#endif /* * Skip if this fd is already known to us. */ @@ -296,6 +440,11 @@ int pv_watchpid_scanfds(pvstate_t state, pvstate_t pristine, info_array[use_idx].watch_pid = watch_pid; info_array[use_idx].watch_fd = fd; +#ifdef __APPLE__ + if (fd_infos[i].proc_fdtype != PROX_FDTYPE_VNODE) { + continue; + } +#endif rc = pv_watchfd_info(state, &(info_array[use_idx]), 1); /* @@ -354,7 +503,12 @@ int pv_watchpid_scanfds(pvstate_t state, pvstate_t pristine, } } + +#ifdef __APPLE__ + free(fd_infos); +#else closedir(dptr); +#endif return 0; }