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