/*      $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);
}