1#include "crypto.hh"
2#include "globals.hh"
3#include "store-api.hh"
4#include "util.hh"
5#include "nar-info-disk-cache.hh"
6#include "thread-pool.hh"
7#include "json.hh"
8#include "derivations.hh"
9
10#include <future>
11
12
13namespace nix {
14
15
16bool Store::isInStore(const Path & path) const
17{
18    return isInDir(path, storeDir);
19}
20
21
22bool Store::isStorePath(const Path & path) const
23{
24    return isInStore(path)
25        && path.size() >= storeDir.size() + 1 + storePathHashLen
26        && path.find('/', storeDir.size() + 1) == Path::npos;
27}
28
29
30void Store::assertStorePath(const Path & path) const
31{
32    if (!isStorePath(path))
33        throw Error(format("path '%1%' is not in the Nix store") % path);
34}
35
36
37Path Store::toStorePath(const Path & path) const
38{
39    if (!isInStore(path))
40        throw Error(format("path '%1%' is not in the Nix store") % path);
41    Path::size_type slash = path.find('/', storeDir.size() + 1);
42    if (slash == Path::npos)
43        return path;
44    else
45        return Path(path, 0, slash);
46}
47
48
49Path Store::followLinksToStore(const Path & _path) const
50{
51    Path path = absPath(_path);
52    while (!isInStore(path)) {
53        if (!isLink(path)) break;
54        string target = readLink(path);
55        path = absPath(target, dirOf(path));
56    }
57    if (!isInStore(path))
58        throw Error(format("path '%1%' is not in the Nix store") % path);
59    return path;
60}
61
62
63Path Store::followLinksToStorePath(const Path & path) const
64{
65    return toStorePath(followLinksToStore(path));
66}
67
68
69string storePathToName(const Path & path)
70{
71    auto base = baseNameOf(path);
72    assert(base.size() == storePathHashLen || (base.size() > storePathHashLen && base[storePathHashLen] == '-'));
73    return base.size() == storePathHashLen ? "" : string(base, storePathHashLen + 1);
74}
75
76
77string storePathToHash(const Path & path)
78{
79    auto base = baseNameOf(path);
80    assert(base.size() >= storePathHashLen);
81    return string(base, 0, storePathHashLen);
82}
83
84
85void checkStoreName(const string & name)
86{
87    string validChars = "+-._?=";
88    /* Disallow names starting with a dot for possible security
89       reasons (e.g., "." and ".."). */
90    if (string(name, 0, 1) == ".")
91        throw Error(format("illegal name: '%1%'") % name);
92    for (auto & i : name)
93        if (!((i >= 'A' && i <= 'Z') ||
94              (i >= 'a' && i <= 'z') ||
95              (i >= '0' && i <= '9') ||
96              validChars.find(i) != string::npos))
97        {
98            throw Error(format("invalid character '%1%' in name '%2%'")
99                % i % name);
100        }
101}
102
103
104/* Store paths have the following form:
105
106   <store>/<h>-<name>
107
108   where
109
110   <store> = the location of the Nix store, usually /nix/store
111
112   <name> = a human readable name for the path, typically obtained
113     from the name attribute of the derivation, or the name of the
114     source file from which the store path is created.  For derivation
115     outputs other than the default "out" output, the string "-<id>"
116     is suffixed to <name>.
117
118   <h> = base-32 representation of the first 160 bits of a SHA-256
119     hash of <s>; the hash part of the store name
120
121   <s> = the string "<type>:sha256:<h2>:<store>:<name>";
122     note that it includes the location of the store as well as the
123     name to make sure that changes to either of those are reflected
124     in the hash (e.g. you won't get /nix/store/<h>-name1 and
125     /nix/store/<h>-name2 with equal hash parts).
126
127   <type> = one of:
128     "text:<r1>:<r2>:...<rN>"
129       for plain text files written to the store using
130       addTextToStore(); <r1> ... <rN> are the references of the
131       path.
132     "source"
133       for paths copied to the store using addToStore() when recursive
134       = true and hashAlgo = "sha256"
135     "output:<id>"
136       for either the outputs created by derivations, OR paths copied
137       to the store using addToStore() with recursive != true or
138       hashAlgo != "sha256" (in that case "source" is used; it's
139       silly, but it's done that way for compatibility).  <id> is the
140       name of the output (usually, "out").
141
142   <h2> = base-16 representation of a SHA-256 hash of:
143     if <type> = "text:...":
144       the string written to the resulting store path
145     if <type> = "source":
146       the serialisation of the path from which this store path is
147       copied, as returned by hashPath()
148     if <type> = "output:<id>":
149       for non-fixed derivation outputs:
150         the derivation (see hashDerivationModulo() in
151         primops.cc)
152       for paths copied by addToStore() or produced by fixed-output
153       derivations:
154         the string "fixed:out:<rec><algo>:<hash>:", where
155           <rec> = "r:" for recursive (path) hashes, or "" for flat
156             (file) hashes
157           <algo> = "md5", "sha1" or "sha256"
158           <hash> = base-16 representation of the path or flat hash of
159             the contents of the path (or expected contents of the
160             path for fixed-output derivations)
161
162   It would have been nicer to handle fixed-output derivations under
163   "source", e.g. have something like "source:<rec><algo>", but we're
164   stuck with this for now...
165
166   The main reason for this way of computing names is to prevent name
167   collisions (for security).  For instance, it shouldn't be feasible
168   to come up with a derivation whose output path collides with the
169   path for a copied source.  The former would have a <s> starting with
170   "output:out:", while the latter would have a <s> starting with
171   "source:".
172*/
173
174
175Path Store::makeStorePath(const string & type,
176    const Hash & hash, const string & name) const
177{
178    /* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
179    string s = type + ":" + hash.to_string(Base16) + ":" + storeDir + ":" + name;
180
181    checkStoreName(name);
182
183    return storeDir + "/"
184        + compressHash(hashString(htSHA256, s), 20).to_string(Base32, false)
185        + "-" + name;
186}
187
188
189Path Store::makeOutputPath(const string & id,
190    const Hash & hash, const string & name) const
191{
192    return makeStorePath("output:" + id, hash,
193        name + (id == "out" ? "" : "-" + id));
194}
195
196
197Path Store::makeFixedOutputPath(bool recursive,
198    const Hash & hash, const string & name) const
199{
200    return hash.type == htSHA256 && recursive
201        ? makeStorePath("source", hash, name)
202        : makeStorePath("output:out", hashString(htSHA256,
203                "fixed:out:" + (recursive ? (string) "r:" : "") +
204                hash.to_string(Base16) + ":"),
205            name);
206}
207
208
209Path Store::makeTextPath(const string & name, const Hash & hash,
210    const PathSet & references) const
211{
212    assert(hash.type == htSHA256);
213    /* Stuff the references (if any) into the type.  This is a bit
214       hacky, but we can't put them in `s' since that would be
215       ambiguous. */
216    string type = "text";
217    for (auto & i : references) {
218        type += ":";
219        type += i;
220    }
221    return makeStorePath(type, hash, name);
222}
223
224
225std::pair<Path, Hash> Store::computeStorePathForPath(const Path & srcPath,
226    bool recursive, HashType hashAlgo, PathFilter & filter) const
227{
228    Hash h = recursive ? hashPath(hashAlgo, srcPath, filter).first : hashFile(hashAlgo, srcPath);
229    string name = baseNameOf(srcPath);
230    Path dstPath = makeFixedOutputPath(recursive, h, name);
231    return std::pair<Path, Hash>(dstPath, h);
232}
233
234
235Path Store::computeStorePathForText(const string & name, const string & s,
236    const PathSet & references) const
237{
238    return makeTextPath(name, hashString(htSHA256, s), references);
239}
240
241
242Store::Store(const Params & params)
243    : Config(params)
244    , state({(size_t) pathInfoCacheSize})
245{
246}
247
248
249std::string Store::getUri()
250{
251    return "";
252}
253
254
255bool Store::isValidPath(const Path & storePath)
256{
257    auto hashPart = storePathToHash(storePath);
258
259    {
260        auto state_(state.lock());
261        auto res = state_->pathInfoCache.get(hashPart);
262        if (res) {
263            stats.narInfoReadAverted++;
264            return *res != 0;
265        }
266    }
267
268    if (diskCache) {
269        auto res = diskCache->lookupNarInfo(getUri(), hashPart);
270        if (res.first != NarInfoDiskCache::oUnknown) {
271            stats.narInfoReadAverted++;
272            auto state_(state.lock());
273            state_->pathInfoCache.upsert(hashPart,
274                res.first == NarInfoDiskCache::oInvalid ? 0 : res.second);
275            return res.first == NarInfoDiskCache::oValid;
276        }
277    }
278
279    bool valid = isValidPathUncached(storePath);
280
281    if (diskCache && !valid)
282        // FIXME: handle valid = true case.
283        diskCache->upsertNarInfo(getUri(), hashPart, 0);
284
285    return valid;
286}
287
288
289/* Default implementation for stores that only implement
290   queryPathInfoUncached(). */
291bool Store::isValidPathUncached(const Path & path)
292{
293    try {
294        queryPathInfo(path);
295        return true;
296    } catch (InvalidPath &) {
297        return false;
298    }
299}
300
301
302ref<const ValidPathInfo> Store::queryPathInfo(const Path & storePath)
303{
304    std::promise<ref<ValidPathInfo>> promise;
305
306    queryPathInfo(storePath,
307        [&](ref<ValidPathInfo> info) {
308            promise.set_value(info);
309        },
310        [&](std::exception_ptr exc) {
311            promise.set_exception(exc);
312        });
313
314    return promise.get_future().get();
315}
316
317
318void Store::queryPathInfo(const Path & storePath,
319    std::function<void(ref<ValidPathInfo>)> success,
320    std::function<void(std::exception_ptr exc)> failure)
321{
322    auto hashPart = storePathToHash(storePath);
323
324    try {
325
326        {
327            auto res = state.lock()->pathInfoCache.get(hashPart);
328            if (res) {
329                stats.narInfoReadAverted++;
330                if (!*res)
331                    throw InvalidPath(format("path '%s' is not valid") % storePath);
332                return success(ref<ValidPathInfo>(*res));
333            }
334        }
335
336        if (diskCache) {
337            auto res = diskCache->lookupNarInfo(getUri(), hashPart);
338            if (res.first != NarInfoDiskCache::oUnknown) {
339                stats.narInfoReadAverted++;
340                {
341                    auto state_(state.lock());
342                    state_->pathInfoCache.upsert(hashPart,
343                        res.first == NarInfoDiskCache::oInvalid ? 0 : res.second);
344                    if (res.first == NarInfoDiskCache::oInvalid ||
345                        (res.second->path != storePath && storePathToName(storePath) != ""))
346                        throw InvalidPath(format("path '%s' is not valid") % storePath);
347                }
348                return success(ref<ValidPathInfo>(res.second));
349            }
350        }
351
352    } catch (std::exception & e) {
353        return callFailure(failure);
354    }
355
356    queryPathInfoUncached(storePath,
357        [this, storePath, hashPart, success, failure](std::shared_ptr<ValidPathInfo> info) {
358
359            if (diskCache)
360                diskCache->upsertNarInfo(getUri(), hashPart, info);
361
362            {
363                auto state_(state.lock());
364                state_->pathInfoCache.upsert(hashPart, info);
365            }
366
367            if (!info
368                || (info->path != storePath && storePathToName(storePath) != ""))
369            {
370                stats.narInfoMissing++;
371                return failure(std::make_exception_ptr(InvalidPath(format("path '%s' is not valid") % storePath)));
372            }
373
374            callSuccess(success, failure, ref<ValidPathInfo>(info));
375
376        }, failure);
377}
378
379
380PathSet Store::queryValidPaths(const PathSet & paths, SubstituteFlag maybeSubstitute)
381{
382    struct State
383    {
384        size_t left;
385        PathSet valid;
386        std::exception_ptr exc;
387    };
388
389    Sync<State> state_(State{paths.size(), PathSet()});
390
391    std::condition_variable wakeup;
392
393    for (auto & path : paths)
394        queryPathInfo(path,
395            [path, &state_, &wakeup](ref<ValidPathInfo> info) {
396                auto state(state_.lock());
397                state->valid.insert(path);
398                assert(state->left);
399                if (!--state->left)
400                    wakeup.notify_one();
401            },
402            [path, &state_, &wakeup](std::exception_ptr exc) {
403                auto state(state_.lock());
404                try {
405                    std::rethrow_exception(exc);
406                } catch (InvalidPath &) {
407                } catch (...) {
408                    state->exc = exc;
409                }
410                assert(state->left);
411                if (!--state->left)
412                    wakeup.notify_one();
413            });
414
415    while (true) {
416        auto state(state_.lock());
417        if (!state->left) {
418            if (state->exc) std::rethrow_exception(state->exc);
419            return state->valid;
420        }
421        state.wait(wakeup);
422    }
423}
424
425
426/* Return a string accepted by decodeValidPathInfo() that
427   registers the specified paths as valid.  Note: it's the
428   responsibility of the caller to provide a closure. */
429string Store::makeValidityRegistration(const PathSet & paths,
430    bool showDerivers, bool showHash)
431{
432    string s = "";
433
434    for (auto & i : paths) {
435        s += i + "\n";
436
437        auto info = queryPathInfo(i);
438
439        if (showHash) {
440            s += info->narHash.to_string(Base16, false) + "\n";
441            s += (format("%1%\n") % info->narSize).str();
442        }
443
444        Path deriver = showDerivers ? info->deriver : "";
445        s += deriver + "\n";
446
447        s += (format("%1%\n") % info->references.size()).str();
448
449        for (auto & j : info->references)
450            s += j + "\n";
451    }
452
453    return s;
454}
455
456
457void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const PathSet & storePaths,
458    bool includeImpureInfo, bool showClosureSize, AllowInvalidFlag allowInvalid)
459{
460    auto jsonList = jsonOut.list();
461
462    for (auto storePath : storePaths) {
463        auto jsonPath = jsonList.object();
464        jsonPath.attr("path", storePath);
465
466        try {
467            auto info = queryPathInfo(storePath);
468            storePath = info->path;
469
470            jsonPath
471                .attr("narHash", info->narHash.to_string())
472                .attr("narSize", info->narSize);
473
474            {
475                auto jsonRefs = jsonPath.list("references");
476                for (auto & ref : info->references)
477                    jsonRefs.elem(ref);
478            }
479
480            if (info->ca != "")
481                jsonPath.attr("ca", info->ca);
482
483            std::pair<uint64_t, uint64_t> closureSizes;
484
485            if (showClosureSize) {
486                closureSizes = getClosureSize(storePath);
487                jsonPath.attr("closureSize", closureSizes.first);
488            }
489
490            if (includeImpureInfo) {
491
492                if (info->deriver != "")
493                    jsonPath.attr("deriver", info->deriver);
494
495                if (info->registrationTime)
496                    jsonPath.attr("registrationTime", info->registrationTime);
497
498                if (info->ultimate)
499                    jsonPath.attr("ultimate", info->ultimate);
500
501                if (!info->sigs.empty()) {
502                    auto jsonSigs = jsonPath.list("signatures");
503                    for (auto & sig : info->sigs)
504                        jsonSigs.elem(sig);
505                }
506
507                auto narInfo = std::dynamic_pointer_cast<const NarInfo>(
508                    std::shared_ptr<const ValidPathInfo>(info));
509
510                if (narInfo) {
511                    if (narInfo->fileHash)
512                        jsonPath.attr("downloadHash", narInfo->fileHash.to_string());
513                    if (narInfo->fileSize)
514                        jsonPath.attr("downloadSize", narInfo->fileSize);
515                    if (showClosureSize)
516                        jsonPath.attr("closureDownloadSize", closureSizes.second);
517                }
518            }
519
520        } catch (InvalidPath &) {
521            jsonPath.attr("valid", false);
522        }
523    }
524}
525
526
527std::pair<uint64_t, uint64_t> Store::getClosureSize(const Path & storePath)
528{
529    uint64_t totalNarSize = 0, totalDownloadSize = 0;
530    PathSet closure;
531    computeFSClosure(storePath, closure, false, false);
532    for (auto & p : closure) {
533        auto info = queryPathInfo(p);
534        totalNarSize += info->narSize;
535        auto narInfo = std::dynamic_pointer_cast<const NarInfo>(
536            std::shared_ptr<const ValidPathInfo>(info));
537        if (narInfo)
538            totalDownloadSize += narInfo->fileSize;
539    }
540    return {totalNarSize, totalDownloadSize};
541}
542
543
544const Store::Stats & Store::getStats()
545{
546    {
547        auto state_(state.lock());
548        stats.pathInfoCacheSize = state_->pathInfoCache.size();
549    }
550    return stats;
551}
552
553
554void Store::buildPaths(const PathSet & paths, BuildMode buildMode)
555{
556    for (auto & path : paths)
557        if (isDerivation(path))
558            unsupported();
559
560    if (queryValidPaths(paths).size() != paths.size())
561        unsupported();
562}
563
564
565void copyStorePath(ref<Store> srcStore, ref<Store> dstStore,
566    const Path & storePath, RepairFlag repair, CheckSigsFlag checkSigs)
567{
568    Activity act(*logger, actCopyPath, fmt("copying path '%s'", storePath));
569
570    auto info = srcStore->queryPathInfo(storePath);
571
572    uint64_t total = 0;
573
574    auto progress = [&](size_t len) {
575        total += len;
576        act.progress(total, info->narSize);
577    };
578
579    struct MyStringSink : StringSink
580    {
581        typedef std::function<void(size_t)> Callback;
582        Callback callback;
583        MyStringSink(Callback callback) : callback(callback) { }
584        void operator () (const unsigned char * data, size_t len) override
585        {
586            StringSink::operator ()(data, len);
587            callback(len);
588        };
589    };
590
591    MyStringSink sink(progress);
592    srcStore->narFromPath({storePath}, sink);
593
594    if (!info->narHash && !checkSigs) {
595        auto info2 = make_ref<ValidPathInfo>(*info);
596        info2->narHash = hashString(htSHA256, *sink.s);
597        if (!info->narSize) info2->narSize = sink.s->size();
598        info = info2;
599    }
600
601    assert(info->narHash);
602
603    if (info->ultimate) {
604        auto info2 = make_ref<ValidPathInfo>(*info);
605        info2->ultimate = false;
606        info = info2;
607    }
608
609    assert(info->narHash);
610
611    dstStore->addToStore(*info, sink.s, repair, checkSigs);
612}
613
614
615void copyPaths(ref<Store> srcStore, ref<Store> dstStore, const PathSet & storePaths,
616    RepairFlag repair, CheckSigsFlag checkSigs, SubstituteFlag substitute)
617{
618    PathSet valid = dstStore->queryValidPaths(storePaths, substitute);
619
620    PathSet missing;
621    for (auto & path : storePaths)
622        if (!valid.count(path)) missing.insert(path);
623
624    Activity act(*logger, actCopyPaths, fmt("copying %d paths", missing.size()));
625
626    std::atomic<size_t> nrDone{0};
627    std::atomic<uint64_t> bytesExpected{0};
628    std::atomic<uint64_t> nrRunning{0};
629
630    auto showProgress = [&]() {
631        act.progress(nrDone, missing.size(), nrRunning);
632    };
633
634    ThreadPool pool;
635
636    processGraph<Path>(pool,
637        PathSet(missing.begin(), missing.end()),
638
639        [&](const Path & storePath) {
640            if (dstStore->isValidPath(storePath)) {
641                nrDone++;
642                showProgress();
643                return PathSet();
644            }
645
646            auto info = srcStore->queryPathInfo(storePath);
647
648            bytesExpected += info->narSize;
649            act.setExpected(actCopyPath, bytesExpected);
650
651            return info->references;
652        },
653
654        [&](const Path & storePath) {
655            checkInterrupt();
656
657            if (!dstStore->isValidPath(storePath)) {
658                MaintainCount<decltype(nrRunning)> mc(nrRunning);
659                showProgress();
660                copyStorePath(srcStore, dstStore, storePath, repair, checkSigs);
661            }
662
663            nrDone++;
664            showProgress();
665        });
666}
667
668
669void copyClosure(ref<Store> srcStore, ref<Store> dstStore,
670    const PathSet & storePaths, RepairFlag repair, CheckSigsFlag checkSigs,
671    SubstituteFlag substitute)
672{
673    PathSet closure;
674    srcStore->computeFSClosure({storePaths}, closure);
675    copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute);
676}
677
678
679ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven)
680{
681    ValidPathInfo info;
682    getline(str, info.path);
683    if (str.eof()) { info.path = ""; return info; }
684    if (hashGiven) {
685        string s;
686        getline(str, s);
687        info.narHash = Hash(s, htSHA256);
688        getline(str, s);
689        if (!string2Int(s, info.narSize)) throw Error("number expected");
690    }
691    getline(str, info.deriver);
692    string s; int n;
693    getline(str, s);
694    if (!string2Int(s, n)) throw Error("number expected");
695    while (n--) {
696        getline(str, s);
697        info.references.insert(s);
698    }
699    if (!str || str.eof()) throw Error("missing input");
700    return info;
701}
702
703
704string showPaths(const PathSet & paths)
705{
706    string s;
707    for (auto & i : paths) {
708        if (s.size() != 0) s += ", ";
709        s += "'" + i + "'";
710    }
711    return s;
712}
713
714
715std::string ValidPathInfo::fingerprint() const
716{
717    if (narSize == 0 || !narHash)
718        throw Error(format("cannot calculate fingerprint of path '%s' because its size/hash is not known")
719            % path);
720    return
721        "1;" + path + ";"
722        + narHash.to_string(Base32) + ";"
723        + std::to_string(narSize) + ";"
724        + concatStringsSep(",", references);
725}
726
727
728void ValidPathInfo::sign(const SecretKey & secretKey)
729{
730    sigs.insert(secretKey.signDetached(fingerprint()));
731}
732
733
734bool ValidPathInfo::isContentAddressed(const Store & store) const
735{
736    auto warn = [&]() {
737        printError(format("warning: path '%s' claims to be content-addressed but isn't") % path);
738    };
739
740    if (hasPrefix(ca, "text:")) {
741        Hash hash(std::string(ca, 5));
742        if (store.makeTextPath(storePathToName(path), hash, references) == path)
743            return true;
744        else
745            warn();
746    }
747
748    else if (hasPrefix(ca, "fixed:")) {
749        bool recursive = ca.compare(6, 2, "r:") == 0;
750        Hash hash(std::string(ca, recursive ? 8 : 6));
751        if (store.makeFixedOutputPath(recursive, hash, storePathToName(path)) == path)
752            return true;
753        else
754            warn();
755    }
756
757    return false;
758}
759
760
761size_t ValidPathInfo::checkSignatures(const Store & store, const PublicKeys & publicKeys) const
762{
763    if (isContentAddressed(store)) return maxSigs;
764
765    size_t good = 0;
766    for (auto & sig : sigs)
767        if (checkSignature(publicKeys, sig))
768            good++;
769    return good;
770}
771
772
773bool ValidPathInfo::checkSignature(const PublicKeys & publicKeys, const std::string & sig) const
774{
775    return verifyDetached(fingerprint(), sig, publicKeys);
776}
777
778
779Strings ValidPathInfo::shortRefs() const
780{
781    Strings refs;
782    for (auto & r : references)
783        refs.push_back(baseNameOf(r));
784    return refs;
785}
786
787
788std::string makeFixedOutputCA(bool recursive, const Hash & hash)
789{
790    return "fixed:" + (recursive ? (std::string) "r:" : "") + hash.to_string();
791}
792
793
794}
795
796
797#include "local-store.hh"
798#include "remote-store.hh"
799
800
801namespace nix {
802
803
804RegisterStoreImplementation::Implementations * RegisterStoreImplementation::implementations = 0;
805
806
807ref<Store> openStore(const std::string & uri_,
808    const Store::Params & extraParams)
809{
810    auto uri(uri_);
811    Store::Params params(extraParams);
812    auto q = uri.find('?');
813    if (q != std::string::npos) {
814        for (auto s : tokenizeString<Strings>(uri.substr(q + 1), "&")) {
815            auto e = s.find('=');
816            if (e != std::string::npos)
817                params[s.substr(0, e)] = s.substr(e + 1);
818        }
819        uri = uri_.substr(0, q);
820    }
821
822    for (auto fun : *RegisterStoreImplementation::implementations) {
823        auto store = fun(uri, params);
824        if (store) {
825            store->warnUnknownSettings();
826            return ref<Store>(store);
827        }
828    }
829
830    throw Error("don't know how to open Nix store '%s'", uri);
831}
832
833
834StoreType getStoreType(const std::string & uri, const std::string & stateDir)
835{
836    if (uri == "daemon") {
837        return tDaemon;
838    } else if (uri == "local") {
839        return tLocal;
840    } else if (uri == "" || uri == "auto") {
841        if (access(stateDir.c_str(), R_OK | W_OK) == 0)
842            return tLocal;
843        else if (pathExists(settings.nixDaemonSocketFile))
844            return tDaemon;
845        else
846            return tLocal;
847    } else {
848        return tOther;
849    }
850}
851
852
853static RegisterStoreImplementation regStore([](
854    const std::string & uri, const Store::Params & params)
855    -> std::shared_ptr<Store>
856{
857    switch (getStoreType(uri, get(params, "state", settings.nixStateDir))) {
858        case tDaemon:
859            return std::shared_ptr<Store>(std::make_shared<UDSRemoteStore>(params));
860        case tLocal:
861            return std::shared_ptr<Store>(std::make_shared<LocalStore>(params));
862        default:
863            return nullptr;
864    }
865});
866
867
868std::list<ref<Store>> getDefaultSubstituters()
869{
870    static auto stores([]() {
871        std::list<ref<Store>> stores;
872
873        StringSet done;
874
875        auto addStore = [&](const std::string & uri) {
876            if (done.count(uri)) return;
877            done.insert(uri);
878            stores.push_back(openStore(uri));
879        };
880
881        for (auto uri : settings.substituters.get())
882            addStore(uri);
883
884        for (auto uri : settings.extraSubstituters.get())
885            addStore(uri);
886
887        stores.sort([](ref<Store> & a, ref<Store> & b) {
888            return a->getPriority() < b->getPriority();
889        });
890
891        return stores;
892    } ());
893
894    return stores;
895}
896
897
898}
899