2024-08-26 19:12:03 +00:00
|
|
|
/* $OpenBSD: kern_sysctl.c,v 1.445 2024/08/26 08:24:25 mvs Exp $ */
|
2023-04-30 01:15:27 +00:00
|
|
|
/* $NetBSD: kern_sysctl.c,v 1.17 1996/05/20 17:49:05 mrg Exp $ */
|
|
|
|
|
|
|
|
/*-
|
|
|
|
* Copyright (c) 1982, 1986, 1989, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
|
|
|
*
|
|
|
|
* This code is derived from software contributed to Berkeley by
|
|
|
|
* Mike Karels at Berkeley Software Design, Inc.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* @(#)kern_sysctl.c 8.4 (Berkeley) 4/14/94
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sysctl system call.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
2024-07-11 15:49:23 +00:00
|
|
|
#include <sys/atomic.h>
|
2023-04-30 01:15:27 +00:00
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/pool.h>
|
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/resourcevar.h>
|
|
|
|
#include <sys/signalvar.h>
|
|
|
|
#include <sys/fcntl.h>
|
|
|
|
#include <sys/file.h>
|
|
|
|
#include <sys/filedesc.h>
|
|
|
|
#include <sys/vnode.h>
|
|
|
|
#include <sys/unistd.h>
|
|
|
|
#include <sys/buf.h>
|
|
|
|
#include <sys/clockintr.h>
|
|
|
|
#include <sys/tty.h>
|
|
|
|
#include <sys/disklabel.h>
|
|
|
|
#include <sys/disk.h>
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <sys/msgbuf.h>
|
|
|
|
#include <sys/vmmeter.h>
|
|
|
|
#include <sys/namei.h>
|
|
|
|
#include <sys/exec.h>
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/percpu.h>
|
|
|
|
#include <sys/sensors.h>
|
|
|
|
#include <sys/pipe.h>
|
|
|
|
#include <sys/eventvar.h>
|
|
|
|
#include <sys/socketvar.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/domain.h>
|
|
|
|
#include <sys/protosw.h>
|
|
|
|
#include <sys/pledge.h>
|
|
|
|
#include <sys/timetc.h>
|
|
|
|
#include <sys/evcount.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <sys/unpcb.h>
|
|
|
|
#include <sys/sched.h>
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/syscallargs.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <sys/witness.h>
|
|
|
|
|
|
|
|
#include <uvm/uvm_extern.h>
|
|
|
|
|
|
|
|
#include <dev/cons.h>
|
|
|
|
|
2023-10-02 04:00:10 +00:00
|
|
|
#include <dev/usb/ucomvar.h>
|
|
|
|
|
2023-04-30 01:15:27 +00:00
|
|
|
#include <net/route.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/ip.h>
|
|
|
|
#include <netinet/ip_var.h>
|
|
|
|
#include <netinet/in_pcb.h>
|
|
|
|
#include <netinet/ip6.h>
|
|
|
|
#include <netinet/tcp.h>
|
|
|
|
#include <netinet/tcp_timer.h>
|
|
|
|
#include <netinet/tcp_var.h>
|
|
|
|
#include <netinet/udp.h>
|
|
|
|
#include <netinet/udp_var.h>
|
|
|
|
#include <netinet6/ip6_var.h>
|
|
|
|
|
|
|
|
#ifdef DDB
|
|
|
|
#include <ddb/db_var.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SYSVMSG
|
|
|
|
#include <sys/msg.h>
|
|
|
|
#endif
|
|
|
|
#ifdef SYSVSEM
|
|
|
|
#include <sys/sem.h>
|
|
|
|
#endif
|
|
|
|
#ifdef SYSVSHM
|
|
|
|
#include <sys/shm.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "audio.h"
|
|
|
|
#include "dt.h"
|
|
|
|
#include "pf.h"
|
2023-10-02 04:00:10 +00:00
|
|
|
#include "ucom.h"
|
2023-04-30 01:15:27 +00:00
|
|
|
#include "video.h"
|
|
|
|
|
|
|
|
extern struct forkstat forkstat;
|
|
|
|
extern struct nchstats nchstats;
|
|
|
|
extern int fscale;
|
|
|
|
extern fixpt_t ccpu;
|
|
|
|
extern long numvnodes;
|
|
|
|
extern int allowdt;
|
|
|
|
extern int audio_record_enable;
|
|
|
|
extern int video_record_enable;
|
|
|
|
extern int autoconf_serial;
|
|
|
|
|
|
|
|
int allowkmem;
|
|
|
|
|
2024-08-23 05:18:42 +00:00
|
|
|
int sysctl_securelevel(void *, size_t *, void *, size_t, struct proc *);
|
2023-04-30 01:15:27 +00:00
|
|
|
int sysctl_diskinit(int, struct proc *);
|
|
|
|
int sysctl_proc_args(int *, u_int, void *, size_t *, struct proc *);
|
|
|
|
int sysctl_proc_cwd(int *, u_int, void *, size_t *, struct proc *);
|
|
|
|
int sysctl_proc_nobroadcastkill(int *, u_int, void *, size_t, void *, size_t *,
|
|
|
|
struct proc *);
|
|
|
|
int sysctl_proc_vmmap(int *, u_int, void *, size_t *, struct proc *);
|
|
|
|
int sysctl_intrcnt(int *, u_int, void *, size_t *);
|
|
|
|
int sysctl_sensors(int *, u_int, void *, size_t *, void *, size_t);
|
|
|
|
int sysctl_cptime2(int *, u_int, void *, size_t *, void *, size_t);
|
|
|
|
int sysctl_audio(int *, u_int, void *, size_t *, void *, size_t);
|
|
|
|
int sysctl_video(int *, u_int, void *, size_t *, void *, size_t);
|
|
|
|
int sysctl_cpustats(int *, u_int, void *, size_t *, void *, size_t);
|
|
|
|
int sysctl_utc_offset(void *, size_t *, void *, size_t);
|
2023-05-18 12:23:26 +00:00
|
|
|
int sysctl_hwbattery(int *, u_int, void *, size_t *, void *, size_t);
|
2023-04-30 01:15:27 +00:00
|
|
|
|
|
|
|
void fill_file(struct kinfo_file *, struct file *, struct filedesc *, int,
|
|
|
|
struct vnode *, struct process *, struct proc *, struct socket *, int);
|
|
|
|
void fill_kproc(struct process *, struct kinfo_proc *, struct proc *, int);
|
|
|
|
|
2024-08-02 17:22:03 +00:00
|
|
|
int kern_sysctl_locked(int *, u_int, void *, size_t *, void *, size_t,
|
|
|
|
struct proc *);
|
|
|
|
int hw_sysctl_locked(int *, u_int, void *, size_t *,void *, size_t,
|
|
|
|
struct proc *);
|
|
|
|
|
2023-04-30 01:15:27 +00:00
|
|
|
int (*cpu_cpuspeed)(int *);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lock to avoid too many processes vslocking a large amount of memory
|
|
|
|
* at the same time.
|
|
|
|
*/
|
|
|
|
struct rwlock sysctl_lock = RWLOCK_INITIALIZER("sysctllk");
|
|
|
|
struct rwlock sysctl_disklock = RWLOCK_INITIALIZER("sysctldlk");
|
|
|
|
|
2024-08-02 17:22:03 +00:00
|
|
|
int
|
|
|
|
sysctl_vslock(void *addr, size_t len)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = rw_enter(&sysctl_lock, RW_WRITE|RW_INTR);
|
|
|
|
if (error)
|
2024-08-06 04:33:39 +00:00
|
|
|
return (error);
|
|
|
|
KERNEL_LOCK();
|
2024-08-02 17:22:03 +00:00
|
|
|
|
|
|
|
if (addr) {
|
|
|
|
if (atop(len) > uvmexp.wiredmax - uvmexp.wired) {
|
|
|
|
error = ENOMEM;
|
2024-08-06 04:33:39 +00:00
|
|
|
goto out;
|
2024-08-02 17:22:03 +00:00
|
|
|
}
|
|
|
|
error = uvm_vslock(curproc, addr, len, PROT_READ | PROT_WRITE);
|
|
|
|
if (error)
|
2024-08-06 04:33:39 +00:00
|
|
|
goto out;
|
2024-08-02 17:22:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
out:
|
|
|
|
KERNEL_UNLOCK();
|
2024-08-06 04:33:39 +00:00
|
|
|
rw_exit_write(&sysctl_lock);
|
2024-08-02 17:22:03 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sysctl_vsunlock(void *addr, size_t len)
|
|
|
|
{
|
|
|
|
KERNEL_ASSERT_LOCKED();
|
|
|
|
|
|
|
|
if (addr)
|
|
|
|
uvm_vsunlock(curproc, addr, len);
|
|
|
|
KERNEL_UNLOCK();
|
2024-08-06 04:33:39 +00:00
|
|
|
rw_exit_write(&sysctl_lock);
|
2024-08-02 17:22:03 +00:00
|
|
|
}
|
|
|
|
|
2023-04-30 01:15:27 +00:00
|
|
|
int
|
|
|
|
sys_sysctl(struct proc *p, void *v, register_t *retval)
|
|
|
|
{
|
|
|
|
struct sys_sysctl_args /* {
|
|
|
|
syscallarg(const int *) name;
|
|
|
|
syscallarg(u_int) namelen;
|
|
|
|
syscallarg(void *) old;
|
|
|
|
syscallarg(size_t *) oldlenp;
|
|
|
|
syscallarg(void *) new;
|
|
|
|
syscallarg(size_t) newlen;
|
|
|
|
} */ *uap = v;
|
2023-05-18 12:23:26 +00:00
|
|
|
int error, dolock = 1;
|
2023-04-30 01:15:27 +00:00
|
|
|
size_t savelen = 0, oldlen = 0;
|
|
|
|
sysctlfn *fn;
|
|
|
|
int name[CTL_MAXNAME];
|
|
|
|
|
|
|
|
if (SCARG(uap, new) != NULL &&
|
|
|
|
(error = suser(p)))
|
|
|
|
return (error);
|
|
|
|
/*
|
|
|
|
* all top-level sysctl names are non-terminal
|
|
|
|
*/
|
|
|
|
if (SCARG(uap, namelen) > CTL_MAXNAME || SCARG(uap, namelen) < 2)
|
|
|
|
return (EINVAL);
|
|
|
|
error = copyin(SCARG(uap, name), name,
|
|
|
|
SCARG(uap, namelen) * sizeof(int));
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
error = pledge_sysctl(p, SCARG(uap, namelen),
|
|
|
|
name, SCARG(uap, new));
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
switch (name[0]) {
|
|
|
|
case CTL_KERN:
|
2024-08-02 17:22:03 +00:00
|
|
|
dolock = 0;
|
2023-04-30 01:15:27 +00:00
|
|
|
fn = kern_sysctl;
|
|
|
|
break;
|
|
|
|
case CTL_HW:
|
2024-08-02 17:22:03 +00:00
|
|
|
dolock = 0;
|
2023-04-30 01:15:27 +00:00
|
|
|
fn = hw_sysctl;
|
|
|
|
break;
|
|
|
|
case CTL_VM:
|
|
|
|
fn = uvm_sysctl;
|
|
|
|
break;
|
|
|
|
case CTL_NET:
|
2024-08-15 02:21:18 +00:00
|
|
|
dolock = 0;
|
2023-04-30 01:15:27 +00:00
|
|
|
fn = net_sysctl;
|
|
|
|
break;
|
|
|
|
case CTL_FS:
|
|
|
|
fn = fs_sysctl;
|
|
|
|
break;
|
|
|
|
case CTL_VFS:
|
|
|
|
fn = vfs_sysctl;
|
|
|
|
break;
|
|
|
|
case CTL_MACHDEP:
|
|
|
|
fn = cpu_sysctl;
|
|
|
|
break;
|
|
|
|
#ifdef DEBUG_SYSCTL
|
|
|
|
case CTL_DEBUG:
|
|
|
|
fn = debug_sysctl;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef DDB
|
|
|
|
case CTL_DDB:
|
|
|
|
fn = ddb_sysctl;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SCARG(uap, oldlenp) &&
|
|
|
|
(error = copyin(SCARG(uap, oldlenp), &oldlen, sizeof(oldlen))))
|
|
|
|
return (error);
|
2024-08-02 17:22:03 +00:00
|
|
|
|
|
|
|
if (dolock) {
|
|
|
|
error = sysctl_vslock(SCARG(uap, old), oldlen);
|
|
|
|
if (error)
|
2023-05-18 12:23:26 +00:00
|
|
|
return (error);
|
2023-04-30 01:15:27 +00:00
|
|
|
savelen = oldlen;
|
|
|
|
}
|
|
|
|
error = (*fn)(&name[1], SCARG(uap, namelen) - 1, SCARG(uap, old),
|
|
|
|
&oldlen, SCARG(uap, new), SCARG(uap, newlen), p);
|
2024-08-02 17:22:03 +00:00
|
|
|
if (dolock)
|
|
|
|
sysctl_vsunlock(SCARG(uap, old), savelen);
|
|
|
|
|
2023-04-30 01:15:27 +00:00
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
if (SCARG(uap, oldlenp))
|
|
|
|
error = copyout(&oldlen, SCARG(uap, oldlenp), sizeof(oldlen));
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Attributes stored in the kernel.
|
|
|
|
*/
|
|
|
|
char hostname[MAXHOSTNAMELEN];
|
|
|
|
int hostnamelen;
|
|
|
|
char domainname[MAXHOSTNAMELEN];
|
|
|
|
int domainnamelen;
|
2024-08-15 02:21:18 +00:00
|
|
|
int hostid;
|
2023-04-30 01:15:27 +00:00
|
|
|
char *disknames = NULL;
|
|
|
|
size_t disknameslen;
|
|
|
|
struct diskstats *diskstats = NULL;
|
|
|
|
size_t diskstatslen;
|
|
|
|
int securelevel;
|
|
|
|
|
|
|
|
/* morally const values reported by sysctl_bounded_arr */
|
|
|
|
static int arg_max = ARG_MAX;
|
|
|
|
static int openbsd = SecBSD;
|
|
|
|
static int posix_version = _POSIX_VERSION;
|
|
|
|
static int ngroups_max = NGROUPS_MAX;
|
|
|
|
static int int_zero = 0;
|
|
|
|
static int int_one = 1;
|
|
|
|
static int maxpartitions = MAXPARTITIONS;
|
|
|
|
static int raw_part = RAW_PART;
|
|
|
|
|
|
|
|
extern int somaxconn, sominconn;
|
|
|
|
extern int nosuidcoredump;
|
|
|
|
extern int maxlocksperuid;
|
|
|
|
extern int uvm_wxabort;
|
|
|
|
extern int global_ptrace;
|
|
|
|
|
|
|
|
const struct sysctl_bounded_args kern_vars[] = {
|
|
|
|
{KERN_OSREV, &openbsd, SYSCTL_INT_READONLY},
|
|
|
|
{KERN_MAXVNODES, &maxvnodes, 0, INT_MAX},
|
|
|
|
{KERN_MAXPROC, &maxprocess, 0, INT_MAX},
|
|
|
|
{KERN_MAXFILES, &maxfiles, 0, INT_MAX},
|
|
|
|
{KERN_NFILES, &numfiles, SYSCTL_INT_READONLY},
|
|
|
|
{KERN_TTYCOUNT, &tty_count, SYSCTL_INT_READONLY},
|
|
|
|
{KERN_ARGMAX, &arg_max, SYSCTL_INT_READONLY},
|
|
|
|
{KERN_POSIX1, &posix_version, SYSCTL_INT_READONLY},
|
|
|
|
{KERN_NGROUPS, &ngroups_max, SYSCTL_INT_READONLY},
|
|
|
|
{KERN_JOB_CONTROL, &int_one, SYSCTL_INT_READONLY},
|
|
|
|
{KERN_SAVED_IDS, &int_one, SYSCTL_INT_READONLY},
|
|
|
|
{KERN_MAXPARTITIONS, &maxpartitions, SYSCTL_INT_READONLY},
|
|
|
|
{KERN_RAWPARTITION, &raw_part, SYSCTL_INT_READONLY},
|
|
|
|
{KERN_MAXTHREAD, &maxthread, 0, INT_MAX},
|
|
|
|
{KERN_NTHREADS, &nthreads, SYSCTL_INT_READONLY},
|
|
|
|
{KERN_SOMAXCONN, &somaxconn, 0, SHRT_MAX},
|
|
|
|
{KERN_SOMINCONN, &sominconn, 0, SHRT_MAX},
|
|
|
|
{KERN_NOSUIDCOREDUMP, &nosuidcoredump, 0, 3},
|
|
|
|
{KERN_FSYNC, &int_one, SYSCTL_INT_READONLY},
|
|
|
|
{KERN_SYSVMSG,
|
|
|
|
#ifdef SYSVMSG
|
|
|
|
&int_one,
|
|
|
|
#else
|
|
|
|
&int_zero,
|
|
|
|
#endif
|
|
|
|
SYSCTL_INT_READONLY},
|
|
|
|
{KERN_SYSVSEM,
|
|
|
|
#ifdef SYSVSEM
|
|
|
|
&int_one,
|
|
|
|
#else
|
|
|
|
&int_zero,
|
|
|
|
#endif
|
|
|
|
SYSCTL_INT_READONLY},
|
|
|
|
{KERN_SYSVSHM,
|
|
|
|
#ifdef SYSVSHM
|
|
|
|
&int_one,
|
|
|
|
#else
|
|
|
|
&int_zero,
|
|
|
|
#endif
|
|
|
|
SYSCTL_INT_READONLY},
|
|
|
|
{KERN_FSCALE, &fscale, SYSCTL_INT_READONLY},
|
|
|
|
{KERN_CCPU, &ccpu, SYSCTL_INT_READONLY},
|
|
|
|
{KERN_NPROCS, &nprocesses, SYSCTL_INT_READONLY},
|
|
|
|
{KERN_SPLASSERT, &splassert_ctl, 0, 3},
|
|
|
|
{KERN_MAXLOCKSPERUID, &maxlocksperuid, 0, INT_MAX},
|
|
|
|
{KERN_WXABORT, &uvm_wxabort, 0, 1},
|
|
|
|
{KERN_NETLIVELOCKS, &int_zero, SYSCTL_INT_READONLY},
|
|
|
|
#ifdef PTRACE
|
|
|
|
{KERN_GLOBAL_PTRACE, &global_ptrace, 0, 1},
|
|
|
|
#endif
|
|
|
|
{KERN_AUTOCONF_SERIAL, &autoconf_serial, SYSCTL_INT_READONLY},
|
|
|
|
};
|
|
|
|
|
|
|
|
int
|
|
|
|
kern_sysctl_dirs(int top_name, int *name, u_int namelen,
|
|
|
|
void *oldp, size_t *oldlenp, void *newp, size_t newlen, struct proc *p)
|
|
|
|
{
|
|
|
|
switch (top_name) {
|
|
|
|
#ifndef SMALL_KERNEL
|
|
|
|
case KERN_PROC:
|
|
|
|
return (sysctl_doproc(name, namelen, oldp, oldlenp));
|
|
|
|
case KERN_PROC_ARGS:
|
|
|
|
return (sysctl_proc_args(name, namelen, oldp, oldlenp, p));
|
|
|
|
case KERN_PROC_CWD:
|
|
|
|
return (sysctl_proc_cwd(name, namelen, oldp, oldlenp, p));
|
|
|
|
case KERN_PROC_NOBROADCASTKILL:
|
|
|
|
return (sysctl_proc_nobroadcastkill(name, namelen,
|
|
|
|
newp, newlen, oldp, oldlenp, p));
|
|
|
|
case KERN_PROC_VMMAP:
|
|
|
|
return (sysctl_proc_vmmap(name, namelen, oldp, oldlenp, p));
|
|
|
|
case KERN_FILE:
|
|
|
|
return (sysctl_file(name, namelen, oldp, oldlenp, p));
|
|
|
|
#endif
|
|
|
|
#if defined(GPROF) || defined(DDBPROF)
|
|
|
|
case KERN_PROF:
|
|
|
|
return (sysctl_doprof(name, namelen, oldp, oldlenp,
|
|
|
|
newp, newlen));
|
|
|
|
#endif
|
|
|
|
case KERN_MALLOCSTATS:
|
|
|
|
return (sysctl_malloc(name, namelen, oldp, oldlenp,
|
|
|
|
newp, newlen, p));
|
|
|
|
case KERN_TTY:
|
|
|
|
return (sysctl_tty(name, namelen, oldp, oldlenp,
|
|
|
|
newp, newlen));
|
|
|
|
case KERN_POOL:
|
|
|
|
return (sysctl_dopool(name, namelen, oldp, oldlenp));
|
|
|
|
#if defined(SYSVMSG) || defined(SYSVSEM) || defined(SYSVSHM)
|
|
|
|
case KERN_SYSVIPC_INFO:
|
|
|
|
return (sysctl_sysvipc(name, namelen, oldp, oldlenp));
|
|
|
|
#endif
|
|
|
|
#ifdef SYSVSEM
|
|
|
|
case KERN_SEMINFO:
|
|
|
|
return (sysctl_sysvsem(name, namelen, oldp, oldlenp,
|
|
|
|
newp, newlen));
|
|
|
|
#endif
|
|
|
|
#ifdef SYSVSHM
|
|
|
|
case KERN_SHMINFO:
|
|
|
|
return (sysctl_sysvshm(name, namelen, oldp, oldlenp,
|
|
|
|
newp, newlen));
|
|
|
|
#endif
|
|
|
|
#ifndef SMALL_KERNEL
|
|
|
|
case KERN_INTRCNT:
|
|
|
|
return (sysctl_intrcnt(name, namelen, oldp, oldlenp));
|
|
|
|
case KERN_WATCHDOG:
|
|
|
|
return (sysctl_wdog(name, namelen, oldp, oldlenp,
|
|
|
|
newp, newlen));
|
|
|
|
#endif
|
|
|
|
#ifndef SMALL_KERNEL
|
|
|
|
case KERN_EVCOUNT:
|
|
|
|
return (evcount_sysctl(name, namelen, oldp, oldlenp,
|
|
|
|
newp, newlen));
|
|
|
|
#endif
|
|
|
|
case KERN_TIMECOUNTER:
|
|
|
|
return (sysctl_tc(name, namelen, oldp, oldlenp, newp, newlen));
|
|
|
|
case KERN_CPTIME2:
|
|
|
|
return (sysctl_cptime2(name, namelen, oldp, oldlenp,
|
|
|
|
newp, newlen));
|
|
|
|
#ifdef WITNESS
|
|
|
|
case KERN_WITNESSWATCH:
|
|
|
|
return witness_sysctl_watch(oldp, oldlenp, newp, newlen);
|
|
|
|
case KERN_WITNESS:
|
|
|
|
return witness_sysctl(name, namelen, oldp, oldlenp,
|
|
|
|
newp, newlen);
|
|
|
|
#endif
|
|
|
|
#if NVIDEO > 0
|
|
|
|
case KERN_VIDEO:
|
|
|
|
return (sysctl_video(name, namelen, oldp, oldlenp,
|
|
|
|
newp, newlen));
|
|
|
|
#endif
|
|
|
|
case KERN_CPUSTATS:
|
|
|
|
return (sysctl_cpustats(name, namelen, oldp, oldlenp,
|
|
|
|
newp, newlen));
|
|
|
|
case KERN_CLOCKINTR:
|
|
|
|
return sysctl_clockintr(name, namelen, oldp, oldlenp, newp,
|
|
|
|
newlen);
|
|
|
|
default:
|
|
|
|
return (ENOTDIR); /* overloaded */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* kernel related system variables.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
kern_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
|
|
|
|
size_t newlen, struct proc *p)
|
|
|
|
{
|
2024-08-02 17:22:03 +00:00
|
|
|
int error;
|
|
|
|
size_t savelen;
|
2023-04-30 01:15:27 +00:00
|
|
|
|
|
|
|
/* dispatch the non-terminal nodes first */
|
|
|
|
if (namelen != 1) {
|
2024-08-20 16:39:32 +00:00
|
|
|
switch (name[0]) {
|
|
|
|
#if NAUDIO > 0
|
|
|
|
case KERN_AUDIO:
|
2024-08-26 19:12:03 +00:00
|
|
|
return (sysctl_audio(name + 1, namelen - 1,
|
|
|
|
oldp, oldlenp, newp, newlen));
|
2024-08-20 16:39:32 +00:00
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-08-02 17:22:03 +00:00
|
|
|
savelen = *oldlenp;
|
|
|
|
if ((error = sysctl_vslock(oldp, savelen)))
|
|
|
|
return (error);
|
|
|
|
error = kern_sysctl_dirs(name[0], name + 1, namelen - 1,
|
2023-04-30 01:15:27 +00:00
|
|
|
oldp, oldlenp, newp, newlen, p);
|
2024-08-02 17:22:03 +00:00
|
|
|
sysctl_vsunlock(oldp, savelen);
|
|
|
|
return (error);
|
2023-04-30 01:15:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (name[0]) {
|
|
|
|
case KERN_OSTYPE:
|
|
|
|
return (sysctl_rdstring(oldp, oldlenp, newp, ostype));
|
|
|
|
case KERN_OSRELEASE:
|
|
|
|
return (sysctl_rdstring(oldp, oldlenp, newp, osrelease));
|
|
|
|
case KERN_OSVERSION:
|
|
|
|
return (sysctl_rdstring(oldp, oldlenp, newp, osversion));
|
|
|
|
case KERN_VERSION:
|
|
|
|
return (sysctl_rdstring(oldp, oldlenp, newp, version));
|
|
|
|
case KERN_NUMVNODES: /* XXX numvnodes is a long */
|
|
|
|
return (sysctl_rdint(oldp, oldlenp, newp, numvnodes));
|
2024-08-23 05:18:42 +00:00
|
|
|
#if NDT > 0
|
|
|
|
case KERN_ALLOWDT:
|
|
|
|
return (sysctl_securelevel_int(oldp, oldlenp, newp, newlen,
|
|
|
|
&allowdt));
|
|
|
|
#endif
|
2024-08-15 02:21:18 +00:00
|
|
|
case KERN_HOSTID:
|
|
|
|
return (sysctl_int(oldp, oldlenp, newp, newlen, &hostid));
|
2024-08-08 20:49:23 +00:00
|
|
|
case KERN_CLOCKRATE:
|
|
|
|
return (sysctl_clockrate(oldp, oldlenp, newp));
|
2024-08-06 04:33:39 +00:00
|
|
|
case KERN_BOOTTIME: {
|
|
|
|
struct timeval bt;
|
|
|
|
memset(&bt, 0, sizeof bt);
|
|
|
|
microboottime(&bt);
|
|
|
|
return (sysctl_rdstruct(oldp, oldlenp, newp, &bt, sizeof bt));
|
|
|
|
}
|
2024-08-02 17:22:03 +00:00
|
|
|
case KERN_MBSTAT: {
|
|
|
|
extern struct cpumem *mbstat;
|
|
|
|
uint64_t counters[MBSTAT_COUNT];
|
|
|
|
struct mbstat mbs;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
memset(&mbs, 0, sizeof(mbs));
|
|
|
|
counters_read(mbstat, counters, MBSTAT_COUNT, NULL);
|
|
|
|
for (i = 0; i < MBSTAT_TYPES; i++)
|
|
|
|
mbs.m_mtypes[i] = counters[i];
|
|
|
|
|
|
|
|
mbs.m_drops = counters[MBSTAT_DROPS];
|
|
|
|
mbs.m_wait = counters[MBSTAT_WAIT];
|
|
|
|
mbs.m_drain = counters[MBSTAT_DRAIN];
|
|
|
|
|
|
|
|
return (sysctl_rdstruct(oldp, oldlenp, newp,
|
|
|
|
&mbs, sizeof(mbs)));
|
|
|
|
}
|
2024-08-08 20:49:23 +00:00
|
|
|
case KERN_MSGBUFSIZE:
|
|
|
|
case KERN_CONSBUFSIZE: {
|
|
|
|
struct msgbuf *mp;
|
|
|
|
mp = (name[0] == KERN_MSGBUFSIZE) ? msgbufp : consbufp;
|
|
|
|
/*
|
|
|
|
* deal with cases where the message buffer has
|
|
|
|
* become corrupted.
|
|
|
|
*/
|
|
|
|
if (!mp || mp->msg_magic != MSG_MAGIC)
|
|
|
|
return (ENXIO);
|
|
|
|
return (sysctl_rdint(oldp, oldlenp, newp, mp->msg_bufs));
|
|
|
|
}
|
2024-08-06 04:33:39 +00:00
|
|
|
case KERN_OSREV:
|
2024-08-20 16:39:32 +00:00
|
|
|
case KERN_MAXPROC:
|
|
|
|
case KERN_MAXFILES:
|
2024-08-06 04:33:39 +00:00
|
|
|
case KERN_NFILES:
|
|
|
|
case KERN_TTYCOUNT:
|
|
|
|
case KERN_ARGMAX:
|
|
|
|
case KERN_POSIX1:
|
|
|
|
case KERN_NGROUPS:
|
|
|
|
case KERN_JOB_CONTROL:
|
|
|
|
case KERN_SAVED_IDS:
|
|
|
|
case KERN_MAXPARTITIONS:
|
|
|
|
case KERN_RAWPARTITION:
|
2024-08-20 16:39:32 +00:00
|
|
|
case KERN_MAXTHREAD:
|
2024-08-06 04:33:39 +00:00
|
|
|
case KERN_NTHREADS:
|
|
|
|
case KERN_SOMAXCONN:
|
|
|
|
case KERN_SOMINCONN:
|
|
|
|
case KERN_FSYNC:
|
|
|
|
case KERN_SYSVMSG:
|
|
|
|
case KERN_SYSVSEM:
|
|
|
|
case KERN_SYSVSHM:
|
|
|
|
case KERN_FSCALE:
|
|
|
|
case KERN_CCPU:
|
|
|
|
case KERN_NPROCS:
|
|
|
|
case KERN_NETLIVELOCKS:
|
|
|
|
case KERN_AUTOCONF_SERIAL:
|
|
|
|
return (sysctl_bounded_arr(kern_vars, nitems(kern_vars), name,
|
|
|
|
namelen, oldp, oldlenp, newp, newlen));
|
2024-08-02 17:22:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
savelen = *oldlenp;
|
|
|
|
if ((error = sysctl_vslock(oldp, savelen)))
|
|
|
|
return (error);
|
|
|
|
error = kern_sysctl_locked(name, namelen, oldp, oldlenp,
|
|
|
|
newp, newlen, p);
|
|
|
|
sysctl_vsunlock(oldp, savelen);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
kern_sysctl_locked(int *name, u_int namelen, void *oldp, size_t *oldlenp,
|
|
|
|
void *newp, size_t newlen, struct proc *p)
|
|
|
|
{
|
2024-08-23 05:18:42 +00:00
|
|
|
int error, stackgap;
|
2024-08-02 17:22:03 +00:00
|
|
|
dev_t dev;
|
|
|
|
extern int pool_debug;
|
|
|
|
|
|
|
|
switch (name[0]) {
|
2023-04-30 01:15:27 +00:00
|
|
|
case KERN_SECURELVL:
|
2024-08-23 05:18:42 +00:00
|
|
|
return (sysctl_securelevel(oldp, oldlenp, newp, newlen, p));
|
2023-04-30 01:15:27 +00:00
|
|
|
case KERN_ALLOWKMEM:
|
|
|
|
return (sysctl_securelevel_int(oldp, oldlenp, newp, newlen,
|
|
|
|
&allowkmem));
|
|
|
|
case KERN_HOSTNAME:
|
|
|
|
error = sysctl_tstring(oldp, oldlenp, newp, newlen,
|
|
|
|
hostname, sizeof(hostname));
|
|
|
|
if (newp && !error)
|
|
|
|
hostnamelen = newlen;
|
|
|
|
return (error);
|
|
|
|
case KERN_DOMAINNAME:
|
|
|
|
if (securelevel >= 1 && domainnamelen && newp)
|
|
|
|
error = EPERM;
|
|
|
|
else
|
|
|
|
error = sysctl_tstring(oldp, oldlenp, newp, newlen,
|
|
|
|
domainname, sizeof(domainname));
|
|
|
|
if (newp && !error)
|
|
|
|
domainnamelen = newlen;
|
|
|
|
return (error);
|
|
|
|
case KERN_CONSBUF:
|
|
|
|
if ((error = suser(p)))
|
|
|
|
return (error);
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case KERN_MSGBUF: {
|
|
|
|
struct msgbuf *mp;
|
|
|
|
mp = (name[0] == KERN_MSGBUF) ? msgbufp : consbufp;
|
2024-08-08 20:49:23 +00:00
|
|
|
/*
|
|
|
|
* deal with cases where the message buffer has
|
|
|
|
* become corrupted.
|
|
|
|
*/
|
2023-04-30 01:15:27 +00:00
|
|
|
if (!mp || mp->msg_magic != MSG_MAGIC)
|
|
|
|
return (ENXIO);
|
|
|
|
return (sysctl_rdstruct(oldp, oldlenp, newp, mp,
|
|
|
|
mp->msg_bufs + offsetof(struct msgbuf, msg_bufc)));
|
|
|
|
}
|
|
|
|
case KERN_CPTIME:
|
|
|
|
{
|
|
|
|
CPU_INFO_ITERATOR cii;
|
|
|
|
struct cpu_info *ci;
|
|
|
|
long cp_time[CPUSTATES];
|
|
|
|
int i, n = 0;
|
|
|
|
|
|
|
|
memset(cp_time, 0, sizeof(cp_time));
|
|
|
|
|
|
|
|
CPU_INFO_FOREACH(cii, ci) {
|
|
|
|
if (!cpu_is_online(ci))
|
|
|
|
continue;
|
|
|
|
n++;
|
|
|
|
for (i = 0; i < CPUSTATES; i++)
|
|
|
|
cp_time[i] += ci->ci_schedstate.spc_cp_time[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < CPUSTATES; i++)
|
|
|
|
cp_time[i] /= n;
|
|
|
|
|
|
|
|
return (sysctl_rdstruct(oldp, oldlenp, newp, &cp_time,
|
|
|
|
sizeof(cp_time)));
|
|
|
|
}
|
|
|
|
case KERN_NCHSTATS:
|
|
|
|
return (sysctl_rdstruct(oldp, oldlenp, newp, &nchstats,
|
|
|
|
sizeof(struct nchstats)));
|
|
|
|
case KERN_FORKSTAT:
|
|
|
|
return (sysctl_rdstruct(oldp, oldlenp, newp, &forkstat,
|
|
|
|
sizeof(struct forkstat)));
|
|
|
|
case KERN_STACKGAPRANDOM:
|
|
|
|
stackgap = stackgap_random;
|
|
|
|
error = sysctl_int(oldp, oldlenp, newp, newlen, &stackgap);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
/*
|
|
|
|
* Safety harness.
|
|
|
|
*/
|
|
|
|
if ((stackgap < ALIGNBYTES && stackgap != 0) ||
|
|
|
|
!powerof2(stackgap) || stackgap >= MAXSSIZ)
|
|
|
|
return (EINVAL);
|
|
|
|
stackgap_random = stackgap;
|
|
|
|
return (0);
|
|
|
|
case KERN_MAXCLUSTERS: {
|
|
|
|
int val = nmbclust;
|
|
|
|
error = sysctl_int(oldp, oldlenp, newp, newlen, &val);
|
|
|
|
if (error == 0 && val != nmbclust)
|
|
|
|
error = nmbclust_update(val);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
case KERN_CACHEPCT: {
|
|
|
|
u_int64_t dmapages;
|
|
|
|
int opct, pgs;
|
|
|
|
opct = bufcachepercent;
|
|
|
|
error = sysctl_int(oldp, oldlenp, newp, newlen,
|
|
|
|
&bufcachepercent);
|
|
|
|
if (error)
|
|
|
|
return(error);
|
|
|
|
if (bufcachepercent > 90 || bufcachepercent < 5) {
|
|
|
|
bufcachepercent = opct;
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
dmapages = uvm_pagecount(&dma_constraint);
|
|
|
|
if (bufcachepercent != opct) {
|
|
|
|
pgs = bufcachepercent * dmapages / 100;
|
|
|
|
bufadjust(pgs); /* adjust bufpages */
|
|
|
|
bufhighpages = bufpages; /* set high water mark */
|
|
|
|
}
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
case KERN_CONSDEV:
|
|
|
|
if (cn_tab != NULL)
|
|
|
|
dev = cn_tab->cn_dev;
|
|
|
|
else
|
|
|
|
dev = NODEV;
|
|
|
|
return sysctl_rdstruct(oldp, oldlenp, newp, &dev, sizeof(dev));
|
|
|
|
case KERN_POOL_DEBUG: {
|
|
|
|
int old_pool_debug = pool_debug;
|
|
|
|
|
|
|
|
error = sysctl_int(oldp, oldlenp, newp, newlen,
|
|
|
|
&pool_debug);
|
|
|
|
if (error == 0 && pool_debug != old_pool_debug)
|
|
|
|
pool_reclaim_all();
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
#if NPF > 0
|
|
|
|
case KERN_PFSTATUS:
|
|
|
|
return (pf_sysctl(oldp, oldlenp, newp, newlen));
|
|
|
|
#endif
|
|
|
|
case KERN_TIMEOUT_STATS:
|
|
|
|
return (timeout_sysctl(oldp, oldlenp, newp, newlen));
|
|
|
|
case KERN_UTC_OFFSET:
|
|
|
|
return (sysctl_utc_offset(oldp, oldlenp, newp, newlen));
|
|
|
|
default:
|
|
|
|
return (sysctl_bounded_arr(kern_vars, nitems(kern_vars), name,
|
|
|
|
namelen, oldp, oldlenp, newp, newlen));
|
|
|
|
}
|
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* hardware related system variables.
|
|
|
|
*/
|
|
|
|
char *hw_vendor, *hw_prod, *hw_uuid, *hw_serial, *hw_ver;
|
|
|
|
int allowpowerdown = 1;
|
|
|
|
int hw_power = 1;
|
|
|
|
|
|
|
|
/* morally const values reported by sysctl_bounded_arr */
|
|
|
|
static int byte_order = BYTE_ORDER;
|
|
|
|
|
|
|
|
const struct sysctl_bounded_args hw_vars[] = {
|
|
|
|
{HW_NCPU, &ncpus, SYSCTL_INT_READONLY},
|
|
|
|
{HW_NCPUFOUND, &ncpusfound, SYSCTL_INT_READONLY},
|
|
|
|
{HW_BYTEORDER, &byte_order, SYSCTL_INT_READONLY},
|
2024-03-29 19:36:15 +00:00
|
|
|
{HW_PAGESIZE, &uvmexp.pagesize, SYSCTL_INT_READONLY},
|
2023-04-30 01:15:27 +00:00
|
|
|
{HW_DISKCOUNT, &disk_count, SYSCTL_INT_READONLY},
|
|
|
|
{HW_POWER, &hw_power, SYSCTL_INT_READONLY},
|
|
|
|
};
|
|
|
|
|
|
|
|
int
|
|
|
|
hw_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
|
|
|
|
size_t newlen, struct proc *p)
|
|
|
|
{
|
|
|
|
extern char machine[], cpu_model[];
|
2024-08-02 17:22:03 +00:00
|
|
|
int err;
|
2023-04-30 01:15:27 +00:00
|
|
|
|
2023-05-18 12:23:26 +00:00
|
|
|
/*
|
|
|
|
* all sysctl names at this level except sensors and battery
|
|
|
|
* are terminal
|
|
|
|
*/
|
|
|
|
if (name[0] != HW_SENSORS && name[0] != HW_BATTERY && namelen != 1)
|
2023-04-30 01:15:27 +00:00
|
|
|
return (ENOTDIR); /* overloaded */
|
|
|
|
|
|
|
|
switch (name[0]) {
|
|
|
|
case HW_MACHINE:
|
|
|
|
return (sysctl_rdstring(oldp, oldlenp, newp, machine));
|
|
|
|
case HW_MODEL:
|
|
|
|
return (sysctl_rdstring(oldp, oldlenp, newp, cpu_model));
|
|
|
|
case HW_NCPUONLINE:
|
|
|
|
return (sysctl_rdint(oldp, oldlenp, newp,
|
|
|
|
sysctl_hwncpuonline()));
|
|
|
|
case HW_PHYSMEM:
|
|
|
|
return (sysctl_rdint(oldp, oldlenp, newp, ptoa(physmem)));
|
|
|
|
case HW_USERMEM:
|
|
|
|
return (sysctl_rdint(oldp, oldlenp, newp,
|
|
|
|
ptoa(physmem - uvmexp.wired)));
|
|
|
|
case HW_DISKNAMES:
|
|
|
|
case HW_DISKSTATS:
|
|
|
|
case HW_CPUSPEED:
|
|
|
|
#ifndef SMALL_KERNEL
|
|
|
|
case HW_SENSORS:
|
|
|
|
case HW_SETPERF:
|
|
|
|
case HW_PERFPOLICY:
|
2024-08-02 17:22:03 +00:00
|
|
|
case HW_BATTERY:
|
2023-04-30 01:15:27 +00:00
|
|
|
#endif /* !SMALL_KERNEL */
|
2024-08-02 17:22:03 +00:00
|
|
|
case HW_ALLOWPOWERDOWN:
|
|
|
|
case HW_UCOMNAMES:
|
|
|
|
#ifdef __HAVE_CPU_TOPOLOGY
|
|
|
|
case HW_SMT:
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
size_t savelen = *oldlenp;
|
|
|
|
if ((err = sysctl_vslock(oldp, savelen)))
|
|
|
|
return (err);
|
|
|
|
err = hw_sysctl_locked(name, namelen, oldp, oldlenp,
|
|
|
|
newp, newlen, p);
|
|
|
|
sysctl_vsunlock(oldp, savelen);
|
|
|
|
return (err);
|
|
|
|
}
|
2023-04-30 01:15:27 +00:00
|
|
|
case HW_VENDOR:
|
|
|
|
if (hw_vendor)
|
|
|
|
return (sysctl_rdstring(oldp, oldlenp, newp,
|
|
|
|
hw_vendor));
|
|
|
|
else
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
case HW_PRODUCT:
|
|
|
|
if (hw_prod)
|
|
|
|
return (sysctl_rdstring(oldp, oldlenp, newp, hw_prod));
|
|
|
|
else
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
case HW_VERSION:
|
|
|
|
if (hw_ver)
|
|
|
|
return (sysctl_rdstring(oldp, oldlenp, newp, hw_ver));
|
|
|
|
else
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
case HW_SERIALNO:
|
|
|
|
if (hw_serial)
|
|
|
|
return (sysctl_rdstring(oldp, oldlenp, newp,
|
|
|
|
hw_serial));
|
|
|
|
else
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
case HW_UUID:
|
|
|
|
if (hw_uuid)
|
|
|
|
return (sysctl_rdstring(oldp, oldlenp, newp, hw_uuid));
|
|
|
|
else
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
case HW_PHYSMEM64:
|
|
|
|
return (sysctl_rdquad(oldp, oldlenp, newp,
|
|
|
|
ptoa((psize_t)physmem)));
|
|
|
|
case HW_USERMEM64:
|
|
|
|
return (sysctl_rdquad(oldp, oldlenp, newp,
|
|
|
|
ptoa((psize_t)physmem - uvmexp.wired)));
|
2024-08-02 17:22:03 +00:00
|
|
|
default:
|
|
|
|
return sysctl_bounded_arr(hw_vars, nitems(hw_vars), name,
|
|
|
|
namelen, oldp, oldlenp, newp, newlen);
|
|
|
|
}
|
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
hw_sysctl_locked(int *name, u_int namelen, void *oldp, size_t *oldlenp,
|
|
|
|
void *newp, size_t newlen, struct proc *p)
|
|
|
|
{
|
|
|
|
int err, cpuspeed;
|
|
|
|
|
|
|
|
switch (name[0]) {
|
|
|
|
case HW_DISKNAMES:
|
|
|
|
err = sysctl_diskinit(0, p);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
if (disknames)
|
|
|
|
return (sysctl_rdstring(oldp, oldlenp, newp,
|
|
|
|
disknames));
|
|
|
|
else
|
|
|
|
return (sysctl_rdstring(oldp, oldlenp, newp, ""));
|
|
|
|
case HW_DISKSTATS:
|
|
|
|
err = sysctl_diskinit(1, p);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
return (sysctl_rdstruct(oldp, oldlenp, newp, diskstats,
|
|
|
|
disk_count * sizeof(struct diskstats)));
|
|
|
|
case HW_CPUSPEED:
|
|
|
|
if (!cpu_cpuspeed)
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
err = cpu_cpuspeed(&cpuspeed);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
return (sysctl_rdint(oldp, oldlenp, newp, cpuspeed));
|
|
|
|
#ifndef SMALL_KERNEL
|
|
|
|
case HW_SENSORS:
|
|
|
|
return (sysctl_sensors(name + 1, namelen - 1, oldp, oldlenp,
|
|
|
|
newp, newlen));
|
|
|
|
case HW_SETPERF:
|
|
|
|
return (sysctl_hwsetperf(oldp, oldlenp, newp, newlen));
|
|
|
|
case HW_PERFPOLICY:
|
|
|
|
return (sysctl_hwperfpolicy(oldp, oldlenp, newp, newlen));
|
|
|
|
#endif /* !SMALL_KERNEL */
|
2023-04-30 01:15:27 +00:00
|
|
|
case HW_ALLOWPOWERDOWN:
|
|
|
|
return (sysctl_securelevel_int(oldp, oldlenp, newp, newlen,
|
|
|
|
&allowpowerdown));
|
2023-10-02 04:00:10 +00:00
|
|
|
case HW_UCOMNAMES: {
|
2024-02-10 13:29:41 +00:00
|
|
|
const char *str = "";
|
|
|
|
#if NUCOM > 0
|
|
|
|
str = sysctl_ucominit();
|
|
|
|
#endif /* NUCOM > 0 */
|
2023-10-02 04:00:10 +00:00
|
|
|
return (sysctl_rdstring(oldp, oldlenp, newp, str));
|
|
|
|
}
|
2023-04-30 01:15:27 +00:00
|
|
|
#ifdef __HAVE_CPU_TOPOLOGY
|
|
|
|
case HW_SMT:
|
|
|
|
return (sysctl_hwsmt(oldp, oldlenp, newp, newlen));
|
2023-05-18 12:23:26 +00:00
|
|
|
#endif
|
|
|
|
#ifndef SMALL_KERNEL
|
|
|
|
case HW_BATTERY:
|
|
|
|
return (sysctl_hwbattery(name + 1, namelen - 1, oldp, oldlenp,
|
|
|
|
newp, newlen));
|
2023-04-30 01:15:27 +00:00
|
|
|
#endif
|
|
|
|
default:
|
2024-08-02 17:22:03 +00:00
|
|
|
return (EOPNOTSUPP);
|
2023-04-30 01:15:27 +00:00
|
|
|
}
|
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
|
|
|
|
2023-05-18 12:23:26 +00:00
|
|
|
#ifndef SMALL_KERNEL
|
|
|
|
|
|
|
|
int hw_battery_chargemode;
|
|
|
|
int hw_battery_chargestart;
|
|
|
|
int hw_battery_chargestop;
|
|
|
|
int (*hw_battery_setchargemode)(int);
|
|
|
|
int (*hw_battery_setchargestart)(int);
|
|
|
|
int (*hw_battery_setchargestop)(int);
|
|
|
|
|
|
|
|
int
|
|
|
|
sysctl_hwchargemode(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
|
|
|
|
{
|
|
|
|
int mode = hw_battery_chargemode;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (!hw_battery_setchargemode)
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
|
|
|
|
error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
|
|
|
|
&mode, -1, 1);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (newp != NULL)
|
|
|
|
error = hw_battery_setchargemode(mode);
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sysctl_hwchargestart(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
|
|
|
|
{
|
|
|
|
int start = hw_battery_chargestart;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (!hw_battery_setchargestart)
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
|
|
|
|
error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
|
|
|
|
&start, 0, 100);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (newp != NULL)
|
|
|
|
error = hw_battery_setchargestart(start);
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sysctl_hwchargestop(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
|
|
|
|
{
|
|
|
|
int stop = hw_battery_chargestop;
|
|
|
|
int error;
|
|
|
|
|
2023-06-07 21:20:56 +00:00
|
|
|
if (!hw_battery_setchargestop)
|
2023-05-18 12:23:26 +00:00
|
|
|
return EOPNOTSUPP;
|
|
|
|
|
|
|
|
error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
|
|
|
|
&stop, 0, 100);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (newp != NULL)
|
|
|
|
error = hw_battery_setchargestop(stop);
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sysctl_hwbattery(int *name, u_int namelen, void *oldp, size_t *oldlenp,
|
|
|
|
void *newp, size_t newlen)
|
|
|
|
{
|
|
|
|
if (namelen != 1)
|
|
|
|
return (ENOTDIR);
|
|
|
|
|
|
|
|
switch (name[0]) {
|
|
|
|
case HW_BATTERY_CHARGEMODE:
|
|
|
|
return (sysctl_hwchargemode(oldp, oldlenp, newp, newlen));
|
|
|
|
case HW_BATTERY_CHARGESTART:
|
|
|
|
return (sysctl_hwchargestart(oldp, oldlenp, newp, newlen));
|
|
|
|
case HW_BATTERY_CHARGESTOP:
|
|
|
|
return (sysctl_hwchargestop(oldp, oldlenp, newp, newlen));
|
|
|
|
default:
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
}
|
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2023-04-30 01:15:27 +00:00
|
|
|
#ifdef DEBUG_SYSCTL
|
|
|
|
/*
|
|
|
|
* Debugging related system variables.
|
|
|
|
*/
|
|
|
|
extern struct ctldebug debug_vfs_busyprt;
|
|
|
|
struct ctldebug debug1, debug2, debug3, debug4;
|
|
|
|
struct ctldebug debug5, debug6, debug7, debug8, debug9;
|
|
|
|
struct ctldebug debug10, debug11, debug12, debug13, debug14;
|
|
|
|
struct ctldebug debug15, debug16, debug17, debug18, debug19;
|
|
|
|
static struct ctldebug *debugvars[CTL_DEBUG_MAXID] = {
|
|
|
|
&debug_vfs_busyprt,
|
|
|
|
&debug1, &debug2, &debug3, &debug4,
|
|
|
|
&debug5, &debug6, &debug7, &debug8, &debug9,
|
|
|
|
&debug10, &debug11, &debug12, &debug13, &debug14,
|
|
|
|
&debug15, &debug16, &debug17, &debug18, &debug19,
|
|
|
|
};
|
|
|
|
int
|
|
|
|
debug_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
|
|
|
|
size_t newlen, struct proc *p)
|
|
|
|
{
|
|
|
|
struct ctldebug *cdp;
|
|
|
|
|
|
|
|
/* all sysctl names at this level are name and field */
|
|
|
|
if (namelen != 2)
|
|
|
|
return (ENOTDIR); /* overloaded */
|
|
|
|
if (name[0] < 0 || name[0] >= nitems(debugvars))
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
cdp = debugvars[name[0]];
|
|
|
|
if (cdp->debugname == 0)
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
switch (name[1]) {
|
|
|
|
case CTL_DEBUG_NAME:
|
|
|
|
return (sysctl_rdstring(oldp, oldlenp, newp, cdp->debugname));
|
|
|
|
case CTL_DEBUG_VALUE:
|
|
|
|
return (sysctl_int(oldp, oldlenp, newp, newlen, cdp->debugvar));
|
|
|
|
default:
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
}
|
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
|
|
|
#endif /* DEBUG_SYSCTL */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reads, or writes that lower the value
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sysctl_int_lower(void *oldp, size_t *oldlenp, void *newp, size_t newlen,
|
|
|
|
int *valp)
|
|
|
|
{
|
2024-08-15 02:21:18 +00:00
|
|
|
unsigned int oldval, newval;
|
2023-04-30 01:15:27 +00:00
|
|
|
int error;
|
|
|
|
|
2024-08-15 02:21:18 +00:00
|
|
|
if (oldp && *oldlenp < sizeof(int))
|
|
|
|
return (ENOMEM);
|
|
|
|
if (newp && newlen != sizeof(int))
|
|
|
|
return (EINVAL);
|
|
|
|
*oldlenp = sizeof(int);
|
|
|
|
|
|
|
|
if (newp) {
|
|
|
|
if ((error = copyin(newp, &newval, sizeof(int))))
|
|
|
|
return (error);
|
|
|
|
do {
|
|
|
|
oldval = atomic_load_int(valp);
|
|
|
|
if (oldval < (unsigned int)newval)
|
|
|
|
return (EPERM); /* do not allow raising */
|
|
|
|
} while (atomic_cas_uint(valp, oldval, newval) != oldval);
|
|
|
|
|
|
|
|
if (oldp) {
|
|
|
|
/* new value has been set although user gets error */
|
|
|
|
if ((error = copyout(&oldval, oldp, sizeof(int))))
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
} else if (oldp) {
|
|
|
|
oldval = atomic_load_int(valp);
|
|
|
|
|
|
|
|
if ((error = copyout(&oldval, oldp, sizeof(int))))
|
|
|
|
return (error);
|
|
|
|
}
|
2023-04-30 01:15:27 +00:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Validate parameters and get old / set new parameters
|
|
|
|
* for an integer-valued sysctl function.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sysctl_int(void *oldp, size_t *oldlenp, void *newp, size_t newlen, int *valp)
|
|
|
|
{
|
2024-08-15 02:21:18 +00:00
|
|
|
return (sysctl_int_bounded(oldp, oldlenp, newp, newlen, valp,
|
|
|
|
INT_MIN, INT_MAX));
|
2023-04-30 01:15:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* As above, but read-only.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sysctl_rdint(void *oldp, size_t *oldlenp, void *newp, int val)
|
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
if (oldp && *oldlenp < sizeof(int))
|
|
|
|
return (ENOMEM);
|
|
|
|
if (newp)
|
|
|
|
return (EPERM);
|
|
|
|
*oldlenp = sizeof(int);
|
|
|
|
if (oldp)
|
|
|
|
error = copyout((caddr_t)&val, oldp, sizeof(int));
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2024-08-23 05:18:42 +00:00
|
|
|
int
|
|
|
|
sysctl_securelevel(void *oldp, size_t *oldlenp, void *newp, size_t newlen,
|
|
|
|
struct proc *p)
|
|
|
|
{
|
|
|
|
int oldval, newval;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (oldp && *oldlenp < sizeof(int))
|
|
|
|
return (ENOMEM);
|
|
|
|
if (newp && newlen != sizeof(int))
|
|
|
|
return (EINVAL);
|
|
|
|
*oldlenp = sizeof(int);
|
|
|
|
|
|
|
|
if (newp) {
|
|
|
|
if ((error = copyin(newp, &newval, sizeof(int))))
|
|
|
|
return (error);
|
|
|
|
do {
|
|
|
|
oldval = atomic_load_int(&securelevel);
|
|
|
|
if ((oldval > 0 || newval < -1) && newval < oldval &&
|
|
|
|
p->p_p->ps_pid != 1)
|
|
|
|
return (EPERM);
|
|
|
|
} while (atomic_cas_uint(&securelevel, oldval, newval) !=
|
|
|
|
oldval);
|
|
|
|
|
|
|
|
if (oldp) {
|
|
|
|
/* new value has been set although user gets error */
|
|
|
|
if ((error = copyout(&oldval, oldp, sizeof(int))))
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
} else if (oldp) {
|
|
|
|
oldval = atomic_load_int(&securelevel);
|
|
|
|
|
|
|
|
if ((error = copyout(&oldval, oldp, sizeof(int))))
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2023-04-30 01:15:27 +00:00
|
|
|
/*
|
|
|
|
* Selects between sysctl_rdint and sysctl_int according to securelevel.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sysctl_securelevel_int(void *oldp, size_t *oldlenp, void *newp, size_t newlen,
|
|
|
|
int *valp)
|
|
|
|
{
|
2024-08-23 05:18:42 +00:00
|
|
|
if (atomic_load_int(&securelevel) > 0)
|
2023-04-30 01:15:27 +00:00
|
|
|
return (sysctl_rdint(oldp, oldlenp, newp, *valp));
|
|
|
|
return (sysctl_int(oldp, oldlenp, newp, newlen, valp));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read-only or bounded integer values.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sysctl_int_bounded(void *oldp, size_t *oldlenp, void *newp, size_t newlen,
|
|
|
|
int *valp, int minimum, int maximum)
|
|
|
|
{
|
2024-07-11 15:49:23 +00:00
|
|
|
int oldval, newval;
|
2023-04-30 01:15:27 +00:00
|
|
|
int error;
|
|
|
|
|
|
|
|
/* read only */
|
2024-07-11 15:49:23 +00:00
|
|
|
if (newp != NULL && minimum > maximum)
|
|
|
|
return (EPERM);
|
2023-04-30 01:15:27 +00:00
|
|
|
|
2024-07-11 15:49:23 +00:00
|
|
|
if (oldp != NULL && *oldlenp < sizeof(int))
|
|
|
|
return (ENOMEM);
|
|
|
|
if (newp != NULL && newlen != sizeof(int))
|
2023-04-30 01:15:27 +00:00
|
|
|
return (EINVAL);
|
2024-07-11 15:49:23 +00:00
|
|
|
*oldlenp = sizeof(int);
|
|
|
|
|
|
|
|
/* copyin() may sleep, call it first */
|
|
|
|
if (newp != NULL) {
|
|
|
|
if ((error = copyin(newp, &newval, sizeof(int))))
|
|
|
|
return (error);
|
|
|
|
/* outside limits */
|
|
|
|
if (newval < minimum || maximum < newval)
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
if (oldp != NULL) {
|
|
|
|
if (newp != NULL)
|
|
|
|
oldval = atomic_swap_uint(valp, newval);
|
|
|
|
else
|
|
|
|
oldval = atomic_load_int(valp);
|
|
|
|
if ((error = copyout(&oldval, oldp, sizeof(int)))) {
|
|
|
|
/* new value has been set although user gets error */
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
} else if (newp != NULL)
|
|
|
|
atomic_store_int(valp, newval);
|
|
|
|
|
2023-04-30 01:15:27 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Array of read-only or bounded integer values.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sysctl_bounded_arr(const struct sysctl_bounded_args *valpp, u_int valplen,
|
|
|
|
int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
|
|
|
|
size_t newlen)
|
|
|
|
{
|
|
|
|
u_int i;
|
|
|
|
if (namelen != 1)
|
|
|
|
return (ENOTDIR);
|
|
|
|
for (i = 0; i < valplen; ++i) {
|
|
|
|
if (valpp[i].mib == name[0]) {
|
|
|
|
return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
|
|
|
|
valpp[i].var, valpp[i].minimum, valpp[i].maximum));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Validate parameters and get old / set new parameters
|
|
|
|
* for an integer-valued sysctl function.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sysctl_quad(void *oldp, size_t *oldlenp, void *newp, size_t newlen,
|
|
|
|
int64_t *valp)
|
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
if (oldp && *oldlenp < sizeof(int64_t))
|
|
|
|
return (ENOMEM);
|
|
|
|
if (newp && newlen != sizeof(int64_t))
|
|
|
|
return (EINVAL);
|
|
|
|
*oldlenp = sizeof(int64_t);
|
|
|
|
if (oldp)
|
|
|
|
error = copyout(valp, oldp, sizeof(int64_t));
|
|
|
|
if (error == 0 && newp)
|
|
|
|
error = copyin(newp, valp, sizeof(int64_t));
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* As above, but read-only.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sysctl_rdquad(void *oldp, size_t *oldlenp, void *newp, int64_t val)
|
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
if (oldp && *oldlenp < sizeof(int64_t))
|
|
|
|
return (ENOMEM);
|
|
|
|
if (newp)
|
|
|
|
return (EPERM);
|
|
|
|
*oldlenp = sizeof(int64_t);
|
|
|
|
if (oldp)
|
|
|
|
error = copyout((caddr_t)&val, oldp, sizeof(int64_t));
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Validate parameters and get old / set new parameters
|
|
|
|
* for a string-valued sysctl function.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sysctl_string(void *oldp, size_t *oldlenp, void *newp, size_t newlen, char *str,
|
|
|
|
size_t maxlen)
|
|
|
|
{
|
|
|
|
return sysctl__string(oldp, oldlenp, newp, newlen, str, maxlen, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sysctl_tstring(void *oldp, size_t *oldlenp, void *newp, size_t newlen,
|
|
|
|
char *str, size_t maxlen)
|
|
|
|
{
|
|
|
|
return sysctl__string(oldp, oldlenp, newp, newlen, str, maxlen, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sysctl__string(void *oldp, size_t *oldlenp, void *newp, size_t newlen,
|
|
|
|
char *str, size_t maxlen, int trunc)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
len = strlen(str) + 1;
|
|
|
|
if (oldp && *oldlenp < len) {
|
|
|
|
if (trunc == 0 || *oldlenp == 0)
|
|
|
|
return (ENOMEM);
|
|
|
|
}
|
|
|
|
if (newp && newlen >= maxlen)
|
|
|
|
return (EINVAL);
|
|
|
|
if (oldp) {
|
|
|
|
if (trunc && *oldlenp < len) {
|
|
|
|
len = *oldlenp;
|
|
|
|
error = copyout(str, oldp, len - 1);
|
|
|
|
if (error == 0)
|
|
|
|
error = copyout("", (char *)oldp + len - 1, 1);
|
|
|
|
} else {
|
|
|
|
error = copyout(str, oldp, len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*oldlenp = len;
|
|
|
|
if (error == 0 && newp) {
|
|
|
|
error = copyin(newp, str, newlen);
|
|
|
|
str[newlen] = 0;
|
|
|
|
}
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* As above, but read-only.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sysctl_rdstring(void *oldp, size_t *oldlenp, void *newp, const char *str)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
len = strlen(str) + 1;
|
|
|
|
if (oldp && *oldlenp < len)
|
|
|
|
return (ENOMEM);
|
|
|
|
if (newp)
|
|
|
|
return (EPERM);
|
|
|
|
*oldlenp = len;
|
|
|
|
if (oldp)
|
|
|
|
error = copyout(str, oldp, len);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Validate parameters and get old / set new parameters
|
|
|
|
* for a structure oriented sysctl function.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sysctl_struct(void *oldp, size_t *oldlenp, void *newp, size_t newlen, void *sp,
|
|
|
|
size_t len)
|
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
if (oldp && *oldlenp < len)
|
|
|
|
return (ENOMEM);
|
|
|
|
if (newp && newlen > len)
|
|
|
|
return (EINVAL);
|
|
|
|
if (oldp) {
|
|
|
|
*oldlenp = len;
|
|
|
|
error = copyout(sp, oldp, len);
|
|
|
|
}
|
|
|
|
if (error == 0 && newp)
|
|
|
|
error = copyin(newp, sp, len);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Validate parameters and get old parameters
|
|
|
|
* for a structure oriented sysctl function.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sysctl_rdstruct(void *oldp, size_t *oldlenp, void *newp, const void *sp,
|
|
|
|
size_t len)
|
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
if (oldp && *oldlenp < len)
|
|
|
|
return (ENOMEM);
|
|
|
|
if (newp)
|
|
|
|
return (EPERM);
|
|
|
|
*oldlenp = len;
|
|
|
|
if (oldp)
|
|
|
|
error = copyout(sp, oldp, len);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef SMALL_KERNEL
|
|
|
|
void
|
|
|
|
fill_file(struct kinfo_file *kf, struct file *fp, struct filedesc *fdp,
|
|
|
|
int fd, struct vnode *vp, struct process *pr, struct proc *p,
|
|
|
|
struct socket *so, int show_pointers)
|
|
|
|
{
|
|
|
|
struct vattr va;
|
|
|
|
|
|
|
|
memset(kf, 0, sizeof(*kf));
|
|
|
|
|
|
|
|
kf->fd_fd = fd; /* might not really be an fd */
|
|
|
|
|
|
|
|
if (fp != NULL) {
|
|
|
|
if (show_pointers)
|
|
|
|
kf->f_fileaddr = PTRTOINT64(fp);
|
|
|
|
kf->f_flag = fp->f_flag;
|
|
|
|
kf->f_iflags = fp->f_iflags;
|
|
|
|
kf->f_type = fp->f_type;
|
|
|
|
kf->f_count = fp->f_count;
|
|
|
|
if (show_pointers)
|
|
|
|
kf->f_ucred = PTRTOINT64(fp->f_cred);
|
|
|
|
kf->f_uid = fp->f_cred->cr_uid;
|
|
|
|
kf->f_gid = fp->f_cred->cr_gid;
|
|
|
|
if (show_pointers)
|
|
|
|
kf->f_ops = PTRTOINT64(fp->f_ops);
|
|
|
|
if (show_pointers)
|
|
|
|
kf->f_data = PTRTOINT64(fp->f_data);
|
|
|
|
kf->f_usecount = 0;
|
|
|
|
|
|
|
|
if (suser(p) == 0 || p->p_ucred->cr_uid == fp->f_cred->cr_uid) {
|
|
|
|
mtx_enter(&fp->f_mtx);
|
|
|
|
kf->f_offset = fp->f_offset;
|
|
|
|
kf->f_rxfer = fp->f_rxfer;
|
|
|
|
kf->f_rwfer = fp->f_wxfer;
|
|
|
|
kf->f_seek = fp->f_seek;
|
|
|
|
kf->f_rbytes = fp->f_rbytes;
|
|
|
|
kf->f_wbytes = fp->f_wbytes;
|
|
|
|
mtx_leave(&fp->f_mtx);
|
|
|
|
} else
|
|
|
|
kf->f_offset = -1;
|
|
|
|
} else if (vp != NULL) {
|
|
|
|
/* fake it */
|
|
|
|
kf->f_type = DTYPE_VNODE;
|
|
|
|
kf->f_flag = FREAD;
|
|
|
|
if (fd == KERN_FILE_TRACE)
|
|
|
|
kf->f_flag |= FWRITE;
|
|
|
|
} else if (so != NULL) {
|
|
|
|
/* fake it */
|
|
|
|
kf->f_type = DTYPE_SOCKET;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* information about the object associated with this file */
|
|
|
|
switch (kf->f_type) {
|
|
|
|
case DTYPE_VNODE:
|
|
|
|
if (fp != NULL)
|
|
|
|
vp = (struct vnode *)fp->f_data;
|
|
|
|
|
|
|
|
if (show_pointers)
|
|
|
|
kf->v_un = PTRTOINT64(vp->v_un.vu_socket);
|
|
|
|
kf->v_type = vp->v_type;
|
|
|
|
kf->v_tag = vp->v_tag;
|
|
|
|
kf->v_flag = vp->v_flag;
|
|
|
|
if (show_pointers)
|
|
|
|
kf->v_data = PTRTOINT64(vp->v_data);
|
|
|
|
if (show_pointers)
|
|
|
|
kf->v_mount = PTRTOINT64(vp->v_mount);
|
|
|
|
if (vp->v_mount)
|
|
|
|
strlcpy(kf->f_mntonname,
|
|
|
|
vp->v_mount->mnt_stat.f_mntonname,
|
|
|
|
sizeof(kf->f_mntonname));
|
|
|
|
|
|
|
|
if (VOP_GETATTR(vp, &va, p->p_ucred, p) == 0) {
|
|
|
|
kf->va_fileid = va.va_fileid;
|
|
|
|
kf->va_mode = MAKEIMODE(va.va_type, va.va_mode);
|
|
|
|
kf->va_size = va.va_size;
|
|
|
|
kf->va_rdev = va.va_rdev;
|
|
|
|
kf->va_fsid = va.va_fsid & 0xffffffff;
|
|
|
|
kf->va_nlink = va.va_nlink;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DTYPE_SOCKET: {
|
|
|
|
int locked = 0;
|
|
|
|
|
|
|
|
if (so == NULL) {
|
|
|
|
so = (struct socket *)fp->f_data;
|
|
|
|
/* if so is passed as parameter it is already locked */
|
2024-01-19 01:25:19 +00:00
|
|
|
solock(so);
|
|
|
|
locked = 1;
|
2023-04-30 01:15:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
kf->so_type = so->so_type;
|
|
|
|
kf->so_state = so->so_state | so->so_snd.sb_state |
|
|
|
|
so->so_rcv.sb_state;
|
|
|
|
if (show_pointers)
|
|
|
|
kf->so_pcb = PTRTOINT64(so->so_pcb);
|
|
|
|
else
|
|
|
|
kf->so_pcb = -1;
|
|
|
|
kf->so_protocol = so->so_proto->pr_protocol;
|
|
|
|
kf->so_family = so->so_proto->pr_domain->dom_family;
|
|
|
|
kf->so_rcv_cc = so->so_rcv.sb_cc;
|
|
|
|
kf->so_snd_cc = so->so_snd.sb_cc;
|
|
|
|
if (isspliced(so)) {
|
|
|
|
if (show_pointers)
|
|
|
|
kf->so_splice =
|
|
|
|
PTRTOINT64(so->so_sp->ssp_socket);
|
|
|
|
kf->so_splicelen = so->so_sp->ssp_len;
|
|
|
|
} else if (issplicedback(so))
|
|
|
|
kf->so_splicelen = -1;
|
|
|
|
if (so->so_pcb == NULL) {
|
|
|
|
if (locked)
|
2024-01-19 01:25:19 +00:00
|
|
|
sounlock(so);
|
2023-04-30 01:15:27 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
switch (kf->so_family) {
|
|
|
|
case AF_INET: {
|
|
|
|
struct inpcb *inpcb = so->so_pcb;
|
|
|
|
|
2024-01-19 01:25:19 +00:00
|
|
|
soassertlocked(so);
|
2023-04-30 01:15:27 +00:00
|
|
|
if (show_pointers)
|
|
|
|
kf->inp_ppcb = PTRTOINT64(inpcb->inp_ppcb);
|
|
|
|
kf->inp_lport = inpcb->inp_lport;
|
|
|
|
kf->inp_laddru[0] = inpcb->inp_laddr.s_addr;
|
|
|
|
kf->inp_fport = inpcb->inp_fport;
|
|
|
|
kf->inp_faddru[0] = inpcb->inp_faddr.s_addr;
|
|
|
|
kf->inp_rtableid = inpcb->inp_rtableid;
|
|
|
|
if (so->so_type == SOCK_RAW)
|
|
|
|
kf->inp_proto = inpcb->inp_ip.ip_p;
|
|
|
|
if (so->so_proto->pr_protocol == IPPROTO_TCP) {
|
|
|
|
struct tcpcb *tcpcb = (void *)inpcb->inp_ppcb;
|
|
|
|
kf->t_rcv_wnd = tcpcb->rcv_wnd;
|
|
|
|
kf->t_snd_wnd = tcpcb->snd_wnd;
|
|
|
|
kf->t_snd_cwnd = tcpcb->snd_cwnd;
|
|
|
|
kf->t_state = tcpcb->t_state;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case AF_INET6: {
|
|
|
|
struct inpcb *inpcb = so->so_pcb;
|
|
|
|
|
2024-01-19 01:25:19 +00:00
|
|
|
soassertlocked(so);
|
2023-04-30 01:15:27 +00:00
|
|
|
if (show_pointers)
|
|
|
|
kf->inp_ppcb = PTRTOINT64(inpcb->inp_ppcb);
|
|
|
|
kf->inp_lport = inpcb->inp_lport;
|
|
|
|
kf->inp_laddru[0] = inpcb->inp_laddr6.s6_addr32[0];
|
|
|
|
kf->inp_laddru[1] = inpcb->inp_laddr6.s6_addr32[1];
|
|
|
|
kf->inp_laddru[2] = inpcb->inp_laddr6.s6_addr32[2];
|
|
|
|
kf->inp_laddru[3] = inpcb->inp_laddr6.s6_addr32[3];
|
|
|
|
kf->inp_fport = inpcb->inp_fport;
|
|
|
|
kf->inp_faddru[0] = inpcb->inp_faddr6.s6_addr32[0];
|
|
|
|
kf->inp_faddru[1] = inpcb->inp_faddr6.s6_addr32[1];
|
|
|
|
kf->inp_faddru[2] = inpcb->inp_faddr6.s6_addr32[2];
|
|
|
|
kf->inp_faddru[3] = inpcb->inp_faddr6.s6_addr32[3];
|
|
|
|
kf->inp_rtableid = inpcb->inp_rtableid;
|
|
|
|
if (so->so_type == SOCK_RAW)
|
|
|
|
kf->inp_proto = inpcb->inp_ipv6.ip6_nxt;
|
|
|
|
if (so->so_proto->pr_protocol == IPPROTO_TCP) {
|
|
|
|
struct tcpcb *tcpcb = (void *)inpcb->inp_ppcb;
|
|
|
|
kf->t_rcv_wnd = tcpcb->rcv_wnd;
|
|
|
|
kf->t_snd_wnd = tcpcb->snd_wnd;
|
|
|
|
kf->t_state = tcpcb->t_state;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case AF_UNIX: {
|
|
|
|
struct unpcb *unpcb = so->so_pcb;
|
|
|
|
|
|
|
|
kf->f_msgcount = unpcb->unp_msgcount;
|
|
|
|
if (show_pointers) {
|
|
|
|
kf->unp_conn = PTRTOINT64(unpcb->unp_conn);
|
|
|
|
kf->unp_refs = PTRTOINT64(
|
|
|
|
SLIST_FIRST(&unpcb->unp_refs));
|
|
|
|
kf->unp_nextref = PTRTOINT64(
|
|
|
|
SLIST_NEXT(unpcb, unp_nextref));
|
|
|
|
kf->v_un = PTRTOINT64(unpcb->unp_vnode);
|
|
|
|
kf->unp_addr = PTRTOINT64(unpcb->unp_addr);
|
|
|
|
}
|
|
|
|
if (unpcb->unp_addr != NULL) {
|
|
|
|
struct sockaddr_un *un = mtod(unpcb->unp_addr,
|
|
|
|
struct sockaddr_un *);
|
|
|
|
memcpy(kf->unp_path, un->sun_path, un->sun_len
|
|
|
|
- offsetof(struct sockaddr_un,sun_path));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (locked)
|
2024-01-19 01:25:19 +00:00
|
|
|
sounlock(so);
|
2023-04-30 01:15:27 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DTYPE_PIPE: {
|
|
|
|
struct pipe *pipe = (struct pipe *)fp->f_data;
|
|
|
|
|
|
|
|
if (show_pointers)
|
|
|
|
kf->pipe_peer = PTRTOINT64(pipe->pipe_peer);
|
|
|
|
kf->pipe_state = pipe->pipe_state;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DTYPE_KQUEUE: {
|
|
|
|
struct kqueue *kqi = (struct kqueue *)fp->f_data;
|
|
|
|
|
|
|
|
kf->kq_count = kqi->kq_count;
|
|
|
|
kf->kq_state = kqi->kq_state;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* per-process information for KERN_FILE_BY[PU]ID */
|
|
|
|
if (pr != NULL) {
|
|
|
|
kf->p_pid = pr->ps_pid;
|
|
|
|
kf->p_uid = pr->ps_ucred->cr_uid;
|
|
|
|
kf->p_gid = pr->ps_ucred->cr_gid;
|
|
|
|
kf->p_tid = -1;
|
|
|
|
strlcpy(kf->p_comm, pr->ps_comm, sizeof(kf->p_comm));
|
|
|
|
}
|
|
|
|
if (fdp != NULL) {
|
|
|
|
fdplock(fdp);
|
|
|
|
kf->fd_ofileflags = fdp->fd_ofileflags[fd];
|
|
|
|
fdpunlock(fdp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get file structures.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sysctl_file(int *name, u_int namelen, char *where, size_t *sizep,
|
|
|
|
struct proc *p)
|
|
|
|
{
|
|
|
|
struct kinfo_file *kf;
|
|
|
|
struct filedesc *fdp;
|
|
|
|
struct file *fp;
|
|
|
|
struct process *pr;
|
|
|
|
size_t buflen, elem_size, elem_count, outsize;
|
|
|
|
char *dp = where;
|
|
|
|
int arg, i, error = 0, needed = 0, matched;
|
|
|
|
u_int op;
|
|
|
|
int show_pointers;
|
|
|
|
|
|
|
|
if (namelen > 4)
|
|
|
|
return (ENOTDIR);
|
|
|
|
if (namelen < 4 || name[2] > sizeof(*kf))
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
buflen = where != NULL ? *sizep : 0;
|
|
|
|
op = name[0];
|
|
|
|
arg = name[1];
|
|
|
|
elem_size = name[2];
|
|
|
|
elem_count = name[3];
|
|
|
|
outsize = MIN(sizeof(*kf), elem_size);
|
|
|
|
|
|
|
|
if (elem_size < 1)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
show_pointers = suser(curproc) == 0;
|
|
|
|
|
|
|
|
kf = malloc(sizeof(*kf), M_TEMP, M_WAITOK);
|
|
|
|
|
|
|
|
#define FILLIT2(fp, fdp, i, vp, pr, so) do { \
|
|
|
|
if (buflen >= elem_size && elem_count > 0) { \
|
|
|
|
fill_file(kf, fp, fdp, i, vp, pr, p, so, show_pointers);\
|
|
|
|
error = copyout(kf, dp, outsize); \
|
|
|
|
if (error) \
|
|
|
|
break; \
|
|
|
|
dp += elem_size; \
|
|
|
|
buflen -= elem_size; \
|
|
|
|
elem_count--; \
|
|
|
|
} \
|
|
|
|
needed += elem_size; \
|
|
|
|
} while (0)
|
|
|
|
#define FILLIT(fp, fdp, i, vp, pr) \
|
|
|
|
FILLIT2(fp, fdp, i, vp, pr, NULL)
|
|
|
|
#define FILLSO(so) \
|
|
|
|
FILLIT2(NULL, NULL, 0, NULL, NULL, so)
|
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case KERN_FILE_BYFILE:
|
|
|
|
/* use the inp-tables to pick up closed connections, too */
|
|
|
|
if (arg == DTYPE_SOCKET) {
|
|
|
|
struct inpcb *inp;
|
|
|
|
|
|
|
|
NET_LOCK();
|
|
|
|
mtx_enter(&tcbtable.inpt_mtx);
|
|
|
|
TAILQ_FOREACH(inp, &tcbtable.inpt_queue, inp_queue)
|
|
|
|
FILLSO(inp->inp_socket);
|
|
|
|
mtx_leave(&tcbtable.inpt_mtx);
|
2024-04-13 04:04:37 +00:00
|
|
|
#ifdef INET6
|
|
|
|
mtx_enter(&tcb6table.inpt_mtx);
|
|
|
|
TAILQ_FOREACH(inp, &tcb6table.inpt_queue, inp_queue)
|
|
|
|
FILLSO(inp->inp_socket);
|
|
|
|
mtx_leave(&tcb6table.inpt_mtx);
|
|
|
|
#endif
|
2023-04-30 01:15:27 +00:00
|
|
|
mtx_enter(&udbtable.inpt_mtx);
|
|
|
|
TAILQ_FOREACH(inp, &udbtable.inpt_queue, inp_queue)
|
|
|
|
FILLSO(inp->inp_socket);
|
|
|
|
mtx_leave(&udbtable.inpt_mtx);
|
2024-01-11 02:33:10 +00:00
|
|
|
#ifdef INET6
|
|
|
|
mtx_enter(&udb6table.inpt_mtx);
|
|
|
|
TAILQ_FOREACH(inp, &udb6table.inpt_queue, inp_queue)
|
|
|
|
FILLSO(inp->inp_socket);
|
|
|
|
mtx_leave(&udb6table.inpt_mtx);
|
|
|
|
#endif
|
2023-04-30 01:15:27 +00:00
|
|
|
mtx_enter(&rawcbtable.inpt_mtx);
|
|
|
|
TAILQ_FOREACH(inp, &rawcbtable.inpt_queue, inp_queue)
|
|
|
|
FILLSO(inp->inp_socket);
|
|
|
|
mtx_leave(&rawcbtable.inpt_mtx);
|
|
|
|
#ifdef INET6
|
|
|
|
mtx_enter(&rawin6pcbtable.inpt_mtx);
|
|
|
|
TAILQ_FOREACH(inp, &rawin6pcbtable.inpt_queue,
|
|
|
|
inp_queue)
|
|
|
|
FILLSO(inp->inp_socket);
|
|
|
|
mtx_leave(&rawin6pcbtable.inpt_mtx);
|
|
|
|
#endif
|
|
|
|
NET_UNLOCK();
|
|
|
|
}
|
|
|
|
fp = NULL;
|
|
|
|
while ((fp = fd_iterfile(fp, p)) != NULL) {
|
|
|
|
if ((arg == 0 || fp->f_type == arg)) {
|
|
|
|
int af, skip = 0;
|
|
|
|
if (arg == DTYPE_SOCKET && fp->f_type == arg) {
|
|
|
|
af = ((struct socket *)fp->f_data)->
|
|
|
|
so_proto->pr_domain->dom_family;
|
|
|
|
if (af == AF_INET || af == AF_INET6)
|
|
|
|
skip = 1;
|
|
|
|
}
|
|
|
|
if (!skip)
|
|
|
|
FILLIT(fp, NULL, 0, NULL, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case KERN_FILE_BYPID:
|
|
|
|
/* A arg of -1 indicates all processes */
|
|
|
|
if (arg < -1) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
matched = 0;
|
2024-01-20 03:21:37 +00:00
|
|
|
LIST_FOREACH(pr, &allprocess, ps_list) {
|
2023-04-30 01:15:27 +00:00
|
|
|
/*
|
|
|
|
* skip system, exiting, embryonic and undead
|
|
|
|
* processes
|
|
|
|
*/
|
|
|
|
if (pr->ps_flags & (PS_SYSTEM | PS_EMBRYO | PS_EXITING))
|
|
|
|
continue;
|
2024-08-08 20:49:23 +00:00
|
|
|
if (arg >= 0 && pr->ps_pid != (pid_t)arg) {
|
2023-04-30 01:15:27 +00:00
|
|
|
/* not the pid we are looking for */
|
|
|
|
continue;
|
|
|
|
}
|
2024-08-12 06:29:20 +00:00
|
|
|
|
|
|
|
refcnt_take(&pr->ps_refcnt);
|
|
|
|
|
2023-04-30 01:15:27 +00:00
|
|
|
matched = 1;
|
|
|
|
fdp = pr->ps_fd;
|
|
|
|
if (pr->ps_textvp)
|
|
|
|
FILLIT(NULL, NULL, KERN_FILE_TEXT, pr->ps_textvp, pr);
|
|
|
|
if (fdp->fd_cdir)
|
|
|
|
FILLIT(NULL, NULL, KERN_FILE_CDIR, fdp->fd_cdir, pr);
|
|
|
|
if (fdp->fd_rdir)
|
|
|
|
FILLIT(NULL, NULL, KERN_FILE_RDIR, fdp->fd_rdir, pr);
|
|
|
|
if (pr->ps_tracevp)
|
|
|
|
FILLIT(NULL, NULL, KERN_FILE_TRACE, pr->ps_tracevp, pr);
|
|
|
|
for (i = 0; i < fdp->fd_nfiles; i++) {
|
|
|
|
if ((fp = fd_getfile(fdp, i)) == NULL)
|
|
|
|
continue;
|
|
|
|
FILLIT(fp, fdp, i, NULL, pr);
|
|
|
|
FRELE(fp, p);
|
|
|
|
}
|
2024-08-12 06:29:20 +00:00
|
|
|
|
|
|
|
refcnt_rele_wake(&pr->ps_refcnt);
|
|
|
|
|
2024-08-08 20:49:23 +00:00
|
|
|
/* pid is unique, stop searching */
|
|
|
|
if (arg >= 0)
|
|
|
|
break;
|
2023-04-30 01:15:27 +00:00
|
|
|
}
|
|
|
|
if (!matched)
|
|
|
|
error = ESRCH;
|
|
|
|
break;
|
|
|
|
case KERN_FILE_BYUID:
|
2024-01-20 03:21:37 +00:00
|
|
|
LIST_FOREACH(pr, &allprocess, ps_list) {
|
2023-04-30 01:15:27 +00:00
|
|
|
/*
|
|
|
|
* skip system, exiting, embryonic and undead
|
|
|
|
* processes
|
|
|
|
*/
|
|
|
|
if (pr->ps_flags & (PS_SYSTEM | PS_EMBRYO | PS_EXITING))
|
|
|
|
continue;
|
|
|
|
if (arg >= 0 && pr->ps_ucred->cr_uid != (uid_t)arg) {
|
|
|
|
/* not the uid we are looking for */
|
|
|
|
continue;
|
|
|
|
}
|
2024-08-12 06:29:20 +00:00
|
|
|
|
|
|
|
refcnt_take(&pr->ps_refcnt);
|
|
|
|
|
2023-04-30 01:15:27 +00:00
|
|
|
fdp = pr->ps_fd;
|
|
|
|
if (fdp->fd_cdir)
|
|
|
|
FILLIT(NULL, NULL, KERN_FILE_CDIR, fdp->fd_cdir, pr);
|
|
|
|
if (fdp->fd_rdir)
|
|
|
|
FILLIT(NULL, NULL, KERN_FILE_RDIR, fdp->fd_rdir, pr);
|
|
|
|
if (pr->ps_tracevp)
|
|
|
|
FILLIT(NULL, NULL, KERN_FILE_TRACE, pr->ps_tracevp, pr);
|
|
|
|
for (i = 0; i < fdp->fd_nfiles; i++) {
|
|
|
|
if ((fp = fd_getfile(fdp, i)) == NULL)
|
|
|
|
continue;
|
|
|
|
FILLIT(fp, fdp, i, NULL, pr);
|
|
|
|
FRELE(fp, p);
|
|
|
|
}
|
2024-08-12 06:29:20 +00:00
|
|
|
|
|
|
|
refcnt_rele_wake(&pr->ps_refcnt);
|
2023-04-30 01:15:27 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
free(kf, M_TEMP, sizeof(*kf));
|
|
|
|
|
|
|
|
if (!error) {
|
|
|
|
if (where == NULL)
|
|
|
|
needed += KERN_FILESLOP * elem_size;
|
|
|
|
else if (*sizep < needed)
|
|
|
|
error = ENOMEM;
|
|
|
|
*sizep = needed;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* try over estimating by 5 procs
|
|
|
|
*/
|
|
|
|
#define KERN_PROCSLOP 5
|
|
|
|
|
|
|
|
int
|
|
|
|
sysctl_doproc(int *name, u_int namelen, char *where, size_t *sizep)
|
|
|
|
{
|
|
|
|
struct kinfo_proc *kproc = NULL;
|
|
|
|
struct proc *p;
|
|
|
|
struct process *pr;
|
|
|
|
char *dp;
|
|
|
|
int arg, buflen, doingzomb, elem_size, elem_count;
|
|
|
|
int error, needed, op;
|
|
|
|
int dothreads = 0;
|
|
|
|
int show_pointers;
|
|
|
|
|
|
|
|
dp = where;
|
|
|
|
buflen = where != NULL ? *sizep : 0;
|
|
|
|
needed = error = 0;
|
|
|
|
|
|
|
|
if (namelen != 4 || name[2] <= 0 || name[3] < 0 ||
|
|
|
|
name[2] > sizeof(*kproc))
|
|
|
|
return (EINVAL);
|
|
|
|
op = name[0];
|
|
|
|
arg = name[1];
|
|
|
|
elem_size = name[2];
|
|
|
|
elem_count = name[3];
|
|
|
|
|
|
|
|
dothreads = op & KERN_PROC_SHOW_THREADS;
|
|
|
|
op &= ~KERN_PROC_SHOW_THREADS;
|
|
|
|
|
|
|
|
show_pointers = suser(curproc) == 0;
|
|
|
|
|
|
|
|
if (where != NULL)
|
|
|
|
kproc = malloc(sizeof(*kproc), M_TEMP, M_WAITOK);
|
|
|
|
|
|
|
|
pr = LIST_FIRST(&allprocess);
|
|
|
|
doingzomb = 0;
|
|
|
|
again:
|
|
|
|
for (; pr != NULL; pr = LIST_NEXT(pr, ps_list)) {
|
|
|
|
/* XXX skip processes in the middle of being zapped */
|
|
|
|
if (pr->ps_pgrp == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Skip embryonic processes.
|
|
|
|
*/
|
|
|
|
if (pr->ps_flags & PS_EMBRYO)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO - make more efficient (see notes below).
|
|
|
|
*/
|
|
|
|
switch (op) {
|
|
|
|
|
|
|
|
case KERN_PROC_PID:
|
|
|
|
/* could do this with just a lookup */
|
|
|
|
if (pr->ps_pid != (pid_t)arg)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KERN_PROC_PGRP:
|
|
|
|
/* could do this by traversing pgrp */
|
|
|
|
if (pr->ps_pgrp->pg_id != (pid_t)arg)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KERN_PROC_SESSION:
|
|
|
|
if (pr->ps_session->s_leader == NULL ||
|
|
|
|
pr->ps_session->s_leader->ps_pid != (pid_t)arg)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KERN_PROC_TTY:
|
|
|
|
if ((pr->ps_flags & PS_CONTROLT) == 0 ||
|
|
|
|
pr->ps_session->s_ttyp == NULL ||
|
|
|
|
pr->ps_session->s_ttyp->t_dev != (dev_t)arg)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KERN_PROC_UID:
|
|
|
|
if (pr->ps_ucred->cr_uid != (uid_t)arg)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KERN_PROC_RUID:
|
|
|
|
if (pr->ps_ucred->cr_ruid != (uid_t)arg)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KERN_PROC_ALL:
|
|
|
|
if (pr->ps_flags & PS_SYSTEM)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KERN_PROC_KTHREAD:
|
|
|
|
/* no filtering */
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
error = EINVAL;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buflen >= elem_size && elem_count > 0) {
|
|
|
|
fill_kproc(pr, kproc, NULL, show_pointers);
|
|
|
|
error = copyout(kproc, dp, elem_size);
|
|
|
|
if (error)
|
|
|
|
goto err;
|
|
|
|
dp += elem_size;
|
|
|
|
buflen -= elem_size;
|
|
|
|
elem_count--;
|
|
|
|
}
|
|
|
|
needed += elem_size;
|
|
|
|
|
|
|
|
/* Skip per-thread entries if not required by op */
|
|
|
|
if (!dothreads)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link) {
|
|
|
|
if (buflen >= elem_size && elem_count > 0) {
|
|
|
|
fill_kproc(pr, kproc, p, show_pointers);
|
|
|
|
error = copyout(kproc, dp, elem_size);
|
|
|
|
if (error)
|
|
|
|
goto err;
|
|
|
|
dp += elem_size;
|
|
|
|
buflen -= elem_size;
|
|
|
|
elem_count--;
|
|
|
|
}
|
|
|
|
needed += elem_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (doingzomb == 0) {
|
|
|
|
pr = LIST_FIRST(&zombprocess);
|
|
|
|
doingzomb++;
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
if (where != NULL) {
|
|
|
|
*sizep = dp - where;
|
|
|
|
if (needed > *sizep) {
|
|
|
|
error = ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
needed += KERN_PROCSLOP * elem_size;
|
|
|
|
*sizep = needed;
|
|
|
|
}
|
|
|
|
err:
|
|
|
|
if (kproc)
|
|
|
|
free(kproc, M_TEMP, sizeof(*kproc));
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fill in a kproc structure for the specified process.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
fill_kproc(struct process *pr, struct kinfo_proc *ki, struct proc *p,
|
|
|
|
int show_pointers)
|
|
|
|
{
|
|
|
|
struct session *s = pr->ps_session;
|
|
|
|
struct tty *tp;
|
|
|
|
struct vmspace *vm = pr->ps_vmspace;
|
|
|
|
struct timespec booted, st, ut, utc;
|
2024-07-08 18:11:41 +00:00
|
|
|
struct tusage tu;
|
2023-04-30 01:15:27 +00:00
|
|
|
int isthread;
|
|
|
|
|
|
|
|
isthread = p != NULL;
|
2024-07-08 18:11:41 +00:00
|
|
|
if (!isthread) {
|
2023-04-30 01:15:27 +00:00
|
|
|
p = pr->ps_mainproc; /* XXX */
|
2024-07-08 18:11:41 +00:00
|
|
|
tuagg_get_process(&tu, pr);
|
|
|
|
} else
|
|
|
|
tuagg_get_proc(&tu, p);
|
2023-04-30 01:15:27 +00:00
|
|
|
|
|
|
|
FILL_KPROC(ki, strlcpy, p, pr, pr->ps_ucred, pr->ps_pgrp,
|
2024-07-08 18:11:41 +00:00
|
|
|
p, pr, s, vm, pr->ps_limit, pr->ps_sigacts, &tu, isthread,
|
2023-04-30 01:15:27 +00:00
|
|
|
show_pointers);
|
|
|
|
|
|
|
|
/* stuff that's too painful to generalize into the macros */
|
|
|
|
if (pr->ps_pptr)
|
|
|
|
ki->p_ppid = pr->ps_ppid;
|
|
|
|
if (s->s_leader)
|
|
|
|
ki->p_sid = s->s_leader->ps_pid;
|
|
|
|
|
|
|
|
if ((pr->ps_flags & PS_CONTROLT) && (tp = s->s_ttyp)) {
|
|
|
|
ki->p_tdev = tp->t_dev;
|
|
|
|
ki->p_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : -1;
|
|
|
|
if (show_pointers)
|
|
|
|
ki->p_tsess = PTRTOINT64(tp->t_session);
|
|
|
|
} else {
|
|
|
|
ki->p_tdev = NODEV;
|
|
|
|
ki->p_tpgid = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fixups that can only be done in the kernel */
|
|
|
|
if ((pr->ps_flags & PS_ZOMBIE) == 0) {
|
|
|
|
if ((pr->ps_flags & PS_EMBRYO) == 0 && vm != NULL)
|
|
|
|
ki->p_vm_rssize = vm_resident_count(vm);
|
2024-07-08 18:11:41 +00:00
|
|
|
calctsru(&tu, &ut, &st, NULL);
|
2023-04-30 01:15:27 +00:00
|
|
|
ki->p_uutime_sec = ut.tv_sec;
|
|
|
|
ki->p_uutime_usec = ut.tv_nsec/1000;
|
|
|
|
ki->p_ustime_sec = st.tv_sec;
|
|
|
|
ki->p_ustime_usec = st.tv_nsec/1000;
|
|
|
|
|
|
|
|
/* Convert starting uptime to a starting UTC time. */
|
|
|
|
nanoboottime(&booted);
|
|
|
|
timespecadd(&booted, &pr->ps_start, &utc);
|
|
|
|
ki->p_ustart_sec = utc.tv_sec;
|
|
|
|
ki->p_ustart_usec = utc.tv_nsec / 1000;
|
|
|
|
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
|
|
if (p->p_cpu != NULL)
|
|
|
|
ki->p_cpuid = CPU_INFO_UNIT(p->p_cpu);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get %cpu and schedule state: just one thread or sum of all? */
|
|
|
|
if (isthread) {
|
|
|
|
ki->p_pctcpu = p->p_pctcpu;
|
|
|
|
ki->p_stat = p->p_stat;
|
|
|
|
} else {
|
|
|
|
ki->p_pctcpu = 0;
|
|
|
|
ki->p_stat = (pr->ps_flags & PS_ZOMBIE) ? SDEAD : SIDL;
|
|
|
|
TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link) {
|
|
|
|
ki->p_pctcpu += p->p_pctcpu;
|
|
|
|
/* find best state: ONPROC > RUN > STOP > SLEEP > .. */
|
|
|
|
if (p->p_stat == SONPROC || ki->p_stat == SONPROC)
|
|
|
|
ki->p_stat = SONPROC;
|
|
|
|
else if (p->p_stat == SRUN || ki->p_stat == SRUN)
|
|
|
|
ki->p_stat = SRUN;
|
|
|
|
else if (p->p_stat == SSTOP || ki->p_stat == SSTOP)
|
|
|
|
ki->p_stat = SSTOP;
|
|
|
|
else if (p->p_stat == SSLEEP)
|
|
|
|
ki->p_stat = SSLEEP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sysctl_proc_args(int *name, u_int namelen, void *oldp, size_t *oldlenp,
|
|
|
|
struct proc *cp)
|
|
|
|
{
|
|
|
|
struct process *vpr;
|
|
|
|
pid_t pid;
|
|
|
|
struct ps_strings pss;
|
|
|
|
struct iovec iov;
|
|
|
|
struct uio uio;
|
|
|
|
int error, cnt, op;
|
|
|
|
size_t limit;
|
|
|
|
char **rargv, **vargv; /* reader vs. victim */
|
|
|
|
char *rarg, *varg, *buf;
|
|
|
|
struct vmspace *vm;
|
|
|
|
vaddr_t ps_strings;
|
|
|
|
|
|
|
|
if (namelen > 2)
|
|
|
|
return (ENOTDIR);
|
|
|
|
if (namelen < 2)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
pid = name[0];
|
|
|
|
op = name[1];
|
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case KERN_PROC_ARGV:
|
|
|
|
case KERN_PROC_NARGV:
|
|
|
|
case KERN_PROC_ENV:
|
|
|
|
case KERN_PROC_NENV:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((vpr = prfind(pid)) == NULL)
|
|
|
|
return (ESRCH);
|
|
|
|
|
|
|
|
if (oldp == NULL) {
|
|
|
|
if (op == KERN_PROC_NARGV || op == KERN_PROC_NENV)
|
|
|
|
*oldlenp = sizeof(int);
|
|
|
|
else
|
|
|
|
*oldlenp = ARG_MAX; /* XXX XXX XXX */
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Either system process or exiting/zombie */
|
|
|
|
if (vpr->ps_flags & (PS_SYSTEM | PS_EXITING))
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
/* Execing - danger. */
|
|
|
|
if ((vpr->ps_flags & PS_INEXEC))
|
|
|
|
return (EBUSY);
|
|
|
|
|
|
|
|
/* Only owner or root can get env */
|
|
|
|
if ((op == KERN_PROC_NENV || op == KERN_PROC_ENV) &&
|
|
|
|
(vpr->ps_ucred->cr_uid != cp->p_ucred->cr_uid &&
|
|
|
|
(error = suser(cp)) != 0))
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
ps_strings = vpr->ps_strings;
|
|
|
|
vm = vpr->ps_vmspace;
|
|
|
|
uvmspace_addref(vm);
|
|
|
|
vpr = NULL;
|
|
|
|
|
|
|
|
buf = malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
|
|
|
|
|
|
|
|
iov.iov_base = &pss;
|
|
|
|
iov.iov_len = sizeof(pss);
|
|
|
|
uio.uio_iov = &iov;
|
|
|
|
uio.uio_iovcnt = 1;
|
|
|
|
uio.uio_offset = (off_t)ps_strings;
|
|
|
|
uio.uio_resid = sizeof(pss);
|
|
|
|
uio.uio_segflg = UIO_SYSSPACE;
|
|
|
|
uio.uio_rw = UIO_READ;
|
|
|
|
uio.uio_procp = cp;
|
|
|
|
|
|
|
|
if ((error = uvm_io(&vm->vm_map, &uio, 0)) != 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (op == KERN_PROC_NARGV) {
|
|
|
|
error = sysctl_rdint(oldp, oldlenp, NULL, pss.ps_nargvstr);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (op == KERN_PROC_NENV) {
|
|
|
|
error = sysctl_rdint(oldp, oldlenp, NULL, pss.ps_nenvstr);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op == KERN_PROC_ARGV) {
|
|
|
|
cnt = pss.ps_nargvstr;
|
|
|
|
vargv = pss.ps_argvstr;
|
|
|
|
} else {
|
|
|
|
cnt = pss.ps_nenvstr;
|
|
|
|
vargv = pss.ps_envstr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -1 to have space for a terminating NUL */
|
|
|
|
limit = *oldlenp - 1;
|
|
|
|
*oldlenp = 0;
|
|
|
|
|
|
|
|
rargv = oldp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* *oldlenp - number of bytes copied out into readers buffer.
|
|
|
|
* limit - maximal number of bytes allowed into readers buffer.
|
|
|
|
* rarg - pointer into readers buffer where next arg will be stored.
|
|
|
|
* rargv - pointer into readers buffer where the next rarg pointer
|
|
|
|
* will be stored.
|
|
|
|
* vargv - pointer into victim address space where the next argument
|
|
|
|
* will be read.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* space for cnt pointers and a NULL */
|
|
|
|
rarg = (char *)(rargv + cnt + 1);
|
|
|
|
*oldlenp += (cnt + 1) * sizeof(char **);
|
|
|
|
|
|
|
|
while (cnt > 0 && *oldlenp < limit) {
|
|
|
|
size_t len, vstrlen;
|
|
|
|
|
|
|
|
/* Write to readers argv */
|
|
|
|
if ((error = copyout(&rarg, rargv, sizeof(rarg))) != 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* read the victim argv */
|
|
|
|
iov.iov_base = &varg;
|
|
|
|
iov.iov_len = sizeof(varg);
|
|
|
|
uio.uio_iov = &iov;
|
|
|
|
uio.uio_iovcnt = 1;
|
|
|
|
uio.uio_offset = (off_t)(vaddr_t)vargv;
|
|
|
|
uio.uio_resid = sizeof(varg);
|
|
|
|
uio.uio_segflg = UIO_SYSSPACE;
|
|
|
|
uio.uio_rw = UIO_READ;
|
|
|
|
uio.uio_procp = cp;
|
|
|
|
if ((error = uvm_io(&vm->vm_map, &uio, 0)) != 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (varg == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* read the victim arg. We must jump through hoops to avoid
|
|
|
|
* crossing a page boundary too much and returning an error.
|
|
|
|
*/
|
|
|
|
more:
|
|
|
|
len = PAGE_SIZE - (((vaddr_t)varg) & PAGE_MASK);
|
|
|
|
/* leave space for the terminating NUL */
|
|
|
|
iov.iov_base = buf;
|
|
|
|
iov.iov_len = len;
|
|
|
|
uio.uio_iov = &iov;
|
|
|
|
uio.uio_iovcnt = 1;
|
|
|
|
uio.uio_offset = (off_t)(vaddr_t)varg;
|
|
|
|
uio.uio_resid = len;
|
|
|
|
uio.uio_segflg = UIO_SYSSPACE;
|
|
|
|
uio.uio_rw = UIO_READ;
|
|
|
|
uio.uio_procp = cp;
|
|
|
|
if ((error = uvm_io(&vm->vm_map, &uio, 0)) != 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
for (vstrlen = 0; vstrlen < len; vstrlen++) {
|
|
|
|
if (buf[vstrlen] == '\0')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't overflow readers buffer. */
|
|
|
|
if (*oldlenp + vstrlen + 1 >= limit) {
|
|
|
|
error = ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = copyout(buf, rarg, vstrlen)) != 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
*oldlenp += vstrlen;
|
|
|
|
rarg += vstrlen;
|
|
|
|
|
|
|
|
/* The string didn't end in this page? */
|
|
|
|
if (vstrlen == len) {
|
|
|
|
varg += vstrlen;
|
|
|
|
goto more;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* End of string. Terminate it with a NUL */
|
|
|
|
buf[0] = '\0';
|
|
|
|
if ((error = copyout(buf, rarg, 1)) != 0)
|
|
|
|
goto out;
|
|
|
|
*oldlenp += 1;
|
|
|
|
rarg += 1;
|
|
|
|
|
|
|
|
vargv++;
|
|
|
|
rargv++;
|
|
|
|
cnt--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*oldlenp >= limit) {
|
|
|
|
error = ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write the terminating null */
|
|
|
|
rarg = NULL;
|
|
|
|
error = copyout(&rarg, rargv, sizeof(rarg));
|
|
|
|
|
|
|
|
out:
|
|
|
|
uvmspace_free(vm);
|
|
|
|
free(buf, M_TEMP, PAGE_SIZE);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sysctl_proc_cwd(int *name, u_int namelen, void *oldp, size_t *oldlenp,
|
|
|
|
struct proc *cp)
|
|
|
|
{
|
|
|
|
struct process *findpr;
|
|
|
|
struct vnode *vp;
|
|
|
|
pid_t pid;
|
|
|
|
int error;
|
|
|
|
size_t lenused, len;
|
|
|
|
char *path, *bp, *bend;
|
|
|
|
|
|
|
|
if (namelen > 1)
|
|
|
|
return (ENOTDIR);
|
|
|
|
if (namelen < 1)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
pid = name[0];
|
|
|
|
if ((findpr = prfind(pid)) == NULL)
|
|
|
|
return (ESRCH);
|
|
|
|
|
|
|
|
if (oldp == NULL) {
|
|
|
|
*oldlenp = MAXPATHLEN * 4;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Either system process or exiting/zombie */
|
|
|
|
if (findpr->ps_flags & (PS_SYSTEM | PS_EXITING))
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
/* Only owner or root can get cwd */
|
|
|
|
if (findpr->ps_ucred->cr_uid != cp->p_ucred->cr_uid &&
|
|
|
|
(error = suser(cp)) != 0)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
len = *oldlenp;
|
|
|
|
if (len > MAXPATHLEN * 4)
|
|
|
|
len = MAXPATHLEN * 4;
|
|
|
|
else if (len < 2)
|
|
|
|
return (ERANGE);
|
|
|
|
*oldlenp = 0;
|
|
|
|
|
|
|
|
/* snag a reference to the vnode before we can sleep */
|
|
|
|
vp = findpr->ps_fd->fd_cdir;
|
|
|
|
vref(vp);
|
|
|
|
|
|
|
|
path = malloc(len, M_TEMP, M_WAITOK);
|
|
|
|
|
|
|
|
bp = &path[len];
|
|
|
|
bend = bp;
|
|
|
|
*(--bp) = '\0';
|
|
|
|
|
|
|
|
/* Same as sys__getcwd */
|
|
|
|
error = vfs_getcwd_common(vp, NULL,
|
|
|
|
&bp, path, len / 2, GETCWD_CHECK_ACCESS, cp);
|
|
|
|
if (error == 0) {
|
|
|
|
*oldlenp = lenused = bend - bp;
|
|
|
|
error = copyout(bp, oldp, lenused);
|
|
|
|
}
|
|
|
|
|
|
|
|
vrele(vp);
|
|
|
|
free(path, M_TEMP, len);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sysctl_proc_nobroadcastkill(int *name, u_int namelen, void *newp, size_t newlen,
|
|
|
|
void *oldp, size_t *oldlenp, struct proc *cp)
|
|
|
|
{
|
|
|
|
struct process *findpr;
|
|
|
|
pid_t pid;
|
|
|
|
int error, flag;
|
|
|
|
|
|
|
|
if (namelen > 1)
|
|
|
|
return (ENOTDIR);
|
|
|
|
if (namelen < 1)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
pid = name[0];
|
|
|
|
if ((findpr = prfind(pid)) == NULL)
|
|
|
|
return (ESRCH);
|
|
|
|
|
|
|
|
/* Either system process or exiting/zombie */
|
|
|
|
if (findpr->ps_flags & (PS_SYSTEM | PS_EXITING))
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
/* Only root can change PS_NOBROADCASTKILL */
|
|
|
|
if (newp != NULL && (error = suser(cp)) != 0)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
/* get the PS_NOBROADCASTKILL flag */
|
|
|
|
flag = findpr->ps_flags & PS_NOBROADCASTKILL ? 1 : 0;
|
|
|
|
|
|
|
|
error = sysctl_int(oldp, oldlenp, newp, newlen, &flag);
|
|
|
|
if (error == 0 && newp) {
|
|
|
|
if (flag)
|
|
|
|
atomic_setbits_int(&findpr->ps_flags,
|
|
|
|
PS_NOBROADCASTKILL);
|
|
|
|
else
|
|
|
|
atomic_clearbits_int(&findpr->ps_flags,
|
|
|
|
PS_NOBROADCASTKILL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Arbitrary but reasonable limit for one iteration. */
|
|
|
|
#define VMMAP_MAXLEN MAXPHYS
|
|
|
|
|
|
|
|
int
|
|
|
|
sysctl_proc_vmmap(int *name, u_int namelen, void *oldp, size_t *oldlenp,
|
|
|
|
struct proc *cp)
|
|
|
|
{
|
|
|
|
struct process *findpr;
|
|
|
|
pid_t pid;
|
|
|
|
int error;
|
|
|
|
size_t oldlen, len;
|
|
|
|
struct kinfo_vmentry *kve, *ukve;
|
|
|
|
u_long *ustart, start;
|
|
|
|
|
|
|
|
if (namelen > 1)
|
|
|
|
return (ENOTDIR);
|
|
|
|
if (namelen < 1)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
/* Provide max buffer length as hint. */
|
|
|
|
if (oldp == NULL) {
|
|
|
|
if (oldlenp == NULL)
|
|
|
|
return (EINVAL);
|
|
|
|
else {
|
|
|
|
*oldlenp = VMMAP_MAXLEN;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pid = name[0];
|
|
|
|
if (pid == cp->p_p->ps_pid) {
|
|
|
|
/* Self process mapping. */
|
|
|
|
findpr = cp->p_p;
|
|
|
|
} else if (pid > 0) {
|
|
|
|
if ((findpr = prfind(pid)) == NULL)
|
|
|
|
return (ESRCH);
|
|
|
|
|
|
|
|
/* Either system process or exiting/zombie */
|
|
|
|
if (findpr->ps_flags & (PS_SYSTEM | PS_EXITING))
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
/* XXX Allow only root for now */
|
|
|
|
if ((error = suser(cp)) != 0)
|
|
|
|
return (error);
|
|
|
|
#else
|
|
|
|
/* Only owner or root can get vmmap */
|
|
|
|
if (findpr->ps_ucred->cr_uid != cp->p_ucred->cr_uid &&
|
|
|
|
(error = suser(cp)) != 0)
|
|
|
|
return (error);
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
/* Only root can get kernel_map */
|
|
|
|
if ((error = suser(cp)) != 0)
|
|
|
|
return (error);
|
|
|
|
findpr = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check the given size. */
|
|
|
|
oldlen = *oldlenp;
|
|
|
|
if (oldlen == 0 || oldlen % sizeof(*kve) != 0)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
/* Deny huge allocation. */
|
|
|
|
if (oldlen > VMMAP_MAXLEN)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Iterate from the given address passed as the first element's
|
|
|
|
* kve_start via oldp.
|
|
|
|
*/
|
|
|
|
ukve = (struct kinfo_vmentry *)oldp;
|
|
|
|
ustart = &ukve->kve_start;
|
|
|
|
error = copyin(ustart, &start, sizeof(start));
|
|
|
|
if (error != 0)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
/* Allocate wired memory to not block. */
|
|
|
|
kve = malloc(oldlen, M_TEMP, M_WAITOK);
|
|
|
|
|
|
|
|
/* Set the base address and read entries. */
|
|
|
|
kve[0].kve_start = start;
|
|
|
|
len = oldlen;
|
|
|
|
error = fill_vmmap(findpr, kve, &len);
|
|
|
|
if (error != 0 && error != ENOMEM)
|
|
|
|
goto done;
|
|
|
|
if (len == 0)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
KASSERT(len <= oldlen);
|
|
|
|
KASSERT((len % sizeof(struct kinfo_vmentry)) == 0);
|
|
|
|
|
|
|
|
error = copyout(kve, oldp, len);
|
|
|
|
|
|
|
|
done:
|
|
|
|
*oldlenp = len;
|
|
|
|
|
|
|
|
free(kve, M_TEMP, oldlen);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize disknames/diskstats for export by sysctl. If update is set,
|
|
|
|
* then we simply update the disk statistics information.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sysctl_diskinit(int update, struct proc *p)
|
|
|
|
{
|
|
|
|
struct diskstats *sdk;
|
|
|
|
struct disk *dk;
|
|
|
|
const char *duid;
|
|
|
|
int error, changed = 0;
|
|
|
|
|
|
|
|
KERNEL_ASSERT_LOCKED();
|
|
|
|
|
|
|
|
if ((error = rw_enter(&sysctl_disklock, RW_WRITE|RW_INTR)) != 0)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
/* Run in a loop, disks may change while malloc sleeps. */
|
|
|
|
while (disk_change) {
|
|
|
|
int tlen;
|
|
|
|
|
|
|
|
disk_change = 0;
|
|
|
|
|
|
|
|
tlen = 0;
|
|
|
|
TAILQ_FOREACH(dk, &disklist, dk_link) {
|
|
|
|
if (dk->dk_name)
|
|
|
|
tlen += strlen(dk->dk_name);
|
|
|
|
tlen += 18; /* label uid + separators */
|
|
|
|
}
|
|
|
|
tlen++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The sysctl_disklock ensures that no other process can
|
|
|
|
* allocate disknames and diskstats while our malloc sleeps.
|
|
|
|
*/
|
|
|
|
free(disknames, M_SYSCTL, disknameslen);
|
|
|
|
free(diskstats, M_SYSCTL, diskstatslen);
|
|
|
|
diskstats = NULL;
|
|
|
|
disknames = NULL;
|
|
|
|
diskstats = mallocarray(disk_count, sizeof(struct diskstats),
|
|
|
|
M_SYSCTL, M_WAITOK|M_ZERO);
|
|
|
|
diskstatslen = disk_count * sizeof(struct diskstats);
|
|
|
|
disknames = malloc(tlen, M_SYSCTL, M_WAITOK|M_ZERO);
|
|
|
|
disknameslen = tlen;
|
|
|
|
disknames[0] = '\0';
|
|
|
|
changed = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (changed) {
|
|
|
|
int l;
|
|
|
|
|
|
|
|
l = 0;
|
|
|
|
sdk = diskstats;
|
|
|
|
TAILQ_FOREACH(dk, &disklist, dk_link) {
|
|
|
|
duid = NULL;
|
|
|
|
if (dk->dk_label && !duid_iszero(dk->dk_label->d_uid))
|
|
|
|
duid = duid_format(dk->dk_label->d_uid);
|
|
|
|
snprintf(disknames + l, disknameslen - l, "%s:%s,",
|
|
|
|
dk->dk_name ? dk->dk_name : "",
|
|
|
|
duid ? duid : "");
|
|
|
|
l += strlen(disknames + l);
|
|
|
|
strlcpy(sdk->ds_name, dk->dk_name,
|
|
|
|
sizeof(sdk->ds_name));
|
|
|
|
mtx_enter(&dk->dk_mtx);
|
|
|
|
sdk->ds_busy = dk->dk_busy;
|
|
|
|
sdk->ds_rxfer = dk->dk_rxfer;
|
|
|
|
sdk->ds_wxfer = dk->dk_wxfer;
|
|
|
|
sdk->ds_seek = dk->dk_seek;
|
|
|
|
sdk->ds_rbytes = dk->dk_rbytes;
|
|
|
|
sdk->ds_wbytes = dk->dk_wbytes;
|
|
|
|
sdk->ds_attachtime = dk->dk_attachtime;
|
|
|
|
sdk->ds_timestamp = dk->dk_timestamp;
|
|
|
|
sdk->ds_time = dk->dk_time;
|
|
|
|
mtx_leave(&dk->dk_mtx);
|
|
|
|
sdk++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Eliminate trailing comma */
|
|
|
|
if (l != 0)
|
|
|
|
disknames[l - 1] = '\0';
|
|
|
|
} else if (update) {
|
|
|
|
/* Just update, number of drives hasn't changed */
|
|
|
|
sdk = diskstats;
|
|
|
|
TAILQ_FOREACH(dk, &disklist, dk_link) {
|
|
|
|
strlcpy(sdk->ds_name, dk->dk_name,
|
|
|
|
sizeof(sdk->ds_name));
|
|
|
|
mtx_enter(&dk->dk_mtx);
|
|
|
|
sdk->ds_busy = dk->dk_busy;
|
|
|
|
sdk->ds_rxfer = dk->dk_rxfer;
|
|
|
|
sdk->ds_wxfer = dk->dk_wxfer;
|
|
|
|
sdk->ds_seek = dk->dk_seek;
|
|
|
|
sdk->ds_rbytes = dk->dk_rbytes;
|
|
|
|
sdk->ds_wbytes = dk->dk_wbytes;
|
|
|
|
sdk->ds_attachtime = dk->dk_attachtime;
|
|
|
|
sdk->ds_timestamp = dk->dk_timestamp;
|
|
|
|
sdk->ds_time = dk->dk_time;
|
|
|
|
mtx_leave(&dk->dk_mtx);
|
|
|
|
sdk++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rw_exit_write(&sysctl_disklock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(SYSVMSG) || defined(SYSVSEM) || defined(SYSVSHM)
|
|
|
|
int
|
|
|
|
sysctl_sysvipc(int *name, u_int namelen, void *where, size_t *sizep)
|
|
|
|
{
|
|
|
|
#ifdef SYSVSEM
|
|
|
|
struct sem_sysctl_info *semsi;
|
|
|
|
#endif
|
|
|
|
#ifdef SYSVSHM
|
|
|
|
struct shm_sysctl_info *shmsi;
|
|
|
|
#endif
|
|
|
|
size_t infosize, dssize, tsize, buflen, bufsiz;
|
|
|
|
int i, nds, error, ret;
|
|
|
|
void *buf;
|
|
|
|
|
|
|
|
if (namelen != 1)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
buflen = *sizep;
|
|
|
|
|
|
|
|
switch (*name) {
|
|
|
|
case KERN_SYSVIPC_MSG_INFO:
|
|
|
|
#ifdef SYSVMSG
|
|
|
|
return (sysctl_sysvmsg(name, namelen, where, sizep));
|
|
|
|
#else
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
#endif
|
|
|
|
case KERN_SYSVIPC_SEM_INFO:
|
|
|
|
#ifdef SYSVSEM
|
|
|
|
infosize = sizeof(semsi->seminfo);
|
|
|
|
nds = seminfo.semmni;
|
|
|
|
dssize = sizeof(semsi->semids[0]);
|
|
|
|
break;
|
|
|
|
#else
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
#endif
|
|
|
|
case KERN_SYSVIPC_SHM_INFO:
|
|
|
|
#ifdef SYSVSHM
|
|
|
|
infosize = sizeof(shmsi->shminfo);
|
|
|
|
nds = shminfo.shmmni;
|
|
|
|
dssize = sizeof(shmsi->shmids[0]);
|
|
|
|
break;
|
|
|
|
#else
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
tsize = infosize + (nds * dssize);
|
|
|
|
|
|
|
|
/* Return just the total size required. */
|
|
|
|
if (where == NULL) {
|
|
|
|
*sizep = tsize;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Not enough room for even the info struct. */
|
|
|
|
if (buflen < infosize) {
|
|
|
|
*sizep = 0;
|
|
|
|
return (ENOMEM);
|
|
|
|
}
|
|
|
|
bufsiz = min(tsize, buflen);
|
|
|
|
buf = malloc(bufsiz, M_TEMP, M_WAITOK|M_ZERO);
|
|
|
|
|
|
|
|
switch (*name) {
|
|
|
|
#ifdef SYSVSEM
|
|
|
|
case KERN_SYSVIPC_SEM_INFO:
|
|
|
|
semsi = (struct sem_sysctl_info *)buf;
|
|
|
|
semsi->seminfo = seminfo;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef SYSVSHM
|
|
|
|
case KERN_SYSVIPC_SHM_INFO:
|
|
|
|
shmsi = (struct shm_sysctl_info *)buf;
|
|
|
|
shmsi->shminfo = shminfo;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
buflen -= infosize;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
if (buflen > 0) {
|
|
|
|
/* Fill in the IPC data structures. */
|
|
|
|
for (i = 0; i < nds; i++) {
|
|
|
|
if (buflen < dssize) {
|
|
|
|
ret = ENOMEM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
switch (*name) {
|
|
|
|
#ifdef SYSVSEM
|
|
|
|
case KERN_SYSVIPC_SEM_INFO:
|
|
|
|
if (sema[i] != NULL)
|
|
|
|
memcpy(&semsi->semids[i], sema[i],
|
|
|
|
dssize);
|
|
|
|
else
|
|
|
|
memset(&semsi->semids[i], 0, dssize);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef SYSVSHM
|
|
|
|
case KERN_SYSVIPC_SHM_INFO:
|
|
|
|
if (shmsegs[i] != NULL)
|
|
|
|
memcpy(&shmsi->shmids[i], shmsegs[i],
|
|
|
|
dssize);
|
|
|
|
else
|
|
|
|
memset(&shmsi->shmids[i], 0, dssize);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
buflen -= dssize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*sizep -= buflen;
|
|
|
|
error = copyout(buf, where, *sizep);
|
|
|
|
free(buf, M_TEMP, bufsiz);
|
|
|
|
/* If copyout succeeded, use return code set earlier. */
|
|
|
|
return (error ? error : ret);
|
|
|
|
}
|
|
|
|
#endif /* SYSVMSG || SYSVSEM || SYSVSHM */
|
|
|
|
|
|
|
|
#ifndef SMALL_KERNEL
|
|
|
|
|
|
|
|
int
|
|
|
|
sysctl_intrcnt(int *name, u_int namelen, void *oldp, size_t *oldlenp)
|
|
|
|
{
|
|
|
|
return (evcount_sysctl(name, namelen, oldp, oldlenp, NULL, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
sysctl_sensors(int *name, u_int namelen, void *oldp, size_t *oldlenp,
|
|
|
|
void *newp, size_t newlen)
|
|
|
|
{
|
|
|
|
struct ksensor *ks;
|
|
|
|
struct sensor *us;
|
|
|
|
struct ksensordev *ksd;
|
|
|
|
struct sensordev *usd;
|
|
|
|
int dev, numt, ret;
|
|
|
|
enum sensor_type type;
|
|
|
|
|
|
|
|
if (namelen != 1 && namelen != 3)
|
|
|
|
return (ENOTDIR);
|
|
|
|
|
|
|
|
dev = name[0];
|
|
|
|
if (namelen == 1) {
|
|
|
|
ret = sensordev_get(dev, &ksd);
|
|
|
|
if (ret)
|
|
|
|
return (ret);
|
|
|
|
|
|
|
|
/* Grab a copy, to clear the kernel pointers */
|
|
|
|
usd = malloc(sizeof(*usd), M_TEMP, M_WAITOK|M_ZERO);
|
|
|
|
usd->num = ksd->num;
|
|
|
|
strlcpy(usd->xname, ksd->xname, sizeof(usd->xname));
|
|
|
|
memcpy(usd->maxnumt, ksd->maxnumt, sizeof(usd->maxnumt));
|
|
|
|
usd->sensors_count = ksd->sensors_count;
|
|
|
|
|
|
|
|
ret = sysctl_rdstruct(oldp, oldlenp, newp, usd,
|
|
|
|
sizeof(struct sensordev));
|
|
|
|
|
|
|
|
free(usd, M_TEMP, sizeof(*usd));
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
type = name[1];
|
|
|
|
numt = name[2];
|
|
|
|
|
|
|
|
ret = sensor_find(dev, type, numt, &ks);
|
|
|
|
if (ret)
|
|
|
|
return (ret);
|
|
|
|
|
|
|
|
/* Grab a copy, to clear the kernel pointers */
|
|
|
|
us = malloc(sizeof(*us), M_TEMP, M_WAITOK|M_ZERO);
|
|
|
|
memcpy(us->desc, ks->desc, sizeof(us->desc));
|
|
|
|
us->tv = ks->tv;
|
|
|
|
us->value = ks->value;
|
|
|
|
us->type = ks->type;
|
|
|
|
us->status = ks->status;
|
|
|
|
us->numt = ks->numt;
|
|
|
|
us->flags = ks->flags;
|
|
|
|
|
|
|
|
ret = sysctl_rdstruct(oldp, oldlenp, newp, us,
|
|
|
|
sizeof(struct sensor));
|
|
|
|
free(us, M_TEMP, sizeof(*us));
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
#endif /* SMALL_KERNEL */
|
|
|
|
|
|
|
|
int
|
|
|
|
sysctl_cptime2(int *name, u_int namelen, void *oldp, size_t *oldlenp,
|
|
|
|
void *newp, size_t newlen)
|
|
|
|
{
|
|
|
|
CPU_INFO_ITERATOR cii;
|
|
|
|
struct cpu_info *ci;
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
if (namelen != 1)
|
|
|
|
return (ENOTDIR);
|
|
|
|
|
|
|
|
CPU_INFO_FOREACH(cii, ci) {
|
|
|
|
if (name[0] == CPU_INFO_UNIT(ci)) {
|
|
|
|
found = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found)
|
|
|
|
return (ENOENT);
|
|
|
|
|
|
|
|
return (sysctl_rdstruct(oldp, oldlenp, newp,
|
|
|
|
&ci->ci_schedstate.spc_cp_time,
|
|
|
|
sizeof(ci->ci_schedstate.spc_cp_time)));
|
|
|
|
}
|
|
|
|
|
|
|
|
#if NAUDIO > 0
|
|
|
|
int
|
|
|
|
sysctl_audio(int *name, u_int namelen, void *oldp, size_t *oldlenp,
|
|
|
|
void *newp, size_t newlen)
|
|
|
|
{
|
|
|
|
if (namelen != 1)
|
|
|
|
return (ENOTDIR);
|
|
|
|
|
|
|
|
if (name[0] != KERN_AUDIO_RECORD)
|
|
|
|
return (ENOENT);
|
|
|
|
|
|
|
|
return (sysctl_int(oldp, oldlenp, newp, newlen, &audio_record_enable));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if NVIDEO > 0
|
|
|
|
int
|
|
|
|
sysctl_video(int *name, u_int namelen, void *oldp, size_t *oldlenp,
|
|
|
|
void *newp, size_t newlen)
|
|
|
|
{
|
|
|
|
if (namelen != 1)
|
|
|
|
return (ENOTDIR);
|
|
|
|
|
|
|
|
if (name[0] != KERN_VIDEO_RECORD)
|
|
|
|
return (ENOENT);
|
|
|
|
|
|
|
|
return (sysctl_int(oldp, oldlenp, newp, newlen, &video_record_enable));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int
|
|
|
|
sysctl_cpustats(int *name, u_int namelen, void *oldp, size_t *oldlenp,
|
|
|
|
void *newp, size_t newlen)
|
|
|
|
{
|
|
|
|
CPU_INFO_ITERATOR cii;
|
|
|
|
struct cpustats cs;
|
|
|
|
struct cpu_info *ci;
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
if (namelen != 1)
|
|
|
|
return (ENOTDIR);
|
|
|
|
|
|
|
|
CPU_INFO_FOREACH(cii, ci) {
|
|
|
|
if (name[0] == CPU_INFO_UNIT(ci)) {
|
|
|
|
found = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found)
|
|
|
|
return (ENOENT);
|
|
|
|
|
|
|
|
memset(&cs, 0, sizeof cs);
|
|
|
|
memcpy(&cs.cs_time, &ci->ci_schedstate.spc_cp_time, sizeof(cs.cs_time));
|
|
|
|
cs.cs_flags = 0;
|
|
|
|
if (cpu_is_online(ci))
|
|
|
|
cs.cs_flags |= CPUSTATS_ONLINE;
|
|
|
|
|
|
|
|
return (sysctl_rdstruct(oldp, oldlenp, newp, &cs, sizeof(cs)));
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sysctl_utc_offset(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
|
|
|
|
{
|
|
|
|
struct timespec adjusted, now;
|
|
|
|
int adjustment_seconds, error, new_offset_minutes, old_offset_minutes;
|
|
|
|
|
|
|
|
old_offset_minutes = utc_offset / 60; /* seconds -> minutes */
|
|
|
|
new_offset_minutes = old_offset_minutes;
|
|
|
|
error = sysctl_securelevel_int(oldp, oldlenp, newp, newlen,
|
|
|
|
&new_offset_minutes);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
if (new_offset_minutes < -24 * 60 || new_offset_minutes > 24 * 60)
|
|
|
|
return EINVAL;
|
|
|
|
if (new_offset_minutes == old_offset_minutes)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
utc_offset = new_offset_minutes * 60; /* minutes -> seconds */
|
|
|
|
adjustment_seconds = (new_offset_minutes - old_offset_minutes) * 60;
|
|
|
|
|
|
|
|
nanotime(&now);
|
|
|
|
adjusted = now;
|
|
|
|
adjusted.tv_sec -= adjustment_seconds;
|
|
|
|
tc_setrealtimeclock(&adjusted);
|
|
|
|
resettodr();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|