about summary refs log tree commit diff
path: root/src/download-via-ssh/download-via-ssh.cc
blob: 7adb93e10dfea8049d635b495c43f37954269c31 (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
#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. If we exit unexpectedly, child will
    // 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";