root/usr/src/cmd/ptools/pwait/pwait.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright 2022 Spencer Evans-Cole.
 */

#include <stdio.h>
#include <stdio_ext.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <sys/types.h>
#include <stropts.h>
#include <poll.h>
#include <procfs.h>
#include <sys/resource.h>

static int count_my_files();
static char *command;

/* slop to account for extra file descriptors opened by libraries we call */
#define SLOP    5

int
main(int argc, char **argv)
{
        unsigned long remain = 0;
        struct pollfd *pollfd;
        struct pollfd *pfd;
        struct rlimit rlim;
        char *arg;
        unsigned i;
        int verbose = 0;
        pid_t mypid = getpid();

        if ((command = strrchr(argv[0], '/')) != NULL)
                command++;
        else
                command = argv[0];

        argc--;
        argv++;

        if (argc > 0 && strcmp(argv[0], "-v") == 0) {
                verbose = 1;
                argc--;
                argv++;
        }

        if (argc <= 0) {
                (void) fprintf(stderr, "usage:\t%s [-v] pid ...\n", command);
                (void) fprintf(stderr, "  (wait for processes to terminate)\n");
                (void) fprintf(stderr,
                    "  -v: verbose; report terminations to standard out\n");
                return (2);
        }

        /* make sure we have enough file descriptors */
        if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
                int nfiles = count_my_files();

                if (rlim.rlim_cur < argc + nfiles + SLOP) {
                        rlim.rlim_cur = argc + nfiles + SLOP;
                        if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
                                (void) fprintf(stderr,
                                    "%s: insufficient file descriptors\n",
                                    command);
                                return (2);
                        }
                }
                (void) enable_extended_FILE_stdio(-1, -1);
        }

        pollfd = (struct pollfd *)malloc(argc*sizeof (struct pollfd));
        if (pollfd == NULL) {
                perror("malloc");
                return (2);
        }

        for (i = 0; i < argc; i++) {
                char psinfofile[100];

                arg = argv[i];
                if (mypid == atol(arg)) {
                        if (verbose) {
                                (void) printf("%s: has the same"
                                    " pid as this process\n", arg);
                        }
                        continue;
                }
                if (strchr(arg, '/') != NULL)
                        (void) strncpy(psinfofile, arg, sizeof (psinfofile));
                else {
                        (void) strcpy(psinfofile, "/proc/");
                        (void) strncat(psinfofile, arg, sizeof (psinfofile)-6);
                }
                (void) strncat(psinfofile, "/psinfo",
                    sizeof (psinfofile) - strlen(psinfofile));

                pfd = &pollfd[i];
                if ((pfd->fd = open(psinfofile, O_RDONLY)) >= 0) {
                        remain++;
                        /*
                         * We set POLLPRI to detect system processes.
                         * We will get POLLNVAL below for a POLLPRI
                         * requested event on a system process.
                         */
                        pfd->events = POLLPRI;
                        pfd->revents = 0;
                } else if (errno == ENOENT) {
                        (void) fprintf(stderr, "%s: no such process: %s\n",
                            command, arg);
                } else {
                        perror(arg);
                }
        }

        while (remain != 0) {
                while (poll(pollfd, argc, INFTIM) < 0) {
                        if (errno != EAGAIN) {
                                perror("poll");
                                return (2);
                        }
                        (void) sleep(2);
                }
                for (i = 0; i < argc; i++) {
                        pfd = &pollfd[i];
                        if (pfd->fd < 0 || (pfd->revents & ~POLLPRI) == 0) {
                                /*
                                 * We don't care if a non-system process
                                 * stopped.  Don't check for that again.
                                 */
                                pfd->events = 0;
                                pfd->revents = 0;
                                continue;
                        }

                        if (verbose) {
                                arg = argv[i];
                                if (pfd->revents & POLLHUP) {
                                        psinfo_t psinfo;

                                        if (pread(pfd->fd, &psinfo,
                                            sizeof (psinfo), (off_t)0)
                                            == sizeof (psinfo)) {
                                                (void) printf("%s: terminated,"
                                                    " wait status 0x%.4x\n",
                                                    arg, psinfo.pr_wstat);
                                        } else {
                                                (void) printf(
                                                    "%s: terminated\n", arg);
                                        }
                                }
                                if (pfd->revents & POLLNVAL)
                                        (void) printf("%s: system process\n",
                                            arg);
                                if (pfd->revents & ~(POLLPRI|POLLHUP|POLLNVAL))
                                        (void) printf("%s: unknown error\n",
                                            arg);
                        }

                        (void) close(pfd->fd);
                        pfd->fd = -1;
                        remain--;
                }
        }

        return (0);
}

/* ARGSUSED1 */
static int
do_count(void *nofilesp, int fd)
{
        (*(int *)nofilesp)++;
        return (0);
}

static int
count_my_files()
{
        int nofiles = 0;

        (void) fdwalk(do_count, &nofiles);
        return (nofiles);
}