root/sys/miscfs/procfs/procfs_subr.c
/*      $NetBSD: procfs_subr.c,v 1.120 2024/07/01 01:35:53 christos Exp $       */

/*-
 * Copyright (c) 2006, 2007, 2008 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Andrew Doran.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Copyright (c) 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Jan-Simon Pendry.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      @(#)procfs_subr.c       8.6 (Berkeley) 5/14/95
 */

/*
 * Copyright (c) 1994 Christopher G. Demetriou.  All rights reserved.
 * Copyright (c) 1993 Jan-Simon Pendry
 *
 * This code is derived from software contributed to Berkeley by
 * Jan-Simon Pendry.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the University of
 *      California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      @(#)procfs_subr.c       8.6 (Berkeley) 5/14/95
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: procfs_subr.c,v 1.120 2024/07/01 01:35:53 christos Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/fstrans.h>
#include <sys/vnode.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/kauth.h>
#include <sys/sysctl.h>

#include <miscfs/procfs/procfs.h>

/*
 * Allocate a pfsnode/vnode pair.  The vnode is referenced.
 * The pid, type, and file descriptor uniquely identify a pfsnode.
 */
int
procfs_allocvp(struct mount *mp, struct vnode **vpp, pid_t pid,
    pfstype type, int fd)
{
        struct pfskey key;

        memset(&key, 0, sizeof(key));
        key.pk_type = type;
        key.pk_pid = pid;
        key.pk_fd = fd;

        return vcache_get(mp, &key, sizeof(key), vpp);
}

int
procfs_rw(void *v)
{
        struct vop_read_args *ap = v;
        struct vnode *vp = ap->a_vp;
        struct uio *uio = ap->a_uio;
        struct lwp *curl;
        struct lwp *l;
        struct pfsnode *pfs = VTOPFS(vp);
        struct proc *p;
        int error;

        if (uio->uio_offset < 0)
                return EINVAL;

        if ((error =
             procfs_proc_lock(vp->v_mount, pfs->pfs_pid, &p, ESRCH)) != 0)
                return error;

        curl = curlwp;

        /*
         * Do not allow init to be modified while in secure mode; it
         * could be duped into changing the security level.
         */
#define M2K(m)  ((m) == UIO_READ ? KAUTH_REQ_PROCESS_PROCFS_READ : \
                 KAUTH_REQ_PROCESS_PROCFS_WRITE)
        mutex_enter(p->p_lock);
        error = kauth_authorize_process(curl->l_cred, KAUTH_PROCESS_PROCFS,
            p, pfs, KAUTH_ARG(M2K(uio->uio_rw)), NULL);
        mutex_exit(p->p_lock);
        if (error) {
                procfs_proc_unlock(p);
                return (error);
        }
#undef  M2K

        mutex_enter(p->p_lock);
        LIST_FOREACH(l, &p->p_lwps, l_sibling) {
                if (l->l_stat != LSZOMB)
                        break;
        }
        /* Process is exiting if no-LWPS or all LWPs are LSZOMB */
        if (l == NULL) {
                mutex_exit(p->p_lock);
                procfs_proc_unlock(p);
                return ESRCH;
        }

        lwp_addref(l);
        mutex_exit(p->p_lock);

        switch (pfs->pfs_type) {
        case PFSnote:
        case PFSnotepg:
                error = procfs_donote(curl, p, pfs, uio);
                break;

        case PFSregs:
                error = procfs_doregs(curl, l, pfs, uio);
                break;

        case PFSfpregs:
                error = procfs_dofpregs(curl, l, pfs, uio);
                break;

        case PFSstatus:
                error = procfs_dostatus(curl, l, pfs, uio);
                break;

        case PFSstat:
                error = procfs_do_pid_stat(curl, l, pfs, uio);
                break;

        case PFSlimit:
                error = procfs_dolimit(curl, p, pfs, uio);
                break;

        case PFSlimits:
                error = procfs_dolimits(curl, p, pfs, uio);
                break;

        case PFSmap:
                error = procfs_domap(curl, p, pfs, uio, 0);
                break;

        case PFSmaps:
                error = procfs_domap(curl, p, pfs, uio, 1);
                break;

        case PFSmem:
                error = procfs_domem(curl, l, pfs, uio);
                break;

        case PFScmdline:
                error = procfs_doprocargs(curl, p, pfs, uio, KERN_PROC_ARGV);
                break;

        case PFSenviron:
                error = procfs_doprocargs(curl, p, pfs, uio, KERN_PROC_ENV);
                break;

        case PFSmeminfo:
                error = procfs_domeminfo(curl, p, pfs, uio);
                break;

        case PFSdevices:
                error = procfs_dodevices(curl, p, pfs, uio);
                break;

        case PFScpuinfo:
                error = procfs_docpuinfo(curl, p, pfs, uio);
                break;

        case PFScpustat:
                error = procfs_docpustat(curl, p, pfs, uio);
                break;

        case PFSloadavg:
                error = procfs_doloadavg(curl, p, pfs, uio);
                break;

        case PFSstatm:
                error = procfs_do_pid_statm(curl, l, pfs, uio);
                break;

        case PFSfd:
                error = procfs_dofd(curl, p, pfs, uio);
                break;

        case PFSuptime:
                error = procfs_douptime(curl, p, pfs, uio);
                break;

        case PFSmounts:
                error = procfs_domounts(curl, p, pfs, uio);
                break;

        case PFSemul:
                error = procfs_doemul(curl, p, pfs, uio);
                break;

        case PFSversion:
                error = procfs_doversion(curl, p, pfs, uio);
                break;

        case PFSauxv:
                error = procfs_doauxv(curl, p, pfs, uio);
                break;

        case PFSsysvipc_msg:
                error = procfs_dosysvipc_msg(curl, p, pfs, uio);
                break;

        case PFSsysvipc_sem:
                error = procfs_dosysvipc_sem(curl, p, pfs, uio);
                break;

        case PFSsysvipc_shm:
                error = procfs_dosysvipc_shm(curl, p, pfs, uio);
                break;

        case PFSmq_msg_def:
                error = procfs_domq_msg_def(curl, p, pfs, uio);
                break;

        case PFSmq_msg_max:
                error = procfs_domq_msg_max(curl, p, pfs, uio);
                break;

        case PFSmq_siz_def:
                error = procfs_domq_siz_def(curl, p, pfs, uio);
                break;

        case PFSmq_siz_max:
                error = procfs_domq_siz_max(curl, p, pfs, uio);
                break;

        case PFSmq_qmax:
                error = procfs_domq_qmax(curl, p, pfs, uio);
                break;

#ifdef __HAVE_PROCFS_MACHDEP
        PROCFS_MACHDEP_NODETYPE_CASES
                error = procfs_machdep_rw(curl, l, pfs, uio);
                break;
#endif

        default:
                error = EOPNOTSUPP;
                break;
        }

        /*
         * Release the references that we acquired earlier.
         */
        lwp_delref(l);
        procfs_proc_unlock(p);

        return (error);
}

/*
 * Get a string from userland into (bf).  Strip a trailing
 * nl character (to allow easy access from the shell).
 * The buffer should be *buflenp + 1 chars long.  vfs_getuserstr
 * will automatically add a nul char at the end.
 *
 * Returns 0 on success or the following errors
 *
 * EINVAL:    file offset is non-zero.
 * EMSGSIZE:  message is longer than kernel buffer
 * EFAULT:    user i/o buffer is not addressable
 */
int
vfs_getuserstr(struct uio *uio, char *bf, int *buflenp)
{
        size_t xlen;
        int error;

        if (uio->uio_offset != 0)
                return (EINVAL);

        xlen = *buflenp;

        /* must be able to read the whole string in one go */
        if (xlen < uio->uio_resid)
                return (EMSGSIZE);
        xlen = uio->uio_resid;

        if ((error = uiomove(bf, xlen, uio)) != 0)
                return (error);

        /* allow multiple writes without seeks */
        uio->uio_offset = 0;

        /* cleanup string and remove trailing newline */
        bf[xlen] = '\0';
        xlen = strlen(bf);
        if (xlen > 0 && bf[xlen-1] == '\n')
                bf[--xlen] = '\0';
        *buflenp = xlen;

        return (0);
}

const vfs_namemap_t *
vfs_findname(const vfs_namemap_t *nm, const char *bf, int buflen)
{

        for (; nm->nm_name; nm++)
                if (memcmp(bf, nm->nm_name, buflen+1) == 0)
                        return (nm);

        return (0);
}

bool
procfs_use_linux_compat(struct mount *mp)
{
        const int flags = VFSTOPROC(mp)->pmnt_flags;

        return (flags & PROCFSMNT_LINUXCOMPAT) ? true : false;
}

struct proc *
procfs_proc_find(struct mount *mp, pid_t pid)
{

        KASSERT(mutex_owned(&proc_lock));
        return procfs_use_linux_compat(mp) ? proc_find_lwpid(pid)
                                           : proc_find(pid);
}

int
procfs_proc_lock(struct mount *mp, int pid, struct proc **bunghole,
                 int notfound)
{
        struct proc *tp;
        int error = 0;

        mutex_enter(&proc_lock);

        if (pid == 0)
                tp = &proc0;
        else if ((tp = procfs_proc_find(mp, pid)) == NULL)
                error = notfound;
        if (tp != NULL && !rw_tryenter(&tp->p_reflock, RW_READER))
                error = EBUSY;

        mutex_exit(&proc_lock);

        *bunghole = tp;
        return error;
}

void
procfs_proc_unlock(struct proc *p)
{

        rw_exit(&p->p_reflock);
}

int
procfs_doemul(struct lwp *curl, struct proc *p,
    struct pfsnode *pfs, struct uio *uio)
{
        const char *ename = p->p_emul->e_name;
        return uiomove_frombuf(__UNCONST(ename), strlen(ename), uio);
}