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 (S_ISBLK(statbuf.st_mode) || 390 S_ISCHR(statbuf.st_mode)) { 391 (void) printf(gettext("%-19s %2lu,%-2lu"), 392 fullpath, 393 major(statbuf.st_rdev), 394 minor(statbuf.st_rdev)); 395 } else { 396 (void) printf(gettext("%-20s - "), fullpath); 397 } 398 } 399 { 400 int diskblks_per_page = 401 (int)(sysconf(_SC_PAGESIZE) >> DEV_BSHIFT); 402 (void) printf(gettext(" %6lu %6lu %6lu"), swapent->ste_start, 403 swapent->ste_pages * diskblks_per_page, 404 swapent->ste_free * diskblks_per_page); 405 } 406 if (swapent->ste_flags & ST_INDEL) 407 (void) printf(" INDEL\n"); 408 else 409 (void) printf("\n"); 410 } 411 return (0); 412 } 413 414 static void 415 dumpadm_err(const char *warning) 416 { 417 (void) fprintf(stderr, "%s (%s):\n", warning, strerror(errno)); 418 (void) fprintf(stderr, gettext( 419 "run dumpadm(1M) to verify dump configuration\n")); 420 } 421 422 static int 423 delete(char *path, off_t offset) 424 { 425 swapres_t swr; 426 int fd; 427 428 swr.sr_name = path; 429 swr.sr_start = offset; 430 431 if (swapctl(SC_REMOVE, &swr) < 0) { 432 switch (errno) { 433 case (ENOSYS): 434 (void) fprintf(stderr, gettext( 435 "%s: Invalid operation for this filesystem type\n"), 436 path); 437 break; 438 default: 439 perror(path); 440 break; 441 } 442 return (2); 443 } 444 445 /* 446 * If our swap -d succeeded, open up /dev/dump and ask what the dump 447 * device is set to. If this returns ENODEV, we just deleted the 448 * dump device, so try to change the dump device to another swap 449 * device. We do this by firing up /usr/sbin/dumpadm -ud swap. 450 */ 451 if ((fd = open("/dev/dump", O_RDONLY)) >= 0) { 452 char dumpdev[MAXPATHLEN]; 453 454 if (ioctl(fd, DIOCGETDEV, dumpdev) == -1) { 455 if (errno == ENODEV) { 456 (void) printf(gettext("%s was dump device --\n" 457 "invoking dumpadm(1M) -d swap to " 458 "select new dump device\n"), path); 459 /* 460 * Close /dev/dump prior to executing dumpadm 461 * since /dev/dump mandates exclusive open. 462 */ 463 (void) close(fd); 464 465 if (system("/usr/sbin/dumpadm -ud swap") == -1) 466 dumpadm_err(gettext( 467 "Warning: failed to execute dumpadm -d swap")); 468 } else 469 dumpadm_err(gettext( 470 "Warning: failed to check dump device")); 471 } 472 (void) close(fd); 473 } else 474 dumpadm_err(gettext("Warning: failed to open /dev/dump")); 475 476 return (0); 477 } 478 479 /* 480 * swapres_t structure units are in 512-blocks 481 */ 482 static int 483 add(char *path, off_t offset, off_t cnt, int flags) 484 { 485 swapres_t swr; 486 487 int fd, have_dumpdev = 1; 488 struct statvfs fsb; 489 490 /* 491 * Before adding swap, we first check to see if we have a dump 492 * device configured. If we don't (errno == ENODEV), and if 493 * our SC_ADD is successful, then run /usr/sbin/dumpadm -ud swap 494 * to attempt to reconfigure the dump device to the new swap. 495 */ 496 if ((fd = open("/dev/dump", O_RDONLY)) >= 0) { 497 char dumpdev[MAXPATHLEN]; 498 499 if (ioctl(fd, DIOCGETDEV, dumpdev) == -1) { 500 if (errno == ENODEV) 501 have_dumpdev = 0; 502 else 503 dumpadm_err(gettext( 504 "Warning: failed to check dump device")); 505 } 506 507 (void) close(fd); 508 509 } else if (!(flags & P1FLAG)) 510 dumpadm_err(gettext("Warning: failed to open /dev/dump")); 511 512 swr.sr_name = path; 513 swr.sr_start = offset; 514 swr.sr_length = cnt; 515 516 if (swapctl(SC_ADD, &swr) < 0) { 517 switch (errno) { 518 case (ENOSYS): 519 (void) fprintf(stderr, gettext( 520 "%s: Invalid operation for this filesystem type\n"), 521 path); 522 break; 523 case (EEXIST): 524 (void) fprintf(stderr, gettext( 525 "%s: Overlapping swap files are not allowed\n"), 526 path); 527 break; 528 default: 529 perror(path); 530 break; 531 } 532 return (2); 533 } 534 535 /* 536 * If the swapctl worked and we don't have a dump device, and /etc 537 * is part of a writeable filesystem, then run dumpadm -ud swap. 538 * If /etc (presumably part of /) is still mounted read-only, then 539 * dumpadm will fail to write its config file, so there's no point 540 * running it now. This also avoids spurious messages during boot 541 * when the first swapadd takes place, at which point / is still ro. 542 * Similarly, if swapadd invoked us with -1 or -2 (but root is 543 * writeable), we don't want to modify the dump device because 544 * /etc/init.d/savecore has yet to execute; if we run dumpadm now 545 * we would lose the user's previous setting. 546 */ 547 if (!have_dumpdev && !(flags & (P1FLAG | P2FLAG)) && 548 statvfs("/etc", &fsb) == 0 && !(fsb.f_flag & ST_RDONLY)) { 549 550 (void) printf( 551 gettext("operating system crash dump was previously " 552 "disabled --\ninvoking dumpadm(1M) -d swap to select " 553 "new dump device\n")); 554 555 if (system("/usr/sbin/dumpadm -ud swap") == -1) 556 dumpadm_err(gettext( 557 "Warning: failed to execute dumpadm -d swap")); 558 } 559 560 return (0); 561 } 562 563 static int 564 valid(char *pathname, off_t offset, off_t length) 565 { 566 struct stat64 f; 567 struct statvfs64 fs; 568 off_t need; 569 570 if (stat64(pathname, &f) < 0 || statvfs64(pathname, &fs) < 0) { 571 (void) perror(pathname); 572 return (errno); 573 } 574 575 if (!((S_ISREG(f.st_mode) && (f.st_mode & S_ISVTX) == S_ISVTX) || 576 S_ISBLK(f.st_mode))) { 577 (void) fprintf(stderr, 578 gettext("\"%s\" is not valid for swapping.\n" 579 "It must be a block device or a regular file with the\n" 580 "\"save user text on execution\" bit set.\n"), 581 pathname); 582 return (EINVAL); 583 } 584 585 if (S_ISREG(f.st_mode)) { 586 if (length == 0) 587 length = (off_t)f.st_size; 588 589 /* 590 * "f.st_blocks < 8" because the first eight 591 * 512-byte sectors are always skipped 592 */ 593 594 if (f.st_size < (length - offset) || f.st_size == 0 || 595 f.st_size > MAXOFF_T || f.st_blocks < 8 || length < 0) { 596 (void) fprintf(stderr, gettext("%s: size is invalid\n"), 597 pathname); 598 return (EINVAL); 599 } 600 601 if (offset < 0) { 602 (void) fprintf(stderr, 603 gettext("%s: low block is invalid\n"), 604 pathname); 605 return (EINVAL); 606 } 607 608 need = roundup(length, fs.f_bsize) / DEV_BSIZE; 609 610 /* 611 * "need > f.st_blocks" to account for indirect blocks 612 * Note: 613 * This can be fooled by a file large enough to 614 * contain indirect blocks that also contains holes. 615 * However, we don't know (and don't want to know) 616 * about the underlying storage implementation. 617 * But, if it doesn't have at least this many blocks, 618 * there must be a hole. 619 */ 620 621 if (need > f.st_blocks) { 622 (void) fprintf(stderr, gettext( 623 "\"%s\" may contain holes - can't swap on it.\n"), 624 pathname); 625 return (EINVAL); 626 } 627 } 628 /* 629 * else, we cannot get st_size for S_ISBLK device and 630 * no meaningful checking can be done. 631 */ 632 633 return (0); 634 } 635