1 /* $NetBSD: swaplist.c,v 1.23 2026/02/16 23:18:54 kre Exp $ */ 2 3 /* 4 * Copyright (c) 1997 Matthew R. Green 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 #include <sys/cdefs.h> 29 30 #ifndef lint 31 __RCSID("$NetBSD: swaplist.c,v 1.23 2026/02/16 23:18:54 kre Exp $"); 32 #endif 33 34 35 #include <sys/param.h> 36 #include <sys/stat.h> 37 #include <sys/swap.h> 38 39 #include <unistd.h> 40 #include <err.h> 41 #include <errno.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <inttypes.h> 46 #include <util.h> 47 48 #define dbtoqb(b) dbtob((int64_t)(b)) 49 50 /* 51 * NOTE: This file is separate from swapctl.c so that pstat can grab it. 52 */ 53 54 #include "swapctl.h" 55 56 static int 57 swapent_cmp(const void *a, const void *b) 58 { 59 const struct swapent *p, *q; 60 size_t l1, l2; 61 62 p = (const struct swapent *)a; 63 q = (const struct swapent *)b; 64 65 /* 66 * Sort by priority first, highest priority first 67 * (which means increasing order of the priority field) 68 */ 69 if (p->se_priority != q->se_priority) 70 return p->se_priority - q->se_priority; 71 72 /* 73 * Then sort by device name. These are arbitrary, 74 * and no order really makes more sense than any 75 * other, but the kernel's lists of swap devices at 76 * the same priority are reordered at will, but for 77 * a better human experience, keeping a constant 78 * ordering is better, this should achieve that. 79 */ 80 if ((l1 = strlen(p->se_path)) != (l2 = strlen(q->se_path))) 81 return l1 > l2 ? 1 : -1; 82 return strcmp(p->se_path, q->se_path); 83 } 84 85 int 86 list_swap(int pri, int kflag, int pflag, int tflag, int dolong, int hflag) 87 { 88 struct swapent *sep, *fsep; 89 long blocksize; 90 char szbuf[5], usbuf[5], avbuf[5]; /* size, used, avail */ 91 const char *header, *suff; 92 size_t l; 93 int hlen, size, inuse, ncounted, pathmax, cw; 94 int rnswap, nswap = swapctl(SWAP_NSWAP, 0, 0), i; 95 int64_t totalsize, totalinuse; 96 97 if (nswap < 1) { 98 puts("no swap devices configured"); 99 return 0; 100 } 101 102 /* 103 * treat the value from swapctl(SWAP_NSWAP) as a hint, 104 * as the value might have changed (swap devices added 105 * or removed) between when that info was obtained, and 106 * when we actually request the stats. 107 * 108 * Assume it will be increasing (if descreasing no harm) 109 * and allow for it to have grown a reasonable amount. 110 * 111 * Then we simply use however many swap table entries 112 * the kernel returns from swapctl(SWAP_STATS), be that 113 * more or less than swapctl(SWAP_NSWAP) claimed, or 114 * as many as or less than we have allocated space for. 115 * If the kernel has added even more than the guess 116 * below, then we will not get all of them. Oh well... 117 */ 118 nswap += 3; 119 120 fsep = sep = (struct swapent *)malloc(nswap * sizeof(*sep)); 121 if (sep == NULL) 122 err(1, "malloc"); 123 rnswap = swapctl(SWAP_STATS, (void *)sep, nswap); 124 if (rnswap < 0) 125 err(1, "SWAP_STATS"); 126 127 if (rnswap == 0) { 128 puts("no swap devices configured"); 129 return 0; 130 } 131 132 /* keep the list ordered, as long as it remains stable */ 133 if (rnswap > 1) 134 qsort(sep, rnswap, sizeof(struct swapent), swapent_cmp); 135 136 pathmax = 11; 137 switch (kflag) { 138 case 1: 139 header = "1K-blocks"; 140 blocksize = 1024; 141 suff = "KBytes"; 142 break; 143 case 2: 144 suff = "MBytes"; 145 header = "1M-blocks"; 146 blocksize = 1024 * 1024; 147 break; 148 case 3: 149 header = "1G-blocks"; 150 blocksize = 1024 * 1024 * 1024; 151 suff = "GBytes"; 152 break; 153 default: 154 suff = "blocks"; 155 if (!hflag) { 156 header = getbsize(&hlen, &blocksize); 157 } else { 158 header = "Size"; 159 blocksize = 1; suff = ""; /* unused */ 160 } 161 break; 162 } 163 if (hflag || kflag) 164 hlen = strlen(header); 165 166 cw = 8; 167 if (dolong && tflag == 0) { 168 totalsize = 0; 169 for (i = rnswap; i-- > 0; sep++) { 170 if ((size_t)pathmax < (l = strlen(sep->se_path))) 171 pathmax = l; 172 totalsize += sep->se_nblks; 173 } 174 sep = fsep; 175 176 if (!hflag) { 177 totalsize = dbtoqb(totalsize) / blocksize; 178 if (totalsize > 9999999990) 179 cw = 11; 180 else if (totalsize > 999999990) 181 cw = 10; 182 else if (totalsize > 99999990) 183 cw = 9; 184 185 if (hlen < cw) 186 hlen = cw; 187 } 188 189 /* 190 * The intent here is that for the 3 size columns, 191 * the units digit (or scale letter with -h) is always 192 * under the rightmost character of its heading, the 193 * '%' character is under the 't' in "Capacity" and 194 * the units digit of the priority is under the 'o' 195 * of "Priority". This looks reasonably good. 196 */ 197 (void)printf("%-*s %*s %*s %*s %8s %s\n", 198 pathmax, "Device", hlen, header, cw, "Used", 199 cw, "Avail", "Capacity", "Priority"); 200 } 201 totalsize = totalinuse = ncounted = 0; 202 for (; rnswap-- > 0; sep++) { 203 if (pflag && sep->se_priority != pri) 204 continue; 205 ncounted++; 206 size = sep->se_nblks; 207 inuse = sep->se_inuse; 208 totalsize += size; 209 totalinuse += inuse; 210 211 if (dolong && tflag == 0) { 212 if (hflag == 0) { 213 (void)printf("%-*s %*ld ", 214 pathmax, sep->se_path, hlen, 215 (long)(dbtoqb(size) / blocksize)); 216 217 (void)printf("%*ld %*ld %5.0f%% %3d\n", 218 cw, (long)(dbtoqb(inuse) / blocksize), cw, 219 (long)(dbtoqb(size - inuse) / blocksize), 220 (double)inuse / (double)size * 100.0, 221 sep->se_priority); 222 } else { 223 if ((humanize_number(szbuf, sizeof(szbuf), 224 (dbtoqb(size)), "", HN_AUTOSCALE, 225 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 226 err(1, "humanize_number"); 227 if ((humanize_number(usbuf, sizeof(usbuf), 228 (dbtoqb(inuse)), "", HN_AUTOSCALE, 229 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 230 err(1, "humanize_number"); 231 if ((humanize_number(avbuf, sizeof(avbuf), 232 (dbtoqb(size-inuse)), "", HN_AUTOSCALE, 233 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 234 err(1, "humanize_number"); 235 (void)printf("%-*s %*s ", 236 pathmax, sep->se_path, hlen, szbuf); 237 238 (void)printf("%*s %*s %5.0f%% %3d\n", 239 cw, usbuf, cw, avbuf, 240 (double)inuse / (double)size * 100.0, 241 sep->se_priority); 242 } 243 } 244 } 245 if (tflag) { 246 if (hflag) { 247 if ((humanize_number(usbuf, sizeof(usbuf), 248 (dbtoqb(totalinuse)), "", HN_AUTOSCALE, 249 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 250 err(1, "humanize_number"); 251 if ((humanize_number(szbuf, sizeof(szbuf), 252 (dbtoqb(totalsize)), "", HN_AUTOSCALE, 253 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 254 err(1, "humanize_number"); 255 (void)printf("%s/%s swap space\n", usbuf, szbuf); 256 } else { 257 (void)printf("%ld/%ld %s swap space\n", 258 (long)(dbtoqb(totalinuse) / blocksize), 259 (long)(dbtoqb(totalsize) / blocksize), 260 suff); 261 } 262 } else if (dolong == 0) { 263 if (hflag) { 264 if ((humanize_number(szbuf, sizeof(szbuf), 265 (dbtoqb(totalsize)), "", HN_AUTOSCALE, 266 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 267 err(1, "humanize_number"); 268 if ((humanize_number(usbuf, sizeof(usbuf), 269 (dbtoqb(totalinuse)), "", HN_AUTOSCALE, 270 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 271 err(1, "humanize_number"); 272 if ((humanize_number(avbuf, sizeof(avbuf), 273 (dbtoqb(totalsize-totalinuse)), "", HN_AUTOSCALE, 274 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 275 err(1, "humanize_number"); 276 (void)printf( 277 "total: %s allocated, %s used, %s available.\n", 278 szbuf, usbuf, avbuf); 279 } else { 280 printf("total: %ld %s allocated, %ld %s used, " 281 "%ld %s available\n", 282 (long)(dbtoqb(totalsize) / blocksize), suff, 283 (long)(dbtoqb(totalinuse) / blocksize), suff, 284 (long)(dbtoqb(totalsize - totalinuse) / blocksize), suff); 285 } 286 } else if (ncounted > 1) { 287 if (hflag) { 288 if ((humanize_number(szbuf, sizeof(szbuf), 289 (dbtoqb(totalsize)), "", HN_AUTOSCALE, 290 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 291 err(1, "humanize_number"); 292 if ((humanize_number(usbuf, sizeof(usbuf), 293 (dbtoqb(totalinuse)), "", HN_AUTOSCALE, 294 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 295 err(1, "humanize_number"); 296 if ((humanize_number(avbuf, sizeof(avbuf), 297 (dbtoqb(totalsize-totalinuse)), "", HN_AUTOSCALE, 298 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 299 err(1, "humanize_number"); 300 (void)printf("%-*s %*s %*s %*s %5.0f%%\n", 301 pathmax, "Total", hlen, szbuf, cw, usbuf, cw, avbuf, 302 (double)(totalinuse) / (double)totalsize * 100.0); 303 } else { 304 (void)printf("%-*s %*ld %*ld %*ld %5.0f%%\n", 305 pathmax, "Total", hlen, 306 (long)(dbtoqb(totalsize) / blocksize), 307 cw, (long)(dbtoqb(totalinuse) / blocksize), cw, 308 (long)(dbtoqb(totalsize - totalinuse) / blocksize), 309 (double)(totalinuse) / (double)totalsize * 100.0); 310 } 311 } 312 if (fsep) 313 (void)free(fsep); 314 315 return 1; 316 } 317