Merge branch 'vendor/TCPDUMP' and update build for the update.
[dragonfly.git] / usr.sbin / installer / libinstaller / diskutil.c
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 }