4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
30 * University Copyright- Copyright (c) 1982, 1986, 1988
31 * The Regents of the University of California
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
40 * Copyright 2017 Jason King.
44 * Swap administrative interface
45 * Used to add/delete/list swap devices.
48 #include <sys/types.h>
49 #include <sys/dumpadm.h>
55 #include <sys/param.h>
58 #include <sys/sysmacros.h>
59 #include <sys/mkdev.h>
61 #include <sys/statvfs.h>
62 #include <sys/uadmin.h>
67 #include <libdiskmgt.h>
68 #include <sys/fs/zfs.h>
69 #include <libcmdutils.h>
70 #include <sys/debug.h>
72 #define LFLAG 0x01 /* swap -l (list swap devices) */
73 #define DFLAG 0x02 /* swap -d (delete swap device) */
74 #define AFLAG 0x04 /* swap -a (add swap device) */
75 #define SFLAG 0x08 /* swap -s (swap info summary) */
76 #define P1FLAG 0x10 /* swap -1 (swapadd pass1; do not modify dump device) */
77 #define P2FLAG 0x20 /* swap -2 (swapadd pass2; do not modify dump device) */
78 #define HFLAG 0x40 /* swap -h (size in human readable format) */
79 #define KFLAG 0x80 /* swap -k (size in kilobytes) */
81 #define NUMBER_WIDTH 64
82 /* make sure nicenum_scale() has enough space */
83 CTASSERT(NUMBER_WIDTH
>= NN_NUMBUF_SZ
);
84 typedef char numbuf_t
[NUMBER_WIDTH
];
86 static char *prognamep
;
88 static int add(char *, off_t
, off_t
, int);
89 static int delete(char *, off_t
);
90 static void usage(void);
91 static int doswap(int flag
);
92 static int valid(char *, off_t
, off_t
);
93 static int list(int flag
);
96 main(int argc
, char **argv
)
106 (void) setlocale(LC_ALL
, "");
108 #if !defined(TEXT_DOMAIN)
109 #define TEXT_DOMAIN "SYS_TEST"
111 (void) textdomain(TEXT_DOMAIN
);
119 while ((c
= getopt(argc
, argv
, "khlsd:a:12")) != EOF
) {
122 case 'l': /* list all the swap devices */
130 * The argument for starting offset is optional.
131 * If no argument is specified, the entire swap file
132 * is added although this will fail if a non-zero
133 * starting offset was specified when added.
135 if ((argc
- optind
) > 1 || flag
!= 0) {
143 s_offset
= strtol(argv
[optind
++], &char_p
, 10);
144 if (errno
!= 0 || *char_p
!= '\0') {
145 (void) fprintf(stderr
,
146 gettext("error in [low block]\n"));
150 ret
= delete(pathname
, s_offset
);
155 * The arguments for starting offset and number of
156 * blocks are optional. If only the starting offset
157 * is specified, all the blocks to the end of the swap
158 * file will be added. If no starting offset is
159 * specified, the entire swap file is assumed.
161 if ((argc
- optind
) > 2 ||
162 (flag
& ~(P1FLAG
| P2FLAG
)) != 0) {
166 if (*optarg
!= '/') {
167 (void) fprintf(stderr
,
168 gettext("%s: path must be absolute\n"),
176 s_offset
= strtol(argv
[optind
++], &char_p
, 10);
177 if (errno
!= 0 || *char_p
!= '\0') {
178 (void) fprintf(stderr
,
179 gettext("error in [low block]\n"));
185 length
= strtol(argv
[optind
++], &char_p
, 10);
186 if (errno
!= 0 || *char_p
!= '\0') {
187 (void) fprintf(stderr
,
188 gettext("error in [nbr of blocks]\n"));
216 if (flag
& ~SFLAG
& ~HFLAG
) {
218 * The only option that can be used with -s is -h.
229 if (flag
& ~KFLAG
& ~HFLAG
& ~LFLAG
) {
237 * do the add here. Check for in use prior to add.
238 * The values for length and offset are set above.
242 * If device is in use for a swap device, print message
245 if (dm_inuse(pathname
, &msg
, DM_WHO_SWAP
, &error
) ||
248 (void) fprintf(stderr
, gettext("Error occurred"
249 " with device in use checking: %s\n"),
252 (void) fprintf(stderr
, "%s", msg
);
257 if ((ret
= valid(pathname
,
258 s_offset
* 512, length
* 512)) == 0) {
259 ret
= add(pathname
, s_offset
, length
, flag
);
262 if (!(flag
& ~HFLAG
& ~KFLAG
)) {
263 /* only -h and/or -k flag, or no flag */
274 (void) fprintf(stderr
, gettext("Usage:\t%s -l\n"), prognamep
);
275 (void) fprintf(stderr
, gettext("\tsub option :\n"));
276 (void) fprintf(stderr
, gettext("\t\t-h : displays size in human "
277 "readable format\n"));
278 (void) fprintf(stderr
, gettext("\t\t-k : displays size in KB\n"));
279 (void) fprintf(stderr
, "\t%s -s\n", prognamep
);
280 (void) fprintf(stderr
, gettext("\tsub option :\n"));
281 (void) fprintf(stderr
, gettext("\t\t-h : displays size in human "
282 "readable format rather than KB\n"));
283 (void) fprintf(stderr
, gettext("\t%s -d <file name> [low block]\n"),
285 (void) fprintf(stderr
, gettext("\t%s -a <file name> [low block]"
286 " [nbr of blocks]\n"), prognamep
);
291 * #define ctok(x) ((ctob(x))>>10)
292 * in a machine independent way. (Both assume a click > 1k)
297 static int factor
= -1;
300 factor
= (int)(sysconf(_SC_PAGESIZE
) >> 10);
301 return ((size_t)(clicks
* factor
));
309 pgcnt_t allocated
, reserved
, available
;
313 * max = total amount of swap space including physical memory
314 * ai.ani_max = MAX(anoninfo.ani_resv, anoninfo.ani_max) +
315 * availrmem - swapfs_minfree;
316 * ai.ani_free = amount of unallocated anonymous memory
317 * (ie. = resverved_unallocated + unreserved)
318 * ai.ani_free = anoninfo.ani_free + (availrmem - swapfs_minfree);
319 * ai.ani_resv = total amount of reserved anonymous memory
320 * ai.ani_resv = anoninfo.ani_resv;
322 * allocated = anon memory not free
323 * reserved = anon memory reserved but not allocated
324 * available = anon memory not reserved
326 if (swapctl(SC_AINFO
, &ai
) == -1) {
331 allocated
= ai
.ani_max
- ai
.ani_free
;
332 reserved
= ai
.ani_resv
- allocated
;
333 available
= ai
.ani_max
- ai
.ani_resv
;
337 * Translations (if any) of these keywords should match with
338 * translations (if any) of the swap.8 man page keywords for
339 * -s option: "allocated", "reserved", "used", "available"
343 int factor
= (int)(sysconf(_SC_PAGESIZE
));
345 nicenum_scale(allocated
, factor
, numbuf
, sizeof (numbuf
), 0);
346 (void) printf(gettext("total: %s allocated + "), numbuf
);
348 nicenum_scale(reserved
, factor
, numbuf
, sizeof (numbuf
), 0);
349 (void) printf(gettext("%s reserved = "), numbuf
);
351 nicenum_scale(allocated
+ reserved
, factor
, numbuf
,
353 (void) printf(gettext("%s used, "), numbuf
);
355 nicenum_scale(available
, factor
, numbuf
, sizeof (numbuf
), 0);
356 (void) printf(gettext("%s available\n"), numbuf
);
358 (void) printf(gettext("total: %luk bytes allocated + %luk"
359 " reserved = %luk used, %luk available\n"),
360 ctok(allocated
), ctok(reserved
),
361 ctok(reserved
) + ctok(allocated
),
371 struct swaptable
*st
;
372 struct swapent
*swapent
;
376 char fullpath
[MAXPATHLEN
+1];
380 if ((num
= swapctl(SC_GETNSWP
, NULL
)) == -1) {
385 (void) fprintf(stderr
, gettext("No swap devices configured\n"));
389 if ((st
= malloc(num
* sizeof (swapent_t
) + sizeof (int)))
391 (void) fprintf(stderr
,
392 gettext("Malloc failed. Please try later.\n"));
396 if ((path
= malloc(num
* MAXPATHLEN
)) == NULL
) {
397 (void) fprintf(stderr
,
398 gettext("Malloc failed. Please try later.\n"));
402 swapent
= st
->swt_ent
;
403 for (i
= 0; i
< num
; i
++, swapent
++) {
404 swapent
->ste_path
= path
;
409 if ((num
= swapctl(SC_LIST
, st
)) == -1) {
416 * Following translations for "swap -l" should account for for
417 * alignment of header and output.
418 * The first translation is for the header. If the alignment
419 * of the header changes, change the next 5 formats as needed
420 * to make alignment of output agree with alignment of the header.
421 * The next four translations are four cases for printing the
423 * The next translation is for printing the 3rd, 4th & 5th fields.
425 * Translations (if any) of the following keywords should match the
426 * translations (if any) of the swap.8 man page keywords for
427 * -l option: "swapfile", "dev", "swaplo", "blocks", "free"
430 gettext("swapfile dev swaplo blocks free\n"));
432 swapent
= st
->swt_ent
;
433 for (i
= 0; i
< num
; i
++, swapent
++) {
434 if (*swapent
->ste_path
!= '/')
435 (void) snprintf(fullpath
, sizeof (fullpath
),
436 "/dev/%s", swapent
->ste_path
);
438 (void) snprintf(fullpath
, sizeof (fullpath
),
439 "%s", swapent
->ste_path
);
440 if (stat(fullpath
, &statbuf
) < 0)
441 if (*swapent
->ste_path
!= '/')
442 (void) printf(gettext("%-20s - "),
445 (void) printf(gettext("%-20s ?,? "),
448 if (S_ISBLK(statbuf
.st_mode
) ||
449 S_ISCHR(statbuf
.st_mode
)) {
450 (void) printf(gettext("%-19s %2lu,%-2lu"),
452 major(statbuf
.st_rdev
),
453 minor(statbuf
.st_rdev
));
455 (void) printf(gettext("%-20s - "), fullpath
);
459 int diskblks_per_page
=
460 (int)(sysconf(_SC_PAGESIZE
) >> DEV_BSHIFT
);
462 nicenum_scale(swapent
->ste_start
, DEV_BSIZE
, numbuf
,
464 (void) printf(gettext(" %8s"), numbuf
);
466 nicenum_scale(swapent
->ste_pages
, DEV_BSIZE
*
467 diskblks_per_page
, numbuf
, sizeof (numbuf
), 0);
468 (void) printf(gettext(" %8s"), numbuf
);
470 nicenum_scale(swapent
->ste_free
, DEV_BSIZE
*
471 diskblks_per_page
, numbuf
, sizeof (numbuf
), 0);
472 (void) printf(gettext(" %8s"), numbuf
);
473 } else if (flag
& KFLAG
) {
474 (void) printf(gettext(" %7luK %7luK %7luK"),
475 swapent
->ste_start
* DEV_BSIZE
/ 1024,
476 swapent
->ste_pages
* diskblks_per_page
*
478 swapent
->ste_free
* diskblks_per_page
*
481 (void) printf(gettext(" %8lu %8lu %8lu"),
483 swapent
->ste_pages
* diskblks_per_page
,
484 swapent
->ste_free
* diskblks_per_page
);
487 if (swapent
->ste_flags
& ST_INDEL
)
488 (void) printf(" INDEL\n");
496 dumpadm_err(const char *warning
)
498 (void) fprintf(stderr
, "%s (%s):\n", warning
, strerror(errno
));
499 (void) fprintf(stderr
, gettext(
500 "run dumpadm(8) to verify dump configuration\n"));
504 delete(char *path
, off_t offset
)
510 swr
.sr_start
= offset
;
512 if (swapctl(SC_REMOVE
, &swr
) < 0) {
515 (void) fprintf(stderr
, gettext(
516 "%s: Invalid operation for this filesystem type\n"),
527 * If our swap -d succeeded, open up /dev/dump and ask what the dump
528 * device is set to. If this returns ENODEV, we just deleted the
529 * dump device, so try to change the dump device to another swap
530 * device. We do this by firing up /usr/sbin/dumpadm -ud swap.
532 if ((fd
= open("/dev/dump", O_RDONLY
)) >= 0) {
533 char dumpdev
[MAXPATHLEN
];
535 if (ioctl(fd
, DIOCGETDEV
, dumpdev
) == -1) {
536 if (errno
== ENODEV
) {
537 (void) printf(gettext("%s was dump device --\n"
538 "invoking dumpadm(8) -d swap to "
539 "select new dump device\n"), path
);
541 * Close /dev/dump prior to executing dumpadm
542 * since /dev/dump mandates exclusive open.
546 if (system("/usr/sbin/dumpadm -ud swap") == -1)
548 "Warning: failed to execute dumpadm -d swap"));
551 "Warning: failed to check dump device"));
555 dumpadm_err(gettext("Warning: failed to open /dev/dump"));
561 * swapres_t structure units are in 512-blocks
564 add(char *path
, off_t offset
, off_t cnt
, int flags
)
568 int fd
, have_dumpdev
= 1;
572 * Before adding swap, we first check to see if we have a dump
573 * device configured. If we don't (errno == ENODEV), and if
574 * our SC_ADD is successful, then run /usr/sbin/dumpadm -ud swap
575 * to attempt to reconfigure the dump device to the new swap.
577 if ((fd
= open("/dev/dump", O_RDONLY
)) >= 0) {
578 char dumpdev
[MAXPATHLEN
];
580 if (ioctl(fd
, DIOCGETDEV
, dumpdev
) == -1) {
585 "Warning: failed to check dump device"));
591 * zvols cannot act as both a swap device and dump device.
593 if (strncmp(dumpdev
, ZVOL_FULL_DEV_DIR
,
594 strlen(ZVOL_FULL_DEV_DIR
)) == 0) {
595 if (strcmp(dumpdev
, path
) == 0) {
596 (void) fprintf(stderr
, gettext("%s: zvol "
597 "cannot be used as a swap device and a "
598 "dump device\n"), path
);
603 } else if (!(flags
& P1FLAG
))
604 dumpadm_err(gettext("Warning: failed to open /dev/dump"));
607 swr
.sr_start
= offset
;
610 if (swapctl(SC_ADD
, &swr
) < 0) {
613 (void) fprintf(stderr
, gettext(
614 "%s: Invalid operation for this filesystem type\n"),
618 (void) fprintf(stderr
, gettext(
619 "%s: Overlapping swap files are not allowed\n"),
630 * If the swapctl worked and we don't have a dump device, and /etc
631 * is part of a writeable filesystem, then run dumpadm -ud swap.
632 * If /etc (presumably part of /) is still mounted read-only, then
633 * dumpadm will fail to write its config file, so there's no point
634 * running it now. This also avoids spurious messages during boot
635 * when the first swapadd takes place, at which point / is still ro.
636 * Similarly, if swapadd invoked us with -1 or -2 (but root is
637 * writeable), we don't want to modify the dump device because
638 * /etc/init.d/savecore has yet to execute; if we run dumpadm now
639 * we would lose the user's previous setting.
641 if (!have_dumpdev
&& !(flags
& (P1FLAG
| P2FLAG
)) &&
642 statvfs("/etc", &fsb
) == 0 && !(fsb
.f_flag
& ST_RDONLY
)) {
645 gettext("operating system crash dump was previously "
646 "disabled --\ninvoking dumpadm(8) -d swap to select "
647 "new dump device\n"));
649 if (system("/usr/sbin/dumpadm -ud swap") == -1)
651 "Warning: failed to execute dumpadm -d swap"));
658 valid(char *pathname
, off_t offset
, off_t length
)
664 if (stat(pathname
, &f
) < 0 || statvfs(pathname
, &fs
) < 0) {
665 (void) perror(pathname
);
669 if (!((S_ISREG(f
.st_mode
) && (f
.st_mode
& S_ISVTX
) == S_ISVTX
) ||
670 S_ISBLK(f
.st_mode
))) {
671 (void) fprintf(stderr
,
672 gettext("\"%s\" is not valid for swapping.\n"
673 "It must be a block device or a regular file with the\n"
674 "\"save user text on execution\" bit set.\n"),
679 if (S_ISREG(f
.st_mode
)) {
681 length
= (off_t
)f
.st_size
;
684 * "f.st_blocks < 8" because the first eight
685 * 512-byte sectors are always skipped
688 if (f
.st_size
< (length
- offset
) || f
.st_size
== 0 ||
689 f
.st_size
> MAXOFF_T
|| f
.st_blocks
< 8 || length
< 0) {
690 (void) fprintf(stderr
, gettext("%s: size is invalid\n"),
696 (void) fprintf(stderr
,
697 gettext("%s: low block is invalid\n"),
702 need
= roundup(length
, fs
.f_bsize
) / DEV_BSIZE
;
705 * "need > f.st_blocks" to account for indirect blocks
707 * This can be fooled by a file large enough to
708 * contain indirect blocks that also contains holes.
709 * However, we don't know (and don't want to know)
710 * about the underlying storage implementation.
711 * But, if it doesn't have at least this many blocks,
712 * there must be a hole.
715 if (need
> f
.st_blocks
) {
716 (void) fprintf(stderr
, gettext(
717 "\"%s\" may contain holes - can't swap on it.\n"),
723 * else, we cannot get st_size for S_ISBLK device and
724 * no meaningful checking can be done.