--- src/killall5.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 132 insertions(+), 13 deletions(-) --- src/killall5.c +++ src/killall5.c 2023-05-02 14:14:02.198263058 +0000 @@ -67,6 +67,9 @@ #endif #define STATNAMELEN 15 +#define DO_NETFS 2 +#define DO_STAT 1 +#define NO_STAT 0 /* Info about a process. */ typedef struct proc { @@ -76,6 +79,8 @@ typedef struct proc { char *argv1; /* Name as found out from argv[1] */ char *argv1base; /* `basename argv[1]` */ char *statname; /* the statname without braces */ + ino_t ino; /* Inode number */ + dev_t dev; /* Device it is on */ pid_t pid; /* Process ID. */ pid_t sid; /* Session ID. */ char kernel; /* Kernel thread or zombie. */ @@ -473,20 +478,54 @@ int readarg(FILE *fp, char *buf, int sz) } /* + * Scan the filedescriptors of pid for /dev/fuse + */ +int is_fuse(const char *pid) { + DIR *dir; + char path[256]; + char buf[256]; + struct dirent *d; + ssize_t len; + + /* Open /proc/pid/fd/ */ + snprintf(path, sizeof(path), "/proc/%s/fd", pid); + if ((dir = opendir(path)) != NULL) { + int dfd = dirfd(dir); + /* Walk through the directory. */ + while ((d = readdir(dir)) != NULL) { + if (*d->d_name == '.') + continue; + /* check for /dev/fuse */ + if ((len = readlinkat(dfd, d->d_name, buf, sizeof(buf))) > 0) { + buf[len] = '\0'; + if (strcmp("/dev/fuse", buf) == 0) + return 1; /* Fuse filesystem */ + } + } + closedir(dir); + } + + /* Not a fuse filesystem */ + return 0; +} + +/* * Read the proc filesystem. * CWD must be /proc to avoid problems if / is affected by the killing (ie depend on fuse). */ -int readproc() +int readproc(int do_stat) { DIR *dir; FILE *fp; PROC *p, *n; struct dirent *d; + struct stat st; char path[PATH_MAX+1]; char buf[PATH_MAX+1]; char *s, *q; unsigned long startcode, endcode; int pid, f; + ssize_t len; char process_status[11]; /* Open the /proc directory. */ @@ -593,8 +632,12 @@ int readproc() p->kernel = 1; fclose(fp); if ( (! list_dz_processes) && - (strchr(process_status, 'Z') != NULL) ) { - /* Ignore zombie processes */ + ( (strchr(process_status, 'D') != NULL) || + (strchr(process_status, 'Z') != NULL) ) ){ + /* Ignore zombie processes or processes in + disk sleep, as attempts + to access the stats of these will + sometimes fail. */ if (p->argv0) free(p->argv0); if (p->argv1) free(p->argv1); if (p->statname) free(p->statname); @@ -661,11 +704,76 @@ int readproc() /* Try to stat the executable. */ snprintf(path, sizeof(path), "/proc/%s/exe", d->d_name); - p->pathname = (char *)xmalloc(PATH_MAX); - memset(p->pathname, 0, PATH_MAX); - if (readlink(path, p->pathname, PATH_MAX) == -1) { - p->pathname = NULL; - } + + p->nfs = 0; + + switch (do_stat) { + case NO_STAT: + if ((len = readlink(path, buf, PATH_MAX)) < 0) + break; + buf[len] = '\0'; + + /* Check for uevent handler, mdmon, and for providers + of FUSE filesystems */ + if ((strncmp(buf, "/usr/sbin/mdmon", 15) == 0) || + (strncmp(buf, "/sbin/mdmon", 11) == 0) || + (strncmp(buf, "/usr/lib/systemd/systemd-udevd", 30) == 0) || + (is_fuse(d->d_name))) { + OMIT *restrict optr; + + xmemalign((void*)&optr, sizeof(void*), alignof(OMIT)); + optr->next = omit; + optr->prev = (OMIT*)0; + optr->pid = pid; + omit = optr; + } + + break; + case DO_NETFS: + if ((p->nfs = check4nfs(path, buf))) + goto link; + /* else fall through */ + case DO_STAT: + if (stat(path, &st) != 0) { + char * ptr; + + len = readlink(path, buf, PATH_MAX); + if (len <= 0) + break; + buf[len] = '\0'; + + ptr = strstr(buf, " (deleted)"); + if (!ptr) + break; + *ptr = '\0'; + len -= strlen(" (deleted)"); + + if (stat(buf, &st) != 0) + break; + p->dev = st.st_dev; + p->ino = st.st_ino; + p->pathname = (char *)xmalloc(len + 1); + memcpy(p->pathname, buf, len); + p->pathname[len] = '\0'; + + /* All done */ + break; + } + + p->dev = st.st_dev; + p->ino = st.st_ino; + + /* Fall through */ + default: + link: + len = readlink(path, buf, PATH_MAX); + if (len > 0) { + p->pathname = (char *)xmalloc(len + 1); + memcpy(p->pathname, buf, len); + p->pathname[len] = '\0'; + } + break; + } /* Link it into the list. */ p->next = plist; @@ -728,6 +836,7 @@ PIDQ_HEAD *pidof(char *prog) { PROC *p; PIDQ_HEAD *q; + struct stat st; char *s; int nfs = 0; int dostat = 0; @@ -742,7 +851,15 @@ PIDQ_HEAD *pidof(char *prog) /* Try to stat the executable. */ memset(real_path, 0, sizeof(real_path)); if ( (prog[0] == '/') && ( realpath(prog, real_path) ) ) { - dostat++; + + if (check4nfs(prog, real_path)) + nfs++; + + if (real_path[0] != '\0') + prog = &real_path[0]; /* Binary located on network FS. */ + + if ((nfs == 0) && (stat(prog, &st) == 0)) + dostat++; /* Binary located on a local FS. */ } /* Get basename of program. */ @@ -758,9 +875,11 @@ PIDQ_HEAD *pidof(char *prog) q = init_pid_q(q); /* First try to find a match based on dev/ino pair. */ - if (dostat) { + if (dostat && !nfs) { for (p = plist; p; p = p->next) { - if (p->pathname && strcmp(real_path, p->pathname) == 0) { + if (p->nfs) + continue; + if (p->dev == st.st_dev && p->ino == st.st_ino) { add_pid_to_q(q, p); foundone++; } @@ -1026,7 +1145,7 @@ int main_pidof(int argc, char **argv) init_nfs(); /* Which network based FS are online? */ /* Print out process-ID's one by one. */ - readproc(); + readproc((flags & PIDOF_NETFS) ? DO_NETFS : DO_STAT); for(f = 0; f < argc; f++) { if ((q = pidof(argv[f])) != NULL) { @@ -1171,7 +1290,7 @@ int main(int argc, char **argv) } /* Read /proc filesystem */ - if (readproc() < 0) { + if (readproc(NO_STAT) < 0) { if (sent_sigstop) kill(-1, SIGCONT); return(1);