1 /* $NetBSD: swaplist.c,v 1.25 2026/03/24 16:38:12 yamt 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.25 2026/03/24 16:38:12 yamt 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 #define pgtoqb(b) ((int64_t)(b) * page_size) 50 51 /* 52 * NOTE: This file is separate from swapctl.c so that pstat can grab it. 53 */ 54 55 #include "swapctl.h" 56 57 static int 58 swapent_cmp(const void *a, const void *b) 59 { 60 const struct swapent *p, *q; 61 size_t l1, l2; 62 63 p = (const struct swapent *)a; 64 q = (const struct swapent *)b; 65 66 /* 67 * Sort by priority first, highest priority first 68 * (which means increasing order of the priority field) 69 */ 70 if (p->se_priority != q->se_priority) 71 return p->se_priority - q->se_priority; 72 73 /* 74 * Then sort by device name. These are arbitrary, 75 * and no order really makes more sense than any 76 * other, but the kernel's lists of swap devices at 77 * the same priority are reordered at will, but for 78 * a better human experience, keeping a constant 79 * ordering is better, this should achieve that. 80 */ 81 if ((l1 = strlen(p->se_path)) != (l2 = strlen(q->se_path))) 82 return l1 > l2 ? 1 : -1; 83 return strcmp(p->se_path, q->se_path); 84 } 85 86 int 87 list_swap(int pri, int kflag, int pflag, int tflag, int dolong, int hflag) 88 { 89 struct swapent *sep, *fsep; 90 long blocksize; 91 /* size, used, bad, avail */ 92 char szbuf[5], usbuf[5], badbuf[5], avbuf[5]; 93 const char *header, *suff; 94 size_t l; 95 int hlen, size, inuse, npgbad, ncounted, pathmax, cw; 96 int rnswap, nswap = swapctl(SWAP_NSWAP, 0, 0), i; 97 int64_t totalsize, totalinuse, totalbad; 98 int page_size = sysconf(_SC_PAGESIZE); 99 100 if (nswap < 1) { 101 puts("no swap devices configured"); 102 return 0; 103 } 104 105 /* 106 * treat the value from swapctl(SWAP_NSWAP) as a hint, 107 * as the value might have changed (swap devices added 108 * or removed) between when that info was obtained, and 109 * when we actually request the stats. 110 * 111 * Assume it will be increasing (if descreasing no harm) 112 * and allow for it to have grown a reasonable amount. 113 * 114 * Then we simply use however many swap table entries 115 * the kernel returns from swapctl(SWAP_STATS), be that 116 * more or less than swapctl(SWAP_NSWAP) claimed, or 117 * as many as or less than we have allocated space for. 118 * If the kernel has added even more than the guess 119 * below, then we will not get all of them. Oh well... 120 */ 121 nswap += 3; 122 123 fsep = sep = (struct swapent *)malloc(nswap * sizeof(*sep)); 124 if (sep == NULL) 125 err(1, "malloc"); 126 rnswap = swapctl(SWAP_STATS, (void *)sep, nswap); 127 if (rnswap < 0) 128 err(1, "SWAP_STATS"); 129 130 if (rnswap == 0) { 131 puts("no swap devices configured"); 132 return 0; 133 } 134 135 /* keep the list ordered, as long as it remains stable */ 136 if (rnswap > 1) 137 qsort(sep, rnswap, sizeof(struct swapent), swapent_cmp); 138 139 pathmax = 11; 140 switch (kflag) { 141 case 1: 142 header = "1K-blocks"; 143 blocksize = 1024; 144 suff = "KBytes"; 145 break; 146 case 2: 147 suff = "MBytes"; 148 header = "1M-blocks"; 149 blocksize = 1024 * 1024; 150 break; 151 case 3: 152 header = "1G-blocks"; 153 blocksize = 1024 * 1024 * 1024; 154 suff = "GBytes"; 155 break; 156 default: 157 suff = "blocks"; 158 if (!hflag) { 159 header = getbsize(&hlen, &blocksize); 160 } else { 161 header = "Size"; 162 blocksize = 1; suff = ""; /* unused */ 163 } 164 break; 165 } 166 if (hflag || kflag) 167 hlen = strlen(header); 168 169 cw = 8; 170 if (dolong && tflag == 0) { 171 totalsize = 0; 172 for (i = rnswap; i-- > 0; sep++) { 173 if ((size_t)pathmax < (l = strlen(sep->se_path))) 174 pathmax = l; 175 totalsize += sep->se_nblks; 176 } 177 sep = fsep; 178 179 if (!hflag) { 180 totalsize = dbtoqb(totalsize) / blocksize; 181 if (totalsize > 9999999990) 182 cw = 11; 183 else if (totalsize > 999999990) 184 cw = 10; 185 else if (totalsize > 99999990) 186 cw = 9; 187 188 if (hlen < cw) 189 hlen = cw; 190 } 191 192 /* 193 * The intent here is that for the 3 size columns, 194 * the units digit (or scale letter with -h) is always 195 * under the rightmost character of its heading, the 196 * '%' character is under the 't' in "Capacity" and 197 * the units digit of the priority is under the 'o' 198 * of "Priority". This looks reasonably good. 199 */ 200 (void)printf("%-*s %*s %*s %*s %*s %8s %s\n", 201 pathmax, "Device", hlen, header, cw, "Used", 202 cw, "Bad", cw, "Avail", "Capacity", "Priority"); 203 } 204 totalsize = totalinuse = totalbad = ncounted = 0; 205 for (; rnswap-- > 0; sep++) { 206 if (pflag && sep->se_priority != pri) 207 continue; 208 ncounted++; 209 size = sep->se_nblks; 210 inuse = sep->se_inuse; 211 npgbad = sep->se_npgbad; 212 totalsize += size; 213 totalinuse += inuse; 214 totalbad += npgbad; 215 216 if (dolong && tflag == 0) { 217 if (hflag == 0) { 218 (void)printf("%-*s %*ld ", 219 pathmax, sep->se_path, hlen, 220 (long)(dbtoqb(size) / blocksize)); 221 222 (void)printf("%*ld %*ld %*ld %5.0f%% %3d\n", 223 cw, (long)(dbtoqb(inuse) / blocksize), cw, 224 (long)(pgtoqb(npgbad) / blocksize), cw, 225 (long)(dbtoqb(size - inuse) / blocksize), 226 (double)inuse / (double)size * 100.0, 227 sep->se_priority); 228 } else { 229 if ((humanize_number(szbuf, sizeof(szbuf), 230 (dbtoqb(size)), "", HN_AUTOSCALE, 231 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 232 err(1, "humanize_number"); 233 if ((humanize_number(usbuf, sizeof(usbuf), 234 (dbtoqb(inuse)), "", HN_AUTOSCALE, 235 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 236 err(1, "humanize_number"); 237 if ((humanize_number(badbuf, sizeof(badbuf), 238 (pgtoqb(npgbad)), "", HN_AUTOSCALE, 239 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 240 err(1, "humanize_number"); 241 if ((humanize_number(avbuf, sizeof(avbuf), 242 (dbtoqb(size-inuse)), "", HN_AUTOSCALE, 243 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 244 err(1, "humanize_number"); 245 (void)printf("%-*s %*s ", 246 pathmax, sep->se_path, hlen, szbuf); 247 248 (void)printf("%*s %*s %*s %5.0f%% %3d\n", 249 cw, usbuf, cw, badbuf, cw, avbuf, 250 (double)inuse / (double)size * 100.0, 251 sep->se_priority); 252 } 253 } 254 } 255 if (tflag) { 256 if (hflag) { 257 if ((humanize_number(usbuf, sizeof(usbuf), 258 (dbtoqb(totalinuse)), "", HN_AUTOSCALE, 259 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 260 err(1, "humanize_number"); 261 if ((humanize_number(szbuf, sizeof(szbuf), 262 (dbtoqb(totalsize)), "", HN_AUTOSCALE, 263 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 264 err(1, "humanize_number"); 265 (void)printf("%s/%s swap space\n", usbuf, szbuf); 266 } else { 267 (void)printf("%ld/%ld %s swap space\n", 268 (long)(dbtoqb(totalinuse) / blocksize), 269 (long)(dbtoqb(totalsize) / blocksize), 270 suff); 271 } 272 } else if (dolong == 0) { 273 if (hflag) { 274 if ((humanize_number(szbuf, sizeof(szbuf), 275 (dbtoqb(totalsize)), "", HN_AUTOSCALE, 276 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 277 err(1, "humanize_number"); 278 if ((humanize_number(usbuf, sizeof(usbuf), 279 (dbtoqb(totalinuse)), "", HN_AUTOSCALE, 280 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 281 err(1, "humanize_number"); 282 if ((humanize_number(avbuf, sizeof(avbuf), 283 (dbtoqb(totalsize-totalinuse)), "", HN_AUTOSCALE, 284 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 285 err(1, "humanize_number"); 286 (void)printf( 287 "total: %s allocated, %s used, %s available.\n", 288 szbuf, usbuf, avbuf); 289 } else { 290 printf("total: %ld %s allocated, %ld %s used, " 291 "%ld %s available\n", 292 (long)(dbtoqb(totalsize) / blocksize), suff, 293 (long)(dbtoqb(totalinuse) / blocksize), suff, 294 (long)(dbtoqb(totalsize - totalinuse) / blocksize), suff); 295 } 296 } else if (ncounted > 1) { 297 if (hflag) { 298 if ((humanize_number(szbuf, sizeof(szbuf), 299 (dbtoqb(totalsize)), "", HN_AUTOSCALE, 300 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 301 err(1, "humanize_number"); 302 if ((humanize_number(usbuf, sizeof(usbuf), 303 (dbtoqb(totalinuse)), "", HN_AUTOSCALE, 304 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 305 err(1, "humanize_number"); 306 if ((humanize_number(badbuf, sizeof(badbuf), 307 (pgtoqb(totalbad)), "", HN_AUTOSCALE, 308 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 309 err(1, "humanize_number"); 310 if ((humanize_number(avbuf, sizeof(avbuf), 311 (dbtoqb(totalsize-totalinuse)), "", HN_AUTOSCALE, 312 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 313 err(1, "humanize_number"); 314 (void)printf("%-*s %*s %*s %*s %*s %5.0f%%\n", 315 pathmax, "Total", hlen, szbuf, cw, usbuf, 316 cw, badbuf, cw, avbuf, 317 (double)(totalinuse) / (double)totalsize * 100.0); 318 } else { 319 (void)printf("%-*s %*ld %*ld %*ld %*ld %5.0f%%\n", 320 pathmax, "Total", hlen, 321 (long)(dbtoqb(totalsize) / blocksize), 322 cw, (long)(dbtoqb(totalinuse) / blocksize), 323 cw, (long)(pgtoqb(totalbad) / blocksize), cw, 324 (long)(dbtoqb(totalsize - totalinuse) / blocksize), 325 (double)(totalinuse) / (double)totalsize * 100.0); 326 } 327 } 328 if (fsep) 329 (void)free(fsep); 330 331 return 1; 332 } 333