frisbeelauncher.in 6.82 KB
Newer Older
1
#!/usr/bin/perl -wT
Leigh B. Stoller's avatar
Leigh B. Stoller committed
2
3
4
5
6
7
8

#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002 University of Utah and the Flux Group.
# All rights reserved.
#

Robert Ricci's avatar
Robert Ricci committed
9
10
11
use Getopt::Std;
use POSIX 'setsid'; # For &daemonize
use Sys::Syslog;
12
use English;
Robert Ricci's avatar
Robert Ricci committed
13
14
15

# Configure variables
my $TB		= "@prefix@";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
16
17
my $TBOPS	= "@TBOPSEMAIL@";

18
19
20
21
22
23
#
# Untaint the path
# 
$ENV{'PATH'} = "/bin:/usr/bin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

Leigh B. Stoller's avatar
Leigh B. Stoller committed
24
25
26
27
#
# Turn off line buffering on output
#
$| = 1;
Robert Ricci's avatar
Robert Ricci committed
28
29
30

use lib "@prefix@/lib";
use libdb;
31
use libtestbed;
Robert Ricci's avatar
Robert Ricci committed
32
33
34
35

# Defines
my $FRISBEED	= "$TB/sbin/frisbeed";
my $BASEADDR	= "234.5.6";
36
my $BASEPORT	= "3564";
Robert Ricci's avatar
Robert Ricci committed
37
38
39
my $LOGFILE	= "$TB/log/frisbeelauncher";

# Process command line options
Leigh B. Stoller's avatar
Leigh B. Stoller committed
40
getopts('d',\%opt);
Robert Ricci's avatar
Robert Ricci committed
41
42
43
44
45
if (@ARGV != 1) {
	exit &usage();
}
$imageid = shift @ARGV;

46
47
48
#
# Untaint the argument.
#
49
if ($imageid =~ /^([-\@\w\+.]+)$/) {
50
51
52
53
54
55
	$imageid = $1;
} else {
	die("Invalid image '$imageid' contains illegal characters.\n");
}


Leigh B. Stoller's avatar
Leigh B. Stoller committed
56
57
58
# Grab the filename to give to frisbee
my $filename = &get_filename($imageid);

59
60
61
62
#
# Make sure that the user has sufficient permissions, and read the image file
# or bomb out right now.
#
63
if (!TBImageIDAccessCheck($UID,$imageid,TB_IMAGEID_READINFO)) {
64
65
66
	die("*** You do not have pemssion to load $imageid on nodes\n");
}

67
if (! -R $filename) {
68
	die("*** You do not have permission to read the image file for\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
69
70
71
72
73
74
75
76
77
	    "imageid $imageid: $filename\n");
}

#
# Need to lock the tables here, since we are going to mess with the
# busy indicator.
#
&lock_tables;

Robert Ricci's avatar
Robert Ricci committed
78
79
80
# Try to discover if some other process is handling this address
$address = &get_address($imageid);

Leigh B. Stoller's avatar
Leigh B. Stoller committed
81
82
83
if ($address && &keepbusy($imageid)) {
        &unlock_tables;
	&debug("A server ($address) is already running for image $imageid\n");
Robert Ricci's avatar
Robert Ricci committed
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
	exit (0);
}

# Pick an address: Die if unsucessful, set address and unlock if sucessful
$address = &pick_address;
&debug("Picked address $address\n");

if (!$address) {
	&unlock_tables;
	die "Unable to find a free address to send on\n";
}

&set_address($imageid,$address);
&unlock_tables;

# Run in the background
100
101
102
if (TBBackGround($LOGFILE)) {
    exit(0);
}
Robert Ricci's avatar
Robert Ricci committed
103

104
105
106
107
108
109
110
#
# Drop root permissions, if we have them
#
if ($EUID == 0) {
	$EUID = $UID;
}

Robert Ricci's avatar
Robert Ricci committed
111
112
113
114
115
116
# Set up a signal handler that will clean up in case we get killed
$SIG{HUP} = $SIG{INT} = $SIG{TERM} = \&cleanup;
# XXX: Any others we should catch?

# Now, we actually launch Frisbee
while (1) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
117
118
119
120
121
122
123
124
125
126
127
        #
        # Each time the server exits, test the busy bit to see if
	# it should keep going. This has to be done with tables locked
	# since another caller is going to bump it.
	#
	&lock_tables();
	if (! &testbusy($imageid)) {
		last;
	}
	&unlock_tables();
	
Robert Ricci's avatar
Robert Ricci committed
128
129
	if ($child_pid = fork()) {
		# Wait for child to exit
Leigh B. Stoller's avatar
Leigh B. Stoller committed
130
131
132
		waitpid($child_pid, 0);

		if ($?) {
133
			SENDMAIL($TBOPS, "Frisbeed Failed!",
Leigh B. Stoller's avatar
Leigh B. Stoller committed
134
135
136
137
138
139
140
141
142
143
144
				 "Imageid: $imageid\n".
				 "Address: $address\n\n".
				 "Process $child_pid exited with value $?.\n".
				 "Please look at the syslog for frisbeed!\n\n".
				 "NOTE: Another frisbeed will not start!\n");
			#
			# Dump early. This will leave the address in
			# in the DB, so that another one will not start
			# until the matter is resolved by someone. 
			# 
			exit(1);
Robert Ricci's avatar
Robert Ricci committed
145
		}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
146
147
	}
	else {
Robert Ricci's avatar
Robert Ricci committed
148
		# Child branch
Leigh B. Stoller's avatar
Leigh B. Stoller committed
149
150
151
152
153
154
155
156
157
158
		# The database format for address is host:port - however,
		# we need to give them as seperate arguments to frisbeed.

		if ($address =~ /(.*):(.*)/) {
			my $addr = $1;
			my $port = $2;

			if (!exec("$FRISBEED -m $addr -p $port $filename")) {
				die("$$: Unable to exec $FRISBEED\n");
			}
Robert Ricci's avatar
Robert Ricci committed
159
		}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
160
		die("$$: Bad address format: $address.\n");
Robert Ricci's avatar
Robert Ricci committed
161
162
163
164
	}
}

&clear_address;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
165
&unlock_tables();
Robert Ricci's avatar
Robert Ricci committed
166
167
168
169
170
171
172
173
exit(0);

######################################################################
# Subroutines
######################################################################

# Print out a usage mesage
sub usage {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
174
	print "Usage: $0 [-d] IMAGEID\n";
Robert Ricci's avatar
Robert Ricci committed
175
176
177
178
179
180
181
182
183
184
185
186
187
	print "-d:	Print debugging output\n";
}

# Only print if -d option was given. Also add $$ on the beginning of the
# string, to ease debugging
sub debug {
	if ($opt{d}) { print "$$: ", @_ };
}

# Grab the address for the passed-in imageid
sub get_address {
	my ($imageid) = @_;

Leigh B. Stoller's avatar
Leigh B. Stoller committed
188
189
190
	my $sth =
	    DBQueryFatal("SELECT imageid,load_address ".
			 "FROM images WHERE imageid='$imageid'");
Robert Ricci's avatar
Robert Ricci committed
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245

	my @row = $sth->fetchrow;
	if (!@row) {
		die "No such imageid: $imageid\n";
	}

	return $row[1];
}

# Grab the filename for the passed-in imageid
sub get_filename {
	my ($imageid) = @_;
	my $image_query = "SELECT path FROM images WHERE " .
		"imageid='$imageid'";

	my $sth = DBQueryFatal($image_query);

	my @row = $sth->fetchrow;
	if (!@row) {
		die "No such imageid: $imageid\n";
	}

	return $row[0];
}


# Lock the tables used in this script - waits indefinitely until it
# succeeds
sub lock_tables {
	while (1) {
		&debug("Locking tables\n");
		my $sth = DBQuery("LOCK TABLES images WRITE"); 
		if (!$sth) {
			print "DB Error locking tables. Waiting a bit ...\n";
			sleep(10);
		} else {
			last;
		}
	}
}

# Unlock the tables used in this script
sub unlock_tables {
	&debug("Unlocking tables\n");
	DBQueryFatal("UNLOCK TABLES"); 
}

# Pick out an address to use
sub pick_address {
	my $address_query = "SELECT load_address FROM images WHERE " .
		"load_address IS NOT NULL and load_address != ''";
	my $sth = DBQueryFatal($address_query);

	my %used_addrs = (); # Loading addresses already taken
	while (@row = $sth->fetchrow) {
246
247
248
249
		$row[0] =~ /^$BASEADDR\.(\d+):(\d+)$/;
		# $1 is the address, $2 the port number
		if ($1 && $2) {
			$used_addrs{$1} = $2;
Robert Ricci's avatar
Robert Ricci committed
250
251
252
253
254
255
		}
	}

	my $address;
	for (my $i = 1; $i < 255; $i++) {
		if (!$used_addrs{$i}) {
256
257
			my $port = $BASEPORT + ($i - 1);
			$address = "${BASEADDR}.${i}:${port}";
Robert Ricci's avatar
Robert Ricci committed
258
259
260
261
262
263
264
265
266
267
			last;
		}
	}

	return $address;
}

# Pass in an imageid, and an address
sub set_address {
	my ($imageid,$address) = @_;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300

	DBQueryFatal("UPDATE images SET load_address='$address',load_busy=1 " .
		     "WHERE imageid='$imageid'");
}

# Bump the busy indicator to keep the frisbeed going.
sub keepbusy($imageid) {
	my ($imageid) = @_;

	DBQueryFatal("UPDATE images SET load_busy=GREATEST(load_busy,1) " .
		     "WHERE imageid='$imageid'");

	return 1;
}

# Test the busy indicator, and set to zero.
sub testbusy($imageid) {
	my ($imageid) = @_;

	my $query_result =
	    DBQueryFatal("select load_busy from images ".
			 "WHERE imageid='$imageid'");

	my @row = $query_result->fetchrow;
	if (!@row) {
	        return 0;
	}
	
	if ($row[0]) {
	    DBQueryFatal("UPDATE images SET load_busy=0 ".
			 "WHERE imageid='$imageid'");
	}
	return $row[0];
Robert Ricci's avatar
Robert Ricci committed
301
302
303
304
305
306
307
308
309
310
311
312
313
}

# Kill off our child process, if started, and clear out registered address
# Also, die off
sub cleanup {
	print STDERR "$$: Killed, cleaning up\n";
	if ($child_pid) {
		kill 15, $child_pid;
	}
	&clear_address;
	exit(1);
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
314
# Clear out the address (and pid) registered to this process
Robert Ricci's avatar
Robert Ricci committed
315
316
317
sub clear_address {
	&debug("Clearing out registered load_address\n");
	# Now, clear out the load_address we had set up
Leigh B. Stoller's avatar
Leigh B. Stoller committed
318
	my $address_clear = "UPDATE images SET load_address='',load_busy=0 " .
Robert Ricci's avatar
Robert Ricci committed
319
320
321
		"WHERE imageid='$imageid'";
	DBQueryFatal($address_clear);
}