diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc index 4cc87ae27860..ac5907b82540 100644 --- a/lib/libc/sys/Makefile.inc +++ b/lib/libc/sys/Makefile.inc @@ -197,6 +197,7 @@ MAN+= abort2.2 \ posix_fadvise.2 \ posix_fallocate.2 \ posix_openpt.2 \ + procctl.2 \ profil.2 \ pselect.2 \ ptrace.2 \ diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map index 222f5f0811f5..fe887c3ddd57 100644 --- a/lib/libc/sys/Symbol.map +++ b/lib/libc/sys/Symbol.map @@ -395,6 +395,7 @@ FBSD_1.3 { ffclock_setestimate; pipe2; posix_fadvise; + procctl; wait6; }; @@ -822,6 +823,8 @@ FBSDprivate_1.0 { __sys_poll; _preadv; __sys_preadv; + _procctl; + __sys_procctl; _profil; __sys_profil; _pselect; diff --git a/lib/libc/sys/procctl.2 b/lib/libc/sys/procctl.2 new file mode 100644 index 000000000000..ee49bc8e6874 --- /dev/null +++ b/lib/libc/sys/procctl.2 @@ -0,0 +1,142 @@ +.\" Copyright (c) 2013 Advanced Computing Technologies LLC +.\" Written by: John H. Baldwin +.\" All rights reserved. +.\" +.\" 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 AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 19, 2013 +.Dt PROCCTL 2 +.Os +.Sh NAME +.Nm procctl +.Nd control processes +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/procctl.h +.Ft int +.Fn procctl "idtype_t idtype" "id_t id" "int cmd" "void *arg" +.Sh DESCRIPTION +The +.Fn procctl +system call provides for control over processes. +The +.Fa idtype +and +.Fa id +arguments specify the set of processes to control. +If multiple processes match the identifier, +.Nm +will make a +.Dq best effort +to control as many of the selected possibles as possible. +An error is only returned if no selected processes successfully complete +the request. +The following identifier types are supported: +.Bl -tag -width Dv P_PGID +.It Dv P_PID +Control the process with the process ID +.Fa id . +.It Dv P_PGID +Control processes belonging to the process group with the ID +.Fa id . +.El +.Pp +The control request to perform is specified by the +.Fa cmd +argument. +The following commands are supported: +.Bl -tag -width Dv PROC_SPROTECT +.It Dv PROC_SPROTECT +Set process protection state. +This is used to mark a process as protected from being killed if the system +exhausts available memory and swap. +The +.Fa arg +parameter must point to an integer containing an operation and zero or more +optional flags. +The following operations are supported: +.Bl -tag -width Dv PPROT_CLEAR +.It Dv PPROT_SET +Mark the selected processes as protected. +.It Dv PPROT_CLEAR +Clear the protected state of selected processes. +.El +.Pp +The following optional flags are supported: +.Bl -tag -width Dv PPROT_DESCEND +.It Dv PPROT_DESCEND +Apply the requested operation to all child processes of each selected process +in addition to each selected process. +.It Dv PPROT_INHERIT +When used with +.Dv PPROT_SET , +mark all future child processes of each selected process as protected. +Future child processes will also mark all of their future child processes. +.El +.El +.Sh RETURN VALUES +If an error occurs, a value of -1 is returned and +.Va errno +is set to indicate the error. +.Sh ERRORS +The +.Fn procctl +system call +will fail if: +.Bl -tag -width Er +.It Bq Er EFAULT +The +.Fa arg +points outside the process's allocated address space. +.It Bq Er EINVAL +The +.Fa cmd +argument specifies an unsupported command. +.Pp +The +.Fa idtype +argument specifies an unsupported identifier type. +.It Bq Er EPERM +The calling process does not have permission to perform the requested +operation on any of the selected processes. +.It Bq Er ESRCH +No processes matched the requested +.Fa idtype +and +.Fa id . +.It Bq Er EINVAL +An invalid operation or flag was passed in +.Fa arg +for a +.Dv PROC_SPROTECT +command. +.El +.Sh SEE ALSO +.Xr ptrace 2 +.Sh HISTORY +The +.Fn procctl +function appeared in +.Fx 10 . diff --git a/sys/compat/freebsd32/freebsd32.h b/sys/compat/freebsd32/freebsd32.h index aa08432dc61c..e6644f604055 100644 --- a/sys/compat/freebsd32/freebsd32.h +++ b/sys/compat/freebsd32/freebsd32.h @@ -342,6 +342,7 @@ struct kinfo_proc32 { char ki_loginclass[LOGINCLASSLEN+1]; char ki_sparestrings[50]; int ki_spareints[KI_NSPARE_INT]; + int ki_flag2; int ki_fibnum; u_int ki_cr_flags; int ki_jid; diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index dbdafeb3f716..deb03f3ef586 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -56,6 +56,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -3000,3 +3001,23 @@ convert_sigevent32(struct sigevent32 *sig32, struct sigevent *sig) } return (0); } + +int +freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap) +{ + void *data; + int error, flags; + + switch (uap->com) { + case PROC_SPROTECT: + error = copyin(PTRIN(uap->data), &flags, sizeof(flags)); + if (error) + return (error); + data = &flags; + break; + default: + return (EINVAL); + } + return (kern_procctl(td, uap->idtype, PAIR32TO64(id_t, uap->id), + uap->com, data)); +} diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master index c52256a3288e..90c3e7553395 100644 --- a/sys/compat/freebsd32/syscalls.master +++ b/sys/compat/freebsd32/syscalls.master @@ -1056,3 +1056,12 @@ 542 AUE_PIPE NOPROTO { int pipe2(int *fildes, int flags); } 543 AUE_NULL NOSTD { int freebsd32_aio_mlock( \ struct aiocb32 *aiocbp); } +#ifdef PAD64_REQUIRED +544 AUE_NULL STD { int freebsd32_procctl(int idtype, int pad, \ + uint32_t id1, uint32_t id2, int com, \ + void *data); } +#else +544 AUE_NULL STD { int freebsd32_procctl(int idtype, \ + uint32_t id1, uint32_t id2, int com, \ + void *data); } +#endif diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index 0828e4803f98..ed343b60308e 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -474,6 +474,7 @@ proc0_init(void *dummy __unused) p->p_sysent = &null_sysvec; p->p_flag = P_SYSTEM | P_INMEM; + p->p_flag2 = 0; p->p_state = PRS_NORMAL; knlist_init_mtx(&p->p_klist, &p->p_mtx); STAILQ_INIT(&p->p_ktr); diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 9cd1da96b640..de7f09fd6ca6 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -489,6 +489,7 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2, * Increase reference counts on shared objects. */ p2->p_flag = P_INMEM; + p2->p_flag2 = 0; p2->p_swtick = ticks; if (p1->p_flag & P_PROFIL) startprofclock(p2); @@ -512,6 +513,11 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2, p2->p_fd = fd; p2->p_fdtol = fdtol; + if (p1->p_flag2 & P2_INHERIT_PROTECTED) { + p2->p_flag |= P_PROTECTED; + p2->p_flag2 |= P2_INHERIT_PROTECTED; + } + /* * p_limit is copy-on-write. Bump its refcount. */ diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index 3fa7a7f811d1..9968e7639019 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -802,6 +802,7 @@ fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp) kp->ki_fd = p->p_fd; kp->ki_vmspace = p->p_vmspace; kp->ki_flag = p->p_flag; + kp->ki_flag2 = p->p_flag2; cred = p->p_ucred; if (cred) { kp->ki_uid = cred->cr_uid; @@ -1161,6 +1162,7 @@ freebsd32_kinfo_proc_out(const struct kinfo_proc *ki, struct kinfo_proc32 *ki32) bcopy(ki->ki_comm, ki32->ki_comm, COMMLEN + 1); bcopy(ki->ki_emul, ki32->ki_emul, KI_EMULNAMELEN + 1); bcopy(ki->ki_loginclass, ki32->ki_loginclass, LOGINCLASSLEN + 1); + CP(*ki, *ki32, ki_flag2); CP(*ki, *ki32, ki_fibnum); CP(*ki, *ki32, ki_cr_flags); CP(*ki, *ki32, ki_jid); diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index 5508dcff15fb..821c779b7e38 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -41,7 +41,9 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include +#include #include #include #include @@ -1240,3 +1242,196 @@ stopevent(struct proc *p, unsigned int event, unsigned int val) msleep(&p->p_step, &p->p_mtx, PWAIT, "stopevent", 0); } while (p->p_step); } + +static int +protect_setchild(struct thread *td, struct proc *p, int flags) +{ + + PROC_LOCK_ASSERT(p, MA_OWNED); + if (p->p_flag & P_SYSTEM || p_cansee(td, p) != 0) + return (0); + if (flags & PPROT_SET) { + p->p_flag |= P_PROTECTED; + if (flags & PPROT_INHERIT) + p->p_flag2 |= P2_INHERIT_PROTECTED; + } else { + p->p_flag &= ~P_PROTECTED; + p->p_flag2 &= ~P2_INHERIT_PROTECTED; + } + return (1); +} + +static int +protect_setchildren(struct thread *td, struct proc *top, int flags) +{ + struct proc *p; + int ret; + + p = top; + ret = 0; + sx_assert(&proctree_lock, SX_LOCKED); + for (;;) { + ret |= protect_setchild(td, p, flags); + PROC_UNLOCK(p); + /* + * If this process has children, descend to them next, + * otherwise do any siblings, and if done with this level, + * follow back up the tree (but not past top). + */ + if (!LIST_EMPTY(&p->p_children)) + p = LIST_FIRST(&p->p_children); + else for (;;) { + if (p == top) { + PROC_LOCK(p); + return (ret); + } + if (LIST_NEXT(p, p_sibling)) { + p = LIST_NEXT(p, p_sibling); + break; + } + p = p->p_pptr; + } + PROC_LOCK(p); + } +} + +static int +protect_set(struct thread *td, struct proc *p, int flags) +{ + int error, ret; + + switch (PPROT_OP(flags)) { + case PPROT_SET: + case PPROT_CLEAR: + break; + default: + return (EINVAL); + } + + if ((PPROT_FLAGS(flags) & ~(PPROT_DESCEND | PPROT_INHERIT)) != 0) + return (EINVAL); + + error = priv_check(td, PRIV_VM_MADV_PROTECT); + if (error) + return (error); + + if (flags & PPROT_DESCEND) + ret = protect_setchildren(td, p, flags); + else + ret = protect_setchild(td, p, flags); + if (ret == 0) + return (EPERM); + return (0); +} + +#ifndef _SYS_SYSPROTO_H_ +struct procctl_args { + idtype_t idtype; + id_t id; + int com; + void *data; +}; +#endif +/* ARGSUSED */ +int +sys_procctl(struct thread *td, struct procctl_args *uap) +{ + int error, flags; + void *data; + + switch (uap->com) { + case PROC_SPROTECT: + error = copyin(uap->data, &flags, sizeof(flags)); + if (error) + return (error); + data = &flags; + break; + default: + return (EINVAL); + } + + return (kern_procctl(td, uap->idtype, uap->id, uap->com, data)); +} + +static int +kern_procctl_single(struct thread *td, struct proc *p, int com, void *data) +{ + + PROC_LOCK_ASSERT(p, MA_OWNED); + switch (com) { + case PROC_SPROTECT: + return (protect_set(td, p, *(int *)data)); + default: + return (EINVAL); + } +} + +int +kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data) +{ + struct pgrp *pg; + struct proc *p; + int error, first_error, ok; + + sx_slock(&proctree_lock); + switch (idtype) { + case P_PID: + p = pfind(id); + if (p == NULL) { + error = ESRCH; + break; + } + if (p->p_state == PRS_NEW) + error = ESRCH; + else + error = p_cansee(td, p); + if (error == 0) + error = kern_procctl_single(td, p, com, data); + PROC_UNLOCK(p); + break; + case P_PGID: + /* + * Attempt to apply the operation to all members of the + * group. Ignore processes in the group that can't be + * seen. Ignore errors so long as at least one process is + * able to complete the request successfully. + */ + pg = pgfind(id); + if (pg == NULL) { + error = ESRCH; + break; + } + PGRP_UNLOCK(pg); + ok = 0; + first_error = 0; + LIST_FOREACH(p, &pg->pg_members, p_pglist) { + PROC_LOCK(p); + if (p->p_state == PRS_NEW || p_cansee(td, p) != 0) { + PROC_UNLOCK(p); + continue; + } + error = kern_procctl_single(td, p, com, data); + PROC_UNLOCK(p); + if (error == 0) + ok = 1; + else if (first_error == 0) + first_error = error; + } + if (ok) + error = 0; + else if (first_error != 0) + error = first_error; + else + /* + * Was not able to see any processes in the + * process group. + */ + error = ESRCH; + break; + default: + error = EINVAL; + break; + } + sx_sunlock(&proctree_lock); + return (error); +} diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 7df50ca23322..e9592141c58c 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -978,5 +978,7 @@ int flags); } 542 AUE_PIPE STD { int pipe2(int *fildes, int flags); } 543 AUE_NULL NOSTD { int aio_mlock(struct aiocb *aiocbp); } +544 AUE_NULL STD { int procctl(idtype_t idtype, id_t id, \ + int com, void *data); } ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 2f03152de69f..5443b61773b8 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -492,11 +492,8 @@ struct proc { struct callout p_limco; /* (c) Limit callout handle */ struct sigacts *p_sigacts; /* (x) Signal actions, state (CPU). */ - /* - * The following don't make too much sense. - * See the td_ or ke_ versions of the same flags. - */ int p_flag; /* (c) P_* flags. */ + int p_flag2; /* (c) P2_* flags. */ enum { PRS_NEW = 0, /* In creation */ PRS_NORMAL, /* threads can be run. */ @@ -641,6 +638,9 @@ struct proc { #define P_SHOULDSTOP(p) ((p)->p_flag & P_STOPPED) #define P_KILLED(p) ((p)->p_flag & P_WKILLED) +/* These flags are kept in p_flag2. */ +#define P2_INHERIT_PROTECTED 0x00000001 /* New children get P_PROTECTED. */ + /* * These were process status values (p_stat), now they are only used in * legacy conversion code. diff --git a/sys/sys/procctl.h b/sys/sys/procctl.h new file mode 100644 index 000000000000..ff577c06be84 --- /dev/null +++ b/sys/sys/procctl.h @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 2013 Advanced Computing Technologies LLC + * Written by: John H. Baldwin + * All rights reserved. + * + * 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 AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _SYS_PROCCTL_H_ +#define _SYS_PROCCTL_H_ + +#define PROC_SPROTECT 1 /* set protected state */ + +/* Operations for PROC_SPROTECT (passed in integer arg). */ +#define PPROT_OP(x) ((x) & 0xf) +#define PPROT_SET 1 +#define PPROT_CLEAR 2 + +/* Flags for PROC_SPROTECT (ORed in with operation). */ +#define PPROT_FLAGS(x) ((x) & ~0xf) +#define PPROT_DESCEND 0x10 +#define PPROT_INHERIT 0x20 + +#ifndef _KERNEL +#include +#include + +__BEGIN_DECLS +int procctl(idtype_t, id_t, int, void *); +__END_DECLS + +#endif + +#endif /* !_SYS_PROCCTL_H_ */ diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h index 17f2b97db9ce..ed11b34ddab0 100644 --- a/sys/sys/syscallsubr.h +++ b/sys/sys/syscallsubr.h @@ -167,6 +167,8 @@ int kern_posix_fadvise(struct thread *td, int fd, off_t offset, off_t len, int advice); int kern_posix_fallocate(struct thread *td, int fd, off_t offset, off_t len); +int kern_procctl(struct thread *td, enum idtype idtype, id_t id, int com, + void *data); int kern_preadv(struct thread *td, int fd, struct uio *auio, off_t offset); int kern_pselect(struct thread *td, int nd, fd_set *in, fd_set *ou, fd_set *ex, struct timeval *tvp, sigset_t *uset, int abi_nfdbits); diff --git a/sys/sys/user.h b/sys/sys/user.h index 349003d73daf..d2e2b6e71284 100644 --- a/sys/sys/user.h +++ b/sys/sys/user.h @@ -84,7 +84,7 @@ * it in two places: function fill_kinfo_proc in sys/kern/kern_proc.c and * function kvm_proclist in lib/libkvm/kvm_proc.c . */ -#define KI_NSPARE_INT 8 +#define KI_NSPARE_INT 7 #define KI_NSPARE_LONG 12 #define KI_NSPARE_PTR 6 @@ -187,6 +187,7 @@ struct kinfo_proc { */ char ki_sparestrings[50]; /* spare string space */ int ki_spareints[KI_NSPARE_INT]; /* spare room for growth */ + int ki_flag2; /* P2_* flags */ int ki_fibnum; /* Default FIB number */ u_int ki_cr_flags; /* Credential flags */ int ki_jid; /* Process jail ID */ diff --git a/sys/vm/vm_mmap.c b/sys/vm/vm_mmap.c index 0f2d531941b7..17f8cad63579 100644 --- a/sys/vm/vm_mmap.c +++ b/sys/vm/vm_mmap.c @@ -56,6 +56,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -68,6 +69,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -739,23 +741,18 @@ sys_madvise(td, uap) { vm_offset_t start, end; vm_map_t map; - struct proc *p; - int error; + int flags; /* * Check for our special case, advising the swap pager we are * "immortal." */ if (uap->behav == MADV_PROTECT) { - error = priv_check(td, PRIV_VM_MADV_PROTECT); - if (error == 0) { - p = td->td_proc; - PROC_LOCK(p); - p->p_flag |= P_PROTECTED; - PROC_UNLOCK(p); - } - return (error); + flags = PPROT_SET; + return (kern_procctl(td, P_PID, td->td_proc->p_pid, + PROC_SPROTECT, &flags)); } + /* * Check for illegal behavior */ diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 8f4d920c1015..65187c50884b 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -132,6 +132,7 @@ SUBDIR= alias \ printenv \ printf \ procstat \ + protect \ rctl \ renice \ rev \ diff --git a/usr.bin/kdump/kdump.c b/usr.bin/kdump/kdump.c index c49b45b6cc17..e3e6927510ce 100644 --- a/usr.bin/kdump/kdump.c +++ b/usr.bin/kdump/kdump.c @@ -1161,6 +1161,18 @@ ktrsyscall(struct ktr_syscall *ktr, u_int flags) ip++; narg--; break; + case SYS_procctl: + putchar('('); + idtypename(*ip, decimal); + c = ','; + ip++; + narg--; + print_number(ip, narg, c); + putchar(','); + procctlcmdname(*ip); + ip++; + narg--; + break; } } while (narg > 0) { diff --git a/usr.bin/kdump/mksubr b/usr.bin/kdump/mksubr index 7fd42b7df2ed..1859086af1c8 100644 --- a/usr.bin/kdump/mksubr +++ b/usr.bin/kdump/mksubr @@ -169,6 +169,7 @@ cat <<_EOF_ #include #include #include +#include #include #include #include @@ -465,6 +466,7 @@ auto_or_type "mountflagsname" "MNT_[A-Z]+[[:space:]]+0x[0-9]+" auto_switch_type "msyncflagsname" "MS_[A-Z]+[[:space:]]+0x[0-9]+" "sys/mman.h" auto_or_type "nfssvcname" "NFSSVC_[A-Z0-9]+[[:space:]]+0x[0-9]+" "nfs/nfssvc.h" auto_switch_type "prioname" "PRIO_[A-Z]+[[:space:]]+[0-9]" "sys/resource.h" +auto_switch_type "procctlcmdname" "PROC_[A-Z]+[[:space:]]+[0-9]" "sys/procctl.h" auto_switch_type "ptraceopname" "PT_[[:alnum:]_]+[[:space:]]+[0-9]+" "sys/ptrace.h" auto_switch_type "quotactlname" "Q_[A-Z]+[[:space:]]+0x[0-9]+" "ufs/ufs/quota.h" auto_or_type "rebootoptname" "RB_[A-Z]+[[:space:]]+0x[0-9]+" "sys/reboot.h" diff --git a/usr.bin/protect/Makefile b/usr.bin/protect/Makefile new file mode 100644 index 000000000000..89bbda8a1bc2 --- /dev/null +++ b/usr.bin/protect/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +PROG= protect +WARNS?= 6 + +.include diff --git a/usr.bin/protect/protect.1 b/usr.bin/protect/protect.1 new file mode 100644 index 000000000000..5a494eef8ec4 --- /dev/null +++ b/usr.bin/protect/protect.1 @@ -0,0 +1,89 @@ +.\" Copyright (c) 2013 Advanced Computing Technologies LLC +.\" Written by: John H. Baldwin +.\" All rights reserved. +.\" +.\" 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 AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 19, 2013 +.Dt PROTECT 1 +.Os +.Sh NAME +.Nm protect +.Nd "protect processes from being killed when swap space is exhausted" +.Sh SYNOPSIS +.Nm +.Op Fl i +.Ar command +.Nm +.Op Fl cdi +.Fl g Ar pgrp | Fl p Ar pid +.Sh DESCRIPTION +The +.Nm +command is used to mark processes as protected. +The kernel does not kill protected processes when swap space is exhausted. +Note that this protected state is not inherited by child processes by default. +.Pp +The options are: +.Bl -tag -width indent +.It Fl c +Remove protection from the specified processes. +.It Fl d +Apply the operation to all current children of the specified processes. +.It Fl i +Apply the operation to all future children of the specified processes. +.It Fl g Ar pgrp +Apply the operation to all processes in the specified process group. +.It Fl p Ar pid +Apply the operation to the specified process. +.It Ar command +Execute +.Ar command +as a protected process. +.El +.Pp +Note that only one of the +.Fl p +or +.Fl g +flags may be specified when adjusting the state of existing processes. +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +Mark the Xorg server as protected: +.Pp +.Dl "pgrep Xorg | xargs protect -p" +Protect all ssh sessions and their child processes: +.Pp +.Dl "pgrep sshd | xargs protect -dip" +Remove protection from all current and future processes: +.Pp +.Dl "protect -cdi -p 1" +.Sh SEE ALSO +.Xr pprotect 2 +.Sh BUGS +If you protect a runaway process that allocates all memory the system will +deadlock. +.Pp +Inheritance of the protected state is not yet implemented. diff --git a/usr.bin/protect/protect.c b/usr.bin/protect/protect.c new file mode 100644 index 000000000000..ba15aa642093 --- /dev/null +++ b/usr.bin/protect/protect.c @@ -0,0 +1,122 @@ +/*- + * Copyright (c) 2013 Advanced Computing Technologies LLC + * Written by: John H. Baldwin + * All rights reserved. + * + * 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 AUTHOR 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 AUTHOR 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void +usage(void) +{ + + fprintf(stderr, "usage: protect [-i] command\n"); + fprintf(stderr, " protect [-cdi] -g pgrp | -p pid\n"); + exit(1); +} + +static id_t +parse_id(char *id) +{ + static bool first = true; + long value; + char *ch; + + if (!first) { + warnx("only one -g or -p flag is permitted"); + usage(); + } + value = strtol(id, &ch, 0); + if (*ch != '\0') { + warnx("invalid process id"); + usage(); + } + return (value); +} + +int +main(int argc, char *argv[]) +{ + idtype_t idtype; + id_t id; + int ch, flags; + bool descend, inherit, idset; + + idtype = P_PID; + id = getpid(); + flags = PPROT_SET; + descend = inherit = idset = false; + while ((ch = getopt(argc, argv, "cdig:p:")) != -1) + switch (ch) { + case 'c': + flags = PPROT_CLEAR; + break; + case 'd': + descend = true; + break; + case 'i': + inherit = true; + break; + case 'g': + idtype = P_PGID; + id = parse_id(optarg); + idset = true; + break; + case 'p': + idtype = P_PID; + id = parse_id(optarg); + idset = true; + break; + } + argc -= optind; + argv += optind; + + if ((idset && argc != 0) || (!idset && (argc == 0 || descend))) + usage(); + + if (descend) + flags |= PPROT_DESCEND; + if (inherit) + flags |= PPROT_INHERIT; + if (procctl(idtype, id, PROC_SPROTECT, &flags) == -1) + err(1, "procctl"); + + if (argc != 0) { + errno = 0; + execvp(*argv, argv); + err(errno == ENOENT ? 127 : 126, "%s", *argv); + } + return (0); +} diff --git a/usr.bin/truss/syscall.h b/usr.bin/truss/syscall.h index ce7d2e94e4f4..b0d3461adbcc 100644 --- a/usr.bin/truss/syscall.h +++ b/usr.bin/truss/syscall.h @@ -40,7 +40,7 @@ enum Argtype { None = 1, Hex, Octal, Int, Name, Ptr, Stat, Ioctl, Quad, Fd_set, Sigaction, Fcntl, Mprot, Mmapflags, Whence, Readlinkres, Umtx, Sigset, Sigprocmask, Kevent, Sockdomain, Socktype, Open, Fcntlflag, Rusage, BinString, Shutdown, Resource, Rlimit, Timeval2, - Pathconf, Rforkflags, ExitStatus, Waitoptions, Idtype }; + Pathconf, Rforkflags, ExitStatus, Waitoptions, Idtype, Procctl }; #define ARG_MASK 0xff #define OUT 0x100 diff --git a/usr.bin/truss/syscalls.c b/usr.bin/truss/syscalls.c index 5369decb44c3..06c25117ab6b 100644 --- a/usr.bin/truss/syscalls.c +++ b/usr.bin/truss/syscalls.c @@ -41,6 +41,7 @@ static const char rcsid[] = #include #include +#include #include #include #include @@ -270,6 +271,8 @@ static struct syscall syscalls[] = { { .name = "wait6", .ret_type = 1, .nargs = 6, .args = { { Idtype, 0 }, { Int, 1 }, { ExitStatus | OUT, 2 }, { Waitoptions, 3 }, { Rusage | OUT, 4 }, { Ptr, 5 } } }, + { .name = "procctl", .ret_type = 1, .nargs = 4, + .args = { { Idtype, 0 }, { Int, 1 }, { Procctl, 2 }, { Ptr, 3 } } }, { .name = 0 }, }; @@ -399,6 +402,10 @@ static struct xlat idtype_arg[] = { X(P_CTID) X(P_CPUID) X(P_PSETID) XEND }; +static struct xlat procctl_arg[] = { + X(PROC_SPROTECT) XEND +}; + #undef X #undef XEND @@ -1198,6 +1205,9 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval, case Idtype: tmp = strdup(xlookup(idtype_arg, args[sc->offset])); break; + case Procctl: + tmp = strdup(xlookup(procctl_arg, args[sc->offset])); + break; default: errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK); }