668 lines
14 KiB
Bash
Executable File
668 lines
14 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
# $NetBSD: postinstall,v 1.20 2002/06/27 02:26:39 itojun Exp $
|
|
#
|
|
# Copyright (c) 2002 The NetBSD Foundation, Inc.
|
|
# All rights reserved.
|
|
#
|
|
# This code is derived from software contributed to The NetBSD Foundation
|
|
# by Luke Mewburn.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
# 3. All advertising materials mentioning features or use of this software
|
|
# must display the following acknowledgement:
|
|
# This product includes software developed by the NetBSD
|
|
# Foundation, Inc. and its contributors.
|
|
# 4. Neither the name of The NetBSD Foundation nor the names of its
|
|
# contributors may be used to endorse or promote products derived
|
|
# from this software without specific prior written permission.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
# POSSIBILITY OF SUCH DAMAGE.
|
|
#
|
|
# postinstall
|
|
# check for or fix configuration changes that occur
|
|
# over time as NetBSD evolves.
|
|
#
|
|
|
|
#
|
|
# checks to add:
|
|
# - sysctl(8) renames
|
|
# - postfix config
|
|
# - de* -> tlp* migration (/etc/ifconfig.de*, $ifconfig_de*,
|
|
# dhclient.conf, ...) ?
|
|
# - support quiet/verbose mode ?
|
|
# - check obsolete file lists -- need to remove non obsolete files from
|
|
# the sets first.
|
|
#
|
|
|
|
#
|
|
# helper functions
|
|
#
|
|
|
|
err()
|
|
{
|
|
exitval=$1
|
|
shift
|
|
echo 1>&2 "${PROGNAME}: $*"
|
|
exit ${exitval}
|
|
}
|
|
|
|
warn()
|
|
{
|
|
echo 1>&2 "${PROGNAME}: $*"
|
|
}
|
|
|
|
msg()
|
|
{
|
|
echo " $*"
|
|
}
|
|
|
|
# additem item description
|
|
# add item to list of supported items to check/fix
|
|
#
|
|
additem()
|
|
{
|
|
[ $# -eq 2 ] || err 2 "USAGE: additem item description"
|
|
items="${items}${items:+ }$1"
|
|
eval desc_$1=\"$2\"
|
|
}
|
|
|
|
# checkdir op dir mode
|
|
# ensure dir exists, and if not, create it with the appropriate mode.
|
|
# returns 0 if ok, 1 otherwise.
|
|
#
|
|
check_dir()
|
|
{
|
|
[ $# -eq 3 ] || err 2 "USAGE: check_dir op dir mode"
|
|
_cdop=$1
|
|
_cddir=$2
|
|
_cdmode=$3
|
|
[ -d "${_cddir}" ] && return 0
|
|
if [ "${_cdop}" = "check" ]; then
|
|
msg "${_cddir} is not a directory"
|
|
return 1
|
|
elif ! mkdir -m "${_cdmode}" "${_cddir}" ; then
|
|
msg "Can't create missing ${_cddir}"
|
|
return 1
|
|
else
|
|
msg "Missing ${_cddir} created"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# check_ids op type file id [...]
|
|
# check if file of type "users" or "groups" contains the relevant ids
|
|
# returns 0 if ok, 1 otherwise.
|
|
#
|
|
check_ids()
|
|
{
|
|
[ $# -ge 4 ] || err 2 "USAGE: checks_ids op type file id [...]"
|
|
_op=$1
|
|
_type=$2
|
|
_file=$3
|
|
shift 3
|
|
_ids=$*
|
|
|
|
if [ ! -f "${_file}" ]; then
|
|
msg "${_file} doesn't exist; can't check for missing ${_type}"
|
|
return 1
|
|
fi
|
|
if [ ! -r "${_file}" ]; then
|
|
msg "${_file} is not readable; can't check for missing ${_type}"
|
|
return 1
|
|
fi
|
|
_missing=$(awk -F: '
|
|
BEGIN {
|
|
for (x = 1; x < ARGC; x++)
|
|
idlist[ARGV[x]]++
|
|
ARGC=1
|
|
}
|
|
{
|
|
found[$1]++
|
|
}
|
|
END {
|
|
for (id in idlist) {
|
|
if (! (id in found))
|
|
print id
|
|
}
|
|
}
|
|
' ${_ids} < ${_file}) || return 1
|
|
if [ -n "${_missing}" ]; then
|
|
msg "Missing ${_type}:" $(echo ${_missing})
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# compare_dir op src dest mode file [file ...]
|
|
# perform op ("check" or "fix") on files in src/ against dest/
|
|
# returns 0 if ok, 1 otherwise.
|
|
#
|
|
compare_dir()
|
|
{
|
|
[ $# -ge 5 ] || err 2 "USAGE: compare_dir op src dest mode file [...]"
|
|
_op=$1
|
|
_src=$2
|
|
_dest=$3
|
|
_mode=$4
|
|
shift 4
|
|
_files=$*
|
|
|
|
if [ ! -d "${_src}" ]; then
|
|
msg "${_src} is not a directory; skipping check"
|
|
return 1
|
|
fi
|
|
check_dir ${_op} ${_dest} 755 || return 1
|
|
|
|
_cmpdir_rv=0
|
|
for f in ${_files}; do
|
|
fs=${_src}/${f}
|
|
fd=${_dest}/${f}
|
|
error=""
|
|
if [ ! -f "${fd}" ]; then
|
|
error="${fd} does not exist"
|
|
elif ! cmp -s ${fs} ${fd} ; then
|
|
error="${fd} != ${fs}"
|
|
else
|
|
continue
|
|
fi
|
|
if [ "${_op}" = "check" ]; then
|
|
msg ${error}
|
|
_cmpdir_rv=1
|
|
elif ! cp -f ${fs} ${fd}; then
|
|
msg "Can't copy ${fs} to ${fd}"
|
|
_cmpdir_rv=1
|
|
elif ! chmod ${_mode} ${fd}; then
|
|
msg "Can't change mode of ${fd} to ${_mode}"
|
|
_cmpdir_rv=1
|
|
else
|
|
msg "Copied ${fs} to ${fd}"
|
|
fi
|
|
done
|
|
return ${_cmpdir_rv}
|
|
}
|
|
|
|
# move_file op src dest --
|
|
# check (op == "check") or move (op != "check") from src to dest.
|
|
# returns 0 if ok, 1 otherwise.
|
|
#
|
|
move_file()
|
|
{
|
|
[ $# -eq 3 ] || err 2 "USAGE: move_file op src dest"
|
|
_fm_op=$1
|
|
_fm_src=$2
|
|
_fm_dest=$3
|
|
|
|
if [ -f "${_fm_src}" -a ! -f "${_fm_dest}" ]; then
|
|
if [ "${_fm_op}" = "check" ]; then
|
|
msg "Move ${_fm_src} to ${_fm_dest}"
|
|
return 1
|
|
fi
|
|
if ! mv ${_fm_src} ${_fm_dest}; then
|
|
msg "Can't move ${_fm_src} to ${_fm_dest}"
|
|
return 1
|
|
fi
|
|
msg "Moved ${_fm_src} to ${_fm_dest}"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# rcconf_is_set name var --
|
|
# load the rcconf for name, and check if obsolete rc.conf(5) variable
|
|
# var is defined or not.
|
|
# returns 0 if defined (even to ""), otherwise 1.
|
|
#
|
|
rcconf_is_set()
|
|
{
|
|
[ $# -eq 2 ] || err 2 "USAGE: rcconf_is_set name var"
|
|
_name=$1
|
|
_var=$2
|
|
(
|
|
for f in \
|
|
${DEST_DIR}/etc/rc.conf \
|
|
${DEST_DIR}/etc/rc.conf.d/${_name}; do
|
|
[ -f "${f}" ] && . "${f}";
|
|
done
|
|
if eval "[ -n \"\${${_var}}\" \
|
|
-o \"\${${_var}-UNSET}\" != \"UNSET\" ]"; then
|
|
msg "Obsolete rc.conf(5) variable '\$${_var}' found."
|
|
exit 0
|
|
else
|
|
exit 1
|
|
fi
|
|
)
|
|
}
|
|
|
|
|
|
#
|
|
# items
|
|
# -----
|
|
#
|
|
|
|
#
|
|
# defaults
|
|
#
|
|
additem defaults "/etc/defaults being up to date"
|
|
do_defaults()
|
|
{
|
|
[ -n "$1" ] || err 2 "USAGE: do_defaults fix|check"
|
|
|
|
compare_dir $1 ${SRC_DIR}/etc/defaults ${DEST_DIR}/etc/defaults 444 \
|
|
daily.conf monthly.conf rc.conf security.conf weekly.conf
|
|
}
|
|
|
|
#
|
|
# mtree
|
|
#
|
|
additem mtree "/etc/mtree being up to date"
|
|
do_mtree()
|
|
{
|
|
[ -n "$1" ] || err 2 "USAGE: do_mtree fix|check"
|
|
|
|
compare_dir $1 ${SRC_DIR}/etc/mtree ${DEST_DIR}/etc/mtree 444 \
|
|
NetBSD.dist special
|
|
}
|
|
|
|
#
|
|
# gid
|
|
#
|
|
additem gid "required GIDs"
|
|
do_gid()
|
|
{
|
|
[ -n "$1" ] || err 2 "USAGE: do_gid fix|check"
|
|
|
|
check_ids $1 groups "${DEST_DIR}/etc/group" \
|
|
named ntpd sshd smmsp
|
|
return $?
|
|
}
|
|
|
|
#
|
|
# uid
|
|
#
|
|
additem uid "required UIDs"
|
|
do_uid()
|
|
{
|
|
[ -n "$1" ] || err 2 "USAGE: do_uid fix|check"
|
|
|
|
check_ids $1 users "${DEST_DIR}/etc/master.passwd" \
|
|
named ntpd sshd smmsp
|
|
return $?
|
|
}
|
|
|
|
|
|
#
|
|
# periodic
|
|
#
|
|
additem periodic "/etc/{daily,weekly,monthly,security} being up to date"
|
|
do_periodic()
|
|
{
|
|
[ -n "$1" ] || err 2 "USAGE: do_periodic fix|check"
|
|
|
|
compare_dir $1 ${SRC_DIR}/etc ${DEST_DIR}/etc 644 \
|
|
daily weekly monthly security
|
|
}
|
|
|
|
#
|
|
# rc
|
|
#
|
|
additem rc "/etc/rc* and /etc/rc.d/ being up to date"
|
|
do_rc()
|
|
{
|
|
[ -n "$1" ] || err 2 "USAGE: do_rc fix|check"
|
|
op=$1
|
|
failed=0
|
|
|
|
compare_dir ${op} ${SRC_DIR}/etc ${DEST_DIR}/etc 644 \
|
|
rc rc.subr rc.shutdown
|
|
failed=$(( ${failed} + $? ))
|
|
|
|
compare_dir ${op} ${SRC_DIR}/etc/rc.d ${DEST_DIR}/etc/rc.d 555 \
|
|
DAEMON LOGIN NETWORKING SERVERS accounting altqd amd \
|
|
apmd bootparams bootconf.sh ccd cleartmp cron \
|
|
dhclient dhcpd dhcrelay dmesg downinterfaces fsck \
|
|
ifwatchd inetd ipfilter ipfs ipmon ipnat ipsec isdnd \
|
|
kdc ldconfig lkm1 lkm2 lkm3 local lpd mopd motd \
|
|
mountall mountcritlocal mountcritremote mountd moused \
|
|
mrouted named ndbootd network newsyslog nfsd \
|
|
nfslocking ntpd ntpdate poffd postfix ppp pwcheck \
|
|
quota racoon rpcbind raidframe rarpd rbootd root \
|
|
route6d routed rtadvd rtsold rwho savecore \
|
|
screenblank sendmail securelevel sshd swap1 swap2 \
|
|
sysdb sysctl syslogd timed ttys virecover wscons xdm \
|
|
xfs ypbind yppasswdd ypserv
|
|
failed=$(( ${failed} + $? ))
|
|
|
|
# check for obsolete rc.d files
|
|
for f in NETWORK gated; do
|
|
fd=${DEST_DIR}/etc/rc.d/${f}
|
|
[ ! -e "${fd}" ] && continue
|
|
if [ "${op}" = "check" ]; then
|
|
msg "Remove obsolete ${fd}"
|
|
failed=1
|
|
elif ! rm ${fd}; then
|
|
msg "Can't remove obsolete ${fd}"
|
|
failed=1
|
|
else
|
|
msg "Removed obsolete ${fd}"
|
|
fi
|
|
done
|
|
|
|
# check for obsolete rc.conf(5) variables
|
|
set -- amd amd_master \
|
|
mountcritlocal critical_filesystems_beforenet \
|
|
mountcritremote critical_filesystems \
|
|
network ip6forwarding \
|
|
sysctl defcorename \
|
|
sysctl nfsiod_flags
|
|
while [ $# -gt 1 ]; do
|
|
if rcconf_is_set $1 $2; then
|
|
failed=1
|
|
fi
|
|
shift 2
|
|
done
|
|
|
|
return ${failed}
|
|
}
|
|
|
|
#
|
|
# ssh
|
|
#
|
|
additem ssh "ssh configuration update"
|
|
do_ssh()
|
|
{
|
|
[ -n "$1" ] || err 2 "USAGE: do_ssh fix|check"
|
|
op=$1
|
|
|
|
failed=0
|
|
_etcssh=${DEST_DIR}/etc/ssh
|
|
if ! check_dir ${op} ${_etcssh} 755; then
|
|
failed=1
|
|
fi
|
|
|
|
if [ ${failed} -eq 0 ]; then
|
|
for f in \
|
|
ssh_known_hosts ssh_known_hosts2 \
|
|
ssh_host_dsa_key ssh_host_dsa_key.pub \
|
|
ssh_host_rsa_key ssh_host_rsa_key.pub \
|
|
ssh_host_key ssh_host_key.pub \
|
|
; do
|
|
if ! move_file ${op} \
|
|
${DEST_DIR}/etc/${f} ${_etcssh}/${f} ;
|
|
then
|
|
failed=1
|
|
fi
|
|
done
|
|
for f in sshd.conf ssh.conf ; do
|
|
# /etc/ssh/ssh{,d}.conf -> ssh{,d}_config
|
|
#
|
|
if ! move_file ${op} \
|
|
${_etcssh}/${f} ${_etcssh}/${f%.conf}_config ;
|
|
then
|
|
failed=1
|
|
fi
|
|
# /etc/ssh{,d}.conf -> /etc/ssh/ssh{,d}_config
|
|
#
|
|
if ! move_file ${op} \
|
|
${DEST_DIR}/etc/${f} ${_etcssh}/${f%.conf}_config ;
|
|
then
|
|
failed=1
|
|
fi
|
|
done
|
|
fi
|
|
|
|
sshdconf=""
|
|
for f in \
|
|
${_etcssh}/sshd_config \
|
|
${_etcssh}/sshd.conf \
|
|
${DEST_DIR}/etc/sshd.conf ; do
|
|
if [ -f "${f}" ]; then
|
|
sshdconf=${f}
|
|
break;
|
|
fi
|
|
done
|
|
if [ -n "${sshdconf}" ]; then
|
|
awk '
|
|
$1 ~ /^[Hh][Oo][Ss][Tt][Kk][Ee][Yy]$/ &&
|
|
$2 ~ /^\/etc\/+ssh_host(_[dr]sa)?_key$/ {
|
|
sub(/\/etc\/+/, "/etc/ssh/");
|
|
}
|
|
{ print }
|
|
' < ${sshdconf} > ${SCRATCHDIR}/sshd_config
|
|
if ! cmp -s ${sshdconf} ${SCRATCHDIR}/sshd_config; then
|
|
diff ${sshdconf} ${SCRATCHDIR}/sshd_config > \
|
|
${SCRATCHDIR}/sshd_config.diffs
|
|
if [ "${op}" = "check" ]; then
|
|
msg "${sshdconf} needs the following changes:"
|
|
failed=1
|
|
elif ! cp -f ${SCRATCHDIR}/sshd_config ${sshdconf}; then
|
|
msg "${sshdconf} changes not applied:"
|
|
failed=1
|
|
else
|
|
msg "${sshdconf} changes applied:"
|
|
fi
|
|
while read _line; do
|
|
msg " ${_line}"
|
|
done < ${SCRATCHDIR}/sshd_config.diffs
|
|
fi
|
|
fi
|
|
|
|
if ! check_dir "${op}" "${DEST_DIR}/var/chroot/sshd" 755 ; then
|
|
failed=1
|
|
fi
|
|
|
|
if rcconf_is_set sshd sshd_conf_dir ; then
|
|
failed=1
|
|
fi
|
|
|
|
return ${failed}
|
|
}
|
|
|
|
#
|
|
# wscons
|
|
#
|
|
additem wscons "wscons configuration file update"
|
|
do_wscons()
|
|
{
|
|
[ -n "$1" ] || err 2 "USAGE: do_wscons fix|check"
|
|
op=$1
|
|
|
|
[ -f ${DEST_DIR}/etc/wscons.conf ] || return 0
|
|
|
|
failed=0
|
|
while read _type _arg1 _rest; do
|
|
if [ "${_type}" = "mux" -a "${_arg1}" = "1" ]; then
|
|
msg \
|
|
"Obsolete wscons.conf(5) entry \""${_type} ${_arg1}"\" found."
|
|
failed=1
|
|
fi
|
|
done < ${DEST_DIR}/etc/wscons.conf
|
|
|
|
return ${failed}
|
|
}
|
|
|
|
#
|
|
# makedev
|
|
#
|
|
additem makedev "/dev/MAKEDEV being up to date"
|
|
do_makedev()
|
|
{
|
|
[ -n "$1" ] || err 2 "USAGE: do_makedev fix|check"
|
|
|
|
compare_dir $1 ${SRC_DIR}/etc/etc.${MACHINE} \
|
|
${DEST_DIR}/dev 555 MAKEDEV
|
|
if [ "${op}" = "fix" ]; then
|
|
(cd ${DEST_DIR}/dev; /bin/sh ./MAKEDEV all)
|
|
fi
|
|
}
|
|
|
|
|
|
#
|
|
# end of items
|
|
# ------------
|
|
#
|
|
|
|
|
|
usage()
|
|
{
|
|
cat 1>&2 << _USAGE_
|
|
Usage: ${PROGNAME} [-s srcdir] [-d destdir] operation [item [...]]
|
|
Perform post-installation checks and/or fixes on a system's
|
|
configuration files. If no items are provided, all checks
|
|
or fixes are applied.
|
|
|
|
Options:
|
|
-s srcdir Source directory to compare from. [${SRC_DIR}]
|
|
-d destdir Destination directory to check. [${DEST_DIR}]
|
|
|
|
Operation may be one of:
|
|
help display this help
|
|
list list available items
|
|
check perform post-installation checks on items
|
|
fix apply fixes that 'check' determines need to be applied
|
|
usage display this usage
|
|
_USAGE_
|
|
exit 1
|
|
}
|
|
|
|
|
|
list()
|
|
{
|
|
echo "Supported items:"
|
|
echo " Item Description"
|
|
echo " ---- -----------"
|
|
for i in ${items}; do
|
|
eval desc="\${desc_${i}}"
|
|
printf " %-12s %s\n" "${i}" "${desc}"
|
|
done
|
|
}
|
|
|
|
|
|
main()
|
|
{
|
|
while getopts s:d: ch; do
|
|
case ${ch} in
|
|
s)
|
|
SRC_DIR=${OPTARG} ;;
|
|
d)
|
|
DEST_DIR=${OPTARG} ;;
|
|
*)
|
|
usage ;;
|
|
esac
|
|
done
|
|
shift $((${OPTIND} - 1))
|
|
[ $# -gt 0 ] || usage
|
|
|
|
[ -d "${SRC_DIR}" ] || err 1 "${SRC_DIR} is not a directory"
|
|
[ -d "${DEST_DIR}" ] || err 1 "${DEST_DIR} is not a directory"
|
|
|
|
op=$1
|
|
shift
|
|
|
|
case "${op}" in
|
|
|
|
usage|help)
|
|
usage
|
|
;;
|
|
|
|
list)
|
|
echo "Source directory: ${SRC_DIR}"
|
|
echo "Target directory: ${DEST_DIR}"
|
|
list
|
|
;;
|
|
|
|
check|fix)
|
|
todo=$*
|
|
: ${todo:=${items}}
|
|
|
|
# ensure that all supplied items are valid
|
|
#
|
|
for i in ${todo}; do
|
|
eval desc=\"\${desc_${i}}\"
|
|
[ -n "${desc}" ] || err 1 "Unsupported ${op} '"${i}"'"
|
|
done
|
|
|
|
# perform each check/fix
|
|
#
|
|
echo "Source directory: ${SRC_DIR}"
|
|
echo "Target directory: ${DEST_DIR}"
|
|
items_passed=
|
|
items_failed=
|
|
for i in ${todo}; do
|
|
echo "${i} ${op}:"
|
|
( eval do_${i} ${op} )
|
|
if [ $? -eq 0 ]; then
|
|
items_passed="${items_passed} ${i}"
|
|
else
|
|
items_failed="${items_failed} ${i}"
|
|
fi
|
|
done
|
|
|
|
if [ "${op}" = "check" ]; then
|
|
plural="checks"
|
|
else
|
|
plural="fixes"
|
|
fi
|
|
|
|
echo "${PROGNAME} ${plural} passed:${items_passed}"
|
|
echo "${PROGNAME} ${plural} failed:${items_failed}"
|
|
|
|
;;
|
|
|
|
*)
|
|
warn "Unknown operation '"${op}"'"
|
|
usage
|
|
;;
|
|
|
|
esac
|
|
}
|
|
|
|
mkdtemp ()
|
|
{
|
|
# Make sure we don't loop forever if mkdir will always fail.
|
|
[ -d /tmp ] || err 1 /tmp is not a directory
|
|
[ -w /tmp ] || err 1 /tmp is not writeable
|
|
|
|
_base=/tmp/_postinstall.$$
|
|
_serial=0
|
|
|
|
while true; do
|
|
_dir=${_base}.${_serial}
|
|
mkdir -m 0700 ${_dir} && break
|
|
_serial=$((${_serial} + 1))
|
|
done
|
|
echo ${_dir}
|
|
}
|
|
|
|
# defaults
|
|
#
|
|
PROGNAME=${0##*/}
|
|
SRC_DIR="/usr/src"
|
|
DEST_DIR="/"
|
|
SCRATCHDIR=$( mkdtemp ) || err 1 "Can't create scratch directory"
|
|
# assume native build if $MACHINE is not set
|
|
if [ -z "${MACHINE}" ]; then
|
|
MACHINE=`/usr/bin/uname -m`
|
|
fi
|
|
trap "/bin/rm -rf ${SCRATCHDIR} ; exit 0" 0 1 2 3 15 # EXIT HUP INT QUIT TERM
|
|
umask 022
|
|
|
|
main $*
|
|
exit 0
|