smfst2@unix.cis.pitt.edu (Seth M Fuller) (06/11/91)
Below is a shell archive of a global mail script I adapted from Larry Wall's gsh. I haven't seen anything like it and I thought it might be useful to others. Seth M. Fuller #!/bin/sh # to extract, remove the header and type "sh filename" if `test ! -s ./README.gmail` then echo "writing ./README.gmail" cat > ./README.gmail << '\End\Of\Shar\' Gmail --- Mail Globally This is gmail. Gmail is a perl script that I adapted from Larry Wall's gsh. It allows you to mail globally to users via an alias file called gusers. I realize there are other ways of doing this with many mail programs, but I wanted a neat, simple solution to use at our offices that wouldn't require a lot of maintenance. Gmail lets you compose your mail message in your favorite editor (or stdin), then mails it to the list of users (or aliases) you specify on the command line. Gmail uses an alias file much like ghosts. The main difference being that the first field is a host name and the second field is a user name. All other fields are aliases. Gmail is also smart enough to know whether your mail program is BSD mail or ATT mail and from that whether or not to prompt for a subject. Gmail tests to see if the file /usr/lib/sendmail exists, if it does it assumes this is a BSD mail program, if not an ATT mail program. If anybody knows of a better way of doing this let me know. If anybody has any comments I can be reached at: Seth M. Fuller Giant Eagle, Inc. MIS Dept. 101 Kappa Drive Pittsburgh, PA 15238 (412)963-6200 e-mail: unix.cis.pitt.edu!smfst2 \End\Of\Shar\ else echo "will not over write ./README.gmail" fi if `test ! -s ./gmail` then echo "writing ./gmail" cat > ./gmail << '\End\Of\Shar\' #! /acct/bin/perl # gmail --- Mail globally via the gusers file # Author: Seth M. Fuller # I adapted this program from gsh, written by Larry Wall. # This program accesses a file called gusers, similar to ghosts, for its # aliases. The main difference is that the first two fields are the host # name for the user and the user's login name. The remaining fields are # any mailing groups that the user belongs to. # Mail globally $SIG{'QUIT'} = 'quit'; # install signal handler for SIGQUIT sub getswitches { while ($ARGV[0] =~ /^-/) { # parse switches $ARGV[0] =~ /^-b/ && ($bsdmail++,shift(@ARGV),next); $ARGV[0] =~ /^-s/ && ($silent++,shift(@ARGV),next); $ARGV[0] =~ /^-d/ && ($from_stdin++,shift(@ARGV),next); $ARGV[0] =~ /^-e/ && ($got_editor++,$editor=$ARGV[1], shift(@ARGV),shift(@ARGV),next); last; } } do getswitches(); # get any switches before class $systype = shift; # get name representing set of users $cmd = join(' ',@ARGV); # remaining args constitute the command $cmd =~ s/'/'"'"'/g; # quote any embedded single quotes $one_of_these = ":$systype:"; # prepare to expand "macros" $one_of_these =~ s/\+/:/g; # we hope to end up with list of $one_of_these =~ s/-/:-/g; # colon separated attributes if (! $got_editor) { $editor = $ENV{'GMEDIT'}; # Get or set editor if (! $editor) { $editor = $ENV{'EDITOR'}; # Get or set editor if (! $editor) { $editor = "vi"; } } } else { $from_stdin = 0; } $remainder = ''; $bsdmail++ if -f "/usr/lib/sendmail"; if ($bsdmail) { $|=1; # Turn off buffering for reading from keyboard print "Subject: "; @subject = &readline(); $subject = join('', @subject); } if ($from_stdin) { # Get mail from stdin print "Enter body of mail message then Ctrl-D (on a line by itself)\n"; `cat >/tmp/gmail$$`; # get input into a handy place } else { system("$editor /tmp/gmail$$"); if ( -f "/tmp/gmail$$" == 0) { unlink "/tmp/gmail$$"; exit 0; } } @ARGV = (); push(@ARGV,'.gurem') if -f '.gurem'; push(@ARGV,'.gusers') if -f '.gusers'; push(@ARGV,'/etc/gusers') if -f '/etc/gusers'; push(@ARGV,'gusers') if -f 'gusers'; line: while (<>) { # for each line of gusers s/[ \t]*\n//; # trim trailing whitespace if (!$_ || /^#/) { # skip blank line or comment next line; } if (/^(\w+)=(.+)/) { # a macro line? $name = $1; $repl = $2; $repl =~ s/\+/:/g; $repl =~ s/-/:-/g; $one_of_these =~ s/:$name:/:$repl:/; # do expansion in "wanted" list $repl =~ s/:/:-/g; $one_of_these =~ s/:-$name:/:-$repl:/; next line; } # we have a normal line @attr = split(' '); # a list of attributes to match against # which we put into an array $host = $attr[0]; # the first attribute is the host name if ($host eq "local") { $host = ""; } else { $host = $host . "!"; } $user = $attr[1]; # the second attribute is the user name $wanted = 0; foreach $attr (@attr) { # iterate over attribute array $wanted++ if index($one_of_these,":$attr:") >= 0; $wanted = -9999 if index($one_of_these,":-$attr:") >= 0; } if ($wanted > 0) { if ($bsdmail) { print "mail -s \"$subject\" $host$user\n" unless $silent; } else { print "mail $host$user\n" unless $silent; } $SIG{'INT'} = 'DEFAULT'; if ($bsdmail) { if (open(PIPE, # mail the msg. "mail -s \"$subject\" $host$user < /tmp/gmail$$ 2>&1|")) { $SIG{'INT'} = 'cont'; close(PIPE); } else { print "(Can't execute mail: $!)\n"; $SIG{'INT'} = 'cont'; } } else { if (open(PIPE, "mail $host$user < /tmp/gmail$$ 2>&1|")) { # mail the msg. $SIG{'INT'} = 'cont'; close(PIPE); } else { print "(Can't execute mail: $!)\n"; $SIG{'INT'} = 'cont'; } } } } unlink "/tmp/gmail$$"; if ($remainder) { chop($remainder); open(gurem,">.gurem") || (printf stderr "Can't make a .gurem file: $!\n"); print gurem 'urem=', $remainder, "\n"; close(gurem); print 'urem=', $remainder, "\n"; } # here are a couple of subroutines that serve as signal handlers sub cont { print "\rContinuing...\n"; $remainder .= "$host+"; } sub quit { $| = 1; print "\r"; $SIG{'INT'} = ''; kill 2, $$; } # This subroutine reads a line from the standard input and returns an # array with all of characters typed by the user up to the newline. sub readline { $more_line = 1; while ($more_line) { $ch = getc; if ($ch lt ' ') { $more_line = 0; } else { push(@line, $ch); } } return @line; } \End\Of\Shar\ else echo "will not over write ./gmail" fi if `test ! -s ./gmail.man` then echo "writing ./gmail.man" cat > ./gmail.man << '\End\Of\Shar\' .TH GMAIL L "10 Jun 1991" .SH NAME gmail \- globally mail to aliases .SH SYNOPSIS .B gmail [options] .I user-alias .SH DESCRIPTION .I Gmail works similarly to mail(1) except that you may specify a set of users mail to. The host/user sets are defined in the file /etc/gusers. Each line of gusers file is of the form of: host user [alias] [alias] [alias] ... If the host is the word "local" no host is specified in the mail command and the message goes to the user on the local system. (An individual user name can be used as a set containing one member.) You can give a command like gmail mis to mail to all people in the MIS Department. .P Gmail starts up an editor to allow you to type in your message. Once you save the message and exit the editor, gmail starts sending it out to all users in the list. Gmail will first check to see if the environment variable GMEDIT is set. If set gmail will use the value of GMEDIT as the editor. If there is no value for GMEDIT, gmail will check to see if the environment variable EDITOR is set and will use that editor, otherwise, gmail defaults to vi as the editor. The -e option allows you to specify the editor to use on the command line (full path not needed). The -e option overrides the envronment variables. You may instead use the -d option to accept the mail message from standard input until end of file. .P You may specify the union of two or more sets by using + as follows: gmail pos+store which will mail to all members of the POS and store groups. .P Commonly used sets should be defined in /etc/gusers. For example, you could add a line that says mis=admin+pos+store Another way to do that would be to add the word "mis" after each of the host/user entries: calvin chas admin mis .br mickey seth retail store mis .br pluto johnc retail pos Users and sets of user can also be excluded: retail=mis-admin Any user so excluded will never be included, even if a subsequent set on the line includes it: foo=abc+def bar=xyz-abc+foo comes out to xyz+def. You can define private user sets by creating .gusers in your current directory with entries just like /etc/gusers. Also, if there is a file .gurem, it defines "urem" to be the remaining users from the last gmail that didn't succeed everywhere. Options are .IP "\-b" 8 Requests a subject for BSD style mail programs. Gmail will try to determine if your mail program is BSD or ATT and request or not request a subject accordingly (in other words you probably won't need to use this option if gmail can figure out whether or not you use BSD mail by whether or not your system has /usr/lib/sendmail). .IP "\-d" 8 Causes gmail to collect input till end of file, and then distribute that input to each invokation of mail. .IP "\-e" 8 Specify the editor to use for the mail message. Incompatible with the -d option. .IP "\-s" 8 Do work silently. .PP Interrupting with a SIGINT will cause the mail to the current user to be skipped and execution resumed with the next user. To stop completely, send a SIGQUIT. .SH RESTRICTIONS Gmail only sends mail. It is not for receiving mail. No provision is made carbon copying, but that should not be needed with well defined sets of users. .SH SEE ALSO mail(1) \End\Of\Shar\ else echo "will not over write ./gmail.man" fi if `test ! -s ./gusers` then echo "writing ./gusers" cat > ./gusers << '\End\Of\Shar\' # This first section gives alternate sets defined in terms of the sets given # by the second section. The order is important--all references must be # forward references. mis=store+pos+admin # This second section defines the basic sets. Each host/user combination # should have a line that specifies which sets it is a member of. # Extra sets should be separated by white space. (The first section isn't # strictly necessary, since all sets could be defined in the second section, # but then it wouldn't be so readable.) # Each line must be of the form of: # host user [alias] [alias] [alias] ... local seth retail store calvin chas admin pluto Jon retail pos pluto anne retail store pluto floyd retail store cricket fredb retail store pluto joe retail pluto johnc retail pos pluto maryann retail pos \End\Of\Shar\ else echo "will not over write ./gusers" fi echo "Finished archive 1 of 1" exit