aboutsummaryrefslogtreecommitdiff
path: root/src/libstore/worker-protocol.hh
blob: 315e982dd9be88fdcc3ff12f5c49f650a035b0ce (plain)
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#pragma once
///@file

#include "common-protocol.hh"

namespace nix {


#define WORKER_MAGIC_1 0x6e697863
#define WORKER_MAGIC_2 0x6478696f

// This must remain 1.35 (Nix 2.18) forever in Lix, since the protocol has
// diverged in CppNix such that we cannot assign newer versions ourselves, the
// protocol is bad in design and implementation and Lix intends to replace it
// entirely.
#define PROTOCOL_VERSION (1 << 8 | 35)
// Nix 2.3 is protocol 1.21 (see RemoteStore::initConnection for client,
// processConnection for server).
#define MIN_SUPPORTED_MINOR_WORKER_PROTO_VERSION 21
#define MIN_SUPPORTED_WORKER_PROTO_VERSION (1 << 8 | MIN_SUPPORTED_MINOR_WORKER_PROTO_VERSION)

#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)


#define REMOVE_AFTER_DROPPING_PROTO_MINOR(protoMinor) \
    static_assert(MIN_SUPPORTED_MINOR_WORKER_PROTO_VERSION <= (protoMinor))


#define STDERR_NEXT  0x6f6c6d67
#define STDERR_READ  0x64617461 // data needed from source
#define STDERR_WRITE 0x64617416 // data for sink
#define STDERR_LAST  0x616c7473
#define STDERR_ERROR 0x63787470
#define STDERR_START_ACTIVITY 0x53545254
#define STDERR_STOP_ACTIVITY  0x53544f50
#define STDERR_RESULT         0x52534c54


class Store;
struct Source;

// items being serialised
struct DerivedPath;
struct BuildResult;
struct KeyedBuildResult;
struct ValidPathInfo;
struct UnkeyedValidPathInfo;
enum TrustedFlag : bool;


/**
 * The "worker protocol", used by unix:// and ssh-ng:// stores.
 *
 * This `struct` is basically just a `namespace`; We use a type rather
 * than a namespace just so we can use it as a template argument.
 */
struct WorkerProto
{
    /**
     * Enumeration of all the request types for the protocol.
     */
    enum struct Op : uint64_t;

    /**
     * Version type for the protocol.
     *
     * @todo Convert to struct with separate major vs minor fields.
     */
    using Version = unsigned int;

    /**
     * A unidirectional read connection, to be used by the read half of the
     * canonical serializers below.
     */
    struct ReadConn {
        Source & from;
        Version version;

        ReadConn(Source & from, Version version) : from(from), version(version) {
            assert(version >= MIN_SUPPORTED_WORKER_PROTO_VERSION);
        }
    };

    /**
     * A unidirectional write connection, to be used by the write half of the
     * canonical serializers below.
     */
    struct WriteConn {
        Version version;

        explicit WriteConn(Version version) : version(version) {
            assert(version >= MIN_SUPPORTED_WORKER_PROTO_VERSION);
        }
    };

    /**
     * Data type for canonical pairs of serialisers for the worker protocol.
     *
     * See https://en.cppreference.com/w/cpp/language/adl for the broader
     * concept of what is going on here.
     */
    template<typename T>
    struct Serialise;
    // This is the definition of `Serialise` we *want* to put here, but
    // do not do so.
    //
    // The problem is that if we do so, C++ will think we have
    // seralisers for *all* types. We don't, of course, but that won't
    // cause an error until link time. That makes for long debug cycles
    // when there is a missing serialiser.
    //
    // By not defining it globally, and instead letting individual
    // serialisers specialise the type, we get back the compile-time
    // errors we would like. When no serialiser exists, C++ sees an
    // abstract "incomplete" type with no definition, and any attempt to
    // use `to` or `from` static methods is a compile-time error because
    // they don't exist on an incomplete type.
    //
    // This makes for a quicker debug cycle, as desired.
#if 0
    {
        static T read(const Store & store, ReadConn conn);
        static WireFormatGenerator write(const Store & store, WriteConn conn, const T & t);
    };
#endif

    /**
     * Wrapper function around `WorkerProto::Serialise<T>::write` that allows us to
     * infer the type instead of having to write it down explicitly.
     */
    template<typename T>
    [[nodiscard]]
    static WireFormatGenerator write(const Store & store, WriteConn conn, const T & t)
    {
        return WorkerProto::Serialise<T>::write(store, conn, t);
    }
};

enum struct WorkerProto::Op : uint64_t
{
    IsValidPath = 1,
    HasSubstitutes = 3, // obsolete since 2012, stubbed to error
    QueryPathHash = 4, // obsolete since 2016, stubbed to error
    QueryReferences = 5, // obsolete since 2016, stubbed to error
    QueryReferrers = 6,
    AddToStore = 7,
    AddTextToStore = 8, // obsolete since protocol 1.25, CppNix 2.4. Use WorkerProto::Op::AddToStore
    BuildPaths = 9,
    EnsurePath = 10,
    AddTempRoot = 11,
    AddIndirectRoot = 12,
    SyncWithGC = 13, // obsolete since CppNix 2.5.0
    FindRoots = 14,
    ExportPath = 16, // obsolete since 2017, stubbed to error
    QueryDeriver = 18, // obsolete since 2016, stubbed to error
    SetOptions = 19,
    CollectGarbage = 20,
    QuerySubstitutablePathInfo = 21,
    QueryDerivationOutputs = 22, // obsolete since protocol 1.21, CppNix 2.4
    QueryAllValidPaths = 23,
    QueryFailedPaths = 24, // obsolete, removed
    ClearFailedPaths = 25, // obsolete, removed
    QueryPathInfo = 26,
    ImportPaths = 27, // obsolete since 2016
    QueryDerivationOutputNames = 28, // obsolete since CppNix 2.4
    QueryPathFromHashPart = 29,
    QuerySubstitutablePathInfos = 30,
    QueryValidPaths = 31,
    QuerySubstitutablePaths = 32,
    QueryValidDerivers = 33,
    OptimiseStore = 34,
    VerifyStore = 35,
    BuildDerivation = 36,
    AddSignatures = 37,
    NarFromPath = 38,
    AddToStoreNar = 39,
    QueryMissing = 40,
    QueryDerivationOutputMap = 41,
    RegisterDrvOutput = 42,
    QueryRealisation = 43,
    AddMultipleToStore = 44,
    AddBuildLog = 45,
    BuildPathsWithResults = 46,
};

/**
 * Convenience for sending operation codes.
 *
 * @todo Switch to using `WorkerProto::Serialise` instead probably. But
 * this was not done at this time so there would be less churn.
 */
inline Sink & operator << (Sink & sink, WorkerProto::Op op)
{
    return sink << (uint64_t) op;
}

/**
 * Convenience for debugging.
 *
 * @todo Perhaps render known opcodes more nicely.
 */
inline std::ostream & operator << (std::ostream & s, WorkerProto::Op op)
{
    return s << (uint64_t) op;
}

/**
 * Declare a canonical serialiser pair for the worker protocol.
 *
 * We specialise the struct merely to indicate that we are implementing
 * the function for the given type.
 *
 * Some sort of `template<...>` must be used with the caller for this to
 * be legal specialization syntax. See below for what that looks like in
 * practice.
 */
#define DECLARE_WORKER_SERIALISER(T) \
    struct WorkerProto::Serialise< T > \
    { \
        static T read(const Store & store, WorkerProto::ReadConn conn); \
        [[nodiscard]] static WireFormatGenerator write(const Store & store, WorkerProto::WriteConn conn, const T & t); \
    };

template<>
DECLARE_WORKER_SERIALISER(DerivedPath);
template<>
DECLARE_WORKER_SERIALISER(BuildResult);
template<>
DECLARE_WORKER_SERIALISER(KeyedBuildResult);
template<>
DECLARE_WORKER_SERIALISER(ValidPathInfo);
template<>
DECLARE_WORKER_SERIALISER(UnkeyedValidPathInfo);
template<>
DECLARE_WORKER_SERIALISER(std::optional<TrustedFlag>);

template<typename T>
DECLARE_WORKER_SERIALISER(std::vector<T>);
template<typename T>
DECLARE_WORKER_SERIALISER(std::set<T>);
template<typename... Ts>
DECLARE_WORKER_SERIALISER(std::tuple<Ts...>);

#define COMMA_ ,
template<typename K, typename V>
DECLARE_WORKER_SERIALISER(std::map<K COMMA_ V>);
#undef COMMA_

}