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_mfs_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_mfsbacked(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 unsigned 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 	if (disk_find(s, dev_name) != NULL) {
157 		/* Already discovered */
158 		return(NULL);
159 	}
160 
161 	AURA_MALLOC(d, disk);
162 
163 	d->device = aura_strdup(dev_name);
164 	d->desc = NULL;
165 	d->we_formatted = 0;
166 	d->capacity = 0;
167 
168 	d->cylinders = -1;	/* -1 indicates "we don't know" */
169 	d->heads = -1;
170 	d->sectors = -1;
171 
172 	d->slice_head = NULL;
173 	d->slice_tail = NULL;
174 
175 	d->next = NULL;
176 	if (s->disk_head == NULL)
177 		s->disk_head = d;
178 	else
179 		s->disk_tail->next = d;
180 
181 	d->prev = s->disk_tail;
182 	s->disk_tail = d;
183 
184 	return(d);
185 }
186 
187 static int
188 disk_description_is_better(const char *existing, const char *new_desc __unused)
189 {
190 	if (existing == NULL)
191 		return(1);
192 	return(0);
193 }
194 
195 const char *
196 disk_get_desc(const struct disk *d)
197 {
198 	return(d->desc);
199 }
200 
201 void
202 disk_set_desc(struct disk *d, const char *desc)
203 {
204 	char *c;
205 
206 	if (!disk_description_is_better(d->desc, desc))
207 		return;
208 	if (d->desc != NULL)
209 		free(d->desc);
210 	d->desc = aura_strdup(desc);
211 
212 	/*
213 	 * Get the disk's total capacity.
214 	 * XXX we should do this with C/H/S ?
215 	 */
216 	c = d->desc;
217 	while (*c != ':' && *c != '\0')
218 		c++;
219 	if (*c == '\0')
220 		d->capacity = 0;
221 	else
222 		d->capacity = atoi(c + 1);
223 }
224 
225 /*
226  * Returns the name of the device node used to represent the disk.
227  * Note that the storage used for the returned string is static,
228  * and the string is overwritten each time this function is called.
229  */
230 const char *
231 disk_get_device_name(const struct disk *d)
232 {
233 	static char tmp_dev_name[256];
234 
235 	snprintf(tmp_dev_name, 256, "%s", d->device);
236 	return(tmp_dev_name);
237 }
238 
239 /*
240  * Returns the name of the device node used to represent the
241  * raw disk device.
242  * Note that the storage used for the returned string is static,
243  * and the string is overwritten each time this function is called.
244  */
245 const char *
246 disk_get_raw_device_name(const struct disk *d)
247 {
248 	static char tmp_dev_name[256];
249 
250 	snprintf(tmp_dev_name, 256, "%s", d->device);
251 	return(tmp_dev_name);
252 }
253 
254 /*
255  * Find the first disk description structure in the given
256  * storage description which matches the given device name
257  * prefix.  Note that this means that if a storage
258  * description s contains disks named "ad0" and "ad1",
259  * disk_find(s, "ad0s1c") will return a pointer to the disk
260  * structure for "ad0".
261  */
262 struct disk *
263 disk_find(const struct storage *s, const char *device)
264 {
265 	struct disk *d = s->disk_head;
266 
267 	while (d != NULL) {
268 		if (strncmp(device, d->device, strlen(d->device)) == 0)
269 			return(d);
270 		d = d->next;
271 	}
272 
273 	return(NULL);
274 }
275 
276 struct disk *
277 disk_next(const struct disk *d)
278 {
279 	return(d->next);
280 }
281 
282 struct slice *
283 disk_slice_first(const struct disk *d)
284 {
285 	return(d->slice_head);
286 }
287 
288 void
289 disk_set_formatted(struct disk *d, int formatted)
290 {
291 	d->we_formatted = formatted;
292 }
293 
294 int
295 disk_get_formatted(const struct disk *d)
296 {
297 	return(d->we_formatted);
298 }
299 
300 void
301 disk_set_geometry(struct disk *d, int cyl, int hd, int sec)
302 {
303 	d->cylinders = cyl;
304 	d->heads = hd;
305 	d->sectors = sec;
306 }
307 
308 void
309 disk_get_geometry(const struct disk *d, int *cyl, int *hd, int *sec)
310 {
311 	*cyl = d->cylinders;
312 	*hd = d->heads;
313 	*sec = d->sectors;
314 }
315 
316 /*
317  * Free the memory allocated to hold the set of disk descriptions.
318  */
319 void
320 disks_free(struct storage *s)
321 {
322 	struct disk *d = s->disk_head, *next;
323 
324 	while (d != NULL) {
325 		next = d->next;
326 		slices_free(d->slice_head);
327 		free(d->desc);
328 		free(d->device);
329 		AURA_FREE(d, disk);
330 		d = next;
331 	}
332 
333 	s->disk_head = NULL;
334 	s->disk_tail = NULL;
335 }
336 
337 /*
338  * Create a new slice description and add it to a disk description.
339  */
340 struct slice *
341 slice_new(struct disk *d, int number, int type, int flags,
342 	  unsigned long start, unsigned long size)
343 {
344 	struct slice *s;
345 	const char *sysid_desc = NULL;
346 	char unknown[256];
347 	int i;
348 
349 	dfui_debug("** adding slice %d (start %ld, size %ld, sysid %d) "
350 	    "to disk %s\n", number, start, size, type, d->device);
351 
352 	AURA_MALLOC(s, slice);
353 
354 	s->parent = d;
355 
356 	s->subpartition_head = NULL;
357 	s->subpartition_tail = NULL;
358 	s->number = number;
359 
360 	s->type = type;
361 	s->flags = flags;
362 	s->start = start;
363 	s->size = size;
364 
365 	for (i = 0; ; i++) {
366 		if (part_types[i].type == type) {
367 			sysid_desc = part_types[i].name;
368 			break;
369 		}
370 		if (part_types[i].type == 255)
371 			break;
372 	}
373 	if (sysid_desc == NULL) {
374 		snprintf(unknown, 256, "??? Unknown, sysid = %d", type);
375 		sysid_desc = unknown;
376 	}
377 
378 	asprintf(&s->desc, "%ldM - %ldM: %s",
379 	    start / 2048, (start + size) / 2048, sysid_desc);
380 	s->capacity = size / 2048;
381 
382 	s->next = NULL;
383 	if (d->slice_head == NULL)
384 		d->slice_head = s;
385 	else
386 		d->slice_tail->next = s;
387 
388 	s->prev = d->slice_tail;
389 	d->slice_tail = s;
390 
391 	return(s);
392 }
393 
394 /*
395  * Find a slice description on a given disk description given the
396  * slice number.
397  */
398 struct slice *
399 slice_find(const struct disk *d, int number)
400 {
401 	struct slice *s = d->slice_head;
402 
403 	while (s != NULL) {
404 		if (s->number == number)
405 			return(s);
406 		s = s->next;
407 	}
408 
409 	return(NULL);
410 }
411 
412 struct slice *
413 slice_next(const struct slice *s)
414 {
415 	return(s->next);
416 }
417 
418 /*
419  * Returns the name of the device node used to represent the slice.
420  * Note that the storage used for the returned string is static,
421  * and the string is overwritten each time this function is called.
422  */
423 const char *
424 slice_get_device_name(const struct slice *s)
425 {
426 	static char tmp_dev_name[256];
427 
428 	snprintf(tmp_dev_name, 256, "%ss%d", s->parent->device, s->number);
429 	return(tmp_dev_name);
430 }
431 
432 /*
433  * Returns the name of the device node used to represent
434  * the raw slice.
435  * Note that the storage used for the returned string is static,
436  * and the string is overwritten each time this function is called.
437  */
438 const char *
439 slice_get_raw_device_name(const struct slice *s)
440 {
441 	static char tmp_dev_name[256];
442 
443 	snprintf(tmp_dev_name, 256, "%ss%d", s->parent->device, s->number);
444 	return(tmp_dev_name);
445 }
446 
447 int
448 slice_get_number(const struct slice *s)
449 {
450 	return(s->number);
451 }
452 
453 const char *
454 slice_get_desc(const struct slice *s)
455 {
456 	return(s->desc);
457 }
458 
459 unsigned long
460 slice_get_capacity(const struct slice *s)
461 {
462 	return(s->capacity);
463 }
464 
465 unsigned long
466 slice_get_start(const struct slice *s)
467 {
468 	return(s->start);
469 }
470 
471 unsigned long
472 slice_get_size(const struct slice *s)
473 {
474 	return(s->size);
475 }
476 
477 int
478 slice_get_type(const struct slice *s)
479 {
480 	return(s->type);
481 }
482 
483 int
484 slice_get_flags(const struct slice *s)
485 {
486 	return(s->flags);
487 }
488 
489 struct subpartition *
490 slice_subpartition_first(const struct slice *s)
491 {
492 	return(s->subpartition_head);
493 }
494 
495 /*
496  * Free all memory for a list of slice descriptions.
497  */
498 void
499 slices_free(struct slice *head)
500 {
501 	struct slice *next;
502 
503 	while (head != NULL) {
504 		next = head->next;
505 		subpartitions_free(head);
506 		free(head->desc);
507 		AURA_FREE(head, slice);
508 		head = next;
509 	}
510 }
511 
512 struct subpartition *
513 subpartition_new_hammer(struct slice *s, const char *mountpoint, long capacity)
514 {
515 	struct subpartition *sp;
516 
517 	AURA_MALLOC(sp, subpartition);
518 
519 	sp->parent = s;
520 
521 	struct subpartition *last = s->subpartition_tail;
522 	if (last == NULL) {
523 		sp->letter = 'a';
524 	} else if (last->letter == 'b') {
525 		sp->letter = 'd';
526 	} else {
527 		sp->letter = (char)(last->letter + 1);
528 	}
529 
530 	sp->mountpoint = aura_strdup(mountpoint);
531 	sp->capacity = capacity;
532 	sp->type = FS_HAMMER;
533 
534 	/*
535 	 * We need this here, because a UFS /boot needs valid values
536 	 */
537 	if (sp->capacity < 1024)
538 		sp->fsize = 1024;
539 	else
540 		sp->fsize = 2048;
541 
542 	if (sp->capacity < 1024)
543 		sp->bsize = 8192;
544 	else
545 		sp->bsize = 16384;
546 
547 	sp->is_swap = 0;
548 	sp->pfs = 0;
549 	if (strcasecmp(mountpoint, "swap") == 0)
550 		sp->is_swap = 1;
551 	if (strcmp(mountpoint, "/") != 0 && strcmp(mountpoint, "/boot") != 0 &&
552 	    strcmp(mountpoint, "swap") != 0)
553 		sp->pfs = 1;
554 
555 	sp->next = NULL;
556 	if (s->subpartition_head == NULL)
557 		s->subpartition_head = sp;
558 	else
559 		s->subpartition_tail->next = sp;
560 
561 	sp->prev = s->subpartition_tail;
562 	s->subpartition_tail = sp;
563 
564 	return(sp);
565 }
566 
567 /*
568  * NOTE: arguments to this function are not checked for sanity.
569  *
570  * fsize and/or bsize may both be -1, indicating
571  * "choose a reasonable default."
572  */
573 struct subpartition *
574 subpartition_new(struct slice *s, const char *mountpoint, long capacity,
575 		 int softupdates, long fsize, long bsize, int mfsbacked)
576 {
577 	struct subpartition *sp, *sptmp;
578 	int letter='d';
579 
580 	AURA_MALLOC(sp, subpartition);
581 
582 	sp->parent = s;
583 
584 	sp->mountpoint = aura_strdup(mountpoint);
585 	sp->capacity = capacity;
586 	sp->type = FS_UFS;
587 
588 	if (fsize == -1) {
589 		if (sp->capacity < 1024)
590 			sp->fsize = 1024;
591 		else
592 			sp->fsize = 2048;
593 	} else {
594 		sp->fsize = fsize;
595 	}
596 
597 	if (bsize == -1) {
598 		if (sp->capacity < 1024)
599 			sp->bsize = 8192;
600 		else
601 			sp->bsize = 16384;
602 	} else {
603 		sp->bsize = bsize;
604 	}
605 
606 	if (softupdates == -1) {
607 		if (strcmp(mountpoint, "/") == 0)
608 			sp->softupdates = 0;
609 		else
610 			sp->softupdates = 1;
611 	} else {
612 		sp->softupdates = softupdates;
613 	}
614 
615 	sp->mfsbacked = mfsbacked;
616 
617 	sp->is_swap = 0;
618 	if (strcasecmp(mountpoint, "swap") == 0)
619 		sp->is_swap = 1;
620 
621 	if (s->subpartition_head == NULL) {
622 		s->subpartition_head = sp;
623 		s->subpartition_tail = sp;
624 	} else {
625 		for (sptmp = s->subpartition_head; sptmp != NULL;
626 		     sptmp = sptmp->next) {
627 			if (strcmp(sptmp->mountpoint, sp->mountpoint) > 0)
628 				break;
629 		}
630 		if (sptmp != NULL) {
631 			if (s->subpartition_head == sptmp)
632 				s->subpartition_head = sp;
633 			else
634 				sptmp->prev->next = sp;
635 			sp->next = sptmp;
636 			sp->prev = sptmp->prev;
637 			sptmp->prev = sp;
638 		} else {
639 			sp->prev = s->subpartition_tail;
640 			s->subpartition_tail->next = sp;
641 			s->subpartition_tail = sp;
642 		}
643 	}
644 
645 	for (sptmp = s->subpartition_head; sptmp != NULL;
646 	     sptmp = sptmp->next) {
647 		if (sptmp->mfsbacked)
648 			sptmp->letter = '@';
649 		else if (strcmp(sptmp->mountpoint, "/") == 0 ||
650 			 strcmp(sptmp->mountpoint, "/dummy") == 0)
651 			sptmp->letter = 'a';
652 		else if (strcasecmp(sptmp->mountpoint, "swap") == 0)
653 			sptmp->letter = 'b';
654 		else
655 			sptmp->letter = letter++;
656 	}
657 
658 	return(sp);
659 }
660 
661 /*
662  * Find the subpartition description in the given storage
663  * description whose mountpoint matches the given string exactly.
664  */
665 struct subpartition *
666 subpartition_find(const struct slice *s, const char *fmt, ...)
667 {
668 	struct subpartition *sp = s->subpartition_head;
669 	char *mountpoint;
670 	va_list args;
671 
672 	va_start(args, fmt);
673 	vasprintf(&mountpoint, fmt, args);
674 	va_end(args);
675 
676 	while (sp != NULL) {
677 		if (strcmp(mountpoint, sp->mountpoint) == 0) {
678 			free(mountpoint);
679 			return(sp);
680 		}
681 		sp = sp->next;
682 	}
683 
684 	free(mountpoint);
685 	return(NULL);
686 }
687 
688 /*
689  * Find the subpartition description in the given storage
690  * description where the given filename would presumably
691  * reside.  This is the subpartition whose mountpoint is
692  * the longest match for the given filename.
693  */
694 struct subpartition *
695 subpartition_of(const struct slice *s, const char *fmt, ...)
696 {
697 	struct subpartition *sp = s->subpartition_head;
698 	struct subpartition *csp = NULL;
699 	size_t len = 0;
700 	char *filename;
701 	va_list args;
702 
703 	va_start(args, fmt);
704 	vasprintf(&filename, fmt, args);
705 	va_end(args);
706 
707 	while (sp != NULL) {
708 		if (strlen(sp->mountpoint) > len &&
709 		    strlen(sp->mountpoint) <= strlen(filename) &&
710 		    strncmp(filename, sp->mountpoint, strlen(sp->mountpoint)) == 0) {
711 				csp = sp;
712 				len = strlen(csp->mountpoint);
713 		}
714 		sp = sp->next;
715 	}
716 
717 	free(filename);
718 	return(csp);
719 }
720 
721 struct subpartition *
722 subpartition_find_capacity(const struct slice *s, long capacity)
723 {
724 	struct subpartition *sp = s->subpartition_head;
725 
726 	while (sp != NULL) {
727 		if (sp->capacity == capacity)
728 			return(sp);
729 		sp = sp->next;
730 	}
731 
732 	return(NULL);
733 }
734 
735 struct subpartition *
736 subpartition_next(const struct subpartition *sp)
737 {
738 	return(sp->next);
739 }
740 
741 int
742 subpartition_get_pfs(const struct subpartition *sp)
743 {
744 	return(sp->pfs);
745 }
746 
747 /*
748  * Returns the name of the device node used to represent
749  * the subpartition.
750  * Note that the storage used for the returned string is static,
751  * and the string is overwritten each time this function is called.
752  */
753 const char *
754 subpartition_get_device_name(const struct subpartition *sp)
755 {
756 	static char tmp_dev_name[256];
757 
758 	snprintf(tmp_dev_name, 256, "%ss%d%c", sp->parent->parent->device,
759 	    sp->parent->number, sp->letter);
760 	return(tmp_dev_name);
761 }
762 
763 /*
764  * Returns the name of the device node used to represent
765  * the raw subpartition.
766  * Note that the storage used for the returned string is static,
767  * and the string is overwritten each time this function is called.
768  */
769 const char *
770 subpartition_get_raw_device_name(const struct subpartition *sp)
771 {
772 	static char tmp_dev_name[256];
773 
774 	snprintf(tmp_dev_name, 256, "r%ss%d%c", sp->parent->parent->device,
775 	    sp->parent->number, sp->letter);
776 	return(tmp_dev_name);
777 }
778 
779 const char *
780 subpartition_get_mountpoint(const struct subpartition *sp)
781 {
782 	return(sp->mountpoint);
783 }
784 
785 char
786 subpartition_get_letter(const struct subpartition *sp)
787 {
788 	return(sp->letter);
789 }
790 
791 unsigned long
792 subpartition_get_fsize(const struct subpartition *sp)
793 {
794 	return(sp->fsize);
795 }
796 
797 unsigned long
798 subpartition_get_bsize(const struct subpartition *sp)
799 {
800 	return(sp->bsize);
801 }
802 
803 unsigned long
804 subpartition_get_capacity(const struct subpartition *sp)
805 {
806 	return(sp->capacity);
807 }
808 
809 int
810 subpartition_is_swap(const struct subpartition *sp)
811 {
812 	return(sp->is_swap);
813 }
814 
815 int
816 subpartition_is_softupdated(const struct subpartition *sp)
817 {
818 	return(sp->softupdates);
819 }
820 int
821 subpartition_is_mfsbacked(const struct subpartition *sp)
822 {
823 	return(sp->mfsbacked);
824 }
825 
826 int
827 subpartition_count(const struct slice *s)
828 {
829 	struct subpartition *sp = s->subpartition_head;
830 	int count = 0;
831 
832 	while (sp != NULL) {
833 		count++;
834 		sp = sp->next;
835 	}
836 
837 	return(count);
838 }
839 
840 void
841 subpartitions_free(struct slice *s)
842 {
843 	struct subpartition *sp = s->subpartition_head, *next;
844 
845 	while (sp != NULL) {
846 		next = sp->next;
847 		free(sp->mountpoint);
848 		AURA_FREE(sp, subpartition);
849 		sp = next;
850 	}
851 
852 	s->subpartition_head = NULL;
853 	s->subpartition_tail = NULL;
854 }
855 
856 long
857 measure_activated_swap(const struct i_fn_args *a)
858 {
859 	FILE *p;
860 	char line[256];
861 	char *word;
862 	long swap = 0;
863 
864 	if ((p = aura_popen("%s%s -k", "r", a->os_root, cmd_name(a, "SWAPINFO"))) == NULL)
865 		return(0);
866 	while (fgets(line, 255, p) != NULL) {
867 		if ((word = strtok(line, " \t")) == NULL)
868 			continue;
869 		if (strcmp(word, "Device") == 0)
870 			continue;
871 		if ((word = strtok(NULL, " \t")) == NULL)
872 			continue;
873 		swap += atol(word);
874 	}
875 	aura_pclose(p);
876 
877 	return(swap / 1024);
878 }
879 
880 long
881 measure_activated_swap_from_slice(const struct i_fn_args *a,
882     const struct disk *d, const struct slice *s)
883 {
884 	FILE *p;
885 	char *dev, *word;
886 	char line[256];
887 	long swap = 0;
888 
889 	if ((p = aura_popen("%s%s -k", "r", a->os_root, cmd_name(a, "SWAPINFO"))) == NULL)
890 		return(0);
891 
892 	asprintf(&dev, "/dev/%ss%d", d->device, s->number);
893 
894 	while (fgets(line, 255, p) != NULL) {
895 		if ((word = strtok(line, " \t")) == NULL)
896 			continue;
897 		if (strcmp(word, "Device") == 0)
898 			continue;
899 		if (strstr(word, dev) != word)
900 			continue;
901 		if ((word = strtok(NULL, " \t")) == NULL)
902 			continue;
903 		swap += atol(word);
904 	}
905 	aura_pclose(p);
906 	free(dev);
907 
908 	return(swap / 1024);
909 }
910 
911 long
912 measure_activated_swap_from_disk(const struct i_fn_args *a,
913 				 const struct disk *d)
914 {
915 	struct slice *s;
916 	long swap = 0;
917 
918 	for (s = d->slice_head; s != NULL; s = s->next)
919 		swap += measure_activated_swap_from_slice(a, d, s);
920 
921 	return(swap);
922 }
923