genilib-jail.in 17.1 KB
Newer Older
1
2
3
#!/usr/bin/perl -w

#
4
# Copyright (c) 2015-2017 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
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 
# {{{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/>.
# 
# }}}
#
use strict;
use English;
use Getopt::Std;
use BSD::Resource;
use POSIX qw(:signal_h);
use File::Basename;
    
#
# Fire up an instance of a FreeBSD jail to run a geni-lib script.
# Since we must be invoked as root, the caller is responsible for all
# permission checks.
#
# This is the simple roll-our-own version. We take advantage of a template
# jail filesystem setup by iocage, but don't use iocage for anything else
# as it is toooo slooooow. We clone this FS per-call to execute the geni-lib
# script and setup a basic jail with no daemons, no network, etc.
#
# XXX This version does not support any resource limits other that a quota
# on disk use (ZFS property) and a per-command time limit (jail config).
#
# XXX Note that we don't use the passed-in user. We just run the script as
# "nobody" in the jail.
#

my $USEJAILRUN = 0;

my $CMD_TIMEOUT = 600;
my $CMD_QUOTA	= "2G";

Mike Hibler's avatar
Mike Hibler committed
54
55
my $PROG = $0;
my $INTERP = "nice -15 /usr/local/bin/python";
56
57
58
59
60
61
62
63
64
65
66
67
68
#my $INTERP = "/bin/sh";

# this is a ZFS snapshot that we make of the base FS
my $JAILROOT = "/iocage/jails";
my $ZFSBASE =  "z/iocage/jails";

my $zfsattrs = "-o compression=off -o quota=$CMD_QUOTA";

my $starttime = time();

sub usage()
{
    print STDOUT
Mike Hibler's avatar
Mike Hibler committed
69
70
71
72
73
74
75
76
77
	"Usage: $PROG [-d] [-n jailname] [-u user] [-p paramfile] [-o outfile] script\n".
	"   or: $PROG [-CR] [-n jailname]\n".
        "Execute the given geni-lib script in a jail or just create/remove a jail\n".
	"Options:\n".
	"  -u user      User to run script as.\n".
	"  -p paramfile JSON params to pass to script.\n".
	"  -o outfile   File in which to place script results.\n".
	"  -n jailname  Name of jail; default is 'py-cage-<pid>'.\n".
	"  -d           Turn on debugging.\n".
78
79
	"  -C           Just create the jail; use 'jexec' to run commands\n".
	"               (use -N too or it will die after CMD_TIMEOUT seconds).\n".
80
	"  -R           Remove an existing (left-over) jail; must specify a name (-n)\n".
81
	"  -N           Do not apply any resource limits.\n".
82
	"  -B base      Which base to use (see /iocage/tags); default: 'py-cage'.\n";
83
84
85
 
    exit(-1);
}
86
my $optlist = "du:p:o:n:CRB:N";
87
my $basename = "py-cage";
88
89
90
91
92
93
94
my $jailname = "py-cage-$$";
my $user = "nobody";
my $uid;
my $gid;
my $pfile;
my $ofile;
my $ifile;
95
96
my $limits = 1;
my $haverctl = 0;
97

Mike Hibler's avatar
Mike Hibler committed
98
99
100
# action: 1: create, 2: destroy, 4: run script
my $action = 4;

101
sub start_jail($$);
102
sub limit_jail($$);
103
104
sub run_jail($$$$);
sub msg(@);
105
sub mysystem($);
106
107
108
109

#
# Configure variables
#
Mike Hibler's avatar
Mike Hibler committed
110
111
my $TBROOT   = "@prefix@";
my $GENILIB  = "$TBROOT/lib/geni-lib/";
112
113
114
115
116
117
118
119
120
121
122
123
my $debug    = 0;

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

#
# Untaint the path
# 
$ENV{'PATH'} = "/bin:/usr/bin:/sbin:/usr/sbin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
Mike Hibler's avatar
Mike Hibler committed
124
125
126
127

# Where geni-lib should be mounted in the jail
my $GENILIB_MNT = "/usr/testbed/lib/geni-lib/";
$ENV{"PYTHONPATH"} = $GENILIB_MNT;
128
129
130
131
132
133
134
135
136

#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libtestbed;

my $jailtag;
my $jailstate = 0;
Mike Hibler's avatar
Mike Hibler committed
137
138
my $jailuuid;
my $snapshot;
139
140

END {
Mike Hibler's avatar
Mike Hibler committed
141
142
    if ($jailstate) {
	my $ecode = $?;
143

144
	if ($debug && $ecode) {
Mike Hibler's avatar
Mike Hibler committed
145
	    print STDERR "Unexpected exit, cleaning up...\n";
146
	}
Mike Hibler's avatar
Mike Hibler committed
147
148
149
	if ($action != 1 || $ecode) {
	    if ($jailstate == 2) {
		msg("Stopping jail");
150
		if (mysystem("jail -qr $jailtag")) {
Mike Hibler's avatar
Mike Hibler committed
151
152
		    print STDERR "*** could not stop jail $jailtag\n";
		}
Mike Hibler's avatar
Mike Hibler committed
153
		if ($limits && $haverctl) {
154
155
		    limit_jail($jailtag, 0);
		}
Mike Hibler's avatar
Mike Hibler committed
156
157
158
159
160
161
162
		msg("Stopping done");
		$jailstate = 1;
	    }
	    if ($jailstate == 1) {
		msg("Destroying jail FS");

		# XXX make sure special FSes get unmounted 
163
164
165
		mysystem("umount $JAILROOT/$jailtag/dev/fd >/dev/null 2>&1");
		mysystem("umount $JAILROOT/$jailtag/dev >/dev/null 2>&1");
		mysystem("umount $JAILROOT/$jailtag$GENILIB_MNT >/dev/null 2>&1");
Mike Hibler's avatar
Mike Hibler committed
166

167
		if (mysystem("zfs destroy -f $ZFSBASE/$jailtag")) {
Mike Hibler's avatar
Mike Hibler committed
168
169
170
		    print STDERR
			"*** could not destroy jail FS for $jailtag\n";
		}
171
		if ($snapshot && mysystem("zfs destroy -f $snapshot")) {
Mike Hibler's avatar
Mike Hibler committed
172
173
174
175
176
		    print STDERR
			"*** could not destroy snapshot $snapshot\n";
		}
		msg("Destroying done");
		$jailstate = 0;
177
178
	    }
	}
Mike Hibler's avatar
Mike Hibler committed
179
	$? = $ecode;
180
181
182
183
184
185
186
187
188
189
190
    }
}

#
# Parse command arguments. Once we return from getopts, all that should
# left are the required arguments.
#
my %options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
Mike Hibler's avatar
Mike Hibler committed
191
192
193
194
195
196
if (defined($options{"C"})) {
    $action = 1;
}
if (defined($options{"R"})) {
    $action = 2;
}
197
198
199
if (defined($options{"N"})) {
    $limits = 0;
}
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
if (defined($options{"d"})) {
    $debug = 1;
}
if (defined($options{"p"})) {
    $pfile = $options{"p"};
}
if (defined($options{"o"})) {
    $ofile = $options{"o"};
}
if (defined($options{"u"})) {
    $user = $options{"u"};
}
if (defined($options{"n"})) {
    $jailname = $options{"n"};
    if ($jailname !~ /^[-\w]+$/) {
	print STDERR "Come on, keep the name simple...\n";
	usage();
    }
}
219
220
221
222
223
224
225
226
if (defined($options{"B"})) {
    my $base = $options{"B"};
    if (! -e "/iocage/tags/$base") {
	print STDERR "No such base '$base'\n";
	usage();
    }
    $basename = $base;
}
227

Mike Hibler's avatar
Mike Hibler committed
228
229
230
231
232
233
#
# Extract params from the environment (if invoked via rungenilib.proxy).
#
if (!$ofile && exists($ENV{'GENILIB_PORTAL_REQUEST_PATH'})) {
    $ofile = $ENV{'GENILIB_PORTAL_REQUEST_PATH'};
}
234
235
236
237
238
239
240
if (!$pfile) {
    if (exists($ENV{'GENILIB_PORTAL_PARAMS_PATH'})) {
	$pfile = $ENV{'GENILIB_PORTAL_PARAMS_PATH'};
    }
    elsif (exists($ENV{'GENILIB_PORTAL_DUMPPARAMS_PATH'})) {
	$pfile = $ENV{'GENILIB_PORTAL_DUMPPARAMS_PATH'};
    }
241
242
}

Mike Hibler's avatar
Mike Hibler committed
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
if ($action == 4) {
    if (@ARGV < 1) {
	print STDERR "Must specify a script\n";
	usage();
    }
    $ifile = $ARGV[0];

    if (!$ofile) {
	print STDERR "Must specify an output file (-o)\n";
	usage();
    }
} else {
    if (@ARGV != 0) {
	print STDERR "Too many args for create/destroy\n";
	usage();
    }
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
}

# Must be a legit user
(undef,undef,$uid,$gid) = getpwnam($user) or
    die("*** $0:\n".
	"    Invalid user '$user'!");

# Must run as root
if ($UID != 0) {
    die("*** $0:\n".
	"    Must be root to run this script!");
}

# Make sure jail infrastructure is there
if (! -d "$JAILROOT") {
    die("*** $0:\n".
	"    $JAILROOT does not exist; is iocage installed?");
}

Mike Hibler's avatar
Mike Hibler committed
278
279
280
281
282
283
284
285
286
287
288
289
290
291
#
# Action checks.
# If only creating (-C) or running normally, then jail should not exist.
# If only removing (-R), then jail should exist.
#
if ($action != 2 && -e "$JAILROOT/$jailname") {
    die("*** $0:\n".
	"    $jailname already exists");
}

#
# XXX figure out the appropriate snapshot.
# This is marginally better than hardwiring a UUID.
#
292
my $path = readlink("/iocage/tags/$basename");
Mike Hibler's avatar
Mike Hibler committed
293
294
295
296
297
298
if (!$path || $path !~ /(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})$/) {
    die("*** $0:\n".
	"    Cannot find UUID for base FS");
}
$jailuuid = $1;

299
300
301
302
303
#
# See if we have RCTL based limits
#
if ($limits) {
    my $enabled = `sysctl -in kern.racct.enable 2>/dev/null`;
Mike Hibler's avatar
Mike Hibler committed
304
    if ($? || $enabled == 0) {
305
306
307
308
309
310
	$haverctl = 0;
    } else {
	$haverctl = 1;
    }
}

Mike Hibler's avatar
Mike Hibler committed
311
312
313
314
315
316
317
318
319
320
321
322
323
324
#
# Removing an existing jail, just set the jailstate and exit.
#
if ($action == 2) {
    if (! -e "$JAILROOT/$jailname") {
	die("*** $0:\n".
	    "    $jailname does not exist");
    }
    $jailtag = $jailname;
    $jailstate = 2;
    $snapshot = "$ZFSBASE/$jailuuid/root\@$jailname";
    exit(0);
}

325
326
327
#
# Create a filesystem
#
Mike Hibler's avatar
Mike Hibler committed
328
msg("Snapshotting base FS");
329
if (mysystem("zfs snapshot $ZFSBASE/$jailuuid/root\@$jailname")) {
Mike Hibler's avatar
Mike Hibler committed
330
    print STDERR "Could not create geni-lib jail snapshot\n";
331
    exit(-1);
Mike Hibler's avatar
Mike Hibler committed
332
333
334
}
msg("Snapshotting done");
$snapshot = "$ZFSBASE/$jailuuid/root\@$jailname";
335
msg("Cloning $basename FS");
336
if (mysystem("zfs clone $zfsattrs $snapshot $ZFSBASE/$jailname")) {
337
    print STDERR "Could not create geni-lib jail FS\n";
338
    exit(-1);
339
340
341
342
343
344
}
msg("Cloning done");
$jailstate = 1;
$jailtag = $jailname;
my $jailrootdir = "$JAILROOT/$jailtag";

Mike Hibler's avatar
Mike Hibler committed
345
346
347
348
349
#
# XXX mount host geni-lib in jail
# This way we don't have to copy in the geni-lib files and they
# will always be up-to-date. Also make dev trees work.
#
350
351
352
353
354
# Note that we avoid gratuitous mountd HUP'ing by invoking mount_nullfs
# directly instead of using mount. Alas, the jail command still does a
# couple of "mount" commands, so we are still HUP happy.
#
if (mysystem("mount_nullfs -o ro $GENILIB $jailrootdir$GENILIB_MNT")) {
Mike Hibler's avatar
Mike Hibler committed
355
    print STDERR "Could not mount $GENILIB in $jailtag\n";
356
    exit(-1);
Mike Hibler's avatar
Mike Hibler committed
357
358
}

359
360
361
362
363
#
# Drop our files into the jail FS
# We create a protected directory owned by the user so that people
# cannot snoop from outside the jail.
#
Mike Hibler's avatar
Mike Hibler committed
364
365
366
my ($j_ifile,$j_ofile,$j_pfile);
if ($action != 1) {
    my $tempdir = "/tmp/genilib/";
367
    if (!mkdir("$jailrootdir$tempdir", 0700)) {
Mike Hibler's avatar
Mike Hibler committed
368
	print STDERR "Could not create geni-lib jail tempdir\n";
369
	exit(-1);
Mike Hibler's avatar
Mike Hibler committed
370
    }
371

Mike Hibler's avatar
Mike Hibler committed
372
373
374
375
    $j_ifile = $tempdir . basename($ifile);
    $j_ofile = $tempdir . basename($ofile);
    $j_pfile = $tempdir . basename($pfile)
	if ($pfile);
376

Mike Hibler's avatar
Mike Hibler committed
377
    msg("Stashing files");
378
    if (mysystem("cp -p $ifile $jailrootdir$j_ifile") ||
379
380
	($pfile && -e $pfile &&
	 mysystem("cp -p $pfile $jailrootdir$j_pfile")) ||
381
	mysystem("chown -R $uid:$gid $jailrootdir$tempdir")) {
Mike Hibler's avatar
Mike Hibler committed
382
	print STDERR "Could not populate jail\n";
383
	exit(-1);
Mike Hibler's avatar
Mike Hibler committed
384
    }
385

Mike Hibler's avatar
Mike Hibler committed
386
387
388
    #
    # XXX adjust the environment for the portal module to reflect the jail.
    #
389
390
    $ENV{'GENILIB_PORTAL_REQUEST_PATH'} = $j_ofile;
    if (exists($ENV{'GENILIB_PORTAL_DUMPPARAMS_PATH'})) {
391
	$ENV{'GENILIB_PORTAL_DUMPPARAMS_PATH'} = $j_pfile;
392
    }
393
    elsif (exists($ENV{'GENILIB_PORTAL_PARAMS_PATH'})) {
394
395
	$ENV{'GENILIB_PORTAL_PARAMS_PATH'} = $j_pfile;
    }
Mike Hibler's avatar
Mike Hibler committed
396
397
398
399
400
401
402
403
404
405
406
407
408

    #
    # Make other aspects of the environment appear a little bit sane.
    # XXX okay, not really THAT sane since the user will not exist in the jail.
    #
    $ENV{'SHELL'} = "/bin/sh";
    $ENV{'USER'} = $user;
    $ENV{'USERNAME'} = $user;
    $ENV{'LOGNAME'} = $user;
    $ENV{'HOME'} = $tempdir;
    delete $ENV{'DISPLAY'};
    delete @ENV{'SUDO_UID', 'SUDO_GID', 'SUDO_USER', 'SUDO_COMMAND'};
    delete @ENV{'SSH_AUTH_SOCK', 'SSH_CLIENT', 'SSH_CONNECTION', 'SSH_TTY'};
409
410
}

411
412
413
414
415
416
417
418
#
# Set rctl resource limits
#
if ($limits && $haverctl && limit_jail($jailtag, 1)) {
    print STDERR "WARNING: could not set rctl limits on geni-lib jail $jailtag\n";
    $haverctl = 0;
}

419
420
421
422
#
# Fire up the jail
#
my $status = -1;
Mike Hibler's avatar
Mike Hibler committed
423
if ($USEJAILRUN && $action != 1) {
424
425
426
427
428
429
430
    msg("Run jail");
    $status = run_jail($jailtag, $jailrootdir, $user, "$INTERP $j_ifile");
    msg("Run done");
} else {
    msg("Start jail");
    if (start_jail($jailtag, $jailrootdir)) {
	print STDERR "Could not start geni-lib jail $jailtag\n";
431
	exit(-1);
432
433
434
435
436
437
438
439
440
441
442
443
    }
    msg("Starting done");
    $jailstate = 2;

    #
    # And execute the command as the indicated jail user.
    #
    # XXX currently we run the command as the real user (-u), but note that
    # they will have no passwd entry inside the jail. May cause problems.
    # If so, we can run as nobody in the jail (-U nobody) or add the real
    # user to the jail (but that is a lot of work for one command...)
    #
Mike Hibler's avatar
Mike Hibler committed
444
445
    if ($action != 1) {
	msg("Execing command");
446
	$status = mysystem("jexec -u $user $jailtag $INTERP $j_ifile");
Mike Hibler's avatar
Mike Hibler committed
447
448
449
450
	msg("Execing done");
    } else {
	$status = 0;
    }
451
452
453
454
455
456
457
458
}

if ($status) {
    if ($status == -1) {
	print STDERR "Could not run jail $jailtag\n";
    } elsif ($status & 127) {
	$status &= 127;
	print STDERR "Jail $jailtag execution died with signal $status\n";
459
	$status = -2;
460
461
    } else {
	$status >>= 8;
462
463
464
	if ($status <= 100 || $debug) {
	    print STDERR "Jail $jailtag execution failed with exit code $status\n";
	}
465
    }
Mike Hibler's avatar
Mike Hibler committed
466
467
468
469
470
471
472

    # XXX odd semantics: if debug is set, don't remove jail on error
    if ($debug) {
	print STDERR "WARNING: not destroying jail, you will need to do it:\n".
	    "    sudo $PROG -R -n $jailtag\n";
	$jailstate = 0;
    }
473
474
}

Mike Hibler's avatar
Mike Hibler committed
475
if ($action != 1) {
476
477
478
479
480
481
482
483
484
485
    #
    # Oh the joys of running as root. Now we need to take away user
    # permission from the jail directory (recall the user can access
    # it from outside) and then verify that the source file isn't a
    # symlink (a cheap-o realpath check). Our caller is responsible
    # for defending the target file.
    #
    my $tempdir = "/tmp/genilib";
    if (-l "$jailrootdir$tempdir" ||
	chown(0, -1, "$jailrootdir$tempdir") != 1) {
Mike Hibler's avatar
Mike Hibler committed
486
	print STDERR "Could not copy back results of command\n";
487
	exit(-1);
Mike Hibler's avatar
Mike Hibler committed
488
    }
489
490
    if (-e "$jailrootdir$j_ofile") {
	if (-l "$jailrootdir$j_ofile" ||
491
	    mysystem("cp $jailrootdir$j_ofile $ofile")) {
492
	    print STDERR "Could not copy back results of command\n";
493
	    exit(-1);
494
495
	}
    }
496
497
498
499
500
501
502
503
    if (exists($ENV{'GENILIB_PORTAL_DUMPPARAMS_PATH'}) &&
	-e "$jailrootdir$j_pfile") {
	if (-l "$jailrootdir$j_pfile" ||
	    mysystem("cp $jailrootdir$j_pfile $pfile")) {
	    print STDERR "Could not copy back parameter block\n";
	    exit(-1);
	}
    }
Mike Hibler's avatar
Mike Hibler committed
504
505
} else {
    print STDERR "Jail '$jailtag' running. Root FS at '$jailrootdir'.\n";
506
507
}

Mike Hibler's avatar
Mike Hibler committed
508
exit($status);
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530

sub start_jail($$)
{
    my ($name,$fs) = @_;

    my $args = "";

    # identity (need host.hostuuid?)
    $args .= "name=$name host.hostname=$name.emulab.net path=$fs ";

    # security
    $args .= "securelevel=2 devfs_ruleset=4 enforce_statfs=2 children.max=0 ";
    $args .= "allow.set_hostname=0 allow.sysvipc=0 allow.raw_sockets=0 ";
    $args .= "allow.chflags=0 allow.mount=0 allow.mount.devfs=0 ";
    $args .= "allow.mount.nullfs=0 allow.mount.procfs=0 allow.mount.tmpfs=0 ";
    $args .= "allow.mount.zfs=0 allow.quotas=0 allow.socket_af=0 ";
    $args .= "mount.devfs=1 mount.fdescfs=1 ";

    # no networking
    $args .= "ip4=disable ip6=disable ";

    # execution params
531
    my $timo = (($limits && !$haverctl) ? $CMD_TIMEOUT : 0);
532
533
534
    $args .= "exec.prestart=/usr/bin/true exec.poststart=/usr/bin/true ";
    $args .= "exec.prestop=/usr/bin/true exec.poststop=/usr/bin/true ";
    $args .= "exec.start='/bin/sh /etc/rc' exec.stop='/bin/sh /etc/rc.shutdown' ";
Mike Hibler's avatar
Mike Hibler committed
535
    $args .= "exec.clean=0 exec.timeout=$timo stop.timeout=30 ";
536
537
538
539

    # other stuff
    $args .= "allow.dying persist";

540
    return mysystem("jail -qc $args >/dev/null 2>&1");
541
542
}

543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
#
# Set/clear kernel resource limits on a running jail.
# Note that the kernel has to enable RACCT by setting kern.racct.enable=1 in
# /boot/loader.conf.
#
sub limit_jail($$)
{
    my ($name,$setem) = @_;
    
    if (!$setem) {
	if (mysystem("rctl -r jail:$name")) {
	    return 1;
	}
	return 0;
    }
    
Mike Hibler's avatar
Mike Hibler committed
559
560
561
562
563
564
565
    #
    # XXX buncha hard wired checks here.
    # * no more than 100 processes in the jail
    # * no more than 200 threads in the jail
    # * no more than 200 open files in the jail
    # * no more than 1GB of VM
    #
566
    my @rules = ();
Mike Hibler's avatar
Mike Hibler committed
567
568
569
570
571
572
573
574
575
576
577
578
579
580
    if (1) {
	# XXX apparently cannot log til after fork is successful,
	#     so log one before actually failing
	push @rules, "jail:$name:maxproc:log=99/jail";
	push @rules, "jail:$name:maxproc:deny=100/jail";
	push @rules, "jail:$name:nthr:log=200/jail";
	push @rules, "jail:$name:nthr:deny=200/jail";
	push @rules, "jail:$name:openfiles:log=200/jail";
	push @rules, "jail:$name:openfiles:deny=200/jail";
	push @rules, "jail:$name:vmemoryuse:log=1g/jail";
	push @rules, "jail:$name:vmemoryuse:deny=1g/jail";
    }

    # Don't leave lingering jails
581
    my $timo = $CMD_TIMEOUT;
Mike Hibler's avatar
Mike Hibler committed
582
    push @rules, "jail:$name:wallclock:log=$timo/jail";
583
584
585
586
587
588
589
590
591
592
593
594
595
    push @rules, "jail:$name:wallclock:sigterm=$timo/jail";
    $timo += 10;
    push @rules, "jail:$name:wallclock:sigkill=$timo/jail";

    foreach my $rule (@rules) {
	if (mysystem("rctl -a '$rule'")) {
	    mysystem("rctl -r jail:$name");
	    return 1;
	}
    }
    return 0;
}

596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
sub run_jail($$$$)
{
    my ($name,$fs,$user,$cmdstr) = @_;

    my $args = "";

    # identity (need host.hostuuid?)
    $args .= "name=$name host.hostname=$name.emulab.net path=$fs ";

    # security
    $args .= "securelevel=2 devfs_ruleset=4 enforce_statfs=2 children.max=0 ";
    $args .= "allow.set_hostname=0 allow.sysvipc=0 allow.raw_sockets=0 ";
    $args .= "allow.chflags=0 allow.mount=0 allow.mount.devfs=0 ";
    $args .= "allow.mount.nullfs=0 allow.mount.procfs=0 allow.mount.tmpfs=0 ";
    $args .= "allow.mount.zfs=0 allow.quotas=0 allow.socket_af=0 ";
    $args .= "mount.devfs=1 mount.fdescfs=1 ";

    # no networking
    $args .= "ip4=disable ip6=disable ";

    # execution params
617
    my $timo = (($limits && !$haverctl) ? $CMD_TIMEOUT : 0);
618
619
620
    $args .= "exec.prestart=/usr/bin/true exec.poststart=/usr/bin/true ";
    $args .= "exec.prestop=/usr/bin/true exec.poststop=/usr/bin/true ";
    $args .= "exec.start='$cmdstr' exec.stop='/usr/bin/true' ";
621
    $args .= "exec.clean=0 exec.timeout=$timo stop.timeout=30 ";
622
623
624
625
626
    $args .= "exec.jail_user=$user ";

    # other stuff
    $args .= "allow.dying";

627
    return mysystem("jail -qc $args >/dev/null 2>&1");
628
629
630
631
}

sub msg(@)
{
Mike Hibler's avatar
Mike Hibler committed
632
633
634
635
636
    if ($debug) {
	my $stamp = time() - $starttime;
	printf STDERR "[%3d] ", $stamp;
	print STDERR @_, "\n";
    }
637
}
638
639
640
641
642
643
644
645
646
647

sub mysystem($)
{
    my $cmd = shift;
    
    if (0) {
	print STDERR "Doing: '$cmd'\n";
    }
    return system($cmd);
}