kern_shutdown.c revision 48948
117658Sjulian/*- 217658Sjulian * Copyright (c) 1986, 1988, 1991, 1993 317658Sjulian * The Regents of the University of California. All rights reserved. 417658Sjulian * (c) UNIX System Laboratories, Inc. 517658Sjulian * All or some portions of this file are derived from material licensed 617658Sjulian * to the University of California by American Telephone and Telegraph 717658Sjulian * Co. or Unix System Laboratories, Inc. and are reproduced herein with 817658Sjulian * the permission of UNIX System Laboratories, Inc. 917658Sjulian * 1017658Sjulian * Redistribution and use in source and binary forms, with or without 1117658Sjulian * modification, are permitted provided that the following conditions 1217658Sjulian * are met: 1317658Sjulian * 1. Redistributions of source code must retain the above copyright 1417658Sjulian * notice, this list of conditions and the following disclaimer. 1517658Sjulian * 2. Redistributions in binary form must reproduce the above copyright 1617658Sjulian * notice, this list of conditions and the following disclaimer in the 1717658Sjulian * documentation and/or other materials provided with the distribution. 1817658Sjulian * 3. All advertising materials mentioning features or use of this software 1917658Sjulian * must display the following acknowledgement: 2017658Sjulian * This product includes software developed by the University of 2117658Sjulian * California, Berkeley and its contributors. 2217658Sjulian * 4. Neither the name of the University nor the names of its contributors 2317658Sjulian * may be used to endorse or promote products derived from this software 2417658Sjulian * without specific prior written permission. 2517658Sjulian * 2617658Sjulian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2717658Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2817658Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2917658Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3017658Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3117658Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3217658Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3317658Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3417658Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3517658Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3617658Sjulian * SUCH DAMAGE. 3717658Sjulian * 3817658Sjulian * @(#)kern_shutdown.c 8.3 (Berkeley) 1/21/94 3948948Sgreen * $Id: kern_shutdown.c,v 1.56 1999/07/20 20:55:50 green Exp $ 4017658Sjulian */ 4117658Sjulian 4217658Sjulian#include "opt_ddb.h" 4333445Seivind#include "opt_hw_wdog.h" 4428976Sbde#include "opt_panic.h" 4528976Sbde#include "opt_show_busybufs.h" 4617658Sjulian 4717658Sjulian#include <sys/param.h> 4817658Sjulian#include <sys/systm.h> 4931275Sbde#include <sys/buf.h> 5017658Sjulian#include <sys/reboot.h> 5117658Sjulian#include <sys/proc.h> 5241137Smsmith#include <sys/vnode.h> 5317658Sjulian#include <sys/malloc.h> 5417658Sjulian#include <sys/kernel.h> 5521776Sbde#include <sys/mount.h> 5639237Sgibbs#include <sys/queue.h> 5717658Sjulian#include <sys/sysctl.h> 5817658Sjulian#include <sys/conf.h> 5917658Sjulian#include <sys/sysproto.h> 6017658Sjulian 6117658Sjulian#include <machine/pcb.h> 6217658Sjulian#include <machine/clock.h> 6317658Sjulian#include <machine/cons.h> 6417658Sjulian#include <machine/md_var.h> 6526812Speter#ifdef SMP 6626812Speter#include <machine/smp.h> /* smp_active, cpuid */ 6726812Speter#endif 6817658Sjulian 6917658Sjulian#include <sys/signalvar.h> 7017658Sjulian 7117658Sjulian#ifndef PANIC_REBOOT_WAIT_TIME 7217658Sjulian#define PANIC_REBOOT_WAIT_TIME 15 /* default to 15 seconds */ 7317658Sjulian#endif 7417658Sjulian 7517658Sjulian/* 7617658Sjulian * Note that stdarg.h and the ANSI style va_start macro is used for both 7717658Sjulian * ANSI and traditional C compilers. 7817658Sjulian */ 7917658Sjulian#include <machine/stdarg.h> 8017658Sjulian 8128769Sbde#ifdef DDB 8217658Sjulian#ifdef DDB_UNATTENDED 8342135Smsmithint debugger_on_panic = 0; 8417658Sjulian#else 8542135Smsmithint debugger_on_panic = 1; 8617658Sjulian#endif 8717658SjulianSYSCTL_INT(_debug, OID_AUTO, debugger_on_panic, CTLFLAG_RW, 8846381Sbillf &debugger_on_panic, 0, "Run debugger on kernel panic"); 8917658Sjulian#endif 9017658Sjulian 9143436SmsmithSYSCTL_NODE(_kern, OID_AUTO, shutdown, CTLFLAG_RW, 0, "Shutdown environment"); 9243436Smsmith 9328000Sjulian#ifdef HW_WDOG 9417658Sjulian/* 9527997Sjulian * If there is a hardware watchdog, point this at the function needed to 9627997Sjulian * hold it off. 9727997Sjulian * It's needed when the kernel needs to do some lengthy operations. 9827997Sjulian * e.g. in wd.c when dumping core.. It's most annoying to have 9927997Sjulian * your precious core-dump only half written because the wdog kicked in. 10027997Sjulian */ 10127997Sjulianwatchdog_tickle_fn wdog_tickler = NULL; 10228000Sjulian#endif /* HW_WDOG */ 10327997Sjulian 10427997Sjulian/* 10517658Sjulian * Variable panicstr contains argument to first call to panic; used as flag 10617658Sjulian * to indicate that the kernel has already called panic. 10717658Sjulian */ 10817658Sjulianconst char *panicstr; 10917658Sjulian 11017658Sjulian/* 11117658Sjulian * callout list for things to do a shutdown 11217658Sjulian */ 11317658Sjuliantypedef struct shutdown_list_element { 11439237Sgibbs LIST_ENTRY(shutdown_list_element) links; 11517658Sjulian bootlist_fn function; 11617658Sjulian void *arg; 11740751Smsmith int priority; 11817658Sjulian} *sle_p; 11917658Sjulian 12017768Sjulian/* 12139237Sgibbs * There are three shutdown lists. Some things need to be shut down 12239237Sgibbs * earlier than others. 12317768Sjulian */ 12439237SgibbsLIST_HEAD(shutdown_list, shutdown_list_element); 12517658Sjulian 12639237Sgibbsstatic struct shutdown_list shutdown_lists[SHUTDOWN_FINAL + 1]; 12739237Sgibbs 12831275Sbdestatic void boot __P((int)) __dead2; 12931275Sbdestatic void dumpsys __P((void)); 13048868Sphkstatic int setdumpdev __P((dev_t dev)); 13117658Sjulian 13248868Sphk 13317658Sjulian#ifndef _SYS_SYSPROTO_H_ 13417658Sjulianstruct reboot_args { 13517658Sjulian int opt; 13617658Sjulian}; 13717658Sjulian#endif 13817658Sjulian/* ARGSUSED */ 13917658Sjulian 14017658Sjulian/* 14117658Sjulian * The system call that results in a reboot 14217658Sjulian */ 14317658Sjulianint 14430994Sphkreboot(p, uap) 14517658Sjulian struct proc *p; 14617658Sjulian struct reboot_args *uap; 14717658Sjulian{ 14817658Sjulian int error; 14917658Sjulian 15046112Sphk if ((error = suser(p))) 15117658Sjulian return (error); 15217658Sjulian 15317658Sjulian boot(uap->opt); 15417658Sjulian return (0); 15517658Sjulian} 15617658Sjulian 15717658Sjulian/* 15817658Sjulian * Called by events that want to shut down.. e.g <CTL><ALT><DEL> on a PC 15917658Sjulian */ 16017658Sjulianvoid 16128769Sbdeshutdown_nice() 16217658Sjulian{ 16317658Sjulian /* Send a signal to init(8) and have it shutdown the world */ 16417658Sjulian if (initproc != NULL) { 16517658Sjulian psignal(initproc, SIGINT); 16617658Sjulian } else { 16717658Sjulian /* No init(8) running, so simply reboot */ 16817658Sjulian boot(RB_NOSYNC); 16917658Sjulian } 17017658Sjulian return; 17117658Sjulian} 17217658Sjulianstatic int waittime = -1; 17317658Sjulianstatic struct pcb dumppcb; 17417658Sjulian 17517658Sjulian/* 17617658Sjulian * Go through the rigmarole of shutting down.. 17717658Sjulian * this used to be in machdep.c but I'll be dammned if I could see 17817658Sjulian * anything machine dependant in it. 17917658Sjulian */ 18031275Sbdestatic void 18117658Sjulianboot(howto) 18217658Sjulian int howto; 18317658Sjulian{ 18417768Sjulian sle_p ep; 18517658Sjulian 18625164Speter#ifdef SMP 18725164Speter if (smp_active) { 18826812Speter printf("boot() called on cpu#%d\n", cpuid); 18925164Speter } 19025164Speter#endif 19127997Sjulian /* 19227997Sjulian * Do any callouts that should be done BEFORE syncing the filesystems. 19327997Sjulian */ 19439237Sgibbs LIST_FOREACH(ep, &shutdown_lists[SHUTDOWN_PRE_SYNC], links) 19517658Sjulian (*ep->function)(howto, ep->arg); 19627997Sjulian 19727997Sjulian /* 19827997Sjulian * Now sync filesystems 19927997Sjulian */ 20017658Sjulian if (!cold && (howto & RB_NOSYNC) == 0 && waittime < 0) { 20117658Sjulian register struct buf *bp; 20217658Sjulian int iter, nbusy; 20317658Sjulian 20417658Sjulian waittime = 0; 20517658Sjulian printf("\nsyncing disks... "); 20617658Sjulian 20730994Sphk sync(&proc0, NULL); 20817658Sjulian 20934266Sjulian /* 21034266Sjulian * With soft updates, some buffers that are 21134266Sjulian * written will be remarked as dirty until other 21234266Sjulian * buffers are written. 21334266Sjulian */ 21417658Sjulian for (iter = 0; iter < 20; iter++) { 21517658Sjulian nbusy = 0; 21617658Sjulian for (bp = &buf[nbuf]; --bp >= buf; ) { 21748225Smckusick if ((bp->b_flags & B_INVAL) == 0 && 21848225Smckusick BUF_REFCNT(bp) > 0) { 21917658Sjulian nbusy++; 22034266Sjulian } else if ((bp->b_flags & (B_DELWRI | B_INVAL)) 22134266Sjulian == B_DELWRI) { 22234266Sjulian /* bawrite(bp);*/ 22334266Sjulian nbusy++; 22417658Sjulian } 22517658Sjulian } 22617658Sjulian if (nbusy == 0) 22717658Sjulian break; 22817658Sjulian printf("%d ", nbusy); 22934266Sjulian sync(&proc0, NULL); 23034266Sjulian DELAY(50000 * iter); 23117658Sjulian } 23241137Smsmith /* 23341137Smsmith * Count only busy local buffers to prevent forcing 23441137Smsmith * a fsck if we're just a client of a wedged NFS server 23541137Smsmith */ 23641137Smsmith nbusy = 0; 23741137Smsmith for (bp = &buf[nbuf]; --bp >= buf; ) { 23848225Smckusick if (((bp->b_flags&B_INVAL) == 0 && BUF_REFCNT(bp)) || 23948225Smckusick ((bp->b_flags & (B_DELWRI|B_INVAL)) == B_DELWRI)) { 24048225Smckusick if (bp->b_dev == NODEV) 24148225Smckusick CIRCLEQ_REMOVE(&mountlist, 24248225Smckusick bp->b_vp->v_mount, mnt_list); 24341137Smsmith else 24441137Smsmith nbusy++; 24546568Speter } 24641137Smsmith 24741137Smsmith 24841137Smsmith } 24917658Sjulian if (nbusy) { 25017658Sjulian /* 25117658Sjulian * Failed to sync all blocks. Indicate this and don't 25217658Sjulian * unmount filesystems (thus forcing an fsck on reboot). 25317658Sjulian */ 25417658Sjulian printf("giving up\n"); 25517658Sjulian#ifdef SHOW_BUSYBUFS 25617658Sjulian nbusy = 0; 25717658Sjulian for (bp = &buf[nbuf]; --bp >= buf; ) { 25848225Smckusick if ((bp->b_flags & B_INVAL) == 0 && 25948225Smckusick BUF_REFCNT(bp) > 0) { 26017658Sjulian nbusy++; 26137555Sbde printf( 26237555Sbde "%d: dev:%08lx, flags:%08lx, blkno:%ld, lblkno:%ld\n", 26337555Sbde nbusy, (u_long)bp->b_dev, 26437555Sbde bp->b_flags, (long)bp->b_blkno, 26537555Sbde (long)bp->b_lblkno); 26617658Sjulian } 26717658Sjulian } 26817658Sjulian DELAY(5000000); /* 5 seconds */ 26917658Sjulian#endif 27017658Sjulian } else { 27117658Sjulian printf("done\n"); 27217658Sjulian /* 27317658Sjulian * Unmount filesystems 27417658Sjulian */ 27517658Sjulian if (panicstr == 0) 27617658Sjulian vfs_unmountall(); 27717658Sjulian } 27839237Sgibbs DELAY(100000); /* wait for console output to finish */ 27917658Sjulian } 28027997Sjulian 28127997Sjulian /* 28227997Sjulian * Ok, now do things that assume all filesystem activity has 28327997Sjulian * been completed. 28427997Sjulian */ 28539237Sgibbs LIST_FOREACH(ep, &shutdown_lists[SHUTDOWN_POST_SYNC], links) 28617768Sjulian (*ep->function)(howto, ep->arg); 28739237Sgibbs splhigh(); 28839522Sdt if ((howto & (RB_HALT|RB_DUMP)) == RB_DUMP && !cold) { 28939237Sgibbs savectx(&dumppcb); 29039237Sgibbs#ifdef __i386__ 29139237Sgibbs dumppcb.pcb_cr3 = rcr3(); 29239237Sgibbs#endif 29339237Sgibbs dumpsys(); 29417768Sjulian } 29539237Sgibbs 29639237Sgibbs /* Now that we're going to really halt the system... */ 29739237Sgibbs LIST_FOREACH(ep, &shutdown_lists[SHUTDOWN_FINAL], links) 29839237Sgibbs (*ep->function)(howto, ep->arg); 29939237Sgibbs 30017658Sjulian if (howto & RB_HALT) { 30117658Sjulian printf("\n"); 30217658Sjulian printf("The operating system has halted.\n"); 30317658Sjulian printf("Please press any key to reboot.\n\n"); 30419274Sjulian switch (cngetc()) { 30519274Sjulian case -1: /* No console, just die */ 30619274Sjulian cpu_halt(); 30719274Sjulian /* NOTREACHED */ 30819274Sjulian default: 30939237Sgibbs howto &= ~RB_HALT; 31019274Sjulian break; 31119274Sjulian } 31239237Sgibbs } else if (howto & RB_DUMP) { 31339237Sgibbs /* System Paniced */ 31417658Sjulian 31539237Sgibbs if (PANIC_REBOOT_WAIT_TIME != 0) { 31639237Sgibbs if (PANIC_REBOOT_WAIT_TIME != -1) { 31739237Sgibbs int loop; 31839237Sgibbs printf("Automatic reboot in %d seconds - " 31939237Sgibbs "press a key on the console to abort\n", 32039237Sgibbs PANIC_REBOOT_WAIT_TIME); 32139237Sgibbs for (loop = PANIC_REBOOT_WAIT_TIME * 10; 32239237Sgibbs loop > 0; --loop) { 32339237Sgibbs DELAY(1000 * 100); /* 1/10th second */ 32439237Sgibbs /* Did user type a key? */ 32539237Sgibbs if (cncheckc() != -1) 32639237Sgibbs break; 32717658Sjulian } 32839237Sgibbs if (!loop) 32939237Sgibbs goto die; 33017658Sjulian } 33139237Sgibbs } else { /* zero time specified - reboot NOW */ 33239237Sgibbs goto die; 33317658Sjulian } 33439237Sgibbs printf("--> Press a key on the console to reboot <--\n"); 33539237Sgibbs cngetc(); 33617658Sjulian } 33717658Sjuliandie: 33817658Sjulian printf("Rebooting...\n"); 33917658Sjulian DELAY(1000000); /* wait 1 sec for printf's to complete and be read */ 34017677Sjulian /* cpu_boot(howto); */ /* doesn't do anything at the moment */ 34117658Sjulian cpu_reset(); 34217658Sjulian for(;;) ; 34317658Sjulian /* NOTREACHED */ 34417658Sjulian} 34517658Sjulian 34617658Sjulian/* 34717658Sjulian * Magic number for savecore 34817658Sjulian * 34917658Sjulian * exported (symorder) and used at least by savecore(8) 35017658Sjulian * 35117658Sjulian */ 35217658Sjulianstatic u_long const dumpmag = 0x8fca0101UL; 35317658Sjulian 35417658Sjulianstatic int dumpsize = 0; /* also for savecore */ 35517658Sjulian 35617658Sjulianstatic int dodump = 1; 35717658Sjulian 35848868SphkSYSCTL_INT(_machdep, OID_AUTO, do_dump, CTLFLAG_RW, &dodump, 0, 35948868Sphk "Try to perform coredump on kernel panic"); 36048868Sphk 36148868Sphkstatic int 36248868Sphksetdumpdev(dev) 36348868Sphk dev_t dev; 36448868Sphk{ 36548868Sphk int maj, psize; 36648868Sphk long newdumplo; 36748868Sphk 36848868Sphk if (dev == NODEV) { 36948868Sphk dumpdev = dev; 37048868Sphk return (0); 37148868Sphk } 37248868Sphk maj = major(dev); 37348868Sphk if (bdevsw(dev) == NULL) 37448868Sphk return (ENXIO); /* XXX is this right? */ 37548868Sphk if (bdevsw(dev)->d_psize == NULL) 37648868Sphk return (ENXIO); /* XXX should be ENODEV ? */ 37748868Sphk psize = bdevsw(dev)->d_psize(dev); 37848868Sphk if (psize == -1) 37948868Sphk return (ENXIO); /* XXX should be ENODEV ? */ 38048868Sphk /* 38148868Sphk * XXX should clean up checking in dumpsys() to be more like this, 38248868Sphk * and nuke dodump sysctl (too many knobs). 38348868Sphk */ 38448868Sphk newdumplo = psize - Maxmem * PAGE_SIZE / DEV_BSIZE; 38548868Sphk if (newdumplo < 0) 38648868Sphk return (ENOSPC); 38748868Sphk dumpdev = dev; 38848868Sphk dumplo = newdumplo; 38948868Sphk return (0); 39048868Sphk} 39148868Sphk 39248868Sphk 39331403Sjulian/* ARGSUSED */ 39431403Sjulianstatic void dump_conf __P((void *dummy)); 39531403Sjulianstatic void 39631403Sjuliandump_conf(dummy) 39731403Sjulian void *dummy; 39831403Sjulian{ 39948868Sphk if (setdumpdev(dumpdev) != 0) 40048868Sphk dumpdev = NODEV; 40131403Sjulian} 40248868Sphk 40331403SjulianSYSINIT(dump_conf, SI_SUB_DUMP_CONF, SI_ORDER_FIRST, dump_conf, NULL) 40431403Sjulian 40548868Sphkstatic int 40648868Sphksysctl_kern_dumpdev SYSCTL_HANDLER_ARGS 40748868Sphk{ 40848868Sphk int error; 40948868Sphk udev_t ndumpdev; 41048868Sphk 41148948Sgreen ndumpdev = dev2budev(dumpdev); 41248868Sphk error = sysctl_handle_opaque(oidp, &ndumpdev, sizeof ndumpdev, req); 41348868Sphk if (error == 0 && req->newptr != NULL) 41448868Sphk error = setdumpdev(udev2dev(ndumpdev, 1)); 41548868Sphk return (error); 41648868Sphk} 41748868Sphk 41848868SphkSYSCTL_PROC(_kern, KERN_DUMPDEV, dumpdev, CTLTYPE_OPAQUE|CTLFLAG_RW, 41948868Sphk 0, sizeof dumpdev, sysctl_kern_dumpdev, "T,dev_t", ""); 42048868Sphk 42117658Sjulian/* 42217658Sjulian * Doadump comes here after turning off memory management and 42317658Sjulian * getting on the dump stack, either when called above, or by 42417658Sjulian * the auto-restart code. 42517658Sjulian */ 42617658Sjulianstatic void 42717658Sjuliandumpsys(void) 42817658Sjulian{ 42917658Sjulian 43017658Sjulian if (!dodump) 43117658Sjulian return; 43217658Sjulian if (dumpdev == NODEV) 43317658Sjulian return; 43446676Sphk if (!(bdevsw(dumpdev))) 43517658Sjulian return; 43646676Sphk if (!(bdevsw(dumpdev)->d_dump)) 43717658Sjulian return; 43817658Sjulian dumpsize = Maxmem; 43947084Speter printf("\ndumping to dev (%d,%d), offset %ld\n", 44047084Speter major(dumpdev), minor(dumpdev), dumplo); 44117658Sjulian printf("dump "); 44246676Sphk switch ((*bdevsw(dumpdev)->d_dump)(dumpdev)) { 44317658Sjulian 44417658Sjulian case ENXIO: 44517658Sjulian printf("device bad\n"); 44617658Sjulian break; 44717658Sjulian 44817658Sjulian case EFAULT: 44917658Sjulian printf("device not ready\n"); 45017658Sjulian break; 45117658Sjulian 45217658Sjulian case EINVAL: 45317658Sjulian printf("area improper\n"); 45417658Sjulian break; 45517658Sjulian 45617658Sjulian case EIO: 45717658Sjulian printf("i/o error\n"); 45817658Sjulian break; 45917658Sjulian 46017658Sjulian case EINTR: 46117658Sjulian printf("aborted from console\n"); 46217658Sjulian break; 46317658Sjulian 46417658Sjulian default: 46517658Sjulian printf("succeeded\n"); 46617658Sjulian break; 46717658Sjulian } 46817658Sjulian} 46917658Sjulian 47017658Sjulian/* 47117658Sjulian * Panic is called on unresolvable fatal errors. It prints "panic: mesg", 47217658Sjulian * and then reboots. If we are called twice, then we avoid trying to sync 47317658Sjulian * the disks as this often leads to recursive panics. 47417658Sjulian */ 47517658Sjulianvoid 47617658Sjulianpanic(const char *fmt, ...) 47717658Sjulian{ 47817658Sjulian int bootopt; 47917658Sjulian va_list ap; 48038874Sache static char buf[256]; 48117658Sjulian 48217658Sjulian bootopt = RB_AUTOBOOT | RB_DUMP; 48317658Sjulian if (panicstr) 48417658Sjulian bootopt |= RB_NOSYNC; 48517658Sjulian else 48617658Sjulian panicstr = fmt; 48717658Sjulian 48817658Sjulian va_start(ap, fmt); 48941514Sarchie (void)vsnprintf(buf, sizeof(buf), fmt, ap); 49038874Sache if (panicstr == fmt) 49138874Sache panicstr = buf; 49217658Sjulian va_end(ap); 49338874Sache printf("panic: %s\n", buf); 49426100Sfsmp#ifdef SMP 49529128Speter /* three seperate prints in case of an unmapped page and trap */ 49629128Speter printf("mp_lock = %08x; ", mp_lock); 49729128Speter printf("cpuid = %d; ", cpuid); 49829128Speter printf("lapic.id = %08x\n", lapic.id); 49926100Sfsmp#endif 50017658Sjulian 50117658Sjulian#if defined(DDB) 50217658Sjulian if (debugger_on_panic) 50317658Sjulian Debugger ("panic"); 50417658Sjulian#endif 50517658Sjulian boot(bootopt); 50617658Sjulian} 50717658Sjulian 50817768Sjulian/* 50940751Smsmith * Three routines to handle adding/deleting items on the 51017768Sjulian * shutdown callout lists 51117768Sjulian * 51217768Sjulian * at_shutdown(): 51317658Sjulian * Take the arguments given and put them onto the shutdown callout list. 51417658Sjulian * However first make sure that it's not already there. 51517658Sjulian * returns 0 on success. 51617658Sjulian */ 51717658Sjulianint 51839237Sgibbsat_shutdown(bootlist_fn function, void *arg, int queue) 51917658Sjulian{ 52040751Smsmith return(at_shutdown_pri(function, arg, queue, SHUTDOWN_PRI_DEFAULT)); 52140751Smsmith} 52217768Sjulian 52340751Smsmith/* 52440751Smsmith * at_shutdown_pri(): 52540751Smsmith * Take the arguments given and put them onto the shutdown callout list 52640751Smsmith * with the given execution priority. 52740751Smsmith * returns 0 on success. 52840751Smsmith */ 52940751Smsmithint 53040751Smsmithat_shutdown_pri(bootlist_fn function, void *arg, int queue, int pri) 53140751Smsmith{ 53243436Smsmith sle_p op, ep, ip; 53340751Smsmith 53448431Speter op = NULL; /* shut up gcc */ 53539237Sgibbs if (queue < SHUTDOWN_PRE_SYNC 53639237Sgibbs || queue > SHUTDOWN_FINAL) { 53739237Sgibbs printf("at_shutdown: bad exit callout queue %d specified\n", 53839237Sgibbs queue); 53917768Sjulian return (EINVAL); 54017768Sjulian } 54117768Sjulian if (rm_at_shutdown(function, arg)) 54239237Sgibbs printf("at_shutdown: exit callout entry was already present\n"); 54317768Sjulian ep = malloc(sizeof(*ep), M_TEMP, M_NOWAIT); 54417768Sjulian if (ep == NULL) 54517768Sjulian return (ENOMEM); 54617658Sjulian ep->function = function; 54717658Sjulian ep->arg = arg; 54840751Smsmith ep->priority = pri; 54940751Smsmith 55040751Smsmith /* Sort into list of items on this queue */ 55140751Smsmith ip = LIST_FIRST(&shutdown_lists[queue]); 55240751Smsmith if (ip == NULL) { 55340751Smsmith LIST_INSERT_HEAD(&shutdown_lists[queue], ep, links); 55440751Smsmith } else { 55543436Smsmith for (; ip != NULL; op = ip, ip = LIST_NEXT(ip, links)) { 55640751Smsmith if (ep->priority < ip->priority) { 55740751Smsmith LIST_INSERT_BEFORE(ip, ep, links); 55840751Smsmith ep = NULL; 55940751Smsmith break; 56040751Smsmith } 56140751Smsmith } 56240751Smsmith if (ep != NULL) 56343436Smsmith LIST_INSERT_AFTER(op, ep, links); 56440751Smsmith } 56517768Sjulian return (0); 56617658Sjulian} 56717768Sjulian 56817658Sjulian/* 56917768Sjulian * Scan the exit callout lists for the given items and remove them. 57017658Sjulian * Returns the number of items removed. 57117658Sjulian */ 57217658Sjulianint 57317658Sjulianrm_at_shutdown(bootlist_fn function, void *arg) 57417658Sjulian{ 57539237Sgibbs sle_p ep; 57639237Sgibbs int count; 57739237Sgibbs int queue; 57817658Sjulian 57917768Sjulian count = 0; 58039237Sgibbs for (queue = SHUTDOWN_PRE_SYNC; queue < SHUTDOWN_FINAL; queue++) { 58139237Sgibbs LIST_FOREACH(ep, &shutdown_lists[queue], links) { 58239237Sgibbs if ((ep->function == function) && (ep->arg == arg)) { 58339237Sgibbs LIST_REMOVE(ep, links); 58439237Sgibbs free(ep, M_TEMP); 58539237Sgibbs count++; 58639237Sgibbs } 58717658Sjulian } 58817658Sjulian } 58917768Sjulian return (count); 59017658Sjulian} 59143436Smsmith 59243436Smsmith/* 59343436Smsmith * Support for poweroff delay. 59443436Smsmith */ 59543436Smsmithstatic int poweroff_delay = 0; 59643436SmsmithSYSCTL_INT(_kern_shutdown, OID_AUTO, poweroff_delay, CTLFLAG_RW, 59743436Smsmith &poweroff_delay, 0, ""); 59843436Smsmith 59943436Smsmithstatic void poweroff_wait(int howto, void *unused) 60043436Smsmith{ 60143436Smsmith if(!(howto & RB_POWEROFF) || poweroff_delay <= 0) 60243436Smsmith return; 60343436Smsmith DELAY(poweroff_delay * 1000); 60443436Smsmith} 60543436Smsmith 60643436Smsmith/* 60743436Smsmith * XXX OK? This implies I know SHUTDOWN_PRI_LAST > SHUTDOWN_PRI_FIRST 60843436Smsmith */ 60943436Smsmithstatic void poweroff_conf(void *unused) 61043436Smsmith{ 61143436Smsmith at_shutdown_pri(poweroff_wait, NULL, SHUTDOWN_FINAL, SHUTDOWN_PRI_FIRST); 61243436Smsmith} 61343436Smsmith 61443436SmsmithSYSINIT(poweroff_conf, SI_SUB_INTRINSIC, SI_ORDER_ANY, poweroff_conf, NULL) 615