--- src/killall5.c +++ src/killall5.c 2009-03-27 11:42:17.780766281 +0100 @@ -40,6 +40,8 @@ #include #include #include +#include +#include char *Version = "@(#)killall5 2.86 31-Jul-2004 miquels@cistron.nl"; @@ -56,7 +58,8 @@ typedef struct proc { dev_t dev; /* Device it is on */ pid_t pid; /* Process ID. */ int sid; /* Session ID. */ - int kernel; /* Kernel thread or zombie. */ + char kernel; /* Kernel thread or zombie. */ + char nfs; /* Binary is loacted on NFS part. */ struct proc *next; /* Pointer to next struct. */ } PROC; @@ -73,9 +76,35 @@ typedef struct { PIDQ *next; } PIDQ_HEAD; +typedef struct shadow +{ + struct shadow *next; + struct shadow *prev; + size_t nlen; + char * name; +} SHADOW; + +typedef struct nfs +{ + struct nfs *next; /* Pointer to next struct. */ + struct nfs *prev; /* Pointer to previous st. */ + SHADOW *shadow; /* Pointer to shadows */ + char * name; + size_t nlen; +} NFS; +#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) +# ifndef restrict +# define restrict __restrict__ +# endif +#endif +#define alignof(type) ((sizeof(type)+(sizeof(void*)-1)) & ~(sizeof(void*)-1)) + /* List of processes. */ PROC *plist; +/* List of NFS mountes partitions. */ +NFS *nlist; + /* Did we stop all processes ? */ int sent_sigstop; @@ -102,6 +131,18 @@ void *xmalloc(int bytes) return p; } +#ifdef __GNUC__ +static inline void xmemalign(void **, size_t, size_t) __attribute__ ((nonnull (1))); +#endif +static inline void xmemalign(void **memptr, size_t alignment, size_t size) +{ + if ((posix_memalign(memptr, alignment, size)) < 0) { + if (sent_sigstop) kill(-1, SIGCONT); + nsyslog(LOG_ERR, "out of memory"); + exit(1); + } +} + /* * See if the proc filesystem is there. Mount if needed. */ @@ -152,6 +193,211 @@ int mount_proc(void) return did_mount; } +static inline int isnetfs(const char * type) +{ + static const char* netfs[] = {"nfs", "nfs4", "smbfs", "cifs", "afs", "ncpfs", (char*)0}; + int n; + for (n = 0; netfs[n]; n++) + if (!strcasecmp(netfs[n], type)) + return 1; + return 0; +} + +/* + * Remember all NFS typed partitions. + */ +void init_nfs(void) +{ + struct stat st; + struct mntent * ent; + FILE * mnt; + + nlist = (NFS*)0; + + if (stat("/proc/version", &st) < 0) + return; + if ((mnt = setmntent("/proc/mounts", "r")) == (FILE*)0) + return; + + while ((ent = getmntent(mnt))) { + if (isnetfs(ent->mnt_type)) { + size_t nlen = strlen(ent->mnt_dir); + NFS *restrict p; + xmemalign((void*)&p, sizeof(void*), alignof(NFS)+(nlen+1)); + p->name = ((char*)p)+alignof(NFS); + p->nlen = nlen; + p->shadow = (SHADOW*)0; + + strcpy(p->name, ent->mnt_dir); + if (nlist) + nlist->prev = p; + p->next = nlist; + p->prev = (NFS*)0; + nlist = p; + } + } + endmntent(mnt); + + if ((mnt = setmntent("/proc/mounts", "r")) == (FILE*)0) + return; + + while ((ent = getmntent(mnt))) { + NFS *p; + + for (p = nlist; p; p = p->next) { + SHADOW * restrict s; + size_t nlen; + + if (strcmp(ent->mnt_dir, p->name) == 0) + continue; + if (strncmp(ent->mnt_dir, p->name, p->nlen) != 0) + continue; + + nlen = strlen(ent->mnt_dir); + xmemalign((void*)&s, sizeof(void*), alignof(SHADOW)+(nlen+1)); + s->name = ((char*)s)+alignof(SHADOW); + s->nlen = nlen; + + strcpy(s->name, ent->mnt_dir); + if (p->shadow) + p->shadow->prev = s; + s->next = p->shadow; + s->prev = (SHADOW*)0; + p->shadow = s; + } + } + endmntent(mnt); +} + +static void clear_shadow(SHADOW *restrict shadow) +{ + SHADOW *s, *n, *l; + + n = shadow; + l = (SHADOW*)0; + for (s = shadow; n; s = n) { + l = s->prev; + n = s->next; + if (s == shadow) { + if (n) n->prev = (SHADOW*)0; + shadow = n; + } else if (l) { + if (n) n->prev = l; + l->next = n; + } + free(s); + } +} + +static void clear_mnt(void) +{ + NFS *p, *n, *l; + + n = nlist; + l = (NFS*)0; + for (p = nlist; n; p = n) { + l = p->prev; + n = p->next; + if (p == nlist) { + if (n) n->prev = (NFS*)0; + nlist = n; + } else if (l) { + if (n) n->prev = l; + l->next = n; + } + if (p->shadow) + clear_shadow(p->shadow); + free(p); + } +} + +/* + * Check if path is ia shadow off a NFS partition. + */ +static int shadow(SHADOW *restrict this, const char *restrict name, const size_t nlen) +{ + SHADOW *s; + + if (!this) + goto out; + for (s = this; s; s = s->next) { + if (nlen < s->nlen) + continue; + if (name[s->nlen] != '\0' && name[s->nlen] != '/') + continue; + if (strncmp(name, s->name, s->nlen) == 0) + return 1; + } +out: + return 0; +} + +/* + * Check path is located on a NFS partition. + */ +int check4nfs(const char * path, char * real) +{ + char buf[PATH_MAX+1]; + const char *curr; + int deep = MAXSYMLINKS; + + if (!nlist) return 0; + + curr = path; + do { + const char *prev; + int len; + + if ((prev = strdupa(curr)) == NULL) { + nsyslog(LOG_ERR, "strdupa(): %s\n", strerror(errno)); + return 0; + } + + errno = 0; + if ((len = readlink(curr, buf, PATH_MAX)) < 0) + break; + buf[len] = '\0'; + + if (buf[0] != '/') { + const char *slash; + + if ((slash = strrchr(prev, '/'))) { + size_t off = slash - prev + 1; + + if (off + len > PATH_MAX) + len = PATH_MAX - off; + + memmove(&buf[off], &buf[0], len + 1); + memcpy(&buf[0], prev, off); + } + } + curr = &buf[0]; + + if (deep-- <= 0) return 0; + + } while (1); + + if (real) strcpy(real, curr); + + if (errno == EINVAL) { + const size_t nlen = strlen(curr); + NFS *p; + for (p = nlist; p; p = p->next) { + if (nlen < p->nlen) + continue; + if (curr[p->nlen] != '\0' && curr[p->nlen] != '/') + continue; + if (!strncmp(curr, p->name, p->nlen)) { + if (shadow(p->shadow, curr, nlen)) + continue; + return 1; + } + } + } + + return 0; +} + int readarg(FILE *fp, char *buf, int sz) { int c = 0, f = 0; @@ -173,8 +419,8 @@ int readproc() PROC *p, *n; struct dirent *d; struct stat st; - char path[256]; - char buf[256]; + char path[PATH_MAX+1]; + char buf[PATH_MAX+1]; char *s, *q; unsigned long startcode, endcode; int pid, f; @@ -191,6 +437,7 @@ int readproc() n = p->next; if (p->argv0) free(p->argv0); if (p->argv1) free(p->argv1); + if (p->statname) free(p->statname); free(p); } plist = NULL; @@ -225,6 +472,9 @@ int readproc() nsyslog(LOG_ERR, "can't get program name from %s\n", path); + if (p->argv0) free(p->argv0); + if (p->argv1) free(p->argv1); + if (p->statname) free(p->statname); free(p); continue; } @@ -248,6 +498,9 @@ int readproc() p->sid = 0; nsyslog(LOG_ERR, "can't read sid from %s\n", path); + if (p->argv0) free(p->argv0); + if (p->argv1) free(p->argv1); + if (p->statname) free(p->statname); free(p); continue; } @@ -256,6 +509,9 @@ int readproc() fclose(fp); } else { /* Process disappeared.. */ + if (p->argv0) free(p->argv0); + if (p->argv1) free(p->argv1); + if (p->statname) free(p->statname); free(p); continue; } @@ -300,13 +556,18 @@ int readproc() } else { /* Process disappeared.. */ + if (p->argv0) free(p->argv0); + if (p->argv1) free(p->argv1); + if (p->statname) free(p->statname); free(p); continue; } /* Try to stat the executable. */ snprintf(path, sizeof(path), "/proc/%s/exe", d->d_name); - if (stat(path, &st) == 0) { + if (check4nfs(path, NULL)) + p->nfs = 1; + if ((p->nfs == 0) && (stat(path, &st) == 0)) { p->dev = st.st_dev; p->ino = st.st_ino; } @@ -374,12 +635,25 @@ PIDQ_HEAD *pidof(char *prog) PIDQ_HEAD *q; struct stat st; char *s; + int nfs = 0; int dostat = 0; int foundone = 0; int ok = 0; + char real[PATH_MAX+1]; /* Try to stat the executable. */ - if (prog[0] == '/' && stat(prog, &st) == 0) dostat++; + if (prog[0] == '/') { + memset(&real[0], 0, sizeof(real)); + + if (check4nfs(prog, real)) + nfs++; /* Binary located on NFS partition. */ + + if (real[0] != '\0') + prog = &real[0]; /* Binary or its symlink located on NFS. */ + + if ((nfs == 0) && (stat(prog, &st) == 0)) + dostat++; /* Binary located on a local file system. */ + } /* Get basename of program. */ if ((s = strrchr(prog, '/')) == NULL) @@ -393,10 +667,30 @@ PIDQ_HEAD *pidof(char *prog) /* First try to find a match based on dev/ino pair. */ if (dostat) { for (p = plist; p; p = p->next) { - if (p->dev == st.st_dev && p->ino == st.st_ino) { - add_pid_to_q(q, p); - foundone++; - } + if (p->nfs) + continue; + if (p->dev != st.st_dev || p->ino != st.st_ino) + continue; + add_pid_to_q(q, p); + foundone++; + } + } + + /* Second try to find a match based on full path name on NFS located binaries */ + if (!foundone && nfs) { + for (p = plist; p; p = p->next) { + char exe [PATH_MAX+1]; + char path[PATH_MAX+1]; + int len; + + snprintf(exe, sizeof(exe), "/proc/%d/exe", p->pid); + if ((len = readlink(exe, path, PATH_MAX)) < 0) + continue; + path[len] = '\0'; + if (strcmp(prog, path) != 0) + continue; + add_pid_to_q(q, p); + foundone++; } } @@ -428,7 +722,7 @@ PIDQ_HEAD *pidof(char *prog) if (ok) add_pid_to_q(q, p); } - return q; + return q; } /* Give usage message and exit. */ @@ -477,6 +771,9 @@ int main_pidof(int argc, char **argv) int first = 1; int i, oind, opt, flags = 0; + /* Which NFS partitions are online? */ + init_nfs(); + for (oind = PIDOF_OMITSZ-1; oind > 0; oind--) opid[oind] = 0; opterr = 0; @@ -561,6 +858,7 @@ int main(int argc, char **argv) PROC *p; int pid, sid = -1; int sig = SIGKILL; + int did_mount; /* Get program name. */ if ((progname = strrchr(argv[0], '/')) == NULL) @@ -583,7 +881,10 @@ int main(int argc, char **argv) } /* First get the /proc filesystem online. */ - mount_proc(); + did_mount = mount_proc(); + + /* Which NFS partitions are online? */ + init_nfs(); /* * Ignoring SIGKILL and SIGSTOP do not make sense, but @@ -604,13 +905,18 @@ int main(int argc, char **argv) kill(-1, SIGCONT); exit(1); } + clear_mnt(); /* Now kill all processes except our session. */ sid = (int)getsid(0); pid = (int)getpid(); - for (p = plist; p; p = p->next) - if (p->pid != pid && p->sid != sid && !p->kernel) - kill(p->pid, sig); + for (p = plist; p; p = p->next) { + if (p->pid == 1 || p->pid == pid || p->sid == sid || p->kernel) { + kill(p->pid, SIGCONT); + continue; + } + kill(p->pid, sig); + } /* And let them continue. */ kill(-1, SIGCONT);