chiark / gitweb /
97c99e50465538f100b39f46c4a53cce70956c3a
[elogind.git] / manager.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <assert.h>
4 #include <errno.h>
5
6 #include "manager.h"
7 #include "hashmap.h"
8 #include "macro.h"
9 #include "strv.h"
10 #include "load-fragment.h"
11
12 Manager* manager_new(void) {
13         Manager *m;
14
15         if (!(m = new0(Manager, 1)))
16                 return NULL;
17
18         if (!(m->names = hashmap_new(string_hash_func, string_compare_func)))
19                 goto fail;
20
21         if (!(m->jobs = hashmap_new(trivial_hash_func, trivial_compare_func)))
22                 goto fail;
23
24         if (!(m->jobs_to_add = hashmap_new(trivial_hash_func, trivial_compare_func)))
25                 goto fail;
26
27         if (!(m->jobs_to_remove = set_new(trivial_hash_func, trivial_compare_func)))
28                 goto fail;
29
30         return m;
31
32 fail:
33         manager_free(m);
34         return NULL;
35 }
36
37 void manager_free(Manager *m) {
38         Name *n;
39
40         assert(m);
41
42         while ((n = hashmap_first(m->names)))
43                 name_free(n);
44
45         hashmap_free(m->names);
46         hashmap_free(m->jobs);
47
48         /* FIXME: This is incomplete */
49
50         hashmap_free(m->jobs_to_add);
51         set_free(m->jobs_to_remove);
52
53         free(m);
54 }
55
56 int manager_add_job(Manager *m, JobType type, Name *name, JobMode mode, Job **_ret) {
57         Job *ret, *other;
58         void *state;
59         Name *dep;
60         int r;
61
62         assert(m);
63         assert(type < _JOB_TYPE_MAX);
64         assert(name);
65         assert(mode < _JOB_MODE_MAX);
66         assert(_ret);
67
68         /* Check for conflicts, first against the jobs we shall
69          * create */
70         if ((other = hashmap_get(m->jobs_to_add, name))) {
71
72                 if (other->type != type)
73                         return -EEXIST;
74
75         } else if (name->meta.job) {
76
77                 if (name->meta.job->type != type) {
78
79                         if (mode == JOB_FAIL)
80                                 return -EEXIST;
81
82                         if ((r = set_put(m->jobs_to_remove, name->meta.job)) < 0)
83                                 return r;
84                 }
85         }
86
87         if (!(ret = job_new(m, type, name)))
88                 return -ENOMEM;
89
90         if ((r = hashmap_put(m->jobs_to_add, name, ret)) < 0)
91                 goto fail;
92
93         if (type == JOB_START || type == JOB_VERIFY_STARTED || type == JOB_RESTART_FINISH) {
94                 SET_FOREACH(dep, ret->name->meta.requires, state)
95                         if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
96                                 goto fail;
97                 SET_FOREACH(dep, ret->name->meta.soft_requires, state)
98                         if ((r = manager_add_job(m, type, dep, JOB_FAIL, NULL)) < 0)
99                                 goto fail;
100                 SET_FOREACH(dep, ret->name->meta.wants, state)
101                         if ((r = manager_add_job(m, type, dep, JOB_FAIL, NULL)) < 0)
102                                 goto fail;
103                 SET_FOREACH(dep, ret->name->meta.requisite, state)
104                         if ((r = manager_add_job(m, JOB_VERIFY_STARTED, dep, mode, NULL)) < 0)
105                                 goto fail;
106                 SET_FOREACH(dep, ret->name->meta.soft_requisite, state)
107                         if ((r = manager_add_job(m, JOB_VERIFY_STARTED, dep, JOB_FAIL, NULL)) < 0)
108                                 goto fail;
109                 SET_FOREACH(dep, ret->name->meta.conflicts, state)
110                         if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
111                                 goto fail;
112
113         } else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) {
114
115                 SET_FOREACH(dep, ret->name->meta.required_by, state)
116                         if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
117                                 goto fail;
118         }
119
120         if (_ret)
121                 *_ret = ret;
122
123         return 0;
124
125 fail:
126         job_free(ret);
127
128         return r;
129 }
130
131
132 Job *manager_get_job(Manager *m, uint32_t id) {
133         assert(m);
134
135         return hashmap_get(m->jobs, UINT32_TO_PTR(id));
136 }
137
138 Name *manager_get_name(Manager *m, const char *name) {
139         assert(m);
140         assert(name);
141
142         return hashmap_get(m->names, name);
143 }
144
145 static int detect_type(Name *name) {
146         char **n;
147
148         assert(name);
149
150         name->meta.type = _NAME_TYPE_INVALID;
151
152         STRV_FOREACH(n, name->meta.names) {
153                 NameType t;
154
155                 if ((t = name_type_from_string(*n)) == _NAME_TYPE_INVALID)
156                         return -EINVAL;
157
158                 if (name->meta.type == _NAME_TYPE_INVALID) {
159                         name->meta.type = t;
160                         continue;
161                 }
162
163                 if (name->meta.type != t)
164                         return -EINVAL;
165         }
166
167         return 0;
168 }
169
170 static int service_load_sysv(Service *s) {
171         assert(s);
172
173         /* Load service data from SysV init scripts, preferably with
174          * LSB headers ... */
175
176         return 0;
177 }
178
179 static int name_load_fstab(Name *n) {
180         assert(n);
181         assert(n->meta.type == NAME_MOUNT || n->meta.type == NAME_AUTOMOUNT);
182
183         /* Load mount data from /etc/fstab */
184
185         return 0;
186 }
187
188 static int snapshot_load(Snapshot *s) {
189         assert(s);
190
191         /* Load snapshots from disk */
192
193         return 0;
194 }
195
196 static int name_load_dropin(Name *n) {
197         assert(n);
198
199         /* Load dependencies from drop-in directories */
200
201         return 0;
202 }
203
204 static int load(Name *name) {
205         int r;
206
207         assert(name);
208
209         if (name->meta.state != NAME_STUB)
210                 return 0;
211
212         if ((r = detect_type(name)) < 0)
213                 return r;
214
215         if (name->meta.type == NAME_SERVICE) {
216
217                 /* Load a .service file */
218                 if ((r = name_load_fragment(name)) == 0)
219                         goto finish;
220
221                 /* Load a classic init script */
222                 if (r == -ENOENT)
223                         if ((r = service_load_sysv(SERVICE(name))) == 0)
224                                 goto finish;
225
226         } else if (name->meta.type == NAME_MOUNT ||
227                    name->meta.type == NAME_AUTOMOUNT) {
228
229                 if ((r = name_load_fstab(name)) == 0)
230                         goto finish;
231
232         } else if (name->meta.type == NAME_SNAPSHOT) {
233
234                 if ((r = snapshot_load(SNAPSHOT(name))) == 0)
235                         goto finish;
236
237         } else {
238                 if ((r = name_load_fragment(name)) == 0)
239                         goto finish;
240         }
241
242         name->meta.state = NAME_FAILED;
243         return r;
244
245 finish:
246         if ((r = name_load_dropin(name)) < 0)
247                 return r;
248
249         name->meta.state = NAME_LOADED;
250         return 0;
251 }
252
253 static int dispatch_load_queue(Manager *m) {
254         Meta *meta;
255
256         assert(m);
257
258         /* Make sure we are not run recursively */
259         if (m->dispatching_load_queue)
260                 return 0;
261
262         m->dispatching_load_queue = true;
263
264         /* Dispatches the load queue. Takes a name from the queue and
265          * tries to load its data until the queue is empty */
266
267         while ((meta = m->load_queue)) {
268                 load(NAME(meta));
269                 LIST_REMOVE(Meta, m->load_queue, meta);
270         }
271
272         m->dispatching_load_queue = false;
273
274         return 0;
275 }
276
277 int manager_load_name(Manager *m, const char *name, Name **_ret) {
278         Name *ret;
279         NameType t;
280         int r;
281
282         assert(m);
283         assert(name);
284         assert(_ret);
285
286         if (!name_is_valid(name))
287                 return -EINVAL;
288
289         /* This will load the service information files, but not actually
290          * start any services or anything */
291
292         if ((ret = manager_get_name(m, name)))
293                 goto finish;
294
295         if ((t = name_type_from_string(name)) == _NAME_TYPE_INVALID)
296                 return -EINVAL;
297
298         if (!(ret = name_new(m)))
299                 return -ENOMEM;
300
301         ret->meta.type = t;
302
303         if (!(ret->meta.names = strv_new(name, NULL))) {
304                 name_free(ret);
305                 return -ENOMEM;
306         }
307
308         if ((r = name_link(ret)) < 0) {
309                 name_free(ret);
310                 return r;
311         }
312
313         /* At this point the new entry is created and linked. However,
314          * not loaded. Now load this entry and all its dependencies
315          * recursively */
316
317         dispatch_load_queue(m);
318
319 finish:
320
321         *_ret = ret;
322         return 0;
323 }