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; /* * We might be called directly by the kernel's fault-handling code, so * we can't assert that the caller is in the global zone. */ /* * Make sure that cmd is one of the valid <sys/uadmin.h> command codes * and that we have appropriate privileges for this action. */ 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); } /* * Serialize these operations on ualock. If it is held, the * system should shutdown, reboot, or remount shortly, unless there is * an error. We need a cv rather than just a mutex because proper * functioning of A_REBOOT relies on being able to interrupt blocked * userland callers. * * We only clear ua_shutdown_thread after A_REMOUNT or A_CONFIG. * Other commands should never return. */ 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) { /* * If we were interrupted, leave, and handle * the signal (or exit, depending on what * happened) */ mutex_exit(&ualock); return (EINTR); } } ua_shutdown_thread = curthread; mutex_exit(&ualock); } switch (cmd) { case A_SHUTDOWN: { proc_t *p = ttoproc(curthread); /* * Release (almost) all of our own resources if we are called * from a user context, however if we are calling kadmin() from * a kernel context then we do not release these resources. */ if (p != &p0) { proc_is_exiting(p); if ((error = exitlwps(0)) != 0) { /* * Another thread in this process also called * exitlwps(). */ 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 { /* * Reset t_cred if not set because much of the * filesystem code depends on CRED() being valid. */ if (curthread->t_cred == NULL) curthread->t_cred = kcred; } /* indicate shutdown in progress */ sys_shutdown = 1; /* * Communcate that init shouldn't be restarted. */ zone_shutdown_global(); killall(ALL_ZONES); /* * If we are calling kadmin() from a kernel context then we * do not release these resources. */ 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; } /* * Allow the reboot/halt/poweroff code a chance to do * anything it needs to whilst we still have filesystems * mounted, like loading any modules necessary for later * performing the actual poweroff. */ if ((mdep != NULL) && (*(char *)mdep == '/')) { buf = i_convert_boot_device_name(mdep, NULL, &buflen); mdpreboot(cmd, fcn, buf); } else mdpreboot(cmd, fcn, mdep); /* * Allow fsflush to finish running and then prevent it * from ever running again so that vfs_unmountall() and * vfs_syncall() can acquire the vfs locks they need. */ sema_p(&fsflush_sema); (void) callb_execute_class(CB_CL_UADMIN_PRE_VFS, NULL); vfs_unmountall(); (void) VFS_MOUNTROOT(rootvfs, ROOT_UNMOUNT); vfs_syncall(); dump_ereports(); dump_messages(); invoke_cb = B_TRUE; /* FALLTHROUGH */ } 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); /* no return expected */ 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; } /* Let other threads enter the shutdown path now */ mutex_enter(&ualock); ua_shutdown_thread = NULL; cv_signal(&uacond); mutex_exit(&ualock); break; case A_REMOUNT: (void) VFS_MOUNTROOT(rootvfs, ROOT_REMOUNT); /* Let other threads enter the shutdown path now */ mutex_enter(&ualock); ua_shutdown_thread = NULL; cv_signal(&uacond); mutex_exit(&ualock); break; case A_FREEZE: { /* * This is the entrypoint for all suspend/resume actions. */ extern int cpr(int, void *); if (modload("misc", "cpr") == -1) return (ENOTSUP); /* Let the CPR module decide what to do with mdep */ 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"); /*NOTREACHED*/ } case A_SDTTEST: { DTRACE_PROBE7(test, int, 1, int, 2, int, 3, int, 4, int, 5, int, 6, int, 7); break; } default: error = EINVAL; } return (error); }
/* * Configure root file system. */ int rootconf(void) { int error; struct vfssw *vsw; extern void pm_init(void); BMDPRINTF(("rootconf: fstype %s\n", rootfs.bo_fstype)); BMDPRINTF(("rootconf: name %s\n", rootfs.bo_name)); BMDPRINTF(("rootconf: flags 0x%x\n", rootfs.bo_flags)); BMDPRINTF(("rootconf: obp_bootpath %s\n", obp_bootpath)); /* * Install cluster modules that were only loaded during * loadrootmodules(). */ if (error = clboot_rootconf()) return (error); if (root_is_svm) { (void) strncpy(rootfs.bo_name, obp_bootpath, BO_MAXOBJNAME); BMDPRINTF(("rootconf: svm: rootfs name %s\n", rootfs.bo_name)); BMDPRINTF(("rootconf: svm: svm name %s\n", svm_bootpath)); } /* * Run _init on the root filesystem (we already loaded it * but we've been waiting until now to _init it) which will * have the side-effect of running vsw_init() on this vfs. * Because all the nfs filesystems are lumped into one * module we need to special case it. */ if (strncmp(rootfs.bo_fstype, "nfs", 3) == 0) { if (modload("fs", "nfs") == -1) { cmn_err(CE_CONT, "Cannot initialize %s filesystem\n", rootfs.bo_fstype); return (ENXIO); } } else { if (modload("fs", rootfs.bo_fstype) == -1) { cmn_err(CE_CONT, "Cannot initialize %s filesystem\n", rootfs.bo_fstype); return (ENXIO); } } RLOCK_VFSSW(); vsw = vfs_getvfsswbyname(rootfs.bo_fstype); RUNLOCK_VFSSW(); VFS_INIT(rootvfs, &vsw->vsw_vfsops, (caddr_t)0); VFS_HOLD(rootvfs); if (root_is_svm) { rootvfs->vfs_flag |= VFS_RDONLY; } /* * This pm-releated call has to occur before root is mounted since we * need to power up all devices. It is placed after VFS_INIT() such * that opening a device via ddi_lyr_ interface just before root has * been mounted would work. */ pm_init(); if (netboot) { if ((error = strplumb()) != 0) { cmn_err(CE_CONT, "Cannot plumb network device\n"); return (error); } } /* * ufs_mountroot() ends up calling getrootdev() * (below) which actually triggers the _init, identify, * probe and attach of the drivers that make up root device * bush; these are also quietly waiting in memory. */ BMDPRINTF(("rootconf: calling VFS_MOUNTROOT %s\n", rootfs.bo_fstype)); error = VFS_MOUNTROOT(rootvfs, ROOT_INIT); vfs_unrefvfssw(vsw); rootdev = rootvfs->vfs_dev; if (error) cmn_err(CE_CONT, "Cannot mount root on %s fstype %s\n", rootfs.bo_name, rootfs.bo_fstype); else cmn_err(CE_CONT, "?root on %s fstype %s\n", rootfs.bo_name, rootfs.bo_fstype); return (error); }