liblocsetup.pm 21.9 KB
Newer Older
1
2
3
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
Mike Hibler's avatar
Mike Hibler committed
4
# Copyright (c) 2000-2005 University of Utah and the Flux Group.
5
6
7
8
# All rights reserved.
#

#
9
# CygWin specific routines and constants for the client bootime setup stuff.
10
11
12
13
14
#
package liblocsetup;
use Exporter;
@ISA = "Exporter";
@EXPORT =
15
    qw ( $CP $LN $RM $MV $TOUCH $EGREP $CHOWN $CHMOD $MOUNT $UMOUNT
Mike Hibler's avatar
Mike Hibler committed
16
	 $NTS $NET $HOSTSFILE
17
	 $TMPASSWD $SFSSD $SFSCD $RPMCMD
18
	 os_account_cleanup os_accounts_start os_accounts_end os_accounts_sync
19
20
	 os_ifconfig_line os_etchosts_line
	 os_setup os_groupadd os_groupgid os_useradd os_userdel os_usermod os_mkdir
21
22
23
	 os_ifconfig_veth
	 os_routing_enable_forward os_routing_enable_gated
	 os_routing_add_manual os_routing_del_manual os_homedirdel
24
25
	 os_groupdel os_samba_mount 
	 os_getnfsmounts os_getnfsmountpoints os_noisycmd
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
	 os_fwconfig_line os_fwrouteconfig_line
       );

# Must come after package declaration!
use English;

# Load up the paths. Its conditionalized to be compatabile with older images.
# Note this file has probably already been loaded by the caller.
BEGIN
{
    if (-e "/etc/emulab/paths.pm") {
	require "/etc/emulab/paths.pm";
	import emulabpaths;
    }
    else {
	my $ETCDIR  = "/etc/rc.d/testbed";
	my $BINDIR  = "/etc/rc.d/testbed";
	my $VARDIR  = "/etc/rc.d/testbed";
	my $BOOTDIR = "/etc/rc.d/testbed";
    }
}

48
49
use librc;

50
#
51
# Various programs and things specific to CygWin on XP and that we want to export.
52
# 
Mike Hibler's avatar
Mike Hibler committed
53
54
$CP		= "/bin/cp";
$LN		= "/bin/ln";
55
$RM		= "/bin/rm";
56
57
58
$MV		= "/bin/mv";
$TOUCH		= "/bin/touch";
$EGREP		= "/bin/egrep -q";
59
$CHOWN		= "/bin/chown";
Mike Hibler's avatar
Mike Hibler committed
60
$CHMOD		= "/bin/chmod";
61
$MOUNT		= "/bin/mount";
62
$UMOUNT		= "/bin/umount";
63

64
# Cygwin.
65
66
67
$MKPASSWD	= "/bin/mkpasswd";
$MKGROUP	= "/bin/mkgroup";
$AWK		= "/bin/gawk";
68
$BASH		= "/bin/bash";
69

70
# Windows under Cygwin.
71
72
$NTS		= "/cygdrive/c/WINDOWS/system32";
$NET		= "$NTS/net";
73
$NETSH		= "$NTS/netsh";
74
$IPCONFIG	= "$NTS/ipconfig";
75
$NTE		= "$NTS/drivers/etc";
Mike Hibler's avatar
Mike Hibler committed
76

77
78
$HOSTSFILE	= "$NTE/hosts";
#$HOSTSFILE	= "/etc/hosts";
79

80
81
82
#
# These are not exported
#
83
84
my $ADDUSERS	= "$BINDIR/addusers.exe";
my $DEVCON	= "$BINDIR/devcon.exe";
85
my $IFCONFIGBIN = "$NETSH interface ip set address";
86
87
my $IFCONFIG	= "$IFCONFIGBIN name=\"%s\" source=static addr=%s mask=%s";
my $IFC_1000MBS = "1000baseTx";
88
89
90
91
92
93
my $IFC_100MBS  = "100baseTx";
my $IFC_10MBS   = "10baseT";
my $IFC_FDUPLEX = "FD";
my $IFC_HDUPLEX = "HD";
my @LOCKFILES   = ("/etc/group.lock", "/etc/gshadow.lock");
my $MKDIR	= "/bin/mkdir";
94
my $RMDIR	= "/bin/rmdir";
95
my $GATED	= "/usr/sbin/gated";
Mike Hibler's avatar
Mike Hibler committed
96
my $ROUTE	= "$NTS/route";
97
98
my $SHELLS	= "/etc/shells";
my $DEFSHELL	= "/bin/tcsh";
99
100
my $winusersfile = "$BOOTDIR/winusers";
my $usershellsfile = "$BOOTDIR/usershells";
101
my $XIMAP	= "$BOOTDIR/xif_map";
102

Russ Fish's avatar
Russ Fish committed
103
104
105
106
107
108
109
110
111
112
113
#
# system() with error checking.
#
sub mysystem($)
{
    my ($cmd) = @_;
    if (system($cmd) != 0) {
	warning("Failed: ($cmd), $!\n");
    }
}

114
115
116
117
#
# OS dependent part of cleanup node state.
# 
sub os_account_cleanup()
118
119
120
{
    # Undo what rc.mounts and rc.accounts did.  

121
122
123
124
125
126
127
    # Get the users list from NT, dumped into /etc/passwd and preened by the
    # os_accounts_sync function.  Root and internal admin accounts should have
    # homedirs under /home, while users are under /users.
    my ($pwd_line, $name);
    if (open(PWDHANDLE, "/etc/passwd")) {
	while ($pwdline = readline(PWDHANDLE)) {
	    if ($pwdline !~ m|:/users/|) {
128
129
		next;
	    }
130
	    $name = substr($pwdline, 0, index($pwdline, ":"));
131
132
133
	    print "Removing user: $name\n";

	    # There is always an NT account.
Russ Fish's avatar
Russ Fish committed
134
	    mysystem("$NET user $name /delete >& /dev/null");
135
136

	    # There will only be an NT homedir if the user has logged in sometime.
137
138
139
140
141
142
143
	    my $das = "C:/'Documents and Settings'";
	    if ( -d "$das/$name" ) {
		print "Removing directory: $das/$name\n";
		system("$CHMOD -Rf 777 $das/$name >& /dev/null");
		system("$CHOWN -Rf root $das/$name >& /dev/null");
		system("$RM -rf $das/$name "); # Show errors.
	    }
Russ Fish's avatar
Russ Fish committed
144
	    # It sometimes also makes user.PCnnn, user.PCnnn.000, etc.
145
146
147
148
149
150
	    if ( `ls -d $das/$name.* 2>/dev/null` ) {
		print "Removing directories: $das/$name.*\n";
		system("$CHMOD -Rf 777 $das/$name.* >& /dev/null");
		system("$CHOWN -Rf root $das/$name.* >& /dev/null");
		system("$RM -rf $das/$name.*"); # Show errors.
	    }
151

152
153
154
155
	    # Unmount the homedir so we can get to the mount point and remove it.
	    system("$UMOUNT /users/$name >& /dev/null");
	    system("$RMDIR /users/$name")
		if ( -d "/users/$name" );
156
	}
157
	close(PWDHANDLE);
158
159
160
161
162

	# Make the CygWin /etc/passwd and /etc/group files match Windows.
	os_accounts_sync();
    }

163
164
165
166
167
168
169
    # Clean out the user /sshkeys directories, leaving /sshkeys/root alone.
    if (opendir(DIRHANDLE, "/sshkeys")) {
	while ($name = readdir(DIRHANDLE)) {
	    if ($name =~ m/^\.+/ || $name =~ m/^root$/) {
		next;
	    }

170
171
172
	    # Open up an existing key dir to the root user.  Even though root
	    # is in the Administrators group, it's locked out by permissions.
	    mysystem("$CHMOD 777 /sshkeys/$name");
Russ Fish's avatar
Russ Fish committed
173
174
	    mysystem("$CHOWN -Rf root /sshkeys/$name");
	    mysystem("$RM -rf /sshkeys/$name");
175
176
	}
	closedir(DIRHANDLE);
177
178
    }

Russ Fish's avatar
Russ Fish committed
179
    # Clean out the /proj subdirectories.
180
181
182
183
184
185
186
187
    if (opendir(DIRHANDLE, "/proj")) {
	while ($name = readdir(DIRHANDLE)) {
	    if ($name =~ m/^\.+/) {
		next;
	    }
	    print "Removing project: $name\n";

	    # Unmount the project dir so we can get to the mount point.
Russ Fish's avatar
Russ Fish committed
188
189
	    mysystem("$UMOUNT /proj/$name");
	    mysystem("$RMDIR /proj/$name");
190
191
192
	}
    }

Russ Fish's avatar
Russ Fish committed
193
194
    # Just unmount /share, everybody gets one.
    mysystem("$UMOUNT /share");
195
196
197
198
199
200
}

# 
# Make the CygWin /etc/passwd and /etc/group files match Windows.
# 
sub os_accounts_sync()
201
202
203
204
{
    unlink @LOCKFILES;

    # Generate the CygWin password and group files from the registry users.
205
    # Note that the group membership is not reported into the CygWin files.
206
207
    print "Resetting the CygWin passwd and group files.\n";

208
    my $cmd = "$MKPASSWD -l | $AWK -F: '";
209
210
211
212
213
214
215
216
    $cmd   .=   'BEGIN{ OFS=":"; ';
    # Keep Windows admin account homedirs under /home so we know what to clean.
    $cmd   .=   '  admin["root"]= admin["Administrator"]= admin["Guest"]= 1; ';
    $cmd   .=   '  admin["HelpAssistant"]= admin["SUPPORT_388945a0"]= 1; }';
    # Make root's UID zero.
    $cmd   .=   '{ if ($1=="root") $3="0"; ';
    # Put genuine user homedirs under /users, instead of /home.
    $cmd   .=   '    else if ( ! admin[$1] ) sub("/home/", "/users/"); print }'; 
217
218
219
220
    $cmd   .= "'";
    # Apply the users' shell preferences.
    $cmd   .= " | sed -f $usershellsfile"
	if (-e $usershellsfile);
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
    $cmd   .= " > /etc/passwd.new";
    ##print "$cmd\n";
    if (system("$cmd") != 0) {
	warning("Could not generate /etc/password.new file: $!\n");
	return -1;
    }

    # Work around "/etc/passwd: Device or resource busy".
    $cmd    = "$MV /etc/passwd /etc/passwd.prev";
    ##print "$cmd\n";
    if (system("$cmd") != 0) {
	warning("Could not $cmd $!\n");
	return -1;
    }
    $cmd    = "$MV /etc/passwd.new /etc/passwd";
236
237
    ##print "$cmd\n";
    if (system("$cmd") != 0) {
238
	warning("Could not $cmd $!\n");
239
240
	return -1;
    }
241

242
    $cmd  = "$MKGROUP -l | $AWK '";
243
244
245
246
247
    # Make a duplicate group line that is a wheel alias for Administrators.
    $cmd .= '/^Administrators:/{print "wheel" substr($0, index($0,":"))} {print}';
    $cmd .= "' > /etc/group";
    ##print "$cmd\n";
    if (system("$cmd") != 0) {
248
	warning("Could not generate /etc/group file: $!\n");
249
250
251
252
253
254
	return -1;
    }
    
    return 0;
}

255
256
257
# Import the mapping from non-control interface names, e.g. "Local Area
# Connection #4" to the Device Instance ID's used as devcon arguments, e.g.
# "@PCI\VEN_8086&DEV_1010&SUBSYS_10128086&REV_01\5&2FA58B96&0&210030".
258
my %dev_map = ();
259
sub get_dev_map()
260
{
261
262
263
    if (! $dev_map) {
	if (! open(DEVMAP, $XIMAP)) {
	    warning("Cannot open $XIMAP $!\n");
Russ Fish's avatar
Tweaks.    
Russ Fish committed
264
	}
265
266
267
268
269
270
271
272
273
	else {
	    while (my $dev_line = <DEVMAP>) {
		chomp($dev_line);
		my ($dev_name, $dev_inst) = split(":", $dev_line, 2);
		$dev_map{$dev_name} = $dev_inst;
	    }
	    close(DEVMAP);
	}
    }
274
275
276
277
278
279
}

#
# Generate and return an ifconfig line that is approriate for putting
# into a shell script (invoked at bootup).
#
280
sub os_ifconfig_line($$$$$$$;$$%)
281
{
282
283
    my ($iface, $inet, $mask, $speed, $duplex, $aliases, $iface_type,
	$settings, $rtabid, $cookie) = @_;
284
285
286
287
288
289
    my ($uplines, $downlines);

    # Handle interfaces missing from ipconfig.
    get_dev_map();
    if ( ! defined( $dev_map{$iface} ) ) {
	# Try rc.cygwin again to disable/re-enable the interface object.
Russ Fish's avatar
Typo.    
Russ Fish committed
290
	system("$BINDIR/rc/rc.cygwin");
291
292
293
294
295
296
297
298
299

	# Reboot if it still fails, in hope that the interface comes back.
	# 
	# We dare not proceed, because using netsh to try to set the IP
	# address on one of the missing addresses will blow away the IP on
	# *another* interface, sometimes the control net interface.  Then
	# we would really be in the soup...
	get_dev_map();
	if ( ! defined( $dev_map{$iface} ) ) {
Russ Fish's avatar
Typo.    
Russ Fish committed
300
	    system("$BINDIR/rc/rc.reboot");
301
302
303
304
305
	    # Sometimes rc.reboot gets fork: Resource temporarily unavailable.
	    print "rc.reboot returned, trying tsshutddn.";
	    system("tsshutdn 1 /REBOOT /DELAY:1");
	    print "tsshutdn failed, sleep forever.";
	    sleep;
306
307
	}
    }
308

309
    if ($inet ne "") {
310
311
312
313
	# Startup.
	$uplines   .= qq{\n    #================================\n    };
	$uplines   .= qq{echo "Enabling $iface on $inet"\n    };
	#
314
	# Re-enable device if necessary (getmac Transport is "Media disconnected".)
315
	my $test   =  qq[getmac /v /fo csv | awk -F, '/^"$iface"/{print \$4}'];
316
	$uplines   .= qq{if [ \`$test\` = '"Media disconnected"' ]; then\n    };
317
	$uplines   .=   "  $DEVCON enable '$dev_map{$iface}'\n    ";
Russ Fish's avatar
Tweaks.    
Russ Fish committed
318
	$uplines   .= qq{  sleep 5\n    };
319
320
321
322
323
324
325
	$uplines   .= qq{fi\n    };
	#
	# Configure.
	$uplines   .= sprintf($IFCONFIG, $iface, $inet, $mask) . qq{\n    };
	#
	# Check that the configuration took!
	my $showip =  qq[$NETSH interface ip show address name="$iface"];
326
327
328
329
330
331
332
333
334
	my $ipconf =  qq[$IPCONFIG /all | tr -d '\\r'];
	my $ipawk  =  qq[/^Ethernet adapter/] .
	    qq[{ ifc = gensub("Ethernet adapter (.*):", "\\\\\\\\1", 1); next }] .
		qq[/IP Address/ && ifc == "$iface"{print \$NF}];
	my $addr1  =  qq[addr1=\`$showip | awk '/IP Address:/{print \$NF}'\`];
	my $addr2  =  qq[addr2=\`$ipconf | awk '$ipawk'\`];
	my $iptest = '[[ "$addr1" != '.$inet.' || "$addr2" != '.$inet.' ]]';
	$uplines   .= qq{$addr1\n    $addr2\n    };
	$uplines   .= qq{if $iptest; then\n    };
335
336
337
338
	#
	# Re-do it if not.
	$uplines   .= qq{  echo "    Config failed on $iface, retrying."\n    };
	$uplines   .=   "  $DEVCON disable '$dev_map{$iface}'\n    ";
Russ Fish's avatar
Tweaks.    
Russ Fish committed
339
	$uplines   .= qq{  sleep 5\n    };
340
	$uplines   .=   "  $DEVCON enable '$dev_map{$iface}'\n    ";
Russ Fish's avatar
Tweaks.    
Russ Fish committed
341
	$uplines   .= qq{  sleep 5\n    };
342
	$uplines   .= sprintf("  " . $IFCONFIG, $iface, $inet, $mask) . qq{\n    };
343
344
	#
	# Re-check.
Russ Fish's avatar
Tweaks.    
Russ Fish committed
345
	$uplines   .= qq{  $addr1\n    $addr2\n    };
346
	$uplines   .= qq{  if $iptest; then\n    };
347
348
349
350
	$uplines   .= qq{    echo "    Reconfig still failed on $iface."\n    };
	$uplines   .= qq{  else echo "    Reconfig succeeded on $iface."\n    };
	$uplines   .= qq{  fi\n    };
	$uplines   .= qq{fi};
Russ Fish's avatar
Russ Fish committed
351

352
353
354
	# Shutdown.
	$downlines .= qq{echo "Disabling $iface from $inet"\n    };
	$downlines .=   "$DEVCON disable '$dev_map{$iface}'\n";
355
356
357
358
359
360
361
362
    }
    
    return ($uplines, $downlines);
}

#
# Specialized function for configing locally hacked veth devices.
#
363
sub os_ifconfig_veth($$$$$;$$$$$)
364
365
366
367
368
369
370
371
372
373
374
375
{
    return "";
}

#
# Generate and return an string that is approriate for putting
# into /etc/hosts.
#
sub os_etchosts_line($$$)
{
    my ($name, $ip, $aliases) = @_;
    
376
377
    # Note: space rather than tab after the host name on Windows.
    return sprintf("%s %s %s", $ip, $name, $aliases);
378
379
}

380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
#
# On Windows NT, accumulate an input file for the addusers command.
# See "AddUsers Automates Creation of a Large Number of Users",
# http://support.microsoft.com/default.aspx?scid=kb;en-us;199878
# 
# The file format is comma-delimited, as follows:
# 
# [Users]
# User Name,Full name,Password,Description,HomeDrive,Homepath,Profile,Script
# 
# [Global] or [Local]
# Group Name,Comment,UserName,...
# 
my @groupNames;
my %groupsByGid;
my %groupMembers;
sub os_accounts_start()
{
    # Remember group info to be put out at the end.
    @groupNames = ();
    %groupsByGid = ();
    %groupMembers = ();

    if (! open(WINUSERS, "> $winusersfile")) {
404
405
406
407
	warning("os_accounts_start: Cannot create $winusersfile .\n");
	return -1;
    }

408
409
410
    # Don't wipe out previous user shell preferences, just add new ones.
    if (! open(USERSHELLS, ">> $usershellsfile")) {
	warning("os_accounts_start: Cannot create or append to $usershellsfile .\n");
411
412
413
414
	return -1;
    }

    # Users come before groups in the addusers.exe account stream.
415
    # Notice the <CR><LF>'s!  It's a Windows file.
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
    print WINUSERS "[Users]\r\n";

    return 0;
}

#
# Remember the mapping from an existing group GID to its name.
#
sub os_groupgid($$)
{
    my($group, $gid) = @_;

    $groupsByGid{$gid} = $group;    # Remember the name associated with the gid.

    return 0;
}

433
434
435
436
437
438
439
#
# Add a new group
# 
sub os_groupadd($$)
{
    my($group, $gid) = @_;

440
441
442
443
    push(@groupNames, $group);      # Remember all of the group names.
    os_groupgid($group, $gid);

    return 0;
444
445
446
447
448
449
450
451
452
}

#
# Delete an old group
# 
sub os_groupdel($)
{
    my($group) = @_;

453
454
    # Unimplemented.
    return -1;
455
456
457
458
459
460
461
462
463
}

#
# Remove a user account.
# 
sub os_userdel($)
{
    my($login) = @_;

464
465
    # Unimplemented.
    return -1;
466
467
468
}

#
469
470
# Modify user group membership and password.
# Changing the login shell is unimplemented.
471
472
473
474
475
# 
sub os_usermod($$$$$$)
{
    my($login, $gid, $glist, $pswd, $root, $shell) = @_;

476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
    if ($root) {
	$glist .= ",0";
    }
    if ($glist ne "") {
	##print "glist '$glist'\n";
	my $gname;
	foreach my $grp (split(/,/, $glist)) {
	    if ( $grp eq "0" ) {
		$gname = "Administrators";
	    }
	    else {
		$gname = $groupsByGid{$grp};
	    }
	    ##print "login $login, grp $grp, gname '$gname'\n";
	    my $cmd = "$NET localgroup $gname | tr -d '\\r' | grep -q '^$login\$'";
	    ##print "    $cmd\n";
	    if (system($cmd)) {
		# Add members into groups using the "net localgroup /add" command.
		$cmd = "$NET localgroup $gname $login /add";
		##print "    $cmd\n";
		if (system($cmd) != 0) {
		    warning("os_usermod error ($cmd)\n");
		}
	    }
	}
    }
502

Russ Fish's avatar
Russ Fish committed
503
    $cmd = "echo -e '$pswd\\n$pswd' | passwd $login >& /dev/null";
504
505
506
507
    ##print "    $cmd\n";
    if (system($cmd) != 0) {
	warning("os_usermod error ($cmd)\n");
    }
508
509
510
511
512
513
514
515
516
}

#
# Add a user.
# 
sub os_useradd($$$$$$$$$)
{
    my($login, $uid, $gid, $pswd, $glist, $homedir, $gcos, $root, $shell) = @_;

517
518
    # Groups have to be created before we can add members.
    my $gname = $groupsByGid{$gid};
519
    warning("Missing group name for gid $gid.\n")
520
521
522
523
524
525
526
527
528
529
	if (!$gname);
    $groupMembers{$gname} .= "$login ";
    $groupMembers{'Administrators'} .= "$login "
	if ($root);
    foreach my $gid (split(/,/, $glist)) {
	$gname = $groupsByGid{$gid};
	if ($gname) {
	    $groupMembers{$gname} .= "$login ";
	}
	else {
530
	    warning("Missing group name for gid $gid.\n");
531
	}
532
    }
533
		     
534
535
    # Map the shell into a full path.
    $shell = MapShell($shell);
536
537
538
    # Change the ones that are different from the default from mkpasswd, /bin/bash.
    print USERSHELLS "/^$login:/s|/bin/bash\$|$shell|\n"
	if ($shell !~ "/bin/bash");
539

540
541
542
    # Use the leading 8 chars of the Unix MD5 passwd hash as a known random
    # password, both here and in Samba.  Skip over a "$1$" prefix.
    my $pwd = $pswd;
543
    $pwd =~ s/^(\$1\$)(.{8}).*/$2/;
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
    
    print WINUSERS "$login,$gcos,$pwd,,,,,\r\n";

    return 0;
}

#
# Finish the input for the addusers command.
#
sub os_accounts_end()
{
    # Dump out the group *creation* lines.
    print WINUSERS "[Local]\r\n";
    foreach my $grp (@groupNames) {
	# Ignore group membership here.  See "net localgroup" below.
	print WINUSERS "$grp,Emulab $grp group,\r\n";
    }
    close WINUSERS;
562
    close USERSHELLS;
563
564
565
566
567
568
569
570
571
       
    # Create the whole batch of groups and accounts listed in the file.
    # /p options: Users don't have to change passwords, and they never expire.
    print "Creating the Windows users and groups.\n";
    my $winfile = "C:/cygwin$winusersfile";
    $winfile =~ s|/|\\|g;
    my $cmd = "$ADDUSERS /c '$winfile' /p:le";
    ##print "    $cmd\n";
    if (system($cmd) != 0) {
572
	warning("AddUsers error ($cmd)\n");
573
574
	return -1;
    }
575
576
577
578
579
580
581
582
583

    # Add members into groups using the "net localgroup /add" command.
    # (Addusers only creates groups, it can't add a user to an existing group.)
    while (my($grp, $members) = each %groupMembers) {
	foreach my $mbr (split(/ /,$members)) {
	    print "  Adding $mbr to $grp.\n";
	    my $cmd = "$NET localgroup $grp $mbr /add > /dev/null";
	    ##print "    $cmd\n";
	    if (system($cmd) != 0) {
584
		warning("net localgroup error ($cmd)\n");
585
586
587
588
589
	    }
	}
    }

    # Make the CygWin /etc/passwd and /etc/group files match Windows.
590
    # Note that the group membership is not reported into the CygWin files.
591
    return os_accounts_sync();
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
}

#
# Remove a homedir. Might someday archive and ship back.
#
sub os_homedirdel($$)
{
    return 0;
}

#
# Create a directory including all intermediate directories.
#
sub os_mkdir($$)
{
    my ($dir, $mode) = @_;

    if (system("$MKDIR -p -m $mode $dir")) {
	return 0;
    }
    return 1;
}

#
# OS Dependent configuration. 
# 
sub os_setup()
{
    return 0;
}
    
#
# OS dependent, routing-related commands
#
sub os_routing_enable_forward()
{
Mike Hibler's avatar
Mike Hibler committed
628
    return "";
629
630
631
632
}

sub os_routing_enable_gated($)
{
Mike Hibler's avatar
Mike Hibler committed
633
    return "";
634
635
636
637
638
639
640
641
}

sub os_routing_add_manual($$$$$;$)
{
    my ($routetype, $destip, $destmask, $gate, $cost, $rtabid) = @_;
    my $cmd;

    if ($routetype eq "host") {
Mike Hibler's avatar
Mike Hibler committed
642
	$cmd = "$ROUTE add $destip $gate";
643
    } elsif ($routetype eq "net") {
Mike Hibler's avatar
Mike Hibler committed
644
	$cmd = "$ROUTE add $destip mask $destmask $gate";
645
    } elsif ($routetype eq "default") {
Mike Hibler's avatar
Mike Hibler committed
646
	$cmd = "$ROUTE add 0.0.0.0 $gate";
647
    } else {
648
	warning("Bad routing entry type: $routetype\n");
649
650
651
	$cmd = "";
    }

Russ Fish's avatar
Russ Fish committed
652
653
654
655
656
657
    # There appears to be a race with interfaces coming on-line.
    #     The route addition failed: Either the interface index is wrong or
    #     the gateway does not lie on the same network as the interface. Check
    #     the IP Address Table for the machine.
    # Re-doing the command later succeeds.
    # Wrap the route command in a loop to make sure it gets done.
Russ Fish's avatar
Tweaks.    
Russ Fish committed
658
659
    # Don't loop forever.
    $cmd = "n=1; while ! ( $ROUTE print | grep -Fq $destip ); do \n
Russ Fish's avatar
Russ Fish committed
660
661
                echo $cmd;\n
                $cmd\n
Russ Fish's avatar
Tweaks.    
Russ Fish committed
662
                let n++; if [[ \$n > 5 ]]; then break; fi
Russ Fish's avatar
Russ Fish committed
663
                sleep 5\n
Russ Fish's avatar
Russ Fish committed
664
665
            done";

666
667
668
669
670
671
672
673
674
    return $cmd;
}

sub os_routing_del_manual($$$$$;$)
{
    my ($routetype, $destip, $destmask, $gate, $cost, $rtabid) = @_;
    my $cmd;

    if ($routetype eq "host") {
Mike Hibler's avatar
Mike Hibler committed
675
	$cmd = "$ROUTE delete $destip";
676
    } elsif ($routetype eq "net") {
Mike Hibler's avatar
Mike Hibler committed
677
	$cmd = "$ROUTE delete $destip";
678
    } elsif ($routetype eq "default") {
Mike Hibler's avatar
Mike Hibler committed
679
	$cmd = "$ROUTE delete 0.0.0.0";
680
    } else {
681
	warning("Bad routing entry type: $routetype\n");
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
	$cmd = "";
    }

    return $cmd;
}

# Map a shell name to a full path using /etc/shells
sub MapShell($)
{
   my ($shell) = @_;

   if ($shell eq "") {
       return $DEFSHELL;
   }

   my $fullpath = `grep '/${shell}\$' $SHELLS`;

   if ($?) {
       return $DEFSHELL;
   }

   # Sanity Check
   if ($fullpath =~ /^([-\w\/]*)$/) {
       $fullpath = $1;
   }
   else {
       $fullpath = $DEFSHELL;
   }
   return $fullpath;
}

713
sub os_samba_mount($$$)
714
715
716
{
    my ($local, $host, $verbose) = @_;

717
    # Unmount each one first, ignore errors.
Russ Fish's avatar
Russ Fish committed
718
    system("$UMOUNT $local");
719

720
721
    # Make the CygWin mount from the Samba path to the local mount point directory.
    my $sambapath = $local;
722
    $sambapath =~ s|^/proj/(.*)|proj-$1|;
Russ Fish's avatar
Typo.    
Russ Fish committed
723
    $sambapath =~ s|^/groups/(.*)/(.*)|$1-$2|;
724
725
    $sambapath =~ s|.*/(.*)|$1|;
    $sambapath = "//$host/$sambapath";
726
727
    if (! -e $local) {
	print "os_samba_mount: Making CygWin '$local' mount point directory.\n"
728
	    if ($verbose);
729
730
	if (! os_mkdir($local, "0755")) {  # Will make whole path if necessary.
	    warning("os_samba_mount: Could not make mount point $local.\n");
731
732
	}
    }
733
734
    elsif (! -d $local) {
	warning("os_samba_mount: Mount point $local is not a directory.\n");
735
    }
736
    print "Mounting '$local' from '$sambapath'.\n"
737
	if ($verbose);
738
739
740
741
742

    # If we don't turn on the -E/--no-executable flag, CygWin mount warns us:
    #     mount: defaulting to '--no-executable' flag for speed since native path
    #            references a remote share.  Use '-f' option to override.
    # Even with -E, exe's and scripts still work properly, so put it in.
743
    $cmd = "$MOUNT -f -E $sambapath $local";
744
    if (system($cmd) != 0) {
745
	warning("os_samba_mount: Failed, $cmd.\n");
746
747
748
749
    }
}

# Extract the local mount point from a remote NFS mount path.
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
sub os_mountlocal($)
{
    my ($remote) = @_;
    my $local = $remote;
    $local =~ s|^.*:||;			# Remove server prefix.
    $local =~ s|^/q/proj/|/proj/|;	# Remove /q prefix from /proj.
    return $local;
}

# Execute a noisy bash command, throwing away the output unless we ask for it.
sub os_noisycmd($$)
{
    my ($cmd, $verbose) = @_;
    my $bashcmd = "$BASH -c '$cmd'" . ($verbose ? "" : " > /dev/null");
    my $ret = system($bashcmd);
    ##print "os_noisycmd cmd '$cmd', ret $ret\n";
    return $ret
}

sub os_fwconfig_line($@)
{
    my ($fwinfo, @fwrules) = @_;
    my ($upline, $downline);
773
    my $errstr = "*** WARNING: Windows firewall not implemented\n";
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811

    warn $errstr;
    $upline = "echo $errstr; exit 1";
    $downline = "echo $errstr; exit 1";

    return ($upline, $downline);
}

sub os_fwrouteconfig_line($$$)
{
    my ($orouter, $fwrouter, $routestr) = @_;
    my ($upline, $downline);

    #
    # XXX assume the original default route should be used to reach servers.
    #
    # For setting up the firewall, this means we create explicit routes for
    # each host via the original default route.
    #
    # For tearing down the firewall, we just remove the explicit routes
    # and let them fall back on the now re-established original default route.
    #
    $upline  = "for vir in $routestr; do\n";
    $upline .= "        $ROUTE delete \$vir >/dev/null 2>&1\n";
    $upline .= "        $ROUTE add -host \$vir gw $orouter || {\n";
    $upline .= "            echo \"Could not establish route for \$vir\"\n";
    $upline .= "            exit 1\n";
    $upline .= "        }\n";
    $upline .= "    done";

    $downline  = "for vir in $routestr; do\n";
    $downline .= "        $ROUTE delete \$vir >/dev/null 2>&1\n";
    $downline .= "    done";

    return ($upline, $downline);
}

1;