#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/swap.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/var.h>
#include <sys/uadmin.h>
#include <sys/signal.h>
#include <sys/time.h>
#include <vm/seg_kmem.h>
#include <sys/modctl.h>
#include <sys/callb.h>
#include <sys/dumphdr.h>
#include <sys/debug.h>
#include <sys/ftrace.h>
#include <sys/cmn_err.h>
#include <sys/panic.h>
#include <sys/ddi.h>
#include <sys/ddi_periodic.h>
#include <sys/sunddi.h>
#include <sys/policy.h>
#include <sys/zone.h>
#include <sys/condvar.h>
#include <sys/thread.h>
#include <sys/sdt.h>
extern ksema_t fsflush_sema;
kmutex_t ualock;
kcondvar_t uacond;
kthread_t *ua_shutdown_thread = NULL;
int sys_shutdown = 0;
volatile int fastreboot_dryrun = 0;
void
killall(zoneid_t zoneid)
{
proc_t *p;
ASSERT(zoneid != GLOBAL_ZONEID);
mutex_enter(&pidlock);
for (p = practive; p != NULL; p = p->p_next) {
if ((zoneid == ALL_ZONES || p->p_zone->zone_id == zoneid) &&
p->p_exec != NULLVP &&
p->p_as != &kas &&
p->p_stat != SZOMB) {
mutex_enter(&p->p_lock);
p->p_flag |= SNOWAIT;
sigtoproc(p, NULL, SIGSTOP);
mutex_exit(&p->p_lock);
}
}
p = practive;
while (p != NULL) {
if ((zoneid == ALL_ZONES || p->p_zone->zone_id == zoneid) &&
p->p_exec != NULLVP &&
p->p_as != &kas &&
p->p_stat != SIDL &&
p->p_stat != SZOMB) {
mutex_enter(&p->p_lock);
if (sigismember(&p->p_sig, SIGKILL)) {
mutex_exit(&p->p_lock);
p = p->p_next;
} else {
sigtoproc(p, NULL, SIGKILL);
mutex_exit(&p->p_lock);
(void) cv_reltimedwait(&p->p_srwchan_cv,
&pidlock, hz, TR_CLOCK_TICK);
p = practive;
}
} else {
p = p->p_next;
}
}
mutex_exit(&pidlock);
}
static void
sdt_test_args(void)
{
DTRACE_PROBE7(test, int, 1, int, 2, int, 3, int, 4, int, 5,
int, 6, int, 7);
}
#pragma GCC push_options
#pragma GCC optimize("optimize-sibling-calls")
static void
sdt_test_args_tail_call(int a, int b, int c, int d, int e, int f, int g)
{
DTRACE_PROBE7(test, int, a, int, b, int, c, int, d,
int, e, int, f, int, g);
}
#pragma GCC pop_options
int
kadmin(int cmd, int fcn, void *mdep, cred_t *credp)
{
int error = 0;
char *buf;
size_t buflen = 0;
boolean_t invoke_cb = B_FALSE;
switch (cmd) {
case A_FTRACE:
case A_SHUTDOWN:
case A_REBOOT:
case A_REMOUNT:
case A_FREEZE:
case A_DUMP:
case A_SDTTEST:
case A_CONFIG:
if (secpolicy_sys_config(credp, B_FALSE) != 0)
return (EPERM);
break;
default:
return (EINVAL);
}
if (cmd == A_SHUTDOWN || cmd == A_REBOOT || cmd == A_REMOUNT ||
cmd == A_CONFIG) {
mutex_enter(&ualock);
while (ua_shutdown_thread != NULL) {
if (cv_wait_sig(&uacond, &ualock) == 0) {
mutex_exit(&ualock);
return (EINTR);
}
}
ua_shutdown_thread = curthread;
mutex_exit(&ualock);
}
switch (cmd) {
case A_SHUTDOWN:
{
proc_t *p = ttoproc(curthread);
if (p != &p0) {
proc_is_exiting(p);
if ((error = exitlwps(0)) != 0) {
mutex_enter(&ualock);
ua_shutdown_thread = NULL;
cv_signal(&uacond);
mutex_exit(&ualock);
return (error);
}
mutex_enter(&p->p_lock);
p->p_flag |= SNOWAIT;
sigfillset(&p->p_ignore);
curthread->t_lwp->lwp_cursig = 0;
curthread->t_lwp->lwp_extsig = 0;
if (p->p_exec) {
vnode_t *exec_vp = p->p_exec;
p->p_exec = NULLVP;
mutex_exit(&p->p_lock);
VN_RELE(exec_vp);
} else {
mutex_exit(&p->p_lock);
}
pollcleanup();
closeall(P_FINFO(curproc));
relvm();
} else {
if (curthread->t_cred == NULL)
curthread->t_cred = kcred;
}
sys_shutdown = 1;
zone_shutdown_global();
killall(ALL_ZONES);
if (ttoproc(curthread) != &p0) {
VN_RELE(PTOU(curproc)->u_cdir);
if (PTOU(curproc)->u_rdir)
VN_RELE(PTOU(curproc)->u_rdir);
if (PTOU(curproc)->u_cwd)
refstr_rele(PTOU(curproc)->u_cwd);
PTOU(curproc)->u_cdir = rootdir;
PTOU(curproc)->u_rdir = NULL;
PTOU(curproc)->u_cwd = NULL;
}
if ((mdep != NULL) && (*(char *)mdep == '/')) {
buf = i_convert_boot_device_name(mdep, NULL, &buflen);
mdpreboot(cmd, fcn, buf);
} else
mdpreboot(cmd, fcn, mdep);
sema_p(&fsflush_sema);
(void) callb_execute_class(CB_CL_UADMIN_PRE_VFS, 0);
vfs_unmountall();
(void) VFS_MOUNTROOT(rootvfs, ROOT_UNMOUNT);
vfs_syncall();
ddi_periodic_fini();
dump_ereports();
dump_messages();
invoke_cb = B_TRUE;
}
case A_REBOOT:
if ((mdep != NULL) && (*(char *)mdep == '/')) {
buf = i_convert_boot_device_name(mdep, NULL, &buflen);
mdboot(cmd, fcn, buf, invoke_cb);
} else
mdboot(cmd, fcn, mdep, invoke_cb);
break;
case A_CONFIG:
switch (fcn) {
case AD_UPDATE_BOOT_CONFIG:
#ifndef __sparc
{
extern void fastboot_update_config(const char *);
fastboot_update_config(mdep);
}
#endif
break;
}
mutex_enter(&ualock);
ua_shutdown_thread = NULL;
cv_signal(&uacond);
mutex_exit(&ualock);
break;
case A_REMOUNT:
(void) VFS_MOUNTROOT(rootvfs, ROOT_REMOUNT);
mutex_enter(&ualock);
ua_shutdown_thread = NULL;
cv_signal(&uacond);
mutex_exit(&ualock);
break;
case A_FREEZE:
{
extern int cpr(int, void *);
if (modload("misc", "cpr") == -1)
return (ENOTSUP);
error = cpr(fcn, mdep);
break;
}
case A_FTRACE:
{
switch (fcn) {
case AD_FTRACE_START:
(void) FTRACE_START();
break;
case AD_FTRACE_STOP:
(void) FTRACE_STOP();
break;
default:
error = EINVAL;
}
break;
}
case A_DUMP:
{
if (fcn == AD_NOSYNC) {
in_sync = 1;
break;
}
panic_bootfcn = fcn;
panic_forced = 1;
if ((mdep != NULL) && (*(char *)mdep == '/')) {
panic_bootstr = i_convert_boot_device_name(mdep,
NULL, &buflen);
} else
panic_bootstr = mdep;
#ifndef __sparc
extern void fastboot_update_and_load(int, char *);
fastboot_update_and_load(fcn, mdep);
#endif
panic("forced crash dump initiated at user request");
}
case A_SDTTEST:
{
sdt_test_args();
sdt_test_args_tail_call(1, 2, 3, 4, 5, 6, 7);
break;
}
default:
error = EINVAL;
}
return (error);
}
int
uadmin(int cmd, int fcn, uintptr_t mdep)
{
int error = 0, rv = 0;
size_t nbytes = 0;
cred_t *credp = CRED();
char *bootargs = NULL;
int reset_status = 0;
if (cmd == A_SHUTDOWN && fcn == AD_FASTREBOOT_DRYRUN) {
ddi_walk_devs(ddi_root_node(), check_driver_quiesce,
&reset_status);
if (reset_status != 0)
return (EIO);
else
return (0);
}
if (cmd == A_SWAPCTL) {
if (get_udatamodel() == DATAMODEL_NATIVE)
error = swapctl(fcn, (void *)mdep, &rv);
#if defined(_SYSCALL32_IMPL)
else
error = swapctl32(fcn, (void *)mdep, &rv);
#endif
return (error ? set_errno(error) : rv);
}
if (mdep != (uintptr_t)NULL &&
(cmd == A_SHUTDOWN || cmd == A_REBOOT || cmd == A_DUMP ||
cmd == A_FREEZE || cmd == A_CONFIG)) {
bootargs = kmem_zalloc(BOOTARGS_MAX, KM_SLEEP);
if ((error = copyinstr((const char *)mdep, bootargs,
BOOTARGS_MAX, &nbytes)) != 0) {
kmem_free(bootargs, BOOTARGS_MAX);
return (set_errno(error));
}
}
if (getzoneid() != GLOBAL_ZONEID)
error = zone_kadmin(cmd, fcn, bootargs, credp);
else
error = kadmin(cmd, fcn, bootargs, credp);
if (bootargs != NULL)
kmem_free(bootargs, BOOTARGS_MAX);
return (error ? set_errno(error) : 0);
}