chiark / gitweb /
add set_replace()
[elogind.git] / manager.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <assert.h>
4 #include <errno.h>
5 #include <string.h>
6
7 #include "manager.h"
8 #include "hashmap.h"
9 #include "macro.h"
10 #include "strv.h"
11 #include "load-fragment.h"
12
13 Manager* manager_new(void) {
14         Manager *m;
15
16         if (!(m = new0(Manager, 1)))
17                 return NULL;
18
19         if (!(m->names = hashmap_new(string_hash_func, string_compare_func)))
20                 goto fail;
21
22         if (!(m->jobs = hashmap_new(trivial_hash_func, trivial_compare_func)))
23                 goto fail;
24
25         if (!(m->jobs_to_add = hashmap_new(trivial_hash_func, trivial_compare_func)))
26                 goto fail;
27
28         if (!(m->jobs_to_remove = set_new(trivial_hash_func, trivial_compare_func)))
29                 goto fail;
30
31         return m;
32
33 fail:
34         manager_free(m);
35         return NULL;
36 }
37
38 void manager_free(Manager *m) {
39         Name *n;
40
41         assert(m);
42
43         while ((n = hashmap_first(m->names)))
44                 name_free(n);
45
46         hashmap_free(m->names);
47         hashmap_free(m->jobs);
48
49         /* FIXME: This is incomplete */
50
51         hashmap_free(m->jobs_to_add);
52         set_free(m->jobs_to_remove);
53
54         free(m);
55 }
56
57 static void transaction_abort(Manager *m) {
58         Job *j;
59
60         assert(m);
61         assert(m->n_dependency_depth == 0);
62
63         while ((j = hashmap_steal_first(m->jobs_to_add)))
64                 job_free(j);
65
66         set_clear(m->jobs_to_remove);
67 }
68
69 static int transaction_activate(Manager *m) {
70         Job *j;
71         int r;
72         void *state;
73
74         assert(m);
75         assert(m->n_dependency_depth == 0);
76
77         /* This applies the changes recorded in jobs_to_add and
78          * jobs_to_remove to the actual list of jobs */
79
80         HASHMAP_FOREACH(j, m->jobs_to_add, state) {
81                 assert(!j->linked);
82
83                 if ((r = hashmap_put(j->manager->jobs, UINT32_TO_PTR(j->id), j)) < 0)
84                         goto rollback;
85         }
86
87         /* all entries are now registered, now make sure the names
88          * know about that. */
89
90         while ((j = hashmap_steal_first(m->jobs_to_add))) {
91                 j->name->meta.job = j;
92                 j->linked = true;
93         }
94
95         while ((j = set_steal_first(m->jobs_to_remove)))
96                 job_free(j);
97
98         return 0;
99
100 rollback:
101
102         HASHMAP_FOREACH(j, m->jobs_to_add, state)
103                 hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
104
105         transaction_abort(m);
106         return r;
107 }
108
109 int manager_add_job(Manager *m, JobType type, Name *name, JobMode mode, Job **_ret) {
110         Job *ret, *other;
111         void *state;
112         Name *dep;
113         int r;
114
115         assert(m);
116         assert(type < _JOB_TYPE_MAX);
117         assert(name);
118         assert(mode < _JOB_MODE_MAX);
119
120         /* Check for conflicts, first against the jobs we shall
121          * create */
122         if ((other = hashmap_get(m->jobs_to_add, name))) {
123
124                 if (other->type != type)
125                         return -EEXIST;
126
127         } else if (name->meta.job) {
128
129                 if (name->meta.job->type != type) {
130
131                         if (mode == JOB_FAIL)
132                                 return -EEXIST;
133
134                         if ((r = set_put(m->jobs_to_remove, name->meta.job)) < 0)
135                                 return r;
136                 }
137         }
138
139         if (!(ret = job_new(m, type, name)))
140                 return -ENOMEM;
141
142         m->n_dependency_depth ++;
143
144         if ((r = hashmap_put(m->jobs_to_add, name, ret)) < 0)
145                 goto fail;
146
147         if (type == JOB_START || type == JOB_VERIFY_STARTED || type == JOB_RESTART_FINISH) {
148                 SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRES], state)
149                         if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
150                                 goto fail;
151                 SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUIRES], state)
152                         if ((r = manager_add_job(m, type, dep, JOB_FAIL, NULL)) < 0)
153                                 goto fail;
154                 SET_FOREACH(dep, ret->name->meta.dependencies[NAME_WANTS], state)
155                         if ((r = manager_add_job(m, type, dep, JOB_FAIL, NULL)) < 0)
156                                 goto fail;
157                 SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUISITE], state)
158                         if ((r = manager_add_job(m, JOB_VERIFY_STARTED, dep, mode, NULL)) < 0)
159                                 goto fail;
160                 SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUISITE], state)
161                         if ((r = manager_add_job(m, JOB_VERIFY_STARTED, dep, JOB_FAIL, NULL)) < 0)
162                                 goto fail;
163                 SET_FOREACH(dep, ret->name->meta.dependencies[NAME_CONFLICTS], state)
164                         if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
165                                 goto fail;
166
167         } else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) {
168
169                 SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRED_BY], state)
170                         if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
171                                 goto fail;
172         }
173
174         if (--m->n_dependency_depth <= 0)
175                 if ((r = transaction_activate(m)) < 0) {
176                         transaction_abort(m);
177                         return r;
178                 }
179
180
181         if (_ret)
182                 *_ret = ret;
183
184         return 0;
185
186 fail:
187         job_free(ret);
188
189         if (--m->n_dependency_depth <= 0)
190                 transaction_abort(m);
191
192         return r;
193 }
194
195
196 Job *manager_get_job(Manager *m, uint32_t id) {
197         assert(m);
198
199         return hashmap_get(m->jobs, UINT32_TO_PTR(id));
200 }
201
202 Name *manager_get_name(Manager *m, const char *name) {
203         assert(m);
204         assert(name);
205
206         return hashmap_get(m->names, name);
207 }
208
209 static int verify_type(Name *name) {
210         char *n;
211         void *state;
212
213         assert(name);
214
215         /* Checks that all aliases of this name have the same and valid type */
216
217         SET_FOREACH(n, name->meta.names, state) {
218                 NameType t;
219
220                 if ((t = name_type_from_string(n)) == _NAME_TYPE_INVALID)
221                         return -EINVAL;
222
223                 if (name->meta.type == _NAME_TYPE_INVALID) {
224                         name->meta.type = t;
225                         continue;
226                 }
227
228                 if (name->meta.type != t)
229                         return -EINVAL;
230         }
231
232         if (name->meta.type == _NAME_TYPE_INVALID)
233                 return -EINVAL;
234
235         return 0;
236 }
237
238 static int service_load_sysv(Service *s) {
239         assert(s);
240
241         /* Load service data from SysV init scripts, preferably with
242          * LSB headers ... */
243
244         return 0;
245 }
246
247 static int name_load_fstab(Name *n) {
248         assert(n);
249         assert(n->meta.type == NAME_MOUNT || n->meta.type == NAME_AUTOMOUNT);
250
251         /* Load mount data from /etc/fstab */
252
253         return 0;
254 }
255
256 static int snapshot_load(Snapshot *s) {
257         assert(s);
258
259         /* Load snapshots from disk */
260
261         return 0;
262 }
263
264 static int name_load_dropin(Name *n) {
265         assert(n);
266
267         /* Load dependencies from drop-in directories */
268
269         return 0;
270 }
271
272 static int load(Name *name) {
273         int r;
274
275         assert(name);
276
277         if (name->meta.state != NAME_STUB)
278                 return 0;
279
280         if ((r = verify_type(name)) < 0)
281                 return r;
282
283         if (name->meta.type == NAME_SERVICE) {
284
285                 /* Load a .service file */
286                 if ((r = name_load_fragment(name)) == 0)
287                         goto finish;
288
289                 /* Load a classic init script */
290                 if (r == -ENOENT)
291                         if ((r = service_load_sysv(SERVICE(name))) == 0)
292                                 goto finish;
293
294         } else if (name->meta.type == NAME_MOUNT ||
295                    name->meta.type == NAME_AUTOMOUNT) {
296
297                 if ((r = name_load_fstab(name)) == 0)
298                         goto finish;
299
300         } else if (name->meta.type == NAME_SNAPSHOT) {
301
302                 if ((r = snapshot_load(SNAPSHOT(name))) == 0)
303                         goto finish;
304
305         } else {
306                 if ((r = name_load_fragment(name)) == 0)
307                         goto finish;
308         }
309
310         name->meta.state = NAME_FAILED;
311         return r;
312
313 finish:
314         if ((r = name_load_dropin(name)) < 0)
315                 return r;
316
317         if ((r = name_link_names(name)) < 0)
318                 return r;
319
320         name->meta.state = NAME_LOADED;
321         return 0;
322 }
323
324 static int dispatch_load_queue(Manager *m) {
325         Meta *meta;
326
327         assert(m);
328
329         /* Make sure we are not run recursively */
330         if (m->dispatching_load_queue)
331                 return 0;
332
333         m->dispatching_load_queue = true;
334
335         /* Dispatches the load queue. Takes a name from the queue and
336          * tries to load its data until the queue is empty */
337
338         while ((meta = m->load_queue)) {
339                 load(NAME(meta));
340                 LIST_REMOVE(Meta, m->load_queue, meta);
341         }
342
343         m->dispatching_load_queue = false;
344
345         return 0;
346 }
347
348 int manager_load_name(Manager *m, const char *name, Name **_ret) {
349         Name *ret;
350         NameType t;
351         int r;
352         char *n;
353
354         assert(m);
355         assert(name);
356         assert(_ret);
357
358         if (!name_is_valid(name))
359                 return -EINVAL;
360
361         /* This will load the service information files, but not actually
362          * start any services or anything */
363
364         if ((ret = manager_get_name(m, name)))
365                 goto finish;
366
367         if ((t = name_type_from_string(name)) == _NAME_TYPE_INVALID)
368                 return -EINVAL;
369
370         if (!(ret = name_new(m)))
371                 return -ENOMEM;
372
373         ret->meta.type = t;
374
375         if (!(n = strdup(name))) {
376                 name_free(ret);
377                 return -ENOMEM;
378         }
379
380         if (set_put(ret->meta.names, n) < 0) {
381                 name_free(ret);
382                 free(n);
383                 return -ENOMEM;
384         }
385
386         if ((r = name_link(ret)) < 0) {
387                 name_free(ret);
388                 return r;
389         }
390
391         /* At this point the new entry is created and linked. However,
392          * not loaded. Now load this entry and all its dependencies
393          * recursively */
394
395         dispatch_load_queue(m);
396
397 finish:
398
399         *_ret = ret;
400         return 0;
401 }
402
403 void manager_dump_jobs(Manager *s, FILE *f) {
404         void *state;
405         Job *j;
406
407         assert(s);
408         assert(f);
409
410         HASHMAP_FOREACH(j, s->jobs, state)
411                 job_dump(j, f);
412 }
413
414 void manager_dump_names(Manager *s, FILE *f) {
415         void *state;
416         Name *n;
417         const char *t;
418
419         assert(s);
420         assert(f);
421
422         HASHMAP_FOREACH_KEY(n, t, s->names, state)
423                 if (name_id(n) == t)
424                         name_dump(n, f);
425 }