diff options
author | obrien <obrien@FreeBSD.org> | 1998-08-23 22:07:21 +0000 |
---|---|---|
committer | obrien <obrien@FreeBSD.org> | 1998-08-23 22:07:21 +0000 |
commit | 663d5a0f32ed8dfc091ffb6153161591ac6ba563 (patch) | |
tree | 60b090a6cbdb64326bb128ea49a231d08eb2680e /contrib/amd/scripts | |
download | FreeBSD-src-663d5a0f32ed8dfc091ffb6153161591ac6ba563.zip FreeBSD-src-663d5a0f32ed8dfc091ffb6153161591ac6ba563.tar.gz |
Virgin import of AMD (am-utils) v6.0a16
Diffstat (limited to 'contrib/amd/scripts')
-rw-r--r-- | contrib/amd/scripts/Makefile.am | 49 | ||||
-rw-r--r-- | contrib/amd/scripts/Makefile.in | 379 | ||||
-rw-r--r-- | contrib/amd/scripts/am-eject.in | 52 | ||||
-rw-r--r-- | contrib/amd/scripts/amd.conf-sample | 94 | ||||
-rw-r--r-- | contrib/amd/scripts/amd.conf.5 | 539 | ||||
-rwxr-xr-x | contrib/amd/scripts/amd2ldif.in | 58 | ||||
-rwxr-xr-x | contrib/amd/scripts/amd2sun.in | 51 | ||||
-rwxr-xr-x | contrib/amd/scripts/ctl-amd.in | 113 | ||||
-rwxr-xr-x | contrib/amd/scripts/ctl-hlfsd.in | 101 | ||||
-rw-r--r-- | contrib/amd/scripts/expn.1 | 1370 | ||||
-rwxr-xr-x | contrib/amd/scripts/expn.in | 1370 | ||||
-rwxr-xr-x | contrib/amd/scripts/fix-amd-map.in | 52 | ||||
-rwxr-xr-x | contrib/amd/scripts/fixrmtab | 24 | ||||
-rw-r--r-- | contrib/amd/scripts/lostaltmail.conf-sample | 84 | ||||
-rwxr-xr-x | contrib/amd/scripts/lostaltmail.in | 648 | ||||
-rwxr-xr-x | contrib/amd/scripts/wait4amd.in | 45 | ||||
-rwxr-xr-x | contrib/amd/scripts/wait4amd2die.in | 49 |
17 files changed, 5078 insertions, 0 deletions
diff --git a/contrib/amd/scripts/Makefile.am b/contrib/amd/scripts/Makefile.am new file mode 100644 index 0000000..9ebd691 --- /dev/null +++ b/contrib/amd/scripts/Makefile.am @@ -0,0 +1,49 @@ +## Process this file with automake to produce Makefile.in + +# Package: am-utils +# Level: Makefile for scripts/ directory +# Author: Erez Zadok + +sbin_SCRIPTS = \ + am-eject \ + amd2ldif \ + amd2sun \ + ctl-amd \ + ctl-hlfsd \ + fixrmtab \ + fix-amd-map \ + lostaltmail \ + wait4amd \ + wait4amd2die + +bin_SCRIPTS = \ + expn + +sysconf_DATA = \ + amd.conf-sample \ + lostaltmail.conf-sample + +# man pages +man_MANS = \ + amd.conf.5 \ + expn.1 + +EXTRA_DIST = \ + am-eject.in \ + amd2ldif.in \ + amd2sun.in \ + ctl-amd.in \ + ctl-hlfsd.in \ + fixrmtab \ + fix-amd-map.in \ + lostaltmail.in \ + wait4amd.in \ + wait4amd2die.in \ + \ + expn.in \ + \ + amd.conf-sample \ + lostaltmail.conf-sample \ + $(man_MANS) + +CLEANFILES = $(sbin_SCRIPTS) $(bin_SCRIPTS) diff --git a/contrib/amd/scripts/Makefile.in b/contrib/amd/scripts/Makefile.in new file mode 100644 index 0000000..1c69cec --- /dev/null +++ b/contrib/amd/scripts/Makefile.in @@ -0,0 +1,379 @@ +# Makefile.in generated automatically by automake 1.3.1 from Makefile.am + +# Copyright (C) 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# Package: am-utils +# Level: Makefile for scripts/ directory +# Author: Erez Zadok + + +SHELL = /bin/sh + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DISTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AR = @AR@ +CC = @CC@ +CPP = @CPP@ +LEX = @LEX@ +LIBTOOL = @LIBTOOL@ +LIBTOOL_LDFLAGS = @LIBTOOL_LDFLAGS@ +LTALLOCA = @LTALLOCA@ +LTLIBOBJS = @LTLIBOBJS@ +PACKAGE = @PACKAGE@ +PERL = @PERL@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +sbin_SCRIPTS = \ + am-eject \ + amd2ldif \ + amd2sun \ + ctl-amd \ + ctl-hlfsd \ + fixrmtab \ + fix-amd-map \ + lostaltmail \ + wait4amd \ + wait4amd2die + +bin_SCRIPTS = \ + expn + +sysconf_DATA = \ + amd.conf-sample \ + lostaltmail.conf-sample + +# man pages +man_MANS = \ + amd.conf.5 \ + expn.1 + +EXTRA_DIST = \ + am-eject.in \ + amd2ldif.in \ + amd2sun.in \ + ctl-amd.in \ + ctl-hlfsd.in \ + fixrmtab \ + fix-amd-map.in \ + lostaltmail.in \ + wait4amd.in \ + wait4amd2die.in \ + \ + expn.in \ + \ + amd.conf-sample \ + lostaltmail.conf-sample \ + $(man_MANS) + +CLEANFILES = $(sbin_SCRIPTS) $(bin_SCRIPTS) +mkinstalldirs = $(SHELL) $(top_srcdir)/aux/mkinstalldirs +CONFIG_HEADER = ../config.h +CONFIG_CLEAN_FILES = am-eject amd2ldif amd2sun ctl-amd ctl-hlfsd expn \ +fix-amd-map lostaltmail wait4amd wait4amd2die +SCRIPTS = $(bin_SCRIPTS) $(sbin_SCRIPTS) + +man1dir = $(mandir)/man1 +man5dir = $(mandir)/man5 +MANS = $(man_MANS) + +NROFF = nroff +DATA = $(sysconf_DATA) + +DIST_COMMON = Makefile.am Makefile.in am-eject.in amd2ldif.in \ +amd2sun.in ctl-amd.in ctl-hlfsd.in expn.in fix-amd-map.in \ +lostaltmail.in wait4amd.in wait4amd2die.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = gtar +GZIP = --best +all: Makefile $(SCRIPTS) $(MANS) $(DATA) + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/./aux/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --localdir=./aux --gnu --include-deps scripts/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + +am-eject: $(top_builddir)/config.status am-eject.in + cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status +amd2ldif: $(top_builddir)/config.status amd2ldif.in + cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status +amd2sun: $(top_builddir)/config.status amd2sun.in + cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status +ctl-amd: $(top_builddir)/config.status ctl-amd.in + cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status +ctl-hlfsd: $(top_builddir)/config.status ctl-hlfsd.in + cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status +expn: $(top_builddir)/config.status expn.in + cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status +fix-amd-map: $(top_builddir)/config.status fix-amd-map.in + cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status +lostaltmail: $(top_builddir)/config.status lostaltmail.in + cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status +wait4amd: $(top_builddir)/config.status wait4amd.in + cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status +wait4amd2die: $(top_builddir)/config.status wait4amd2die.in + cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status + +install-binSCRIPTS: $(bin_SCRIPTS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(bindir) + @list='$(bin_SCRIPTS)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(INSTALL_SCRIPT) $$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`"; \ + $(INSTALL_SCRIPT) $$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`; \ + else if test -f $(srcdir)/$$p; then \ + echo " $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`"; \ + $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`; \ + else :; fi; fi; \ + done + +uninstall-binSCRIPTS: + @$(NORMAL_UNINSTALL) + list='$(bin_SCRIPTS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`; \ + done + +install-sbinSCRIPTS: $(sbin_SCRIPTS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(sbindir) + @list='$(sbin_SCRIPTS)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(INSTALL_SCRIPT) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed '$(transform)'`"; \ + $(INSTALL_SCRIPT) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed '$(transform)'`; \ + else if test -f $(srcdir)/$$p; then \ + echo " $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(sbindir)/`echo $$p|sed '$(transform)'`"; \ + $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(sbindir)/`echo $$p|sed '$(transform)'`; \ + else :; fi; fi; \ + done + +uninstall-sbinSCRIPTS: + @$(NORMAL_UNINSTALL) + list='$(sbin_SCRIPTS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(sbindir)/`echo $$p|sed '$(transform)'`; \ + done + +install-man1: + $(mkinstalldirs) $(DESTDIR)$(man1dir) + @list='$(man1_MANS)'; \ + l2='$(man_MANS)'; for i in $$l2; do \ + case "$$i" in \ + *.1*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man1dir)/$$inst"; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(man1dir)/$$inst; \ + done + +uninstall-man1: + @list='$(man1_MANS)'; \ + l2='$(man_MANS)'; for i in $$l2; do \ + case "$$i" in \ + *.1*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f $(DESTDIR)$(man1dir)/$$inst"; \ + rm -f $(DESTDIR)$(man1dir)/$$inst; \ + done + +install-man5: + $(mkinstalldirs) $(DESTDIR)$(man5dir) + @list='$(man5_MANS)'; \ + l2='$(man_MANS)'; for i in $$l2; do \ + case "$$i" in \ + *.5*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man5dir)/$$inst"; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(man5dir)/$$inst; \ + done + +uninstall-man5: + @list='$(man5_MANS)'; \ + l2='$(man_MANS)'; for i in $$l2; do \ + case "$$i" in \ + *.5*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f $(DESTDIR)$(man5dir)/$$inst"; \ + rm -f $(DESTDIR)$(man5dir)/$$inst; \ + done +install-man: $(MANS) + @$(NORMAL_INSTALL) + $(MAKE) install-man1 install-man5 +uninstall-man: + @$(NORMAL_UNINSTALL) + $(MAKE) uninstall-man1 uninstall-man5 + +install-sysconfDATA: $(sysconf_DATA) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(sysconfdir) + @list='$(sysconf_DATA)'; for p in $$list; do \ + if test -f $(srcdir)/$$p; then \ + echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(sysconfdir)/$$p"; \ + $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(sysconfdir)/$$p; \ + else if test -f $$p; then \ + echo " $(INSTALL_DATA) $$p $(DESTDIR)$(sysconfdir)/$$p"; \ + $(INSTALL_DATA) $$p $(DESTDIR)$(sysconfdir)/$$p; \ + fi; fi; \ + done + +uninstall-sysconfDATA: + @$(NORMAL_UNINSTALL) + list='$(sysconf_DATA)'; for p in $$list; do \ + rm -f $(DESTDIR)$(sysconfdir)/$$p; \ + done +tags: TAGS +TAGS: + + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = scripts + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file; \ + done +info: +dvi: +check: all + $(MAKE) +installcheck: +install-exec: install-binSCRIPTS install-sbinSCRIPTS install-sysconfDATA + @$(NORMAL_INSTALL) + +install-data: install-man + @$(NORMAL_INSTALL) + +install: install-exec install-data all + @: + +uninstall: uninstall-binSCRIPTS uninstall-sbinSCRIPTS uninstall-man uninstall-sysconfDATA + +install-strip: + $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' INSTALL_SCRIPT='$(INSTALL_PROGRAM)' install +installdirs: + $(mkinstalldirs) $(DATADIR)$(bindir) $(DATADIR)$(sbindir) \ + $(DESTDIR)$(mandir)/man1 $(DESTDIR)$(mandir)/man5 \ + $(DATADIR)$(sysconfdir) + + +mostlyclean-generic: + -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -rm -f Makefile $(DISTCLEANFILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +mostlyclean: mostlyclean-generic + +clean: clean-generic mostlyclean + +distclean: distclean-generic clean + -rm -f config.status + -rm -f libtool + +maintainer-clean: maintainer-clean-generic distclean + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +.PHONY: uninstall-binSCRIPTS install-binSCRIPTS uninstall-sbinSCRIPTS \ +install-sbinSCRIPTS install-man1 uninstall-man1 install-man5 \ +uninstall-man5 install-man uninstall-man uninstall-sysconfDATA \ +install-sysconfDATA tags distdir info dvi installcheck install-exec \ +install-data install uninstall all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/contrib/amd/scripts/am-eject.in b/contrib/amd/scripts/am-eject.in new file mode 100644 index 0000000..ed7e2d0 --- /dev/null +++ b/contrib/amd/scripts/am-eject.in @@ -0,0 +1,52 @@ +#!/bin/sh +# auto-unmount floppy/cd directory before ejecting device +# script taken from Debian Linux's amd +# +# Package: am-utils-6.0 +# (Additional) author: Erez Zadok <ezk@cs.columbia.edu> + +# set path +prefix=@prefix@ +exec_prefix=@exec_prefix@ +PATH=@sbindir@:@bindir@:/usr/ucb:/usr/bin:/bin:${PATH} +export PATH + +if [ $# -ne 1 ]; then + echo "Usage: $0 cd|fd" + exit 2 +fi + +# determine toplevel mount point of amd +fs=`amq | grep ' toplvl ' | cut -d' ' -f1` +if [ "$fs" = "" ]; then + echo "Cannot determine amd toplevel directory" + exit 2 +fi + +# append name of medium +case "$1" in + cd|fd) fs=$fs/$1;; + *) echo "Usage: $0 cd|fd"; exit 2;; +esac + +# is the medium mounted? +if amq | grep -q "^$fs" >/dev/null 2>&1; then + # if yes, try to unmount it + sync + amq -u $fs + sleep 2 + if amq | grep -q "^$fs" >/dev/null 2>&1; then + # failed, bail out + echo -n "Cannot unmount $fs; in use by:" + fuser -uv -m $fs + echo "" + exit 1 + fi +else + echo "$fs not mounted" +fi + +case $1 in + cd) eject;; # eject CD-ROM + fd) echo "Ok to remove disk";; +esac diff --git a/contrib/amd/scripts/amd.conf-sample b/contrib/amd/scripts/amd.conf-sample new file mode 100644 index 0000000..59ce1f8 --- /dev/null +++ b/contrib/amd/scripts/amd.conf-sample @@ -0,0 +1,94 @@ +# -*- text -*- +# A SAMPLE CONFIGURATION FILE FOR AMD + +############################################################################## +# GLOBAL OPTIONS SECTION (must be first in amd.conf file) +[ global ] +# (amd -n) +normalize_hostnames = yes | no +# (amd -p) +print_pid = yes | no +pid_file = /dev/stdout | /var/run/amd.pid +# (amd -r) +restart_mounts = yes | no +unmount_on_exit = no | yes +# (amd -a) +auto_dir = /a +# duration in seconds that a looked up name remain cached (amd -c) +cache_duration = 300 +# (amd -d) +local_domain = cs.columbia.edu +# (amd -k) +karch = sun4m +arch = sun4 +# if you don't like autoconf picking up "sunos5" as the os-type, override it +os = sos5 +# (amd -o) +osver = 2.5.1 +# if you print_version after setting up "os", it will show it. (amd -v) +print_version = yes | no + +# (amd -l) +log_file = /var/log/amd | syslog | syslog:facility +# NFS (RPC/UDP) retry interval, in tenths of secs (amd -t interval.counter) +nfs_retry_interval = 8 +nfs_retransmit_counter = 110 +# (amd -w) +dismount_interval = 120 +# (amd -y) +nis_domain = nisDom-CS.columbia.edu +# (amd -x) +log_options = fatal,error,user,warn,info,map,stats,all +# (amd -D) +debug_options = all,amq,daemon,fork,full,info,mem,mtab,str,test,trace +# (amd -S) +plock = no | yes +# selectors on /defaults are off by default +selectors_on_default = yes | no +# should browsable maps show number of entries to df/statfs (default=no) +show_statfs_entries = yes | no +# (hpux) cluster name (amd -C) +cluster = ??? +# LDAP (Lightweight Directory Access Protocol) options +ldap_base = ldap.your.domain:389 +ldap_hostports = "ou=Marketing, o=AMD Ltd, c=US" +ldap_cache_seconds = 0 (default) +ldap_cache_maxmem = 131072 (default) +# default base name for hesiod maps +hesiod_base = automount +# these 5 options can be overridden by each map individually +browsable_dirs = yes | no | full +map_options = cache:=all +map_type = file|hesiod|ndbm|nis|nisplus|passwd|union|ldap +mount_type = nfs | autofs +search_path = /etc/local:/etc/amdmaps:/misc/yp +# alternate RPC program number to register with the port mapper +portmap_program = 300019-300029 +# Use fully qualified host names +fully_qualified_hosts = yes | no + +############################################################################## +# DEFINE AN AMD MOUNT POINT +[ /home ] +# map name must be defined, all else are optional +map_name = /etc/amd.home | amd.home +map_options = cache:=all +# if map type is not defined, will search all map types (default) +map_type = file|hesiod|ndbm|nis|nisplus|passwd|union|ldap +search_path = /etc/local:/etc/amdmaps:/misc/yp +# an amd or autofs mount point +mount_type = nfs | autofs +browsable_dirs = yes | no +# an optional tag to be used with amd -T tag. untagged entries are always +# used. Tagged ones get used only if specified with "amd -T" +tag = test + +############################################################################## +# DEFINE ANOTHER AMD MOUNT POINT +[ /src ] +map_name = /usr/local/lib/amdmaps/amu.src +# regular amd (nfs) mount point (default) +# don't try the "autofs" type. It is not implemented yet. +mount_type = nfs + +############################################################################## diff --git a/contrib/amd/scripts/amd.conf.5 b/contrib/amd/scripts/amd.conf.5 new file mode 100644 index 0000000..2022fad --- /dev/null +++ b/contrib/amd/scripts/amd.conf.5 @@ -0,0 +1,539 @@ +.\" +.\" Copyright (c) 1997-1998 Erez Zadok +.\" Copyright (c) 1990 Jan-Simon Pendry +.\" Copyright (c) 1990 Imperial College of Science, Technology & Medicine +.\" Copyright (c) 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Jan-Simon Pendry at Imperial College, London. +.\" +.\" 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 acknowledgment: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +.\" +.\" %W% (Berkeley) %G% +.\" +.\" $Id: amd.conf.5,v 5.2.2.1 1997/02/09 15:11:15 ezk beta $ +.\" +.TH AMQ.CONF 8 "7 August 1997" +.SH NAME +amd.conf \- amd configuration file +.SH SYNOPSIS +.B amd.conf +.SH DESCRIPTION +The +.B amd.conf +file is the configuration file for amd, as part of the am-utils suite. +.P +.B amd.conf +contains runtime configuration information for the +.B amd +automounter program. +.\" ************************************************************************** +.SH FILE FORMAT +.P +The file consists of sections and parameters. A section begins with the +name of the section in square brackets and continues until the next section +begins or the end the file is reached. Sections contain parameters of the +form 'name = value'. +.P +The file is line-based - that is, each newline-terminated line represents +either a comment, a section name or a parameter. No line-continuation +syntax is available. +.P +Section, parameter names and their values are case sensitive. +.P +Only the first equals sign in a parameter is significant. Whitespace before +or after the first equals sign is discarded. Leading, trailing and +internal whitespace in section and parameter names is irrelevant. Leading +and trailing whitespace in a parameter value is discarded. Internal +whitespace within a parameter value is not allowed, unless the whole +parameter value is quoted with double quotes as in 'name = "some value"'. +.P +Any line beginning with a pound sign (#) is ignored, as are lines containing +only whitespace. +.P +The values following the equals sign in parameters are all either a string +(no quotes needed if string does not include spaces) or a boolean, which may +be given as yes/no. Case is significant in all values. Some items such as +cache timeouts are numeric. +.\" ************************************************************************** +.SH SECTIONS +.SS The [global] section +Parameters in this section either apply to amd as a whole, or to all other +regular map sections which follow. There should be only one global section +defined in one configuration file. +.P +It is highly recommended that this section be specified first in the +configuration file. If it is not, then regular map sections which precede +it will not use global values defined later. + +.SS Regular [/map] sections +Parameters in regular (non-global) sections apply to a single map entry. +For example, if the map section +.B [/homes] +is defined, then all parameters following it will be applied to the +.I /homes +amd-managed mount point. +.\" ************************************************************************** +.SH PARAMETERS +.SS Parameters common to all sections +These parameters can be specified either in the global or a map specific +section. Entries specified in a map-specific section override the default +value or one defined in the global section. If such a common parameter is +specified only in the global section, it is applicable to all regular map +sections that follow. +.\" ************************************************************************** +.TP +.BR browsable_dirs " (string, default=no)" +If "yes", then amd's top-level mount points will be browsable to +.BR readdir (3) +calls. This means you could run for example +.BR ls (3) +and see what keys are available to mount in that directory. Not all entries +are made visible to readdir(3): the "/default" entry, wildcard +entries, and those with a "/" in them are not included. If you specify +"full" to this option, all but "/default" will be visible. +Note that if you run a command which will attempt to +.BR stat (2) +the entries, such as often done by "ls -l" or "ls -F", amd will attempt to +mount +.I every +entry in that map. This is often called a ``mount storm''. + +.TP +.BR map_options " (string, default no options)" +This option is the same as specifying map options on the command line to +amd, such as "cache:=all". + +.TP +.BR map_type " (string, default search all map types)" +If specified, amd will initialize the map only for the type given. This is +useful to avoid the default map search type used by amd which takes longer +and can have undesired side-effects such as initializing NIS even if not +used. Possible values are + +.nf +\fBfile\fR plain files +\fBhesiod\fR Hesiod name service from MIT +\fBldap\fR Lightweight Directory Access Protocol +\fBndbm\fR (New) dbm style hash files +\fBnis\fR Network Information Services (version 2) +\fBnisplus\fR Network Information Services Plus (version 3) +\fBpasswd\fR local password files +\fBunion\fR union maps +.fi + +.TP +.BR mount_type " (string, default=nfs)" +All amd mount types default to NFS. That is, amd is an NFS server on the +map mount points, for the local host it is running on. If "autofs" is +specified, amd will be an autofs server for those mount points. + +.TP +.BR search_path " (string, default no search path)" +This provides a (colon-delimited) search path for file maps. Using a search +path, sites can allow for local map customizations and overrides, and can +distributed maps in several locations as needed. + +.\" ************************************************************************** +.SS Parameters applicable to the global section only + +.TP +.BR arch " (string, default to compiled in value)" +Allows you to override the value of the +.I arch +amd variable. + +.TP +.BR auto_dir " (string, default=/a)" +Same as the +.B \-a +option to amd. This sets the private directory where amd will create +sub-directories for its real mount points. + +.TP +.BR cache_duration " (numeric, default=300)" +Same as the +.B \-c +option to amd. Sets the duration in seconds that looked up map entries +remain in the cache. + +.TP +.BR cluster " (string, default no cluster)" +Same as the +.B \-C +option to amd. Specifies the alternate HP-UX cluster to use. + +.TP +.BR debug_options " (string, default no debug options)" +Same as the +.B \-D +option to amd. Specify any debugging options for amd. Works only if +am-utils was configured for debugging using the --enable-debug option. The +"mem" option alone can be turned on via --enable-debug=mem. Otherwise +debugging options are ignored. Options are comma delimited, and can be +preceded by the string "no" to negate their meaning. You can get the list +of supported debugging options by running amd \-v. Possible values are: + +.nf +\fBall\fR all options +\fBamq\fR register for amq +\fBdaemon\fR enter daemon mode +\fBfork\fR fork server +\fBfull\fR program trace +\fBinfo\fR info service specific debugging (hesiod, nis, etc.) +\fBmem\fR trace memory allocations +\fBmtab\fR use local "./mtab" file +\fBstr\fR debug string munging +\fBtest\fR full debug but no daemon +\fBtrace\fR protocol trace +.fi + +.TP +.BR dismount_interval " (numeric, default=120)" +Same as the +.B \-w +option to amd. Specify in seconds, the time between attempts to dismount +file systems that have exceeded their cached times. + +.TP +.BR fully_qualified_hosts " (string, default=no)" +If "yes", +.I Amd +will perform RPC authentication using fully-qualified host names. This is +necessary for some systems, and especially when performing cross-domain +mounting. For this function to work, the +.I Amd +variable ${hostd} is used, requiring that ${domain} not be null. + +.TP +.BR hesiod_base " (string, default=automount)" +Specify the base name for hesiod maps. + +.TP +.BR karch " (string, default to karch of the system)" +Same as the +.B \-k +option to amd. Allows you to override the kernel-architecture of your +system. Useful for example on Sun (Sparc) machines, where you can build one +amd binary, and run it on multiple machines, yet you want each one to get +the correct +.I karch +variable set (for example, sun4c, sun4m, sun4u, etc.) Note that if not +specified, amd will use uname(2) to figure out the kernel architecture of +the machine. + +.TP +.BR ldap_base " (string, default not set)" +Specify the base name for LDAP. + +.TP +.BR ldap_cache_maxmem " (numeric, default=131072)" +Specify the maximum memory amd should use to cache LDAP entries. + +.TP +.BR ldap_cache_seconds " (numeric, default=0)" +Specify the number of seconds to keep entries in the cache. + +.TP +.BR ldap_hostports " (string, default not set)" +Specify LDAP-specific values such as country and organization. + +.TP +.BR local_domain " (string, default no sub-domain)" +Same as the +.B \-d +option to amd. Specify the local domain name. If this option is not given +the domain name is determined from the hostname, by removing the first +component of the fully-qualified host name. + +.TP +.BR log_file " (string, default=/dev/stderr)" +Same as the +.B \-l +option to amd. Specify a file name to log amd events to. +If the string +.B /dev/stderr +is specified, amd will send its events to the standard error file descriptor. +If the string +.B syslog +is given, amd will record its events with the system logger +.BR syslogd (8). +The default syslog facility used is LOG_DAEMON. If you +wish to change it, append its name to the log file name, delimited by a +single colon. For example, if +.I logfile +is the string +.B syslog:local7 +then amd will log messages via +.IR syslog (3) +using the LOG_LOCAL7 facility (if it exists on the system). + +.TP +.BR log_options " (string, default no logging options)" +Same as the +.B \-x +option to amd. Specify any logging options for amd. Options are comma +delimited, and can be preceded by the string "no" to negate their meaning. +The "debug" logging option is only available if am-utils was configured with +--enable-debug. You can get the list of supported debugging options by +running amd \-v. Possible values are: + +.nf +\fBall\fR all messages +\fBdebug\fR debug messages +\fBerror\fR non-fatal system errors +\fBfatal\fR fatal errors +\fBinfo\fR information +\fBmap\fR map errors +\fBstats\fR additional statistical information +\fBuser\fR non-fatal user errors +\fBwarn\fR warnings +\fBwarning\fR warnings +.fi + +.TP +.BR nfs_retransmit_counter " (numeric, default=110)" +Same as the +.I counter +part of the +.BI \-t " interval.counter" +option to amd. Specifies the retransmit counter's value in +.I tenths +of seconds. + +.TP +.BR nfs_retry_interval " (numeric, default=8)" +Same as the +.I interval +part of the +.BI \-t " interval.counter" +option to amd. Specifies the interval in +.I tenths +of seconds, between NFS/RPC/UDP retries. + +.TP +.BR nis_domain " (string, default to local NIS domain name)" +Same as the +.B \-y +option to amd. Specify an alternative NIS domain from which to fetch the +NIS maps. The default is the system domain name. This option is ignored if +NIS support is not available. + +.TP +.BR normalize_hostnames " (boolean, default=no)" +Same as the +.B \-n +option to amd. If "yes", then the name refereed to by ${rhost} is +normalized relative to the host database before being used. The effect is +to translate aliases into ``official'' names. + +.TP +.BR os " (string, default to compiled in value)" +Same as the +.B \-O +option to amd. Allows you to override the compiled-in name of the operating +system. Useful when the built-in name is not desired for backward +compatibility reasons. For example, if the build in name is ``sunos5'', you +can override it to ``sos5'', and use older maps which were written with the +latter in mind. + +.TP +.BR osver " (string, default to compiled in value)" +Same as the +.B \-o +option to amd. Override the compiled-in version number of the operating +system. Useful when the built in version is not desired for backward +compatibility reasons. For example, if the build in version is ``2.5.1'', +you can override it to ``5.5.1'', and use older maps that were written with +the latter in mind. + +.TP +.BR pid_file " (string, default=/dev/stdout)" +Specify a file to store the process ID of the running daemon into. If not +specified, amd will print its process id onto the standard output. Useful +for killing amd after it had run. Note that the PID of a running amd can +also be retrieved via +.B amq +.BR \-p . +This file is used only if the print_pid option is on. + +.TP +.BR plock " (boolean, default=yes)" +Same as the +.B \-S +option to amd. +If "yes", lock the running executable pages of amd into memory. To improve +amd's performance, systems that support the +.BR plock (3) +call can lock the amd process into memory. This way there is less chance it +the operating system will schedule, page out, and swap the amd process as +needed. This improves amd's performance, at the cost of reserving the +memory used by the amd process (making it unavailable for other processes). + +.TP +.BR portmap_program " (numeric, default=300019)" +Specify an alternate Port-mapper RPC program number, other than the official +number. This is useful when running multiple amd processes. For example, +you can run another amd in "test" mode, without affecting the primary amd +process in any way. For safety reasons, the alternate program numbers that +can be specified must be in the range 300019-300029, inclusive. +.B amq +has an option +.B -P +which can be used to specify an alternate program number of an amd to +contact. In this way, amq can fully control any number of amd processes +running on the same host. + +.TP +.BR print_pid " (boolean, default=no)" +Same as the +.B \-p +option to amd. If "yes", amd will print its process ID upon starting. + +.TP +.BR print_version " (boolean, default=no)" +Same as the +.B \-v +option to amd, but the version prints and amd continues to run. If "yes", +amd will print its version information string, which includes some +configuration and compilation values. + +.TP +.BR restart_mounts " (boolean, default=no)" +Same as the +.B \-r +option to amd. If "yes" +.B amd +will scan the mount table to determine which file systems are currently +mounted. Whenever one of these would have been auto-mounted, +.B amd +inherits it. + +.TP +.BR selectors_on_default " (boolean, default=no)" +If "yes", then the /default entry of maps will be look for and process any +selectors before setting defaults for all other keys in that map. Useful +when you want to set different options for a complete map based on some +parameters. For example, you may want to better the NFS performance over +slow slip-based networks as follows: + +.nf +/defaults \\ + wire==slip-net;opts:=intr,rsize=1024,wsize=1024 \\ + wire!=slip-net;opts:=intr,rsize=8192,wsize=8192 +.fi + +.TP +.BR show_statfs_entries " (boolean), default=no)" +If "yes", then all maps which are browsable will also show the number of +entries (keys) they have when "df" runs. (This is accomplished by returning +non-zero values to the statfs(2) system call). + +.TP +.BR unmount_on_exist " (boolean), default=no)" +If "yes", then amd will attempt to unmount all file systems which it knows +about. Normally amd leaves all (esp. NFS) mounted file systems intact. +Note that amd does not know about file systems mounted before it starts up, +unless the restart_mounts option or +.B \-r +flag are used. + +.\" ************************************************************************** +.SS Parameters applicable to regular map sections + +.TP +.BR map_name " (string, must be specified)" +Name of the map where the keys are located. + +.TP +.BR tag " (string, default no tag)" +Each map entry in the configuration file can be tagged. If no tag is +specified, that map section will always be processed by amd. If it is +specified, then amd will process the map if the +.B -T +option was given to amd, and the value given to that command-line option +matches that in the map section. + +.\" ************************************************************************** +.SH EXAMPLES +Here is a real amd configuration file I use daily. +.P +.nf +# GLOBAL OPTIONS SECTION +[ global ] +normalize_hostnames = no +print_pid = no +restart_mounts = yes +auto_dir = /n +log_file = /var/log/amd +log_options = all +#debug_options = all +plock = no +selectors_on_default = yes +# config.guess picks up "sunos5" and I don't want to edit my maps yet +os = sos5 +# if you print_version after setting up "os", it will show it. +print_version = no +map_type = file +search_path = /etc/amdmaps:/usr/lib/amd:/usr/local/AMD/lib +browsable_dirs = yes + +# DEFINE AN AMD MOUNT POINT +[ /u ] +map_name = amd.u + +[ /proj ] +map_name = amd.proj + +[ /src ] +map_name = amd.src + +[ /misc ] +map_name = amd.misc + +[ /import ] +map_name = amd.import + +[ /tftpboot/.amd ] +tag = tftpboot +map_name = amd.tftpboot +.fi +.\" ************************************************************************** +.SH "SEE ALSO" +.BR amd (8), +.BR amq (8), +.BR ctl-amd (8). +.SH AUTHORS +Erez Zadok <ezk@cs.columbia.edu>, Department of Computer Science, Columbia +University, New York, USA. +.P +Other authors and contributors to am-utils are listed in the +.B AUTHORS +file distributed with am-utils. diff --git a/contrib/amd/scripts/amd2ldif.in b/contrib/amd/scripts/amd2ldif.in new file mode 100755 index 0000000..6d3c28a --- /dev/null +++ b/contrib/amd/scripts/amd2ldif.in @@ -0,0 +1,58 @@ +#!@PERL@ + +$usage=<<EOU; +Usage $0 mapname base < mapfile >mapfile.ldif + +mapname: name of the amd map beeing converted to ldif +base : The LDAP search base. Do not forget the quotes! + +This script should/could be used in a Makefile together +with ldif2ldbm(8C) to automagically update the ldap +databases and restart slapd whenever a master copy of +the maps have changed. Remember "cd /var/yp; make" ? +EOU + +my $fmt = "%-12s: %s\n"; +my $tfmt = "%-15s: %s\n"; +my $mapname = $ARGV[0] or die $usage; +my $base = $ARGV[1] or die $usage; +$time = time(); + +print "dn: cn=amdmap timestamp, $base\n"; +printf "$tfmt", "cn", "amdmap timestamp"; +printf "$tfmt", "objectClass", "amdmapTimestamp"; +printf "$tfmt", "amdmapTimestamp", $time; +print "\n"; + +my $line = ""; +my $done = 0; + +while (<STDIN>) { + chomp; + if (/\s*(.+)\\\s*/) { + if ($line) { + $line .= " ".$1; + } else { + $line = $1; + } + $done = 0; + } else { + s/^\s+//g; + $line .= $_; + $done = 1; + } + if ($done) { + my @vals = split(/\s+/,$line); + my $key = shift @vals; + my $entry; + + print "dn: cn=amdmap $mapname\[$key\], $base\n"; + printf "$fmt","cn","amdmap $mapname\[$key\]"; + printf "$fmt","objectClass", "amdmap"; + printf "$fmt","amdmapName", $mapname; + printf "$fmt","amdmapKey", $key; + printf "$fmt","amdmapValue", join(' ',@vals); + print "\n"; + $line = ""; $done = 0; + } +} diff --git a/contrib/amd/scripts/amd2sun.in b/contrib/amd/scripts/amd2sun.in new file mode 100755 index 0000000..df69b09 --- /dev/null +++ b/contrib/amd/scripts/amd2sun.in @@ -0,0 +1,51 @@ +#!@PERL@ +# convert amd maps to Sun automount maps +# usage: amd2sun file +# +# Package: am-utils-6.0 +# Author: "Mark D. Baushke" <mdb@cisco.com> + +print "# file created by amd2sun +# +# DO NOT EDIT THIS FILE AT ALL +# It is automatically generated from the amd mount map - edit that instead +# +"; +while (<>) { + print, next if /^#/; + chop; + $line = $_; + while ($line =~ /\\$/) { + chop $line; + $line2 = <>; + $line2 =~ s/^\s*//; + $line .= $line2; + chop $line; + } + + next unless $line =~ /^([^\s]+)\s+(.*)$/; + + $fs = $1; $rest=$2; + + if ($fs =~ /^\/defaults/) { + ($defopts = $rest) =~ s/^.*[\s;]opts:=([^;\s]+)[;\s]*.*$/\1/; + next; + } + + $opts=$defopts; + + if ($rest =~ /opts:=([^;\s]+)[;\s]/) { + $opts = $1; + } + + $opts =~ s/,ping=[-\d]+//g; + + ($rhost = $rest) =~ s/^.*[\s;]rhost:=([^;\s]+)[;\s]*.*$/\1/; + ($rfs = $rest) =~ s/^.*[\s;]rfs:=([^;\s]+)[;\s]*.*$/\1/; + + if ($rest =~ /sublink:=([^;\s]+)[;\s]/ ) { + $rfs .= "/$1"; + } + + print "$fs -$opts $rhost:$rfs\n"; +} diff --git a/contrib/amd/scripts/ctl-amd.in b/contrib/amd/scripts/ctl-amd.in new file mode 100755 index 0000000..665a7dd --- /dev/null +++ b/contrib/amd/scripts/ctl-amd.in @@ -0,0 +1,113 @@ +#!/bin/sh +# control starting, stopping, or restarting amd. +# usage: ctl-amd [start | stop | restart] +# +# Package: am-utils-6.0 +# Author: Erez Zadok <ezk@cs.columbia.edu> + +# set path +prefix=@prefix@ +exec_prefix=@exec_prefix@ +PATH=@sbindir@:@bindir@:/usr/ucb:/usr/bin:/bin:${PATH} +export PATH + +# kill the named process(es) +killproc() +{ +# first try to get PID via an amq RPC +pid=`amq -p 2>/dev/null` +if test "$pid" != "" +then + kill $pid + return 0 +fi + +# try bsd style ps +pscmd="ps axc" +pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'` +if test "$pid" != "" +then + kill $pid + return 0 +fi + +# try bsd44 style ps +pscmd="ps -x" +pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'` +if test "$pid" != "" +then + kill $pid + return 0 +fi + +# try svr4 style ps +pscmd="ps -e" +pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'` +if test "$pid" != "" +then + kill $pid + return 0 +fi + +# failed +return 1 +} + +# search for amd.conf file +CF_FILE="${prefix}/etc/amd.conf" +# any local copy of the conf file overrides the "global" one +if [ -f /etc/amd.conf ] +then + CF_FILE="/etc/amd.conf" +fi +if [ -f ${prefix}/etc/amd.conf ] +then + CF_FILE="${prefix}/etc/amd.conf" +fi +if [ -f /etc/local/amd.conf ] +then + CF_FILE="/etc/local/amd.conf" +fi + +# if have the directory /tftpboot/.amd, then add a tag to include it +CF_TAG="" +if [ -d /tftpboot/.amd ] +then + CF_TAG="-T tftpboot" +fi + +case "$1" in +'start') + # + # Start the amd automounter. + # + if [ -x @sbindir@/amd ] + then + # do not specify full path of amd so killproc() works + amd -F $CF_FILE $CF_TAG + fi + ;; + +'stop') + # prepend space to program name to ensure only amd process dies + killproc " amd" + ;; + +'restart') + # kill amd, wait for it to die, then restart + echo "killing amd..." + ctl-amd stop + wait4amd2die + if [ $? != 0 ] + then + echo "NOT restarting amd!" + else + echo "Restarting amd..." + ctl-amd start + fi + ;; + +*) + echo "Usage: @sbindir@/ctl-amd [ start | stop | restart ]" + ;; +esac diff --git a/contrib/amd/scripts/ctl-hlfsd.in b/contrib/amd/scripts/ctl-hlfsd.in new file mode 100755 index 0000000..a6e97f6 --- /dev/null +++ b/contrib/amd/scripts/ctl-hlfsd.in @@ -0,0 +1,101 @@ +#!/bin/sh +# control starting, stopping, or restarting hlfsd. +# usage: ctl-hlfsd [start | stop | restart] +# +# Package: am-utils-6.0 +# Author: Erez Zadok <ezk@cs.columbia.edu> + +# set path +prefix=@prefix@ +exec_prefix=@exec_prefix@ +PATH=@sbindir@:@bindir@:/usr/ucb:/usr/bin:/bin:${PATH} +export PATH + +# kill the named process(es) +killproc() +{ +# try bsd style ps +pscmd="ps axc" +pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'` +if test "$pid" != "" +then + kill $pid + return 0 +fi + +# try bsd44 style ps +pscmd="ps -x" +pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'` +if test "$pid" != "" +then + kill $pid + return 0 +fi + +# try svr4 style ps +pscmd="ps -e" +pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'` +if test "$pid" != "" +then + kill $pid + return 0 +fi + +# failed +return 1 +} + +# locate logs directory +if [ -d /var/log ]; then + logdir="/var/log" +else + logdir="/tmp" +fi + +# locate the mail spool directory +if [ -d /var/mail/. ]; then + maildir="/var/mail" + altmaildir="/var/alt_mail" +else + maildir="/usr/spool/mail" + altmaildir="/usr/spool/alt_mail" +fi + +# locate any optional password file +if [ -f ${prefix}/etc/passwd ]; then + PASSWD_FILE="-P ${prefix}/etc/passwd" +else + PASSWD_FILE="" +fi + +case "$1" in +'start') + # + # Start the hlfsd mail redirector service + # + if [ -x @sbindir@/hlfsd -a -h $maildir ] + then + echo @sbindir@/hlfsd ${PASSWD_FILE} -a $altmaildir -x all -D fork -l $logdir/hlfsd /mail/home .mailspool + @sbindir@/hlfsd ${PASSWD_FILE} -a $altmaildir -x all -D fork -l $logdir/hlfsd /mail/home .mailspool & + fi + ;; + +'stop') + # prepend space to program name to ensure only amd process dies + killproc " hlfsd" + ;; + +'restart') + # kill hlfsd, wait for it to die, then restart + echo "killing hlfsd..." + ctl-hlfsd stop + echo "Waiting for 10 seconds..." + sleep 10 # hope that's enough + echo "Restarting hlfsd..." + ctl-hlfsd start + ;; + +*) + echo "Usage: @sbindir@/ctl-hlfsd [ start | stop | restart ]" + ;; +esac diff --git a/contrib/amd/scripts/expn.1 b/contrib/amd/scripts/expn.1 new file mode 100644 index 0000000..b2bd1b6 --- /dev/null +++ b/contrib/amd/scripts/expn.1 @@ -0,0 +1,1370 @@ +#!@PERL@ +'di '; +'ds 00 \\"'; +'ig 00 '; +# +# THIS PROGRAM IS ITS OWN MANUAL PAGE. INSTALL IN man & bin. +# + +# hardcoded constants, should work fine for BSD-based systems +#require 'sys/socket.ph'; # perl 4 +use Socket; # perl 5 +$AF_INET = &AF_INET; +$SOCK_STREAM = &SOCK_STREAM; +$sockaddr = 'S n a4 x8'; + +# system requirements: +# must have 'nslookup' and 'hostname' programs. + +# $Header: /home/muir/bin/RCS/expn,v 3.9 1995/10/02 17:51:35 muir Exp muir $ + +# TODO: +# less magic should apply to command-line addresses +# less magic should apply to local addresses +# add magic to deal with cross-domain cnames + +# Checklist: (hard addresses) +# 250 Kimmo Suominen <"|/usr/local/mh/lib/slocal -user kim"@grendel.tac.nyc.ny.us> +# harry@hofmann.cs.Berkeley.EDU -> harry@tenet (.berkeley.edu) [dead] +# bks@cs.berkeley.edu -> shiva.CS (.berkeley.edu) [dead] +# dan@tc.cornell.edu -> brown@tiberius (.tc.cornell.edu) + +############################################################################# +# +# Copyright (c) 1993 David Muir Sharnoff +# All rights reserved. +# +# 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 David Muir Sharnoff. +# 4. The name of David Sharnoff may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE DAVID MUIR SHARNOFF ``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 DAVID MUIR SHARNOFF 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. +# +# This copyright notice derrived from material copyrighted by the Regents +# of the University of California. +# +# Contributions accepted. +# +############################################################################# + +# overall structure: +# in an effort to not trace each address individually, but rather +# ask each server in turn a whole bunch of questions, addresses to +# be expanded are queued up. +# +# This means that all accounting w.r.t. an address must be stored in +# various arrays. Generally these arrays are indexed by the +# string "$addr *** $server" where $addr is the address to be +# expanded "foo" or maybe "foo@bar" and $server is the hostname +# of the SMTP server to contact. +# + +# important global variables: +# +# @hosts : list of servers still to be contacted +# $server : name of the current we are currently looking at +# @users = $users{@hosts[0]} : addresses to expand at this server +# $u = $users[0] : the current address being expanded +# $names{"$users[0] *** $server"} : the 'name' associated with the address +# $mxbacktrace{"$users[0] *** $server"} : record of mx expansion +# $mx_secondary{$server} : other mx relays at the same priority +# $domainify_fallback{"$users[0] *** $server"} : alternative names to try +# instead of $server if $server doesn't work +# $temporary_redirect{"$users[0] *** $server"} : when trying alternates, +# temporarily channel all tries along current path +# $giveup{$server} : do not bother expanding addresses at $server +# $verbose : -v +# $watch : -w +# $vw : -v or -w +# $debug : -d +# $valid : -a +# $levels : -1 +# S : the socket connection to $server + +$have_nslookup = 1; # we have the nslookup program +$port = 'smtp'; +$av0 = $0; +$ENV{'PATH'} .= ":/usr/etc" unless $ENV{'PATH'} =~ m,/usr/etc,; +$ENV{'PATH'} .= ":/usr/ucb" unless $ENV{'PATH'} =~ m,/usr/ucb,; +select(STDERR); + +$0 = "$av0 - running hostname"; +chop($name = `hostname || uname -n`); + +$0 = "$av0 - lookup host FQDN and IP addr"; +($hostname,$aliases,$type,$len,$thisaddr) = gethostbyname($name); + +$0 = "$av0 - parsing args"; +$usage = "Usage: $av0 [-1avwd] user[\@host] [user2[host2] ...]"; +for $a (@ARGV) { + die $usage if $a eq "-"; + while ($a =~ s/^(-.*)([1avwd])/$1/) { + eval '$'."flag_$2 += 1"; + } + next if $a eq "-"; + die $usage if $a =~ /^-/; + &expn(&parse($a,$hostname,undef,1)); +} +$verbose = $flag_v; +$watch = $flag_w; +$vw = $flag_v + $flag_w; +$debug = $flag_d; +$valid = $flag_a; +$levels = $flag_1; + +die $usage unless @hosts; +if ($valid) { + if ($valid == 1) { + $validRequirement = 0.8; + } elsif ($valid == 2) { + $validRequirement = 1.0; + } elsif ($valid == 3) { + $validRequirement = 0.9; + } else { + $validRequirement = (1 - (1/($valid-3))); + print "validRequirement = $validRequirement\n" if $debug; + } +} + +$0 = "$av0 - building local socket"; +($name,$aliases,$proto) = getprotobyname('tcp'); +($name,$aliases,$port) = getservbyname($port,'tcp') + unless $port =~ /^\d+/; +$this = pack($sockaddr, &AF_INET, 0, $thisaddr); + +HOST: +while (@hosts) { + $server = shift(@hosts); + @users = split(' ',$users{$server}); + delete $users{$server}; + + # is this server already known to be bad? + $0 = "$av0 - looking up $server"; + if ($giveup{$server}) { + &giveup('mx domainify',$giveup{$server}); + next; + } + + # do we already have an mx record for this host? + next HOST if &mxredirect($server,*users); + + # look it up, or try for an mx. + $0 = "$av0 - gethostbyname($server)"; + + ($name,$aliases,$type,$len,$thataddr) = gethostbyname($server); + # if we can't get an A record, try for an MX record. + unless($thataddr) { + &mxlookup(1,$server,"$server: could not resolve name",*users); + next HOST; + } + + # get a connection, or look for an mx + $0 = "$av0 - socket to $server"; + $that = pack($sockaddr, &AF_INET, $port, $thataddr); + socket(S, &AF_INET, &SOCK_STREAM, $proto) + || die "socket: $!"; + $0 = "$av0 - bind to $server"; + bind(S, $this) + || die "bind $hostname,0: $!"; + $0 = "$av0 - connect to $server"; + print "debug = $debug server = $server\n" if $debug > 8; + if (! connect(S, $that) || ($debug == 10 && $server =~ /relay\d.UU.NET$/i)) { + $0 = "$av0 - $server: could not connect: $!\n"; + $emsg = $!; + unless (&mxlookup(0,$server,"$server: could not connect: $!",*users)) { + &giveup('mx',"$server: Could not connect: $emsg"); + } + next HOST; + } + select((select(S),$| = 1)[0]); # don't buffer output to S + + # read the greeting + $0 = "$av0 - talking to $server"; + &alarm("greeting with $server",''); + while(<S>) { + alarm(0); + print if $watch; + if (/^(\d+)([- ])/) { + if ($1 != 220) { + $0 = "$av0 - bad numeric response from $server"; + &alarm("giving up after bad response from $server",''); + &read_response($2,$watch); + alarm(0); + print STDERR "$server: NOT 220 greeting: $_" + if ($debug || $vw); + if (&mxlookup(0,$server,"$server: did not respond with a 220 greeting",*users)) { + close(S); + next HOST; + } + } + last if ($2 eq " "); + } else { + $0 = "$av0 - bad response from $server"; + print STDERR "$server: NOT 220 greeting: $_" + if ($debug || $vw); + unless (&mxlookup(0,$server,"$server: did not respond with SMTP codes",*users)) { + &giveup('',"$server: did not talk SMTP"); + } + close(S); + next HOST; + } + &alarm("greeting with $server",''); + } + alarm(0); + + # if this causes problems, remove it + $0 = "$av0 - sending helo to $server"; + &alarm("sending helo to $server",""); + &ps("helo $hostname"); + while(<S>) { + print if $watch; + last if /^\d+ /; + } + alarm(0); + + # try the users, one by one + USER: + while(@users) { + $u = shift(@users); + $0 = "$av0 - expanding $u [\@$server]"; + + # do we already have a name for this user? + $oldname = $names{"$u *** $server"}; + + print &compact($u,$server)." ->\n" if ($verbose && ! $valid); + if ($valid) { + # + # when running with -a, we delay taking any action + # on the results of our query until we have looked + # at the complete output. @toFinal stores expansions + # that will be final if we take them. @toExpn stores + # expnansions that are not final. @isValid keeps + # track of our ability to send mail to each of the + # expansions. + # + @isValid = (); + @toFinal = (); + @toExpn = (); + } + +# ($ecode,@expansion) = &expn_vrfy($u,$server); + (@foo) = &expn_vrfy($u,$server); + ($ecode,@expansion) = @foo; + if ($ecode) { + &giveup('',$ecode,$u); + last USER; + } + + for $s (@expansion) { + $s =~ s/[\n\r]//g; + $0 = "$av0 - parsing $server: $s"; + + $skipwatch = $watch; + + if ($s =~ /^[25]51([- ]).*<(.+)>/) { + print "$s" if $watch; + print "(pretending 250$1<$2>)" if ($debug && $watch); + print "\n" if $watch; + $s = "250$1<$2>"; + $skipwatch = 0; + } + + if ($s =~ /^250([- ])(.+)/) { + print "$s\n" if $skipwatch; + ($done,$addr) = ($1,$2); + ($newhost, $newaddr, $newname) = &parse($addr,$server,$oldname, $#expansion == 0); + print "($newhost, $newaddr, $newname) = &parse($addr, $server, $oldname)\n" if $debug; + if (! $newhost) { + # no expansion is possible w/o a new server to call + if ($valid) { + push(@isValid, &validAddr($newaddr)); + push(@toFinal,$newaddr,$server,$newname); + } else { + &verbose(&final($newaddr,$server,$newname)); + } + } else { + $newmxhost = &mx($newhost,$newaddr); + print "$newmxhost = &mx($newhost)\n" + if ($debug && $newhost ne $newmxhost); + $0 = "$av0 - parsing $newaddr [@$newmxhost]"; + print "levels = $levels, level{$u *** $server} = ".$level{"$u *** $server"}."\n" if ($debug > 1); + # If the new server is the current one, + # it would have expanded things for us + # if it could have. Mx records must be + # followed to compare server names. + # We are also done if the recursion + # count has been exceeded. + if (&trhost($newmxhost) eq &trhost($server) || ($levels && $level{"$u *** $server"} >= $levels)) { + if ($valid) { + push(@isValid, &validAddr($newaddr)); + push(@toFinal,$newaddr,$newmxhost,$newname); + } else { + &verbose(&final($newaddr,$newmxhost,$newname)); + } + } else { + # more work to do... + if ($valid) { + push(@isValid, &validAddr($newaddr)); + push(@toExpn,$newmxhost,$newaddr,$newname,$level{"$u *** $server"}); + } else { + &verbose(&expn($newmxhost,$newaddr,$newname,$level{"$u *** $server"})); + } + } + } + last if ($done eq " "); + next; + } + # 550 is a known code... Should the be + # included in -a output? Might be a bug + # here. Does it matter? Can assume that + # there won't be UNKNOWN USER responses + # mixed with valid users? + if ($s =~ /^(550)([- ])/) { + if ($valid) { + print STDERR "\@$server:$u ($oldname) USER UNKNOWN\n"; + } else { + &verbose(&final($u,$server,$oldname,"USER UNKNOWN")); + } + last if ($2 eq " "); + next; + } + # 553 is a known code... + if ($s =~ /^(553)([- ])/) { + if ($valid) { + print STDERR "\@$server:$u ($oldname) USER AMBIGUOUS\n"; + } else { + &verbose(&final($u,$server,$oldname,"USER AMBIGUOUS")); + } + last if ($2 eq " "); + next; + } + # 252 is a known code... + if ($s =~ /^(252)([- ])/) { + if ($valid) { + print STDERR "\@$server:$u ($oldname) REFUSED TO VRFY\n"; + } else { + &verbose(&final($u,$server,$oldname,"REFUSED TO VRFY")); + } + last if ($2 eq " "); + next; + } + &giveup('',"$server: did not grok '$s'",$u); + last USER; + } + + if ($valid) { + # + # now we decide if we are going to take these + # expansions or roll them back. + # + $avgValid = &average(@isValid); + print "avgValid = $avgValid\n" if $debug; + if ($avgValid >= $validRequirement) { + print &compact($u,$server)." ->\n" if $verbose; + while (@toExpn) { + &verbose(&expn(splice(@toExpn,0,4))); + } + while (@toFinal) { + &verbose(&final(splice(@toFinal,0,3))); + } + } else { + print "Tossing some valid to avoid invalid ".&compact($u,$server)."\n" if ($avgValid > 0.0 && ($vw || $debug)); + print &compact($u,$server)." ->\n" if $verbose; + &verbose(&final($u,$server,$newname)); + } + } + } + + &alarm("sending 'quit' to $server",''); + $0 = "$av0 - sending 'quit' to $server"; + &ps("quit"); + while(<S>) { + print if $watch; + last if /^\d+ /; + } + close(S); + alarm(0); +} + +$0 = "$av0 - printing final results"; +print "----------\n" if $vw; +select(STDOUT); +for $f (sort @final) { + print "$f\n"; +} +unlink("/tmp/expn$$"); +exit(0); + + +# abandon all attempts deliver to $server +# register the current addresses as the final ones +sub giveup +{ + local($redirect_okay,$reason,$user) = @_; + local($us,@so,$nh,@remaining_users); + local($pk,$file,$line); + ($pk, $file, $line) = caller; + + $0 = "$av0 - giving up on $server: $reason"; + # + # add back a user if we gave up in the middle + # + push(@users,$user) if $user; + # + # don't bother with this system anymore + # + unless ($giveup{$server}) { + $giveup{$server} = $reason; + print STDERR "$reason\n"; + } + print "Giveup at $file:$line!!! redirect okay = $redirect_okay; $reason\n" if $debug; + # + # Wait! + # Before giving up, see if there is a chance that + # there is another host to redirect to! + # (Kids, don't do this at home! Hacking is a dangerous + # crime and you could end up behind bars.) + # + for $u (@users) { + if ($redirect_okay =~ /\bmx\b/) { + next if &try_fallback('mx',$u,*server, + *mx_secondary, + *already_mx_fellback); + } + if ($redirect_okay =~ /\bdomainify\b/) { + next if &try_fallback('domainify',$u,*server, + *domainify_fallback, + *already_domainify_fellback); + } + push(@remaining_users,$u); + } + @users = @remaining_users; + for $u (@users) { + print &compact($u,$server)." ->\n" if ($verbose && $valid && $u); + &verbose(&final($u,$server,$names{"$u *** $server"},$reason)); + } +} +# +# This routine is used only within &giveup. It checks to +# see if we really have to giveup or if there is a second +# chance because we did something before that can be +# backtracked. +# +# %fallback{"$user *** $host"} tracks what is able to fallback +# %fellback{"$user *** $host"} tracks what has fallen back +# +# If there is a valid backtrack, then queue up the new possibility +# +sub try_fallback +{ + local($method,$user,*host,*fall_table,*fellback) = @_; + local($us,$fallhost,$oldhost,$ft,$i); + + if ($debug > 8) { + print "Fallback table $method:\n"; + for $i (sort keys %fall_table) { + print "\t'$i'\t\t'$fall_table{$i}'\n"; + } + print "Fellback table $method:\n"; + for $i (sort keys %fellback) { + print "\t'$i'\t\t'$fellback{$i}'\n"; + } + print "U: $user H: $host\n"; + } + + $us = "$user *** $host"; + if (defined $fellback{$us}) { + # + # Undo a previous fallback so that we can try again + # Nested fallbacks are avoided because they could + # lead to infinite loops + # + $fallhost = $fellback{$us}; + print "Already $method fell back from $us -> \n" if $debug; + $us = "$user *** $fallhost"; + $oldhost = $fallhost; + } elsif (($method eq 'mx') && (defined $mxbacktrace{$us}) && (defined $mx_secondary{$mxbacktrace{$us}})) { + print "Fallback an MX expansion $us -> \n" if $debug; + $oldhost = $mxbacktrace{$us}; + } else { + print "Oldhost($host, $us) = " if $debug; + $oldhost = $host; + } + print "$oldhost\n" if $debug; + if (((defined $fall_table{$us}) && ($ft = $us)) || ((defined $fall_table{$oldhost}) && ($ft = $oldhost))) { + print "$method Fallback = ".$fall_table{$ft}."\n" if $debug; + local(@so,$newhost); + @so = split(' ',$fall_table{$ft}); + $newhost = shift(@so); + print "Falling back ($method) $us -> $newhost (from $oldhost)\n" if $debug; + if ($method eq 'mx') { + if (! defined ($mxbacktrace{"$user *** $newhost"})) { + if (defined $mxbacktrace{"$user *** $oldhost"}) { + print "resetting oldhost $oldhost to the original: " if $debug; + $oldhost = $mxbacktrace{"$user *** $oldhost"}; + print "$oldhost\n" if $debug; + } + $mxbacktrace{"$user *** $newhost"} = $oldhost; + print "mxbacktrace $user *** $newhost -> $oldhost\n" if $debug; + } + $mx{&trhost($oldhost)} = $newhost; + } else { + $temporary_redirect{$us} = $newhost; + } + if (@so) { + print "Can still $method $us: @so\n" if $debug; + $fall_table{$ft} = join(' ',@so); + } else { + print "No more fallbacks for $us\n" if $debug; + delete $fall_table{$ft}; + } + if (defined $create_host_backtrack{$us}) { + $create_host_backtrack{"$user *** $newhost"} + = $create_host_backtrack{$us}; + } + $fellback{"$user *** $newhost"} = $oldhost; + &expn($newhost,$user,$names{$us},$level{$us}); + return 1; + } + delete $temporary_redirect{$us}; + $host = $oldhost; + return 0; +} +# return 1 if you could send mail to the address as is. +sub validAddr +{ + local($addr) = @_; + $res = &do_validAddr($addr); + print "validAddr($addr) = $res\n" if $debug; + $res; +} +sub do_validAddr +{ + local($addr) = @_; + local($urx) = "[-A-Za-z_.0-9+]+"; + + # \u + return 0 if ($addr =~ /^\\/); + # ?@h + return 1 if ($addr =~ /.\@$urx$/); + # @h:? + return 1 if ($addr =~ /^\@$urx\:./); + # h!u + return 1 if ($addr =~ /^$urx!./); + # u + return 1 if ($addr =~ /^$urx$/); + # ? + print "validAddr($addr) = ???\n" if $debug; + return 0; +} +# Some systems use expn and vrfy interchangeably. Some only +# implement one or the other. Some check expn against mailing +# lists and vrfy against users. It doesn't appear to be +# consistent. +# +# So, what do we do? We try everything! +# +# +# Ranking of result codes: good: 250, 251/551, 252, 550, anything else +# +# Ranking of inputs: best: user@host.domain, okay: user +# +# Return value: $error_string, @responses_from_server +sub expn_vrfy +{ + local($u,$server) = @_; + local(@c) = ('expn', 'vrfy'); + local(@try_u) = $u; + local(@ret,$code); + + if (($u =~ /(.+)@(.+)/) && (&trhost($2) eq &trhost($server))) { + push(@try_u,$1); + } + + TRY: + for $c (@c) { + for $try_u (@try_u) { + &alarm("${c}'ing $try_u on $server",'',$u); + &ps("$c $try_u"); + alarm(0); + $s = <S>; + if ($s eq '') { + return "$server: lost connection"; + } + if ($s !~ /^(\d+)([- ])/) { + return "$server: garbled reply to '$c $try_u'"; + } + if ($1 == 250) { + $code = 250; + @ret = ("",$s); + push(@ret,&read_response($2,$debug)); + return (@ret); + } + if ($1 == 551 || $1 == 251) { + $code = $1; + @ret = ("",$s); + push(@ret,&read_response($2,$debug)); + next; + } + if ($1 == 252 && ($code == 0 || $code == 550)) { + $code = 252; + @ret = ("",$s); + push(@ret,&read_response($2,$watch)); + next; + } + if ($1 == 550 && $code == 0) { + $code = 550; + @ret = ("",$s); + push(@ret,&read_response($2,$watch)); + next; + } + &read_response($2,$watch); + } + } + return "$server: expn/vrfy not implemented" unless @ret; + return @ret; +} +# sometimes the old parse routine (now parse2) didn't +# reject funky addresses. +sub parse +{ + local($oldaddr,$server,$oldname,$one_to_one) = @_; + local($newhost, $newaddr, $newname, $um) = &parse2($oldaddr,$server,$oldname,$one_to_one); + if ($newaddr =~ m,^["/],) { + return (undef, $oldaddr, $newname) if $valid; + return (undef, $um, $newname); + } + return ($newhost, $newaddr, $newname); +} + +# returns ($new_smtp_server,$new_address,$new_name) +# given a response from a SMTP server ($newaddr), the +# current host ($server), the old "name" and a flag that +# indicates if it is being called during the initial +# command line parsing ($parsing_args) +sub parse2 +{ + local($newaddr,$context_host,$old_name,$parsing_args) = @_; + local(@names) = $old_name; + local($urx) = "[-A-Za-z_.0-9+]+"; + local($unmangle); + + # + # first, separate out the address part. + # + + # + # [NAME] <ADDR [(NAME)]> + # [NAME] <[(NAME)] ADDR + # ADDR [(NAME)] + # (NAME) ADDR + # [(NAME)] <ADDR> + # + if ($newaddr =~ /^\<(.*)\>$/) { + print "<A:$1>\n" if $debug; + ($newaddr) = &trim($1); + print "na = $newaddr\n" if $debug; + } + if ($newaddr =~ /^([^\<\>]*)\<([^\<\>]*)\>([^\<\>]*)$/) { + # address has a < > pair in it. + print "N:$1 <A:$2> N:$3\n" if $debug; + ($newaddr) = &trim($2); + unshift(@names, &trim($3,$1)); + print "na = $newaddr\n" if $debug; + } + if ($newaddr =~ /^([^\(\)]*)\(([^\(\)]*)\)([^\(\)]*)$/) { + # address has a ( ) pair in it. + print "A:$1 (N:$2) A:$3\n" if $debug; + unshift(@names,&trim($2)); + local($f,$l) = (&trim($1),&trim($3)); + if (($f && $l) || !($f || $l)) { + # address looks like: + # foo (bar) baz or (bar) + # not allowed! + print STDERR "Could not parse $newaddr\n" if $vw; + return(undef,$newaddr,&firstname(@names)); + } + $newaddr = $f if $f; + $newaddr = $l if $l; + print "newaddr now = $newaddr\n" if $debug; + } + # + # @foo:bar + # j%k@l + # a@b + # b!a + # a + # + $unmangle = $newaddr; + if ($newaddr =~ /^\@($urx)\:(.+)$/) { + print "(\@:)" if $debug; + # this is a bit of a cheat, but it seems necessary + return (&domainify($1,$context_host,$2),$2,&firstname(@names),$unmangle); + } + if ($newaddr =~ /^(.+)\@($urx)$/) { + print "(\@)" if $debug; + return (&domainify($2,$context_host,$newaddr),$newaddr,&firstname(@names),$unmangle); + } + if ($parsing_args) { + if ($newaddr =~ /^($urx)\!(.+)$/) { + return (&domainify($1,$context_host,$newaddr),$newaddr,&firstname(@names),$unmangle); + } + if ($newaddr =~ /^($urx)$/) { + return ($context_host,$newaddr,&firstname(@names),$unmangle); + } + print STDERR "Could not parse $newaddr\n"; + } + print "(?)" if $debug; + return(undef,$newaddr,&firstname(@names),$unmangle); +} +# return $u (@$server) unless $u includes reference to $server +sub compact +{ + local($u, $server) = @_; + local($se) = $server; + local($sp); + $se =~ s/(\W)/\\$1/g; + $sp = " (\@$server)"; + if ($u !~ /$se/i) { + return "$u$sp"; + } + return $u; +} +# remove empty (spaces don't count) members from an array +sub trim +{ + local(@v) = @_; + local($v,@r); + for $v (@v) { + $v =~ s/^\s+//; + $v =~ s/\s+$//; + push(@r,$v) if ($v =~ /\S/); + } + return(@r); +} +# using the host part of an address, and the server name, add the +# servers' domain to the address if it doesn't already have a +# domain. Since this sometimes fails, save a back reference so +# it can be unrolled. +sub domainify +{ + local($host,$domain_host,$u) = @_; + local($domain,$newhost); + + # cut of trailing dots + $host =~ s/\.$//; + $domain_host =~ s/\.$//; + + if ($domain_host !~ /\./) { + # + # domain host isn't, keep $host whatever it is + # + print "domainify($host,$domain_host) = $host\n" if $debug; + return $host; + } + + # + # There are several weird situtations that need to be + # accounted for. They have to do with domain relay hosts. + # + # Examples: + # host server "right answer" + # + # shiva.cs cs.berkeley.edu shiva.cs.berkeley.edu + # shiva cs.berkeley.edu shiva.cs.berekley.edu + # cumulus reed.edu @reed.edu:cumulus.uucp + # tiberius tc.cornell.edu tiberius.tc.cornell.edu + # + # The first try must always be to cut the domain part out of + # the server and tack it onto the host. + # + # A reasonable second try is to tack the whole server part onto + # the host and for each possible repeated element, eliminate + # just that part. + # + # These extra "guesses" get put into the %domainify_fallback + # array. They will be used to give addresses a second chance + # in the &giveup routine + # + + local(%fallback); + + local($long); + $long = "$host $domain_host"; + $long =~ tr/A-Z/a-z/; + print "long = $long\n" if $debug; + if ($long =~ s/^([^ ]+\.)([^ ]+) \2(\.[^ ]+\.[^ ]+)/$1$2$3/) { + # matches shiva.cs cs.berkeley.edu and returns shiva.cs.berkeley.edu + print "condensed fallback $host $domain_host -> $long\n" if $debug; + $fallback{$long} = 9; + } + + local($fh); + $fh = $domain_host; + while ($fh =~ /\./) { + print "FALLBACK $host.$fh = 1\n" if $debug > 7; + $fallback{"$host.$fh"} = 1; + $fh =~ s/^[^\.]+\.//; + } + + $fallback{"$host.$domain_host"} = 2; + + ($domain = $domain_host) =~ s/^[^\.]+//; + $fallback{"$host$domain"} = 6 + if ($domain =~ /\./); + + if ($host =~ /\./) { + # + # Host is already okay, but let's look for multiple + # interpretations + # + print "domainify($host,$domain_host) = $host\n" if $debug; + delete $fallback{$host}; + $domainify_fallback{"$u *** $host"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback; + return $host; + } + + $domain = ".$domain_host" + if ($domain !~ /\..*\./); + $newhost = "$host$domain"; + + $create_host_backtrack{"$u *** $newhost"} = $domain_host; + print "domainify($host,$domain_host) = $newhost\n" if $debug; + delete $fallback{$newhost}; + $domainify_fallback{"$u *** $newhost"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback; + if ($debug) { + print "fallback = "; + print $domainify_fallback{"$u *** $newhost"} + if defined($domainify_fallback{"$u *** $newhost"}); + print "\n"; + } + return $newhost; +} +# return the first non-empty element of an array +sub firstname +{ + local(@names) = @_; + local($n); + while(@names) { + $n = shift(@names); + return $n if $n =~ /\S/; + } + return undef; +} +# queue up more addresses to expand +sub expn +{ + local($host,$addr,$name,$level) = @_; + if ($host) { + $host = &trhost($host); + + if (($debug > 3) || (defined $giveup{$host})) { + unshift(@hosts,$host) unless $users{$host}; + } else { + push(@hosts,$host) unless $users{$host}; + } + $users{$host} .= " $addr"; + $names{"$addr *** $host"} = $name; + $level{"$addr *** $host"} = $level + 1; + print "expn($host,$addr,$name)\n" if $debug; + return "\t$addr\n"; + } else { + return &final($addr,'NONE',$name); + } +} +# compute the numerical average value of an array +sub average +{ + local(@e) = @_; + return 0 unless @e; + local($e,$sum); + for $e (@e) { + $sum += $e; + } + $sum / @e; +} +# print to the server (also to stdout, if -w) +sub ps +{ + local($p) = @_; + print ">>> $p\n" if $watch; + print S "$p\n"; +} +# return case-adjusted name for a host (for comparison purposes) +sub trhost +{ + # treat foo.bar as an alias for Foo.BAR + local($host) = @_; + local($trhost) = $host; + $trhost =~ tr/A-Z/a-z/; + if ($trhost{$trhost}) { + $host = $trhost{$trhost}; + } else { + $trhost{$trhost} = $host; + } + $trhost{$trhost}; +} +# re-queue users if an mx record dictates a redirect +# don't allow a user to be redirected more than once +sub mxredirect +{ + local($server,*users) = @_; + local($u,$nserver,@still_there); + + $nserver = &mx($server); + + if (&trhost($nserver) ne &trhost($server)) { + $0 = "$av0 - mx redirect $server -> $nserver\n"; + for $u (@users) { + if (defined $mxbacktrace{"$u *** $nserver"}) { + push(@still_there,$u); + } else { + $mxbacktrace{"$u *** $nserver"} = $server; + print "mxbacktrace{$u *** $nserver} = $server\n" + if ($debug > 1); + &expn($nserver,$u,$names{"$u *** $server"}); + } + } + @users = @still_there; + if (! @users) { + return $nserver; + } else { + return undef; + } + } + return undef; +} +# follow mx records, return a hostname +# also follow temporary redirections comming from &domainify and +# &mxlookup +sub mx +{ + local($h,$u) = @_; + + for (;;) { + if (defined $mx{&trhost($h)} && $h ne $mx{&trhost($h)}) { + $0 = "$av0 - mx expand $h"; + $h = $mx{&trhost($h)}; + return $h; + } + if ($u) { + if (defined $temporary_redirect{"$u *** $h"}) { + $0 = "$av0 - internal redirect $h"; + print "Temporary redirect taken $u *** $h -> " if $debug; + $h = $temporary_redirect{"$u *** $h"}; + print "$h\n" if $debug; + next; + } + $htr = &trhost($h); + if (defined $temporary_redirect{"$u *** $htr"}) { + $0 = "$av0 - internal redirect $h"; + print "temporary redirect taken $u *** $h -> " if $debug; + $h = $temporary_redirect{"$u *** $htr"}; + print "$h\n" if $debug; + next; + } + } + return $h; + } +} +# look up mx records with the name server. +# re-queue expansion requests if possible +# optionally give up on this host. +sub mxlookup +{ + local($lastchance,$server,$giveup,*users) = @_; + local(*T); + local(*NSLOOKUP); + local($nh, $pref,$cpref); + local($o0) = $0; + local($nserver); + local($name,$aliases,$type,$len,$thataddr); + local(%fallback); + + return 1 if &mxredirect($server,*users); + + if ((defined $mx{$server}) || (! $have_nslookup)) { + return 0 unless $lastchance; + &giveup('mx domainify',$giveup); + return 0; + } + + $0 = "$av0 - nslookup of $server"; + open(T,">/tmp/expn$$") || die "open > /tmp/expn$$: $!\n"; + print T "set querytype=MX\n"; + print T "$server\n"; + close(T); + $cpref = 1.0E12; + undef $nserver; + open(NSLOOKUP,"nslookup < /tmp/expn$$ 2>&1 |") || die "open nslookup: $!"; + while(<NSLOOKUP>) { + print if ($debug > 2); + if (/mail exchanger = ([-A-Za-z_.0-9+]+)/) { + $nh = $1; + if (/preference = (\d+)/) { + $pref = $1; + if ($pref < $cpref) { + $nserver = $nh; + $cpref = $pref; + } elsif ($pref) { + $fallback{$pref} .= " $nh"; + } + } + } + if (/Non-existent domain/) { + # + # These addresss are hosed. Kaput! Dead! + # However, if we created the address in the + # first place then there is a chance of + # salvation. + # + 1 while(<NSLOOKUP>); + close(NSLOOKUP); + return 0 unless $lastchance; + &giveup('domainify',"$server: Non-existent domain",undef,1); + return 0; + } + + } + close(NSLOOKUP); + unlink("/tmp/expn$$"); + unless ($nserver) { + $0 = "$o0 - finished mxlookup"; + return 0 unless $lastchance; + &giveup('mx domainify',"$server: Could not resolve address"); + return 0; + } + + # provide fallbacks in case $nserver doesn't work out + if (defined $fallback{$cpref}) { + $mx_secondary{$server} = $fallback{$cpref}; + } + + $0 = "$av0 - gethostbyname($nserver)"; + ($name,$aliases,$type,$len,$thataddr) = gethostbyname($nserver); + + unless ($thataddr) { + $0 = $o0; + return 0 unless $lastchance; + &giveup('mx domainify',"$nserver: could not resolve address"); + return 0; + } + print "MX($server) = $nserver\n" if $debug; + print "$server -> $nserver\n" if $vw && !$debug; + $mx{&trhost($server)} = $nserver; + # redeploy the users + unless (&mxredirect($server,*users)) { + return 0 unless $lastchance; + &giveup('mx domainify',"$nserver: only one level of mx redirect allowed"); + return 0; + } + $0 = "$o0 - finished mxlookup"; + return 1; +} +# if mx expansion did not help to resolve an address +# (ie: foo@bar became @baz:foo@bar, then undo the +# expansion). +# this is only used by &final +sub mxunroll +{ + local(*host,*addr) = @_; + local($r) = 0; + print "looking for mxbacktrace{$addr *** $host}\n" + if ($debug > 1); + while (defined $mxbacktrace{"$addr *** $host"}) { + print "Unrolling MX expnasion: \@$host:$addr -> " + if ($debug || $verbose); + $host = $mxbacktrace{"$addr *** $host"}; + print "\@$host:$addr\n" + if ($debug || $verbose); + $r = 1; + } + return 1 if $r; + $addr = "\@$host:$addr" + if ($host =~ /\./); + return 0; +} +# register a completed expnasion. Make the final address as +# simple as possible. +sub final +{ + local($addr,$host,$name,$error) = @_; + local($he); + local($hb,$hr); + local($au,$ah); + + if ($error =~ /Non-existent domain/) { + # + # If we created the domain, then let's undo the + # damage... + # + if (defined $create_host_backtrack{"$addr *** $host"}) { + while (defined $create_host_backtrack{"$addr *** $host"}) { + print "Un&domainifying($host) = " if $debug; + $host = $create_host_backtrack{"$addr *** $host"}; + print "$host\n" if $debug; + } + $error = "$host: could not locate"; + } else { + # + # If we only want valid addresses, toss out + # bad host names. + # + if ($valid) { + print STDERR "\@$host:$addr ($name) Non-existent domain\n"; + return ""; + } + } + } + + MXUNWIND: { + $0 = "$av0 - final parsing of \@$host:$addr"; + ($he = $host) =~ s/(\W)/\\$1/g; + if ($addr !~ /@/) { + # addr does not contain any host + $addr = "$addr@$host"; + } elsif ($addr !~ /$he/i) { + # if host part really something else, use the something + # else. + if ($addr =~ m/(.*)\@([^\@]+)$/) { + ($au,$ah) = ($1,$2); + print "au = $au ah = $ah\n" if $debug; + if (defined $temporary_redirect{"$addr *** $ah"}) { + $addr = "$au\@".$temporary_redirect{"$addr *** $ah"}; + print "Rewrite! to $addr\n" if $debug; + next MXUNWIND; + } + } + # addr does not contain full host + if ($valid) { + if ($host =~ /^([^\.]+)(\..+)$/) { + # host part has a . in it - foo.bar + ($hb, $hr) = ($1, $2); + if ($addr =~ /\@([^\.\@]+)$/ && ($1 eq $hb)) { + # addr part has not . + # and matches beginning of + # host part -- tack on a + # domain name. + $addr .= $hr; + } else { + &mxunroll(*host,*addr) + && redo MXUNWIND; + } + } else { + &mxunroll(*host,*addr) + && redo MXUNWIND; + } + } else { + $addr = "${addr}[\@$host]" + if ($host =~ /\./); + } + } + } + $name = "$name " if $name; + $error = " $error" if $error; + if ($valid) { + push(@final,"$name<$addr>"); + } else { + push(@final,"$name<$addr>$error"); + } + "\t$name<$addr>$error\n"; +} + +sub alarm +{ + local($alarm_action,$alarm_redirect,$alarm_user) = @_; + alarm(3600); + $SIG{ALRM} = 'handle_alarm'; +} +# this involves one great big ugly hack. +# the "next HOST" unwinds the stack! +sub handle_alarm +{ + &giveup($alarm_redirect,"Timed out during $alarm_action",$alarm_user); + next HOST; +} + +# read the rest of the current smtp daemon's response (and toss it away) +sub read_response +{ + local($done,$watch) = @_; + local(@resp); + print $s if $watch; + while(($done eq "-") && ($s = <S>) && ($s =~ /^\d+([- ])/)) { + print $s if $watch; + $done = $1; + push(@resp,$s); + } + return @resp; +} +# print args if verbose. Return them in any case +sub verbose +{ + local(@tp) = @_; + print "@tp" if $verbose; +} +# to pass perl -w: +@tp; +$flag_a; +$flag_d; +$flag_1; +%already_domainify_fellback; +%already_mx_fellback; +&handle_alarm; +################### BEGIN PERL/TROFF TRANSITION +.00 ; + +'di +.nr nl 0-1 +.nr % 0 +.\\"'; __END__ +.\" ############## END PERL/TROFF TRANSITION +.TH EXPN 1 "March 11, 1993" +.AT 3 +.SH NAME +expn \- recursively expand mail aliases +.SH SYNOPSIS +.B expn +.RI [ -a ] +.RI [ -v ] +.RI [ -w ] +.RI [ -d ] +.RI [ -1 ] +.IR user [@ hostname ] +.RI [ user [@ hostname ]]... +.SH DESCRIPTION +.B expn +will use the SMTP +.B expn +and +.B vrfy +commands to expand mail aliases. +It will first look up the addresses you provide on the command line. +If those expand into addresses on other systems, it will +connect to the other systems and expand again. It will keep +doing this until no further expansion is possible. +.SH OPTIONS +The default output of +.B expn +can contain many lines which are not valid +email addresses. With the +.I -aa +flag, only expansions that result in legal addresses +are used. Since many mailing lists have an illegal +address or two, the single +.IR -a , +address, flag specifies that a few illegal addresses can +be mixed into the results. More +.I -a +flags vary the ratio. Read the source to track down +the formula. With the +.I -a +option, you should be able to construct a new mailing +list out of an existing one. +.LP +If you wish to limit the number of levels deep that +.B expn +will recurse as it traces addresses, use the +.I -1 +option. For each +.I -1 +another level will be traversed. So, +.I -111 +will traverse no more than three levels deep. +.LP +The normal mode of operation for +.B expn +is to do all of its work silently. +The following options make it more verbose. +It is not necessary to make it verbose to see what it is +doing because as it works, it changes its +.BR argv [0] +variable to reflect its current activity. +To see how it is expanding things, the +.IR -v , +verbose, flag will cause +.B expn +to show each address before +and after translation as it works. +The +.IR -w , +watch, flag will cause +.B expn +to show you its conversations with the mail daemons. +Finally, the +.IR -d , +debug, flag will expose many of the inner workings so that +it is possible to eliminate bugs. +.SH ENVIRONMENT +No enviroment variables are used. +.SH FILES +.PD 0 +.B /tmp/expn$$ +.B temporary file used as input to +.BR nslookup . +.SH SEE ALSO +.BR aliases (5), +.BR sendmail (8), +.BR nslookup (8), +RFC 823, and RFC 1123. +.SH BUGS +Not all mail daemons will implement +.B expn +or +.BR vrfy . +It is not possible to verify addresses that are served +by such daemons. +.LP +When attempting to connect to a system to verify an address, +.B expn +only tries one IP address. Most mail daemons +will try harder. +.LP +It is assumed that you are running domain names and that +the +.BR nslookup (8) +program is available. If not, +.B expn +will not be able to verify many addresses. It will also pause +for a long time unless you change the code where it says +.I $have_nslookup = 1 +to read +.I $have_nslookup = +.IR 0 . +.LP +Lastly, +.B expn +does not handle every valid address. If you have an example, +please submit a bug report. +.SH CREDITS +In 1986 or so, Jon Broome wrote a program of the same name +that did about the same thing. It has since suffered bit rot +and Jon Broome has dropped off the face of the earth! +(Jon, if you are out there, drop me a line) +.SH AVAILABILITY +The latest version of +.B expn +is available through anonymous ftp at +.IR ftp://ftp.idiom.com/pub/muir-programs/expn . +.SH AUTHOR +.I David Muir Sharnoff\ \ \ \ <muir@idiom.com> diff --git a/contrib/amd/scripts/expn.in b/contrib/amd/scripts/expn.in new file mode 100755 index 0000000..b2bd1b6 --- /dev/null +++ b/contrib/amd/scripts/expn.in @@ -0,0 +1,1370 @@ +#!@PERL@ +'di '; +'ds 00 \\"'; +'ig 00 '; +# +# THIS PROGRAM IS ITS OWN MANUAL PAGE. INSTALL IN man & bin. +# + +# hardcoded constants, should work fine for BSD-based systems +#require 'sys/socket.ph'; # perl 4 +use Socket; # perl 5 +$AF_INET = &AF_INET; +$SOCK_STREAM = &SOCK_STREAM; +$sockaddr = 'S n a4 x8'; + +# system requirements: +# must have 'nslookup' and 'hostname' programs. + +# $Header: /home/muir/bin/RCS/expn,v 3.9 1995/10/02 17:51:35 muir Exp muir $ + +# TODO: +# less magic should apply to command-line addresses +# less magic should apply to local addresses +# add magic to deal with cross-domain cnames + +# Checklist: (hard addresses) +# 250 Kimmo Suominen <"|/usr/local/mh/lib/slocal -user kim"@grendel.tac.nyc.ny.us> +# harry@hofmann.cs.Berkeley.EDU -> harry@tenet (.berkeley.edu) [dead] +# bks@cs.berkeley.edu -> shiva.CS (.berkeley.edu) [dead] +# dan@tc.cornell.edu -> brown@tiberius (.tc.cornell.edu) + +############################################################################# +# +# Copyright (c) 1993 David Muir Sharnoff +# All rights reserved. +# +# 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 David Muir Sharnoff. +# 4. The name of David Sharnoff may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE DAVID MUIR SHARNOFF ``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 DAVID MUIR SHARNOFF 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. +# +# This copyright notice derrived from material copyrighted by the Regents +# of the University of California. +# +# Contributions accepted. +# +############################################################################# + +# overall structure: +# in an effort to not trace each address individually, but rather +# ask each server in turn a whole bunch of questions, addresses to +# be expanded are queued up. +# +# This means that all accounting w.r.t. an address must be stored in +# various arrays. Generally these arrays are indexed by the +# string "$addr *** $server" where $addr is the address to be +# expanded "foo" or maybe "foo@bar" and $server is the hostname +# of the SMTP server to contact. +# + +# important global variables: +# +# @hosts : list of servers still to be contacted +# $server : name of the current we are currently looking at +# @users = $users{@hosts[0]} : addresses to expand at this server +# $u = $users[0] : the current address being expanded +# $names{"$users[0] *** $server"} : the 'name' associated with the address +# $mxbacktrace{"$users[0] *** $server"} : record of mx expansion +# $mx_secondary{$server} : other mx relays at the same priority +# $domainify_fallback{"$users[0] *** $server"} : alternative names to try +# instead of $server if $server doesn't work +# $temporary_redirect{"$users[0] *** $server"} : when trying alternates, +# temporarily channel all tries along current path +# $giveup{$server} : do not bother expanding addresses at $server +# $verbose : -v +# $watch : -w +# $vw : -v or -w +# $debug : -d +# $valid : -a +# $levels : -1 +# S : the socket connection to $server + +$have_nslookup = 1; # we have the nslookup program +$port = 'smtp'; +$av0 = $0; +$ENV{'PATH'} .= ":/usr/etc" unless $ENV{'PATH'} =~ m,/usr/etc,; +$ENV{'PATH'} .= ":/usr/ucb" unless $ENV{'PATH'} =~ m,/usr/ucb,; +select(STDERR); + +$0 = "$av0 - running hostname"; +chop($name = `hostname || uname -n`); + +$0 = "$av0 - lookup host FQDN and IP addr"; +($hostname,$aliases,$type,$len,$thisaddr) = gethostbyname($name); + +$0 = "$av0 - parsing args"; +$usage = "Usage: $av0 [-1avwd] user[\@host] [user2[host2] ...]"; +for $a (@ARGV) { + die $usage if $a eq "-"; + while ($a =~ s/^(-.*)([1avwd])/$1/) { + eval '$'."flag_$2 += 1"; + } + next if $a eq "-"; + die $usage if $a =~ /^-/; + &expn(&parse($a,$hostname,undef,1)); +} +$verbose = $flag_v; +$watch = $flag_w; +$vw = $flag_v + $flag_w; +$debug = $flag_d; +$valid = $flag_a; +$levels = $flag_1; + +die $usage unless @hosts; +if ($valid) { + if ($valid == 1) { + $validRequirement = 0.8; + } elsif ($valid == 2) { + $validRequirement = 1.0; + } elsif ($valid == 3) { + $validRequirement = 0.9; + } else { + $validRequirement = (1 - (1/($valid-3))); + print "validRequirement = $validRequirement\n" if $debug; + } +} + +$0 = "$av0 - building local socket"; +($name,$aliases,$proto) = getprotobyname('tcp'); +($name,$aliases,$port) = getservbyname($port,'tcp') + unless $port =~ /^\d+/; +$this = pack($sockaddr, &AF_INET, 0, $thisaddr); + +HOST: +while (@hosts) { + $server = shift(@hosts); + @users = split(' ',$users{$server}); + delete $users{$server}; + + # is this server already known to be bad? + $0 = "$av0 - looking up $server"; + if ($giveup{$server}) { + &giveup('mx domainify',$giveup{$server}); + next; + } + + # do we already have an mx record for this host? + next HOST if &mxredirect($server,*users); + + # look it up, or try for an mx. + $0 = "$av0 - gethostbyname($server)"; + + ($name,$aliases,$type,$len,$thataddr) = gethostbyname($server); + # if we can't get an A record, try for an MX record. + unless($thataddr) { + &mxlookup(1,$server,"$server: could not resolve name",*users); + next HOST; + } + + # get a connection, or look for an mx + $0 = "$av0 - socket to $server"; + $that = pack($sockaddr, &AF_INET, $port, $thataddr); + socket(S, &AF_INET, &SOCK_STREAM, $proto) + || die "socket: $!"; + $0 = "$av0 - bind to $server"; + bind(S, $this) + || die "bind $hostname,0: $!"; + $0 = "$av0 - connect to $server"; + print "debug = $debug server = $server\n" if $debug > 8; + if (! connect(S, $that) || ($debug == 10 && $server =~ /relay\d.UU.NET$/i)) { + $0 = "$av0 - $server: could not connect: $!\n"; + $emsg = $!; + unless (&mxlookup(0,$server,"$server: could not connect: $!",*users)) { + &giveup('mx',"$server: Could not connect: $emsg"); + } + next HOST; + } + select((select(S),$| = 1)[0]); # don't buffer output to S + + # read the greeting + $0 = "$av0 - talking to $server"; + &alarm("greeting with $server",''); + while(<S>) { + alarm(0); + print if $watch; + if (/^(\d+)([- ])/) { + if ($1 != 220) { + $0 = "$av0 - bad numeric response from $server"; + &alarm("giving up after bad response from $server",''); + &read_response($2,$watch); + alarm(0); + print STDERR "$server: NOT 220 greeting: $_" + if ($debug || $vw); + if (&mxlookup(0,$server,"$server: did not respond with a 220 greeting",*users)) { + close(S); + next HOST; + } + } + last if ($2 eq " "); + } else { + $0 = "$av0 - bad response from $server"; + print STDERR "$server: NOT 220 greeting: $_" + if ($debug || $vw); + unless (&mxlookup(0,$server,"$server: did not respond with SMTP codes",*users)) { + &giveup('',"$server: did not talk SMTP"); + } + close(S); + next HOST; + } + &alarm("greeting with $server",''); + } + alarm(0); + + # if this causes problems, remove it + $0 = "$av0 - sending helo to $server"; + &alarm("sending helo to $server",""); + &ps("helo $hostname"); + while(<S>) { + print if $watch; + last if /^\d+ /; + } + alarm(0); + + # try the users, one by one + USER: + while(@users) { + $u = shift(@users); + $0 = "$av0 - expanding $u [\@$server]"; + + # do we already have a name for this user? + $oldname = $names{"$u *** $server"}; + + print &compact($u,$server)." ->\n" if ($verbose && ! $valid); + if ($valid) { + # + # when running with -a, we delay taking any action + # on the results of our query until we have looked + # at the complete output. @toFinal stores expansions + # that will be final if we take them. @toExpn stores + # expnansions that are not final. @isValid keeps + # track of our ability to send mail to each of the + # expansions. + # + @isValid = (); + @toFinal = (); + @toExpn = (); + } + +# ($ecode,@expansion) = &expn_vrfy($u,$server); + (@foo) = &expn_vrfy($u,$server); + ($ecode,@expansion) = @foo; + if ($ecode) { + &giveup('',$ecode,$u); + last USER; + } + + for $s (@expansion) { + $s =~ s/[\n\r]//g; + $0 = "$av0 - parsing $server: $s"; + + $skipwatch = $watch; + + if ($s =~ /^[25]51([- ]).*<(.+)>/) { + print "$s" if $watch; + print "(pretending 250$1<$2>)" if ($debug && $watch); + print "\n" if $watch; + $s = "250$1<$2>"; + $skipwatch = 0; + } + + if ($s =~ /^250([- ])(.+)/) { + print "$s\n" if $skipwatch; + ($done,$addr) = ($1,$2); + ($newhost, $newaddr, $newname) = &parse($addr,$server,$oldname, $#expansion == 0); + print "($newhost, $newaddr, $newname) = &parse($addr, $server, $oldname)\n" if $debug; + if (! $newhost) { + # no expansion is possible w/o a new server to call + if ($valid) { + push(@isValid, &validAddr($newaddr)); + push(@toFinal,$newaddr,$server,$newname); + } else { + &verbose(&final($newaddr,$server,$newname)); + } + } else { + $newmxhost = &mx($newhost,$newaddr); + print "$newmxhost = &mx($newhost)\n" + if ($debug && $newhost ne $newmxhost); + $0 = "$av0 - parsing $newaddr [@$newmxhost]"; + print "levels = $levels, level{$u *** $server} = ".$level{"$u *** $server"}."\n" if ($debug > 1); + # If the new server is the current one, + # it would have expanded things for us + # if it could have. Mx records must be + # followed to compare server names. + # We are also done if the recursion + # count has been exceeded. + if (&trhost($newmxhost) eq &trhost($server) || ($levels && $level{"$u *** $server"} >= $levels)) { + if ($valid) { + push(@isValid, &validAddr($newaddr)); + push(@toFinal,$newaddr,$newmxhost,$newname); + } else { + &verbose(&final($newaddr,$newmxhost,$newname)); + } + } else { + # more work to do... + if ($valid) { + push(@isValid, &validAddr($newaddr)); + push(@toExpn,$newmxhost,$newaddr,$newname,$level{"$u *** $server"}); + } else { + &verbose(&expn($newmxhost,$newaddr,$newname,$level{"$u *** $server"})); + } + } + } + last if ($done eq " "); + next; + } + # 550 is a known code... Should the be + # included in -a output? Might be a bug + # here. Does it matter? Can assume that + # there won't be UNKNOWN USER responses + # mixed with valid users? + if ($s =~ /^(550)([- ])/) { + if ($valid) { + print STDERR "\@$server:$u ($oldname) USER UNKNOWN\n"; + } else { + &verbose(&final($u,$server,$oldname,"USER UNKNOWN")); + } + last if ($2 eq " "); + next; + } + # 553 is a known code... + if ($s =~ /^(553)([- ])/) { + if ($valid) { + print STDERR "\@$server:$u ($oldname) USER AMBIGUOUS\n"; + } else { + &verbose(&final($u,$server,$oldname,"USER AMBIGUOUS")); + } + last if ($2 eq " "); + next; + } + # 252 is a known code... + if ($s =~ /^(252)([- ])/) { + if ($valid) { + print STDERR "\@$server:$u ($oldname) REFUSED TO VRFY\n"; + } else { + &verbose(&final($u,$server,$oldname,"REFUSED TO VRFY")); + } + last if ($2 eq " "); + next; + } + &giveup('',"$server: did not grok '$s'",$u); + last USER; + } + + if ($valid) { + # + # now we decide if we are going to take these + # expansions or roll them back. + # + $avgValid = &average(@isValid); + print "avgValid = $avgValid\n" if $debug; + if ($avgValid >= $validRequirement) { + print &compact($u,$server)." ->\n" if $verbose; + while (@toExpn) { + &verbose(&expn(splice(@toExpn,0,4))); + } + while (@toFinal) { + &verbose(&final(splice(@toFinal,0,3))); + } + } else { + print "Tossing some valid to avoid invalid ".&compact($u,$server)."\n" if ($avgValid > 0.0 && ($vw || $debug)); + print &compact($u,$server)." ->\n" if $verbose; + &verbose(&final($u,$server,$newname)); + } + } + } + + &alarm("sending 'quit' to $server",''); + $0 = "$av0 - sending 'quit' to $server"; + &ps("quit"); + while(<S>) { + print if $watch; + last if /^\d+ /; + } + close(S); + alarm(0); +} + +$0 = "$av0 - printing final results"; +print "----------\n" if $vw; +select(STDOUT); +for $f (sort @final) { + print "$f\n"; +} +unlink("/tmp/expn$$"); +exit(0); + + +# abandon all attempts deliver to $server +# register the current addresses as the final ones +sub giveup +{ + local($redirect_okay,$reason,$user) = @_; + local($us,@so,$nh,@remaining_users); + local($pk,$file,$line); + ($pk, $file, $line) = caller; + + $0 = "$av0 - giving up on $server: $reason"; + # + # add back a user if we gave up in the middle + # + push(@users,$user) if $user; + # + # don't bother with this system anymore + # + unless ($giveup{$server}) { + $giveup{$server} = $reason; + print STDERR "$reason\n"; + } + print "Giveup at $file:$line!!! redirect okay = $redirect_okay; $reason\n" if $debug; + # + # Wait! + # Before giving up, see if there is a chance that + # there is another host to redirect to! + # (Kids, don't do this at home! Hacking is a dangerous + # crime and you could end up behind bars.) + # + for $u (@users) { + if ($redirect_okay =~ /\bmx\b/) { + next if &try_fallback('mx',$u,*server, + *mx_secondary, + *already_mx_fellback); + } + if ($redirect_okay =~ /\bdomainify\b/) { + next if &try_fallback('domainify',$u,*server, + *domainify_fallback, + *already_domainify_fellback); + } + push(@remaining_users,$u); + } + @users = @remaining_users; + for $u (@users) { + print &compact($u,$server)." ->\n" if ($verbose && $valid && $u); + &verbose(&final($u,$server,$names{"$u *** $server"},$reason)); + } +} +# +# This routine is used only within &giveup. It checks to +# see if we really have to giveup or if there is a second +# chance because we did something before that can be +# backtracked. +# +# %fallback{"$user *** $host"} tracks what is able to fallback +# %fellback{"$user *** $host"} tracks what has fallen back +# +# If there is a valid backtrack, then queue up the new possibility +# +sub try_fallback +{ + local($method,$user,*host,*fall_table,*fellback) = @_; + local($us,$fallhost,$oldhost,$ft,$i); + + if ($debug > 8) { + print "Fallback table $method:\n"; + for $i (sort keys %fall_table) { + print "\t'$i'\t\t'$fall_table{$i}'\n"; + } + print "Fellback table $method:\n"; + for $i (sort keys %fellback) { + print "\t'$i'\t\t'$fellback{$i}'\n"; + } + print "U: $user H: $host\n"; + } + + $us = "$user *** $host"; + if (defined $fellback{$us}) { + # + # Undo a previous fallback so that we can try again + # Nested fallbacks are avoided because they could + # lead to infinite loops + # + $fallhost = $fellback{$us}; + print "Already $method fell back from $us -> \n" if $debug; + $us = "$user *** $fallhost"; + $oldhost = $fallhost; + } elsif (($method eq 'mx') && (defined $mxbacktrace{$us}) && (defined $mx_secondary{$mxbacktrace{$us}})) { + print "Fallback an MX expansion $us -> \n" if $debug; + $oldhost = $mxbacktrace{$us}; + } else { + print "Oldhost($host, $us) = " if $debug; + $oldhost = $host; + } + print "$oldhost\n" if $debug; + if (((defined $fall_table{$us}) && ($ft = $us)) || ((defined $fall_table{$oldhost}) && ($ft = $oldhost))) { + print "$method Fallback = ".$fall_table{$ft}."\n" if $debug; + local(@so,$newhost); + @so = split(' ',$fall_table{$ft}); + $newhost = shift(@so); + print "Falling back ($method) $us -> $newhost (from $oldhost)\n" if $debug; + if ($method eq 'mx') { + if (! defined ($mxbacktrace{"$user *** $newhost"})) { + if (defined $mxbacktrace{"$user *** $oldhost"}) { + print "resetting oldhost $oldhost to the original: " if $debug; + $oldhost = $mxbacktrace{"$user *** $oldhost"}; + print "$oldhost\n" if $debug; + } + $mxbacktrace{"$user *** $newhost"} = $oldhost; + print "mxbacktrace $user *** $newhost -> $oldhost\n" if $debug; + } + $mx{&trhost($oldhost)} = $newhost; + } else { + $temporary_redirect{$us} = $newhost; + } + if (@so) { + print "Can still $method $us: @so\n" if $debug; + $fall_table{$ft} = join(' ',@so); + } else { + print "No more fallbacks for $us\n" if $debug; + delete $fall_table{$ft}; + } + if (defined $create_host_backtrack{$us}) { + $create_host_backtrack{"$user *** $newhost"} + = $create_host_backtrack{$us}; + } + $fellback{"$user *** $newhost"} = $oldhost; + &expn($newhost,$user,$names{$us},$level{$us}); + return 1; + } + delete $temporary_redirect{$us}; + $host = $oldhost; + return 0; +} +# return 1 if you could send mail to the address as is. +sub validAddr +{ + local($addr) = @_; + $res = &do_validAddr($addr); + print "validAddr($addr) = $res\n" if $debug; + $res; +} +sub do_validAddr +{ + local($addr) = @_; + local($urx) = "[-A-Za-z_.0-9+]+"; + + # \u + return 0 if ($addr =~ /^\\/); + # ?@h + return 1 if ($addr =~ /.\@$urx$/); + # @h:? + return 1 if ($addr =~ /^\@$urx\:./); + # h!u + return 1 if ($addr =~ /^$urx!./); + # u + return 1 if ($addr =~ /^$urx$/); + # ? + print "validAddr($addr) = ???\n" if $debug; + return 0; +} +# Some systems use expn and vrfy interchangeably. Some only +# implement one or the other. Some check expn against mailing +# lists and vrfy against users. It doesn't appear to be +# consistent. +# +# So, what do we do? We try everything! +# +# +# Ranking of result codes: good: 250, 251/551, 252, 550, anything else +# +# Ranking of inputs: best: user@host.domain, okay: user +# +# Return value: $error_string, @responses_from_server +sub expn_vrfy +{ + local($u,$server) = @_; + local(@c) = ('expn', 'vrfy'); + local(@try_u) = $u; + local(@ret,$code); + + if (($u =~ /(.+)@(.+)/) && (&trhost($2) eq &trhost($server))) { + push(@try_u,$1); + } + + TRY: + for $c (@c) { + for $try_u (@try_u) { + &alarm("${c}'ing $try_u on $server",'',$u); + &ps("$c $try_u"); + alarm(0); + $s = <S>; + if ($s eq '') { + return "$server: lost connection"; + } + if ($s !~ /^(\d+)([- ])/) { + return "$server: garbled reply to '$c $try_u'"; + } + if ($1 == 250) { + $code = 250; + @ret = ("",$s); + push(@ret,&read_response($2,$debug)); + return (@ret); + } + if ($1 == 551 || $1 == 251) { + $code = $1; + @ret = ("",$s); + push(@ret,&read_response($2,$debug)); + next; + } + if ($1 == 252 && ($code == 0 || $code == 550)) { + $code = 252; + @ret = ("",$s); + push(@ret,&read_response($2,$watch)); + next; + } + if ($1 == 550 && $code == 0) { + $code = 550; + @ret = ("",$s); + push(@ret,&read_response($2,$watch)); + next; + } + &read_response($2,$watch); + } + } + return "$server: expn/vrfy not implemented" unless @ret; + return @ret; +} +# sometimes the old parse routine (now parse2) didn't +# reject funky addresses. +sub parse +{ + local($oldaddr,$server,$oldname,$one_to_one) = @_; + local($newhost, $newaddr, $newname, $um) = &parse2($oldaddr,$server,$oldname,$one_to_one); + if ($newaddr =~ m,^["/],) { + return (undef, $oldaddr, $newname) if $valid; + return (undef, $um, $newname); + } + return ($newhost, $newaddr, $newname); +} + +# returns ($new_smtp_server,$new_address,$new_name) +# given a response from a SMTP server ($newaddr), the +# current host ($server), the old "name" and a flag that +# indicates if it is being called during the initial +# command line parsing ($parsing_args) +sub parse2 +{ + local($newaddr,$context_host,$old_name,$parsing_args) = @_; + local(@names) = $old_name; + local($urx) = "[-A-Za-z_.0-9+]+"; + local($unmangle); + + # + # first, separate out the address part. + # + + # + # [NAME] <ADDR [(NAME)]> + # [NAME] <[(NAME)] ADDR + # ADDR [(NAME)] + # (NAME) ADDR + # [(NAME)] <ADDR> + # + if ($newaddr =~ /^\<(.*)\>$/) { + print "<A:$1>\n" if $debug; + ($newaddr) = &trim($1); + print "na = $newaddr\n" if $debug; + } + if ($newaddr =~ /^([^\<\>]*)\<([^\<\>]*)\>([^\<\>]*)$/) { + # address has a < > pair in it. + print "N:$1 <A:$2> N:$3\n" if $debug; + ($newaddr) = &trim($2); + unshift(@names, &trim($3,$1)); + print "na = $newaddr\n" if $debug; + } + if ($newaddr =~ /^([^\(\)]*)\(([^\(\)]*)\)([^\(\)]*)$/) { + # address has a ( ) pair in it. + print "A:$1 (N:$2) A:$3\n" if $debug; + unshift(@names,&trim($2)); + local($f,$l) = (&trim($1),&trim($3)); + if (($f && $l) || !($f || $l)) { + # address looks like: + # foo (bar) baz or (bar) + # not allowed! + print STDERR "Could not parse $newaddr\n" if $vw; + return(undef,$newaddr,&firstname(@names)); + } + $newaddr = $f if $f; + $newaddr = $l if $l; + print "newaddr now = $newaddr\n" if $debug; + } + # + # @foo:bar + # j%k@l + # a@b + # b!a + # a + # + $unmangle = $newaddr; + if ($newaddr =~ /^\@($urx)\:(.+)$/) { + print "(\@:)" if $debug; + # this is a bit of a cheat, but it seems necessary + return (&domainify($1,$context_host,$2),$2,&firstname(@names),$unmangle); + } + if ($newaddr =~ /^(.+)\@($urx)$/) { + print "(\@)" if $debug; + return (&domainify($2,$context_host,$newaddr),$newaddr,&firstname(@names),$unmangle); + } + if ($parsing_args) { + if ($newaddr =~ /^($urx)\!(.+)$/) { + return (&domainify($1,$context_host,$newaddr),$newaddr,&firstname(@names),$unmangle); + } + if ($newaddr =~ /^($urx)$/) { + return ($context_host,$newaddr,&firstname(@names),$unmangle); + } + print STDERR "Could not parse $newaddr\n"; + } + print "(?)" if $debug; + return(undef,$newaddr,&firstname(@names),$unmangle); +} +# return $u (@$server) unless $u includes reference to $server +sub compact +{ + local($u, $server) = @_; + local($se) = $server; + local($sp); + $se =~ s/(\W)/\\$1/g; + $sp = " (\@$server)"; + if ($u !~ /$se/i) { + return "$u$sp"; + } + return $u; +} +# remove empty (spaces don't count) members from an array +sub trim +{ + local(@v) = @_; + local($v,@r); + for $v (@v) { + $v =~ s/^\s+//; + $v =~ s/\s+$//; + push(@r,$v) if ($v =~ /\S/); + } + return(@r); +} +# using the host part of an address, and the server name, add the +# servers' domain to the address if it doesn't already have a +# domain. Since this sometimes fails, save a back reference so +# it can be unrolled. +sub domainify +{ + local($host,$domain_host,$u) = @_; + local($domain,$newhost); + + # cut of trailing dots + $host =~ s/\.$//; + $domain_host =~ s/\.$//; + + if ($domain_host !~ /\./) { + # + # domain host isn't, keep $host whatever it is + # + print "domainify($host,$domain_host) = $host\n" if $debug; + return $host; + } + + # + # There are several weird situtations that need to be + # accounted for. They have to do with domain relay hosts. + # + # Examples: + # host server "right answer" + # + # shiva.cs cs.berkeley.edu shiva.cs.berkeley.edu + # shiva cs.berkeley.edu shiva.cs.berekley.edu + # cumulus reed.edu @reed.edu:cumulus.uucp + # tiberius tc.cornell.edu tiberius.tc.cornell.edu + # + # The first try must always be to cut the domain part out of + # the server and tack it onto the host. + # + # A reasonable second try is to tack the whole server part onto + # the host and for each possible repeated element, eliminate + # just that part. + # + # These extra "guesses" get put into the %domainify_fallback + # array. They will be used to give addresses a second chance + # in the &giveup routine + # + + local(%fallback); + + local($long); + $long = "$host $domain_host"; + $long =~ tr/A-Z/a-z/; + print "long = $long\n" if $debug; + if ($long =~ s/^([^ ]+\.)([^ ]+) \2(\.[^ ]+\.[^ ]+)/$1$2$3/) { + # matches shiva.cs cs.berkeley.edu and returns shiva.cs.berkeley.edu + print "condensed fallback $host $domain_host -> $long\n" if $debug; + $fallback{$long} = 9; + } + + local($fh); + $fh = $domain_host; + while ($fh =~ /\./) { + print "FALLBACK $host.$fh = 1\n" if $debug > 7; + $fallback{"$host.$fh"} = 1; + $fh =~ s/^[^\.]+\.//; + } + + $fallback{"$host.$domain_host"} = 2; + + ($domain = $domain_host) =~ s/^[^\.]+//; + $fallback{"$host$domain"} = 6 + if ($domain =~ /\./); + + if ($host =~ /\./) { + # + # Host is already okay, but let's look for multiple + # interpretations + # + print "domainify($host,$domain_host) = $host\n" if $debug; + delete $fallback{$host}; + $domainify_fallback{"$u *** $host"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback; + return $host; + } + + $domain = ".$domain_host" + if ($domain !~ /\..*\./); + $newhost = "$host$domain"; + + $create_host_backtrack{"$u *** $newhost"} = $domain_host; + print "domainify($host,$domain_host) = $newhost\n" if $debug; + delete $fallback{$newhost}; + $domainify_fallback{"$u *** $newhost"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback; + if ($debug) { + print "fallback = "; + print $domainify_fallback{"$u *** $newhost"} + if defined($domainify_fallback{"$u *** $newhost"}); + print "\n"; + } + return $newhost; +} +# return the first non-empty element of an array +sub firstname +{ + local(@names) = @_; + local($n); + while(@names) { + $n = shift(@names); + return $n if $n =~ /\S/; + } + return undef; +} +# queue up more addresses to expand +sub expn +{ + local($host,$addr,$name,$level) = @_; + if ($host) { + $host = &trhost($host); + + if (($debug > 3) || (defined $giveup{$host})) { + unshift(@hosts,$host) unless $users{$host}; + } else { + push(@hosts,$host) unless $users{$host}; + } + $users{$host} .= " $addr"; + $names{"$addr *** $host"} = $name; + $level{"$addr *** $host"} = $level + 1; + print "expn($host,$addr,$name)\n" if $debug; + return "\t$addr\n"; + } else { + return &final($addr,'NONE',$name); + } +} +# compute the numerical average value of an array +sub average +{ + local(@e) = @_; + return 0 unless @e; + local($e,$sum); + for $e (@e) { + $sum += $e; + } + $sum / @e; +} +# print to the server (also to stdout, if -w) +sub ps +{ + local($p) = @_; + print ">>> $p\n" if $watch; + print S "$p\n"; +} +# return case-adjusted name for a host (for comparison purposes) +sub trhost +{ + # treat foo.bar as an alias for Foo.BAR + local($host) = @_; + local($trhost) = $host; + $trhost =~ tr/A-Z/a-z/; + if ($trhost{$trhost}) { + $host = $trhost{$trhost}; + } else { + $trhost{$trhost} = $host; + } + $trhost{$trhost}; +} +# re-queue users if an mx record dictates a redirect +# don't allow a user to be redirected more than once +sub mxredirect +{ + local($server,*users) = @_; + local($u,$nserver,@still_there); + + $nserver = &mx($server); + + if (&trhost($nserver) ne &trhost($server)) { + $0 = "$av0 - mx redirect $server -> $nserver\n"; + for $u (@users) { + if (defined $mxbacktrace{"$u *** $nserver"}) { + push(@still_there,$u); + } else { + $mxbacktrace{"$u *** $nserver"} = $server; + print "mxbacktrace{$u *** $nserver} = $server\n" + if ($debug > 1); + &expn($nserver,$u,$names{"$u *** $server"}); + } + } + @users = @still_there; + if (! @users) { + return $nserver; + } else { + return undef; + } + } + return undef; +} +# follow mx records, return a hostname +# also follow temporary redirections comming from &domainify and +# &mxlookup +sub mx +{ + local($h,$u) = @_; + + for (;;) { + if (defined $mx{&trhost($h)} && $h ne $mx{&trhost($h)}) { + $0 = "$av0 - mx expand $h"; + $h = $mx{&trhost($h)}; + return $h; + } + if ($u) { + if (defined $temporary_redirect{"$u *** $h"}) { + $0 = "$av0 - internal redirect $h"; + print "Temporary redirect taken $u *** $h -> " if $debug; + $h = $temporary_redirect{"$u *** $h"}; + print "$h\n" if $debug; + next; + } + $htr = &trhost($h); + if (defined $temporary_redirect{"$u *** $htr"}) { + $0 = "$av0 - internal redirect $h"; + print "temporary redirect taken $u *** $h -> " if $debug; + $h = $temporary_redirect{"$u *** $htr"}; + print "$h\n" if $debug; + next; + } + } + return $h; + } +} +# look up mx records with the name server. +# re-queue expansion requests if possible +# optionally give up on this host. +sub mxlookup +{ + local($lastchance,$server,$giveup,*users) = @_; + local(*T); + local(*NSLOOKUP); + local($nh, $pref,$cpref); + local($o0) = $0; + local($nserver); + local($name,$aliases,$type,$len,$thataddr); + local(%fallback); + + return 1 if &mxredirect($server,*users); + + if ((defined $mx{$server}) || (! $have_nslookup)) { + return 0 unless $lastchance; + &giveup('mx domainify',$giveup); + return 0; + } + + $0 = "$av0 - nslookup of $server"; + open(T,">/tmp/expn$$") || die "open > /tmp/expn$$: $!\n"; + print T "set querytype=MX\n"; + print T "$server\n"; + close(T); + $cpref = 1.0E12; + undef $nserver; + open(NSLOOKUP,"nslookup < /tmp/expn$$ 2>&1 |") || die "open nslookup: $!"; + while(<NSLOOKUP>) { + print if ($debug > 2); + if (/mail exchanger = ([-A-Za-z_.0-9+]+)/) { + $nh = $1; + if (/preference = (\d+)/) { + $pref = $1; + if ($pref < $cpref) { + $nserver = $nh; + $cpref = $pref; + } elsif ($pref) { + $fallback{$pref} .= " $nh"; + } + } + } + if (/Non-existent domain/) { + # + # These addresss are hosed. Kaput! Dead! + # However, if we created the address in the + # first place then there is a chance of + # salvation. + # + 1 while(<NSLOOKUP>); + close(NSLOOKUP); + return 0 unless $lastchance; + &giveup('domainify',"$server: Non-existent domain",undef,1); + return 0; + } + + } + close(NSLOOKUP); + unlink("/tmp/expn$$"); + unless ($nserver) { + $0 = "$o0 - finished mxlookup"; + return 0 unless $lastchance; + &giveup('mx domainify',"$server: Could not resolve address"); + return 0; + } + + # provide fallbacks in case $nserver doesn't work out + if (defined $fallback{$cpref}) { + $mx_secondary{$server} = $fallback{$cpref}; + } + + $0 = "$av0 - gethostbyname($nserver)"; + ($name,$aliases,$type,$len,$thataddr) = gethostbyname($nserver); + + unless ($thataddr) { + $0 = $o0; + return 0 unless $lastchance; + &giveup('mx domainify',"$nserver: could not resolve address"); + return 0; + } + print "MX($server) = $nserver\n" if $debug; + print "$server -> $nserver\n" if $vw && !$debug; + $mx{&trhost($server)} = $nserver; + # redeploy the users + unless (&mxredirect($server,*users)) { + return 0 unless $lastchance; + &giveup('mx domainify',"$nserver: only one level of mx redirect allowed"); + return 0; + } + $0 = "$o0 - finished mxlookup"; + return 1; +} +# if mx expansion did not help to resolve an address +# (ie: foo@bar became @baz:foo@bar, then undo the +# expansion). +# this is only used by &final +sub mxunroll +{ + local(*host,*addr) = @_; + local($r) = 0; + print "looking for mxbacktrace{$addr *** $host}\n" + if ($debug > 1); + while (defined $mxbacktrace{"$addr *** $host"}) { + print "Unrolling MX expnasion: \@$host:$addr -> " + if ($debug || $verbose); + $host = $mxbacktrace{"$addr *** $host"}; + print "\@$host:$addr\n" + if ($debug || $verbose); + $r = 1; + } + return 1 if $r; + $addr = "\@$host:$addr" + if ($host =~ /\./); + return 0; +} +# register a completed expnasion. Make the final address as +# simple as possible. +sub final +{ + local($addr,$host,$name,$error) = @_; + local($he); + local($hb,$hr); + local($au,$ah); + + if ($error =~ /Non-existent domain/) { + # + # If we created the domain, then let's undo the + # damage... + # + if (defined $create_host_backtrack{"$addr *** $host"}) { + while (defined $create_host_backtrack{"$addr *** $host"}) { + print "Un&domainifying($host) = " if $debug; + $host = $create_host_backtrack{"$addr *** $host"}; + print "$host\n" if $debug; + } + $error = "$host: could not locate"; + } else { + # + # If we only want valid addresses, toss out + # bad host names. + # + if ($valid) { + print STDERR "\@$host:$addr ($name) Non-existent domain\n"; + return ""; + } + } + } + + MXUNWIND: { + $0 = "$av0 - final parsing of \@$host:$addr"; + ($he = $host) =~ s/(\W)/\\$1/g; + if ($addr !~ /@/) { + # addr does not contain any host + $addr = "$addr@$host"; + } elsif ($addr !~ /$he/i) { + # if host part really something else, use the something + # else. + if ($addr =~ m/(.*)\@([^\@]+)$/) { + ($au,$ah) = ($1,$2); + print "au = $au ah = $ah\n" if $debug; + if (defined $temporary_redirect{"$addr *** $ah"}) { + $addr = "$au\@".$temporary_redirect{"$addr *** $ah"}; + print "Rewrite! to $addr\n" if $debug; + next MXUNWIND; + } + } + # addr does not contain full host + if ($valid) { + if ($host =~ /^([^\.]+)(\..+)$/) { + # host part has a . in it - foo.bar + ($hb, $hr) = ($1, $2); + if ($addr =~ /\@([^\.\@]+)$/ && ($1 eq $hb)) { + # addr part has not . + # and matches beginning of + # host part -- tack on a + # domain name. + $addr .= $hr; + } else { + &mxunroll(*host,*addr) + && redo MXUNWIND; + } + } else { + &mxunroll(*host,*addr) + && redo MXUNWIND; + } + } else { + $addr = "${addr}[\@$host]" + if ($host =~ /\./); + } + } + } + $name = "$name " if $name; + $error = " $error" if $error; + if ($valid) { + push(@final,"$name<$addr>"); + } else { + push(@final,"$name<$addr>$error"); + } + "\t$name<$addr>$error\n"; +} + +sub alarm +{ + local($alarm_action,$alarm_redirect,$alarm_user) = @_; + alarm(3600); + $SIG{ALRM} = 'handle_alarm'; +} +# this involves one great big ugly hack. +# the "next HOST" unwinds the stack! +sub handle_alarm +{ + &giveup($alarm_redirect,"Timed out during $alarm_action",$alarm_user); + next HOST; +} + +# read the rest of the current smtp daemon's response (and toss it away) +sub read_response +{ + local($done,$watch) = @_; + local(@resp); + print $s if $watch; + while(($done eq "-") && ($s = <S>) && ($s =~ /^\d+([- ])/)) { + print $s if $watch; + $done = $1; + push(@resp,$s); + } + return @resp; +} +# print args if verbose. Return them in any case +sub verbose +{ + local(@tp) = @_; + print "@tp" if $verbose; +} +# to pass perl -w: +@tp; +$flag_a; +$flag_d; +$flag_1; +%already_domainify_fellback; +%already_mx_fellback; +&handle_alarm; +################### BEGIN PERL/TROFF TRANSITION +.00 ; + +'di +.nr nl 0-1 +.nr % 0 +.\\"'; __END__ +.\" ############## END PERL/TROFF TRANSITION +.TH EXPN 1 "March 11, 1993" +.AT 3 +.SH NAME +expn \- recursively expand mail aliases +.SH SYNOPSIS +.B expn +.RI [ -a ] +.RI [ -v ] +.RI [ -w ] +.RI [ -d ] +.RI [ -1 ] +.IR user [@ hostname ] +.RI [ user [@ hostname ]]... +.SH DESCRIPTION +.B expn +will use the SMTP +.B expn +and +.B vrfy +commands to expand mail aliases. +It will first look up the addresses you provide on the command line. +If those expand into addresses on other systems, it will +connect to the other systems and expand again. It will keep +doing this until no further expansion is possible. +.SH OPTIONS +The default output of +.B expn +can contain many lines which are not valid +email addresses. With the +.I -aa +flag, only expansions that result in legal addresses +are used. Since many mailing lists have an illegal +address or two, the single +.IR -a , +address, flag specifies that a few illegal addresses can +be mixed into the results. More +.I -a +flags vary the ratio. Read the source to track down +the formula. With the +.I -a +option, you should be able to construct a new mailing +list out of an existing one. +.LP +If you wish to limit the number of levels deep that +.B expn +will recurse as it traces addresses, use the +.I -1 +option. For each +.I -1 +another level will be traversed. So, +.I -111 +will traverse no more than three levels deep. +.LP +The normal mode of operation for +.B expn +is to do all of its work silently. +The following options make it more verbose. +It is not necessary to make it verbose to see what it is +doing because as it works, it changes its +.BR argv [0] +variable to reflect its current activity. +To see how it is expanding things, the +.IR -v , +verbose, flag will cause +.B expn +to show each address before +and after translation as it works. +The +.IR -w , +watch, flag will cause +.B expn +to show you its conversations with the mail daemons. +Finally, the +.IR -d , +debug, flag will expose many of the inner workings so that +it is possible to eliminate bugs. +.SH ENVIRONMENT +No enviroment variables are used. +.SH FILES +.PD 0 +.B /tmp/expn$$ +.B temporary file used as input to +.BR nslookup . +.SH SEE ALSO +.BR aliases (5), +.BR sendmail (8), +.BR nslookup (8), +RFC 823, and RFC 1123. +.SH BUGS +Not all mail daemons will implement +.B expn +or +.BR vrfy . +It is not possible to verify addresses that are served +by such daemons. +.LP +When attempting to connect to a system to verify an address, +.B expn +only tries one IP address. Most mail daemons +will try harder. +.LP +It is assumed that you are running domain names and that +the +.BR nslookup (8) +program is available. If not, +.B expn +will not be able to verify many addresses. It will also pause +for a long time unless you change the code where it says +.I $have_nslookup = 1 +to read +.I $have_nslookup = +.IR 0 . +.LP +Lastly, +.B expn +does not handle every valid address. If you have an example, +please submit a bug report. +.SH CREDITS +In 1986 or so, Jon Broome wrote a program of the same name +that did about the same thing. It has since suffered bit rot +and Jon Broome has dropped off the face of the earth! +(Jon, if you are out there, drop me a line) +.SH AVAILABILITY +The latest version of +.B expn +is available through anonymous ftp at +.IR ftp://ftp.idiom.com/pub/muir-programs/expn . +.SH AUTHOR +.I David Muir Sharnoff\ \ \ \ <muir@idiom.com> diff --git a/contrib/amd/scripts/fix-amd-map.in b/contrib/amd/scripts/fix-amd-map.in new file mode 100755 index 0000000..6746462 --- /dev/null +++ b/contrib/amd/scripts/fix-amd-map.in @@ -0,0 +1,52 @@ +#!@PERL@ +# +# fix an old-syntax amd map to new one +# +# takes any number of files on the command line, and produces +# a fixed map on stdout. +# +# Package: am-utils-6.0 +# Author: Erez Zadok <ezk@cs.columbia.edu> +# + +############################################################################## +### MAINTAINER EDITABLE SECTION + +# Mappings of old names to new ones: +# Update when needed, do not forget commas but not on the last entry! +# For your convenience, this is the complete list of all OSs that were +# supported by amd-upl102, in their old names: +# +# 386bsd acis43 aix3 aoi aux bsd43 bsd44 bsdi11 +# concentrix dgux fpx4 freebsd hcx hlh42 hpux irix3 irix4 irix5 isc3 +# linux mach2 mach3 netbsd news4 next osf1 pyrOSx riscix riscos +# rtu6 sos3 sos4 sos5 stellix svr4 u2_2 u3_0 u4_0 u4_2 u4_3 u4_4 +# umax43 utek utx32 xinu43 +# +%mappings = ( + "sos4", "sunos4", + "sos5", "sunos5", + "freebsd", "freebsd2" +); + +############################################################################## +### DO NOT EDIT ANYTHING BELOW + +# This is a trivial parser and works as follows: +# (1) read each line +# (2) search of regexps that start with '=', continue with a word to replace +# and end with a non-value name (whitespace, ';', or newline +while (<>) { + # skip trivial lines + if ($_ =~ /^$/ || $_ =~ /^#/) { + print; + next; + } + # modify the line if needed + foreach $m (keys %mappings) { + $val = $mappings{$m}; + $_ =~ s/=$m([^a-zA-Z0-9_])/=$val$1/g; + } + # print the (possibly) modified line + print; +} diff --git a/contrib/amd/scripts/fixrmtab b/contrib/amd/scripts/fixrmtab new file mode 100755 index 0000000..33b7bcf --- /dev/null +++ b/contrib/amd/scripts/fixrmtab @@ -0,0 +1,24 @@ +#!/bin/sh +# +# Invalidate /etc/rmtab entries for hosts named. +# Restart mountd for changes to take effect. +# +# usage: fixrmtab host1 host2 ... +# +# Package: am-utils-6.0 +# Author: Andreas Stolcke <stolcke@icsi.berkeley.edu> + +#set -x + +RMTAB=/etc/rmtab +TMP=/tmp/rmtab.$$ + +if [ ! -f /etc/rmtab ]; then + exit 0 +fi + +for host in $* +do + sed -e '/^'$host':/s/^./#/' $RMTAB > $TMP && cp $TMP $RMTAB +done +rm -f $TMP diff --git a/contrib/amd/scripts/lostaltmail.conf-sample b/contrib/amd/scripts/lostaltmail.conf-sample new file mode 100644 index 0000000..a20158c --- /dev/null +++ b/contrib/amd/scripts/lostaltmail.conf-sample @@ -0,0 +1,84 @@ +# -*- perl -*- +############################################################################## +# # +# CONFIGURABLE VALUES # +# # +############################################################################## + +$MAILGRUNT="postmaster"; # To whom to send log mail if mail is prefered. + +$TMPDIR="/tmp/"; # Place lostmail can do its dirty work. + +$LOCAL_LOCK_EXT=".lock"; # Name of file local mailer uses to lock + # spool file. This the correct setting for + # /bin/mail + +$SYSTEM_FROM_ADDRESS="Mailer-Daemon"; + +$MAILDIR="/var/alt_mail"; # What directory should I run out of. +$MAILER='/usr/lib/sendmail -t'; # Which mailer should I use. + +$LOCALMAILJUNK='.*~|\#.*|core'; # Files name patterns that might appear in + # alt_mail and should be ignored. This REGEXP + # gets or'ed with $MAILJUNK below. + +$SMTPHOST='localhost'; # The name of a local host which speaks SMTP + # and knows *all* your aliases. You probably + # don't want to change this. If the machine + # running lost_alt mail doesn't run an SMTP, + # daemon then something is either wrong or you + # should be setting `noverify' to prevent + # SMTP verification. + +$HOSTNAME='localhost'; # Hostname to use for SMTP HELO + +# Subject of lost log mail message. Must define $MAILGRUNT. +# I overwrite this variable in the subroutine Clean_up. Please make sure I +# haven't noodle-headdly forgotten to remove that hack in the distribution! +# No newline here please. The script will insert it for you. +$LOG_SUBJECT="Log of lostmail resends"; + +############################################################################## +# # +# DEFAULTED CONFIGURATIONS # +# # +############################################################################## + +$LOGFILE="$TMPDIR" . "lostlog"; + + +# MAILJUNK is a pattern of ignorable alt_mail files which are either common +# to most platforms or actually produced by this script. You should customize +# this REGEXP by hacking at $LOCALMAILJUNK above. +$MAILJUNK='[a-z]\.[0-9]*|\.\.?|lost\+found'; + +$LOCKEXT=".lostlock"; # our lock file extension. Should not need to + # modify + +$MESSAGE_DELIM="^From[^:]"; # /bin/mail message delimiter. Your milage + # may differ + +$HEADER_BODY_DELIM="\n"; # RFC 822 header-body delimiter. + +$RESENT_TO="Resent-To: "; # +$RESENT_FROM="Resent-From: "; # Resent headers (RFC 822). +$RESENT_DATE="Resent-Date: "; # You probably don't want to muck with these. +$RESENT_INFO="X-Resent-Info: "; # (special one to alert folks about mail). + + +############################################################################## +# # +# LOSTMAIL DEFINITIONS (DON'T TOUCH) # +# # +############################################################################## + +$FALSE=0; +$TRUE=(! $FALSE); + +$OK=$TRUE; +$ABORT_RESEND=2; +$LOCK_RETRIES=10; # The number of seconds/retries lost mail + # should wait before requeing or aborting a + # resend. + +TRUE; # Ansures true return from include file. diff --git a/contrib/amd/scripts/lostaltmail.in b/contrib/amd/scripts/lostaltmail.in new file mode 100755 index 0000000..5ba454c --- /dev/null +++ b/contrib/amd/scripts/lostaltmail.in @@ -0,0 +1,648 @@ +#!@PERL@ -sw +# +# Package: am-utils-6.0 +# Author: James Tanis <jtt@cs.columbia.edu> +# + +############################################################################ +# +# lostaltmail -- remail files files found alt_mail (or -a argument to hlfsd) to +# whomever should receive it. This version is for SMTP varient which +# support VRFY as a non-expanding verifier!!! (sendmail V8 is a an +# example). +# +# Usage: lostaltmail [-debug] [-nomail] [-noverify] +# +# GLOBAL VARIABLES (as if you care :-) ) +# Probably a very incomplete list. +# +# Everything in the config file for this program *and* ... +# +# $debug: set it from the command line with -debug. Does the obvious +# $nomail: set it from the command line with -nomail. *Not* implied by +# $debug +# $currentTO: The addresss we are currently checking on. Actually this is +# left over from an earlier version of lostaltmail and will hopefully +# go away. +# $noverify: set it from the address line. Avoid verification of $currentTO. +# This should be relatively safe as long as your are willing to +# endure bounces from mail that cannot be redelivered as opposed to +# just getting a warning. UNTESTED (but should work). +# +# $logopen: state variable indicating weather the log file (should there be +# one) is in fact open. +# +# @allentries: Array of all the directory entries in $MAILDIR +# @allnames: Array of all *likely* recipients. It is created from @allentries +# sans junk files (see $MAILJUNK and $LOCALMAILJUNK) +# @wanderers: Array of all the files associated with a *single* address +# which might need remailing. Should lostaltmail die unexpectedly, +# it might leave a temporary file containing messages it was +# currently trying to deliver. These will get picked and resent +# later. +# +# VRFY: Handle onto SMTP verification channel. Not to be confused with mail +# delivery; only verification occurs accross this handle. +# +############################################################################ + +############################################################################## +# # +# SMTP_SEND # +# # +############################################################################## +# +# Send a message to the smtp channel. Inserts the necessary NEWLINE if it +# does not exist; +# I stole this from myself. It shouldn nott be printing errors to STDERR, but +# this is a quick hack. +# +sub smtp_send { + local ($msg) = @_; + local ($length); + + $length=length($msg); + + if ( $msg !~ /^.*\n$/ ) { + $msg = $msg . "\n"; + $length++; + } + + + if ( ! syswrite (VRFY, $msg, $length)) { + print STDERR "Failing SMTP write: $msg"; + return 0; + } + + return 1; +} + +############################################################################## +# # +# SMTP_RECV # +# # +############################################################################## +# +# Read in lines from SMTP connection and return the final +# Really hideous -- please excuse. +# +sub smtp_recv { + local ($line,$rin, $win, $ein, $readbuf, $ret); + $readbuf = ""; + + $rin = $win = $ein = ''; # Null fd sets, + vec ($rin, fileno(VRFY), 1) = 1; # Stolen straight from the example; + $ein = $rin | $win; # This is probably useless + + +LINE_OF_INPUT: + while (1) { # Read in all the input + + if ((select ( $rin, $win, $ein, 600.0))[0] == 0 ) { + print "select returned -1" if ($debug); + return -1; # timeout + } + sysread (VRFY, $readbuf, 1024); + chop ($readbuf); + + foreach $line ( split('\n', $readbuf)) { + + # This loop is actually needed since V8 has a multi-line greet. + + ( $line =~ /^(\d\d\d).*/ && ($SMTP_retval=$1)) || + warn "Badly formed reply from SMTP peer: $line\n"; + + # Space after return code indicates EOT + + if ($line =~ /^\d\d\d /) { + $ret = $line; # Oddly $line is in a different context here; + # and thus we need to export it out of the + # while loop via $ret. + last LINE_OF_INPUT; + } + } # End of read. + } # End of input. + + return $ret; +} + + + + +############################################################################## +# # +# LOG_INFO # +# # +############################################################################## +# +# +# Opens appropriate logging file -- STDOUT (cron) or temp file (mail). +# +sub Log_info { + local($message) = @_; + + if ( !$logopened ) { + if ( $MAILGRUNT eq "" || $debug) { + open (LOGFILE, ">-") || die "Unable to open stdout"; + } + else { + # Snarf the log into a tmp file for final mailing to MAILGRUNT + $logfile = $LOGFILE . ".$$"; + open (LOGFILE, (">". "$logfile")) || die "Unable to create log file"; + } + } + + $logopened=1; # Note that the log is now open + + # Heart of the function. + print LOGFILE "$message"; + + print LOGFILE "\n" if ( index($message,"\n") == -1 ); +} + +############################################################################## +# # +# LOCK_FILE # +# # +############################################################################## + +# +# Tries to grab a lock on the supplied file name. +# Spins for a bit if it can't on the assumption that the lock will be released +# quickly. If it times out and it's allowed to requeue, it will defer +# until later, other wise write a message to loginfo. + +# If a recurring error or really unexpected situation arrises, return +# ABORT_RESEND +# +# PARAMETERS +# mailfile: path to the file to resend. +# should_requeue: BOOLEAN - TRUE if the mailfile should be put on the +# queue for a later retry if we can not finish +# now. + +sub Lock_file { + + local($mailfile,$should_requeue,$i,$new_lost_file) = @_; + +# We need to rename the current mailbox so that mail can loop back into it if +# the resent mail just gets looped right back to us. + $new_lost_file = $mailfile . ".$$"; + +# make a tmpfile name based on mailfile; + $lostlockfile = "$mailfile" . "$LOCKEXT"; + + if ( ! open(LOCKFILE, (">" . $lostlockfile)) ) { + printf(STDERR "Could not create lostlockfile for %s: %s\n", $mailfile,$!); + return $ABORT_RESEND; + } + close(LOCKFILE); + + $maillockfile = "$mailfile" . "$LOCAL_LOCK_EXT"; + + for ($i=0; $i < $LOCK_RETRIES && ! link ($lostlockfile, $maillockfile); + $i++) { + sleep(1); + } + + unlink($lostlockfile); # No matter what eliminate our cruft + + if ( $i == $LOCK_RETRIES ) { + &Log_info("Could not grab lock on: " . "$mailfile" . " :timed out"); + if ( $should_requeue ) { + &Log_info("Requeing " . "$mailfile" . " for later retry"); + $retry_list .= " $mailfile"; + } + else { + &Log_info("Giving up on: " . "$mailfile"); + } + + return $ABORT_RESEND; + } + + # We created the link and therefore have the lock + + if (rename ($mailfile, $new_lost_file) == 0 ){ + # Failed to rename file -- this is serious. + unlink($maillockfile); + return $ABORT_RESEND; + } + + unlink($maillockfile); + return $new_lost_file; + +} + +############################################################################## +# # +# PARSE NEXT MAIL MESSAGE # +# # +############################################################################## +# +# Parameters: +# mailfile: handle of mailfile to use. +# +# Parses the next message in the mail file and inserts it in $current_msg +# +sub Get_next_msg { + local($mailfile,$found_body_delimiter) = @_; + + # If this is the first message in the spool file, read the first line + # otherwise use the MESSAGE_DELIM line from the previous message (which we + # were forced to overread). + + $done=$FALSE; + $found_body_delimiter=$FALSE; + + # This if eats the very first "From " line and should never fire again. + if ( ! defined $current_msg ) {<$mailfile>}; + undef ($current_msg); # Erase the old message. + + + # Read the mailfile and pass through all the lines up until the next + # message delimiter. Kill any previous resend headers. + while ( <$mailfile> ) { + last if (/$MESSAGE_DELIM/); + next if ( !$found_body_delimiter && /[Rr][Ee][Ss][Ee][Nn][Tt]-.+:/); + if ( !$found_body_delimiter && /^$HEADER_BODY_DELIM/) { + &Splice_in_resent_headers(); + $found_body_delimiter=$TRUE; + } + if (defined($current_msg)) { + $current_msg .= $_; + } else { + $current_msg = $_; + } + } + + # Return TRUE when we have hit the end of the file. + if (!defined($_) || $_ eq "" ) { + return $TRUE; + } else { + return $FALSE; + } +} + +############################################################################## +# # +# SPLICE IN RESENT_HEADERS # +# # +############################################################################## +# +# Insert the Resent- headers at the *current location* of the message stream +# (In Engish, print out a few Resent-X: lines and return :-) ) +# In addition splice in the X-resent-info: header. + +# +# Paremters: None. +# Return: None +# +sub Splice_in_resent_headers { + local($date,$utctime,$weekday,$time,$month,$hostname); + + $current_msg .= "$RESENT_TO" . "$currentTO" . "\n"; + $current_msg .= "$RESENT_FROM" . "$SYSTEM_FROM_ADDRESS" . "\n"; + + # Calculate date and time. It is a bit of a shame to do this each time + # the time needs to be acurate. + + @utctime=gmtime(time); + + $weekday=(Sun,Mon,Tue,Wed,Thu,Fri,Sat)[$utctime[6]]; + + + # If the minutes or second do not take two columns each, patch em up. + if ( $utctime[1] < 10 ) { + if ( $utctime[0] < 10 ) { + $time=sprintf("%d:0%d:0%d",$utctime[2],$utctime[1],$utctime[0]); + } + else { + $time=sprintf("%d:0%d:%d",$utctime[2],$utctime[1],$utctime[0]); + } + } + else { + if ( $utctime[0] < 10 ) { + $time=sprintf("%d:%d:0%d",$utctime[2],$utctime[1],$utctime[0]); + } + else { + $time=sprintf("%d:%2d:%2d",$utctime[2],$utctime[1],$utctime[0]); + } + } + + $month=(Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)[$utctime[4]]; + + $date=sprintf("%s, %d %s %d %s UTC", $weekday, $utctime[3], $month, (($utctime[5] < 93 ? 20 : 19).$utctime[5]), $time); + + $current_msg .= "$RESENT_DATE" . $date . "\n"; + + if ( defined $RESENT_INFO && $RESENT_INFO ne "") { + $hostname=`uname -n`; + $current_msg .= "$RESENT_INFO" . "Lost mail resent from ". $hostname; + } + + return; +} + +############################################################################## +# # +# DO_REMAIL # +# # +############################################################################## +# +# Actually resends the mail. Talks to the process configured as $MAILER +# We need better handling. +# +sub Do_remail { + open (MAILER, "| $MAILER $currentTO") || return $ABORT_RESEND; + print MAILER $current_msg; + close (MAILER); +} + +############################################################################## +# # +# CLEAN_UP # +# # +############################################################################## +# +# Clean up my messes. +# +sub Clean_up { + local ($hostname); + + # Ugly local hack that you should never have seen, but I forgot to + # remove. Hopefully it did not kill you (I tried as you see), but you + # should eiter remove or update it for yourself. I find the message + # subject needs to have the hostname to be useful. + # + chop ($hostname=`uname -n`); + $LOG_SUBJECT="$LOG_SUBJECT from $hostname" if ( $hostname =~ /.*\.cs\.columbia\.edu/ ); + # + # End of ugly local hack + + # Mail any log info to MAILGRUNT. + if (defined($logfile) && $logfile ne "" ) { + close (LOGFILE); # Flush logfile output. + if ( -s $logfile ) { + open (MAILER, "| $MAILER $MAILGRUNT"); + + print MAILER "To: $MAILGRUNT\n"; + print MAILER "Subject: $LOG_SUBJECT\n"; + print MAILER "$HEADER_BODY_DELIM"; + + open (LOGFILE, "< $logfile"); + + while (<LOGFILE>) { + print MAILER $_; + } + close (MAILER); + close (LOGFILE); + } + + unlink($logfile); + } + exit(0); +} + + +############################################################################## +# # +# COLLECT_WANDERERS # +# # +############################################################################## + +# +# Collects other files that appear to be mail file for the $currentTO +# but were not remailed successfully. +# +# Parameters: none (but uses $currentTO) +# Return: True if a old mail directory is found. False otherwise. +# Side effects: $wanderers set. +# +sub Collect_wanderers { + + undef (@wanderers); + + # Slurp in the directory and close. + + return ($found); +} + +############################################################################# +# # +# REMAIL ALL # +# # +############################################################################# + +# +# Takes an array of files that all seem to share a common repcipient and +# remails them if possible. +# +# Parameters: None (uses @wanderers). +# +sub Remail_all { + local($file,$i); + + $i=0; + foreach $file (@wanderers) { + if ( !open (LOSTFILE, "< $file")) { + &Log_info("Could not open " . "$file" . " for remailing"); + next; + } + + do { # Power loop! + $done = &Get_next_msg(LOSTFILE); # Retrieve the next message... + &Do_remail; # and remail it. + } until $done; + undef ($current_msg); # Erase the final remailed message. + + close(LOSTFILE); # Tidy up. + + unlink ($file); # Remove the remailed file + $i++; + } + +} + +############################################################################# +# # +# CHECK_USER # +# # +############################################################################# + +# +# Checks the password tables for the uid of $currentTO. If the user is +# uid 0 (ie *supposed* to get mail in altmail) or unknown the resend is +# aborted. +# +# +sub Check_user { + local (@passwdinfo); + undef (@passwdinfo); + + if ( !&vrfy_user($currentTO) ) { + &Log_info("Possible non user mail file: $currentTO"); + return $ABORT_RESEND; + } + + @passwdinfo = getpwnam($currentTO); + + print "Non user mailable mail: Name: $currentTO\n" + if ( $debug && ! defined @passwdinfo ); + + return !$ABORT_RESEND if ( ! defined @passwdinfo ); # A non user but evidently mailable + + print "Check User(): Name: $currentTO -- UID: $passwdinfo[2]\n" if ($debug); + + return $ABORT_RESEND if ( $passwdinfo[2] == 0 ); + + + return !$ABORT_RESEND; +} + +############################################################################# +# # +# VRFY USER # +# # +############################################################################# +# +# Use SMTP VRFY to insure that argument is in fact a legal mail id. +# Boolean: TRUE if mailable account, FALSE if not. + +sub vrfy_user { + + local ($mailname,$repl) = @_; + + if ( !&smtp_send("vrfy $mailname") ) { + &Log_info("Failed sending to vrfy smtp command for: $mailname"); + return 0; + } + + $repl = &smtp_recv; + + print "VRFY REPLY: $repl\n" if ($debug); + + return ( $repl =~ /^2\d\d/ ); + + +} + + +############################################################################# +# # +# MAIN PROC # +# # +############################################################################# + +# dummy code to shut up perl -w +$debug = 0 if !defined($debug); +print $nomail if $debug > 1; +print $RESENT_FROM if $debug > 1; +print $logopen if $debug > 1; +print $LOCAL_LOCK_EXT if $debug > 1; +print $RESENT_TO if $debug > 1; +print $LOCKEXT if $debug > 1; +print $RESENT_DATE if $debug > 1; +print $MESSAGE_DELIM if $debug > 1; +print $SMTP_retval if $debug > 1; +print $found if $debug > 1; +print $retry_list if $debug > 1; +print $MAILJUNK if $debug > 1; +print $noverify if $debug > 1; +print $SYSTEM_FROM_ADDRESS if $debug > 1; + +# BEGIN: stuff +$prefix="@prefix@"; +$CONFIGDIR="@sysconfdir@"; # Directory where global config lives +require "$CONFIGDIR/lostaltmail.conf" if (-f "$CONFIGDIR/lostaltmail.conf"); +require "/etc/global/lostaltmail.conf" if (-f "/etc/global/lostaltmail.conf"); +require "/etc/os/lostaltmail.conf" if (-f "/etc/os/lostaltmail.conf"); +require "/etc/local/lostaltmail.conf" if (-f "/etc/local/lostaltmail.conf"); + + +require "ctime.pl"; +use Socket; +#require "sys/socket.ph"; + +# SET some initial state variales +$logopen = 0; + +# +# Change to alt_dir +# +# Important!! This directory should be local. Folks will be responsible +# for finding this out for themselves. +# +chdir ( $MAILDIR ) || die "Cannot change to $MAILDIR (`x' bit not set?)"; + +# +# slurp in directory +# +opendir (MAIL, ".") || die "Cannot open $MAILDIR (`r' bit not set?)"; +@allentries= readdir (MAIL); +closedir (MAIL); +@allnames = grep (!/$LOCALMAILJUNK|$MAILJUNK/, @allentries); + +# Open chanel to SMTP for verification -- unless this option is +# configured off. + +if ( ! $noverify ) { + local($addr, $port,$sockaddr); + + socket (VRFY, &AF_INET, &SOCK_STREAM, 0) || + die "Could not create TCP socket (SMTP channel)"; + + $addr = (gethostbyname($SMTPHOST))[4]; # Just use the first addr + + die "Could not obtain STMP host ($SMTPHOST) address" + if ( $addr eq "" ); + + $port = (getservbyname('smtp','tcp'))[2]; # Get smtp port. + die "Could not obtain SMTP port number" if (!defined($port)); + + printf("SMTP: address: %s port: $port\n", + join ('.',unpack('C4',$addr))) if ($debug); + + $sockaddr = pack('n2C4x8',2, $port ,unpack('C4',$addr)); + + printf("Sockaddr: %s\n", join (' ',unpack('C14',$sockaddr))) if ($debug); + + connect (VRFY, $sockaddr) || + die "Could not connect to SMTP daemon on $SMTPHOST"; + + print "Establshed SMTP channel\n" if ($debug); + + &smtp_recv; # Greet wait + &smtp_send("helo $SMTPHOST"); # Helo message for picky SMTPs + &smtp_recv; # Helo reply + + # Connection is up and ready to VRFY +} + +# main stuff starts here +foreach $currentTO (@allnames) { + next if ( &Check_user == $ABORT_RESEND); + + undef (@wanderers); # Just reset this at each pass. + @wanderers=grep (/$currentTO\.\d+/, @allentries); + + $remail_file = &Lock_file($currentTO,$FALSE); # Need to lock the spool. + + next if ( $remail_file eq $ABORT_RESEND); # Could not get that lock + + push (@wanderers, $remail_file); # Try to resend "old" files. + print "List to remail: @wanderers\n" if ($debug); + # check if there is something to remail + &Remail_all if ( defined @wanderers && !$nomail); +} + +# this stuff should run at the end +foreach $file (grep (/$LOCALMAILJUNK/,@allentries)) { + + if ($debug) { + print "Would unlink $file\n" if ($debug); + } else { + unlink $file if (-f $file); + } + +} +&Clean_up; # Do a clean exit. diff --git a/contrib/amd/scripts/wait4amd.in b/contrib/amd/scripts/wait4amd.in new file mode 100755 index 0000000..5fd5030 --- /dev/null +++ b/contrib/amd/scripts/wait4amd.in @@ -0,0 +1,45 @@ +#!/bin/sh +# wait for amd to start up and then execute program +# usage: wait4amd <hostname> [<command> [args ...]] +# If only hostname is supplied, command defaults to rsh $hostname +# +# Package: am-utils-6.0 +# Author: Erez Zadok <ezk@cs.columbia.edu> + +#set -x + +if [ "X$1" = "X" ]; then + echo "Usage: wait4amd <hostname> [<command> [args ...]]" + exit 1 +else + hostname=$1 + shift +fi + +# set path +prefix=@prefix@ +exec_prefix=@exec_prefix@ +PATH=@sbindir@:@bindir@:${PATH} +export PATH + +while true +do + amq -h $hostname > /dev/null 2>&1 + if [ $? != 0 ] + then + # failed + echo "Amd not up. Sleeping..." + sleep 5; + else + echo "Amd is active on host $hostname!" + cmd=$* + if [ -z "${cmd}" ] + then + cmd="rlogin $hostname" + fi + echo "Running: $cmd" + $cmd + echo "Sleep 1 second" + sleep 1 + fi +done diff --git a/contrib/amd/scripts/wait4amd2die.in b/contrib/amd/scripts/wait4amd2die.in new file mode 100755 index 0000000..d3541e7 --- /dev/null +++ b/contrib/amd/scripts/wait4amd2die.in @@ -0,0 +1,49 @@ +#!/bin/sh +# wait for amd to die on local host before returning from program. +# Usage: wait4amd2die [delay [count]] +# If not specified, delay=5 seconds and count=6 (total 30 seconds) +# If at end of total delay amd is till up, return 1; else return 0. +# +# Package: am-utils-6.0 +# Author: Erez Zadok <ezk@cs.columbia.edu> + +#set -x + +# set path +prefix=@prefix@ +exec_prefix=@exec_prefix@ +PATH=@sbindir@:@bindir@:/usr/bin:/bin:${PATH} +export PATH + +# how long to wait? +if test -n "$1" +then + delay=$1 +else + delay=5 +fi +# how many times to delay +if test -n "$2" +then + count=$2 +else + count=6 +fi + +i=1 +maxcount=`expr $count + 1` +while [ $i != $maxcount ]; do + # run amq + @sbindir@/amq > /dev/null 2>&1 + if [ $? != 0 ] + then + # amq failed to run (because amd is dead) + echo "wait4amd2die: amd is down!" + exit 0 + fi + echo "wait4amd2die: delay $delay sec ($i of $count)..." + sleep $delay + i=`expr $i + 1` +done +echo "wait4amd2die: amd is still up..." +exit 1 |