1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 /* 43 * Swap administrative interface 44 * Used to add/delete/list swap devices. 45 */ 46 47 #include <sys/types.h> 48 #include <sys/dumpadm.h> 49 #include <string.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <unistd.h> 53 #include <errno.h> 54 #include <sys/param.h> 55 #include <dirent.h> 56 #include <sys/swap.h> 57 #include <sys/sysmacros.h> 58 #include <sys/mkdev.h> 59 #include <sys/stat.h> 60 #include <sys/statvfs.h> 61 #include <sys/uadmin.h> 62 #include <vm/anon.h> 63 #include <fcntl.h> 64 #include <locale.h> 65 #include <libintl.h> 66 67 #define LFLAG 0x01 /* swap -l (list swap devices) */ 68 #define DFLAG 0x02 /* swap -d (delete swap device) */ 69 #define AFLAG 0x04 /* swap -a (add swap device) */ 70 #define SFLAG 0x08 /* swap -s (swap info summary) */ 71 #define P1FLAG 0x10 /* swap -1 (swapadd pass1; do not modify dump device) */ 72 #define P2FLAG 0x20 /* swap -2 (swapadd pass2; do not modify dump device) */ 73 74 static char *prognamep; 75 76 static int add(char *, off_t, off_t, int); 77 static int delete(char *, off_t); 78 static void usage(void); 79 static int doswap(void); 80 static int valid(char *, off_t, off_t); 81 static int list(void); 82 83 int 84 main(int argc, char **argv) 85 { 86 int c, flag = 0; 87 int ret; 88 off_t s_offset = 0; 89 off_t length = 0; 90 char *pathname; 91 92 (void) setlocale(LC_ALL, ""); 93 94 #if !defined(TEXT_DOMAIN) 95 #define TEXT_DOMAIN "SYS_TEST" 96 #endif 97 (void) textdomain(TEXT_DOMAIN); 98 99 prognamep = argv[0]; 100 if (argc < 2) { 101 usage(); 102 exit(1); 103 } 104 105 while ((c = getopt(argc, argv, "lsd:a:12")) != EOF) { 106 char *char_p; 107 switch (c) { 108 case 'l': /* list all the swap devices */ 109 if (argc != 2 || flag) { 110 usage(); 111 exit(1); 112 } 113 flag |= LFLAG; 114 ret = list(); 115 break; 116 case 's': 117 if (argc != 2 || flag) { 118 usage(); 119 exit(1); 120 } 121 flag |= SFLAG; 122 ret = doswap(); 123 break; 124 case 'd': 125 /* 126 * The argument for starting offset is optional. 127 * If no argument is specified, the entire swap file 128 * is added although this will fail if a non-zero 129 * starting offset was specified when added. 130 */ 131 if ((argc - optind) > 1 || flag != 0) { 132 usage(); 133 exit(1); 134 } 135 flag |= DFLAG; 136 pathname = optarg; 137 if (optind < argc) { 138 errno = 0; 139 s_offset = strtol(argv[optind++], &char_p, 10); 140 if (errno != 0 || *char_p != '\0') { 141 (void) fprintf(stderr, 142 gettext("error in [low block]\n")); 143 exit(1); 144 } 145 } 146 ret = delete(pathname, s_offset); 147 break; 148 149 case 'a': 150 /* 151 * The arguments for starting offset and number of 152 * blocks are optional. If only the starting offset 153 * is specified, all the blocks to the end of the swap 154 * file will be added. If no starting offset is 155 * specified, the entire swap file is assumed. 156 */ 157 if ((argc - optind) > 2 || 158 (flag & ~(P1FLAG | P2FLAG)) != 0) { 159 usage(); 160 exit(1); 161 } 162 if (*optarg != '/') { 163 (void) fprintf(stderr, 164 gettext("%s: path must be absolute\n"), 165 prognamep); 166 exit(1); 167 } 168 flag |= AFLAG; 169 pathname = optarg; 170 if (optind < argc) { 171 errno = 0; 172 s_offset = strtol(argv[optind++], &char_p, 10); 173 if (errno != 0 || *char_p != '\0') { 174 (void) fprintf(stderr, 175 gettext("error in [low block]\n")); 176 exit(1); 177 } 178 } 179 if (optind < argc) { 180 errno = 0; 181 length = strtol(argv[optind++], &char_p, 10); 182 if (errno != 0 || *char_p != '\0') { 183 (void) fprintf(stderr, 184 gettext("error in [nbr of blocks]\n")); 185 exit(1); 186 } 187 } 188 if ((ret = valid(pathname, 189 s_offset * 512, length * 512)) == 0) 190 ret = add(pathname, s_offset, length, flag); 191 break; 192 193 case '1': 194 flag |= P1FLAG; 195 break; 196 197 case '2': 198 flag |= P2FLAG; 199 break; 200 201 case '?': 202 usage(); 203 exit(1); 204 } 205 } 206 if (!flag) { 207 usage(); 208 exit(1); 209 } 210 return (ret); 211 } 212 213 214 static void 215 usage(void) 216 { 217 (void) fprintf(stderr, gettext("Usage:\t%s -l\n"), prognamep); 218 (void) fprintf(stderr, "\t%s -s\n", prognamep); 219 (void) fprintf(stderr, gettext("\t%s -d <file name> [low block]\n"), 220 prognamep); 221 (void) fprintf(stderr, gettext("\t%s -a <file name> [low block]" 222 " [nbr of blocks]\n"), prognamep); 223 } 224 225 /* 226 * Implement: 227 * #define ctok(x) ((ctob(x))>>10) 228 * in a machine independent way. (Both assume a click > 1k) 229 */ 230 static size_t 231 ctok(pgcnt_t clicks) 232 { 233 static int factor = -1; 234 235 if (factor == -1) 236 factor = (int)(sysconf(_SC_PAGESIZE) >> 10); 237 return ((size_t)(clicks * factor)); 238 } 239 240 241 static int 242 doswap(void) 243 { 244 struct anoninfo ai; 245 pgcnt_t allocated, reserved, available; 246 247 /* 248 * max = total amount of swap space including physical memory 249 * ai.ani_max = MAX(anoninfo.ani_resv, anoninfo.ani_max) + 250 * availrmem - swapfs_minfree; 251 * ai.ani_free = amount of unallocated anonymous memory 252 * (ie. = resverved_unallocated + unreserved) 253 * ai.ani_free = anoninfo.ani_free + (availrmem - swapfs_minfree); 254 * ai.ani_resv = total amount of reserved anonymous memory 255 * ai.ani_resv = anoninfo.ani_resv; 256 * 257 * allocated = anon memory not free 258 * reserved = anon memory reserved but not allocated 259 * available = anon memory not reserved 260 */ 261 if (swapctl(SC_AINFO, &ai) == -1) { 262 perror(prognamep); 263 return (2); 264 } 265 266 allocated = ai.ani_max - ai.ani_free; 267 reserved = ai.ani_resv - allocated; 268 available = ai.ani_max - ai.ani_resv; 269 270 /* 271 * TRANSLATION_NOTE 272 * Translations (if any) of these keywords should match with 273 * translations (if any) of the swap.1M man page keywords for 274 * -s option: "allocated", "reserved", "used", "available" 275 */ 276 (void) printf(gettext("total: %luk bytes allocated + %luk reserved = \ 277 %luk used, %luk available\n"), 278 ctok(allocated), ctok(reserved), ctok(reserved) + ctok(allocated), 279 ctok(available)); 280 281 return (0); 282 } 283 284 static int 285 list(void) 286 { 287 struct swaptable *st; 288 struct swapent *swapent; 289 int i; 290 struct stat64 statbuf; 291 char *path; 292 char fullpath[MAXPATHLEN+1]; 293 int num; 294 295 if ((num = swapctl(SC_GETNSWP, NULL)) == -1) { 296 perror(prognamep); 297 return (2); 298 } 299 if (num == 0) { 300 (void) fprintf(stderr, gettext("No swap devices configured\n")); 301 return (1); 302 } 303 304 if ((st = malloc(num * sizeof (swapent_t) + sizeof (int))) 305 == NULL) { 306 (void) fprintf(stderr, 307 gettext("Malloc failed. Please try later.\n")); 308 perror(prognamep); 309 return (2); 310 } 311 if ((path = malloc(num * MAXPATHLEN)) == NULL) { 312 (void) fprintf(stderr, 313 gettext("Malloc failed. Please try later.\n")); 314 perror(prognamep); 315 return (2); 316 } 317 swapent = st->swt_ent; 318 for (i = 0; i < num; i++, swapent++) { 319 swapent->ste_path = path; 320 path += MAXPATHLEN; 321 } 322 323 st->swt_n = num; 324 if ((num = swapctl(SC_LIST, st)) == -1) { 325 perror(prognamep); 326 return (2); 327 } 328 329 /* 330 * TRANSLATION_NOTE 331 * Following translations for "swap -l" should account for for 332 * alignment of header and output. 333 * The first translation is for the header. If the alignment 334 * of the header changes, change the next 5 formats as needed 335 * to make alignment of output agree with alignment of the header. 336 * The next four translations are four cases for printing the 337 * 1st & 2nd fields. 338 * The next translation is for printing the 3rd, 4th & 5th fields. 339 * 340 * Translations (if any) of the following keywords should match the 341 * translations (if any) of the swap.1M man page keywords for 342 * -l option: "swapfile", "dev", "swaplo", "blocks", "free" 343 */ 344 (void) printf( 345 gettext("swapfile dev swaplo blocks free\n")); 346 347 swapent = st->swt_ent; 348 for (i = 0; i < num; i++, swapent++) { 349 if (*swapent->ste_path != '/') 350 (void) snprintf(fullpath, sizeof (fullpath), 351 "/dev/%s", swapent->ste_path); 352 else 353 (void) snprintf(fullpath, sizeof (fullpath), 354 "%s", swapent->ste_path); 355 if (stat64(fullpath, &statbuf) < 0) 356 if (*swapent->ste_path != '/') 357 (void) printf(gettext("%-20s - "), 358 swapent->ste_path); 359 else 360 (void) printf(gettext("%-20s ?,? "), 361 fullpath); 362 else { 363 if (statbuf.st_mode & (S_IFBLK | S_IFCHR)) 364 (void) printf(gettext("%-19s %2lu,%-2lu"), 365 fullpath, 366 major(statbuf.st_rdev), 367 minor(statbuf.st_rdev)); 368 else 369 (void) printf(gettext("%-20s - "), fullpath); 370 } 371 { 372 int diskblks_per_page = 373 (int)(sysconf(_SC_PAGESIZE) >> DEV_BSHIFT); 374 (void) printf(gettext(" %6lu %6lu %6lu"), swapent->ste_start, 375 swapent->ste_pages * diskblks_per_page, 376 swapent->ste_free * diskblks_per_page); 377 } 378 if (swapent->ste_flags & ST_INDEL) 379 (void) printf(" INDEL\n"); 380 else 381 (void) printf("\n"); 382 } 383 return (0); 384 } 385 386 static void 387 dumpadm_err(const char *warning) 388 { 389 (void) fprintf(stderr, "%s (%s):\n", warning, strerror(errno)); 390 (void) fprintf(stderr, gettext( 391 "run dumpadm(1M) to verify dump configuration\n")); 392 } 393 394 static int 395 delete(char *path, off_t offset) 396 { 397 swapres_t swr; 398 int fd; 399 400 swr.sr_name = path; 401 swr.sr_start = offset; 402 403 if (swapctl(SC_REMOVE, &swr) < 0) { 404 switch (errno) { 405 case (ENOSYS): 406 (void) fprintf(stderr, gettext( 407 "%s: Invalid operation for this filesystem type\n"), 408 path); 409 break; 410 default: 411 perror(path); 412 break; 413 } 414 return (2); 415 } 416 417 /* 418 * If our swap -d succeeded, open up /dev/dump and ask what the dump 419 * device is set to. If this returns ENODEV, we just deleted the 420 * dump device, so try to change the dump device to another swap 421 * device. We do this by firing up /usr/sbin/dumpadm -ud swap. 422 */ 423 if ((fd = open("/dev/dump", O_RDONLY)) >= 0) { 424 char dumpdev[MAXPATHLEN]; 425 426 if (ioctl(fd, DIOCGETDEV, dumpdev) == -1) { 427 if (errno == ENODEV) { 428 (void) printf(gettext("%s was dump device --\n" 429 "invoking dumpadm(1M) -d swap to " 430 "select new dump device\n"), path); 431 /* 432 * Close /dev/dump prior to executing dumpadm 433 * since /dev/dump mandates exclusive open. 434 */ 435 (void) close(fd); 436 437 if (system("/usr/sbin/dumpadm -ud swap") == -1) 438 dumpadm_err(gettext( 439 "Warning: failed to execute dumpadm -d swap")); 440 } else 441 dumpadm_err(gettext( 442 "Warning: failed to check dump device")); 443 } 444 (void) close(fd); 445 } else 446 dumpadm_err(gettext("Warning: failed to open /dev/dump")); 447 448 return (0); 449 } 450 451 /* 452 * swapres_t structure units are in 512-blocks 453 */ 454 static int 455 add(char *path, off_t offset, off_t cnt, int flags) 456 { 457 swapres_t swr; 458 459 int fd, have_dumpdev = 1; 460 struct statvfs fsb; 461 462 /* 463 * Before adding swap, we first check to see if we have a dump 464 * device configured. If we don't (errno == ENODEV), and if 465 * our SC_ADD is successful, then run /usr/sbin/dumpadm -ud swap 466 * to attempt to reconfigure the dump device to the new swap. 467 */ 468 if ((fd = open("/dev/dump", O_RDONLY)) >= 0) { 469 char dumpdev[MAXPATHLEN]; 470 471 if (ioctl(fd, DIOCGETDEV, dumpdev) == -1) { 472 if (errno == ENODEV) 473 have_dumpdev = 0; 474 else 475 dumpadm_err(gettext( 476 "Warning: failed to check dump device")); 477 } 478 479 (void) close(fd); 480 481 } else if (!(flags & P1FLAG)) 482 dumpadm_err(gettext("Warning: failed to open /dev/dump")); 483 484 swr.sr_name = path; 485 swr.sr_start = offset; 486 swr.sr_length = cnt; 487 488 if (swapctl(SC_ADD, &swr) < 0) { 489 switch (errno) { 490 case (ENOSYS): 491 (void) fprintf(stderr, gettext( 492 "%s: Invalid operation for this filesystem type\n"), 493 path); 494 break; 495 case (EEXIST): 496 (void) fprintf(stderr, gettext( 497 "%s: Overlapping swap files are not allowed\n"), 498 path); 499 break; 500 default: 501 perror(path); 502 break; 503 } 504 return (2); 505 } 506 507 /* 508 * If the swapctl worked and we don't have a dump device, and /etc 509 * is part of a writeable filesystem, then run dumpadm -ud swap. 510 * If /etc (presumably part of /) is still mounted read-only, then 511 * dumpadm will fail to write its config file, so there's no point 512 * running it now. This also avoids spurious messages during boot 513 * when the first swapadd takes place, at which point / is still ro. 514 * Similarly, if swapadd invoked us with -1 or -2 (but root is 515 * writeable), we don't want to modify the dump device because 516 * /etc/init.d/savecore has yet to execute; if we run dumpadm now 517 * we would lose the user's previous setting. 518 */ 519 if (!have_dumpdev && !(flags & (P1FLAG | P2FLAG)) && 520 statvfs("/etc", &fsb) == 0 && !(fsb.f_flag & ST_RDONLY)) { 521 522 (void) printf( 523 gettext("operating system crash dump was previously " 524 "disabled --\ninvoking dumpadm(1M) -d swap to select " 525 "new dump device\n")); 526 527 if (system("/usr/sbin/dumpadm -ud swap") == -1) 528 dumpadm_err(gettext( 529 "Warning: failed to execute dumpadm -d swap")); 530 } 531 532 return (0); 533 } 534 535 static int 536 valid(char *pathname, off_t offset, off_t length) 537 { 538 struct stat64 f; 539 struct statvfs64 fs; 540 off_t need; 541 542 if (stat64(pathname, &f) < 0 || statvfs64(pathname, &fs) < 0) { 543 (void) perror(pathname); 544 return (errno); 545 } 546 547 if (!((S_ISREG(f.st_mode) && (f.st_mode & S_ISVTX) == S_ISVTX) || 548 S_ISBLK(f.st_mode))) { 549 (void) fprintf(stderr, 550 gettext("\"%s\" is not valid for swapping.\n" 551 "It must be a block device or a regular file with the\n" 552 "\"save user text on execution\" bit set.\n"), 553 pathname); 554 return (EINVAL); 555 } 556 557 if (S_ISREG(f.st_mode)) { 558 if (length == 0) 559 length = (off_t)f.st_size; 560 561 /* 562 * "f.st_blocks < 8" because the first eight 563 * 512-byte sectors are always skipped 564 */ 565 566 if (f.st_size < (length - offset) || f.st_size == 0 || 567 f.st_size > MAXOFF_T || f.st_blocks < 8 || length < 0) { 568 (void) fprintf(stderr, gettext("%s: size is invalid\n"), 569 pathname); 570 return (EINVAL); 571 } 572 573 if (offset < 0) { 574 (void) fprintf(stderr, 575 gettext("%s: low block is invalid\n"), 576 pathname); 577 return (EINVAL); 578 } 579 580 need = roundup(length, fs.f_bsize) / DEV_BSIZE; 581 582 /* 583 * "need > f.st_blocks" to account for indirect blocks 584 * Note: 585 * This can be fooled by a file large enough to 586 * contain indirect blocks that also contains holes. 587 * However, we don't know (and don't want to know) 588 * about the underlying storage implementation. 589 * But, if it doesn't have at least this many blocks, 590 * there must be a hole. 591 */ 592 593 if (need > f.st_blocks) { 594 (void) fprintf(stderr, gettext( 595 "\"%s\" may contain holes - can't swap on it.\n"), 596 pathname); 597 return (EINVAL); 598 } 599 } 600 /* 601 * else, we cannot get st_size for S_ISBLK device and 602 * no meaningful checking can be done. 603 */ 604 605 return (0); 606 } 607