1 /*
2  * Copyright (c)2004 The DragonFly Project.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  *   Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  *
11  *   Redistributions in binary form must reproduce the above copyright
12  *   notice, this list of conditions and the following disclaimer in
13  *   the documentation and/or other materials provided with the
14  *   distribution.
15  *
16  *   Neither the name of the DragonFly Project nor the names of its
17  *   contributors may be used to endorse or promote products derived
18  *   from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31  * OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * diskutil.c
36  * Disk utility functions for installer.
37  * $Id: diskutil.c,v 1.44 2005/02/07 06:41:42 cpressey Exp $
38  */
39 
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 
45 #include "libaura/mem.h"
46 #include "libaura/fspred.h"
47 #include "libaura/popen.h"
48 
49 #include "libdfui/dfui.h"
50 #include "libdfui/dump.h"
51 
52 #define NEEDS_DISKUTIL_STRUCTURE_DEFINITIONS
53 #include "diskutil.h"
54 #undef NEEDS_DISKUTIL_STRUCTURE_DEFINITIONS
55 
56 #include "commands.h"
57 #include "functions.h"
58 #include "sysids.h"
59 #include "uiutil.h"
60 
61 static int	disk_description_is_better(const char *, const char *);
62 
63 /** STORAGE DESCRIPTORS **/
64 
65 struct storage *
66 storage_new(void)
67 {
68 	struct storage *s;
69 
70 	AURA_MALLOC(s, storage);
71 
72 	s->disk_head = NULL;
73 	s->disk_tail = NULL;
74 	s->selected_disk = NULL;
75 	s->selected_slice = NULL;
76 	s->ram = -1;
77 
78 	return(s);
79 }
80 
81 int
82 storage_get_tmpfs_status(const char *mountpoint, struct storage *s)
83 {
84 	struct subpartition *sp;
85 	sp = NULL;
86 	for (sp = slice_subpartition_first(s->selected_slice);
87 		sp != NULL; sp = subpartition_next(sp)) {
88 		if(strcmp(subpartition_get_mountpoint(sp), mountpoint) == 0) {
89 			if(subpartition_is_tmpfsbacked(sp) == 1) {
90 				return 1;
91 			} else {
92 				return 0;
93 			}
94 		}
95 	}
96 	return 0;
97 }
98 
99 void
100 storage_free(struct storage *s)
101 {
102 	disks_free(s);
103 	AURA_FREE(s, storage);
104 }
105 
106 void
107 storage_set_memsize(struct storage *s, unsigned long memsize)
108 {
109 	s->ram = memsize;
110 }
111 
112 long
113 storage_get_memsize(const struct storage *s)
114 {
115 	return(s->ram);
116 }
117 
118 struct disk *
119 storage_disk_first(const struct storage *s)
120 {
121 	return(s->disk_head);
122 }
123 
124 void
125 storage_set_selected_disk(struct storage *s, struct disk *d)
126 {
127 	s->selected_disk = d;
128 }
129 
130 struct disk *
131 storage_get_selected_disk(const struct storage *s)
132 {
133 	return(s->selected_disk);
134 }
135 
136 void
137 storage_set_selected_slice(struct storage *s, struct slice *sl)
138 {
139 	s->selected_slice = sl;
140 }
141 
142 struct slice *
143 storage_get_selected_slice(const struct storage *s)
144 {
145 	return(s->selected_slice);
146 }
147 
148 /*
149  * Create a new disk description structure.
150  */
151 struct disk *
152 disk_new(struct storage *s, const char *dev_name)
153 {
154 	struct disk *d;
155 
156 	AURA_MALLOC(d, disk);
157 
158 	d->device = aura_strdup(dev_name);
159 	d->desc = NULL;
160 	d->serno = NULL;
161 	d->we_formatted = 0;
162 	d->capacity = 0;
163 
164 	d->cylinders = -1;	/* -1 indicates "we don't know" */
165 	d->heads = -1;
166 	d->sectors = -1;
167 
168 	d->slice_head = NULL;
169 	d->slice_tail = NULL;
170 
171 	d->next = NULL;
172 	if (s->disk_head == NULL)
173 		s->disk_head = d;
174 	else
175 		s->disk_tail->next = d;
176 
177 	d->prev = s->disk_tail;
178 	s->disk_tail = d;
179 
180 	return(d);
181 }
182 
183 static int
184 disk_description_is_better(const char *existing, const char *new_desc __unused)
185 {
186 	if (existing == NULL)
187 		return(1);
188 	return(0);
189 }
190 
191 const char *
192 disk_get_desc(const struct disk *d)
193 {
194 	return(d->desc);
195 }
196 
197 void
198 disk_set_desc(struct disk *d, const char *desc)
199 {
200 	char *c;
201 
202 	if (!disk_description_is_better(d->desc, desc))
203 		return;
204 	if (d->desc != NULL)
205 		free(d->desc);
206 	d->desc = aura_strdup(desc);
207 
208 	/*
209 	 * Get the disk's total capacity.
210 	 * XXX we should do this with C/H/S ?
211 	 */
212 	c = d->desc;
213 	while (*c != ':' && *c != '\0')
214 		c++;
215 	if (*c == '\0')
216 		d->capacity = 0;
217 	else
218 		d->capacity = atoi(c + 1);
219 }
220 
221 /*
222  * Returns the name of the device node used to represent the disk.
223  * Note that the storage used for the returned string is static,
224  * and the string is overwritten each time this function is called.
225  */
226 const char *
227 disk_get_device_name(const struct disk *d)
228 {
229 	static char tmp_dev_name[256];
230 
231 	snprintf(tmp_dev_name, 256, "%s", d->device);
232 	return(tmp_dev_name);
233 }
234 
235 const char *
236 disk_get_serno(const struct disk *d)
237 {
238 	return(d->serno);
239 }
240 
241 void
242 disk_set_serno(struct disk *d, const char *serno)
243 {
244 	d->serno = aura_strdup(serno);
245 }
246 
247 int
248 disk_get_number(const struct disk *d)
249 {
250 	return(d->number);
251 }
252 
253 void
254 disk_set_number(struct disk *d, const int number)
255 {
256 	d->number = number;
257 }
258 
259 /*
260  * Find the first disk description structure in the given
261  * storage description which matches the given device name
262  * prefix.  Note that this means that if a storage
263  * description s contains disks named "ad0" and "ad1",
264  * disk_find(s, "ad0s1c") will return a pointer to the disk
265  * structure for "ad0".
266  */
267 struct disk *
268 disk_find(const struct storage *s, const char *device)
269 {
270 	struct disk *d = s->disk_head;
271 
272 	while (d != NULL) {
273 		if (strncmp(device, d->device, strlen(d->device)) == 0 &&
274 		    strlen(device) == strlen(d->device))
275 			return(d);
276 		d = d->next;
277 	}
278 
279 	return(NULL);
280 }
281 
282 struct disk *
283 disk_next(const struct disk *d)
284 {
285 	return(d->next);
286 }
287 
288 struct slice *
289 disk_slice_first(const struct disk *d)
290 {
291 	return(d->slice_head);
292 }
293 
294 void
295 disk_set_formatted(struct disk *d, int formatted)
296 {
297 	d->we_formatted = formatted;
298 }
299 
300 int
301 disk_get_formatted(const struct disk *d)
302 {
303 	return(d->we_formatted);
304 }
305 
306 void
307 disk_set_geometry(struct disk *d, int cyl, int hd, int sec)
308 {
309 	d->cylinders = cyl;
310 	d->heads = hd;
311 	d->sectors = sec;
312 }
313 
314 void
315 disk_get_geometry(const struct disk *d, int *cyl, int *hd, int *sec)
316 {
317 	*cyl = d->cylinders;
318 	*hd = d->heads;
319 	*sec = d->sectors;
320 }
321 
322 /*
323  * Free the memory allocated to hold the set of disk descriptions.
324  */
325 void
326 disks_free(struct storage *s)
327 {
328 	struct disk *d = s->disk_head, *next;
329 
330 	while (d != NULL) {
331 		next = d->next;
332 		slices_free(d->slice_head);
333 		free(d->desc);
334 		free(d->device);
335 		AURA_FREE(d, disk);
336 		d = next;
337 	}
338 
339 	s->disk_head = NULL;
340 	s->disk_tail = NULL;
341 }
342 
343 /*
344  * Create a new slice description and add it to a disk description.
345  */
346 struct slice *
347 slice_new(struct disk *d, int number, int type, int flags,
348 	  unsigned long start, unsigned long size)
349 {
350 	struct slice *s;
351 	const char *sysid_desc = NULL;
352 	char unknown[256];
353 	int i;
354 
355 	dfui_debug("** adding slice %d (start %ld, size %ld, sysid %d) "
356 	    "to disk %s\n", number, start, size, type, d->device);
357 
358 	AURA_MALLOC(s, slice);
359 
360 	s->parent = d;
361 
362 	s->subpartition_head = NULL;
363 	s->subpartition_tail = NULL;
364 	s->number = number;
365 
366 	s->type = type;
367 	s->flags = flags;
368 	s->start = start;
369 	s->size = size;
370 
371 	for (i = 0; ; i++) {
372 		if (part_types[i].type == type) {
373 			sysid_desc = part_types[i].name;
374 			break;
375 		}
376 		if (part_types[i].type == 255)
377 			break;
378 	}
379 	if (sysid_desc == NULL) {
380 		snprintf(unknown, 256, "??? Unknown, sysid = %d", type);
381 		sysid_desc = unknown;
382 	}
383 
384 	asprintf(&s->desc, "%ldM - %ldM: %s",
385 	    start / 2048, (start + size) / 2048, sysid_desc);
386 	s->capacity = size / 2048;
387 
388 	s->next = NULL;
389 	if (d->slice_head == NULL)
390 		d->slice_head = s;
391 	else
392 		d->slice_tail->next = s;
393 
394 	s->prev = d->slice_tail;
395 	d->slice_tail = s;
396 
397 	return(s);
398 }
399 
400 /*
401  * Find a slice description on a given disk description given the
402  * slice number.
403  */
404 struct slice *
405 slice_find(const struct disk *d, int number)
406 {
407 	struct slice *s = d->slice_head;
408 
409 	while (s != NULL) {
410 		if (s->number == number)
411 			return(s);
412 		s = s->next;
413 	}
414 
415 	return(NULL);
416 }
417 
418 struct slice *
419 slice_next(const struct slice *s)
420 {
421 	return(s->next);
422 }
423 
424 /*
425  * Returns the name of the device node used to represent the slice.
426  * Note that the storage used for the returned string is static,
427  * and the string is overwritten each time this function is called.
428  */
429 const char *
430 slice_get_device_name(const struct slice *s)
431 {
432 	static char tmp_dev_name[256];
433 
434 	snprintf(tmp_dev_name, 256, "%ss%d", s->parent->device, s->number);
435 	return(tmp_dev_name);
436 }
437 
438 int
439 slice_get_number(const struct slice *s)
440 {
441 	return(s->number);
442 }
443 
444 const char *
445 slice_get_desc(const struct slice *s)
446 {
447 	return(s->desc);
448 }
449 
450 unsigned long
451 slice_get_capacity(const struct slice *s)
452 {
453 	return(s->capacity);
454 }
455 
456 unsigned long
457 slice_get_start(const struct slice *s)
458 {
459 	return(s->start);
460 }
461 
462 unsigned long
463 slice_get_size(const struct slice *s)
464 {
465 	return(s->size);
466 }
467 
468 int
469 slice_get_type(const struct slice *s)
470 {
471 	return(s->type);
472 }
473 
474 int
475 slice_get_flags(const struct slice *s)
476 {
477 	return(s->flags);
478 }
479 
480 struct subpartition *
481 slice_subpartition_first(const struct slice *s)
482 {
483 	return(s->subpartition_head);
484 }
485 
486 /*
487  * Free all memory for a list of slice descriptions.
488  */
489 void
490 slices_free(struct slice *head)
491 {
492 	struct slice *next;
493 
494 	while (head != NULL) {
495 		next = head->next;
496 		subpartitions_free(head);
497 		free(head->desc);
498 		AURA_FREE(head, slice);
499 		head = next;
500 	}
501 }
502 
503 struct subpartition *
504 subpartition_new_hammer(struct slice *s, const char *mountpoint, long capacity,
505     int encrypted)
506 {
507 	struct subpartition *sp;
508 
509 	AURA_MALLOC(sp, subpartition);
510 
511 	sp->parent = s;
512 
513 	struct subpartition *last = s->subpartition_tail;
514 	if (last == NULL) {
515 		sp->letter = 'a';
516 	} else if (last->letter == 'b') {
517 		sp->letter = 'd';
518 	} else {
519 		sp->letter = (char)(last->letter + 1);
520 	}
521 
522 	sp->mountpoint = aura_strdup(mountpoint);
523 	sp->capacity = capacity;
524 	sp->encrypted = encrypted;
525 	sp->type = FS_HAMMER;
526 
527 	/*
528 	 * We need this here, because a UFS /boot needs valid values
529 	 */
530 	if (sp->capacity < 1024)
531 		sp->fsize = 1024;
532 	else
533 		sp->fsize = 2048;
534 
535 	if (sp->capacity < 1024)
536 		sp->bsize = 8192;
537 	else
538 		sp->bsize = 16384;
539 
540 	sp->is_swap = 0;
541 	sp->pfs = 0;
542 	if (strcasecmp(mountpoint, "swap") == 0)
543 		sp->is_swap = 1;
544 	if (strcmp(mountpoint, "/") != 0 && strcmp(mountpoint, "/boot") != 0 &&
545 	    strcmp(mountpoint, "swap") != 0)
546 		sp->pfs = 1;
547 
548 	sp->next = NULL;
549 	if (s->subpartition_head == NULL)
550 		s->subpartition_head = sp;
551 	else
552 		s->subpartition_tail->next = sp;
553 
554 	sp->prev = s->subpartition_tail;
555 	s->subpartition_tail = sp;
556 
557 	return(sp);
558 }
559 
560 /*
561  * NOTE: arguments to this function are not checked for sanity.
562  *
563  * fsize and/or bsize may both be -1, indicating
564  * "choose a reasonable default."
565  */
566 struct subpartition *
567 subpartition_new_ufs(struct slice *s, const char *mountpoint, long capacity,
568     int encrypted, int softupdates, long fsize, long bsize, int tmpfsbacked)
569 {
570 	struct subpartition *sp, *sptmp;
571 	int letter='d';
572 
573 	AURA_MALLOC(sp, subpartition);
574 
575 	sp->parent = s;
576 
577 	sp->mountpoint = aura_strdup(mountpoint);
578 	sp->capacity = capacity;
579 	sp->encrypted = encrypted;
580 	sp->type = FS_UFS;
581 
582 	if (fsize == -1) {
583 		if (sp->capacity < 1024)
584 			sp->fsize = 1024;
585 		else
586 			sp->fsize = 2048;
587 	} else {
588 		sp->fsize = fsize;
589 	}
590 
591 	if (bsize == -1) {
592 		if (sp->capacity < 1024)
593 			sp->bsize = 8192;
594 		else
595 			sp->bsize = 16384;
596 	} else {
597 		sp->bsize = bsize;
598 	}
599 
600 	if (softupdates == -1) {
601 		if (strcmp(mountpoint, "/") == 0)
602 			sp->softupdates = 0;
603 		else
604 			sp->softupdates = 1;
605 	} else {
606 		sp->softupdates = softupdates;
607 	}
608 
609 	sp->tmpfsbacked = tmpfsbacked;
610 
611 	sp->is_swap = 0;
612 	if (strcasecmp(mountpoint, "swap") == 0)
613 		sp->is_swap = 1;
614 
615 	if (s->subpartition_head == NULL) {
616 		s->subpartition_head = sp;
617 		s->subpartition_tail = sp;
618 	} else {
619 		for (sptmp = s->subpartition_head; sptmp != NULL;
620 		     sptmp = sptmp->next) {
621 			if (strcmp(sptmp->mountpoint, sp->mountpoint) > 0)
622 				break;
623 		}
624 		if (sptmp != NULL) {
625 			if (s->subpartition_head == sptmp)
626 				s->subpartition_head = sp;
627 			else
628 				sptmp->prev->next = sp;
629 			sp->next = sptmp;
630 			sp->prev = sptmp->prev;
631 			sptmp->prev = sp;
632 		} else {
633 			sp->prev = s->subpartition_tail;
634 			s->subpartition_tail->next = sp;
635 			s->subpartition_tail = sp;
636 		}
637 	}
638 
639 	for (sptmp = s->subpartition_head; sptmp != NULL;
640 	     sptmp = sptmp->next) {
641 		if (sptmp->tmpfsbacked)
642 			sptmp->letter = '@';
643 		else if (strcmp(sptmp->mountpoint, "/") == 0 ||
644 			 strcmp(sptmp->mountpoint, "/dummy") == 0)
645 			sptmp->letter = 'a';
646 		else if (strcasecmp(sptmp->mountpoint, "swap") == 0)
647 			sptmp->letter = 'b';
648 		else
649 			sptmp->letter = letter++;
650 	}
651 
652 	return(sp);
653 }
654 
655 /*
656  * Find the subpartition description in the given storage
657  * description whose mountpoint matches the given string exactly.
658  */
659 struct subpartition *
660 subpartition_find(const struct slice *s, const char *fmt, ...)
661 {
662 	struct subpartition *sp = s->subpartition_head;
663 	char *mountpoint;
664 	va_list args;
665 
666 	va_start(args, fmt);
667 	vasprintf(&mountpoint, fmt, args);
668 	va_end(args);
669 
670 	while (sp != NULL) {
671 		if (strcmp(mountpoint, sp->mountpoint) == 0) {
672 			free(mountpoint);
673 			return(sp);
674 		}
675 		sp = sp->next;
676 	}
677 
678 	free(mountpoint);
679 	return(NULL);
680 }
681 
682 /*
683  * Find the subpartition description in the given storage
684  * description where the given filename would presumably
685  * reside.  This is the subpartition whose mountpoint is
686  * the longest match for the given filename.
687  */
688 struct subpartition *
689 subpartition_of(const struct slice *s, const char *fmt, ...)
690 {
691 	struct subpartition *sp = s->subpartition_head;
692 	struct subpartition *csp = NULL;
693 	size_t len = 0;
694 	char *filename;
695 	va_list args;
696 
697 	va_start(args, fmt);
698 	vasprintf(&filename, fmt, args);
699 	va_end(args);
700 
701 	while (sp != NULL) {
702 		if (strlen(sp->mountpoint) > len &&
703 		    strlen(sp->mountpoint) <= strlen(filename) &&
704 		    strncmp(filename, sp->mountpoint, strlen(sp->mountpoint)) == 0) {
705 				csp = sp;
706 				len = strlen(csp->mountpoint);
707 		}
708 		sp = sp->next;
709 	}
710 
711 	free(filename);
712 	return(csp);
713 }
714 
715 struct subpartition *
716 subpartition_find_capacity(const struct slice *s, long capacity)
717 {
718 	struct subpartition *sp = s->subpartition_head;
719 
720 	while (sp != NULL) {
721 		if (sp->capacity == capacity)
722 			return(sp);
723 		sp = sp->next;
724 	}
725 
726 	return(NULL);
727 }
728 
729 struct subpartition *
730 subpartition_next(const struct subpartition *sp)
731 {
732 	return(sp->next);
733 }
734 
735 int
736 subpartition_get_pfs(const struct subpartition *sp)
737 {
738 	return(sp->pfs);
739 }
740 
741 /*
742  * Returns the name of the device node used to represent
743  * the subpartition, either by serial number or traditional style.
744  * Note that the storage used for the returned string is static,
745  * and the string is overwritten each time this function is called.
746  */
747 const char *
748 subpartition_get_device_name(const struct subpartition *sp)
749 {
750 	static char tmp_dev_name[256];
751 
752 	if (sp->parent->parent->serno != NULL)
753 		snprintf(tmp_dev_name, 256, "serno/%s.s%d%c",
754 		    sp->parent->parent->serno, sp->parent->number, sp->letter);
755 	else
756 		snprintf(tmp_dev_name, 256, "%ss%d%c",
757 		    sp->parent->parent->device, sp->parent->number, sp->letter);
758 	return(tmp_dev_name);
759 }
760 
761 const char *
762 subpartition_get_mountpoint(const struct subpartition *sp)
763 {
764 	return(sp->mountpoint);
765 }
766 
767 char
768 subpartition_get_letter(const struct subpartition *sp)
769 {
770 	return(sp->letter);
771 }
772 
773 unsigned long
774 subpartition_get_fsize(const struct subpartition *sp)
775 {
776 	return(sp->fsize);
777 }
778 
779 unsigned long
780 subpartition_get_bsize(const struct subpartition *sp)
781 {
782 	return(sp->bsize);
783 }
784 
785 long
786 subpartition_get_capacity(const struct subpartition *sp)
787 {
788 	return(sp->capacity);
789 }
790 
791 void
792 subpartition_clr_encrypted(struct subpartition *sp)
793 {
794 	sp->encrypted = 0;
795 }
796 
797 int
798 subpartition_is_encrypted(const struct subpartition *sp)
799 {
800 	return(sp->encrypted);
801 }
802 
803 int
804 subpartition_is_swap(const struct subpartition *sp)
805 {
806 	return(sp->is_swap);
807 }
808 
809 int
810 subpartition_is_softupdated(const struct subpartition *sp)
811 {
812 	return(sp->softupdates);
813 }
814 int
815 subpartition_is_tmpfsbacked(const struct subpartition *sp)
816 {
817 	return(sp->tmpfsbacked);
818 }
819 
820 int
821 subpartition_count(const struct slice *s)
822 {
823 	struct subpartition *sp = s->subpartition_head;
824 	int count = 0;
825 
826 	while (sp != NULL) {
827 		count++;
828 		sp = sp->next;
829 	}
830 
831 	return(count);
832 }
833 
834 void
835 subpartitions_free(struct slice *s)
836 {
837 	struct subpartition *sp = s->subpartition_head, *next;
838 
839 	while (sp != NULL) {
840 		next = sp->next;
841 		free(sp->mountpoint);
842 		AURA_FREE(sp, subpartition);
843 		sp = next;
844 	}
845 
846 	s->subpartition_head = NULL;
847 	s->subpartition_tail = NULL;
848 }
849 
850 long
851 measure_activated_swap(const struct i_fn_args *a)
852 {
853 	FILE *p;
854 	char line[256];
855 	char *word;
856 	long swap = 0;
857 
858 	if ((p = aura_popen("%s%s -k", "r", a->os_root, cmd_name(a, "SWAPINFO"))) == NULL)
859 		return(0);
860 	while (fgets(line, 255, p) != NULL) {
861 		if ((word = strtok(line, " \t")) == NULL)
862 			continue;
863 		if (strcmp(word, "Device") == 0)
864 			continue;
865 		if ((word = strtok(NULL, " \t")) == NULL)
866 			continue;
867 		swap += atol(word);
868 	}
869 	aura_pclose(p);
870 
871 	return(swap / 1024);
872 }
873 
874 long
875 measure_activated_swap_from_slice(const struct i_fn_args *a,
876     const struct disk *d, const struct slice *s)
877 {
878 	FILE *p;
879 	char *dev, *word;
880 	char line[256];
881 	long swap = 0;
882 
883 	if ((p = aura_popen("%s%s -k", "r", a->os_root, cmd_name(a, "SWAPINFO"))) == NULL)
884 		return(0);
885 
886 	asprintf(&dev, "/dev/%ss%d", d->device, s->number);
887 
888 	while (fgets(line, 255, p) != NULL) {
889 		if ((word = strtok(line, " \t")) == NULL)
890 			continue;
891 		if (strcmp(word, "Device") == 0)
892 			continue;
893 		if (strstr(word, dev) != word)
894 			continue;
895 		if ((word = strtok(NULL, " \t")) == NULL)
896 			continue;
897 		swap += atol(word);
898 	}
899 	aura_pclose(p);
900 	free(dev);
901 
902 	return(swap / 1024);
903 }
904 
905 long
906 measure_activated_swap_from_disk(const struct i_fn_args *a,
907 				 const struct disk *d)
908 {
909 	struct slice *s;
910 	long swap = 0;
911 
912 	for (s = d->slice_head; s != NULL; s = s->next)
913 		swap += measure_activated_swap_from_slice(a, d, s);
914 
915 	return(swap);
916 }
917 
918 void *
919 swapoff_all(const struct i_fn_args *a)
920 {
921 	FILE *p;
922 
923 	if ((p = aura_popen("%s%s off; %s%s | %s%s \"^/dev\" | %s%s '{print $1;}' | %s%s %s%s", "r",
924 		    a->os_root, cmd_name(a, "DUMPON"),
925 		    a->os_root, cmd_name(a, "SWAPINFO"),
926 		    a->os_root, cmd_name(a, "GREP"),
927 		    a->os_root, cmd_name(a, "AWK"),
928 		    a->os_root, cmd_name(a, "XARGS"),
929 		    a->os_root, cmd_name(a, "SWAPOFF"))) != NULL)
930 		aura_pclose(p);
931 
932 	return(p);
933 }
934 
935 void *
936 remove_all_mappings(const struct i_fn_args *a)
937 {
938 	FILE *p;
939 
940 	if ((p = aura_popen("%s%s -1 /dev/mapper | %s%s -vw control | %s%s -n 1 %s%s luksClose", "r",
941 		    a->os_root, cmd_name(a, "LS"),
942 		    a->os_root, cmd_name(a, "GREP"),
943 		    a->os_root, cmd_name(a, "XARGS"),
944 		    a->os_root, cmd_name(a, "CRYPTSETUP"))) != NULL)
945 		aura_pclose(p);
946 
947 	return(p);
948 }
949