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