[SOURCE] shutdownx 1.0: alternate shutdown util with APM pow

bridged with qdn.public.qnxrtp.x86
Post Reply
Guest

[SOURCE] shutdownx 1.0: alternate shutdown util with APM pow

Post by Guest » Thu Oct 09, 2003 5:39 pm

Stock "shutdown" command can't power off the ATX supply.
I tried numerous things to hook APM BIOS calls during shutdown
with no success.

So I whipped up a custom shutdown command which have
some expandability, and builtin APM power-off capability.
I believe the shutdown sequence resembles that of the stock shutdown.

Usage:
/usr/sbin/shutdownx -S poweroff
Build:
make
Make a package:
make package

Note: you can't replace phshutdown, but it seems to work from Photon
without serious screws.
--
kabe

#!/bin/sh
# This is shutdownx, a shell archive (produced by GNU sharutils 4.2.1)
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 2003-10-10 02:32 JST by <kabe@sra-tohoku.co.jp>.
# Source directory was `/autofs/home/kabe/qnx/x'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 338 -rw-rw-r-- shutdownx/Makefile
# 6253 -rw-rw-r-- shutdownx/apmoff.c
# 4292 -rw-rw-r-- shutdownx/package.qpg
# 24937 -rw-rw-r-- shutdownx/shutdown.c
#
echo=echo
if touch -am -t 200112312359.59 $$.touch >/dev/null 2>&1 && test ! -f 200112312359.59 -a -f $$.touch; then
shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'
elif touch -am 123123592001.59 $$.touch >/dev/null 2>&1 && test ! -f 123123592001.59 -a ! -f 123123592001.5 -a -f $$.touch; then
shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'
elif touch -am 1231235901 $$.touch >/dev/null 2>&1 && test ! -f 1231235901 -a -f $$.touch; then
shar_touch='touch -am $3$4$5$6$2 "$8"'
else
shar_touch=:
echo
$echo 'WARNING: not restoring timestamps. Consider getting and'
$echo "installing GNU \`touch', distributed in GNU File Utilities..."
echo
fi
rm -f 200112312359.59 123123592001.59 123123592001.5 1231235901 $$.touch
#
if mkdir _sh01621; then
$echo 'x -' 'creating lock directory'
else
$echo 'failed to create lock directory'
exit 1
fi
# ============= shutdownx/Makefile ==============
if test ! -d 'shutdownx'; then
$echo 'x -' 'creating directory' 'shutdownx'
mkdir 'shutdownx'
fi
if test -f 'shutdownx/Makefile' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'shutdownx/Makefile' '(file already exists)'
else
$echo 'x -' extracting 'shutdownx/Makefile' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'shutdownx/Makefile' &&
#
#
#
X
PROGRAM=shutdownx
OBJS = shutdown.o apmoff.o
X
CFLAGS=-Wc,-Wall -DUSE_BUILTIN_APMOFF=1
CC = qcc
CCLINK=$(CC)
X
all: $(PROGRAM)
X
$(PROGRAM): $(OBJS)
X $(CCLINK) -o $@ $(OBJS) $(LDFLAGS)
X
clean::
X rm -f *.o a.out core
X rm -f $(PROGRAM)
X
package: package.qpg $(PROGRAM)
X packager -f . package.qpg
clean::
X rm -f *.qpm *.qpk
X rm -f *.qpr
SHAR_EOF
(set 20 03 09 04 20 55 05 'shutdownx/Makefile'; eval "$shar_touch") &&
chmod 0664 'shutdownx/Makefile' ||
$echo 'restore of' 'shutdownx/Makefile' 'failed'
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'shutdownx/Makefile'`"
test 338 -eq "$shar_count" ||
$echo 'shutdownx/Makefile:' 'original size' '338,' 'current size' "$shar_count!"
fi
# ============= shutdownx/apmoff.c ==============
if test -f 'shutdownx/apmoff.c' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'shutdownx/apmoff.c' '(file already exists)'
else
$echo 'x -' extracting 'shutdownx/apmoff.c' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'shutdownx/apmoff.c' &&
/*
X * apmoff.c
X * APM power shutdown
X *
X * for QNX RTP(x86)
X */
#if __QNXNTO__ && __X86__
/* Ok, QNXRTP on x86 platform */
#else
#error Only for x86 architecture.
#endif
X
#if USE_BUILTIN_APMOFF /*{*/
X
#include <stdio.h>
#include <sys/types.h> /*u_short*/
#include <string.h> /*strcmp*/
#include <x86/v86.h>
X
/*NetBSD-ish defines*/
/*APM*/
#define APM_SYSTEM_BIOS 0x15 /*int 15h*/
#define APM_DEV_APM_BIOS 0x0000 /*for BX in int 15h BIOS call*/
#define APM_INSTALLATION_CHECK 0x5300 /*for AX*/
#define APM_32BIT_CONNECT 0x5303
#define APM_DISCONNECT 0x5304
#define APM_SET_POWER_STATE 0x5307
#define APM_GET_POWER_STATUS 0x530a
#define APM_DRIVER_VERSION 0x530e /*set version of this driver*/
#define APM_GET_CAPABILITIES 0x5310
struct {
X u_short apm_flags;
X u_short apm_version; /* BCD xx.xx */
X u_short apm_driver_version; /* BCD xx.xx */
} apm_info;
X
/* for struct _v86reg */
#define PSL_C 1 /* carry flag mask */
X
#define BH(reg) ((((reg).ebx)>>8)&0xff)
#define BL(reg) (((reg).ebx)&0xff)
#define CH(reg) ((((reg).ecx)>>8)&0xff)
#define CL(reg) (((reg).ecx)&0xff)
X
X
#define DPRINTF(x) if (option.verbose>=2) { printf x; } /**/
X
static struct {
X int verbose;
X int dryrun;
X enum {Standby=1, Suspend=2, Off=3} setstate; /* CX of Set Power State */
X int wait_sigterm;
} option = {
X .verbose = 0,
X .dryrun = 0,
X .setstate = Off,
X .wait_sigterm = 0
};
X
/* apmoff(): connect and issue APM power state command
X * verbose_level: >=1 for verbosity
X * next_state: "off" "standby" "suspend"
X * dryrun: boolean; nonzero to do nothing
X */
int
apmoff(int verbose_level, char *next_state, int dryrun)
{
X struct _v86reg reg;
X int s;
X
X option.verbose = verbose_level;
X option.setstate = Off; /* default */
X option.dryrun = dryrun;
X
X if (!stricmp(next_state, "standby")) {
X option.setstate = Standby;
X } else
X if (!stricmp(next_state, "suspend")) {
X option.setstate = Suspend;
X }
X
X /* installation check */
X DPRINTF(("APM: Installation Check\n"));
X memset(&reg, 0, sizeof(reg));
X reg.eax = APM_INSTALLATION_CHECK; /*APM Installation Check*/
X reg.ebx = APM_DEV_APM_BIOS; /*APM BIOS*/
X s = _intr_v86(APM_SYSTEM_BIOS, &reg, NULL,0);
X if (s || reg.efl&PSL_C) { fprintf(stderr,"error on _intr_v86\n"); return s; }
X /* result:
X * AH.AL: APM version number (BCD)
X * BH.BL: "PM"
X * CX: 0 has16bit
X * 1 has32bit
X * 2 Idle slows CPU
X * 3 disable
X * 4 disengage
X */
X DPRINTF((" APM Supported: %s\n", (reg.efl&PSL_C)?"no":"yes"));
X apm_info.apm_version = reg.eax;
X apm_info.apm_flags = reg.ecx;
X DPRINTF((" BX signature=<%c%c>%s\n",
X (int)BH(reg), (int)BL(reg),
X ((reg.ebx&0xffff)==('P'<<8)+'M')?" (ok)":" (MISMATCH)"));
X DPRINTF((" BIOS APM version %x.%x\n",
X (apm_info.apm_version>>8)&255, apm_info.apm_version&255 ));
X if(apm_info.apm_flags & 1) { DPRINTF((" 16bit mode interface\n")); }
X if(apm_info.apm_flags & 2) { DPRINTF((" 32bit mode interface\n")); }
X if(apm_info.apm_flags & 4) { DPRINTF((" Slow CPU on Idle\n")); }
X DPRINTF((" BIOS PM %s\n", (apm_info.apm_flags&8)?"disabled":"enabled"));
X DPRINTF((" BIOS PM %s\n", (apm_info.apm_flags&16)?"disengaged":"engaged"));
X
X if ((reg.ebx&0xffff) != ('P'<<8)+'M') {
X fprintf(stderr, "APM is not supported on this machine.\n");
X return 1;
X }
X
X /* disconnect first, in case somebody was connected */
X memset(&reg, 0, sizeof(reg));
X reg.eax = APM_DISCONNECT;
X reg.ebx = APM_DEV_APM_BIOS; /*APM BIOS*/
X s = _intr_v86(APM_SYSTEM_BIOS, &reg, NULL,0);
X if (s) { fprintf(stderr,"error on _intr_v86\n"); return s; }
X /*if (reg.efl&PSL_C) { fprintf(stderr, "APM: disconnect failed\n"); return 1; }*/
X
X /* connect 32 */
X DPRINTF(("APM: Connect32\n"));
X memset(&reg, 0, sizeof(reg));
X reg.eax = APM_32BIT_CONNECT;
X reg.ebx = APM_DEV_APM_BIOS; /*APM BIOS*/
X s = _intr_v86(APM_SYSTEM_BIOS, &reg, NULL,0);
X if (s) { fprintf(stderr,"error on _intr_v86\n"); exit(s); }
X if (reg.efl&PSL_C) {
X fprintf(stderr, "APM: Connect32 failed (code 0x%02lX)\n", reg.eax);
X return 1;
X }
X /*
X * AX: code32 realmode CS
X * (E)BX: entry point offset
X * CX: code16 realmode CS
X * ESI: 16 code32 length-1
X * 16 code16 length-1
X * DX: data realmode DS
X * DI: data length
X */
X DPRINTF((" 32Bit entry=%04X:%04X (len %04X+1)\n", (int)reg.eax, (int)reg.ebx, (int)(reg.esi&0xffff)));
X DPRINTF((" 16Bit entry=%04X:%04X (len %04X+1)\n", (int)reg.ecx, (int)reg.ebx, (int)((reg.esi>>16)&0xffff)));
X DPRINTF((" Data region=%04X:( 0) (len %04X+1)\n", (int)reg.edx, (int)reg.edi));
X
X /* set my version (needed to access 1.1- BIOS calls) (APM 1.1) */
X apm_info.apm_driver_version = 0x0101; /* set to APM 1.1 */
X DPRINTF(("APM: set Driver Version to %x.%x\n",
X (apm_info.apm_driver_version>>8)&255, apm_info.apm_driver_version&255
X ));
X memset(&reg, 0, sizeof(reg));
X reg.eax = APM_DRIVER_VERSION;
X reg.ebx = APM_DEV_APM_BIOS;
X reg.ecx = apm_info.apm_driver_version;
X s = _intr_v86(APM_SYSTEM_BIOS, &reg, NULL,0);
X /* result:
X * AH.AL: APM connection version
X */
X if (s) { fprintf(stderr,"error on _intr_v86\n"); return s; }
X if (reg.efl&PSL_C) {
X fprintf(stderr, "APM: set Driver Version failed (code 0x%02X)\n", (int)(reg.eax>>8));
X /* what should I do? */
X /* some BIOS seems to accept 1.1 calls regardess of error,
X * so just continue for now */
X /*exit(1);*/
X } else {
X DPRINTF((" Connected as version %x.%x\n", (int)((reg.eax>>8)&255), (int)(reg.eax&255) ));
X }
X
X /* set power status (to OFF etc) (APM 1.1) */
X DPRINTF(("APM: Set Power State (All, %d)\n", option.setstate));
X memset(&reg, 0, sizeof(reg));
X reg.eax = APM_SET_POWER_STATE;
X reg.ebx = APM_DEV_APM_BIOS +0x0001; /* all devices */
X reg.ecx = option.setstate;
X if (!option.dryrun) {
X s = _intr_v86(APM_SYSTEM_BIOS, &reg, NULL,0);
X }
X if (s) { fprintf(stderr,"error on _intr_v86\n"); return s; }
X if (reg.efl&PSL_C) {
X fprintf(stderr, "APM: Set Power State failed (code 0x%02X)\n", (int)(reg.eax>>8));
X goto off_fail;
X }
X /* NOTREACHED */
off_fail:
X goto disconnect;
X
X return 0;
X
disconnect:
X DPRINTF(("APM: Disconnect\n"));
X memset(&reg, 0, sizeof(reg));
X reg.eax = APM_DISCONNECT;
X reg.ebx = APM_DEV_APM_BIOS; /*APM BIOS*/
X s = _intr_v86(APM_SYSTEM_BIOS, &reg, NULL,0);
X if (s) { fprintf(stderr,"error on _intr_v86\n"); return s; }
X return s;
}
X
#endif /*USE_BUILTIN_APMOFF }*/
SHAR_EOF
(set 20 03 09 04 20 55 05 'shutdownx/apmoff.c'; eval "$shar_touch") &&
chmod 0664 'shutdownx/apmoff.c' ||
$echo 'restore of' 'shutdownx/apmoff.c' 'failed'
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'shutdownx/apmoff.c'`"
test 6253 -eq "$shar_count" ||
$echo 'shutdownx/apmoff.c:' 'original size' '6253,' 'current size' "$shar_count!"
fi
# ============= shutdownx/package.qpg ==============
if test -f 'shutdownx/package.qpg' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'shutdownx/package.qpg' '(file already exists)'
else
$echo 'x -' extracting 'shutdownx/package.qpg' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'shutdownx/package.qpg' &&
<QPG:Generation>
X <QPG:Options>
X <QPG:User unattended="yes" verbosity="2" listfiles="yes"/>
X <QPG:Defaults type="qnx_package"/>
X <QPG:Source></QPG:Source>
X <QPG:Release number="+"/>
X <QPG:Build></QPG:Build>
X <QPG:FileSorting strip="no"/>
X <QPG:Package targets="combine"/>
X <QPG:Repository generate="yes"/>
X <QPG:FinalDir></QPG:FinalDir>
X <QPG:Cleanup></QPG:Cleanup>
X </QPG:Options>
X
X <QPG:Responsible>
X <QPG:Company></QPG:Company>
X <QPG:Department></QPG:Department>
X <QPG:Group></QPG:Group>
X <QPG:Team></QPG:Team>
X <QPG:Employee></QPG:Employee>
X <QPG:EmailAddress></QPG:EmailAddress>
X </QPG:Responsible>
X
X <QPG:Values>
X <QPG:Files>
X <QPG:Add file="./shutdownx" install="/opt/sbin/"/>
X </QPG:Files>
X
X <QPG:PackageFilter>
X <QPM:PackageManifest>
X <QPM:PackageDescription>
X <QPM:PackageType>Application</QPM:PackageType>
X <QPM:PackageRepository></QPM:PackageRepository>
X <QPM:FileVersion>1.9</QPM:FileVersion>
X </QPM:PackageDescription>
X
X <QPM:ProductDescription>
X <QPM:ProductName>shutdownx</QPM:ProductName>
X <QPM:ProductIdentifier>shutdownx</QPM:ProductIdentifier>
X <QPM:ProductEmail></QPM:ProductEmail>
X <QPM:VendorName>Public</QPM:VendorName>
X <QPM:VendorInstallName>vega</QPM:VendorInstallName>
X <QPM:VendorURL></QPM:VendorURL>
X <QPM:VendorEmbedURL></QPM:VendorEmbedURL>
X <QPM:VendorEmail></QPM:VendorEmail>
X <QPM:AuthorName>Taketo Kabe</QPM:AuthorName>
X <QPM:AuthorURL></QPM:AuthorURL>
X <QPM:AuthorEmbedURL></QPM:AuthorEmbedURL>
X <QPM:AuthorEmail>kabe#sra-tohoku.co.jp</QPM:AuthorEmail>
X <QPM:ProductIconSmall></QPM:ProductIconSmall>
X <QPM:ProductIconLarge></QPM:ProductIconLarge>
X <QPM:ProductDescriptionShort>configurable shutdown</QPM:ProductDescriptionShort>
X <QPM:ProductDescriptionLong>shutdownx is an alternate shutdown command, which could have configurable order of terminating the processes, and optional powerdown feature.</QPM:ProductDescriptionLong>
X <QPM:ProductDescriptionURL></QPM:ProductDescriptionURL>
X <QPM:ProductDescriptionEmbedURL></QPM:ProductDescriptionEmbedURL>
X </QPM:ProductDescription>
X
X <QPM:ReleaseDescription>
X <QPM:ReleaseVersion>1.0</QPM:ReleaseVersion>
X <QPM:ReleaseUrgency>Low</QPM:ReleaseUrgency>
X <QPM:ReleaseStability>Stable</QPM:ReleaseStability>
X <QPM:ReleaseNoteMinor></QPM:ReleaseNoteMinor>
X <QPM:ReleaseNoteMajor></QPM:ReleaseNoteMajor>
X <QPM:CountryExclude></QPM:CountryExclude>
X <QPM:ReleaseCopyright>QNX Open Community License</QPM:ReleaseCopyright>
X </QPM:ReleaseDescription>
X
X <QPM:ContentDescription>
X <QPM:ContentTopic xmlmultiple="true">System/Startup and Shutdown</QPM:ContentTopic>
X <QPM:ContentKeyword>shutdown,poweroff,halt</QPM:ContentKeyword>
X <QPM:TargetOS>qnx6</QPM:TargetOS>
X <QPM:HostOS>none</QPM:HostOS>
X <QPM:DisplayEnvironment xmlmultiple="true">None</QPM:DisplayEnvironment>
X <QPM:TargetAudience xmlmultiple="true">Administrator</QPM:TargetAudience>
X </QPM:ContentDescription>
X
X <QPM:LicenseUrl></QPM:LicenseUrl>
X </QPM:PackageManifest>
X </QPG:PackageFilter>
X
X <QPG:PackageFilter proc="none" target="none">
X <QPM:PackageManifest>
X <QPM:ProductInstallationDependencies>
X <QPM:ProductRequirements></QPM:ProductRequirements>
X </QPM:ProductInstallationDependencies>
X </QPM:PackageManifest>
X </QPG:PackageFilter>
X
X <QPG:PackageFilter proc="x86" target="none">
X <QPM:PackageManifest>
X <QPM:ProductInstallationDependencies>
X <QPM:ProductRequirements></QPM:ProductRequirements>
X </QPM:ProductInstallationDependencies>
X </QPM:PackageManifest>
X </QPG:PackageFilter>
X </QPG:Values>
</QPG:Generation>
SHAR_EOF
(set 20 03 09 04 20 50 28 'shutdownx/package.qpg'; eval "$shar_touch") &&
chmod 0664 'shutdownx/package.qpg' ||
$echo 'restore of' 'shutdownx/package.qpg' 'failed'
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'shutdownx/package.qpg'`"
test 4292 -eq "$shar_count" ||
$echo 'shutdownx/package.qpg:' 'original size' '4292,' 'current size' "$shar_count!"
fi
# ============= shutdownx/shutdown.c ==============
if test -f 'shutdownx/shutdown.c' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'shutdownx/shutdown.c' '(file already exists)'
else
$echo 'x -' extracting 'shutdownx/shutdown.c' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'shutdownx/shutdown.c' &&
/*
X * alternate shutdown command
X *
X * Goodies:
X * Configurable order,signal,timeout of killing processes
X * Able to invoke external program during shutdown
X * Builtin APM poweroff
X *
X * shutdown3: use shutdown.conf
X * each line of shutdown.conf is member of linked list
X * which in turn has linked list of process belonging to the line
X */
static const char _rcsid[] = "$Id: shutdown.c,v 1.13 2003/10/09 01:27:12 kabe Exp $";
X
/*#define USE_BUILTIN_APMOFF 1*/
X
#include <stdio.h>
#include <unistd.h> /*getopt*/
#include <ctype.h> /*tolower*/
#include <string.h> /*strdup*/
#include <stdlib.h> /*strtoul*/
#include <libgen.h> /*basename*/
#include <dirent.h> /*DIR,opendir,readdir*/
#include <fcntl.h> /* O_RDONLY*/
#include <malloc.h>
#include <signal.h> /*SIGTERM*/
#include <sys/neutrino.h>
#include <sys/procfs.h>
#include <sys/procmgr.h> /*procmgr_daemon*/
#include <sys/sysmgr.h> /*sysmgr_reboot*/
#include <spawn.h> /*spawn*/
X
struct {
X int verbose;
X int dryrun;
} global = {
X .verbose = 1, /* TODO offset */
X .dryrun =0,
};
/* verbose level:
X * quiet(0)
X * default(1)
X * -v (2)
X */
X
/*
X * Format considerations:
X * ordering
X * prefix matching (longest match prevail?)
X * desired order: :other / * / :daemon / ?*
X * signal spec
X * wait spec
X * "catch-all" for apps
X * "sid==1" for daemon
X * stop & halt if "-S system"
X * wait before reboot?
X * shutdown level (system/user/photon)
X * invoke external program
X * comment line
X * TODO
X * get rid or make global.verbose more sane (currently off by one)
X * don't use /bin/sh when no glob/redirect/quote needed
X * (spawned shellscript has "/bin/sh" as argv[0]; how to specify?)
X * support for user-defined shutdown mode (predicate)
X * don't overload ":pause" for sleep and prompt
X * check return of kill for no privilege
X * read pidfile?
X * -S user|photon
X * update utmp? (stock shutdown says it does but actually doesn't)
X * =kill procs invoked by :sh (need to wait?) -> use :rescan explicitly...
X * parallel wait during kill
X */
X
static const char default_shutdown_conf[] =
X "# Builtin default shutdown.conf\n"
X "#:sh /etc/rc.d/rc.shutdown %S\n"
X ":echo Shutting down apps...\n"
X "# kill init first to prevent respawning login\n"
X "init sig=SIGINT\n"
/* tinit blocks HUP&TERM, will be zombie on INT|KILL */
X "tinit sig=SIGINT,wait=800\n"
X "# shells\n"
X "-* sig=SIGHUP\n"
X "# other apps\n"
X ":other\n"
X "# daemon\n"
X ":echo Shutting down daemons...\n"
X /*"#:select name=* sid=1\n"*/
X ":daemon\n"
X "# fsys\n"
X ":echo Syncing filesystems...\n"
X ":sync\n"
X ":echo Shutting down filesystems....\n"
X "devb-* wait=8000\n"
X "devf-* wait=8000\n"
X "pipe\n"
X ":if system :echo-n Shutdown complete.\\nPress Enter to reboot...\n"
X ":if system :pause\n"
X "# Photon\n"
X "photon\n"
X "# disp drivers\n"
X ":echo Shutting down display drivers...\n"
X "phfont*\n"
X "fontsleuth\n"
X "pwm\n"
X "devg-*\n"
X "devi-*\n"
X "io-gr*\n"
X "fs-*\n"
X "pci-*\n"
X "devc-con nokill # otherwise messages will stop\n"
X "devc-*\n"
#if USE_BUILTIN_APMOFF
X ":if poweroff :echo Power down...\n"
X ":if poweroff :pause wait=300 # yield to console driver\n"
X ":if poweroff :apmoff\n"
#else
X "apmdriver sig=SIGPWR\n" /* not implemented */
#endif
X ":echo Rebooting...\n"
X ":pause wait=1000 # needs to yield to console driver\n"
X ":reboot\n"
;
X
void do_shutdown(const char *shutdown_conf, const char *trues[]);
X
int
main(int argc, char *argv[])
{
X int c;
X char *shutdown_conf = NULL;
X struct {
X int count;
X const char *a[8];
X } trues = {1, {"reboot", NULL} };
X const char **shutdown_mode = &trues.a[0];
X
X while (EOF != (c = getopt(argc, argv, "bpDS:vqc:d"))) {
X switch (c) {
X case 'b': /* no reboot; synonym of -S system */
X *shutdown_mode = "system"; break;
X case 'p': /* poweroff (from NetBSD) */
X *shutdown_mode = "poweroff"; break;
X case 'S':
X switch(tolower(optarg[0])) {
X case 'r': *shutdown_mode = "reboot"; break;
X case 's': *shutdown_mode = "system"; break;
X case 'p': *shutdown_mode = "poweroff"; break;
X default:
X fprintf(stderr, "Invalid shutdown type (%s).\n", optarg);
X exit(1);
X }
X break;
X case 'D': /* dryrun */
X global.dryrun++;
X break;
X case 'v': /* verbose */
X global.verbose++;
X break;
X case 'c': /* specify shutdown.conf */
X shutdown_conf = optarg;
X break;
X case 'd': /* dump default conf */
X exit(fputs(default_shutdown_conf, stdout));
X break;
X /*case 'n': node*/
X /*case 'f': fast*/
X case 'q': /* quiet */
X global.verbose=0;
X break;
X default:
X return execlp("use", "use", argv[0], NULL);
X /*NOTREACHED*/
X }/*esac*/
X }/*wend getopt*/
X
X /* add some predicate values */
X if (global.dryrun && trues.count<8-1) trues.a[trues.count++]="dryrun";
X if (global.verbose>=2 && trues.count<8-1) trues.a[trues.count++]="verbose";
X if (global.verbose==0 && trues.count<8-1) trues.a[trues.count++]="quiet";
X trues.a[trues.count] = NULL;
X
X /* now, do the shutdown things */
X do_shutdown(shutdown_conf, trues.a);
X return 0;
}
X
/*
X * Escape "\n" into "\n" and copy into "to"
X * XXX target buffer overflow
X */
char *
unescape(/*return*/ char *to, register const char *from)
{
X if (to == NULL) {
X fprintf(stderr,"unescape: target NULL??\n");
X return to;
X }
X while (*from) {
X if (*from == '\\') {
X from++;
X if (isdigit(*from)) {
X /* \0 \14 */
X int oct = (*from++)-'0';
X if (isdigit(*from)) oct=(oct<<3)+(*from++)-'0';
X if (isdigit(*from)) oct=(oct<<3)+(*from++)-'0';
X *to++ = oct;
X continue;
X }
X
X switch(*from) {
X case 'n': *to++ = '\n'; from++; continue;
X }
X }
X *to++ = *from++;
X }
X *to = '\0'; /*terminate*/
X return to;
}
X
#define I(x) "\033[4m" x "\033[0m"
#define B(x) "\033[1m" x "\033[0m"
static const char _usage[] __attribute__((section("QNX_usage"),unused)) =
"%C - alternate shutdown utility\n"
"\n"
"%C [-vDd] [-S system|reboot|poweroff] [-c shutdown.conf]\n"
"Options:\n"
" -v Verbose.\n"
" -D Dryrun.\n"
" -S system|reboot|poweroff\n"
" Final action. Default is \"reboot\".\n"
#if USE_BUILTIN_APMOFF
" \"poweroff\" uses a builtin APM driver facility;\n"
" do not use it if you have a dedicated APM resmgr running.\n"
#endif
" -c FILE\n"
" Use alternate shutdown.conf other than the builtin default.\n"
" -d Dump out the builtin shutdown.conf.\n"
"\n"
"shutdown.conf format:\n"
" # COMMENT\n"
" Ignored.\n"
" NAME [sig=SIGxxxx] [wait=N] [nokill]\n"
" Send SIGTERM (or SIGxxxx) to a process named NAME\n"
" and wait for N millisecs (default 5000) before sending SIGKILL.\n"
" nokill designates not to terminate this process.\n"
" NAME* [sig=SIGxxxx] [wait=N] [nokill]\n"
" Ditto, but prefix match.\n"
" :echo MESSAGE\n"
" Output MESSAGE. \\n is translated to newline.\n"
" :echo-n MESSAGE\n"
" Output MESSAGE without trailing newline.\n"
" :daemon [sig=SIGxxxx] [wait=N]\n"
" Select all processes with session-ID==1.\n"
" :other [sig=SIGxxxx] [wait=N]\n"
" Select all other processes.\n"
" :pause\n"
" Wait for Enter key to be pressed.\n"
" :pause wait=N\n"
" Wait for N millisecs.\n"
" :sh COMMAND ARGS...\n"
" Invoke an external command.\n"
" :rescan\n"
" Rescan the process table for new process.\n"
" You will need this if previous :sh forked something.\n"
" :reboot\n"
" Kernel Reboot.\n"
#if USE_BUILTIN_APMOFF
" :apmoff\n"
" Issue power-off to APM BIOS.\n"
#endif
" :if [!]PREDICATE COMMAND\n"
" Simple \"if\". COMMAND can be any of the above.\n"
" Valid PREDICATE will be 0,1, or option parameter to -S. \n"
" All others evaluate to false.\n"
" Example:\n"
" :if system :echo Today is a good day to die.\n"
"\n"
"See also:\n shutdown(1), sysmgr_reboot(2)\n"
"\n";
#undef B
#undef I
X
/* A struct representing a single shutdown.conf line */
struct confline_t {
X struct confline_t *cf_next;
X char *cf_spec; /* filename prefix, or message */
X enum {
X CONF_EXACT, CONF_PREFIX, CONF_DAEMON, CONF_OTHER,
X CONF_ECHO,
X CONF_PAUSE,
X CONF_RESCAN,
X CONF_SYNC,
X CONF_REBOOT,
X CONF_APMOFF,
X CONF_SHELL,
X } cf_type;
X int cf_wait; /* in millisecs */
X int cf_signal; /* signal to send */
X struct procent_t *cf_memberproc; /* list of processes */
};
X
/* "cfs_" is for bunch of conflines, "cf_" is for a single confline */
X
/*
X * cfs_parse_shutdown_conf:
X * create a list of conflines from shutdown.conf
X *
X * conf: filename of shutdown.conf. NULL uses the builtin default table.
X * trues: NULL terminated string array which deemed "true" in predicate
X * return: linked list of whole conflines
X */
struct confline_t *
cfs_parse_shutdown_conf(const char * conf, const char * const trues[])
{
X struct {
X enum {SRC_FILE,SRC_STRING} srctype;
X union {
X const char *confstr;
X FILE *fid;
X } d;
X int linenum;
X } source;
X struct confline_t *conflist = NULL; /* return */
X struct confline_t **conflist_tailv = &conflist; /* where to connect the new line */
X
X if (conf==NULL) {
X source.srctype = SRC_STRING;
X source.d.confstr = default_shutdown_conf;
X source.linenum = 0;
X } else {
X source.srctype = SRC_FILE;
X source.d.fid = fopen(conf, "r");
X if (!source.d.fid) {
X fprintf(stderr, "Cannot open %s\n", conf);
X return NULL;
X }
X source.linenum = 0;
X }
X
X while (1 /*fgets(fid) != NULL*/) {
X struct confline_t newconf;
X char buf[1024];
X char *token, *_keeper;
X
X if (source.srctype == SRC_FILE) {
X char *p;
X if (NULL == fgets(buf, sizeof buf, source.d.fid)) break /*while*/;
X if ((p =strrchr(buf, '\r'))) *p = '\0';
X if ((p =strrchr(buf, '\n'))) *p = '\0';
X source.linenum++;
X } else
X if (source.srctype == SRC_STRING) {
X char *ptr; size_t len;
X ptr = strchr(source.d.confstr,'\n');
X if (ptr==NULL) /* no line */ break /*while*/;
X len = ptr-source.d.confstr;
X if (len > (sizeof buf)-1) len = (sizeof buf)-1; /*clamp*/
X memcpy(buf, source.d.confstr, len);
X buf[len] = '\0'; /*terminate,clobbering \n*/
X source.d.confstr = ptr+1; /*next line*/
X source.linenum++;
X } else {
X /* can't happen */
X fprintf(stderr, "barf,barf,barf\n"); exit(1);
X }
X /* now buf[] holds a conf line */
X
X /* set defaults...*/
X newconf.cf_spec = NULL;
X newconf.cf_type = CONF_EXACT;
X newconf.cf_wait = 5000; /* 5000ms */
X newconf.cf_signal = SIGTERM;
X newconf.cf_memberproc = NULL;
X newconf.cf_next = NULL;
X
X token = strchr(buf, '#');
X if (token) *token='\0'; /* clobber comment */
X /* initial token */
X token = strtok_r(buf, " \t", &_keeper);
X if (NULL == token) goto _next_line;
X
X /* build newconf from the line */
X
X _reparse_top:
X if (token[0] == ':') {
X /* command line */
X int echo_newline = 1;
X if ((echo_newline=1, !strcmp(token, ":echo")) ||
X (echo_newline=0, !strcmp(token, ":echo-n"))) {
X char *str = strtok_r(NULL, "", &_keeper); /*get all remaining*/
X newconf.cf_spec = malloc(strlen(str)+2);
X newconf.cf_type = CONF_ECHO;
X newconf.cf_wait = 0;
X unescape(newconf.cf_spec, str);
X if (echo_newline) strcat(newconf.cf_spec, "\n");
X } else
X if (!strcmp(token, ":sh")) {
X /* external command */
X char *str = strtok_r(NULL, "", &_keeper);
X newconf.cf_spec = malloc(strlen(str)+2);
X newconf.cf_type = CONF_SHELL;
X newconf.cf_wait = 0;
X unescape(newconf.cf_spec, str);
X } else
X if (!strcmp(token, ":other")) {
X newconf.cf_type = CONF_OTHER;
X } else
X if (!strcmp(token, ":daemon")) {
X newconf.cf_type = CONF_DAEMON;
X } else
X if (!strcmp(token, ":pause")) {
X newconf.cf_type = CONF_PAUSE;
X newconf.cf_wait = 0;
X } else
X if (!strcmp(token, ":rescan")) {
X newconf.cf_type = CONF_RESCAN;
X newconf.cf_wait = 20; /* wait a bit to yield */
X } else
X if (!strcmp(token, ":sync")) {
X newconf.cf_type = CONF_SYNC;
X newconf.cf_wait = 50; /* wait a bit to yield */
X } else
X if (!strcmp(token, ":reboot")) {
X newconf.cf_type = CONF_REBOOT;
X newconf.cf_wait = 0;
X } else
X if (!strcmp(token, ":apmoff")) {
X newconf.cf_type = CONF_APMOFF;
X newconf.cf_wait = 0;
X } else
X if (!strcmp(token, ":if")) {
X /* predicates are evaluated on parse stage */
X /* :if 1 :echo hogehoge */
X int apple;
X int invert = 0;
X token = strtok_r(NULL, " \t", &_keeper);
X while (token[0]=='!') {
X invert = !invert;
X token++;
X if (*token=='\0') /* stray "! " */
X token = strtok_r(NULL, " \t", &_keeper);
X }
X if (!strcmp(token, "0"))
X apple = 0;
X else if (!strcmp(token, "1"))
X apple = 1;
X else {
X int i;
X /* textual predicate */
X apple = 0;
X for (i=0; trues; i++) {
X if (!strcmp(trues, token)) {
X /* matched */
X apple = 1;
X break;
X }
X }/*next i*/
X }
X if (invert) apple = !apple;
X if (apple) {
X /*predicate true*/
X if (global.verbose>=3) printf("Line %d: predicate true\n", source.linenum);
X _reparse_command:
X token = strtok_r(NULL, " \t", &_keeper);
X goto _reparse_top;
X } else {
X /* predicate false */
X if (global.verbose>=3) printf("Line %d: predicate false\n", source.linenum);
X goto _next_line;
X }
X /*NOTREACHED*/
X } else {
X fprintf(stderr, "Line %d: Unknown directive \"%s\"; ignoring\n", source.linenum, token);
X goto _next_line;
X }
X } else {
X /* procname glob match */
X char *p;
X newconf.cf_spec = strdup(token);
X p = strchr(newconf.cf_spec, '*');
X if (p) {
X /* prefix match*/
X /* devc-*
X * ^p */
X *p = '\0'; /* clobber '*' */
X newconf.cf_type = CONF_PREFIX;
X } else {
X /* exact match */
X newconf.cf_type = CONF_EXACT;
X }
X }
X
X /* _get_options: */
X /* tinit sig=SIGINT
X * ^ptr */
X while (NULL != (token = strtok_r(NULL, " \t,", &_keeper))) {
X /* tinit sig=SIGINT wait=8000
X * ^ptr ^ptr */
X if (!strncmp(token, "sig=", 4)) {
X const struct { char *name; int sig; } *st,
X sigtab[] = {
X { "HUP", SIGHUP },
X { "INT", SIGINT },
X { "TERM", SIGTERM },
X { "KILL", SIGKILL },
X { "PWR", SIGPWR },
X { NULL, 0 }
X };
X /* signal */
X token+=4; /* skip "sig=" */
X if (!strncmp(token, "SIG", 3)) token+=3; /* skip "SIG" prefix */
X for (st=sigtab; st->name; st++ ) {
X if (!strcmp(token, st->name)) {
X newconf.cf_signal = st->sig;
X break;
X }
X }
X if (!st->name) { /*falloff*/
X /* numeric? */
X fprintf(stderr, "Line %d: Unrecognized sig=%s directive\n", source.linenum, token);
X goto _next_line;
X }
X }/*sig=*/
X else if (!strncmp(token, "wait=", 5)) {
X /* wait */
X token+=5;
X newconf.cf_wait = strtoul(token,&token,0);
X } else if (!strcmp(token, "nokill")) {
X newconf.cf_signal = 0;
X } else {
X fprintf(stderr, "Line %d: Unrecognized option \"%s\"\n", source.linenum, token);
X goto _next_line;
X }
X }/*wend options*/
X
X /* _got_confline: */
X {
X struct confline_t *aconf = malloc(sizeof (struct confline_t));
X if (aconf==NULL) { fprintf(stderr, "Out of memory\n"); exit(1); }
X *aconf = newconf;
X /* append to list (not prepend) */
X *conflist_tailv = aconf;
X conflist_tailv = &aconf->cf_next;
X }
X _next_line: ;
X }/* wend fgets*/
X
X if (source.srctype==SRC_FILE && source.d.fid) fclose(source.d.fid);
X
X return conflist;
}
X
struct procent_t {
X struct procent_t *pe_next;
X char *pe_name;
X pid_t pe_pid;
X pid_t pe_sid;
};
X
/* Create new procent from pid
X * return: new procent
X */
struct procent_t *
ent_frompid(pid_t pid)
{
X struct procent_t *ent;
X /*filedes_t*/int fd;
X char buf[512];
X procfs_info pinfo;
X daddr_t loc_argv0;
X
X sprintf(buf, "/proc/%lu/as", (long)pid);
X
X fd = open(buf, O_RDONLY);
X if (fd < 0) return NULL;
X
X if (devctl(fd, DCMD_PROC_INFO, &pinfo, sizeof pinfo, NULL) != 0 ||
X lseek(fd, pinfo.initial_stack+sizeof(int) /*&argv*/, SEEK_SET) == -1 ||
X read(fd, &loc_argv0, sizeof loc_argv0) /*argv*/ != sizeof loc_argv0 ||
X lseek(fd, loc_argv0, SEEK_SET) /*argv[0]*/ == -1 ||
X (read(fd, buf, sizeof buf)) <= 0 ||
X 0) {
X close(fd);
X return NULL;
X }
X
X close(fd);
X
X ent = malloc(sizeof(struct procent_t));
X if (ent == NULL) {
X fprintf(stderr, "Insufficient memory to build process list.\n");
X exit(1);
X }
X buf[(sizeof buf)-1] = '\0';
X ent->pe_name = strdup(basename(buf));
X ent->pe_pid = pid;
X ent->pe_sid = pinfo.sid;
X ent->pe_next = NULL;
X
X return ent;
}
X
void
ent_free(struct procent_t *this)
{
X free(this->pe_name);
}
X
/*
X * cfs_assign_proc_to_conf:
X * Assigns the given process to a matching line within conflines.
X * confs: linked lines of whole conflines
X * ent: process description
X * return: (assigned confline); NULL if no matching line
X */
struct confline_t *
cfs_assign_proc_to_conf(struct confline_t *confs, struct procent_t *ent)
{
X int matchlen = -99;
X struct confline_t *assign_to = NULL;
X
X /* Desired order for zero-length match:
X * ?* :daemon * :other
X */
X
X for ( ; confs; confs=confs->cf_next) {
X switch (confs->cf_type) {
X int n;
X case CONF_EXACT:
X n = strlen(confs->cf_spec);
X if (!strcmp(ent->pe_name, confs->cf_spec) &&
X matchlen <= n) /*is eq or better match*/ {
X assign_to = confs;
X matchlen = n;
X }
X break;
X case CONF_PREFIX:
X n = strlen(confs->cf_spec);
X if (!strncmp(ent->pe_name, confs->cf_spec, n) &&
X matchlen <= n) /* is eq or better match */ {
X assign_to = confs;
X matchlen = n;
X }
X break;
X case CONF_DAEMON:
X if (ent->pe_sid == 1 && matchlen <= 0) {
X assign_to = confs;
X matchlen = 1; /* just above "*" */
X }
X break;
X case CONF_OTHER:
X if (matchlen <= -99) {
X assign_to = confs;
X matchlen = -99;
X }
X break;
X default:
X /* ignore non-proc line */
X break;
X }/*esac*/
X }/*next confs*/
X if (NULL == assign_to) {
X fprintf(stderr, "Warning: proc <%s> won't be shut down; check shutdown.conf\n", ent->pe_name);
X return NULL;
X }
X ent->pe_next = assign_to->cf_memberproc;
X assign_to->cf_memberproc = ent;
X // printf("proc <%s> assigned to conf type %d <%s>\n", ent->pe_name, assign_to->cf_type, assign_to->cf_spec?assign_to->cf_spec:"(null)");
X return assign_to;
}
X
/* return value of cf_action_*() */
enum cf_stat { CFSTAT_OK=0, CFSTAT_RESCAN=9 };
X
static enum cf_stat
cf_action_echo(const struct confline_t *this)
{
X if (global.verbose) fputs(this->cf_spec, stdout); fflush(stdout);
X if (this->cf_wait) delay(this->cf_wait);
X return CFSTAT_OK;
}
static enum cf_stat
cf_action_shell(const struct confline_t *this)
{
X int s;
X struct inheritance inherit;
X char *nargv[4];
X if (global.verbose>=2) { printf("Invoking <%s>\n", this->cf_spec); }
X
X /* XXX: we want to enable SIGHUP,SIGINT for children... */
X inherit.flags = SPAWN_SETSIGDEF;
X sigfillset(&inherit.sigdefault);
X /* use "sh -c ...", assuming /bin/sh is still available... */
X nargv[0] = "sh"; nargv[1] = "-c"; nargv[2] = this->cf_spec;
X nargv[3] = NULL;
X s = spawn("/bin/sh", 0,NULL, &inherit, nargv, NULL);
X
X /* reload /proc/ in case it forked something;
X * but this seldom works because /proc/ isn't updated immediately */
X return CFSTAT_RESCAN;
}
static enum cf_stat
cf_action_pause(const struct confline_t *this)
{
X /* TODO: prompting and plain delay() should be a different command */
X if (this->cf_spec && global.verbose) {
X fputs(this->cf_spec, stdout); fflush(stdout);
X }
X if (this->cf_wait==0) {
X getchar();
X } else {
X delay(this->cf_wait);
X }
X return CFSTAT_OK;
}
static enum cf_stat
cf_action_rescan(const struct confline_t *this)
{
X if (this->cf_wait) delay(this->cf_wait);
X /* "this" doesn't know the whole confline_t top, so just return
X * rescan errorcode here. Toplevel cfs_ loop should catch this
X * and issue cfs_rescan_procs(whole_conf) */
X return CFSTAT_RESCAN;
}
static enum cf_stat
cf_action_sync(const struct confline_t *this)
{
X if (this->cf_wait) delay(this->cf_wait);
X if (global.verbose>=2) printf("Issue sync()\n");
X sync();
X return CFSTAT_OK;
}
static enum cf_stat
cf_action_reboot(const struct confline_t *this)
{
X if (this->cf_wait) delay(this->cf_wait);
X if (global.dryrun) return 0;
X if (geteuid() != 0) {
X fprintf(stderr, "You must be root to shutdown the system.\n");
X return 1;
X }
X if (global.verbose>=2) printf("Issue sysmgr_reboot()\n");
X sysmgr_reboot();
X /*NOTREACHED*/
X return CFSTAT_OK;
}
static enum cf_stat
cf_action_apmoff(const struct confline_t *this)
{
#if USE_BUILTIN_APMOFF
X extern int apmoff(int verbose, char *nextstate, int dryrun);
X int v;
X v = global.verbose-1; if (v<0) v=0;
X apmoff(v, "off", global.dryrun);
X /* NOTREACHED */
#else
X printf("Builtin APM not configured.\n");
#endif
X return CFSTAT_OK;
}
static enum cf_stat
cf_action_kill(const struct confline_t *this)
{
X struct procent_t *ent;
X
X for (ent = this->cf_memberproc; ent; ent=ent->pe_next) {
X int waitcount;
X
X if (global.verbose>=2) {
X printf(" %s", ent->pe_name);
X if (global.verbose>=3)
X printf(" [%lu] wait=%d sig=%d", (long)ent->pe_pid, this->cf_wait, this->cf_signal);
X printf("\n");
X }
X
X if (global.dryrun) continue;
X if (this->cf_signal==0) continue; /* nokill */
X
X kill(ent->pe_pid, this->cf_signal); /*XXX check return*/
X
X waitcount=this->cf_wait;
X while ( waitcount>0 ) {
X if ( kill(ent->pe_pid, 0) ) goto _died;
X delay(10); /* 10ms wait */
X waitcount -= 10;
X }
X if ( kill(ent->pe_pid, 0) == 0 ) {
X /* still not dead, SIGKILL it */
X printf("\t%s (%lu) not responding, sending SIGKILL...\n",
X ent->pe_name, (long)ent->pe_pid);
X kill(ent->pe_pid, SIGKILL);
X break;
X }
X _died: ;
X
X }/*next ent*/
X return CFSTAT_OK;
}
X
static enum cf_stat
cf_action(const struct confline_t *this)
{
X enum cf_stat s;
//*XXX*/if (global.verbose>=4) { printf("action line <%s> type %d\n", this->cf_spec?this->cf_spec:"(null)", this->cf_type); }
X switch (this->cf_type) {
X case CONF_ECHO:
X s = cf_action_echo(this); break;
X case CONF_PAUSE:
X s = cf_action_pause(this); break;
X case CONF_REBOOT:
X s = cf_action_reboot(this); break;
X case CONF_APMOFF:
X s = cf_action_apmoff(this); break;
X case CONF_SHELL:
X s = cf_action_shell(this); break;
X case CONF_RESCAN:
X s = cf_action_rescan(this); break;
X case CONF_SYNC:
X s = cf_action_sync(this); break;
X case CONF_EXACT: /* differences are in cfs_assign_proc_to_conf() */
X case CONF_PREFIX:
X case CONF_DAEMON:
X case CONF_OTHER:
X s = cf_action_kill(this); break;
X }/*esac*/
X return s;
}
X
/* rebuild the proclist from current /proc/ state */
struct confline_t *
cfs_rescan_procs(struct confline_t *confs)
{
X struct confline_t *cfscan;
X DIR *dir;
X struct dirent *dirent;
X pid_t mypid;
X
X /* first, clobber all procent in each line.
X * (does nothing for vanilla lines) */
X for (cfscan=confs; cfscan; cfscan=cfscan->cf_next) {
X struct procent_t *ent, *x;
X for (ent=cfscan->cf_memberproc; ent; ent=x) {
X x = ent->pe_next;
X ent_free(ent);
X }
X cfscan->cf_memberproc = NULL;
X }
X
X mypid = getpid();
X
X /* scan through /proc , and
X * distribute procs into conflines->cf_memberproc */
X dir = opendir("/proc");
X if (dir == NULL) {
X fprintf(stderr, "Unable to access procfs.\n");
X exit(1);
X }
X while (NULL != (dirent = readdir(dir))) {
X struct procent_t *ent;
X
X pid_t pid = strtoul(dirent->d_name, NULL, 0);
X if (pid == 1) continue; /* procnto */
X if (pid <= 0) continue; /* not [0-9] */
X if (pid == mypid) continue; /* don't kill myself */
X
X ent = ent_frompid(pid);
X if (NULL == ent) continue;
X cfs_assign_proc_to_conf(confs, ent);
X
X }/*wend readdir*/
X closedir(dir);
X
X return confs;
}
X
void
cfs_dokills(struct confline_t *conflines)
{
X struct confline_t *cf_scan;
X /* start killing */
X for(cf_scan=conflines; cf_scan; cf_scan=cf_scan->cf_next) {
X switch(cf_action(cf_scan)) {
X case CFSTAT_OK: break;
X case CFSTAT_RESCAN:
X //sleep(1);
X cfs_rescan_procs(conflines);
X break;
X }
X //cfs_rescan_procs(conflines); //XXXreload every time
X }
X /* return error status? */
}
X
void
do_shutdown(const char *shutdown_conf, const char *trues[])
{
X struct confline_t *conflines = NULL;
X
X conflines = cfs_parse_shutdown_conf(shutdown_conf, trues);
X if (conflines == NULL) {
X fprintf(stderr, "Failed parsing shutdown.conf\n");
X exit(1);
X }
X
//{struct confline_t *cf;
//for (cf=conflines; cf; cf=cf->cf_next) {
// printf("type=%d spec=<%s>\n", cf->cf_type, cf->cf_spec?cf->cf_spec:"(null)");
//}
//}
X if (global.verbose>=3) {
X const char **p;
X printf("Predicates:");
X for (p=trues; *p; p++) { printf(" <%s>", *p); }
X printf("\n");
X }
X
X /* I should be immune to parent (shell) death */
X /*
X * want to detach from parent process,
X * as parent shell sends HUP-HUP-KILL on exit,
X * but want stdin console input/stdout output
X */
X signal(SIGTERM, SIG_IGN);
X signal(SIGINT , SIG_IGN);
X
X chdir("/");
X if (!global.dryrun)
X if (fork()) exit(0);
X /* now is child */
X setsid();
X // procmgr_daemon(0, /*PROCMGR_DAEMON_NOCHDIR|*/PROCMGR_DAEMON_NODEVNULL);
X // if (fork()) exit(0); /*wavier session leader*/
X
X /* fill in the conf with processes */
X cfs_rescan_procs(conflines);
X
X /* the show begins */
X cfs_dokills(conflines);
}
/* use return status from cf_action instead of longjmp */
SHAR_EOF
(set 20 03 10 09 10 27 12 'shutdownx/shutdown.c'; eval "$shar_touch") &&
chmod 0664 'shutdownx/shutdown.c' ||
$echo 'restore of' 'shutdownx/shutdown.c' 'failed'
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'shutdownx/shutdown.c'`"
test 24937 -eq "$shar_count" ||
$echo 'shutdownx/shutdown.c:' 'original size' '24937,' 'current size' "$shar_count!"
fi
rm -fr _sh01621
exit 0

Jens H Jorgensen

Re: [SOURCE] shutdownx 1.0: alternate shutdown util with APM

Post by Jens H Jorgensen » Thu Oct 09, 2003 5:57 pm

Thank you - it works great.

--
Jens

<kabe@sra-tohoku.co.jp> wrote in message news:bm45iq$m42$1@inn.qnx.com...
Stock "shutdown" command can't power off the ATX supply.
I tried numerous things to hook APM BIOS calls during shutdown
with no success.

So I whipped up a custom shutdown command which have
some expandability, and builtin APM power-off capability.
I believe the shutdown sequence resembles that of the stock shutdown.

Usage:
/usr/sbin/shutdownx -S poweroff
Build:
make
Make a package:
make package

Post Reply

Return to “qdn.public.qnxrtp.x86”