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

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:PackageTypeApplication</QPM:PackageType>
X QPM:PackageRepository</QPM:PackageRepository>
X QPM:FileVersion1.9</QPM:FileVersion>
X </QPM:PackageDescription>
X
X QPM:ProductDescription
X QPM:ProductNameshutdownx</QPM:ProductName>
X QPM:ProductIdentifiershutdownx</QPM:ProductIdentifier>
X QPM:ProductEmail</QPM:ProductEmail>
X QPM:VendorNamePublic</QPM:VendorName>
X QPM:VendorInstallNamevega</QPM:VendorInstallName>
X QPM:VendorURL</QPM:VendorURL>
X QPM:VendorEmbedURL</QPM:VendorEmbedURL>
X QPM:VendorEmail</QPM:VendorEmail>
X QPM:AuthorNameTaketo Kabe</QPM:AuthorName>
X QPM:AuthorURL</QPM:AuthorURL>
X QPM:AuthorEmbedURL</QPM:AuthorEmbedURL>
X QPM:AuthorEmailkabe#sra-tohoku.co.jp</QPM:AuthorEmail>
X QPM:ProductIconSmall</QPM:ProductIconSmall>
X QPM:ProductIconLarge</QPM:ProductIconLarge>
X QPM:ProductDescriptionShortconfigurable shutdown</QPM:ProductDescriptionShort>
X QPM:ProductDescriptionLongshutdownx 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:ReleaseVersion1.0</QPM:ReleaseVersion>
X QPM:ReleaseUrgencyLow</QPM:ReleaseUrgency>
X QPM:ReleaseStabilityStable</QPM:ReleaseStability>
X QPM:ReleaseNoteMinor</QPM:ReleaseNoteMinor>
X QPM:ReleaseNoteMajor</QPM:ReleaseNoteMajor>
X QPM:CountryExclude</QPM:CountryExclude>
X QPM:ReleaseCopyrightQNX 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:ContentKeywordshutdown,poweroff,halt</QPM:ContentKeyword>
X QPM:TargetOSqnx6</QPM:TargetOS>
X QPM:HostOSnone</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

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