about summary refs log tree commit diff
path: root/src/download-via-ssh/download-via-ssh.cc
blob: 003d7de2bf862b087d9eb35d2befdfab7c498915 (plain) (blame)
1
2
3
4
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include "shared.hh"
#include "util.hh"
#include "serialise.hh"
#include "archive.hh"
#include "affinity.hh"
#include "globals.hh"

#include <iostream>
#include <unistd.h>

using namespace nix;
using std::pair;
using std::cout;
using std::endl;

// !!! TODO:
// * Respect more than the first host
// * use a database
// * show progress

static pair<FdSink, FdSource> connect(string conn) {
    Pipe to, from;
    to.create();
    from.create();
    pid_t child = fork();
    switch (child) {
        case -1:
            throw SysError("unable to fork");
        case 0:
            try {
                restoreAffinity();
                if (dup2(to.readSide, STDIN_FILENO) == -1)
                    throw SysError("dupping stdin");
                if (dup2(from.writeSide, STDOUT_FILENO) == -1)
                    throw SysError("dupping stdout");
                execlp("ssh"
                      , "ssh"
                      , "-x"
                      , "-T"
                      , conn.c_str()
                      , "nix-store --serve"
                      , NULL);
                throw SysError("executing ssh");
            } catch (std::exception & e) {
                std::cerr << "error: " << e.what() << std::endl;
            }
            _exit(1);
    }
    // If child exits unexpectedly, we'll EPIPE or EOF early.
    // If we exit unexpectedly, child will EPIPE or EOF early.
    // So no need to keep track of it.

    return pair<FdSink, FdSource>(to.writeSide.borrow(), from.readSide.borrow());
}

static void substitute(pair<FdSink, FdSource> & pipes, Path storePath, Path destPath) {
    writeString("substitute", pipes.first);
    writeString(storePath, pipes.first);
    pipes.first.flush();
    restorePath(destPath, pipes.second);
    cout << endl;
}

static void query(pair<FdSink, FdSource> & pipes) {
    using std::cin;
    writeString("query", pipes.first);
    for (string line; getline(cin, line);) {
        Strings tokenized = tokenizeString<Strings>(line);
        string cmd = tokenized.front();
        writeString(cmd, pipes.first);
        tokenized.pop_front();
        foreach (Strings::iterator, i, tokenized)
        writeStrings(tokenized, pipes.first);
        pipes.first.flush();
        if (cmd == "have") {
            PathSet paths = readStrings<PathSet>(pipes.second);
            foreach (PathSet::iterator, i, paths)
                cout << *i << endl;
        } else if (cmd == "info") {
            for (Path path = readString(pipes.second); !path.empty(); path = readString(pipes.second)) {
                cout << path << endl;
                cout << readString(pipes.second) << endl;
                PathSet references = readStrings<PathSet>(pipes.second);
                cout << references.size() << endl;
                foreach (PathSet::iterator, i, references)
                    cout << *i << endl;
                cout << readLongLong(pipes.second) << endl;
                cout << readLongLong(pipes.second) << endl;
            }
        } else
            throw Error(format("Unknown substituter query `%1%'") % cmd);
        cout << endl;
    }
    writeString("", pipes.first);
}

void run(Strings args)
{
    if (args.empty())
        throw UsageError("download-via-ssh requires an argument");

    if (settings.sshSubstituterHosts.empty())
        return;

    cout << endl;

    pair<FdSink, FdSource> pipes = connect(settings.sshSubstituterHosts.front());

    Strings::iterator i = args.begin();
    if (*i == "--query")
        query(pipes);
    else if (*i == "--substitute")
        if (args.size() != 3)
            throw UsageError("download-via-ssh: --substitute takes exactly two arguments");
        else {
            Path storePath = *++i;
            Path destPath = *++i;
            substitute(pipes, storePath, destPath);
        }
    else
        throw UsageError(format("download-via-ssh: unknown command `%1%'") % *i);
}

void printHelp()
{
    std::cerr << "Usage: download-via-ssh --query|--substitute store-path dest-path" << std::endl;
}


string programId = "download-via-ssh";