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, lvlInfo, actCopyPath, fmt("copying path '%s'", storePath),
569        {storePath, srcStore->getUri(), dstStore->getUri()});
570    PushActivity pact(act.id);
571
572    auto info = srcStore->queryPathInfo(storePath);
573
574    uint64_t total = 0;
575
576    auto progress = [&](size_t len) {
577        total += len;
578        act.progress(total, info->narSize);
579    };
580
581    struct MyStringSink : StringSink
582    {
583        typedef std::function<void(size_t)> Callback;
584        Callback callback;
585        MyStringSink(Callback callback) : callback(callback) { }
586        void operator () (const unsigned char * data, size_t len) override
587        {
588            StringSink::operator ()(data, len);
589            callback(len);
590        };
591    };
592
593    MyStringSink sink(progress);
594    srcStore->narFromPath({storePath}, sink);
595
596    if (!info->narHash) {
597        auto info2 = make_ref<ValidPathInfo>(*info);
598        info2->narHash = hashString(htSHA256, *sink.s);
599        if (!info->narSize) info2->narSize = sink.s->size();
600        info = info2;
601    }
602
603    if (info->ultimate) {
604        auto info2 = make_ref<ValidPathInfo>(*info);
605        info2->ultimate = false;
606        info = info2;
607    }
608
609    dstStore->addToStore(*info, sink.s, repair, checkSigs);
610}
611
612
613void copyPaths(ref<Store> srcStore, ref<Store> dstStore, const PathSet & storePaths,
614    RepairFlag repair, CheckSigsFlag checkSigs, SubstituteFlag substitute)
615{
616    PathSet valid = dstStore->queryValidPaths(storePaths, substitute);
617
618    PathSet missing;
619    for (auto & path : storePaths)
620        if (!valid.count(path)) missing.insert(path);
621
622    Activity act(*logger, lvlInfo, actCopyPaths, fmt("copying %d paths", missing.size()));
623
624    std::atomic<size_t> nrDone{0};
625    std::atomic<uint64_t> bytesExpected{0};
626    std::atomic<uint64_t> nrRunning{0};
627
628    auto showProgress = [&]() {
629        act.progress(nrDone, missing.size(), nrRunning);
630    };
631
632    ThreadPool pool;
633
634    processGraph<Path>(pool,
635        PathSet(missing.begin(), missing.end()),
636
637        [&](const Path & storePath) {
638            if (dstStore->isValidPath(storePath)) {
639                nrDone++;
640                showProgress();
641                return PathSet();
642            }
643
644            auto info = srcStore->queryPathInfo(storePath);
645
646            bytesExpected += info->narSize;
647            act.setExpected(actCopyPath, bytesExpected);
648
649            return info->references;
650        },
651
652        [&](const Path & storePath) {
653            checkInterrupt();
654
655            if (!dstStore->isValidPath(storePath)) {
656                MaintainCount<decltype(nrRunning)> mc(nrRunning);
657                showProgress();
658                copyStorePath(srcStore, dstStore, storePath, repair, checkSigs);
659            }
660
661            nrDone++;
662            showProgress();
663        });
664}
665
666
667void copyClosure(ref<Store> srcStore, ref<Store> dstStore,
668    const PathSet & storePaths, RepairFlag repair, CheckSigsFlag checkSigs,
669    SubstituteFlag substitute)
670{
671    PathSet closure;
672    srcStore->computeFSClosure({storePaths}, closure);
673    copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute);
674}
675
676
677ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven)
678{
679    ValidPathInfo info;
680    getline(str, info.path);
681    if (str.eof()) { info.path = ""; return info; }
682    if (hashGiven) {
683        string s;
684        getline(str, s);
685        info.narHash = Hash(s, htSHA256);
686        getline(str, s);
687        if (!string2Int(s, info.narSize)) throw Error("number expected");
688    }
689    getline(str, info.deriver);
690    string s; int n;
691    getline(str, s);
692    if (!string2Int(s, n)) throw Error("number expected");
693    while (n--) {
694        getline(str, s);
695        info.references.insert(s);
696    }
697    if (!str || str.eof()) throw Error("missing input");
698    return info;
699}
700
701
702string showPaths(const PathSet & paths)
703{
704    string s;
705    for (auto & i : paths) {
706        if (s.size() != 0) s += ", ";
707        s += "'" + i + "'";
708    }
709    return s;
710}
711
712
713std::string ValidPathInfo::fingerprint() const
714{
715    if (narSize == 0 || !narHash)
716        throw Error(format("cannot calculate fingerprint of path '%s' because its size/hash is not known")
717            % path);
718    return
719        "1;" + path + ";"
720        + narHash.to_string(Base32) + ";"
721        + std::to_string(narSize) + ";"
722        + concatStringsSep(",", references);
723}
724
725
726void ValidPathInfo::sign(const SecretKey & secretKey)
727{
728    sigs.insert(secretKey.signDetached(fingerprint()));
729}
730
731
732bool ValidPathInfo::isContentAddressed(const Store & store) const
733{
734    auto warn = [&]() {
735        printError(format("warning: path '%s' claims to be content-addressed but isn't") % path);
736    };
737
738    if (hasPrefix(ca, "text:")) {
739        Hash hash(std::string(ca, 5));
740        if (store.makeTextPath(storePathToName(path), hash, references) == path)
741            return true;
742        else
743            warn();
744    }
745
746    else if (hasPrefix(ca, "fixed:")) {
747        bool recursive = ca.compare(6, 2, "r:") == 0;
748        Hash hash(std::string(ca, recursive ? 8 : 6));
749        if (store.makeFixedOutputPath(recursive, hash, storePathToName(path)) == path)
750            return true;
751        else
752            warn();
753    }
754
755    return false;
756}
757
758
759size_t ValidPathInfo::checkSignatures(const Store & store, const PublicKeys & publicKeys) const
760{
761    if (isContentAddressed(store)) return maxSigs;
762
763    size_t good = 0;
764    for (auto & sig : sigs)
765        if (checkSignature(publicKeys, sig))
766            good++;
767    return good;
768}
769
770
771bool ValidPathInfo::checkSignature(const PublicKeys & publicKeys, const std::string & sig) const
772{
773    return verifyDetached(fingerprint(), sig, publicKeys);
774}
775
776
777Strings ValidPathInfo::shortRefs() const
778{
779    Strings refs;
780    for (auto & r : references)
781        refs.push_back(baseNameOf(r));
782    return refs;
783}
784
785
786std::string makeFixedOutputCA(bool recursive, const Hash & hash)
787{
788    return "fixed:" + (recursive ? (std::string) "r:" : "") + hash.to_string();
789}
790
791
792}
793
794
795#include "local-store.hh"
796#include "remote-store.hh"
797
798
799namespace nix {
800
801
802RegisterStoreImplementation::Implementations * RegisterStoreImplementation::implementations = 0;
803
804
805ref<Store> openStore(const std::string & uri_,
806    const Store::Params & extraParams)
807{
808    auto uri(uri_);
809    Store::Params params(extraParams);
810    auto q = uri.find('?');
811    if (q != std::string::npos) {
812        for (auto s : tokenizeString<Strings>(uri.substr(q + 1), "&")) {
813            auto e = s.find('=');
814            if (e != std::string::npos)
815                params[s.substr(0, e)] = s.substr(e + 1);
816        }
817        uri = uri_.substr(0, q);
818    }
819
820    for (auto fun : *RegisterStoreImplementation::implementations) {
821        auto store = fun(uri, params);
822        if (store) {
823            store->warnUnknownSettings();
824            return ref<Store>(store);
825        }
826    }
827
828    throw Error("don't know how to open Nix store '%s'", uri);
829}
830
831
832StoreType getStoreType(const std::string & uri, const std::string & stateDir)
833{
834    if (uri == "daemon") {
835        return tDaemon;
836    } else if (uri == "local") {
837        return tLocal;
838    } else if (uri == "" || uri == "auto") {
839        if (access(stateDir.c_str(), R_OK | W_OK) == 0)
840            return tLocal;
841        else if (pathExists(settings.nixDaemonSocketFile))
842            return tDaemon;
843        else
844            return tLocal;
845    } else {
846        return tOther;
847    }
848}
849
850
851static RegisterStoreImplementation regStore([](
852    const std::string & uri, const Store::Params & params)
853    -> std::shared_ptr<Store>
854{
855    switch (getStoreType(uri, get(params, "state", settings.nixStateDir))) {
856        case tDaemon:
857            return std::shared_ptr<Store>(std::make_shared<UDSRemoteStore>(params));
858        case tLocal:
859            return std::shared_ptr<Store>(std::make_shared<LocalStore>(params));
860        default:
861            return nullptr;
862    }
863});
864
865
866std::list<ref<Store>> getDefaultSubstituters()
867{
868    static auto stores([]() {
869        std::list<ref<Store>> stores;
870
871        StringSet done;
872
873        auto addStore = [&](const std::string & uri) {
874            if (done.count(uri)) return;
875            done.insert(uri);
876            stores.push_back(openStore(uri));
877        };
878
879        for (auto uri : settings.substituters.get())
880            addStore(uri);
881
882        for (auto uri : settings.extraSubstituters.get())
883            addStore(uri);
884
885        stores.sort([](ref<Store> & a, ref<Store> & b) {
886            return a->getPriority() < b->getPriority();
887        });
888
889        return stores;
890    } ());
891
892    return stores;
893}
894
895
896}
897