2023-12-10 21:28:14 +02:00
# include "legacy-ssh-store.hh"
2023-03-22 15:23:36 +02:00
# include "ssh-store-config.hh"
2017-02-07 20:28:40 +02:00
# include "archive.hh"
# include "pool.hh"
# include "remote-store.hh"
# include "serve-protocol.hh"
2023-05-26 21:11:08 +03:00
# include "serve-protocol-impl.hh"
2022-03-01 21:43:07 +02:00
# include "build-result.hh"
2017-02-07 20:28:40 +02:00
# include "store-api.hh"
2021-03-02 05:50:41 +02:00
# include "path-with-outputs.hh"
2017-03-03 20:05:50 +02:00
# include "ssh.hh"
2017-05-01 17:08:13 +03:00
# include "derivations.hh"
2020-09-21 19:40:11 +03:00
# include "callback.hh"
2017-02-07 20:28:40 +02:00
namespace nix {
2023-12-10 21:28:14 +02:00
std : : string LegacySSHStoreConfig : : doc ( )
2017-02-07 20:28:40 +02:00
{
2023-12-10 21:28:14 +02:00
return
# include "legacy-ssh-store.md"
;
}
2023-03-22 15:23:36 +02:00
2023-03-23 10:35:35 +02:00
2022-02-20 21:24:07 +02:00
struct LegacySSHStore : : Connection : public ServeProto : : BasicClientConnection
2023-12-10 21:28:14 +02:00
{
std : : unique_ptr < SSHMaster : : Connection > sshConn ;
bool good = true ;
2020-09-10 11:55:51 +03:00
} ;
2017-04-13 16:55:38 +03:00
2023-12-10 21:28:14 +02:00
LegacySSHStore : : LegacySSHStore ( const std : : string & scheme , const std : : string & host , const Params & params )
: StoreConfig ( params )
, CommonSSHStoreConfig ( params )
, LegacySSHStoreConfig ( params )
, Store ( params )
, host ( host )
, connections ( make_ref < Pool < Connection > > (
std : : max ( 1 , ( int ) maxConnections ) ,
[ this ] ( ) { return openConnection ( ) ; } ,
[ ] ( const ref < Connection > & r ) { return r - > good ; }
) )
, master (
host ,
sshKey ,
sshPublicHostKey ,
// Use SSH master only if using more than 1 connection.
connections - > capacity ( ) > 1 ,
compress ,
logFD )
2020-09-10 11:55:51 +03:00
{
2023-12-10 21:28:14 +02:00
}
2017-05-02 13:01:46 +03:00
2023-04-17 20:40:46 +03:00
2023-12-10 21:28:14 +02:00
ref < LegacySSHStore : : Connection > LegacySSHStore : : openConnection ( )
{
auto conn = make_ref < Connection > ( ) ;
conn - > sshConn = master . startCommand (
fmt ( " %s --serve --write " , remoteProgram )
+ ( remoteStore . get ( ) = = " " ? " " : " --store " + shellEscape ( remoteStore . get ( ) ) ) ) ;
conn - > to = FdSink ( conn - > sshConn - > in . get ( ) ) ;
conn - > from = FdSource ( conn - > sshConn - > out . get ( ) ) ;
try {
conn - > to < < SERVE_MAGIC_1 < < SERVE_PROTOCOL_VERSION ;
conn - > to . flush ( ) ;
2017-02-07 20:28:40 +02:00
2023-12-10 21:28:14 +02:00
StringSink saved ;
2017-02-07 20:28:40 +02:00
try {
2023-12-10 21:28:14 +02:00
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 ) {
/* In case the other side is waiting for our input,
close it . */
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-02-07 20:28:40 +02:00
}
2023-12-10 21:28:14 +02:00
conn - > remoteVersion = readInt ( conn - > from ) ;
if ( GET_PROTOCOL_MAJOR ( conn - > remoteVersion ) ! = 0x200 )
throw Error ( " unsupported 'nix-store --serve' protocol version on '%s' " , host ) ;
2017-02-07 20:28:40 +02:00
2023-12-10 21:28:14 +02:00
} catch ( EndOfFile & e ) {
throw Error ( " cannot connect to '%1%' " , host ) ;
2017-02-07 20:28:40 +02:00
}
2023-12-10 21:28:14 +02:00
return conn ;
} ;
2017-02-07 20:28:40 +02:00
2023-12-10 21:28:14 +02:00
std : : string LegacySSHStore : : getUri ( )
{
return * uriSchemes ( ) . begin ( ) + " :// " + host ;
}
2023-12-07 17:49:29 +02:00
2017-09-08 17:55:27 +03:00
2023-12-10 21:28:14 +02:00
void LegacySSHStore : : queryPathInfoUncached ( const StorePath & path ,
Callback < std : : shared_ptr < const ValidPathInfo > > callback ) noexcept
{
try {
auto conn ( connections - > get ( ) ) ;
2017-02-07 20:28:40 +02:00
2023-12-10 21:28:14 +02:00
/* No longer support missing NAR hash */
assert ( GET_PROTOCOL_MINOR ( conn - > remoteVersion ) > = 4 ) ;
2017-02-07 20:28:40 +02:00
2023-12-10 21:28:14 +02:00
debug ( " querying remote host '%s' for info on '%s' " , host , printStorePath ( path ) ) ;
2017-02-07 20:28:40 +02:00
2023-12-10 21:28:14 +02:00
conn - > to < < ServeProto : : Command : : QueryPathInfos < < PathSet { printStorePath ( path ) } ;
conn - > to . flush ( ) ;
2017-02-07 20:28:40 +02:00
2023-12-10 21:28:14 +02:00
auto p = readString ( conn - > from ) ;
if ( p . empty ( ) ) return callback ( nullptr ) ;
auto path2 = parseStorePath ( p ) ;
assert ( path = = path2 ) ;
auto info = std : : make_shared < ValidPathInfo > (
path ,
ServeProto : : Serialise < UnkeyedValidPathInfo > : : read ( * this , * conn ) ) ;
2018-08-03 22:10:03 +03:00
2023-12-10 21:28:14 +02:00
if ( info - > narHash = = Hash : : dummy )
throw Error ( " NAR hash is now mandatory " ) ;
2017-02-07 20:28:40 +02:00
2023-12-10 21:28:14 +02:00
auto s = readString ( conn - > from ) ;
assert ( s = = " " ) ;
2017-02-07 20:28:40 +02:00
2023-12-10 21:28:14 +02:00
callback ( std : : move ( info ) ) ;
} catch ( . . . ) { callback . rethrow ( ) ; }
}
2017-02-07 20:28:40 +02:00
2023-12-10 21:28:14 +02:00
void LegacySSHStore : : addToStore ( const ValidPathInfo & info , Source & source ,
RepairFlag repair , CheckSigsFlag checkSigs )
{
debug ( " adding path '%s' to remote host '%s' " , printStorePath ( info . path ) , host ) ;
2020-08-12 06:13:17 +03:00
2023-12-10 21:28:14 +02:00
auto conn ( connections - > get ( ) ) ;
2020-08-12 06:13:17 +03:00
2023-12-10 21:28:14 +02:00
if ( GET_PROTOCOL_MINOR ( conn - > remoteVersion ) > = 5 ) {
2017-05-01 17:08:13 +03:00
conn - > to
2023-12-10 21:28:14 +02:00
< < ServeProto : : Command : : AddToStoreNar
< < printStorePath ( info . path )
< < ( info . deriver ? printStorePath ( * info . deriver ) : " " )
< < info . narHash . to_string ( HashFormat : : Base16 , false ) ;
ServeProto : : write ( * this , * conn , info . references ) ;
conn - > to
< < info . registrationTime
< < info . narSize
< < info . ultimate
< < info . sigs
< < renderContentAddress ( info . ca ) ;
try {
copyNAR ( source , conn - > to ) ;
} catch ( . . . ) {
conn - > good = false ;
throw ;
}
conn - > to . flush ( ) ;
2020-08-12 06:13:17 +03:00
2023-12-10 21:28:14 +02:00
} else {
2017-05-01 17:08:13 +03:00
2023-12-10 21:28:14 +02:00
conn - > to
< < ServeProto : : Command : : ImportPaths
< < 1 ;
try {
copyNAR ( source , conn - > to ) ;
} catch ( . . . ) {
conn - > good = false ;
throw ;
}
conn - > to
< < exportMagic
< < printStorePath ( info . path ) ;
ServeProto : : write ( * this , * conn , info . references ) ;
conn - > to
< < ( info . deriver ? printStorePath ( * info . deriver ) : " " )
< < 0
< < 0 ;
2017-05-01 17:08:13 +03:00
conn - > to . flush ( ) ;
}
2017-02-07 20:28:40 +02:00
2023-12-10 21:28:14 +02:00
if ( readInt ( conn - > from ) ! = 1 )
throw Error ( " failed to add path '%s' to remote host '%s' " , printStorePath ( info . path ) , host ) ;
}
2021-07-19 16:43:08 +03:00
2020-08-12 06:13:17 +03:00
2023-12-10 21:28:14 +02:00
void LegacySSHStore : : narFromPath ( const StorePath & path , Sink & sink )
{
auto conn ( connections - > get ( ) ) ;
2020-08-12 06:13:17 +03:00
2023-12-10 21:28:14 +02:00
conn - > to < < ServeProto : : Command : : DumpStorePath < < printStorePath ( path ) ;
conn - > to . flush ( ) ;
copyNAR ( conn - > from , sink ) ;
}
2020-08-12 06:13:17 +03:00
2022-02-20 21:24:07 +02:00
static ServeProto : : BuildOptions buildSettings ( )
2023-12-10 21:28:14 +02:00
{
2022-02-20 21:24:07 +02:00
return {
2023-12-10 21:28:14 +02:00
. maxSilentTime = settings . maxSilentTime ,
. buildTimeout = settings . buildTimeout ,
. maxLogSize = settings . maxLogSize ,
. nrRepeats = 0 , // buildRepeat hasn't worked for ages anyway
. enforceDeterminism = 0 ,
. keepFailed = settings . keepFailed ,
2022-02-20 21:24:07 +02:00
} ;
2023-12-10 21:28:14 +02:00
}
2020-08-12 06:13:17 +03:00
2023-12-10 21:28:14 +02:00
BuildResult LegacySSHStore : : buildDerivation ( const StorePath & drvPath , const BasicDerivation & drv ,
BuildMode buildMode )
{
auto conn ( connections - > get ( ) ) ;
2017-02-07 20:28:40 +02:00
2022-02-20 21:24:07 +02:00
conn - > putBuildDerivationRequest ( * this , drvPath , drv , buildSettings ( ) ) ;
2017-03-16 12:44:01 +02:00
2023-12-10 21:28:14 +02:00
return ServeProto : : Serialise < BuildResult > : : read ( * this , * conn ) ;
}
2017-03-16 12:44:01 +02:00
2023-12-10 21:28:14 +02:00
void LegacySSHStore : : buildPaths ( const std : : vector < DerivedPath > & drvPaths , BuildMode buildMode , std : : shared_ptr < Store > evalStore )
{
if ( evalStore & & evalStore . get ( ) ! = this )
throw Error ( " building on an SSH store is incompatible with '--eval-store' " ) ;
auto conn ( connections - > get ( ) ) ;
conn - > to < < ServeProto : : Command : : BuildPaths ;
Strings ss ;
for ( auto & p : drvPaths ) {
auto sOrDrvPath = StorePathWithOutputs : : tryFromDerivedPath ( p ) ;
std : : visit ( overloaded {
[ & ] ( const StorePathWithOutputs & s ) {
ss . push_back ( s . to_string ( * this ) ) ;
} ,
[ & ] ( const 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 ) ) ;
} ,
[ & ] ( std : : monostate ) {
throw Error ( " wanted build derivation that is itself a build product, but the legacy ssh protocol doesn't support that. Try using ssh-ng:// " ) ;
} ,
} , sOrDrvPath ) ;
2017-03-16 12:44:01 +02:00
}
2023-12-10 21:28:14 +02:00
conn - > to < < ss ;
2017-03-16 12:44:01 +02:00
2022-02-20 21:24:07 +02:00
ServeProto : : write ( * this , * conn , buildSettings ( ) ) ;
2017-03-16 14:50:01 +02:00
2023-12-10 21:28:14 +02:00
conn - > to . flush ( ) ;
2017-03-16 14:50:01 +02:00
2023-12-10 21:28:14 +02:00
BuildResult result ;
result . status = ( BuildResult : : Status ) readInt ( conn - > from ) ;
2017-05-02 15:18:46 +03:00
2023-12-10 21:28:14 +02:00
if ( ! result . success ( ) ) {
conn - > from > > result . errorMsg ;
throw Error ( result . status , result . errorMsg ) ;
2017-05-02 15:18:46 +03:00
}
2023-12-10 21:28:14 +02:00
}
2018-08-31 00:28:47 +03:00
2020-10-08 18:36:51 +03:00
2023-12-10 21:28:14 +02:00
void LegacySSHStore : : computeFSClosure ( const StorePathSet & paths ,
StorePathSet & out , bool flipDirection ,
bool includeOutputs , bool includeDerivers )
{
if ( flipDirection | | includeDerivers ) {
Store : : computeFSClosure ( paths , out , flipDirection , includeOutputs , includeDerivers ) ;
return ;
2022-12-26 22:21:08 +02:00
}
2023-12-10 21:28:14 +02:00
auto conn ( connections - > get ( ) ) ;
conn - > to
< < ServeProto : : Command : : QueryClosure
< < includeOutputs ;
ServeProto : : write ( * this , * conn , paths ) ;
conn - > to . flush ( ) ;
for ( auto & i : ServeProto : : Serialise < StorePathSet > : : read ( * this , * conn ) )
out . insert ( i ) ;
}
StorePathSet LegacySSHStore : : queryValidPaths ( const StorePathSet & paths ,
SubstituteFlag maybeSubstitute )
{
auto conn ( connections - > get ( ) ) ;
2022-02-20 21:24:07 +02:00
return conn - > queryValidPaths ( * this ,
false , paths , maybeSubstitute ) ;
2023-12-10 21:28:14 +02:00
}
void LegacySSHStore : : connect ( )
{
auto conn ( connections - > get ( ) ) ;
}
unsigned int LegacySSHStore : : getProtocol ( )
{
auto conn ( connections - > get ( ) ) ;
return conn - > remoteVersion ;
}
/**
* The legacy ssh protocol doesn ' t support checking for trusted - user .
* Try using ssh - ng : // instead if you want to know.
*/
std : : optional < TrustedFlag > isTrustedClient ( )
{
return std : : nullopt ;
}
2017-02-07 20:28:40 +02:00
2020-10-06 14:36:55 +03:00
static RegisterStoreImplementation < LegacySSHStore , LegacySSHStoreConfig > regLegacySSHStore ;
2017-02-07 20:28:40 +02:00
}