diff --git a/tmcd/common/libsetup.pm b/tmcd/common/libsetup.pm index fc51cb269780aef92f89d0fc580a38363c6acaed..382d92180f579aa5fbfebdc4426dfbefe8f8f95b 100644 --- a/tmcd/common/libsetup.pm +++ b/tmcd/common/libsetup.pm @@ -79,6 +79,7 @@ sub TMNICKNAME() { "$SETUPDIR/nickname"; } sub FINDIF() { "$SETUPDIR/findif"; } sub HOSTSFILE() { "/etc/hosts"; } sub TMMOUNTDB() { "$SETUPDIR/mountdb"; } +sub TMSFSMOUNTDB() { "$SETUPDIR/sfsmountdb"; } sub TMROUTECONFIG() { ($vnodedir ? $vnodedir : $SETUPDIR) . "/rc.route";} sub TMTRAFFICCONFIG() { ($vnodedir ? $vnodedir : $SETUPDIR) . "/rc.traffic";} sub TMTUNNELCONFIG() { ($vnodedir ? $vnodedir : $SETUPDIR) . "/rc.tunnel";} @@ -86,13 +87,21 @@ sub TMVTUNDCONFIG() { ($vnodedir ? $vnodedir : $SETUPDIR) . "/vtund.conf";} sub TMPASSDB() { "$SETUPDIR/passdb"; } sub TMGROUPDB() { "$SETUPDIR/groupdb"; } +# +# Whether or not to use SFS (the self-certifying file system). If this +# is 0, fall back to NFS. Note that it doesn't hurt to set this to 1 +# even if TMCD is not serving out SFS mounts, or if this node is not +# running SFS. It'll deal and fall back to NFS. +# +my $USESFS = 1; + # # This is the VERSION. We send it through to tmcd so it knows what version # responses this file is expecting. # # BE SURE TO BUMP THIS AS INCOMPATIBILE CHANGES TO TMCD ARE MADE! # -sub TMCD_VERSION() { 5; }; +sub TMCD_VERSION() { 6; }; # # These are the TMCC commands. @@ -117,6 +126,8 @@ sub TMCCCMD_TUNNEL() { "tunnels"; } sub TMCCCMD_NSECONFIGS(){ "nseconfigs"; } sub TMCCCMD_VNODELIST() { "vnodelist"; } sub TMCCCMD_ISALIVE() { "isalive"; } +sub TMCCCMD_SFSHOSTID() { "sfshostid"; } +sub TMCCCMD_SFSMOUNTS() { "sfsmounts"; } # # Some things never change. @@ -129,7 +140,8 @@ my $VTUND = "/usr/local/sbin/vtund"; # This is a debugging thing for my home network. # #my $NODE = "-p 7778 REDIRECT=192.168.100.1"; -$NODE = ""; +my $NODE = "-p 7779"; +#$NODE = ""; # Locals my $pid = ""; @@ -324,13 +336,21 @@ sub domounts() my %MDB; my %mounts; my %deletes; - - $TM = OPENTMCC(TMCCCMD_MOUNTS); + my %sfsmounts; + my %sfsdeletes; + + $TM = OPENTMCC(TMCCCMD_MOUNTS, "USESFS=$USESFS"); while (<$TM>) { - if ($_ =~ /REMOTE=([-:\@\w\.\/]+) LOCAL=([-\@\w\.\/]+)/) { + if ($_ =~ /^REMOTE=([-:\@\w\.\/]+) LOCAL=([-\@\w\.\/]+)/) { $mounts{$1} = $2; } + elsif ($_ =~ /^SFS REMOTE=([-:\@\w\.\/]+) LOCAL=([-\@\w\.\/]+)/) { + $sfsmounts{$1} = $2; + } + else { + warn "*** WARNING: Malformed mount information: $_\n"; + } } CLOSETMCC($TM); @@ -419,6 +439,269 @@ sub domounts() # Write the DB back out! dbmclose(%MDB); + # + # Now, do basically the same thing over again, but this time for + # SFS mounted stuff + # + + if (scalar(%sfsmounts)) { + dbmopen(%MDB, TMSFSMOUNTDB, 0660); + + # + # First symlink all the mounts we are told to. For each one + # that is not currently symlinked, and can be, add it to the + # DB. + # + while (($remote, $local) = each %sfsmounts) { + if (-l $local) { + if (readlink($local) eq ("/sfs/" . $remote)) { + $MDB{$remote} = $local; + next; + } + if (readlink($local) ne ("/sfs/" . $remote)) { + print STDOUT " Unlinking incorrect symlink $local\n"; + if ( ! unlink($local)) { + warn "*** WARNING: Could not unlink $local: $!\n"; + next; + } + } + } + + $dir = $local; + $dir =~ s/(.*)\/[^\/]*$/$1/; + if (! -e $dir) { + print STDOUT " Making directory $dir\n"; + if (! os_mkdir($dir, 755)) { + warn "*** WARNING: Could not make directory $local: $!\n"; + next; + } + } + print STDOUT " Symlinking $remote on $local\n"; + if (! symlink("/sfs/" . $remote . "/", $local)) { + warn "*** WARNING: Could not make symlink $local: $!\n"; + next; + } + + $MDB{$remote} = $local; + } + + # + # Now delete the ones that we symlinked previously, but are + # now no longer in the mount set (as told to us by the TMCD). + # Note, we cannot delete them directly from MDB since that + # would mess up the foreach loop, so just stick them in temp + # and postpass it. + # + while (($remote, $local) = each %MDB) { + if (defined($sfsmounts{$remote})) { + next; + } + + if (! -e $local) { + $sfsdeletes{$remote} = $local; + next; + } + + print STDOUT " Deleting symlink $local\n"; + if (! unlink($local)) { + warn "*** WARNING: Could not delete $local: $!\n"; + next; + } + + # + # Only delete from set if we can actually unlink it. This way + # we can retry it later (or next time). + # + $sfsdeletes{$remote} = $local; + } + while (($remote, $local) = each %sfsdeletes) { + delete($MDB{$remote}); + } + + # Write the DB back out! + dbmclose(%MDB); + } + else { + # There were no SFS mounts reported, so disable SFS + $usesfs = 0; + } + + return 0; +} + +# +# Do SFS hostid setup. +# Creates an SFS host key for this node, if it doesn't already exist, +# and sends it to TMCD +# +sub dosfshostid () +{ + my $TM; + my $myhostid; + + # Do I already have a host key? +# if (-e "/etc/sfs/sfs_host_key") { +# print STDOUT " This node already has a host key\n"; +# } +# else { +# # Create my host key +# if (! -e "/etc/sfs") { +# if (! os_mkdir("/etc/sfs", 0755)) { +# warn "*** WARNING: Could not make directory /etc/sfs: $!\n"; +# } +# } + +# if (! -e "/etc/sfs/sfs_host_key") { +# print STDOUT " Creating SFS host key\n"; +# if (system("sfskey gen -KPn $vname.$eid.$pid ". +# "/etc/sfs/sfs_host_key")) { +# warn "*** WARNING: Could not generate SFS host key: $!\n"; +# $USESFS = 0; +# return 1; +# } +# } +# } + if (! -e "/etc/sfs/sfs_host_key") { + warn "*** This node does not have a host key, skipping SFS stuff\n"; + $USESFS = 0; + return 1; + } + + # Start SFS server and client (this has to be done now, as opposed + # to in an rc script, because the hostkey has to exist) +# if (system($SFSSD)) { +# warn "*** Failed to start sfssd\n"; +# $USESFS = 0; +# return 1; +# } +# if (system($SFSCD)) { +# warn "*** Failed to start sfscd\n"; +# $USESFS = 0; +# return 1; +# } + + # Give hostid to TMCD + # IAMHERE: sfssd needs to be running for this call to succeed + # Directory needs to exist on fs/proj + open(SFSKEY, "sfskey hostid - |") + or die "Cannot start sfskey"; + $myhostid = <SFSKEY>; + close(SFSKEY); + if (defined($myhostid)) { + if ( $myhostid =~ /^([-\.\w_]*:[a-z0-9]*)$/ ) { + $myhostid = $1; + print STDOUT " Hostid: $myhostid\n"; + RUNTMCC(TMCCCMD_SFSHOSTID, "$myhostid"); + } + else { + warn "*** WARNING: Invalid hostid\n"; + } + } + else { + warn "*** WARNING: Could not retrieve this node's hostid (is sfssd running?)\n"; + $USESFS = 0; + } + + return 0; +} + +# +# Create SFS "mounts" (which are really just symlinks). The remote side +# of the mount is relative to /sfs and includes the server hostname, the +# server's hostid, and the path on the server. The local side is simply +# the path relative to / to symlink to the remote sfs directory. This +# closely follows domounts() +# +sub dosfsmounts() +{ + my $TM; + my %MBD; + my %mounts; + my %deletes; + + die("*** Don't call dosfsmounts!"); + + $TM = OPENTMCC(TMCCCMD_SFSMOUNTS); + + while (<$TM>) { + if ($_ =~ /REMOTE=([-:\@\w\.\/]+) LOCAL=([-\@\w\.\/]+)/) { + $mounts{$1} = $2; + } + } + CLOSETMCC($TM); + + dbmopen(%MDB, TMSFSMOUNTDB, 0660); + + # + # First symlink all the mounts we are told to. For each one that is + # not currently symlinked, and can be, add it to the DB. + # + while (($remote, $local) = each %mounts) { + if (-l $local && readlink($local) eq ("/sfs/" . $remote)) { + $MDB{$remote} = $local; + next; + } + if ( ! -l $local ) { + print STDOUT " Unlinking incorrect symlink $local\n"; + if ( ! unlink($local)) { + warn "*** WARNING: Could not unlink $local: $!\n"; + next; + } + } + + $dir = $local; + $dir =~ s/(.*)\/[^\/]*$/$1/; + if (! -e $dir) { + print STDOUT " Making directory $dir\n"; + if (! os_mkdir($dir, 755)) { + warn "*** WARNING: Could not make directory $local: $!\n"; + next; + } + } + print STDOUT " Symlinking $remote on $local\n"; + if (! symlink("/sfs/" . $remote, $local)) { + warn "*** WARNING: Could not make symlink $local: $!\n"; + next; + } + + $MDB{$remote} = $local; + } + + # + # Now delete the ones that we symlinked previously, but are now no + # longer in the mount set (as told to us by the TMCD). Note, we + # cannot delete them directly from MDB since that would mess up the + # foreach loop, so just stick them in temp and postpass it. + # + while (($remote, $local) = each %MDB) { + if (defined($mounts{$remote})) { + next; + } + + if (! -e $local) { + $deletes{$remote} = $local; + next; + } + + print STDOUT " Deleting symlink $local\n"; + if (! unlink($local)) { + warn "*** WARNING: Could not delete $local: $!\n"; + next; + } + + # + # Only delete from set if we can actually unlink it. This way + # we can retry it later (or next time). + # + $deletes{$remote} = $local; + } + while (($remote, $local) = each %deletes) { + delete($MDB{$remote}); + } + + # Write the DB back out! + dbmclose(%MDB); + return 0; } @@ -696,6 +979,7 @@ sub doaccounts() my %newaccounts = (); my %newgroups = (); my %pubkeys = (); + my @sfskeys = (); my %deletes = (); my %lastmod = (); my %PWDDB; @@ -740,6 +1024,13 @@ sub doaccounts() push(@{$pubkeys{$1}}, $2); next; } + elsif ($_ =~ /^SFSKEY KEY="(.*)"/) { + # + # SFS key goes into the array. + # + push(@sfskeys, $1); + next; + } else { warn "*** WARNING: Bad accounts line: $_\n"; } @@ -966,7 +1257,7 @@ sub doaccounts() next; } } - + if (!open(AUTHKEYS, "> $sshdir/authorized_keys.new")) { warn("*** WARNING: Could not open $sshdir/keys.new: $!\n"); next; @@ -1016,6 +1307,78 @@ sub doaccounts() # Write the DB back out! dbmclose(%PWDDB); + # + # Create sfs_users file and populate it with public SFS keys + # + do { + if (!open(SFSKEYS, "> /etc/sfs/sfs_users.new")) { + warn("*** WARNING: Could not open /etc/sfs/sfs_users.new: $!\n"); + next; + } + + print SFSKEYS "#\n"; + print SFSKEYS "# DO NOT EDIT! This file auto generated by ". + "Emulab.Net account software.\n"; + print SFSKEYS "#\n"; + print SFSKEYS "# Please use the web interface to edit your ". + "SFS public key list.\n"; + print SFSKEYS "#\n"; + foreach my $key (@sfskeys) { + print SFSKEYS "$key\n"; + } + close(SFSKEYS); + + # Because sfs_users only contains public keys, sfs_users.pub is + # exactly the same + if (system("cp -p -f /etc/sfs/sfs_users.new ". + "/etc/sfs/sfs_users.pub.new")) { + warn("*** Could not copy /etc/sfs/sfs_users.new to ". + "sfs_users.pub.new: $!\n"); + next; + } + + if (!chown(0, 0, "/etc/sfs/sfs_users.new")) { + warn("*** WARNING: Could not chown /etc/sfs/sfs_users.new: $!\n"); + next; + } + if (!chmod(0600, "/etc/sfs/sfs_users.new")) { + warn("*** WARNING: Could not chmod /etc/sfs/sfs_users.new: $!\n"); + next; + } + + if (!chown(0, 0, "/etc/sfs/sfs_users.pub.new")) { + warn("*** WARNING: Could not chown /etc/sfs/sfs_users.pub.new: $!\n"); + next; + } + if (!chmod(0644, "/etc/sfs/sfs_users.pub.new")) { + warn("*** WARNING: Could not chmod /etc/sfs/sfs_users.pub.new: $!\n"); + next; + } + + # Save off old key files and move in new ones + foreach my $keyfile ("/etc/sfs/sfs_users", + "/etc/sfs/sfs_users.pub") { + if (-e $keyfile) { + if (system("cp -p -f $keyfile $keyfile.old")) { + warn("*** Could not save off $keyfile: $!\n"); + next; + } + if (!chown(0, 0, "$keyfile")) { + warn("*** Could not chown $keyfile: $!\n"); + } + if (!chmod(0600, "$keyfile")) { + warn("*** Could not chmod $keyfile: $!\n"); + } + } + if (system("mv -f $keyfile.new $keyfile")) { + warn("*** Could not mv $keyfile: ~!\n"); + } + } + + # The do-while is an easy way out in case of errors + } + while 0; + return 0; } @@ -1469,8 +1832,17 @@ sub bootsetup() # create_nicknames(); + if ($USESFS && ! MFS()) { + # + # Setup SFS hostid. + # + print STDOUT "Creating node SFS hostid ... \n"; + dosfshostid(); + } + # - # Mount the project and user directories + # Mount the project and user directories and symlink SFS "mounted" + # directories # print STDOUT "Mounting project and home directories ... \n"; domounts(); @@ -1563,8 +1935,17 @@ sub nodeupdate() return 0; } + if ($USESFS && ! MFS()) { + # + # Setup SFS hostid. + # + print STDOUT "Creating node SFS hostid ... \n"; + dosfshostid(); + } + # - # Mount the project and user directories + # Mount the project and user directories and symlink SFS "mounted" + # directories # print STDOUT "Mounting project and home directories ... \n"; domounts(); diff --git a/tmcd/decls.h b/tmcd/decls.h index 864959d4f2a5c4d090e64d410183faf1caf34300..a99d03862c003c20fbd9921bdb50fef7ec918c0a 100644 --- a/tmcd/decls.h +++ b/tmcd/decls.h @@ -24,4 +24,4 @@ * NB: See ron/libsetup.pm. That is version 4! I'll merge that in. */ #define DEFAULT_VERSION 2 -#define CURRENT_VERSION 5 +#define CURRENT_VERSION 6 diff --git a/tmcd/freebsd/liblocsetup.pm b/tmcd/freebsd/liblocsetup.pm index ac84f60284b57e53d8d62b17214e36e51cefcbb5..97bb3df718fa1e40afdf0b7080e2438c06a20213 100644 --- a/tmcd/freebsd/liblocsetup.pm +++ b/tmcd/freebsd/liblocsetup.pm @@ -14,7 +14,7 @@ package liblocsetup; use Exporter; @ISA = "Exporter"; @EXPORT = - qw ( $CP $EGREP $MOUNT $UMOUNT $TMPASSWD + qw ( $CP $EGREP $MOUNT $UMOUNT $TMPASSWD $SFSSD $SFSCD os_cleanup_node os_ifconfig_line os_etchosts_line os_setup os_groupadd os_useradd os_userdel os_usermod os_mkdir os_rpminstall_line update_delays @@ -41,6 +41,8 @@ $MOUNT = "/sbin/mount"; $UMOUNT = "/sbin/umount"; $TMGROUP = "$SETUPDIR/group"; $TMPASSWD = "$SETUPDIR/master.passwd"; +$SFSSD = "/usr/local/sbin/sfssd"; +$SFSCD = "/usr/local/sbin/sfscd"; # # These are not exported diff --git a/tmcd/libsetup.pm b/tmcd/libsetup.pm index fc51cb269780aef92f89d0fc580a38363c6acaed..382d92180f579aa5fbfebdc4426dfbefe8f8f95b 100644 --- a/tmcd/libsetup.pm +++ b/tmcd/libsetup.pm @@ -79,6 +79,7 @@ sub TMNICKNAME() { "$SETUPDIR/nickname"; } sub FINDIF() { "$SETUPDIR/findif"; } sub HOSTSFILE() { "/etc/hosts"; } sub TMMOUNTDB() { "$SETUPDIR/mountdb"; } +sub TMSFSMOUNTDB() { "$SETUPDIR/sfsmountdb"; } sub TMROUTECONFIG() { ($vnodedir ? $vnodedir : $SETUPDIR) . "/rc.route";} sub TMTRAFFICCONFIG() { ($vnodedir ? $vnodedir : $SETUPDIR) . "/rc.traffic";} sub TMTUNNELCONFIG() { ($vnodedir ? $vnodedir : $SETUPDIR) . "/rc.tunnel";} @@ -86,13 +87,21 @@ sub TMVTUNDCONFIG() { ($vnodedir ? $vnodedir : $SETUPDIR) . "/vtund.conf";} sub TMPASSDB() { "$SETUPDIR/passdb"; } sub TMGROUPDB() { "$SETUPDIR/groupdb"; } +# +# Whether or not to use SFS (the self-certifying file system). If this +# is 0, fall back to NFS. Note that it doesn't hurt to set this to 1 +# even if TMCD is not serving out SFS mounts, or if this node is not +# running SFS. It'll deal and fall back to NFS. +# +my $USESFS = 1; + # # This is the VERSION. We send it through to tmcd so it knows what version # responses this file is expecting. # # BE SURE TO BUMP THIS AS INCOMPATIBILE CHANGES TO TMCD ARE MADE! # -sub TMCD_VERSION() { 5; }; +sub TMCD_VERSION() { 6; }; # # These are the TMCC commands. @@ -117,6 +126,8 @@ sub TMCCCMD_TUNNEL() { "tunnels"; } sub TMCCCMD_NSECONFIGS(){ "nseconfigs"; } sub TMCCCMD_VNODELIST() { "vnodelist"; } sub TMCCCMD_ISALIVE() { "isalive"; } +sub TMCCCMD_SFSHOSTID() { "sfshostid"; } +sub TMCCCMD_SFSMOUNTS() { "sfsmounts"; } # # Some things never change. @@ -129,7 +140,8 @@ my $VTUND = "/usr/local/sbin/vtund"; # This is a debugging thing for my home network. # #my $NODE = "-p 7778 REDIRECT=192.168.100.1"; -$NODE = ""; +my $NODE = "-p 7779"; +#$NODE = ""; # Locals my $pid = ""; @@ -324,13 +336,21 @@ sub domounts() my %MDB; my %mounts; my %deletes; - - $TM = OPENTMCC(TMCCCMD_MOUNTS); + my %sfsmounts; + my %sfsdeletes; + + $TM = OPENTMCC(TMCCCMD_MOUNTS, "USESFS=$USESFS"); while (<$TM>) { - if ($_ =~ /REMOTE=([-:\@\w\.\/]+) LOCAL=([-\@\w\.\/]+)/) { + if ($_ =~ /^REMOTE=([-:\@\w\.\/]+) LOCAL=([-\@\w\.\/]+)/) { $mounts{$1} = $2; } + elsif ($_ =~ /^SFS REMOTE=([-:\@\w\.\/]+) LOCAL=([-\@\w\.\/]+)/) { + $sfsmounts{$1} = $2; + } + else { + warn "*** WARNING: Malformed mount information: $_\n"; + } } CLOSETMCC($TM); @@ -419,6 +439,269 @@ sub domounts() # Write the DB back out! dbmclose(%MDB); + # + # Now, do basically the same thing over again, but this time for + # SFS mounted stuff + # + + if (scalar(%sfsmounts)) { + dbmopen(%MDB, TMSFSMOUNTDB, 0660); + + # + # First symlink all the mounts we are told to. For each one + # that is not currently symlinked, and can be, add it to the + # DB. + # + while (($remote, $local) = each %sfsmounts) { + if (-l $local) { + if (readlink($local) eq ("/sfs/" . $remote)) { + $MDB{$remote} = $local; + next; + } + if (readlink($local) ne ("/sfs/" . $remote)) { + print STDOUT " Unlinking incorrect symlink $local\n"; + if ( ! unlink($local)) { + warn "*** WARNING: Could not unlink $local: $!\n"; + next; + } + } + } + + $dir = $local; + $dir =~ s/(.*)\/[^\/]*$/$1/; + if (! -e $dir) { + print STDOUT " Making directory $dir\n"; + if (! os_mkdir($dir, 755)) { + warn "*** WARNING: Could not make directory $local: $!\n"; + next; + } + } + print STDOUT " Symlinking $remote on $local\n"; + if (! symlink("/sfs/" . $remote . "/", $local)) { + warn "*** WARNING: Could not make symlink $local: $!\n"; + next; + } + + $MDB{$remote} = $local; + } + + # + # Now delete the ones that we symlinked previously, but are + # now no longer in the mount set (as told to us by the TMCD). + # Note, we cannot delete them directly from MDB since that + # would mess up the foreach loop, so just stick them in temp + # and postpass it. + # + while (($remote, $local) = each %MDB) { + if (defined($sfsmounts{$remote})) { + next; + } + + if (! -e $local) { + $sfsdeletes{$remote} = $local; + next; + } + + print STDOUT " Deleting symlink $local\n"; + if (! unlink($local)) { + warn "*** WARNING: Could not delete $local: $!\n"; + next; + } + + # + # Only delete from set if we can actually unlink it. This way + # we can retry it later (or next time). + # + $sfsdeletes{$remote} = $local; + } + while (($remote, $local) = each %sfsdeletes) { + delete($MDB{$remote}); + } + + # Write the DB back out! + dbmclose(%MDB); + } + else { + # There were no SFS mounts reported, so disable SFS + $usesfs = 0; + } + + return 0; +} + +# +# Do SFS hostid setup. +# Creates an SFS host key for this node, if it doesn't already exist, +# and sends it to TMCD +# +sub dosfshostid () +{ + my $TM; + my $myhostid; + + # Do I already have a host key? +# if (-e "/etc/sfs/sfs_host_key") { +# print STDOUT " This node already has a host key\n"; +# } +# else { +# # Create my host key +# if (! -e "/etc/sfs") { +# if (! os_mkdir("/etc/sfs", 0755)) { +# warn "*** WARNING: Could not make directory /etc/sfs: $!\n"; +# } +# } + +# if (! -e "/etc/sfs/sfs_host_key") { +# print STDOUT " Creating SFS host key\n"; +# if (system("sfskey gen -KPn $vname.$eid.$pid ". +# "/etc/sfs/sfs_host_key")) { +# warn "*** WARNING: Could not generate SFS host key: $!\n"; +# $USESFS = 0; +# return 1; +# } +# } +# } + if (! -e "/etc/sfs/sfs_host_key") { + warn "*** This node does not have a host key, skipping SFS stuff\n"; + $USESFS = 0; + return 1; + } + + # Start SFS server and client (this has to be done now, as opposed + # to in an rc script, because the hostkey has to exist) +# if (system($SFSSD)) { +# warn "*** Failed to start sfssd\n"; +# $USESFS = 0; +# return 1; +# } +# if (system($SFSCD)) { +# warn "*** Failed to start sfscd\n"; +# $USESFS = 0; +# return 1; +# } + + # Give hostid to TMCD + # IAMHERE: sfssd needs to be running for this call to succeed + # Directory needs to exist on fs/proj + open(SFSKEY, "sfskey hostid - |") + or die "Cannot start sfskey"; + $myhostid = <SFSKEY>; + close(SFSKEY); + if (defined($myhostid)) { + if ( $myhostid =~ /^([-\.\w_]*:[a-z0-9]*)$/ ) { + $myhostid = $1; + print STDOUT " Hostid: $myhostid\n"; + RUNTMCC(TMCCCMD_SFSHOSTID, "$myhostid"); + } + else { + warn "*** WARNING: Invalid hostid\n"; + } + } + else { + warn "*** WARNING: Could not retrieve this node's hostid (is sfssd running?)\n"; + $USESFS = 0; + } + + return 0; +} + +# +# Create SFS "mounts" (which are really just symlinks). The remote side +# of the mount is relative to /sfs and includes the server hostname, the +# server's hostid, and the path on the server. The local side is simply +# the path relative to / to symlink to the remote sfs directory. This +# closely follows domounts() +# +sub dosfsmounts() +{ + my $TM; + my %MBD; + my %mounts; + my %deletes; + + die("*** Don't call dosfsmounts!"); + + $TM = OPENTMCC(TMCCCMD_SFSMOUNTS); + + while (<$TM>) { + if ($_ =~ /REMOTE=([-:\@\w\.\/]+) LOCAL=([-\@\w\.\/]+)/) { + $mounts{$1} = $2; + } + } + CLOSETMCC($TM); + + dbmopen(%MDB, TMSFSMOUNTDB, 0660); + + # + # First symlink all the mounts we are told to. For each one that is + # not currently symlinked, and can be, add it to the DB. + # + while (($remote, $local) = each %mounts) { + if (-l $local && readlink($local) eq ("/sfs/" . $remote)) { + $MDB{$remote} = $local; + next; + } + if ( ! -l $local ) { + print STDOUT " Unlinking incorrect symlink $local\n"; + if ( ! unlink($local)) { + warn "*** WARNING: Could not unlink $local: $!\n"; + next; + } + } + + $dir = $local; + $dir =~ s/(.*)\/[^\/]*$/$1/; + if (! -e $dir) { + print STDOUT " Making directory $dir\n"; + if (! os_mkdir($dir, 755)) { + warn "*** WARNING: Could not make directory $local: $!\n"; + next; + } + } + print STDOUT " Symlinking $remote on $local\n"; + if (! symlink("/sfs/" . $remote, $local)) { + warn "*** WARNING: Could not make symlink $local: $!\n"; + next; + } + + $MDB{$remote} = $local; + } + + # + # Now delete the ones that we symlinked previously, but are now no + # longer in the mount set (as told to us by the TMCD). Note, we + # cannot delete them directly from MDB since that would mess up the + # foreach loop, so just stick them in temp and postpass it. + # + while (($remote, $local) = each %MDB) { + if (defined($mounts{$remote})) { + next; + } + + if (! -e $local) { + $deletes{$remote} = $local; + next; + } + + print STDOUT " Deleting symlink $local\n"; + if (! unlink($local)) { + warn "*** WARNING: Could not delete $local: $!\n"; + next; + } + + # + # Only delete from set if we can actually unlink it. This way + # we can retry it later (or next time). + # + $deletes{$remote} = $local; + } + while (($remote, $local) = each %deletes) { + delete($MDB{$remote}); + } + + # Write the DB back out! + dbmclose(%MDB); + return 0; } @@ -696,6 +979,7 @@ sub doaccounts() my %newaccounts = (); my %newgroups = (); my %pubkeys = (); + my @sfskeys = (); my %deletes = (); my %lastmod = (); my %PWDDB; @@ -740,6 +1024,13 @@ sub doaccounts() push(@{$pubkeys{$1}}, $2); next; } + elsif ($_ =~ /^SFSKEY KEY="(.*)"/) { + # + # SFS key goes into the array. + # + push(@sfskeys, $1); + next; + } else { warn "*** WARNING: Bad accounts line: $_\n"; } @@ -966,7 +1257,7 @@ sub doaccounts() next; } } - + if (!open(AUTHKEYS, "> $sshdir/authorized_keys.new")) { warn("*** WARNING: Could not open $sshdir/keys.new: $!\n"); next; @@ -1016,6 +1307,78 @@ sub doaccounts() # Write the DB back out! dbmclose(%PWDDB); + # + # Create sfs_users file and populate it with public SFS keys + # + do { + if (!open(SFSKEYS, "> /etc/sfs/sfs_users.new")) { + warn("*** WARNING: Could not open /etc/sfs/sfs_users.new: $!\n"); + next; + } + + print SFSKEYS "#\n"; + print SFSKEYS "# DO NOT EDIT! This file auto generated by ". + "Emulab.Net account software.\n"; + print SFSKEYS "#\n"; + print SFSKEYS "# Please use the web interface to edit your ". + "SFS public key list.\n"; + print SFSKEYS "#\n"; + foreach my $key (@sfskeys) { + print SFSKEYS "$key\n"; + } + close(SFSKEYS); + + # Because sfs_users only contains public keys, sfs_users.pub is + # exactly the same + if (system("cp -p -f /etc/sfs/sfs_users.new ". + "/etc/sfs/sfs_users.pub.new")) { + warn("*** Could not copy /etc/sfs/sfs_users.new to ". + "sfs_users.pub.new: $!\n"); + next; + } + + if (!chown(0, 0, "/etc/sfs/sfs_users.new")) { + warn("*** WARNING: Could not chown /etc/sfs/sfs_users.new: $!\n"); + next; + } + if (!chmod(0600, "/etc/sfs/sfs_users.new")) { + warn("*** WARNING: Could not chmod /etc/sfs/sfs_users.new: $!\n"); + next; + } + + if (!chown(0, 0, "/etc/sfs/sfs_users.pub.new")) { + warn("*** WARNING: Could not chown /etc/sfs/sfs_users.pub.new: $!\n"); + next; + } + if (!chmod(0644, "/etc/sfs/sfs_users.pub.new")) { + warn("*** WARNING: Could not chmod /etc/sfs/sfs_users.pub.new: $!\n"); + next; + } + + # Save off old key files and move in new ones + foreach my $keyfile ("/etc/sfs/sfs_users", + "/etc/sfs/sfs_users.pub") { + if (-e $keyfile) { + if (system("cp -p -f $keyfile $keyfile.old")) { + warn("*** Could not save off $keyfile: $!\n"); + next; + } + if (!chown(0, 0, "$keyfile")) { + warn("*** Could not chown $keyfile: $!\n"); + } + if (!chmod(0600, "$keyfile")) { + warn("*** Could not chmod $keyfile: $!\n"); + } + } + if (system("mv -f $keyfile.new $keyfile")) { + warn("*** Could not mv $keyfile: ~!\n"); + } + } + + # The do-while is an easy way out in case of errors + } + while 0; + return 0; } @@ -1469,8 +1832,17 @@ sub bootsetup() # create_nicknames(); + if ($USESFS && ! MFS()) { + # + # Setup SFS hostid. + # + print STDOUT "Creating node SFS hostid ... \n"; + dosfshostid(); + } + # - # Mount the project and user directories + # Mount the project and user directories and symlink SFS "mounted" + # directories # print STDOUT "Mounting project and home directories ... \n"; domounts(); @@ -1563,8 +1935,17 @@ sub nodeupdate() return 0; } + if ($USESFS && ! MFS()) { + # + # Setup SFS hostid. + # + print STDOUT "Creating node SFS hostid ... \n"; + dosfshostid(); + } + # - # Mount the project and user directories + # Mount the project and user directories and symlink SFS "mounted" + # directories # print STDOUT "Mounting project and home directories ... \n"; domounts(); diff --git a/tmcd/tmcd.c b/tmcd/tmcd.c index 6647c935f2f6e62ad9061bce40c1334666e03122..0679d45ecf10fab08ab9a5857fcfc84aacdc6f49 100644 --- a/tmcd/tmcd.c +++ b/tmcd/tmcd.c @@ -21,6 +21,7 @@ #include <sys/wait.h> #include <sys/fcntl.h> #include <paths.h> +#include <setjmp.h> #include <mysql/mysql.h> #include "decls.h" #include "config.h" @@ -43,12 +44,15 @@ #define USERDIR "/users" #define RELOADPID "emulab-ops" #define RELOADEID "reloading" +//#define FSHOSTID "/usr/testbed/etc/fshostid" +#define FSHOSTID "./fshostid" #define TESTMODE #define NETMASK "255.255.255.0" /* Defined in configure and passed in via the makefile */ #define DBNAME_SIZE 64 +#define HOSTID_SIZE (32+64) #define DEFAULT_DBNAME TBDBNAME int debug = 0; @@ -56,6 +60,7 @@ static int insecure = 0; static int portnum = TBSERVER_PORT; static char dbname[DBNAME_SIZE]; static struct in_addr myipaddr; +static char fshostid[HOSTID_SIZE]; static int nodeidtoexp(char *nodeid, char *pid, char *eid, char *gid); static int iptonodeid(struct in_addr, char *,char *,char *,char *,int *); static int nodeidtonickname(char *nodeid, char *nickname); @@ -106,6 +111,8 @@ COMMAND_PROTOTYPE(doready); COMMAND_PROTOTYPE(doreadycount); COMMAND_PROTOTYPE(dolog); COMMAND_PROTOTYPE(domounts); +COMMAND_PROTOTYPE(dosfshostid); +//COMMAND_PROTOTYPE(dosfsmounts); COMMAND_PROTOTYPE(doloadinfo); COMMAND_PROTOTYPE(doreset); COMMAND_PROTOTYPE(dorouting); @@ -139,6 +146,8 @@ struct command { { "ready", doready }, { "log", dolog }, { "mounts", domounts }, + { "sfshostid", dosfshostid }, +// { "sfsmounts", dosfsmounts }, { "loadinfo", doloadinfo}, { "reset", doreset}, { "routing", dorouting}, @@ -241,6 +250,29 @@ main(int argc, char **argv) info("daemon starting (version %d)\n", CURRENT_VERSION); info("%s\n", build_info); + /* + * Get FS's SFS hostid + * XXX This approach is somewhat kludgy + */ + strcpy(fshostid, ""); + if (access(FSHOSTID,R_OK) == 0) { + fp = fopen(FSHOSTID, "r"); + if (!fp) { + error("Failed to get FS's hostid"); + } + else { + fgets(fshostid, HOSTID_SIZE, fp); + if (rindex(fshostid, '\n')) { + *rindex(fshostid, '\n') = 0; + } + else { + error("fshostid from %s may be corrupt: %s", + FSHOSTID, fshostid); + } + fclose(fp); + } + } + /* * Grab our IP for security check below. */ @@ -1073,8 +1105,9 @@ COMMAND_PROTOTYPE(doaccounts) row = mysql_fetch_row(res); while (nrows) { MYSQL_ROW nextrow; - MYSQL_RES *pubkeys_res; - int pubkeys_nrows, i, root = 0; + MYSQL_RES *pubkeys_res; + MYSQL_RES *sfskeys_res; + int pubkeys_nrows, sfskeys_nrows, i, root = 0; int auxgids[128], gcount = 0; char glist[BUFSIZ]; @@ -1213,6 +1246,40 @@ COMMAND_PROTOTYPE(doaccounts) } } mysql_free_result(pubkeys_res); + + if (vers < 6) + goto skipkeys; + + /* + * Need a list of SFS keys for this user. + */ + sfskeys_res = mydb_query("select comment,pubkey " + " from user_sfskeys " + "where uid='%s'", + 2, row[0]); + + if (!sfskeys_res) { + error("ACCOUNTS: %s: DB Error getting SFS keys\n", row[0]); + goto skipkeys; + } + if ((sfskeys_nrows = (int)mysql_num_rows(sfskeys_res))) { + while (sfskeys_nrows) { + MYSQL_ROW sfskey_row; + + sfskey_row = mysql_fetch_row(sfskeys_res); + + sprintf(buf, "SFSKEY KEY=\"%s\"\n", + sfskey_row[1]); + + client_writeback(sock, buf, strlen(buf), tcp); + sfskeys_nrows--; + + info("ACCOUNTS: SFSKEY LOGIN=%s COMMENT=%s\n", + row[0], sfskey_row[0]); + } + } + mysql_free_result(sfskeys_res); + skipkeys: row = nextrow; } @@ -2018,6 +2085,8 @@ COMMAND_PROTOTYPE(domounts) char gid[64]; char buf[MYBUFSIZE]; int nrows; + int usesfs; + char *bp; /* * Now check reserved table @@ -2028,22 +2097,74 @@ COMMAND_PROTOTYPE(domounts) } /* - * Return project mount first. + * Should SFS mounts be served? */ - sprintf(buf, "REMOTE=%s/%s LOCAL=%s/%s\n", - FSPROJDIR, pid, PROJDIR, pid); - client_writeback(sock, buf, strlen(buf), tcp); + usesfs = 0; + if (vers >= 6 && strlen(fshostid)) { + while ((bp = strsep(&rdata, " ")) != NULL) { + if (sscanf(bp, "USESFS=%d", &usesfs) == 1) { + continue; + } + error("Unknown parameter to domounts: %s\n", + bp); + break; + } + + if (debug) { + if (usesfs) { + info("Using SFS\n"); + } + else { + info("Not using SFS\n"); + } + } + } + /* - * If pid!=gid, then this is group experiment, and we return - * a mount for the group directory too. + * If SFS is in use, the project mount is done via SFS. */ - if (strcmp(pid, gid)) { - sprintf(buf, "REMOTE=%s/%s/%s LOCAL=%s/%s/%s\n", - FSGROUPDIR, pid, gid, GROUPDIR, pid, gid); + if (!usesfs) { + /* + * Return project mount first. + */ + sprintf(buf, "REMOTE=%s/%s LOCAL=%s/%s\n", + FSPROJDIR, pid, PROJDIR, pid); client_writeback(sock, buf, strlen(buf), tcp); + info("MOUNTS: %s", buf); + + /* + * If pid!=gid, then this is group experiment, and we return + * a mount for the group directory too. + */ + if (strcmp(pid, gid)) { + sprintf(buf, "REMOTE=%s/%s/%s LOCAL=%s/%s/%s\n", + FSGROUPDIR, pid, gid, GROUPDIR, pid, gid); + client_writeback(sock, buf, strlen(buf), tcp); + info("MOUNTS: %s", buf); + } } + else { + /* + * Return SFS-based project mount. + */ + sprintf(buf, "SFS REMOTE=%s%s/%s LOCAL=%s/%s\n", + fshostid, FSDIR_PROJ, pid, PROJDIR, pid); + client_writeback(sock, buf, strlen(buf), tcp); + info("MOUNTS: %s", buf); + /* + * Return SFS-based group mount. + */ + if (strcmp(pid, gid)) { + sprintf(buf, "SFS REMOTE=%s%s/%s/%s LOCAL=%s/%s/%s\n", + fshostid, FSDIR_GROUPS, pid, gid, + GROUPDIR, pid, gid); + client_writeback(sock, buf, strlen(buf), tcp); + info("MOUNTS: %s", buf); + } + } + /* * Now check for aux project access. Return a list of mounts for * those projects. @@ -2115,6 +2236,126 @@ COMMAND_PROTOTYPE(domounts) return 0; } +/* + * Used by dosfshostid to make sure NFS doesn't give us problems. + * (This code really unnerves me) + */ +int sfshostiddeadfl; +jmp_buf sfshostiddeadline; +static void +dosfshostiddead() +{ + sfshostiddeadfl = 1; + longjmp(sfshostiddeadline, 1); +} + +/* + * Create dirsearch entry for node. + */ +COMMAND_PROTOTYPE(dosfshostid) +{ + char pid[64]; + char eid[64]; + char gid[64]; + char nickname[128]; + char nodehostid[HOSTID_SIZE]; + char sfspath[MYBUFSIZE], dspath[MYBUFSIZE]; + + if (!strlen(fshostid)) { + /* SFS not being used */ + info("dosfshostid: Called while SFS is not in use\n"); + return 0; + } + + /* + * Now check reserved table + */ + if (nodeidtoexp(nodeid, pid, eid, gid)) { + error("dosfshostid: %s: Node is free\n", nodeid); + return 1; + } + + if (nodeidtonickname(nodeid, nickname)) + strcpy(nickname, nodeid); + + /* XXX Make sure that the dirsearch dir exists (more NFS goop) */ + + /* + * Create symlink names + */ + if (! sscanf(rdata, "%s", nodehostid)) { + error("dosfshostid: No hostid reported!\n"); + return 1; + } + sprintf(sfspath, "/sfs/%s", nodehostid); + // sprintf(dspath, "/proj/%s/.sfs/%s/%s", pid, eid, nickname); + sprintf(dspath, "/proj/.sfs/%s.%s.%s", nickname, eid, pid); + + /* + * Really, there should be a cleaner way of doing this, but + * this works, at least for now. Perhaps using the DB and a + * symlinking deamon alone would be better. + */ + if (setjmp(sfshostiddeadline) == 0) { + sfshostiddeadfl = 0; + signal(SIGALRM, dosfshostiddead); + alarm(1); + + unlink(dspath); + if (symlink(sfspath, dspath) < 0) + sfshostiddeadfl = 1; + } + alarm(0); + if (sfshostiddeadfl) { + errorc("symlinking %s to %s", dspath, sfspath); + return 1; + } + + return 0; +} + +/* + * Return SFS-based mounts. + * XXX This is _completely_ unused, and will be removed. + */ +COMMAND_PROTOTYPE(dosfsmounts) +{ + char pid[64]; + char eid[64]; + char gid[64]; + char buf[MYBUFSIZE]; + + /* + * Now check reserved table + */ + if (nodeidtoexp(nodeid, pid, eid, gid)) { + error("MOUNTS: %s: Node is free\n", nodeid); + return 1; + } + + if (strlen(fshostid)) { + /* + * Return project mount first. + */ + sprintf(buf, "REMOTE=%s%s/%s LOCAL=%s/%s\n", + fshostid, FSDIR_PROJ, pid, PROJDIR, pid); + client_writeback(sock, buf, strlen(buf), tcp); + + /* + * If pid!= gid, then this is group experiment, and we return + * a mount for the group directory too. + */ + if (strcmp(pid, gid)) { + sprintf(buf, "REMOTE=%s%s/%s/%s LOCAL=%s/%s/%s\n", + fshostid, FSDIR_GROUPS, pid, gid, + GROUPDIR, pid, gid); + client_writeback(sock, buf, strlen(buf), tcp); + } + } + + return 0; +} + /* * Return routing stuff. */