:: [devuan-dev] bug#227: nbd-client: A…
Góra strony
Delete this message
Reply to this message
Autor: Jesse Smith
Data:  
Dla: Mark Hindley
CC: David Kuehling, 227
Temat: [devuan-dev] bug#227: nbd-client: At shutdown nbd-client disabled before file-systems could be cleanly unmounted
On 2023-02-16 12:17 p.m., Mark Hindley wrote:
> Control: reassign -1 sysvinit-utils
> Control: affects -1 nbd-client
>
> Jesse,
>
> Thanks for you quick reply.
>
> On Thu, Feb 16, 2023 at 11:54:32AM -0400, Jesse Smith wrote:
>> I see two possible ways we could fix this:
>>
>> 1. Create a command line flag which disables the SIGSTOP and SIGCONT
>> signals being sent. This is an easy fix, quick and dirty. The potential
>> downside is if someone disables the STOP signal then maybe processes
>> terminate, move groups, or are replaced before we get around to sending
>> them the KILL signal. This probably won't happen, but it means killall5
>> is working with a "moving target".
>>
>> 2. We can run SIGSTOP on all processes _except_ those in the omit list.
>> This will be a lot slower than the existing "kill(-1, SIGSTOP)" call we
>> currently make. But I think it's more correct.
>>
>> Basically the new work flow would look like this:
>>
>> 1. Send all processes except those omitted the SIGSTOP command.
>> 2. Send all processes except those omitted the SIGKILL command.
>> 3. Send all processes except those omitted the SIGCONT command.
>>
>> Option #2 is slow and ugly, but seems "correct" from a behaviour point
>> of view.
> I don't really like #1: it will mean patching callers to get the (more?) correct
> behaviour. So #2 seems better. Will it be significantly slower?. You could only
> use it when omitpid is specified? If there are no omitpids, the current
> behaviour seems fine.
>


I looked at this issue some more and it turns out option #2 doesn't
really makes sense with the way killall5 works. The SIGSTOP signal is
sent out to basically pause everything on the system so we can sort
through what is available to be terminated and what should be spared. If
we want to go through the whole process tree to see what should or
should not be stopped first, then it removes the point of sending
SIGSTOP to give us a fixed collection of processes.

In other words, sending SIGSTOP only makes sense if we later want to
freeze everything and then sort processes into what we will terminate
and what will be omitted. If we are going to go through all the
processes and send some the SIGSTOP signal and not others, then we might
as well just skip the STOP and move straight on to terminating
appropriate processes.

I've tried to work in a reasonable middle ground. Now, if no processes
are in the "omit" list, we stick to the original behaviour. We stop
everything, sort through which processes we will terminate, then send
everything the SIGCONT signal. Just like before.

However, if there are any processes in the "omit" list, then we err on
the side of caution. Nothing is sent STOP or CONT, we just sort through
the processes we know about quickly and try to terminate everything
we're supposed to kill. And ignore anything in the process list.

It's not perfect, we could conceivably miss a process that was created
while we were sorting through the list after the point we'd normally
send SIGSTOP. However, this approach makes certain we do not damage any
process that can't handle receiving SIGSTOP.

The downside is that a process could get created after killall5 is run
and before it is terminated. However, this is always going to be the
case when we allow some programs to be in the "omit" list. Any of the
omitted programs could spawn a new process, so that risk already
existed. This doesn't really make the situation (much) worse.

I've tested this change and it seems to work. I'm attaching a new copy
of killall5 for testing. It's also available on GitHub in the 3.07
branch of sysvinit.

Also, as a bonus, a bunch of the killall5 code didn't initialize
variables, seeming to assume a specific code path or that the compiler
would initialize variables to zero/NULL. Since this isn't always true,
I've initialized variables we might now use in unexpected order or which
should be NULL/false at the start of the program.

- Jesse

/*
 * killall5.c    Kill all processes except processes that have the
 *        same session id, so that the shell that called us
 *        won't be killed. Typically used in shutdown scripts.
 *
 * pidof.c    Tries to get the pid of the process[es] named.
 *
 * Version:    2.86 30-Jul-2004 MvS
 *
 * Usage:    killall5 [-][signal]
 *        pidof [-s] [-o omitpid [-o omitpid]] program [program..]
 *
 * Authors:    Miquel van Smoorenburg, miquels@???
 *
 *        Riku Meskanen, <mesrik@???>
 *        - return all running pids of given program name
 *        - single shot '-s' option for backwards compatibility
 *        - omit pid '-o' option and %PPID (parent pid metavariable)
 *        - syslog() only if not a connected to controlling terminal
 *        - swapped out programs pids are caught now
 *
 *        Werner Fink
 *        - make omit dynamic
 *        - provide '-n' to skip stat(2) syscall on network based FS
 *
 *        This file is part of the sysvinit suite,
 *        Copyright (C) 1991-2004 Miquel van Smoorenburg.
 *
 *        This program is free software; you can redistribute it and/or modify
 *        it under the terms of the GNU General Public License as published by
 *        the Free Software Foundation; either version 2 of the License, or
 *        (at your option) any later version.
 *
 *        This program is distributed in the hope that it will be useful,
 *        but WITHOUT ANY WARRANTY; without even the implied warranty of
 *        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *        GNU General Public License for more details.
 *
 *        You should have received a copy of the GNU General Public License
 *        along with this program; if not, write to the Free Software
 *        Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
#include <dirent.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <mntent.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <syslog.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>


#ifndef PATH_MAX
#  ifdef MAXPATHLEN
#    define PATH_MAX MAXPATHLEN
#  else
#    define PATH_MAX 2048
#  endif
#endif


#define STATNAMELEN    15


/* Info about a process. */
typedef struct proc {
    char *pathname;        /* full path to executable        */
    char *argv0;        /* Name as found out from argv[0] */
    char *argv0base;    /* `basename argv[1]`          */
    char *argv1;        /* Name as found out from argv[1] */
    char *argv1base;    /* `basename argv[1]`          */
    char *statname;        /* the statname without braces    */
    pid_t pid;        /* Process ID.              */
    pid_t sid;        /* Session ID.              */
    char kernel;        /* Kernel thread or zombie.      */
    char nfs;        /* Name found on network FS.      */
    struct proc *next;    /* Pointer to next struct.       */
} PROC;


/* pid queue */

typedef struct pidq {
    PROC        *proc;
    struct pidq    *next;
} PIDQ;


typedef struct {
    PIDQ        *head;
    PIDQ        *tail;
    PIDQ        *next;
} PIDQ_HEAD;


typedef struct _s_omit {
    struct _s_omit *next;
    struct _s_omit *prev;
    pid_t pid;
} OMIT;


typedef struct _s_shadow
{
    struct _s_shadow *next;
    struct _s_shadow *prev;
    size_t nlen;
    char * name;
} SHADOW;


typedef struct _s_nfs
{
    struct _s_nfs *next;    /* Pointer to next struct. */
    struct _s_nfs *prev;    /* Pointer to previous st. */
    SHADOW *shadow;        /* Pointer to shadows      */
    size_t nlen;
    char * name;
} NFS;


/* List of processes. */
PROC *plist = NULL;

/* List of processes to omit. */
OMIT *omit = NULL;

/* List of NFS mountes partitions. */
NFS *nlist = NULL;

/* Did we stop all processes ? */
int sent_sigstop = 0;
int scripts_too = 0;

/* Should pidof try to list processes in I/O wait (D) and zombie (Z) states? */
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
int list_dz_processes = FALSE;

char *progname;    /* the name of the running program */
#ifdef __GNUC__
__attribute__ ((format (printf, 2, 3)))
#endif
void nsyslog(int pri, char *fmt, ...);


#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
# ifndef  inline
#  define inline    __inline__
# endif
# ifndef  restrict
#  define restrict    __restrict__
# endif
#endif
#define alignof(type)    ((sizeof(type)+(sizeof(void*)-1)) & ~(sizeof(void*)-1))


/*
 *    Malloc space, barf if out of memory.
 */
#ifdef __GNUC__
static void *xmalloc(size_t) __attribute__ ((__malloc__));
#endif
static void *xmalloc(size_t bytes)
{
    void *p;


    if ((p = malloc(bytes)) == NULL) {
        if (sent_sigstop) kill(-1, SIGCONT);
        nsyslog(LOG_ERR, "out of memory");
        exit(1);
    }
    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.
 */
int mount_proc(void)
{
    struct stat    st;
    char        *args[] = { "mount", "-t", "proc", "proc", "/proc", 0 };
    pid_t        pid, rc;
    int        wst;
    int        did_mount = 0;


    /* Stat /proc/version to see if /proc is mounted. */
    if (stat("/proc/version", &st) < 0 && errno == ENOENT) {


        /* It's not there, so mount it. */
        if ((pid = fork()) < 0) {
            nsyslog(LOG_ERR, "cannot fork");
            exit(1);
        }
        if (pid == 0) {
            /* Try a few mount binaries. */
            execv("/bin/mount", args);
            execv("/sbin/mount", args);


            /* Okay, I give up. */
            nsyslog(LOG_ERR, "cannot execute mount");
            exit(1);
        }
        /* Wait for child. */
        while ((rc = wait(&wst)) != pid)
            if (rc < 0 && errno == ECHILD)
                break;
        if (rc != pid || WEXITSTATUS(wst) != 0)
            nsyslog(LOG_ERR, "mount returned non-zero exit status");


        did_mount = 1;
    }


    /* See if mount succeeded. */
    if (stat("/proc/version", &st) < 0) {
        if (errno == ENOENT)
            nsyslog(LOG_ERR, "/proc not mounted, failed to mount.");
        else
            nsyslog(LOG_ERR, "/proc unavailable.");
        exit(1);
    }


    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 a 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;
}


/*
 * Get the maximal number of symlinks to follow.  Use sysconf() on
 * Hurd where the hardcoded value MAXSYMLINKS is not available.
 */
static int maxsymlinks(void)
{
    int v = sysconf(_SC_SYMLOOP_MAX);
#ifdef MAXSYMLINKS
    if (v == -1)
        return MAXSYMLINKS;
#endif
    return v;
}


/*
 *     Check path is located on a network based 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)     /* real is defined elsewhere as being PATH_MAX + 1 */
        {
           memset(real, '\0', PATH_MAX + 1);
           strncpy(real, curr, PATH_MAX);
        }


    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;


    while (f < (sz-1) && (c = fgetc(fp)) != EOF && c)
        buf[f++] = c;
    buf[f] = 0;


    return (c == EOF && f == 0) ? c : f;
}


/*
 *    Read the proc filesystem.
 *    CWD must be /proc to avoid problems if / is affected by the killing (ie depend on fuse).
 */
int readproc()
{
    DIR        *dir;
    FILE        *fp;
    PROC        *p, *n;
    struct dirent    *d;
    char        path[PATH_MAX+1];
    char        buf[PATH_MAX+1];
    char        *s, *q;
    unsigned long    startcode, endcode;
    int        pid, f;
        char            process_status[11];


    /* Open the /proc directory. */
    if (chdir("/proc") == -1) {
        nsyslog(LOG_ERR, "chdir /proc failed");
        return -1;
    }
    if ((dir = opendir(".")) == NULL) {
        nsyslog(LOG_ERR, "cannot opendir(/proc)");
        return -1;
    }


    /* Free the already existing process list. */
    n = plist;
    for (p = plist; n; p = n) {
        n = p->next;
        if (p->argv0) free(p->argv0);
        if (p->argv1) free(p->argv1);
        if (p->statname) free(p->statname);
        if (p->pathname) free(p->pathname);
        free(p);
    }
    plist = NULL;


    /* Walk through the directory. */
    while ((d = readdir(dir)) != NULL) {


        /* See if this is a process */
        if ((pid = atoi(d->d_name)) == 0) continue;


        /* Get a PROC struct . */
        p = (PROC *)xmalloc(sizeof(PROC));
        memset(p, 0, sizeof(PROC));


        /* Open the status file. */
        snprintf(path, sizeof(path), "%s/stat", d->d_name);


        /* Read SID & statname from it. */
        if ((fp = fopen(path, "r")) != NULL) {
            size_t len;


            len = fread(buf, sizeof(char), sizeof(buf)-1, fp);
            buf[len] = '\0';


            if (buf[0] == '\0') {
                nsyslog(LOG_ERR,
                    "can't read from %s\n", path);
                fclose(fp);
                free(p);
                continue;
            }


            /* See if name starts with '(' */
            s = buf;
            while (*s && *s != ' ') s++;
            if (*s) s++;
            if (*s == '(') {
                /* Read program name. */
                q = strrchr(buf, ')');
                if (q == NULL) {
                    p->sid = 0;
                    nsyslog(LOG_ERR,
                    "can't get program name from /proc/%s\n",
                        path);
                    fclose(fp);
                    if (p->argv0) free(p->argv0);
                    if (p->argv1) free(p->argv1);
                    if (p->statname) free(p->statname);
                    if (p->pathname) free(p->pathname);
                    free(p);
                    continue;
                }
                s++;
            } else {
                q = s;
                while (*q && *q != ' ') q++;
            }
            if (*q) *q++ = 0;
            while (*q == ' ') q++;
            p->statname = (char *)xmalloc(strlen(s)+1);
            strcpy(p->statname, s);


            /* Get session, startcode, endcode. */
            startcode = endcode = 0;
            if (sscanf(q,   "%10s %*d %*d %d %*d %*d %*u %*u "
                    "%*u %*u %*u %*u %*u %*d %*d "
                    "%*d %*d %*d %*d %*u %*u %*d "
                    "%*u %lu %lu",
                    process_status, 
                    &p->sid, &startcode, &endcode) != 4) {


                p->sid = 0;
                nsyslog(LOG_ERR, "can't read sid from %s\n",
                    path);
                fclose(fp);
                if (p->argv0) free(p->argv0);
                if (p->argv1) free(p->argv1);
                if (p->statname) free(p->statname);
                free(p->pathname);
                free(p);
                continue;
            }
            if (startcode == 0 && endcode == 0)
                p->kernel = 1;
            fclose(fp);
                        if ( (! list_dz_processes) &&
                               (strchr(process_status, 'Z') != NULL) ) {
                           /* Ignore zombie processes */
                              if (p->argv0) free(p->argv0);
                              if (p->argv1) free(p->argv1);
                              if (p->statname) free(p->statname);
                             free(p);
                             continue;
                        }
        } else {
            /* Process disappeared.. */
            if (p->argv0) free(p->argv0);
            if (p->argv1) free(p->argv1);
            if (p->statname) free(p->statname);
            if (p->pathname) free(p->pathname);
            free(p);
            continue;
        }


        snprintf(path, sizeof(path), "%s/cmdline", d->d_name);
        if ((fp = fopen(path, "r")) != NULL) {


            /* Now read argv[0] */
            f = readarg(fp, buf, sizeof(buf));


            if (buf[0]) {
                /* Store the name into malloced memory. */
                p->argv0 = (char *)xmalloc(f + 1);
                strcpy(p->argv0, buf);


                /* Get a pointer to the basename. */
                p->argv0base = strrchr(p->argv0, '/');
                if (p->argv0base != NULL)
                    p->argv0base++;
                else
                    p->argv0base = p->argv0;
            }


            /* And read argv[1] */
            while ((f = readarg(fp, buf, sizeof(buf))) != EOF)
                if (buf[0] != '-') break;


            if (buf[0]) {
                /* Store the name into malloced memory. */
                p->argv1 = (char *)xmalloc(f + 1);
                strcpy(p->argv1, buf);


                /* Get a pointer to the basename. */
                p->argv1base = strrchr(p->argv1, '/');
                if (p->argv1base != NULL)
                    p->argv1base++;
                else
                    p->argv1base = p->argv1;
            }


            fclose(fp);


        } else {
            /* Process disappeared.. */
            if (p->argv0) free(p->argv0);
            if (p->argv1) free(p->argv1);
            if (p->statname) free(p->statname);
            if (p->pathname) free(p->pathname);
            free(p);
            continue;
        }


        /* Try to stat the executable. */
        snprintf(path, sizeof(path), "/proc/%s/exe", d->d_name);
                p->pathname = (char *)xmalloc(PATH_MAX);
         if (readlink(path, p->pathname, PATH_MAX) == -1) {
             p->pathname = NULL;
         }


        /* Link it into the list. */
        p->next = plist;
        plist = p;
        p->pid = pid;
    }
    closedir(dir);


    /* Done. */
    return 0;
}


PIDQ_HEAD *init_pid_q(PIDQ_HEAD *q)
{
    q->head =  q->next = q->tail = NULL;
    return q;
}


int empty_q(PIDQ_HEAD *q)
{
    return (q->head == NULL);
}


int add_pid_to_q(PIDQ_HEAD *q, PROC *p)
{
    PIDQ *tmp;


    tmp = (PIDQ *)xmalloc(sizeof(PIDQ));


    tmp->proc = p;
    tmp->next = NULL;


    if (empty_q(q)) {
        q->head = tmp;
        q->tail  = tmp;
    } else {
        q->tail->next = tmp;
        q->tail = tmp;
    }
    return 0;
}


PROC *get_next_from_pid_q(PIDQ_HEAD *q)
{
    PROC        *p;
    PIDQ        *tmp = q->head;


    if (!empty_q(q)) {
        p = q->head->proc;
        q->head = tmp->next;
        free(tmp);
        return p;
    }


    return NULL;
}


/* Try to get the process ID of a given process. */
PIDQ_HEAD *pidof(char *prog)
{
    PROC        *p;
    PIDQ_HEAD    *q;
    char        *s;
    int        nfs = 0;
    int        dostat = 0;
    int        foundone = 0;
    int        ok = 0;
    const int    root = (getuid() == 0);
    char        real_path[PATH_MAX+1];


    if (! prog)
        return NULL;


    /* Try to stat the executable. */
    if ( (prog[0] == '/') && ( realpath(prog, real_path) ) ) {
        memset(&real_path[0], 0, sizeof(real_path));
        dostat++;
    }


    /* Get basename of program. */
    if ((s = strrchr(prog, '/')) == NULL)
        s = prog;
    else
        s++;


    if (! *s)
        return NULL;


    q = (PIDQ_HEAD *)xmalloc(sizeof(PIDQ_HEAD));
    q = init_pid_q(q);


    /* First try to find a match based on dev/ino pair. */
    if (dostat) {
        for (p = plist; p; p = p->next) {
            if (p->pathname && strcmp(real_path, p->pathname) == 0) {
                add_pid_to_q(q, p);
                foundone++;
            }
        }
    }


    /* Second try to find a match based on full path name on
     * network FS located binaries */
    if (!foundone && nfs) {
        for (p = plist; p; p = p->next) {
            if (!p->pathname)
                continue;
            if (!p->nfs)
                continue;
            if (strcmp(prog, p->pathname) != 0)
                continue;
            add_pid_to_q(q, p);
            foundone++;
        }
    }


    /* If we didn't find a match based on dev/ino, try the name. */
    if (!foundone) for (p = plist; p; p = p->next) {
        if (prog[0] == '/') {
            if (!p->pathname) {
                if (root)
                    continue;
                goto fallback; 
            }
            if (strcmp(prog, p->pathname)) {
                int len = strlen(prog);
                if (strncmp(prog, p->pathname, len))
                {
                    if (scripts_too)
                        goto fallback;
                    continue;
                }
                if (strcmp(" (deleted)", p->pathname + len))
                {
                    if (scripts_too)
                        goto fallback;
                    continue;
                }
            }
            add_pid_to_q(q, p);
            continue;
        }


    fallback:
        ok = 0;


        /*             matching        nonmatching
         * proc name   prog name       prog name
         * ---         -----------     ------------
         *   b         b, p/b, q/b
         * p/b         b, p/b          q/b
         *
         * Algorithm: Match if:
         *    cmd = arg
         * or cmd = base(arg)
         * or base(cmd) = arg
         *
         * Specifically, do not match just because base(cmd) = base(arg)
         * as was done in earlier versions of this program, since this
         * allows /aaa/foo to match /bbb/foo .
         */
        ok |=
            (p->argv0 && strcmp(p->argv0, prog) == 0)
            || (p->argv0 && s != prog && strcmp(p->argv0, s) == 0)
            || (p->argv0base && strcmp(p->argv0base, prog) == 0);


        /* For scripts, compare argv[1] as well. */
        if (
            scripts_too && p->statname && p->argv1base
            && !strncmp(p->statname, p->argv1base, STATNAMELEN)
        ) {
            ok |=
                (p->argv1 && strcmp(p->argv1, prog) == 0)
                || (p->argv1 && s != prog && strcmp(p->argv1, s) == 0)
                || (p->argv1base && strcmp(p->argv1base, prog) == 0);
        }


        /*
         *    if we have a space in argv0, process probably
         *    used setproctitle so try statname.
         */
        if (strlen(s) <= STATNAMELEN &&
            (p->argv0 == NULL ||
             p->argv0[0] == 0 ||
             strchr(p->argv0, ' '))) {
            ok |= (strcmp(p->statname, s) == 0);
        }


        /*
         *    if we have a `-' as the first character, process
         *    probably used as a login shell
         */
        if (strlen(s) <= STATNAMELEN &&
            p->argv1 == NULL &&
            (p->argv0 != NULL &&
             p->argv0[0] == '-')) {
            ok |= (strcmp(p->statname, s) == 0);
        }


        if (ok) add_pid_to_q(q, p);
    }


    return q;
}


/* Give usage message and exit. */
void usage(void)
{
    nsyslog(LOG_ERR, "usage: killall5 -signum [-o omitpid] [-o omitpid] ...");
    closelog();
    exit(1);
}



void pidof_usage(void)
{
   printf("pidof usage: [options] <program-name>\n\n");
   printf(" -c           Return PIDs with the same root directory\n");
   printf(" -d <sep>     Use the provided character as output separator\n");
   printf(" -h           Display this help text\n");
   printf(" -n           Avoid using stat system function on network shares\n");
   printf(" -o <pid>     Omit results with a given PID\n");
   printf(" -q           Quiet mode. Do not display output\n");
   printf(" -s           Only return one PID\n");
   printf(" -x           Return PIDs of shells running scripts with a matching name\n");
   printf(" -z           List zombie and I/O waiting processes. May cause pidof to hang.\n");
   printf("\n");
}



/* write to syslog file if not open terminal */
#ifdef __GNUC__
__attribute__ ((format (printf, 2, 3)))
#endif
void nsyslog(int pri, char *fmt, ...)
{
    va_list  args;


    va_start(args, fmt);


    if (ttyname(0) == NULL) {
        vsyslog(pri, fmt, args);
    } else {
        fprintf(stderr, "%s: ",progname);
        vfprintf(stderr, fmt, args);
        fprintf(stderr, "\n");
    }


    va_end(args);
}


#define PIDOF_SINGLE    0x01
#define PIDOF_OMIT    0x02
#define PIDOF_NETFS    0x04
#define PIDOF_QUIET     0x08


/*
 *    Pidof functionality.
 */
int main_pidof(int argc, char **argv)
{
    PIDQ_HEAD    *q;
    PROC        *p;
    char        *token, *here;
    int        f;
    int        first = 1;
    int        opt, flags = 0;
    int        chroot_check = 0;
    struct stat    st;
    char        tmp[512];
        char            sep = ' ';


    omit = (OMIT*)0;
    nlist = (NFS*)0;
    opterr = 0;


    if ((token = getenv("PIDOF_NETFS")) && (strcmp(token,"no") != 0))
        flags |= PIDOF_NETFS;


    while ((opt = getopt(argc,argv,"qhco:d:sxzn")) != EOF) switch (opt) {
        case '?':
            nsyslog(LOG_ERR,"invalid options on command line!\n");
            closelog();
            exit(1);
        case 'c':
            if (geteuid() == 0) chroot_check = 1;
            break;
                case 'h':
                        pidof_usage();
                        exit(0);
                case 'd':
                        sep = optarg[0];
                        break;
        case 'o':
            here = optarg;
            while ((token = strsep(&here, ",;:"))) {
                OMIT *restrict optr;
                pid_t opid;


                if (strcmp("%PPID", token) == 0)
                    opid = getppid();
                else
                    opid = (pid_t)atoi(token);


                if (opid < 1) {
                    nsyslog(LOG_ERR,
                        "illegal omit pid value "
                        "(%s)!\n", token);
                    continue;
                }
                xmemalign((void*)&optr, sizeof(void*), alignof(OMIT));
                optr->next = omit;
                optr->prev = (OMIT*)0;
                optr->pid  = opid;
                omit = optr;
            }
            flags |= PIDOF_OMIT;
            break;
                case 'q':
                        flags |= PIDOF_QUIET;
                        break; 
        case 's':
            flags |= PIDOF_SINGLE;
            break;
        case 'x':
            scripts_too++;
            break;
                case 'z':
                        list_dz_processes = TRUE;
                        break;
        case 'n':
            flags |= PIDOF_NETFS;
            break;
        default:
            /* Nothing */
            break;
    }
    argc -= optind;
    argv += optind;


    /* Check if we are in a chroot */
    if (chroot_check) {
        snprintf(tmp, 512, "/proc/%d/root", getpid());
        if (stat(tmp, &st) < 0) {
            nsyslog(LOG_ERR, "stat failed for %s!\n", tmp);
            closelog();
            exit(1);
        }
    }


    if (flags & PIDOF_NETFS)
        init_nfs();        /* Which network based FS are online? */


    /* Print out process-ID's one by one. */
    readproc();


    for(f = 0; f < argc; f++) {
        if ((q = pidof(argv[f])) != NULL) {
            pid_t spid = 0;
            while ((p = get_next_from_pid_q(q))) {
                if ((flags & PIDOF_OMIT) && omit) {
                    OMIT * optr;
                    for (optr = omit; optr; optr = optr->next) {
                        if (optr->pid == p->pid)
                            break;
                    }


                    /*
                     *    On a match, continue with
                     *    the for loop above.
                     */
                    if (optr)
                        continue;
                }
                if (flags & PIDOF_SINGLE) {
                    if (spid)
                        continue;
                    else
                        spid = 1;
                }
                if (chroot_check) {
                    struct stat st2;
                    snprintf(tmp, 512, "/proc/%d/root",
                         p->pid);
                    if (stat(tmp, &st2) < 0 ||
                        st.st_dev != st2.st_dev ||
                        st.st_ino != st2.st_ino) {
                        continue;
                    }
                }


                if ( ~flags & PIDOF_QUIET ) {
                    if (! first)
                        printf("%c", sep);
                    printf("%d", p->pid);
                }
                first = 0;
            }
        }
    }
    if (!first)
        {
            if ( ~flags & PIDOF_QUIET ) 
        printf("\n");
        }


    clear_mnt();


    closelog();
    return(first ? 1 : 0);
}


/* Main for either killall or pidof. */
int main(int argc, char **argv)
{
    PROC        *p;
    int        pid, sid = -1;
    int        sig = SIGKILL;
    int        c;


    /* return non-zero if no process was killed */
    int        retval = 2;


    /* Get program name. */
    if ((progname = strrchr(argv[0], '/')) == NULL)
        progname = argv[0];
    else
        progname++;


    /* Now connect to syslog. */
    openlog(progname, LOG_CONS|LOG_PID, LOG_DAEMON);


    /* Were we called as 'pidof' ? */
    if (strcmp(progname, "pidof") == 0)
        return main_pidof(argc, argv);


    /* Right, so we are "killall". */
    omit = (OMIT*)0;


    if (argc > 1) {
        for (c = 1; c < argc; c++) {
            if (argv[c][0] == '-') (argv[c])++;
            if (argv[c][0] == 'o') {
                char * token, * here;


                if (++c >= argc)
                    usage();


                here = argv[c];
                while ((token = strsep(&here, ",;:"))) {
                    OMIT *restrict optr;
                    pid_t opid = (pid_t)atoi(token);


                    if (opid < 1) {
                        nsyslog(LOG_ERR,
                            "illegal omit pid value "
                            "(%s)!\n", token);
                        continue;
                    }
                    xmemalign((void*)&optr, sizeof(void*), alignof(OMIT));
                    optr->next = omit;
                    optr->prev = (OMIT*)0;
                    optr->pid  = opid;
                    omit = optr;
                }
            }
            else if ((sig = atoi(argv[1])) <= 0 || sig > 31)
                usage();
        }
    }


    /* First get the /proc filesystem online. */
    mount_proc();


    /*
     *    Ignoring SIGKILL and SIGSTOP do not make sense, but
     *    someday kill(-1, sig) might kill ourself if we don't
     *    do this. This certainly is a valid concern for SIGTERM-
     *    Linux 2.1 might send the calling process the signal too.
     */
    signal(SIGTERM, SIG_IGN);
    signal(SIGSTOP, SIG_IGN);
    signal(SIGKILL, SIG_IGN);


    /* lock us into memory */
    mlockall(MCL_CURRENT | MCL_FUTURE);


        /* Get our session ID and PID to make sure we do not kill ourselves or our session. */
    sid = (int)getsid(0);
    pid = (int)getpid();


    /* Now stop all processes, unless there are some we should omit. */
        if (! omit)
        {
        kill(-1, SIGSTOP);
        sent_sigstop = 1;
        }


    /* Read /proc filesystem */
    if (readproc() < 0) {
             if (sent_sigstop)
        kill(-1, SIGCONT);
           return(1);
    }


    /* Now kill all processes except init (pid 1) and our session. */
    for (p = plist; p; p = p->next) {
        if (p->pid == 1 || p->pid == pid || p->sid == sid || p->kernel)
            continue;


        if (omit) {
            OMIT * optr;
            for (optr = omit; optr; optr = optr->next) {
                if (optr->pid == p->pid)
                    break;
            }


            /* On a match, continue with the for loop above. */
            if (optr)
                continue;
        }      /* end of skipping PIDs to omit */


        kill(p->pid, sig);
        retval = 0;
    }


    /* And let them continue. */
        if (sent_sigstop)
        kill(-1, SIGCONT);


    /* Done. */
    closelog();


    /* Force the kernel to run the scheduler */
    usleep(1);


    return retval;
}