4bbfeb0f98257bbab1e4896309dfdfc00b5861e9
[project/ubox.git] / block.c
1 /*
2  * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
3  * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License version 2.1
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include <stdio.h>
16 #include <unistd.h>
17 #include <syslog.h>
18 #include <libgen.h>
19 #include <glob.h>
20
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <sys/swap.h>
24 #include <sys/mount.h>
25
26 #include <uci.h>
27 #include <uci_blob.h>
28
29 #include <libubox/list.h>
30 #include <libubox/vlist.h>
31 #include <libubox/blobmsg_json.h>
32 #include <libubox/avl-cmp.h>
33
34 #include "libblkid-tiny/libblkid-tiny.h"
35
36 enum {
37         TYPE_MOUNT,
38         TYPE_SWAP,
39 };
40
41 struct mount {
42         struct vlist_node node;
43         int type;
44
45         char *target;
46         char *path;
47         char *options;
48         char *uuid;
49         char *label;
50         char *device;
51         int extroot;
52         int overlay;
53         int disabled_fsck;
54         unsigned int prio;
55 };
56
57 static struct vlist_tree mounts;
58 static struct blob_buf b;
59 static LIST_HEAD(devices);
60 static int anon_mount, anon_swap, auto_mount, auto_swap;
61 static unsigned int delay_root;
62
63 enum {
64         CFG_ANON_MOUNT,
65         CFG_ANON_SWAP,
66         CFG_AUTO_MOUNT,
67         CFG_AUTO_SWAP,
68         CFG_DELAY_ROOT,
69         __CFG_MAX
70 };
71
72 static const struct blobmsg_policy config_policy[__CFG_MAX] = {
73         [CFG_ANON_SWAP] = { .name = "anon_swap", .type = BLOBMSG_TYPE_INT32 },
74         [CFG_ANON_MOUNT] = { .name = "anon_mount", .type = BLOBMSG_TYPE_INT32 },
75         [CFG_AUTO_SWAP] = { .name = "auto_swap", .type = BLOBMSG_TYPE_INT32 },
76         [CFG_AUTO_MOUNT] = { .name = "auto_mount", .type = BLOBMSG_TYPE_INT32 },
77         [CFG_DELAY_ROOT] = { .name = "delay_root", .type = BLOBMSG_TYPE_INT32 },
78 };
79
80 enum {
81         MOUNT_UUID,
82         MOUNT_LABEL,
83         MOUNT_ENABLE,
84         MOUNT_TARGET,
85         MOUNT_DEVICE,
86         MOUNT_OPTIONS,
87         __MOUNT_MAX
88 };
89
90 static const struct uci_blob_param_list config_attr_list = {
91         .n_params = __CFG_MAX,
92         .params = config_policy,
93 };
94
95 static const struct blobmsg_policy mount_policy[__MOUNT_MAX] = {
96         [MOUNT_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
97         [MOUNT_LABEL] = { .name = "label", .type = BLOBMSG_TYPE_STRING },
98         [MOUNT_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
99         [MOUNT_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
100         [MOUNT_OPTIONS] = { .name = "options", .type = BLOBMSG_TYPE_STRING },
101         [MOUNT_ENABLE] = { .name = "enabled", .type = BLOBMSG_TYPE_INT32 },
102 };
103
104 static const struct uci_blob_param_list mount_attr_list = {
105         .n_params = __MOUNT_MAX,
106         .params = mount_policy,
107 };
108
109 enum {
110         SWAP_ENABLE,
111         SWAP_UUID,
112         SWAP_DEVICE,
113         SWAP_PRIO,
114         __SWAP_MAX
115 };
116
117 static const struct blobmsg_policy swap_policy[__SWAP_MAX] = {
118         [SWAP_ENABLE] = { .name = "enabled", .type = BLOBMSG_TYPE_INT32 },
119         [SWAP_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
120         [SWAP_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
121         [SWAP_PRIO] = { .name = "priority", .type = BLOBMSG_TYPE_INT32 },
122 };
123
124 static const struct uci_blob_param_list swap_attr_list = {
125         .n_params = __SWAP_MAX,
126         .params = swap_policy,
127 };
128
129 static char *blobmsg_get_strdup(struct blob_attr *attr)
130 {
131         if (!attr)
132                 return NULL;
133
134         return strdup(blobmsg_get_string(attr));
135 }
136
137 static int mount_add(struct uci_section *s)
138 {
139         struct blob_attr *tb[__MOUNT_MAX] = { 0 };
140         struct mount *m;
141
142         blob_buf_init(&b, 0);
143         uci_to_blob(&b, s, &mount_attr_list);
144         blobmsg_parse(mount_policy, __MOUNT_MAX, tb, blob_data(b.head), blob_len(b.head));
145
146         if (!tb[MOUNT_LABEL] && !tb[MOUNT_UUID] && !tb[MOUNT_DEVICE])
147                 return -1;
148
149         if (tb[MOUNT_ENABLE] && !blobmsg_get_u32(tb[MOUNT_ENABLE]))
150                 return -1;
151
152         m = malloc(sizeof(struct mount));
153         m->type = TYPE_MOUNT;
154         m->uuid = blobmsg_get_strdup(tb[MOUNT_UUID]);
155         m->label = blobmsg_get_strdup(tb[MOUNT_LABEL]);
156         m->target = blobmsg_get_strdup(tb[MOUNT_TARGET]);
157         m->options = blobmsg_get_strdup(tb[MOUNT_OPTIONS]);
158         m->device = blobmsg_get_strdup(tb[MOUNT_DEVICE]);
159         m->overlay = m->extroot = 0;
160         if (m->target && !strcmp(m->target, "/"))
161                 m->extroot = 1;
162         if (m->target && !strcmp(m->target, "/overlay"))
163                 m->extroot = m->overlay = 1;
164
165         if (m->uuid)
166                 vlist_add(&mounts, &m->node, m->uuid);
167         else if (m->label)
168                 vlist_add(&mounts, &m->node, m->label);
169         else if (m->device)
170                 vlist_add(&mounts, &m->node, m->device);
171
172         return 0;
173 }
174
175 static int swap_add(struct uci_section *s)
176 {
177         struct blob_attr *tb[__SWAP_MAX] = { 0 };
178         struct mount *m;
179
180         blob_buf_init(&b, 0);
181         uci_to_blob(&b, s, &swap_attr_list);
182         blobmsg_parse(swap_policy, __SWAP_MAX, tb, blob_data(b.head), blob_len(b.head));
183
184         if (!tb[SWAP_UUID] && !tb[SWAP_DEVICE])
185                 return -1;
186
187         m = malloc(sizeof(struct mount));
188         memset(m, 0, sizeof(struct mount));
189         m->type = TYPE_SWAP;
190         m->uuid = blobmsg_get_strdup(tb[SWAP_UUID]);
191         m->device = blobmsg_get_strdup(tb[SWAP_DEVICE]);
192         if (tb[SWAP_PRIO])
193                 m->prio = blobmsg_get_u32(tb[SWAP_PRIO]);
194         if (m->prio)
195                 m->prio = ((m->prio << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK) | SWAP_FLAG_PREFER;
196
197         if ((!tb[SWAP_ENABLE]) || blobmsg_get_u32(tb[SWAP_ENABLE]))
198                 vlist_add(&mounts, &m->node, (m->uuid) ? (m->uuid) : (m->device));
199
200         return 0;
201 }
202
203 static int global_add(struct uci_section *s)
204 {
205         struct blob_attr *tb[__CFG_MAX] = { 0 };
206
207         blob_buf_init(&b, 0);
208         uci_to_blob(&b, s, &config_attr_list);
209         blobmsg_parse(config_policy, __CFG_MAX, tb, blob_data(b.head), blob_len(b.head));
210
211         if ((tb[CFG_ANON_MOUNT]) && blobmsg_get_u32(tb[CFG_ANON_MOUNT]))
212                 anon_mount = 1;
213         if ((tb[CFG_ANON_SWAP]) && blobmsg_get_u32(tb[CFG_ANON_SWAP]))
214                 anon_swap = 1;
215
216         if ((tb[CFG_AUTO_MOUNT]) && blobmsg_get_u32(tb[CFG_AUTO_MOUNT]))
217                 auto_mount = 1;
218         if ((tb[CFG_AUTO_SWAP]) && blobmsg_get_u32(tb[CFG_AUTO_SWAP]))
219                 auto_swap = 1;
220
221         if (tb[CFG_DELAY_ROOT])
222                 delay_root = blobmsg_get_u32(tb[CFG_DELAY_ROOT]);
223
224         return 0;
225 }
226
227 static struct mount* find_swap(const char *uuid, const char *device)
228 {
229         struct mount *m;
230
231         vlist_for_each_element(&mounts, m, node) {
232                 if (m->type != TYPE_SWAP)
233                         continue;
234                 if (uuid && m->uuid && !strcmp(m->uuid, uuid))
235                         return m;
236                 if (device && m->device && !strcmp(m->device, device))
237                         return m;
238         }
239
240         return NULL;
241 }
242
243 static struct mount* find_block(const char *uuid, const char *label, const char *device,
244                                 const char *target)
245 {
246         struct mount *m;
247
248         vlist_for_each_element(&mounts, m, node) {
249                 if (m->type != TYPE_MOUNT)
250                         continue;
251                 if (m->uuid && uuid && !strcmp(m->uuid, uuid))
252                         return m;
253                 if (m->label && label && !strcmp(m->label, label))
254                         return m;
255                 if (m->target && target && !strcmp(m->target, target))
256                         return m;
257                 if (m->device && device && !strcmp(m->device, device))
258                         return m;
259         }
260
261         return NULL;
262 }
263
264 static void mounts_update(struct vlist_tree *tree, struct vlist_node *node_new,
265                           struct vlist_node *node_old)
266 {
267 }
268
269 static int config_load(char *cfg)
270 {
271         struct uci_context *ctx;
272         struct uci_package *pkg;
273         struct uci_element *e;
274
275         vlist_init(&mounts, avl_strcmp, mounts_update);
276
277         ctx = uci_alloc_context();
278         if (cfg) {
279                 char path[32];
280                 snprintf(path, 32, "%s/etc/config", cfg);
281                 uci_set_confdir(ctx, path);
282         }
283
284         if (uci_load(ctx, "fstab", &pkg))
285                 return -1;
286
287         vlist_update(&mounts);
288         uci_foreach_element(&pkg->sections, e) {
289                 struct uci_section *s = uci_to_section(e);
290
291                 if (!strcmp(s->type, "mount"))
292                         mount_add(s);
293                 if (!strcmp(s->type, "swap"))
294                         swap_add(s);
295                 if (!strcmp(s->type, "global"))
296                         global_add(s);
297         }
298         vlist_flush(&mounts);
299
300         return 0;
301 }
302
303 static int _cache_load(const char *path)
304 {
305         int gl_flags = GLOB_NOESCAPE | GLOB_MARK;
306         int j;
307         glob_t gl;
308
309         if (glob(path, gl_flags, NULL, &gl) < 0)
310                 return -1;
311
312         for (j = 0; j < gl.gl_pathc; j++) {
313                 struct blkid_struct_probe *pr = malloc(sizeof(struct blkid_struct_probe));
314                 memset(pr, 0, sizeof(struct blkid_struct_probe));
315                 probe_block(gl.gl_pathv[j], pr);
316                 if (pr->err)
317                         free(pr);
318                 else
319                         list_add_tail(&pr->list, &devices);
320         }
321
322         globfree(&gl);
323
324         return 0;
325 }
326
327 static void cache_load(int mtd)
328 {
329         if (mtd)
330                 _cache_load("/dev/mtdblock*");
331         _cache_load("/dev/mmcblk*");
332         _cache_load("/dev/sd*");
333 }
334
335 static int print_block_info(struct blkid_struct_probe *pr)
336 {
337         printf("%s:", pr->dev);
338         if (pr->uuid[0])
339                 printf(" UUID=\"%s\"", pr->uuid);
340
341         if (pr->label[0])
342                 printf(" LABEL=\"%s\"", pr->label);
343
344         if (pr->name[0])
345                 printf(" NAME=\"%s\"", pr->name);
346
347         if (pr->version[0])
348                 printf(" VERSION=\"%s\"", pr->version);
349
350         printf(" TYPE=\"%s\"\n", pr->id->name);
351
352         return 0;
353 }
354
355 static int print_block_uci(struct blkid_struct_probe *pr)
356 {
357         if (!strcmp(pr->id->name, "swap")) {
358                 printf("config 'swap'\n");
359         } else {
360                 printf("config 'mount'\n");
361                 printf("\toption\ttarget\t'/mnt/%s'\n", basename(pr->dev));
362         }
363         if (pr->uuid[0])
364                 printf("\toption\tuuid\t'%s'\n", pr->uuid);
365         else
366                 printf("\toption\tdevice\t'%s'\n", basename(pr->dev));
367         printf("\toption\tenabled\t'0'\n\n");
368
369         return 0;
370 }
371
372 static struct blkid_struct_probe* find_block_info(char *uuid, char *label, char *path)
373 {
374         struct blkid_struct_probe *pr = NULL;
375
376         if (uuid)
377                 list_for_each_entry(pr, &devices, list)
378                         if (!strcmp(pr->uuid, uuid))
379                                 return pr;
380
381         if (label)
382                 list_for_each_entry(pr, &devices, list)
383                         if (strcmp(pr->label, label))
384                                 return pr;
385
386         if (path)
387                 list_for_each_entry(pr, &devices, list)
388                         if (!strcmp(pr->dev, path))
389                                 return pr;
390
391         return NULL;
392 }
393
394 static char* find_mount_point(char *block)
395 {
396         FILE *fp = fopen("/proc/mounts", "r");
397         static char line[256];
398         int len = strlen(block);
399         char *point = NULL;
400
401         if(!fp)
402                 return NULL;
403
404         while (fgets(line, sizeof(line), fp)) {
405                 if (!strncmp(line, block, len)) {
406                         char *p = &line[len + 1];
407                         char *t = strstr(p, " ");
408
409                         if (!t)
410                                 return NULL;
411                         *t = '\0';
412                         point = p;
413                         break;
414                 }
415         }
416
417         fclose(fp);
418
419         return point;
420 }
421
422 static void mkdir_p(char *dir)
423 {
424         char *l = strrchr(dir, '/');
425
426         if (l) {
427                 *l = '\0';
428                 mkdir_p(dir);
429                 *l = '/';
430                 mkdir(dir, 0755);
431         }
432 }
433
434 static int mount_device(struct blkid_struct_probe *pr, int hotplug)
435 {
436         struct mount *m;
437         char *device = basename(pr->dev);
438
439         if (!pr)
440                 return -1;
441
442         if (!strcmp(pr->id->name, "swap")) {
443                 if (hotplug && !auto_swap)
444                         return -1;
445                 m = find_swap(pr->uuid, device);
446                 if (m || anon_swap)
447                         swapon(pr->dev, (m) ? (m->prio) : (0));
448
449                 return 0;
450         }
451
452         if (hotplug && !auto_mount)
453                 return -1;
454
455         if (find_mount_point(pr->dev)) {
456                 fprintf(stderr, "%s is already mounted\n", pr->dev);
457                 return -1;
458         }
459
460         m = find_block(pr->uuid, pr->label, device, NULL);
461         if (m && m->extroot)
462                 return -1;
463
464         if (m) {
465                 char *target = m->target;
466                 char _target[] = "/mnt/mmcblk123";
467                 int err = 0;
468
469                 if (!target) {
470                         snprintf(_target, sizeof(_target), "/mnt/%s", device);
471                         target = _target;
472                 }
473                 mkdir_p(target);
474                 err = mount(pr->dev, target, pr->id->name, 0, (m->options) ? (m->options) : (""));
475                 if (err)
476                         fprintf(stderr, "mounting %s (%s) as %s failed (%d) - %s\n",
477                                         pr->dev, pr->id->name, target, err, strerror(err));
478                 return err;
479         }
480
481         if (anon_mount) {
482                 char target[] = "/mnt/mmcblk123";
483                 int err = 0;
484
485                 snprintf(target, sizeof(target), "/mnt/%s", device);
486                 mkdir_p(target);
487                 err = mount(pr->dev, target, pr->id->name, 0, "");
488                 if (err)
489                         fprintf(stderr, "mounting %s (%s) as %s failed (%d) - %s\n",
490                                         pr->dev, pr->id->name, target, err, strerror(err));
491                 return err;
492         }
493
494         return 0;
495 }
496
497 static int umount_device(struct blkid_struct_probe *pr)
498 {
499         struct mount *m;
500         char *device = basename(pr->dev);
501         char *mp;
502         int err;
503
504         if (!pr)
505                 return -1;
506
507         if (!strcmp(pr->id->name, "swap"))
508                 return -1;
509
510         mp = find_mount_point(pr->dev);
511         if (!mp)
512                 return -1;
513
514         m = find_block(pr->uuid, pr->label, device, NULL);
515         if (m && m->extroot)
516                 return -1;
517
518         err = umount2(mp, MNT_DETACH);
519         if (err)
520                 fprintf(stderr, "unmounting %s (%s)  failed (%d) - %s\n",
521                         pr->dev, mp, err, strerror(err));
522         else
523                 fprintf(stderr, "unmounted %s (%s)\n",
524                         pr->dev, mp);
525
526         return err;
527 }
528
529 static int main_hotplug(int argc, char **argv)
530 {
531         char path[32];
532         char *action, *device, *mount_point;
533
534         action = getenv("ACTION");
535         device = getenv("DEVNAME");
536
537         if (!action || !device)
538                 return -1;
539         snprintf(path, sizeof(path), "/dev/%s", device);
540
541         if (!strcmp(action, "remove")) {
542                 int err = 0;
543                 mount_point = find_mount_point(path);
544                 if (mount_point)
545                         err = umount2(mount_point, MNT_DETACH);
546
547                 if (err)
548                         fprintf(stderr, "umount of %s failed (%d) - %s\n",
549                                         mount_point, err, strerror(err));
550
551                 return 0;
552         } else if (strcmp(action, "add")) {
553                 fprintf(stderr, "Unkown action %s\n", action);
554
555                 return -1;
556         }
557
558         if (config_load(NULL))
559                 return -1;
560         cache_load(0);
561
562         return mount_device(find_block_info(NULL, NULL, path), 1);
563 }
564
565 static int find_block_mtd(char *name, char *part, int plen)
566 {
567         FILE *fp = fopen("/proc/mtd", "r");
568         static char line[256];
569         char *index = NULL;
570
571         if(!fp)
572                 return -1;
573
574         while (!index && fgets(line, sizeof(line), fp)) {
575                 if (strstr(line, name)) {
576                         char *eol = strstr(line, ":");
577
578                         if (!eol)
579                                 continue;
580
581                         *eol = '\0';
582                         index = &line[3];
583                 }
584         }
585
586         fclose(fp);
587
588         if (!index)
589                 return -1;
590
591         snprintf(part, plen, "/dev/mtdblock%s", index);
592
593         return 0;
594 }
595
596 static int check_extroot(char *path)
597 {
598         struct blkid_struct_probe *pr = NULL;
599         char fs[32];
600
601         if (find_block_mtd("rootfs", fs, sizeof(fs)))
602                 return -1;
603
604         list_for_each_entry(pr, &devices, list) {
605                 if (!strcmp(pr->dev, fs)) {
606                         struct stat s;
607                         FILE *fp = NULL;
608                         char tag[32];
609                         char uuid[32] = { 0 };
610
611                         snprintf(tag, sizeof(tag), "%s/etc/.extroot-uuid", path);
612                         if (stat(tag, &s)) {
613                                 fp = fopen(tag, "w+");
614                                 if (!fp) {
615                                         fprintf(stderr, "extroot: failed to write uuid tag file\n");
616                                         /* return 0 to continue boot regardless of error */
617                                         return 0;
618                                 }
619                                 fputs(pr->uuid, fp);
620                                 fclose(fp);
621                                 return 0;
622                         }
623
624                         fp = fopen(tag, "r");
625                         if (!fp) {
626                                 fprintf(stderr, "extroot: failed to open uuid tag file\n");
627                                 return -1;
628                         }
629
630                         fgets(uuid, sizeof(uuid), fp);
631                         fclose(fp);
632                         if (!strcmp(uuid, pr->uuid))
633                                 return 0;
634                         fprintf(stderr, "extroot: uuid tag does not match rom uuid\n");
635                 }
636         }
637         return -1;
638 }
639
640 static int mount_extroot(char *cfg)
641 {
642         char overlay[] = "/tmp/overlay";
643         char mnt[] = "/tmp/mnt";
644         char *path = mnt;
645         struct blkid_struct_probe *pr;
646         struct mount *m;
647         int err = -1;
648
649         if (config_load(cfg))
650                 return -2;
651
652         m = find_block(NULL, NULL, NULL, "/");
653         if (!m)
654                 m = find_block(NULL, NULL, NULL, "/overlay");
655
656         if (!m || !m->extroot)
657                 return -1;
658
659         pr = find_block_info(m->uuid, m->label, NULL);
660
661         if (!pr && delay_root){
662                 fprintf(stderr, "extroot: is not ready yet, retrying in %ui seconds\n", delay_root);
663                 sleep(delay_root);
664                 pr = find_block_info(m->uuid, m->label, NULL);
665         }
666         if (pr) {
667                 if (strncmp(pr->id->name, "ext", 3)) {
668                         fprintf(stderr, "extroot: %s is not supported, try ext4\n", pr->id->name);
669                         return -1;
670                 }
671                 if (m->overlay)
672                         path = overlay;
673                 mkdir_p(path);
674
675                 err = mount(pr->dev, path, pr->id->name, 0, (m->options) ? (m->options) : (""));
676
677                 if (err) {
678                         fprintf(stderr, "mounting %s (%s) as %s failed (%d) - %s\n",
679                                         pr->dev, pr->id->name, path, err, strerror(err));
680                 } else if (m->overlay) {
681                         err = check_extroot(path);
682                         if (err)
683                                 umount(path);
684                 }
685         }
686
687         return err;
688 }
689
690 static int main_extroot(int argc, char **argv)
691 {
692         struct blkid_struct_probe *pr;
693         char fs[32] = { 0 };
694         char fs_data[32] = { 0 };
695         int err = -1;
696
697         if (!getenv("PREINIT"))
698                 return -1;
699
700         if (argc != 2) {
701                 fprintf(stderr, "Usage: block extroot mountpoint\n");
702                 return -1;
703         }
704
705         mkblkdev();
706         cache_load(1);
707
708         find_block_mtd("rootfs", fs, sizeof(fs));
709         if (!fs[0])
710                 return -2;
711
712         pr = find_block_info(NULL, NULL, fs);
713         if (!pr)
714                 return -3;
715
716         find_block_mtd("rootfs_data", fs_data, sizeof(fs_data));
717         if (fs_data[0]) {
718                 pr = find_block_info(NULL, NULL, fs_data);
719                 if (pr && !strcmp(pr->id->name, "jffs2")) {
720                         char cfg[] = "/tmp/jffs_cfg";
721
722                         mkdir_p(cfg);
723                         if (!mount(fs_data, cfg, "jffs2", MS_NOATIME, NULL)) {
724                                 err = mount_extroot(cfg);
725                                 umount2(cfg, MNT_DETACH);
726                         }
727                         if (err < 0)
728                                 rmdir("/tmp/overlay");
729                         rmdir(cfg);
730                         return err;
731                 }
732         }
733
734         return mount_extroot(NULL);
735 }
736
737 static int main_mount(int argc, char **argv)
738 {
739         struct blkid_struct_probe *pr;
740
741         if (config_load(NULL))
742                 return -1;
743
744         cache_load(0);
745         list_for_each_entry(pr, &devices, list)
746                 mount_device(pr, 0);
747
748         return 0;
749 }
750
751 static int main_umount(int argc, char **argv)
752 {
753         struct blkid_struct_probe *pr;
754
755         if (config_load(NULL))
756                 return -1;
757
758         cache_load(0);
759         list_for_each_entry(pr, &devices, list)
760                 umount_device(pr);
761
762         return 0;
763 }
764
765 static int main_detect(int argc, char **argv)
766 {
767         struct blkid_struct_probe *pr;
768
769         cache_load(0);
770         printf("config 'global'\n");
771         printf("\toption\tanon_swap\t'0'\n");
772         printf("\toption\tanon_mount\t'0'\n");
773         printf("\toption\tauto_swap\t'1'\n");
774         printf("\toption\tauto_mount\t'1'\n\n");
775         printf("\toption\tdelay_root\t'0'\n\n");
776         list_for_each_entry(pr, &devices, list)
777                 print_block_uci(pr);
778
779         return 0;
780 }
781
782 static int main_info(int argc, char **argv)
783 {
784         int i;
785         struct blkid_struct_probe *pr;
786
787         cache_load(1);
788         if (argc == 2) {
789                 list_for_each_entry(pr, &devices, list)
790                         print_block_info(pr);
791
792                 return 0;
793         };
794
795         for (i = 2; i < argc; i++) {
796                 struct stat s;
797
798                 if (stat(argv[i], &s)) {
799                         fprintf(stderr, "failed to stat %s\n", argv[i]);
800                         continue;
801                 }
802                 if (!S_ISBLK(s.st_mode)) {
803                         fprintf(stderr, "%s is not a block device\n", argv[i]);
804                         continue;
805                 }
806                 pr = find_block_info(NULL, NULL, argv[i]);
807                 if (pr)
808                         print_block_info(pr);
809         }
810
811         return 0;
812 }
813
814 static int main_swapon(int argc, char **argv)
815 {
816         if (argc != 2) {
817                 fprintf(stderr, "Usage: swapoff [-a] [DEVICE]\n\nStop swapping on DEVICE\n\n\t-a      Stop swapping on all swap devices\n");
818                 return -1;
819         }
820
821         if (!strcmp(argv[1], "-a")) {
822                 struct blkid_struct_probe *pr;
823
824                 cache_load(0);
825                 list_for_each_entry(pr, &devices, list) {
826                         if (strcmp(pr->id->name, "swap"))
827                                 continue;
828                         if (swapon(pr->dev, 0))
829                                 fprintf(stderr, "failed to swapon %s\n", pr->dev);
830                 }
831         } else {
832                 struct stat s;
833                 int err;
834
835                 if (stat(argv[1], &s) || !S_ISBLK(s.st_mode)) {
836                         fprintf(stderr, "%s is not a block device\n", argv[1]);
837                         return -1;
838                 }
839                 err = swapon(argv[1], 0);
840                 if (err) {
841                         fprintf(stderr, "failed to swapon %s (%d)\n", argv[1], err);
842                         return err;
843                 }
844         }
845
846         return 0;
847 }
848
849 static int main_swapoff(int argc, char **argv)
850 {
851         if (argc != 2) {
852                 fprintf(stderr, "Usage: swapoff [-a] [DEVICE]\n\nStop swapping on DEVICE\n\n\t-a      Stop swapping on all swap devices\n");
853                 return -1;
854         }
855
856         if (!strcmp(argv[1], "-a")) {
857                 FILE *fp = fopen("/proc/swaps", "r");
858                 char line[256];
859
860                 if (!fp) {
861                         fprintf(stderr, "failed to open /proc/swaps\n");
862                         return -1;
863                 }
864                 fgets(line, sizeof(line), fp);
865                 while (fgets(line, sizeof(line), fp)) {
866                         char *end = strchr(line, ' ');
867                         int err;
868
869                         if (!end)
870                                 continue;
871                         *end = '\0';
872                         err = swapoff(line);
873                         if (err)
874                                 fprintf(stderr, "failed to swapoff %s (%d)\n", line, err);
875                 }
876                 fclose(fp);
877         } else {
878                 struct stat s;
879                 int err;
880
881                 if (stat(argv[1], &s) || !S_ISBLK(s.st_mode)) {
882                         fprintf(stderr, "%s is not a block device\n", argv[1]);
883                         return -1;
884                 }
885                 err = swapoff(argv[1]);
886                 if (err) {
887                         fprintf(stderr, "fsiled to swapoff %s (%d)\n", argv[1], err);
888                         return err;
889                 }
890         }
891
892         return 0;
893 }
894
895 int main(int argc, char **argv)
896 {
897         char *base = basename(*argv);
898
899         umask(0);
900
901         if (!strcmp(base, "swapon"))
902                 return main_swapon(argc, argv);
903
904         if (!strcmp(base, "swapoff"))
905                 return main_swapoff(argc, argv);
906
907         if ((argc > 1) && !strcmp(base, "block")) {
908                 if (!strcmp(argv[1], "info"))
909                         return main_info(argc, argv);
910
911                 if (!strcmp(argv[1], "detect"))
912                         return main_detect(argc, argv);
913
914                 if (!strcmp(argv[1], "hotplug"))
915                         return main_hotplug(argc, argv);
916
917                 if (!strcmp(argv[1], "extroot"))
918                         return main_extroot(argc, argv);
919
920                 if (!strcmp(argv[1], "mount"))
921                         return main_mount(argc, argv);
922
923                 if (!strcmp(argv[1], "umount"))
924                         return main_umount(argc, argv);
925         }
926
927         fprintf(stderr, "Usage: block <info|mount|umount|detect>\n");
928
929         return -1;
930 }