Cover V14, i04

Article

apr2005.tar

Questions and Answers

Amy Rich

In the December issue, one of the questions covered modifying root's crontab using scp and ssh. In order for the supplied script to work correctly, each command after the ssh needs to be quoted so that it is not executed on the local machine instead of the intended target machine. Thanks to Josh Simon for catching this error. The corrected script should be:

 #!/bin/sh
 for i in host1 host2 host2 host4; do
   scp master.cron root@${i}:/location/to/crontab/file
   ssh root@${i} "chmod 0400 /location/to/crontab/file;" \
"chown root /location/to/crontab/file;" \
"crontab /location/to/crontab/file;" \
"crontab -l"
Q I'm running Solaris 9 on a V880, and I'm having an issue with a script running from cron. The script is a slightly modified version of the updatedb script that comes with locate. Here's my version of the script (available at http://www.gnu.org/licenses/licenses.html#GPL) with all of the comments and copyright information edited out for simplicity's sake:

#!/bin/sh

usage="\
Usage: updatedb [--localpaths='dir1 dir2...'] [--netpaths='dir1 dir2...']
 [--prunepaths='dir1 dir2...'] [--output=dbfile] [--netuser=user]
 [--old-format] [--version] [--help]"

old=no
for arg
do
  opt='echo $arg|sed 's/^\([^=]*\).*/\1/''
  val='echo $arg|sed 's/^[^=]*=\(.*\)/\1/''
  case "$opt" in
    --localpaths) SEARCHPATHS="$val" ;;
    --netpaths) NETPATHS="$val" ;;
    --prunepaths) PRUNEPATHS="$val" ;;
    --output) LOCATE_DB="$val" ;;
    --netuser) NETUSER="$val" ;;
    --old-format) old=yes ;;
    --version) echo "GNU updatedb version 4.1"; exit 0 ;;
    --help) echo "$usage"; exit 0 ;;
    *) echo "updatedb: invalid option $opt
$usage" >&2
       exit 1 ;;
  esac
done
     
: ${SEARCHPATHS="/"}
: ${NETPATHS=}
: ${PRUNEPATHS="/tmp /usr/tmp /var /users /usr/local/src \
  /root /proc /dev /devices /data/amanda"}

 test -z "$PRUNEREGEX" &&
  PRUNEREGEX='echo $PRUNEPATHS|sed -e 's,^,\\\(^,' -e 's, \
    ,$\\\)\\\|\\\(^,g' -e 's,$,$\\\),''

: ${LOCATE_DB=/usr/local/var/locatedb}
     
if test -d /root/tmp; then
  : ${TMPDIR=/root/tmp}
else
  : ${TMPDIR=/tmp}
fi
     
: ${NETUSER=daemon}
: ${LIBEXECDIR=/usr/local/libexec}
: ${BINDIR=/usr/local/bin}
: ${find=find}
: ${frcode=frcode}
: ${bigram=bigram}
: ${code=code}

PATH=$LIBEXECDIR:$BINDIR:/usr/ucb:/bin:/usr/bin:$PATH export PATH

if test $old = no; then
{
if test -n "$SEARCHPATHS"; then
  $find $SEARCHPATHS \
  \( -fstype nfs -o -fstype NFS -o -type d -regex "$PRUNEREGEX" \) \
    -prune -o -print
fi

if test -n "$NETPATHS"; then
  su $NETUSER -c \
  "$find $NETPATHS \\( -type d -regex \"$PRUNEREGEX\" -prune \\) \
   -o -print"
fi
} | sort -f | $frcode > $LOCATE_DB.n

if test -s $LOCATE_DB.n; then
  rm -f $LOCATE_DB
  mv $LOCATE_DB.n $LOCATE_DB
  chmod 644 $LOCATE_DB
else
  echo "updatedb: new database would be empty" >&2
  rm -f $LOCATE_DB.n
fi

else

bigrams=$TMPDIR/f.bigrams$$
filelist=$TMPDIR/f.list$$

trap 'rm -f $bigrams $filelist; exit' 1 15
{
if test -n "$SEARCHPATHS"; then
  $find $SEARCHPATHS \
  \( -fstype nfs -o -fstype NFS -o -type d -regex "$PRUNEREGEX" \) \
    -prune -o -print
fi
if test -n "$NETPATHS"; then
  su $NETUSER -c \
  "$find $NETPATHS \\( -type d -regex \"$PRUNEREGEX\" -prune \\) \
   -o -print"
fi
} | tr / '\001' | sort -f | tr '\001' / > $filelist

$bigram < $filelist | sort | uniq -c | sort -nr |
  awk '{ if (NR <= 128) print $2 }' | tr -d '\012' > $bigrams
$code $bigrams < $filelist > $LOCATE_DB
chmod 644 $LOCATE_DB

rm -f $bigrams $filelist $errs

fi
If I run this script by hand, everything works fine, but when I invoke it from cron:

0 0,4,8,12,16,20 * * * * /usr/local/bin/updatedb.local > /dev/null 2>&1
I get this error in email:

Your "cron" job on db
 * /usr/local/bin/updatedb.local > /dev/null 2>&1
produced the following output:

 sh: TT_DB: not found
According to what I've been able to find, TT_DB is related to ToolTalk's rpc.ttdbserverd. I have no idea why that would ever be called with the updatedb.local script, so I was hoping you could enlighten me and help me debug this issue.

A There's actually nothing wrong with your script, and if you look closely at the error you'll see what the real problem is. The output lists your script as * /usr/local/bin/updatedb.local > /dev/null 2>&1. Note that there's an extra asterisk in there. You've put too many fields into the cron entry, and the first asterisk is being interpreted by the shell. The first (non-dotfile) entry in your root directory must be the directory TT_DB. Cron is trying to execute this directory and so gives you the somewhat misleading error message.

Q We're migrating services from a FreeBSD 4.2 machine to a new FreeBSD 5.3 machine and have been experiencing problems with LPRng and printing out banner pages. We have a custom banner page that we're printing using the pclbanner Bourne shell script. I've copied this over from the old machine and set up the printcap file in the same way. When I try to print a page, though, I see the following errors in the logs:

Status: BANNER: expr: illegal option -- A at 09:38:13.218
Status: BANNER: usage: expr [-e] expression at 09:38:13.219
My printcap file contains the following:

hp5100dtn
:af=acct
:bp=/usr/libexec/filters/pclbanner
:filter=/usr/libexec/filters/ifhp
:ifhp=model=hp5,status,sync,pagecount,waitend
:lf=log
:db=2,print+4
:mx=0
:prefix_z=1200,letter,portrait
:sd=/var/spool/lpd/%P
:sh@
:lp=<ip address>%9100
I haven't changed the pclbanner script or the printcap at all, and I even tried compiling LPRng from scratch to see if that was the problem. Any suggestions on how I can get this to work again?

A You're running into an issue with the change in the way expr behaves on FreeBSD 5.x. From the FreeBSD 5.x release notes:

expr(1) is now compliant with POSIX.2-1992 (and thus also with POSIX.1-2001). Some programs depend on the old, historic behavior and do not properly protect their arguments to keep them from being misinterpreted as command-line options. (the devel/libtool port/package, used by many GNU programs, is a notable example). The old behavior can be requested by enabling compatibility mode for expr(1) as described in check_utility_compat(3).

You can try to rewrite your pclbanner script to use the POSIX-compliant invocation of expr, or you can turn on compatibility mode so that expr uses the pre-POSIX syntax. The latter may be your best bet if you don't understand Bourne shell scripting very well or can't decipher what's being incorrectly passed to the pclbanner program. From the check_utility_compat(3) man page:

The check_utility_compat() function checks whether utility should behave in a traditional (FreeBSD 4.7-compatible) manner, or in accordance with IEEE Std 1003.1-2001 ('POSIX.1''). The configuration is given as a comma-separated list of utility names; if the list is present but empty, all supported utilities assume their most compatible mode. The check_utility_compat() function first checks for an environment variable named _COMPAT_FreeBSD_4. If that environment variable does not exist, then check_utility_compat() will attempt to read the contents of a symbolic link named /etc/compat-FreeBSD-4-util. If no configuration is found, compatibility mode is disabled.

Because there will be issues with any programs that require the POSIX syntax, I would suggest setting the environment variable in the pclbanner script instead of making a global modification in the profiles of all users or in the /etc/compat-FreeBSD-4-util file. Near the top of your pclbanner script, add the following line:

export _COMPAT_FreeBSD_4=expr
This runs expr in compatibility mode just for this specific application and does not affect any other programs that call expr.

Q I have an HP-UX 11i running a mail server using an in-house compiled sendmail 8.13.2. I've created the cf file with an absolute basic mc file that only contains the following:

divert(0)dnl
OSTYPE(hpux11)dnl
MASQUERADE_AS('my.domain')dnl
FEATURE('masquerade_entire_domain')dnl
FEATURE('allmasquerade')dnl
FEATURE('masquerade_envelope')dnl
FEATURE('always_add_domain')dnl
FEATURE('nouucp','reject')dnl
FEATURE('use_cw_file')dnl
LOCAL_USER('root')dnl
EXPOSED_USER('root')dnl
MAILER(local)dnl
MAILER(smtp)dnl
The compile and the installation work without any errors, and the daemon starts up just like it's supposed to. I wrote a few scripts that use telnet and expect to connect to port 25 to do some automated testing, and I turned up something strange when sending test messages using this method. The following gets sent to the SMTP port after connection:

ehlo host.my.domain
mail from: myaddress@my.domain
rcpt to: myaddress@my.domain
data
From: myaddress@my.domain
To: myaddress@my.domain
Subject: test

... Testing, 1, 2, 3 ...
.
I receive the mail on the local machine, as expected, but the contents of the message have been modified so that the leading dot is missing from the body:

.. Testing, 1, 2, 3 ...
At first I thought this might be an issue with my script, so I manually telnet'ed to port 25 and entered the above text. The body was once again modified, but I'm not sure what's eating that first dot. Did I mis-configure the cf file somehow, or did something go wrong in the compile? Any insight would go a long way toward helping me track down this issue.

A The behavior you're seeing from sendmail, known as dot-stuffing, is actually documented in RFC 821 and the superseding RFC, 2821. These RFCs are available from http://www.faqs.org/, among other places. From looking at your script, you already know that a line consisting of a dot followed by a CRLF terminates the current message. Dot-stuffing is designed to prevent message termination due to the contents of the message body as submitted by an end user. From RFC 2821, section 4.1.1.4:

The mail data is terminated by a line containing only a period, that is, the character sequence "<CRLF>.<CRLF>" (see section 4.5.2). This is the end of mail data indication.

And from section 4.5.2 of the same document:

Without some provision for data transparency, the character sequence "<CRLF>.<CRLF>" ends the mail text and cannot be sent by the user. In general, users are not aware of such "forbidden" sequences. To allow all user composed text to be transmitted transparently, the following procedures are used:

- Before sending a line of mail text, the SMTP client checks the first character of the line. If it is a period, one additional period is inserted at the beginning of the line.

- When a line of mail text is received by the SMTP server, it checks the line. If the line is composed of a single period, it is treated as the end of mail indicator. If the first character is a period, and there are other characters on the line, the first character is deleted.

This means is that the initiating SMTP server will add a dot to the beginning of any line of text in the DATA portion of the connection that starts with a dot. This prevents the receiving SMTP server from seeing a lone dot in a message body line and inadvertently terminating the message in the middle of the body.

Because you're connecting to the SMTP port directly, there is no "sending" SMTP server to add the additional dot into the DATA portion of the message. As a result, the SMTP daemon to which you're connecting (which happens to be sendmail, but this is not a sendmail-specific issue) strips off the first dot and leaves you with only three. If you want your message body to remain unchanged, you'll need to manually dot-stuff the message in your script or when you connect interactively.

As a side note, technically your MAIL FROM and RCPT TO SMTP commands should have no space between the colon and the address. From the same RFC, section 3.3:

MAIL FROM:<reverse-path> [SP <mail-parameters> ] <CRLF>
RCPT TO:<forward-path> [ SP <rcpt-parameters> ] <CRLF>
If service extensions were negotiated, then these become as follows (section 4.1.1.2 and 4.1.1.3):

"MAIL FROM:" ("<>" / Reverse-Path)
  [SP Mail-parameters] CRLF

"RCPT TO:" ("<Postmaster@" domain ">" / "<Postmaster>" / Forward-Path)
  [SP Rcpt-parameters] CRLF
Amy Rich has more than a decade of Unix systems administration experience in various types of environments. Her current roles include that of Senior Systems Administrator for the University Systems Group at Tufts University, Unix systems administration consultant, and author. She can be reached at: qna@oceanwave.com.