chiark / gitweb /
0ef7a77ff80cd920e73fdb5642ab9a9bed722741
[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 int name_link_names(Name *n) {
84         char *t;
85         void *state;
86         int r;
87
88         assert(n);
89
90         if (!n->meta.linked)
91                 return 0;
92
93         /* Link all names that aren't linked yet */
94
95         SET_FOREACH(t, n->meta.names, state)
96                 if ((r = hashmap_put(n->meta.manager->names, t, n)) < 0) {
97
98                         if (r == -EEXIST && hashmap_get(n->meta.manager->names, t) == n)
99                                 continue;
100
101                         return r;
102                 }
103
104         return 0;
105 }
106
107 int name_link(Name *n) {
108         int r;
109
110         assert(n);
111         assert(!set_isempty(n->meta.names));
112         assert(!n->meta.linked);
113
114         n->meta.linked = true;
115
116         if ((r = name_link_names(n) < 0)) {
117                 char *t;
118                 void *state;
119
120                 /* Rollback the registered names */
121                 SET_FOREACH(t, n->meta.names, state)
122                         hashmap_remove(n->meta.manager->names, t);
123
124                 n->meta.linked = false;
125                 return r;
126         }
127
128         if (n->meta.state == NAME_STUB)
129                 LIST_PREPEND(Meta, n->meta.manager->load_queue, &n->meta);
130
131         return 0;
132 }
133
134 static void bidi_set_free(Name *name, Set *s) {
135         void *state;
136         Name *other;
137
138         assert(name);
139
140         /* Frees the set and makes sure we are dropped from the
141          * inverse pointers */
142
143         SET_FOREACH(other, s, state) {
144                 NameDependency d;
145
146                 for (d = 0; d < _NAME_DEPENDENCY_MAX; d++)
147                         set_remove(other->meta.dependencies[d], name);
148         }
149
150         set_free(s);
151 }
152
153 void name_free(Name *name) {
154         NameDependency d;
155         char *t;
156
157         assert(name);
158
159         /* Detach from next 'bigger' objects */
160         if (name->meta.linked) {
161                 char *t;
162                 void *state;
163
164                 SET_FOREACH(t, name->meta.names, state)
165                         assert_se(hashmap_remove(name->meta.manager->names, t) == name);
166
167                 if (name->meta.state == NAME_STUB)
168                         LIST_REMOVE(Meta, name->meta.manager->load_queue, &name->meta);
169         }
170
171         /* Free data and next 'smaller' objects */
172         if (name->meta.job)
173                 job_free(name->meta.job);
174
175         for (d = 0; d < _NAME_DEPENDENCY_MAX; d++)
176                 bidi_set_free(name, name->meta.dependencies[d]);
177
178         switch (name->meta.type) {
179
180                 case NAME_SOCKET: {
181                         unsigned i;
182                         Socket *s = SOCKET(name);
183
184                         for (i = 0; i < s->n_fds; i++)
185                                 close_nointr(s->fds[i]);
186                         break;
187                 }
188
189                 case NAME_DEVICE: {
190                         Device *d = DEVICE(name);
191
192                         free(d->sysfs);
193                         break;
194                 }
195
196                 case NAME_MOUNT: {
197                         Mount *m = MOUNT(name);
198
199                         free(m->path);
200                         break;
201                 }
202
203                 case NAME_AUTOMOUNT: {
204                         Automount *a = AUTOMOUNT(name);
205
206                         free(a->path);
207                         break;
208                 }
209
210                 default:
211                         ;
212         }
213
214         free(name->meta.description);
215
216         while ((t = set_steal_first(name->meta.names)))
217                 free(t);
218         set_free(name->meta.names);
219
220         free(name);
221 }
222
223 bool name_is_ready(Name *name) {
224
225         assert(name);
226
227         if (name->meta.state != NAME_LOADED)
228                 return false;
229
230         assert(name->meta.type < _NAME_TYPE_MAX);
231
232         switch (name->meta.type) {
233                 case NAME_SERVICE: {
234                         Service *s = SERVICE(name);
235
236                         return
237                                 s->state == SERVICE_RUNNING ||
238                                 s->state == SERVICE_RELOAD_PRE ||
239                                 s->state == SERVICE_RELOAD ||
240                                 s->state == SERVICE_RELOAD_POST;
241                 }
242
243                 case NAME_TIMER: {
244                         Timer *t = TIMER(name);
245
246                         return
247                                 t->state == TIMER_WAITING ||
248                                 t->state == TIMER_RUNNING;
249                 }
250
251                 case NAME_SOCKET: {
252                         Socket *s = SOCKET(name);
253
254                         return
255                                 s->state == SOCKET_LISTENING ||
256                                 s->state == SOCKET_RUNNING;
257                 }
258
259                 case NAME_MILESTONE:
260                         return MILESTONE(name)->state == MILESTONE_ACTIVE;
261
262                 case NAME_DEVICE:
263                         return DEVICE(name)->state == DEVICE_AVAILABLE;
264
265                 case NAME_MOUNT:
266                         return MOUNT(name)->state == MOUNT_MOUNTED;
267
268                 case NAME_AUTOMOUNT: {
269                         Automount *a = AUTOMOUNT(name);
270
271                         return
272                                 a->state == AUTOMOUNT_WAITING ||
273                                 a->state == AUTOMOUNT_RUNNING;
274                 }
275
276                 case NAME_SNAPSHOT:
277                         return SNAPSHOT(name)->state == SNAPSHOT_ACTIVE;
278
279
280                 case _NAME_TYPE_MAX:
281                 case _NAME_TYPE_INVALID:
282                         ;
283         }
284
285         assert_not_reached("Unknown name type.");
286         return false;
287 }
288
289 static int ensure_in_set(Set **s, void *data) {
290         int r;
291
292         assert(s);
293         assert(data);
294
295         if (!*s)
296                 if (!(*s = set_new(trivial_hash_func, trivial_compare_func)))
297                         return -ENOMEM;
298
299         if ((r = set_put(*s, data) < 0))
300                 if (r != -EEXIST)
301                         return r;
302
303         return 0;
304 }
305
306 int name_augment(Name *n) {
307         int r;
308         void* state;
309         Name *other;
310
311         assert(n);
312
313         /* Adds in the missing links to make all dependencies bidirectional */
314
315         SET_FOREACH(other, n->meta.dependencies[NAME_BEFORE], state)
316                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_AFTER], n) < 0))
317                         return r;
318         SET_FOREACH(other, n->meta.dependencies[NAME_AFTER], state)
319                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_BEFORE], n) < 0))
320                         return r;
321
322         SET_FOREACH(other, n->meta.dependencies[NAME_CONFLICTS], state)
323                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_CONFLICTS], n) < 0))
324                         return r;
325
326         SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRES], state)
327                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_REQUIRED_BY], n) < 0))
328                         return r;
329         SET_FOREACH(other, n->meta.dependencies[NAME_REQUISITE], state)
330                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_REQUIRED_BY], n) < 0))
331                         return r;
332
333         SET_FOREACH(other, n->meta.dependencies[NAME_WANTS], state)
334                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n) < 0))
335                         return r;
336         SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUIRES], 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_REQUISITE], state)
340                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n) < 0))
341                         return r;
342
343         return r;
344 }
345
346 static int ensure_merge(Set **s, Set *other) {
347
348         if (!other)
349                 return 0;
350
351         if (*s)
352                 return set_merge(*s, other);
353
354         if (!(*s = set_copy(other)))
355                 return -ENOMEM;
356
357         return 0;
358 }
359
360 int name_merge(Name *name, Name *other) {
361         int r;
362         NameDependency d;
363
364         assert(name);
365         assert(other);
366
367         assert(name->meta.manager == other->meta.manager);
368
369         if (name->meta.type != other->meta.type)
370                 return -EINVAL;
371
372         if (other->meta.state != NAME_STUB)
373                 return -EINVAL;
374
375         /* Merge names */
376         if ((r = ensure_merge(&name->meta.names, other->meta.names)) < 0)
377                 return r;
378
379         /* Merge dependencies */
380         for (d = 0; d < _NAME_DEPENDENCY_MAX; d++)
381                 if ((r = ensure_merge(&name->meta.dependencies[d], other->meta.dependencies[d])) < 0)
382                         return r;
383
384         if (name->meta.linked)
385                 if ((r = name_link_names(name)) < 0)
386                         return r;
387
388         return 0;
389 }
390
391 const char* name_id(Name *n) {
392         assert(n);
393
394         return set_first(n->meta.names);
395 }
396
397 void name_dump(Name *n, FILE *f) {
398
399         static const char* const state_table[_NAME_STATE_MAX] = {
400                 [NAME_STUB] = "stub",
401                 [NAME_LOADED] = "loaded",
402                 [NAME_FAILED] = "failed"
403         };
404
405         static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
406                 [SOCKET_DEAD] = "dead",
407                 [SOCKET_BEFORE] = "before",
408                 [SOCKET_START_PRE] = "start-pre",
409                 [SOCKET_START] = "start",
410                 [SOCKET_START_POST] = "start-post",
411                 [SOCKET_LISTENING] = "listening",
412                 [SOCKET_RUNNING] = "running",
413                 [SOCKET_STOP_PRE] = "stop-pre",
414                 [SOCKET_STOP] = "stop",
415                 [SOCKET_STOP_POST] = "stop-post",
416                 [SOCKET_MAINTAINANCE] = "maintainance"
417         };
418
419         void *state;
420         char *t;
421
422         assert(n);
423
424         fprintf(f,
425                 "Name %s\n"
426                 "\tDescription: %s\n"
427                 "\tState: %s\n",
428                 name_id(n),
429                 n->meta.description ? n->meta.description : name_id(n),
430                 state_table[n->meta.state]);
431
432         fprintf(f, "\tNames: ");
433         SET_FOREACH(t, n->meta.names, state)
434                 fprintf(f, "%s ", t);
435         fprintf(f, "\n");
436
437         switch (n->meta.type) {
438                 case NAME_SOCKET: {
439                         int r;
440                         char *s = NULL;
441                         const char *t;
442
443                         if ((r = address_print(&n->socket.address, &s)) < 0)
444                                 t = strerror(-r);
445                         else
446                                 t = s;
447
448                         fprintf(f, "\t%s in state %s\n", t, socket_state_table[n->socket.state]);
449                         free(s);
450                         break;
451                 }
452
453                 default:
454                         ;
455         }
456
457         if (n->meta.job) {
458                 fprintf(f, "\t");
459                 job_dump(n->meta.job, f);
460         }
461 }