exports_setup.proxy.in 7.78 KB
Newer Older
1
#!/usr/bin/perl -wT
Leigh B. Stoller's avatar
Leigh B. Stoller committed
2 3

#
4
# Copyright (c) 2000-2015 University of Utah and the Flux Group.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
24 25
#

26
use English;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
27
use Errno;
28
use Fcntl ':flock';
29
use Getopt::Std;
30 31 32 33 34 35 36 37

#
# Create and /etc/exports file based on current reserved table and project
# members.
#
# usage: exports_setup
#

38
my %opts = ();
39
getopts('Si', \%opts);
40

41 42 43
#
# Configure variables
#
44
my $TBOPS       = "@TBOPSEMAIL@";
45
my $LINUX_FSNODE= @LINUX_FSNODE@;
46
my $INC_MOUNTD  = @INCREMENTAL_MOUNTD@;
47

48 49 50 51 52 53 54 55
my $etcdir;
my $exports;
my $exportsnew;
my $exportsback;
my $exportshead;
my $exportstail;
my $pidfile;
my $daemon;
56
my $incremental;
57
my $issamba = 0;
58 59 60

# Are we modifying the Samba config file or the NFS exports?
if (defined($opts{'S'})) {
61
    $issamba	 = 1;
62
    $etcdir      = ($LINUX_FSNODE ? "/etc/samba" : "/usr/local/etc");
63 64 65 66 67
    $exports	 = "$etcdir/smb.conf";
    $exportsnew  = "$etcdir/smb.conf.new";
    $exportsback = "$etcdir/smb.conf.backup";
    $exportshead = "$etcdir/smb.conf.head";
    $exportstail = "$etcdir/smb.conf.tail";
68
    if (-r "/var/run/samba/smbd.pid") {
69
	$pidfile = "/var/run/samba/smbd.pid";
70
    } elsif (-r "/var/run/smbd.pid") {
71
	$pidfile = "/var/run/smbd.pid";
72 73
    } else {
	fatal("Cannot locate pidfile for smbd\n");
74
    }
75 76 77 78 79 80 81 82 83 84 85
    $daemon      = "smbd";
}
else {
    $etcdir      = "/etc";
    $exports	 = "$etcdir/exports";
    $exportsnew  = "$etcdir/exports.new";
    $exportsback = "$etcdir/exports.backup";
    $exportshead = "$etcdir/exports.head";
    $exportstail = "$etcdir/exports.tail";
    $pidfile     = "/var/run/mountd.pid";
    $daemon      = "mountd";
86
    $exportfs    = ($LINUX_FSNODE ? "/usr/sbin/exportfs -ra" : undef);
87

88 89
    # incremental only applies to mountd
    if (defined($opts{'i'})) {
90 91 92 93 94
	if ($INC_MOUNTD) {
	    $incremental = 1;
	} else {
	    print "WARNING: incremental updates not supported, ignoring option\n";
	}
95
    }
96 97
}

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
my $dbg		= 0;
my @row;

#
# We don't want to run this script unless its the real version.
#
if ($UID != 0) {
    die("Must be root!");
}

# un-taint path
$ENV{'PATH'} = '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

$| = 1; #Turn off line buffering on output

114 115 116 117 118 119
#
# Testbed Support libraries
# 
use lib "@prefix@/lib";
use libtestbed;

120 121 122 123 124
#
# Take our input and write it to the tail file.
#
open(TAIL, ">$exportstail") || fatal("Couldn't open $exportstail\n");
while (<STDIN>) {
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
    #
    # Pass through if samba or a comment or blank line.
    # We don't test samba lines since we assume the samba invocation
    # of us (-S) will follow the normal invocation and have the same
    # directories.
    #
    if ($issamba || $_ !~ /^\//) {
	print TAIL $_;
	next;
    }

    #
    # As we do this, we parse the lines and check each of the exported
    # filesystems to make sure they exist. Otherwise mountd may prematurely
    # fail. We used to do this on the boss-side, but that would stat the
    # directories across NFS, and in some cases, before the filesystem
    # was even exported!
    #
    my @tokens = ();
    my $numdirs = 0;
    my $dirsdone = 0;
    foreach my $token (split) {
	if (!$dirsdone) {
	    # starts with a slash, assume it is an exported dir
	    if ($token =~ /^\//) {
		if (! -d "$token") {
		    print STDERR "$token: does not exist, ignored\n";
		    next;
		}
		$numdirs++;
	    } else {
		$dirsdone = 1;
	    }
	}
	push(@tokens, $token);
    }
    
    # if there were valid directories to export, do so
    if ($numdirs > 0) {
	print TAIL join(' ', @tokens), "\n";
    }
166 167 168 169 170 171 172 173 174 175 176 177 178
}
close(TAIL);
chmod(0444, $exportstail);

#
# Generate a warning so that no one tries to edit the file by hand
#
open(MAP, ">$exportsnew") || fatal("Couldn't open $exportsnew\n");
print MAP
    "#\n".
    "# ******************************************************************\n".
    "# DO NOT EDIT THIS FILE. IT IS A CREATION, A FIGMENT, A CONTRIVANCE!\n".
    "#\n".
179
    "# Edit $exportshead, then run exports_setup on boss.\n".
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
    "# ******************************************************************\n".
    "#\n";
close(MAP);
chmod(0644, $exportsnew);

#
# Now tack on the head part of the file.
#
system("cat $exportshead >> $exportsnew") == 0 or
    fatal("Failed to concat $exportshead to $exportsnew\n");

#
# Now the tail of the file.
# 
system("cat $exportstail >> $exportsnew") == 0 or
    fatal("Failed to concat $exportstail to $exportsnew\n");

197 198 199
#
# Do nothing if no change.
#
200 201 202 203 204
# Note that for now, we are actually saving the diffs so we can
# compare to the new differential behavior of mountd in case of
# an inconsisency. But only for mountd, not smbd.
#
my $outfile;
205
if ($issamba) {
206 207 208 209 210
    system("/usr/bin/diff -q $exports $exportsnew >/dev/null");
} else {
    $outfile = "/tmp/exports_diff." . time();
    system("/usr/bin/diff $exports $exportsnew >$outfile 2>&1");
}
211 212
if (! $?) {
#    print "No changes to $exports; skipping ...\n";
213 214
    unlink($outfile)
	if ($outfile);
215 216 217
    exit(0);
}

218 219 220 221 222 223 224 225 226 227 228 229
#
# Back up the existing exports, and then mv in the new one.
#
system("cp $exports $exportsback") == 0 or
    fatal("Could not back up $exports to $exportsback\n");

system("mv $exportsnew $exports") == 0 or
    fatal("Could not mv $exportsnew to $exports\n");

# Avoid accidental editing.
chmod(0444, $exports);

230
my $checkstamp = 0;
231 232 233 234 235 236 237
if (!$LINUX_FSNODE) {
    my $daemonpid = `cat $pidfile`;
    $daemonpid =~ s/\n//;
    # untaint
    if ($daemonpid =~ /^([-\@\w.]+)$/) {
	$daemonpid = $1;
    }
238 239 240 241 242 243 244 245 246

    #
    # Utah feature: if mountd is maintaining a timestamp file, we use
    # that to determine when it has finished processing the exports.
    #
    # XXX since mountd always blindly updates the file, we just remove it
    # here so we don't have to bother checking the content, we just have to
    # wait for it to exist.
    #
247
    if (!$issamba && unlink("/var/run/mountd.ts") != 0) {
248 249 250
	$checkstamp = 1;
    }

251 252 253 254 255 256 257 258
    if ($incremental) {
	if (kill('USR1', $daemonpid) == 0) {
	    fatal("Could not kill(USR1) process $daemonpid ($daemon): $!");
	}
    } else {
	if (kill('HUP', $daemonpid) == 0) {
	    fatal("Could not kill(HUP) process $daemonpid ($daemon): $!");
	}
259
    }
260
}
261 262
else {
    # Not supporting Samba at this time. 
263
    if (!$issamba) {
264 265 266 267 268 269 270
	#
	# run exportfs. linux handles exports changes much more gracefully
	# then freebsd does.
	#
        system($exportfs) == 0 or
            fatal("Could not run $exportfs\n");
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
271
}
272

273
#
274
# In FreeBSD, must allow time to react since HUP'ing mountd causes all
275 276
# mounts to briefly become invalid. Our caller (exports_setup) checks for
# this, but we need to make sure that processing has at least started!
277
#
278 279 280
# Note that Utah has hacked mountd to write a timestamp when it is done.
# Hence we can tell exactly when it has finished. In this case, our caller
# will not wait at all.
281
#
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
if ($checkstamp) {
    # On Utah Emulab with thousands of mount points, this can take 15 seconds!
    my $wtime = $incremental ? 8 : 15;

    for (my $i = 0; $i < $wtime; $i++) {
	print "exports_setup.proxy: waiting for mountd to finish ($i)...\n";
	if (-e "/var/run/mountd.ts") {
	    print "exports_setup.proxy: mountd done.\n";
	    last;
	}
	sleep(1);
    }
} else {
    sleep(1);
}
297

298 299 300 301 302
exit(0);

sub fatal {
    local($msg) = $_[0];

303
    SENDMAIL($TBOPS, "Exports Setup Failed", $msg);    
304 305 306
    die($msg);
}