chiark / gitweb /
implement transaction engine
[elogind.git] / name.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <assert.h>
4 #include <errno.h>
5 #include <string.h>
6
7 #include "set.h"
8 #include "name.h"
9 #include "macro.h"
10 #include "strv.h"
11
12 NameType name_type_from_string(const char *n) {
13         NameType t;
14         static const char* suffixes[_NAME_TYPE_MAX] = {
15                 [NAME_SERVICE] = ".service",
16                 [NAME_TIMER] = ".timer",
17                 [NAME_SOCKET] = ".socket",
18                 [NAME_MILESTONE] = ".milestone",
19                 [NAME_DEVICE] = ".device",
20                 [NAME_MOUNT] = ".mount",
21                 [NAME_AUTOMOUNT] = ".automount",
22                 [NAME_SNAPSHOT] = ".snapshot",
23         };
24
25         assert(n);
26
27         for (t = 0; t < _NAME_TYPE_MAX; t++)
28                 if (endswith(n, suffixes[t]))
29                         return t;
30
31         return _NAME_TYPE_INVALID;
32 }
33
34 #define VALID_CHARS                             \
35         "0123456789"                            \
36         "abcdefghijklmnopqrstuvwxyz"            \
37         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"            \
38         "-_"
39
40 bool name_is_valid(const char *n) {
41         NameType t;
42         const char *e, *i;
43
44         assert(n);
45
46         t = name_type_from_string(n);
47         if (t < 0 || t >= _NAME_TYPE_MAX)
48                 return false;
49
50         if (!(e = strrchr(n, '.')))
51                 return false;
52
53         for (i = n; i < e; i++)
54                 if (!strchr(VALID_CHARS, *i))
55                         return false;
56
57         return true;
58 }
59
60 Name *name_new(Manager *m) {
61         Name *n;
62
63         assert(m);
64
65         if (!(n = new0(Name, 1)))
66                 return NULL;
67
68         if (!(n->meta.names = set_new(string_hash_func, string_compare_func))) {
69                 free(n);
70                 return NULL;
71         }
72
73         /* Not much initialization happening here at this time */
74         n->meta.manager = m;
75         n->meta.type = _NAME_TYPE_INVALID;
76         n->meta.state = NAME_STUB;
77
78         /* We don't link the name here, that is left for name_link() */
79
80         return n;
81 }
82
83 /* FIXME: Does not rollback on failure! */
84 int name_link_names(Name *n, bool replace) {
85         char *t;
86         void *state;
87         int r;
88
89         assert(n);
90
91         if (!n->meta.linked)
92                 return 0;
93
94         /* Link all names that aren't linked yet. */
95
96         SET_FOREACH(t, n->meta.names, state)
97                 if (replace) {
98                         if ((r = hashmap_replace(n->meta.manager->names, t, n)) < 0)
99                                 return r;
100                 } else {
101                         if ((r = hashmap_put(n->meta.manager->names, t, n)) < 0)
102                                 return r;
103                 }
104
105         return 0;
106 }
107
108 int name_link(Name *n) {
109         int r;
110
111         assert(n);
112         assert(!set_isempty(n->meta.names));
113         assert(!n->meta.linked);
114
115         n->meta.linked = true;
116
117         if ((r = name_link_names(n, false) < 0)) {
118                 char *t;
119                 void *state;
120
121                 /* Rollback the registered names */
122                 SET_FOREACH(t, n->meta.names, state)
123                         hashmap_remove_value(n->meta.manager->names, t, n);
124
125                 n->meta.linked = false;
126                 return r;
127         }
128
129         if (n->meta.state == NAME_STUB)
130                 LIST_PREPEND(Meta, n->meta.manager->load_queue, &n->meta);
131
132         return 0;
133 }
134
135 static void bidi_set_free(Name *name, Set *s) {
136         void *state;
137         Name *other;
138
139         assert(name);
140
141         /* Frees the set and makes sure we are dropped from the
142          * inverse pointers */
143
144         SET_FOREACH(other, s, state) {
145                 NameDependency d;
146
147                 for (d = 0; d < _NAME_DEPENDENCY_MAX; d++)
148                         set_remove(other->meta.dependencies[d], name);
149         }
150
151         set_free(s);
152 }
153
154 void name_free(Name *name) {
155         NameDependency d;
156         char *t;
157
158         assert(name);
159
160         /* Detach from next 'bigger' objects */
161         if (name->meta.linked) {
162                 char *t;
163                 void *state;
164
165                 SET_FOREACH(t, name->meta.names, state)
166                         hashmap_remove_value(name->meta.manager->names, t, name);
167
168                 if (name->meta.state == NAME_STUB)
169                         LIST_REMOVE(Meta, name->meta.manager->load_queue, &name->meta);
170         }
171
172         /* Free data and next 'smaller' objects */
173         if (name->meta.job)
174                 job_free(name->meta.job);
175
176         for (d = 0; d < _NAME_DEPENDENCY_MAX; d++)
177                 bidi_set_free(name, name->meta.dependencies[d]);
178
179         switch (name->meta.type) {
180
181                 case NAME_SOCKET: {
182                         unsigned i;
183                         Socket *s = SOCKET(name);
184
185                         for (i = 0; i < s->n_fds; i++)
186                                 close_nointr(s->fds[i]);
187                         break;
188                 }
189
190                 case NAME_DEVICE: {
191                         Device *d = DEVICE(name);
192
193                         free(d->sysfs);
194                         break;
195                 }
196
197                 case NAME_MOUNT: {
198                         Mount *m = MOUNT(name);
199
200                         free(m->path);
201                         break;
202                 }
203
204                 case NAME_AUTOMOUNT: {
205                         Automount *a = AUTOMOUNT(name);
206
207                         free(a->path);
208                         break;
209                 }
210
211                 default:
212                         ;
213         }
214
215         free(name->meta.description);
216
217         while ((t = set_steal_first(name->meta.names)))
218                 free(t);
219         set_free(name->meta.names);
220
221         free(name);
222 }
223
224 bool name_is_ready(Name *name) {
225
226         assert(name);
227
228         if (name->meta.state != NAME_LOADED)
229                 return false;
230
231         assert(name->meta.type < _NAME_TYPE_MAX);
232
233         switch (name->meta.type) {
234                 case NAME_SERVICE: {
235                         Service *s = SERVICE(name);
236
237                         return
238                                 s->state == SERVICE_RUNNING ||
239                                 s->state == SERVICE_RELOAD_PRE ||
240                                 s->state == SERVICE_RELOAD ||
241                                 s->state == SERVICE_RELOAD_POST;
242                 }
243
244                 case NAME_TIMER: {
245                         Timer *t = TIMER(name);
246
247                         return
248                                 t->state == TIMER_WAITING ||
249                                 t->state == TIMER_RUNNING;
250                 }
251
252                 case NAME_SOCKET: {
253                         Socket *s = SOCKET(name);
254
255                         return
256                                 s->state == SOCKET_LISTENING ||
257                                 s->state == SOCKET_RUNNING;
258                 }
259
260                 case NAME_MILESTONE:
261                         return MILESTONE(name)->state == MILESTONE_ACTIVE;
262
263                 case NAME_DEVICE:
264                         return DEVICE(name)->state == DEVICE_AVAILABLE;
265
266                 case NAME_MOUNT:
267                         return MOUNT(name)->state == MOUNT_MOUNTED;
268
269                 case NAME_AUTOMOUNT: {
270                         Automount *a = AUTOMOUNT(name);
271
272                         return
273                                 a->state == AUTOMOUNT_WAITING ||
274                                 a->state == AUTOMOUNT_RUNNING;
275                 }
276
277                 case NAME_SNAPSHOT:
278                         return SNAPSHOT(name)->state == SNAPSHOT_ACTIVE;
279
280
281                 case _NAME_TYPE_MAX:
282                 case _NAME_TYPE_INVALID:
283                         ;
284         }
285
286         assert_not_reached("Unknown name type.");
287         return false;
288 }
289
290 static int ensure_in_set(Set **s, void *data) {
291         int r;
292
293         assert(s);
294         assert(data);
295
296         if (!*s)
297                 if (!(*s = set_new(trivial_hash_func, trivial_compare_func)))
298                         return -ENOMEM;
299
300         if ((r = set_put(*s, data) < 0))
301                 if (r != -EEXIST)
302                         return r;
303
304         return 0;
305 }
306
307 /* FIXME: Does not rollback on failure! */
308 int name_augment(Name *n) {
309         int r;
310         void* state;
311         Name *other;
312
313         assert(n);
314
315         /* Adds in the missing links to make all dependencies
316          * bidirectional. */
317
318         SET_FOREACH(other, n->meta.dependencies[NAME_BEFORE], state)
319                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_AFTER], n) < 0))
320                         return r;
321         SET_FOREACH(other, n->meta.dependencies[NAME_AFTER], state)
322                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_BEFORE], n) < 0))
323                         return r;
324
325         SET_FOREACH(other, n->meta.dependencies[NAME_CONFLICTS], state)
326                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_CONFLICTS], n) < 0))
327                         return r;
328
329         SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRES], state)
330                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_REQUIRED_BY], n) < 0))
331                         return r;
332         SET_FOREACH(other, n->meta.dependencies[NAME_REQUISITE], state)
333                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_REQUIRED_BY], n) < 0))
334                         return r;
335
336         SET_FOREACH(other, n->meta.dependencies[NAME_WANTS], state)
337                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n) < 0))
338                         return r;
339         SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUIRES], state)
340                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n) < 0))
341                         return r;
342         SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUISITE], state)
343                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n) < 0))
344                         return r;
345
346         return r;
347 }
348
349 static int ensure_merge(Set **s, Set *other) {
350
351         if (!other)
352                 return 0;
353
354         if (*s)
355                 return set_merge(*s, other);
356
357         if (!(*s = set_copy(other)))
358                 return -ENOMEM;
359
360         return 0;
361 }
362
363 /* FIXME: Does not rollback on failure! */
364 int name_merge(Name *name, Name *other) {
365         int r;
366         NameDependency d;
367
368         assert(name);
369         assert(other);
370         assert(name->meta.manager == other->meta.manager);
371
372         /* This merges 'other' into 'name'. FIXME: This does not
373          * rollback on failure. */
374
375         if (name->meta.type != other->meta.type)
376                 return -EINVAL;
377
378         if (other->meta.state != NAME_STUB)
379                 return -EINVAL;
380
381         /* Merge names */
382         if ((r = ensure_merge(&name->meta.names, other->meta.names)) < 0)
383                 return r;
384
385         /* Merge dependencies */
386         for (d = 0; d < _NAME_DEPENDENCY_MAX; d++)
387                 if ((r = ensure_merge(&name->meta.dependencies[d], other->meta.dependencies[d])) < 0)
388                         return r;
389
390         /* Hookup new deps and names */
391         if (name->meta.linked) {
392                 if ((r = name_augment(name)) < 0)
393                         return r;
394
395                 if ((r = name_link_names(name, true)) < 0)
396                         return r;
397         }
398
399         return 0;
400 }
401
402 const char* name_id(Name *n) {
403         assert(n);
404
405         return set_first(n->meta.names);
406 }
407
408 void name_dump(Name *n, FILE *f) {
409
410         static const char* const state_table[_NAME_STATE_MAX] = {
411                 [NAME_STUB] = "stub",
412                 [NAME_LOADED] = "loaded",
413                 [NAME_FAILED] = "failed"
414         };
415
416         static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
417                 [SOCKET_DEAD] = "dead",
418                 [SOCKET_BEFORE] = "before",
419                 [SOCKET_START_PRE] = "start-pre",
420                 [SOCKET_START] = "start",
421                 [SOCKET_START_POST] = "start-post",
422                 [SOCKET_LISTENING] = "listening",
423                 [SOCKET_RUNNING] = "running",
424                 [SOCKET_STOP_PRE] = "stop-pre",
425                 [SOCKET_STOP] = "stop",
426                 [SOCKET_STOP_POST] = "stop-post",
427                 [SOCKET_MAINTAINANCE] = "maintainance"
428         };
429
430         void *state;
431         char *t;
432
433         assert(n);
434
435         fprintf(f,
436                 "Name %s\n"
437                 "\tDescription: %s\n"
438                 "\tName State: %s\n",
439                 name_id(n),
440                 n->meta.description ? n->meta.description : name_id(n),
441                 state_table[n->meta.state]);
442
443         fprintf(f, "\tNames: ");
444         SET_FOREACH(t, n->meta.names, state)
445                 fprintf(f, "%s ", t);
446         fprintf(f, "\n");
447
448         switch (n->meta.type) {
449                 case NAME_SOCKET: {
450                         int r;
451                         char *s = NULL;
452                         const char *t;
453
454                         if ((r = address_print(&n->socket.address, &s)) < 0)
455                                 t = strerror(-r);
456                         else
457                                 t = s;
458
459                         fprintf(f,
460                                 "\tAddress: %s\n"
461                                 "\tSocket State: %s\n",
462                                 t,
463                                 socket_state_table[n->socket.state]);
464
465                         free(s);
466                         break;
467                 }
468
469                 default:
470                         ;
471         }
472
473         if (n->meta.job) {
474                 fprintf(f, "\t");
475                 job_dump(n->meta.job, f);
476         }
477 }