chiark / gitweb /
add functions for dumping server state
[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(Name *n) {
84         char **t;
85         int r;
86         void *state;
87
88         assert(n);
89         assert(!set_isempty(n->meta.names));
90         assert(!n->meta.linked);
91
92         SET_FOREACH(t, n->meta.names, state)
93                 if ((r = hashmap_put(n->meta.manager->names, t, n)) < 0)
94                         goto fail;
95
96         if (n->meta.state == NAME_STUB)
97                 LIST_PREPEND(Meta, n->meta.manager->load_queue, &n->meta);
98
99         n->meta.linked = true;
100
101         return 0;
102
103 fail:
104         SET_FOREACH(t, n->meta.names, state)
105                 assert_se(hashmap_remove(n->meta.manager->names, t) == n);
106
107         return r;
108 }
109
110 static void bidi_set_free(Name *name, Set *s) {
111         void *state;
112         Name *other;
113
114         assert(name);
115
116         /* Frees the set and makes sure we are dropped from the
117          * inverse pointers */
118
119         SET_FOREACH(other, s, state) {
120                 NameDependency d;
121
122                 for (d = 0; d < _NAME_DEPENDENCY_MAX; d++)
123                         set_remove(other->meta.dependencies[d], name);
124         }
125
126         set_free(s);
127 }
128
129 void name_free(Name *name) {
130         NameDependency d;
131         char *t;
132
133         assert(name);
134
135         /* Detach from next 'bigger' objects */
136         if (name->meta.linked) {
137                 char **t;
138                 void *state;
139
140                 SET_FOREACH(t, name->meta.names, state)
141                         assert_se(hashmap_remove(name->meta.manager->names, t) == name);
142
143                 if (name->meta.state == NAME_STUB)
144                         LIST_REMOVE(Meta, name->meta.manager->load_queue, &name->meta);
145         }
146
147         /* Free data and next 'smaller' objects */
148         if (name->meta.job)
149                 job_free(name->meta.job);
150
151         for (d = 0; d < _NAME_DEPENDENCY_MAX; d++)
152                 bidi_set_free(name, name->meta.dependencies[d]);
153
154         switch (name->meta.type) {
155
156                 case NAME_SOCKET: {
157                         unsigned i;
158                         Socket *s = SOCKET(name);
159
160                         for (i = 0; i < s->n_fds; i++)
161                                 nointr_close(s->fds[i]);
162                         break;
163                 }
164
165                 case NAME_DEVICE: {
166                         Device *d = DEVICE(name);
167
168                         free(d->sysfs);
169                         break;
170                 }
171
172                 case NAME_MOUNT: {
173                         Mount *m = MOUNT(name);
174
175                         free(m->path);
176                         break;
177                 }
178
179                 case NAME_AUTOMOUNT: {
180                         Automount *a = AUTOMOUNT(name);
181
182                         free(a->path);
183                         break;
184                 }
185
186                 default:
187                         ;
188         }
189
190         free(name->meta.description);
191
192         while ((t = set_steal_first(name->meta.names)))
193                 free(t);
194         set_free(name->meta.names);
195
196         free(name);
197 }
198
199 bool name_is_ready(Name *name) {
200
201         assert(name);
202
203         if (name->meta.state != NAME_LOADED)
204                 return false;
205
206         assert(name->meta.type < _NAME_TYPE_MAX);
207
208         switch (name->meta.type) {
209                 case NAME_SERVICE: {
210                         Service *s = SERVICE(name);
211
212                         return
213                                 s->state == SERVICE_RUNNING ||
214                                 s->state == SERVICE_RELOAD_PRE ||
215                                 s->state == SERVICE_RELOAD ||
216                                 s->state == SERVICE_RELOAD_POST;
217                 }
218
219                 case NAME_TIMER: {
220                         Timer *t = TIMER(name);
221
222                         return
223                                 t->state == TIMER_WAITING ||
224                                 t->state == TIMER_RUNNING;
225                 }
226
227                 case NAME_SOCKET: {
228                         Socket *s = SOCKET(name);
229
230                         return
231                                 s->state == SOCKET_LISTENING ||
232                                 s->state == SOCKET_RUNNING;
233                 }
234
235                 case NAME_MILESTONE:
236                         return MILESTONE(name)->state == MILESTONE_ACTIVE;
237
238                 case NAME_DEVICE:
239                         return DEVICE(name)->state == DEVICE_AVAILABLE;
240
241                 case NAME_MOUNT:
242                         return MOUNT(name)->state == MOUNT_MOUNTED;
243
244                 case NAME_AUTOMOUNT: {
245                         Automount *a = AUTOMOUNT(name);
246
247                         return
248                                 a->state == AUTOMOUNT_WAITING ||
249                                 a->state == AUTOMOUNT_RUNNING;
250                 }
251
252                 case NAME_SNAPSHOT:
253                         return SNAPSHOT(name)->state == SNAPSHOT_ACTIVE;
254
255
256                 case _NAME_TYPE_MAX:
257                 case _NAME_TYPE_INVALID:
258                         ;
259         }
260
261         assert_not_reached("Unknown name type.");
262         return false;
263 }
264
265 static int ensure_in_set(Set **s, void *data) {
266         int r;
267
268         assert(s);
269         assert(data);
270
271         if (!*s)
272                 if (!(*s = set_new(trivial_hash_func, trivial_compare_func)))
273                         return -ENOMEM;
274
275         if ((r = set_put(*s, data) < 0))
276                 if (r != -EEXIST)
277                         return r;
278
279         return 0;
280 }
281
282 int name_augment(Name *n) {
283         int r;
284         void* state;
285         Name *other;
286
287         assert(n);
288
289         /* Adds in the missing links to make all dependencies bidirectional */
290
291         SET_FOREACH(other, n->meta.dependencies[NAME_BEFORE], state)
292                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_AFTER], n) < 0))
293                         return r;
294         SET_FOREACH(other, n->meta.dependencies[NAME_AFTER], state)
295                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_BEFORE], n) < 0))
296                         return r;
297
298         SET_FOREACH(other, n->meta.dependencies[NAME_CONFLICTS], state)
299                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_CONFLICTS], n) < 0))
300                         return r;
301
302         SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRES], state)
303                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_REQUIRED_BY], n) < 0))
304                         return r;
305         SET_FOREACH(other, n->meta.dependencies[NAME_REQUISITE], state)
306                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_REQUIRED_BY], n) < 0))
307                         return r;
308
309         SET_FOREACH(other, n->meta.dependencies[NAME_WANTS], state)
310                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n) < 0))
311                         return r;
312         SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUIRES], state)
313                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n) < 0))
314                         return r;
315         SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUISITE], state)
316                 if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n) < 0))
317                         return r;
318
319         return r;
320 }
321
322 static int ensure_merge(Set **s, Set *other) {
323
324         if (!other)
325                 return 0;
326
327         if (*s)
328                 return set_merge(*s, other);
329
330         if (!(*s = set_copy(other)))
331                 return -ENOMEM;
332
333         return 0;
334 }
335
336 int name_merge(Name *name, Name *other) {
337         int r;
338         NameDependency d;
339
340         assert(name);
341         assert(other);
342
343         assert(name->meta.manager == other->meta.manager);
344
345         if (name->meta.type != other->meta.type)
346                 return -EINVAL;
347
348         if (other->meta.state != NAME_STUB)
349                 return -EINVAL;
350
351         /* Merge names */
352         if ((r = ensure_merge(&name->meta.names, other->meta.names)) < 0)
353                 return r;
354
355         /* Merge dependencies */
356         for (d = 0; d < _NAME_DEPENDENCY_MAX; d++)
357                 if ((r = ensure_merge(&name->meta.dependencies[d], other->meta.dependencies[d])) < 0)
358                         return r;
359
360         return 0;
361 }
362
363 const char* name_id(Name *n) {
364         assert(n);
365
366         return set_first(n->meta.names);
367 }
368
369 void name_dump(Name *n, FILE *f) {
370
371         static const char* const state_table[_NAME_STATE_MAX] = {
372                 [NAME_STUB] = "STUB",
373                 [NAME_LOADED] = "LOADED",
374                 [NAME_FAILED] = "FAILED"
375         };
376
377         assert(n);
378
379         fprintf(stderr,
380                 "Name %s (%s), state %s\n",
381                 name_id(n),
382                 n->meta.description ? n->meta.description : name_id(n),
383                 state_table[n->meta.state]);
384
385         if (n->meta.job) {
386                 fprintf(f, "\tâ–¶ ");
387                 job_dump(n->meta.job, f);
388         }
389 }