chiark / gitweb /
add functions for dumping server state
[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 int manager_add_job(Manager *m, JobType type, Name *name, JobMode mode, Job **_ret) {
58         Job *ret, *other;
59         void *state;
60         Name *dep;
61         int r;
62
63         assert(m);
64         assert(type < _JOB_TYPE_MAX);
65         assert(name);
66         assert(mode < _JOB_MODE_MAX);
67         assert(_ret);
68
69         /* Check for conflicts, first against the jobs we shall
70          * create */
71         if ((other = hashmap_get(m->jobs_to_add, name))) {
72
73                 if (other->type != type)
74                         return -EEXIST;
75
76         } else if (name->meta.job) {
77
78                 if (name->meta.job->type != type) {
79
80                         if (mode == JOB_FAIL)
81                                 return -EEXIST;
82
83                         if ((r = set_put(m->jobs_to_remove, name->meta.job)) < 0)
84                                 return r;
85                 }
86         }
87
88         if (!(ret = job_new(m, type, name)))
89                 return -ENOMEM;
90
91         if ((r = hashmap_put(m->jobs_to_add, name, ret)) < 0)
92                 goto fail;
93
94         if (type == JOB_START || type == JOB_VERIFY_STARTED || type == JOB_RESTART_FINISH) {
95                 SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRES], state)
96                         if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
97                                 goto fail;
98                 SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUIRES], state)
99                         if ((r = manager_add_job(m, type, dep, JOB_FAIL, NULL)) < 0)
100                                 goto fail;
101                 SET_FOREACH(dep, ret->name->meta.dependencies[NAME_WANTS], state)
102                         if ((r = manager_add_job(m, type, dep, JOB_FAIL, NULL)) < 0)
103                                 goto fail;
104                 SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUISITE], state)
105                         if ((r = manager_add_job(m, JOB_VERIFY_STARTED, dep, mode, NULL)) < 0)
106                                 goto fail;
107                 SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUISITE], state)
108                         if ((r = manager_add_job(m, JOB_VERIFY_STARTED, dep, JOB_FAIL, NULL)) < 0)
109                                 goto fail;
110                 SET_FOREACH(dep, ret->name->meta.dependencies[NAME_CONFLICTS], state)
111                         if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
112                                 goto fail;
113
114         } else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) {
115
116                 SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRED_BY], state)
117                         if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
118                                 goto fail;
119         }
120
121         if (_ret)
122                 *_ret = ret;
123
124         return 0;
125
126 fail:
127         job_free(ret);
128
129         return r;
130 }
131
132
133 Job *manager_get_job(Manager *m, uint32_t id) {
134         assert(m);
135
136         return hashmap_get(m->jobs, UINT32_TO_PTR(id));
137 }
138
139 Name *manager_get_name(Manager *m, const char *name) {
140         assert(m);
141         assert(name);
142
143         return hashmap_get(m->names, name);
144 }
145
146 static int verify_type(Name *name) {
147         char *n;
148         void *state;
149
150         assert(name);
151
152         /* Checks that all aliases of this name have the same and valid type */
153
154         SET_FOREACH(n, name->meta.names, state) {
155                 NameType t;
156
157                 if ((t = name_type_from_string(n)) == _NAME_TYPE_INVALID)
158                         return -EINVAL;
159
160                 if (name->meta.type == _NAME_TYPE_INVALID) {
161                         name->meta.type = t;
162                         continue;
163                 }
164
165                 if (name->meta.type != t)
166                         return -EINVAL;
167         }
168
169         if (name->meta.type == _NAME_TYPE_INVALID)
170                 return -EINVAL;
171
172         return 0;
173 }
174
175 static int service_load_sysv(Service *s) {
176         assert(s);
177
178         /* Load service data from SysV init scripts, preferably with
179          * LSB headers ... */
180
181         return 0;
182 }
183
184 static int name_load_fstab(Name *n) {
185         assert(n);
186         assert(n->meta.type == NAME_MOUNT || n->meta.type == NAME_AUTOMOUNT);
187
188         /* Load mount data from /etc/fstab */
189
190         return 0;
191 }
192
193 static int snapshot_load(Snapshot *s) {
194         assert(s);
195
196         /* Load snapshots from disk */
197
198         return 0;
199 }
200
201 static int name_load_dropin(Name *n) {
202         assert(n);
203
204         /* Load dependencies from drop-in directories */
205
206         return 0;
207 }
208
209 static int load(Name *name) {
210         int r;
211
212         assert(name);
213
214         if (name->meta.state != NAME_STUB)
215                 return 0;
216
217         if ((r = verify_type(name)) < 0)
218                 return r;
219
220         if (name->meta.type == NAME_SERVICE) {
221
222                 /* Load a .service file */
223                 if ((r = name_load_fragment(name)) == 0)
224                         goto finish;
225
226                 /* Load a classic init script */
227                 if (r == -ENOENT)
228                         if ((r = service_load_sysv(SERVICE(name))) == 0)
229                                 goto finish;
230
231         } else if (name->meta.type == NAME_MOUNT ||
232                    name->meta.type == NAME_AUTOMOUNT) {
233
234                 if ((r = name_load_fstab(name)) == 0)
235                         goto finish;
236
237         } else if (name->meta.type == NAME_SNAPSHOT) {
238
239                 if ((r = snapshot_load(SNAPSHOT(name))) == 0)
240                         goto finish;
241
242         } else {
243                 if ((r = name_load_fragment(name)) == 0)
244                         goto finish;
245         }
246
247         name->meta.state = NAME_FAILED;
248         return r;
249
250 finish:
251         if ((r = name_load_dropin(name)) < 0)
252                 return r;
253
254         name->meta.state = NAME_LOADED;
255         return 0;
256 }
257
258 static int dispatch_load_queue(Manager *m) {
259         Meta *meta;
260
261         assert(m);
262
263         /* Make sure we are not run recursively */
264         if (m->dispatching_load_queue)
265                 return 0;
266
267         m->dispatching_load_queue = true;
268
269         /* Dispatches the load queue. Takes a name from the queue and
270          * tries to load its data until the queue is empty */
271
272         while ((meta = m->load_queue)) {
273                 load(NAME(meta));
274                 LIST_REMOVE(Meta, m->load_queue, meta);
275         }
276
277         m->dispatching_load_queue = false;
278
279         return 0;
280 }
281
282 int manager_load_name(Manager *m, const char *name, Name **_ret) {
283         Name *ret;
284         NameType t;
285         int r;
286         char *n;
287
288         assert(m);
289         assert(name);
290         assert(_ret);
291
292         if (!name_is_valid(name))
293                 return -EINVAL;
294
295         /* This will load the service information files, but not actually
296          * start any services or anything */
297
298         if ((ret = manager_get_name(m, name)))
299                 goto finish;
300
301         if ((t = name_type_from_string(name)) == _NAME_TYPE_INVALID)
302                 return -EINVAL;
303
304         if (!(ret = name_new(m)))
305                 return -ENOMEM;
306
307         ret->meta.type = t;
308
309         if (!(n = strdup(name))) {
310                 name_free(ret);
311                 return -ENOMEM;
312         }
313
314         if (set_put(ret->meta.names, n) < 0) {
315                 name_free(ret);
316                 free(n);
317                 return -ENOMEM;
318         }
319
320         if ((r = name_link(ret)) < 0) {
321                 name_free(ret);
322                 return r;
323         }
324
325         /* At this point the new entry is created and linked. However,
326          * not loaded. Now load this entry and all its dependencies
327          * recursively */
328
329         dispatch_load_queue(m);
330
331 finish:
332
333         *_ret = ret;
334         return 0;
335 }
336
337 void manager_dump_jobs(Manager *s, FILE *f) {
338         void *state;
339         Job *j;
340
341         assert(s);
342         assert(f);
343
344         HASHMAP_FOREACH(j, s->jobs, state)
345                 job_dump(j, f);
346 }
347
348 void manager_dump_names(Manager *s, FILE *f) {
349         void *state;
350         Name *n;
351
352         assert(s);
353         assert(f);
354
355         HASHMAP_FOREACH(n, s->names, state)
356                 name_dump(n, f);
357 }