Commit 8cac9c47 authored by Leigh Stoller's avatar Leigh Stoller

Add generation of per-project email lists, as per Dave's request. The

lists are stored on users:/etc/mail/lists. For example, you can send
email to ron-users@users.emulab.net. An alias entry is added (and
newaliases run) if there is no alias in /etc/mail/aliases (by the proxy
of course). There are two new options to genelists (on boss):

	"Use the -a option to generate lists for all projects.\n".
	"Use the -n option to generate lists for a new user.\n";

With no options, generate the all users and active users lists
(renamed to emulab-users and emulab-active-users). With the -n option
(used by mkacct) regen the lists for all the projects the user is a
member of.

It would be nice to archive the email, but that requires a publically
readable directory and a u+S archive file; the mailer daemon runs as
user daemon, and the project tree is 770, so it cannot write the
archive file. At some point we will have to go to majordomo or spam
filtering, when the first list is spamm'ed. Sigh.
parent 2e536ba3
#!/usr/bin/perl -wT
use Fcntl ':flock';
use English;
use Getopt::Std;
sub usage() {
print STDOUT "Usage: genelists\n".
"Generate the email list files after things change\n";
print STDOUT "Usage: genelists [-a] [-n uid]\n".
"Generate the email list files after things change.\n".
"Use the -a option to generate lists for all projects.\n".
"Use the -n option to generate lists for a new user.\n";
exit(-1);
}
my $optlist = "";
my $optlist = "an:";
# Configure variables
my $TB = "@prefix@";
......@@ -15,13 +18,15 @@ my $TBOPS = "@TBOPSEMAIL@";
my $USERS = "@USERNODE@";
my $TBACTIVE = "@TBACTIVEARCHIVE@";
my $TBALL = "@TBUSERSARCHIVE@";
my $PROJROOT = "/proj";
# Note no -n option. We redirect stdin from the new exports file below.
my $SSH = "$TB/bin/sshtb -l root $USERS";
my $PROG = "/usr/testbed/sbin/genelists.proxy";
my $lockfile = "/var/tmp/testbed_genelists_lockfile";
my $tempfile = "/var/tmp/testbed_genelists_tempfile";
my $userlist;
my $newuser;
my $allprojects;
my $d = 0;
#
......@@ -42,7 +47,7 @@ if ($EUID != 0) {
" Must be root! Maybe its a development version?\n");
}
# XXX Hacky!
if ($TB ne "/usr/testbed") {
if (1 && $TB ne "/usr/testbed") {
die("*** $0:\n".
" Wrong version. Maybe its a development version?\n");
}
......@@ -53,7 +58,33 @@ if ($TB ne "/usr/testbed") {
$ENV{'PATH'} = '/bin:/usr/bin:/usr/sbin:/usr/local/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
if (@ARGV != 0) {
#
# Parse command arguments. Once we return from getopts, all that should
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (@ARGV) {
usage();
}
if (defined($options{"a"})) {
$allprojects = 1;
}
if (defined($options{"n"})) {
$newuser = $options{"n"};
#
# Untaint.
#
if ($newuser =~ /^([-\w]+)$/) {
$newuser = $1;
}
else {
die("Tainted argument $newuser!\n");
}
}
if (defined($newuser) && defined($allprojects)) {
usage();
}
......@@ -71,6 +102,7 @@ while (flock(LOCK, LOCK_EX|LOCK_NB) == 0) {
foreach my $active ( 0, 1 ) {
my $progarg;
my $userlist;
if ($active) {
print "Getting Active Users\n" if $d;
......@@ -84,8 +116,8 @@ foreach my $active ( 0, 1 ) {
DBFatal("Getting Active Users!");
}
$userlist = "$TBOPS\n".
"$TBACTIVE\n";
$progarg = "-active";
"$TBACTIVE";
$progarg = "emulab-active-users";
}
else {
print "Getting All Users\n" if $d;
......@@ -96,9 +128,66 @@ foreach my $active ( 0, 1 ) {
DBFatal("Getting Users!");
}
$userlist = "$TBOPS\n".
"$TBALL\n";
$progarg = "-all";
"$TBALL";
$progarg = "emulab-users";
}
genelist($query_result, $userlist, $progarg);
}
#
# Regen project lists. Either all the lists, or just the projects
# that "newuser" is a member of.
#
if (defined($newuser) || defined($allprojects)) {
my $proj_result;
if (defined($newuser)) {
$proj_result =
DBQueryFatal("select pid from group_membership ".
"where uid='$newuser' and pid=gid");
}
else {
$proj_result =
DBQueryFatal("select pid from projects");
}
while (($pid) = $proj_result->fetchrow_array) {
print "Getting project members for $pid\n" if $d;
my $query_result =
DBQueryFatal("SELECT distinct u.usr_email from ".
" group_membership as p ".
"left join users as u on u.uid=p.uid ".
"where p.pid='$pid' and p.pid=p.gid and ".
" p.trust!='none' ".
"order by u.usr_email");
if ($query_result->numrows) {
my $archive = "$PROJROOT/$pid/$pid-users.mail";
#
# This would be nice, but will not work since the mailer daemon
# cannot access files in /proj/$pid.
#
if (0 && ! -e $archive) {
open(ARCHIVE, ">>$archive") or
fatal("Could not create $archive: $!");
close(ARCHIVE);
chmod(0666, "$archive") or
fatal("Could not chmod(666) $archive: $!");
}
genelist($query_result, undef, "$pid-users");
}
}
}
#
# Generate and fire over a list.
#
sub genelist($$$)
{
my($query_result, $inituserlist, $progarg) = @_;
open(LIST,"> $tempfile") ||
fatal("Couldn't open $tempfile: $!\n");
......@@ -106,21 +195,18 @@ foreach my $active ( 0, 1 ) {
print LIST "#\n";
print LIST "# WARNING! THIS FILE IS AUTOGENERATED. DO NOT EDIT!\n";
print LIST "#\n";
if (defined($inituserlist)) {
print LIST "$inituserlist\n";
}
for ($i = 0; $i < $query_result->numrows; $i++) {
$user_email = ($query_result->fetchrow_array())[0];
my $user_email = ($query_result->fetchrow_array())[0];
if (! defined($user_email)) {
next;
}
if ($userlist) {
$userlist .= "$user_email\n";
}
else {
$userlist = "$user_email\n";
}
print LIST "$user_email\n";
print "$user_email\n" if $d;
}
print LIST $userlist;
print $userlist if $d;
close(LIST);
#
......
......@@ -5,13 +5,15 @@ use Fcntl ':flock';
#
# Insert new version of testbed emails lists into place on operations node.
# The single argument indicates which list is being piped into the script
# from the control node.
# from the control node. We create a new alias in the aliases file as well.
#
# usage: genelists.proxy [-all | -active]
# NB: We do not prune dead lists yet.
#
# usage: genelists.proxy <list name>
#
sub usage() {
print STDOUT "Usage: genelists.proxy [-all | -active]\n".
"Generate the email list files after things change\n";
print STDOUT "Usage: genelists.proxy <list name>\n".
"Generate email list file after things change!\n";
exit(-1);
}
......@@ -20,17 +22,19 @@ sub usage() {
#
my $TBOPS = "@TBOPSEMAIL@";
my $maildir = "/etc/mail/lists";
my $tempfile = "$maildir/tempfile.$$";
my $alllist = "$maildir/testbed-users";
my $activelist = "$maildir/testbed-active-users";
my $maildir = "/etc/mail";
my $listdir = "$maildir/lists";
my $tempfile = "$listdir/tempfile.$$";
my $aliasfile = "$maildir/aliases";
my $listname;
my $thelist;
#
# We don't want to run this script unless its the real version.
#
if ($UID != 0) {
die("Must be root!");
die("*** $0:\n".
" Must be root!\n");
}
# un-taint path
......@@ -46,15 +50,21 @@ $| = 1;
use lib "@prefix@/lib";
use libtestbed;
if (@ARGV != 1 || ($ARGV[0] ne "-all" && $ARGV[0] ne "-active")) {
if (@ARGV != 1) {
usage();
}
if ($ARGV[0] eq "-active") {
$thelist = $activelist;
$listname = $ARGV[0];
#
# Untaint the arguments.
#
if ($listname =~ /^([-\@\w]+)$/) {
$listname = $1;
}
else {
$thelist = $alllist;
die("Tainted argument $listname!\n");
}
$thelist = "$listdir/$listname";
#
# Take our input and write it to the temp file.
......@@ -73,12 +83,32 @@ chmod(0644, $tempfile);
system("/bin/mv $tempfile $thelist") == 0 ||
fatal("Could not move $tempfile to $thelist: $!");
#
# See if the aliases exists in the alias file. If not, append it and
# rebuild with newaliases.
#
if (system("egrep -q -s '^${listname}:' $aliasfile")) {
print "Adding new alias $listname to aliases file\n";
open(ALIASES, ">>$aliasfile") or
fatal("opening $aliasfile to append new alias: $!");
printf ALIASES "%-24s%s\n", "${listname}:", ":include:${thelist}";
close(ALIASES) or
fatal("Could not close $aliasfile after modification: $!");
system("newaliases") == 0 or
fatal("Could not rebuild the aliases database!");
}
exit(0);
sub fatal {
local($msg) = $_[0];
SENDMAIL($TBOPS, "genelists.proxy failure", $msg);
die($msg);
die("*** $0:\n".
" $msg\n");
}
......@@ -177,7 +177,7 @@ $UID = $EUID;
# this command must be run when EUID==UID==0 because its a setuid
# PERL script.
#
system("$GENELISTS");
system("$GENELISTS -n $user");
#
# Make user on local. We don't give them a password since they are not
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment