aboutsummaryrefslogtreecommitdiff
path: root/src/libstore/profiles.hh
blob: 9672a17bdb1f5e9754fd64de2dd42a66c53ea725 (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
#pragma once
/**
 * @file Implementation of Profiles.
 *
 * See the manual for additional information.
 */

#include "types.hh"
#include "pathlocks.hh"

#include <time.h>


namespace nix {

class StorePath;


/**
 * A positive number identifying a generation for a given profile.
 *
 * Generation numbers are assigned sequentially. Each new generation is
 * assigned 1 + the current highest generation number.
 */
typedef uint64_t GenerationNumber;

/**
 * A generation is a revision of a profile.
 *
 * Each generation is a mapping (key-value pair) from an identifier
 * (`number`) to a store object (specified by `path`).
 */
struct Generation
{
    /**
     * The number of a generation is its unique identifier within the
     * profile.
     */
    GenerationNumber number;
    /**
     * The store path identifies the store object that is the contents
     * of the generation.
     *
     * These store paths / objects are not unique to the generation
     * within a profile. Lix tries to ensure successive generations have
     * distinct contents to avoid bloat, but nothing stops two
     * non-adjacent generations from having the same contents.
     *
     * @todo Use `StorePath` instead of `Path`?
     */
    Path path;

    /**
     * When the generation was created. This is extra metadata about the
     * generation used to make garbage collecting old generations more
     * convenient.
     */
    time_t creationTime;
};

/**
 * All the generations of a profile
 */
typedef std::list<Generation> Generations;


/**
 * Find all generations for the given profile.
 *
 * @param profile A profile specified by its name and location combined
 * into a path. E.g. if "foo" is the name of the profile, and "/bar/baz"
 * is the directory it is in, then the path "/bar/baz/foo" would be the
 * argument for this parameter.
 *
 * @return The pair of:
 *
 *   - The list of currently present generations for the specified profile,
 *     sorted by ascending generation number.
 *
 *   - The number of the current/active generation.
 *
 * Note that the current/active generation need not be the latest one.
 */
std::pair<Generations, std::optional<GenerationNumber>> findGenerations(Path profile);

class LocalFSStore;

/**
 * Create a new generation of the given profile
 *
 * If the previous generation (not the currently active one!) has a
 * distinct store object, a fresh generation number is mapped to the
 * given store object, referenced by path. Otherwise, the previous
 * generation is assumed.
 *
 * The behavior of reusing existing generations like this makes this
 * procedure idempotent. It also avoids clutter.
 */
Path createGeneration(LocalFSStore & store, Path profile, StorePath outPath);

/**
 * Unconditionally delete a generation
 *
 * @param profile A profile specified by its name and location combined into a path.
 *
 * @param gen The generation number specifying exactly which generation
 * to delete.
 *
 * Because there is no check of whether the generation to delete is
 * active, this is somewhat unsafe.
 *
 * @todo Should we expose this at all?
 */
void deleteGeneration(const Path & profile, GenerationNumber gen);

/**
 * Delete the given set of generations.
 *
 * @param profile The profile, specified by its name and location combined into a path, whose generations we want to delete.
 *
 * @param gensToDelete The generations to delete, specified by a set of
 * numbers.
 *
 * @param dryRun Log what would be deleted instead of actually doing
 * so.
 *
 * Trying to delete the currently active generation will fail, and cause
 * no generations to be deleted.
 */
void deleteGenerations(const Path & profile, const std::set<GenerationNumber> & gensToDelete, bool dryRun);

/**
 * Delete generations older than `max` passed the current generation.
 *
 * @param profile The profile, specified by its name and location combined into a path, whose generations we want to delete.
 *
 * @param max How many generations to keep up to the current one. Must
 * be at least 1 so we don't delete the current one.
 *
 * @param dryRun Log what would be deleted instead of actually doing
 * so.
 */
void deleteGenerationsGreaterThan(const Path & profile, GenerationNumber max, bool dryRun);

/**
 * Delete all generations other than the current one
 *
 * @param profile The profile, specified by its name and location combined into a path, whose generations we want to delete.
 *
 * @param dryRun Log what would be deleted instead of actually doing
 * so.
 */
void deleteOldGenerations(const Path & profile, bool dryRun);

/**
 * Delete generations older than `t`, except for the most recent one
 * older than `t`.
 *
 * @param profile The profile, specified by its name and location combined into a path, whose generations we want to delete.
 *
 * @param dryRun Log what would be deleted instead of actually doing
 * so.
 */
void deleteGenerationsOlderThan(const Path & profile, time_t t, bool dryRun);

/**
 * Parse a temp spec intended for `deleteGenerationsOlderThan()`.
 *
 * Throws an exception if `timeSpec` fails to parse.
 */
time_t parseOlderThanTimeSpec(std::string_view timeSpec);

/**
 * Smaller wrapper around `replaceSymlink` for replacing the current
 * generation of a profile. Does not enforce proper structure.
 *
 * @todo Always use `switchGeneration()` instead, and delete this.
 */
void switchLink(Path link, Path target);

/**
 * Roll back a profile to the specified generation, or to the most
 * recent one older than the current.
 */
void switchGeneration(
    const Path & profile,
    std::optional<GenerationNumber> dstGen,
    bool dryRun);

/**
 * Ensure exclusive access to a profile.  Any command that modifies
 * the profile first acquires this lock.
 */
void lockProfile(PathLocks & lock, const Path & profile);

/**
 * Optimistic locking is used by long-running operations like `nix-env
 * -i'.  Instead of acquiring the exclusive lock for the entire
 * duration of the operation, we just perform the operation
 * optimistically (without an exclusive lock), and check at the end
 * whether the profile changed while we were busy (i.e., the symlink
 * target changed).  If so, the operation is restarted.  Restarting is
 * generally cheap, since the build results are still in the Nix
 * store.  Most of the time, only the user environment has to be
 * rebuilt.
 */
std::string optimisticLockProfile(const Path & profile);

/**
 * Create and return the path to a directory suitable for storing the user’s
 * profiles.
 */
Path profilesDir();

/**
 * Return the path to the profile directory for root (but don't try creating it)
 */
Path rootProfilesDir();

/**
 * Create and return the path to the file used for storing the users's channels
 */
Path defaultChannelsDir();

/**
 * Return the path to the channel directory for root (but don't try creating it)
 */
Path rootChannelsDir();

/**
 * Resolve the default profile (~/.nix-profile by default,
 * $XDG_STATE_HOME/nix/profile if XDG Base Directory Support is enabled),
 * and create if doesn't exist
 */
Path getDefaultProfile();

}