2017-02-07 18:28:40 +00:00
# include "archive.hh"
# include "pool.hh"
# include "remote-store.hh"
# include "serve-protocol.hh"
# include "store-api.hh"
2021-03-02 03:50:41 +00:00
# include "path-with-outputs.hh"
2017-02-07 18:28:40 +00:00
# include "worker-protocol.hh"
2017-03-03 18:05:50 +00:00
# include "ssh.hh"
2017-05-01 14:08:13 +00:00
# include "derivations.hh"
2020-09-21 16:40:11 +00:00
# include "callback.hh"
2017-02-07 18:28:40 +00:00
namespace nix {
2020-09-10 08:55:51 +00:00
struct LegacySSHStoreConfig : virtual StoreConfig
2017-02-07 18:28:40 +00:00
{
2020-09-10 08:55:51 +00:00
using StoreConfig : : StoreConfig ;
2020-09-15 07:10:37 +00:00
const Setting < int > maxConnections { ( StoreConfig * ) this , 1 , " max-connections " , " maximum number of concurrent SSH connections " } ;
const Setting < Path > sshKey { ( StoreConfig * ) this , " " , " ssh-key " , " path to an SSH private key " } ;
2021-02-25 01:52:22 +00:00
const Setting < std : : string > sshPublicHostKey { ( StoreConfig * ) this , " " , " base64-ssh-public-host-key " , " The public half of the host's SSH key " } ;
2020-09-15 07:10:37 +00:00
const Setting < bool > compress { ( StoreConfig * ) this , false , " compress " , " whether to compress the connection " } ;
const Setting < Path > remoteProgram { ( StoreConfig * ) this , " nix-store " , " remote-program " , " path to the nix-store executable on the remote system " } ;
const Setting < std : : string > remoteStore { ( StoreConfig * ) this , " " , " remote-store " , " URI of the store on the remote system " } ;
2020-09-14 12:04:02 +00:00
const std : : string name ( ) override { return " Legacy SSH Store " ; }
2020-09-10 08:55:51 +00:00
} ;
2017-04-13 13:55:38 +00:00
2020-12-20 15:33:12 +00:00
struct LegacySSHStore : public virtual LegacySSHStoreConfig , public virtual Store
2020-09-10 08:55:51 +00:00
{
2017-05-02 10:01:46 +00:00
// Hack for getting remote build log output.
2020-09-15 07:10:37 +00:00
// Intentionally not in `LegacySSHStoreConfig` so that it doesn't appear in
// the documentation
const Setting < int > logFD { ( StoreConfig * ) this , - 1 , " log-fd " , " file descriptor to which SSH's stderr is connected " } ;
2017-05-02 10:01:46 +00:00
2017-02-07 18:28:40 +00:00
struct Connection
{
2017-03-03 18:05:50 +00:00
std : : unique_ptr < SSHMaster : : Connection > sshConn ;
2017-02-07 18:28:40 +00:00
FdSink to ;
FdSource from ;
2017-05-01 14:08:13 +00:00
int remoteVersion ;
2018-08-03 19:10:03 +00:00
bool good = true ;
2017-02-07 18:28:40 +00:00
} ;
2017-03-03 18:05:50 +00:00
std : : string host ;
2017-02-07 18:28:40 +00:00
ref < Pool < Connection > > connections ;
2017-03-03 18:05:50 +00:00
SSHMaster master ;
2017-02-07 18:28:40 +00:00
2020-09-11 09:11:05 +00:00
static std : : set < std : : string > uriSchemes ( ) { return { " ssh " } ; }
2020-09-08 12:50:23 +00:00
2020-09-11 09:11:05 +00:00
LegacySSHStore ( const string & scheme , const string & host , const Params & params )
2020-09-11 09:06:18 +00:00
: StoreConfig ( params )
2020-12-20 15:33:12 +00:00
, LegacySSHStoreConfig ( params )
2020-09-10 08:55:51 +00:00
, Store ( params )
2017-02-07 18:28:40 +00:00
, host ( host )
, connections ( make_ref < Pool < Connection > > (
2017-04-13 13:55:38 +00:00
std : : max ( 1 , ( int ) maxConnections ) ,
2017-02-07 18:28:40 +00:00
[ this ] ( ) { return openConnection ( ) ; } ,
2018-08-03 19:10:03 +00:00
[ ] ( const ref < Connection > & r ) { return r - > good ; }
2017-02-07 18:28:40 +00:00
) )
2017-03-03 18:05:50 +00:00
, master (
host ,
2017-04-13 13:55:38 +00:00
sshKey ,
2021-02-25 01:52:22 +00:00
sshPublicHostKey ,
2017-03-03 18:05:50 +00:00
// Use SSH master only if using more than 1 connection.
connections - > capacity ( ) > 1 ,
2017-05-02 10:01:46 +00:00
compress ,
logFD )
2017-02-07 18:28:40 +00:00
{
}
ref < Connection > openConnection ( )
{
auto conn = make_ref < Connection > ( ) ;
2018-08-03 16:07:46 +00:00
conn - > sshConn = master . startCommand (
2018-08-06 15:27:08 +00:00
fmt ( " %s --serve --write " , remoteProgram )
2018-08-03 16:07:46 +00:00
+ ( remoteStore . get ( ) = = " " ? " " : " --store " + shellEscape ( remoteStore . get ( ) ) ) ) ;
2017-03-03 18:05:50 +00:00
conn - > to = FdSink ( conn - > sshConn - > in . get ( ) ) ;
conn - > from = FdSource ( conn - > sshConn - > out . get ( ) ) ;
2017-02-07 18:28:40 +00:00
try {
conn - > to < < SERVE_MAGIC_1 < < SERVE_PROTOCOL_VERSION ;
conn - > to . flush ( ) ;
2021-09-23 15:48:52 +00:00
StringSink saved ;
try {
TeeSource tee ( conn - > from , saved ) ;
unsigned int magic = readInt ( tee ) ;
if ( magic ! = SERVE_MAGIC_2 )
throw Error ( " 'nix-store --serve' protocol mismatch from '%s' " , host ) ;
} catch ( SerialisationError & e ) {
2021-09-23 16:01:04 +00:00
/* In case the other side is waiting for our input,
close it . */
2021-09-23 15:48:52 +00:00
conn - > sshConn - > in . close ( ) ;
auto msg = conn - > from . drain ( ) ;
throw Error ( " 'nix-store --serve' protocol mismatch from '%s', got '%s' " ,
host , chomp ( * saved . s + msg ) ) ;
}
2017-05-01 14:08:13 +00:00
conn - > remoteVersion = readInt ( conn - > from ) ;
if ( GET_PROTOCOL_MAJOR ( conn - > remoteVersion ) ! = 0x200 )
2017-07-30 11:27:57 +00:00
throw Error ( " unsupported 'nix-store --serve' protocol version on '%s' " , host ) ;
2017-02-07 18:28:40 +00:00
} catch ( EndOfFile & e ) {
2017-07-30 11:27:57 +00:00
throw Error ( " cannot connect to '%1%' " , host ) ;
2017-02-07 18:28:40 +00:00
}
return conn ;
} ;
string getUri ( ) override
{
2020-09-11 09:11:05 +00:00
return * uriSchemes ( ) . begin ( ) + " :// " + host ;
2017-02-07 18:28:40 +00:00
}
2019-12-05 18:11:09 +00:00
void queryPathInfoUncached ( const StorePath & path ,
2018-09-25 16:54:16 +00:00
Callback < std : : shared_ptr < const ValidPathInfo > > callback ) noexcept override
2017-02-07 18:28:40 +00:00
{
2018-03-27 20:16:01 +00:00
try {
2017-02-07 18:28:40 +00:00
auto conn ( connections - > get ( ) ) ;
2020-08-06 18:31:48 +00:00
/* No longer support missing NAR hash */
assert ( GET_PROTOCOL_MINOR ( conn - > remoteVersion ) > = 4 ) ;
2019-12-05 18:11:09 +00:00
debug ( " querying remote host '%s' for info on '%s' " , host , printStorePath ( path ) ) ;
2017-02-07 18:28:40 +00:00
2019-12-05 18:11:09 +00:00
conn - > to < < cmdQueryPathInfos < < PathSet { printStorePath ( path ) } ;
2017-02-07 18:28:40 +00:00
conn - > to . flush ( ) ;
2019-12-05 18:11:09 +00:00
auto p = readString ( conn - > from ) ;
if ( p . empty ( ) ) return callback ( nullptr ) ;
2020-08-06 18:31:48 +00:00
auto path2 = parseStorePath ( p ) ;
assert ( path = = path2 ) ;
/* Hash will be set below. FIXME construct ValidPathInfo at end. */
auto info = std : : make_shared < ValidPathInfo > ( path , Hash : : dummy ) ;
2017-02-07 18:28:40 +00:00
PathSet references ;
2019-12-05 18:11:09 +00:00
auto deriver = readString ( conn - > from ) ;
if ( deriver ! = " " )
info - > deriver = parseStorePath ( deriver ) ;
2020-09-30 00:41:18 +00:00
info - > references = worker_proto : : read ( * this , conn - > from , Phantom < StorePathSet > { } ) ;
2017-02-07 18:28:40 +00:00
readLongLong ( conn - > from ) ; // download size
info - > narSize = readLongLong ( conn - > from ) ;
2020-08-06 18:31:48 +00:00
{
2017-09-08 14:55:27 +00:00
auto s = readString ( conn - > from ) ;
2020-08-06 18:31:48 +00:00
if ( s = = " " )
throw Error ( " NAR hash is now mandatory " ) ;
info - > narHash = Hash : : parseAnyPrefixed ( s ) ;
2017-09-08 14:55:27 +00:00
}
2020-08-06 18:31:48 +00:00
info - > ca = parseContentAddressOpt ( readString ( conn - > from ) ) ;
info - > sigs = readStrings < StringSet > ( conn - > from ) ;
2017-09-08 14:55:27 +00:00
2017-02-07 18:28:40 +00:00
auto s = readString ( conn - > from ) ;
assert ( s = = " " ) ;
2018-03-27 20:16:01 +00:00
callback ( std : : move ( info ) ) ;
} catch ( . . . ) { callback . rethrow ( ) ; }
2017-02-07 18:28:40 +00:00
}
2018-03-21 22:12:22 +00:00
void addToStore ( const ValidPathInfo & info , Source & source ,
2020-07-13 15:37:44 +00:00
RepairFlag repair , CheckSigsFlag checkSigs ) override
2017-02-07 18:28:40 +00:00
{
2019-12-05 18:11:09 +00:00
debug ( " adding path '%s' to remote host '%s' " , printStorePath ( info . path ) , host ) ;
2017-02-07 18:28:40 +00:00
auto conn ( connections - > get ( ) ) ;
2018-08-30 23:00:01 +00:00
if ( GET_PROTOCOL_MINOR ( conn - > remoteVersion ) > = 5 ) {
2018-08-03 19:10:03 +00:00
conn - > to
< < cmdAddToStoreNar
2019-12-05 18:11:09 +00:00
< < printStorePath ( info . path )
< < ( info . deriver ? printStorePath ( * info . deriver ) : " " )
2020-08-05 18:42:48 +00:00
< < info . narHash . to_string ( Base16 , false ) ;
2020-09-30 00:41:18 +00:00
worker_proto : : write ( * this , conn - > to , info . references ) ;
2019-12-05 18:11:09 +00:00
conn - > to
2018-08-03 19:10:03 +00:00
< < info . registrationTime
< < info . narSize
< < info . ultimate
< < info . sigs
2020-06-02 00:37:43 +00:00
< < renderContentAddress ( info . ca ) ;
2018-08-03 19:10:03 +00:00
try {
copyNAR ( source , conn - > to ) ;
} catch ( . . . ) {
conn - > good = false ;
throw ;
}
conn - > to . flush ( ) ;
} else {
conn - > to
< < cmdImportPaths
< < 1 ;
try {
copyNAR ( source , conn - > to ) ;
} catch ( . . . ) {
conn - > good = false ;
throw ;
}
conn - > to
< < exportMagic
2019-12-05 18:11:09 +00:00
< < printStorePath ( info . path ) ;
2020-09-30 00:41:18 +00:00
worker_proto : : write ( * this , conn - > to , info . references ) ;
2019-12-05 18:11:09 +00:00
conn - > to
< < ( info . deriver ? printStorePath ( * info . deriver ) : " " )
2018-08-03 19:10:03 +00:00
< < 0
< < 0 ;
conn - > to . flush ( ) ;
}
2017-02-07 18:28:40 +00:00
if ( readInt ( conn - > from ) ! = 1 )
2019-12-05 18:11:09 +00:00
throw Error ( " failed to add path '%s' to remote host '%s' " , printStorePath ( info . path ) , host ) ;
2017-02-07 18:28:40 +00:00
}
2019-12-05 18:11:09 +00:00
void narFromPath ( const StorePath & path , Sink & sink ) override
2017-02-07 18:28:40 +00:00
{
auto conn ( connections - > get ( ) ) ;
2019-12-05 18:11:09 +00:00
conn - > to < < cmdDumpStorePath < < printStorePath ( path ) ;
2017-02-07 18:28:40 +00:00
conn - > to . flush ( ) ;
2018-03-21 21:56:02 +00:00
copyNAR ( conn - > from , sink ) ;
2017-02-07 18:28:40 +00:00
}
2019-12-05 18:11:09 +00:00
std : : optional < StorePath > queryPathFromHashPart ( const std : : string & hashPart ) override
2019-01-18 12:34:23 +00:00
{ unsupported ( " queryPathFromHashPart " ) ; }
2017-02-07 18:28:40 +00:00
2019-12-05 18:11:09 +00:00
StorePath addToStore ( const string & name , const Path & srcPath ,
2020-03-29 05:04:55 +00:00
FileIngestionMethod method , HashType hashAlgo ,
2017-06-28 16:11:01 +00:00
PathFilter & filter , RepairFlag repair ) override
2019-01-18 12:34:23 +00:00
{ unsupported ( " addToStore " ) ; }
2017-02-07 18:28:40 +00:00
2019-12-05 18:11:09 +00:00
StorePath addTextToStore ( const string & name , const string & s ,
const StorePathSet & references , RepairFlag repair ) override
2019-01-18 12:34:23 +00:00
{ unsupported ( " addTextToStore " ) ; }
2017-02-07 18:28:40 +00:00
2020-08-12 03:13:17 +00:00
private :
void putBuildSettings ( Connection & conn )
{
conn . to
< < settings . maxSilentTime
< < settings . buildTimeout ;
if ( GET_PROTOCOL_MINOR ( conn . remoteVersion ) > = 2 )
conn . to
< < settings . maxLogSize ;
if ( GET_PROTOCOL_MINOR ( conn . remoteVersion ) > = 3 )
conn . to
< < settings . buildRepeat
< < settings . enforceDeterminism ;
}
public :
2019-12-05 18:11:09 +00:00
BuildResult buildDerivation ( const StorePath & drvPath , const BasicDerivation & drv ,
2017-02-07 18:28:40 +00:00
BuildMode buildMode ) override
2017-05-01 14:08:13 +00:00
{
auto conn ( connections - > get ( ) ) ;
conn - > to
< < cmdBuildDerivation
2019-12-05 18:11:09 +00:00
< < printStorePath ( drvPath ) ;
writeDerivation ( conn - > to , * this , drv ) ;
2020-08-12 03:13:17 +00:00
putBuildSettings ( * conn ) ;
2017-05-01 14:08:13 +00:00
conn - > to . flush ( ) ;
BuildResult status ;
status . status = ( BuildResult : : Status ) readInt ( conn - > from ) ;
conn - > from > > status . errorMsg ;
if ( GET_PROTOCOL_MINOR ( conn - > remoteVersion ) > = 3 )
conn - > from > > status . timesBuilt > > status . isNonDeterministic > > status . startTime > > status . stopTime ;
2021-01-26 09:50:44 +00:00
if ( GET_PROTOCOL_MINOR ( conn - > remoteVersion ) > = 6 ) {
status . builtOutputs = worker_proto : : read ( * this , conn - > from , Phantom < DrvOutputs > { } ) ;
}
2017-05-01 14:08:13 +00:00
return status ;
}
2017-02-07 18:28:40 +00:00
2021-07-19 13:43:08 +00:00
void buildPaths ( const std : : vector < DerivedPath > & drvPaths , BuildMode buildMode , std : : shared_ptr < Store > evalStore ) override
2020-08-12 03:13:17 +00:00
{
2021-07-19 13:43:08 +00:00
if ( evalStore & & evalStore . get ( ) ! = this )
throw Error ( " building on an SSH store is incompatible with '--eval-store' " ) ;
2020-08-12 03:13:17 +00:00
auto conn ( connections - > get ( ) ) ;
conn - > to < < cmdBuildPaths ;
Strings ss ;
2021-03-02 03:50:41 +00:00
for ( auto & p : drvPaths ) {
2021-04-05 13:48:18 +00:00
auto sOrDrvPath = StorePathWithOutputs : : tryFromDerivedPath ( p ) ;
2021-03-02 03:50:41 +00:00
std : : visit ( overloaded {
[ & ] ( StorePathWithOutputs s ) {
ss . push_back ( s . to_string ( * this ) ) ;
} ,
[ & ] ( StorePath drvPath ) {
throw Error ( " wanted to fetch '%s' but the legacy ssh protocol doesn't support merely substituting drv files via the build paths command. It would build them instead. Try using ssh-ng:// " , printStorePath ( drvPath ) ) ;
} ,
} , sOrDrvPath ) ;
}
2020-08-12 03:13:17 +00:00
conn - > to < < ss ;
putBuildSettings ( * conn ) ;
conn - > to . flush ( ) ;
BuildResult result ;
result . status = ( BuildResult : : Status ) readInt ( conn - > from ) ;
if ( ! result . success ( ) ) {
conn - > from > > result . errorMsg ;
throw Error ( result . status , result . errorMsg ) ;
}
}
2019-12-05 18:11:09 +00:00
void ensurePath ( const StorePath & path ) override
2019-01-18 12:34:23 +00:00
{ unsupported ( " ensurePath " ) ; }
2017-02-07 18:28:40 +00:00
2019-12-05 18:11:09 +00:00
void computeFSClosure ( const StorePathSet & paths ,
StorePathSet & out , bool flipDirection = false ,
2017-03-16 10:44:01 +00:00
bool includeOutputs = false , bool includeDerivers = false ) override
{
if ( flipDirection | | includeDerivers ) {
Store : : computeFSClosure ( paths , out , flipDirection , includeOutputs , includeDerivers ) ;
return ;
}
auto conn ( connections - > get ( ) ) ;
conn - > to
< < cmdQueryClosure
2019-12-05 18:11:09 +00:00
< < includeOutputs ;
2020-09-30 00:41:18 +00:00
worker_proto : : write ( * this , conn - > to , paths ) ;
2017-03-16 10:44:01 +00:00
conn - > to . flush ( ) ;
2020-09-30 00:41:18 +00:00
for ( auto & i : worker_proto : : read ( * this , conn - > from , Phantom < StorePathSet > { } ) )
2020-06-16 20:20:18 +00:00
out . insert ( i ) ;
2017-03-16 10:44:01 +00:00
}
2019-12-05 18:11:09 +00:00
StorePathSet queryValidPaths ( const StorePathSet & paths ,
2017-06-28 16:11:01 +00:00
SubstituteFlag maybeSubstitute = NoSubstitute ) override
2017-03-16 12:50:01 +00:00
{
auto conn ( connections - > get ( ) ) ;
conn - > to
< < cmdQueryValidPaths
< < false // lock
2019-12-05 18:11:09 +00:00
< < maybeSubstitute ;
2020-09-30 00:41:18 +00:00
worker_proto : : write ( * this , conn - > to , paths ) ;
2017-03-16 12:50:01 +00:00
conn - > to . flush ( ) ;
2020-09-30 00:41:18 +00:00
return worker_proto : : read ( * this , conn - > from , Phantom < StorePathSet > { } ) ;
2017-03-16 12:50:01 +00:00
}
2017-05-02 12:18:46 +00:00
void connect ( ) override
{
auto conn ( connections - > get ( ) ) ;
}
2018-08-30 21:28:47 +00:00
unsigned int getProtocol ( ) override
{
auto conn ( connections - > get ( ) ) ;
return conn - > remoteVersion ;
}
2020-10-08 15:36:51 +00:00
std : : optional < const Realisation > queryRealisation ( const DrvOutput & ) override
// TODO: Implement
{ unsupported ( " queryRealisation " ) ; }
2017-02-07 18:28:40 +00:00
} ;
2020-10-06 11:36:55 +00:00
static RegisterStoreImplementation < LegacySSHStore , LegacySSHStoreConfig > regLegacySSHStore ;
2017-02-07 18:28:40 +00:00
}