chiark / gitweb /
add mount enumerator
[elogind.git] / mount.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <errno.h>
4 #include <stdio.h>
5 #include <mntent.h>
6
7 #include "unit.h"
8 #include "mount.h"
9 #include "load-fragment.h"
10 #include "load-fstab.h"
11 #include "load-dropin.h"
12 #include "log.h"
13
14 static int mount_init(Unit *u) {
15         int r;
16         Mount *m = MOUNT(u);
17
18         assert(m);
19
20         /* Load a .mount file */
21         if ((r = unit_load_fragment(u)) < 0)
22                 return r;
23
24         /* Load entry from /etc/fstab */
25         if ((r = unit_load_fstab(u)) < 0)
26                 return r;
27
28         /* Load drop-in directory data */
29         if ((r = unit_load_dropin(u)) < 0)
30                 return r;
31
32         return r;
33 }
34
35 static void mount_done(Unit *u) {
36         Mount *d = MOUNT(u);
37
38         assert(d);
39         free(d->what);
40         free(d->where);
41 }
42
43 static void mount_dump(Unit *u, FILE *f, const char *prefix) {
44
45         static const char* const state_table[_MOUNT_STATE_MAX] = {
46                 [MOUNT_DEAD] = "dead",
47                 [MOUNT_MOUNTING] = "mounting",
48                 [MOUNT_MOUNTED] = "mounted",
49                 [MOUNT_UNMOUNTING] = "unmounting",
50                 [MOUNT_MAINTAINANCE] = "maintainance"
51         };
52
53         Mount *s = MOUNT(u);
54
55         assert(s);
56
57         fprintf(f,
58                 "%sMount State: %s\n"
59                 "%sWhere: %s\n"
60                 "%sWhat: %s\n",
61                 prefix, state_table[s->state],
62                 prefix, s->where,
63                 prefix, s->what);
64 }
65
66 static void mount_shutdown(Manager *m) {
67 }
68
69 static int mount_add_node_links(Mount *m) {
70         Unit *device;
71         char *e;
72         int r;
73
74         assert(m);
75
76         /* Adds in links to the device that this node is based on */
77
78         if (!path_startswith(m->what, "/dev/"))
79                 return 0;
80
81         if (!(e = unit_name_escape_path("node-", m->what+1, ".device")))
82                 return -ENOMEM;
83
84         r = manager_load_unit(UNIT(m)->meta.manager, e, &device);
85         free(e);
86
87         if (r < 0)
88                 return r;
89
90         if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, device)) < 0)
91                 return r;
92
93         if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, device)) < 0)
94                 return r;
95
96         if ((r = unit_add_dependency(device, UNIT_WANTS, UNIT(m))) < 0)
97                 return r;
98
99         return 0;
100 }
101
102 static int mount_add_path_links(Mount *m) {
103         Iterator i;
104         Unit *other;
105         int r;
106
107         /* Adds in link to other mount points, that might lie below or
108          * above us in the hierarchy */
109
110         HASHMAP_FOREACH(other, UNIT(m)->meta.manager->units, i) {
111                 Mount *n;
112
113                 if (other->meta.type != UNIT_MOUNT)
114                         continue;
115
116                 n = MOUNT(other);
117
118                 if (path_startswith(m->where, n->where)) {
119
120                         if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, other)) < 0)
121                                 return r;
122
123                         if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, other)) < 0)
124                                 return r;
125
126                 } else if (startswith(n->where, m->where)) {
127
128                         if ((r = unit_add_dependency(UNIT(m), UNIT_BEFORE, other)) < 0)
129                                 return r;
130
131                         if ((r = unit_add_dependency(other, UNIT_REQUIRES, UNIT(m))) < 0)
132                                 return r;
133                 }
134         }
135
136         return 0;
137 }
138
139 static int mount_add_one(Manager *m, const char *what, const char *where) {
140         char *e;
141         int r;
142         Unit *u;
143         bool delete;
144
145         assert(what);
146         assert(where);
147
148         /* probably some kind of swap, which we don't cover for now */
149         if (where[0] != '/')
150                 return 0;
151
152         if (streq(where, "/"))
153                 e = strdup("rootfs.mount");
154         else
155                 e = unit_name_escape_path("fs-", where+1, ".mount");
156
157         if (!e)
158                 return -ENOMEM;
159
160         if (!(u = manager_get_unit(m, e))) {
161                 delete = true;
162
163                 if (!(u = unit_new(m))) {
164                         free(e);
165                         return -ENOMEM;
166                 }
167
168                 r = unit_add_name(u, e);
169                 free(e);
170
171                 if (r < 0)
172                         goto fail;
173
174                 if (!(MOUNT(u)->what = strdup(what)) ||
175                     !(MOUNT(u)->where = strdup(where)) ||
176                     !(u->meta.description = strdup(where))) {
177                         r = -ENOMEM;
178                         goto fail;
179                 }
180         } else {
181                 delete = false;
182                 free(e);
183         }
184
185         if ((r = mount_add_node_links(MOUNT(u))) < 0)
186                 goto fail;
187
188         if ((r = mount_add_path_links(MOUNT(u))) < 0)
189                 goto fail;
190
191         unit_add_to_load_queue(u);
192         return 0;
193
194 fail:
195         if (delete && u)
196                 unit_free(u);
197
198         return 0;
199 }
200
201 static char *fstab_node_to_udev_node(char *p) {
202         char *dn, *t;
203         int r;
204
205         /* FIXME: to follow udev's logic 100% we need to leave valid
206          * UTF8 chars unescaped */
207
208         if (startswith(p, "LABEL=")) {
209
210                 if (!(t = strdup(p+6)))
211                         return NULL;
212
213                 r = asprintf(&dn, "/dev/disk/by-label/%s", xescape(t, "/ "));
214                 free(t);
215
216                 if (r < 0)
217                         return NULL;
218
219                 return dn;
220         }
221
222         if (startswith(p, "UUID=")) {
223
224                 if (!(t = strdup(p+5)))
225                         return NULL;
226
227                 r = asprintf(&dn, "/dev/disk/by-uuid/%s", ascii_strlower(xescape(t, "/ ")));
228                 free(t);
229
230                 if (r < 0)
231                         return NULL;
232
233                 return dn;
234         }
235
236         return strdup(p);
237 }
238
239 static int mount_load_etc_fstab(Manager *m) {
240         FILE *f;
241         int r;
242         struct mntent* me;
243
244         assert(m);
245
246         errno = 0;
247         if (!(f = setmntent("/etc/fstab", "r")))
248                 return -errno;
249
250         while ((me = getmntent(f))) {
251                 char *where, *what;
252
253                 if (!(what = fstab_node_to_udev_node(me->mnt_fsname))) {
254                         r = -ENOMEM;
255                         goto finish;
256                 }
257
258                 if (!(where = strdup(me->mnt_dir))) {
259                         free(what);
260                         r = -ENOMEM;
261                         goto finish;
262                 }
263
264                 if (what[0] == '/')
265                         path_kill_slashes(what);
266
267                 if (where[0] == '/')
268                         path_kill_slashes(where);
269
270                 r = mount_add_one(m, what, where);
271                 free(what);
272                 free(where);
273
274                 if (r < 0)
275                         goto finish;
276         }
277
278         r = 0;
279 finish:
280
281         endmntent(f);
282         return r;
283 }
284
285 static int mount_load_proc_mounts(Manager *m) {
286         FILE *f;
287         int r;
288
289         assert(m);
290
291         if (!(f = fopen("/proc/self/mountinfo", "r")))
292                 return -errno;
293
294         for (;;) {
295                 int k;
296                 char *device, *path, *d, *p;
297
298                 if ((k = fscanf(f,
299                                 "%*s "       /* (1) mount id */
300                                 "%*s "       /* (2) parent id */
301                                 "%*s "       /* (3) major:minor */
302                                 "%*s "       /* (4) root */
303                                 "%ms "       /* (5) mount point */
304                                 "%*s"        /* (6) mount options */
305                                 "%*[^-]"     /* (7) optional fields */
306                                 "- "         /* (8) seperator */
307                                 "%*s "       /* (9) file system type */
308                                 "%ms"       /* (10) mount source */
309                                 "%*[^\n]", /* some rubbish at the end */
310                                 &path,
311                                 &device)) != 2) {
312
313                         if (k == EOF) {
314                                 if (feof(f))
315                                         break;
316
317                                 r = -errno;
318                                 goto finish;
319                         }
320
321                         r = -EBADMSG;
322                         goto finish;
323                 }
324
325                 if (!(d = cunescape(device))) {
326                         free(device);
327                         free(path);
328                         r = -ENOMEM;
329                         goto finish;
330                 }
331                 free(device);
332
333                 if (!(p = cunescape(path))) {
334                         free(d);
335                         free(path);
336                         r = -ENOMEM;
337                         goto finish;
338                 }
339                 free(path);
340
341                 r = mount_add_one(m, d, p);
342                 free(d);
343                 free(p);
344
345                 if (r < 0)
346                         goto finish;
347         }
348
349         r = 0;
350
351 finish:
352         fclose(f);
353
354         return r;
355 }
356
357 static int mount_enumerate(Manager *m) {
358         int r;
359         assert(m);
360
361         if ((r = mount_load_etc_fstab(m)) < 0)
362                 goto fail;
363
364         if ((r = mount_load_proc_mounts(m)) < 0)
365                 goto fail;
366
367         return 0;
368
369 fail:
370         mount_shutdown(m);
371         return r;
372 }
373
374 static UnitActiveState mount_active_state(Unit *u) {
375
376         static const UnitActiveState table[_MOUNT_STATE_MAX] = {
377                 [MOUNT_DEAD] = UNIT_INACTIVE,
378                 [MOUNT_MOUNTING] = UNIT_ACTIVATING,
379                 [MOUNT_MOUNTED] = UNIT_ACTIVE,
380                 [MOUNT_UNMOUNTING] = UNIT_DEACTIVATING,
381                 [MOUNT_MAINTAINANCE] = UNIT_INACTIVE,
382         };
383
384         return table[MOUNT(u)->state];
385 }
386
387 const UnitVTable mount_vtable = {
388         .suffix = ".mount",
389
390         .init = mount_init,
391         .done = mount_done,
392         .dump = mount_dump,
393
394         .enumerate = mount_enumerate,
395         .shutdown = mount_shutdown,
396
397         .active_state = mount_active_state
398 };