Correct BSD License clause numbering from 1-2-4 to 1-2-3.
[dragonfly.git] / sbin / swapon / swapon.c
1 /*
2  * Copyright (c) 1980, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#) Copyright (c) 1980, 1993 The Regents of the University of California.  All rights reserved.
30  * @(#)swapon.c 8.1 (Berkeley) 6/5/93
31  * $FreeBSD: src/sbin/swapon/swapon.c,v 1.8.2.2 2001/07/30 10:30:11 dd Exp $
32  */
33
34 #include <sys/param.h>
35 #include <sys/stat.h>
36 #include <sys/sysctl.h>
37 #include <sys/diskslice.h>
38 #include <sys/ioctl_compat.h>
39 #include <vm/vm_param.h>
40
41 #include <err.h>
42 #include <errno.h>
43 #include <fstab.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <fcntl.h>
49 #include <libutil.h>
50
51 static void usage(void);
52 static int swap_on_off(char *name, int doingall, int trim);
53 static void swaplist(int lflag, int sflag, int hflag);
54
55 enum { SWAPON, SWAPOFF, SWAPCTL } orig_prog, which_prog = SWAPCTL;
56
57 int
58 main(int argc, char **argv)
59 {
60         struct fstab *fsp;
61         char *ptr;
62         int ret;
63         int ch;
64         int doall, sflag, lflag, hflag, qflag, eflag;
65
66         if ((ptr = strrchr(argv[0], '/')) == NULL)
67                 ptr = argv[0];
68         if (strstr(ptr, "swapon"))
69                 which_prog = SWAPON;
70         else if (strstr(ptr, "swapoff"))
71                 which_prog = SWAPOFF;
72         orig_prog = which_prog;
73
74         sflag = lflag = hflag = qflag = doall = eflag = 0;
75         while ((ch = getopt(argc, argv, "AadeghklmqsU")) != -1) {
76                 switch((char)ch) {
77                 case 'A':
78                         if (which_prog == SWAPCTL) {
79                                 doall = 1;
80                                 which_prog = SWAPON;
81                         } else {
82                                 usage();
83                         }
84                         break;
85                 case 'a':
86                         if (which_prog == SWAPON || which_prog == SWAPOFF)
87                                 doall = 1;
88                         else
89                                 which_prog = SWAPON;
90                         break;
91                 case 'd':
92                         if (which_prog == SWAPCTL)
93                                 which_prog = SWAPOFF;
94                         else
95                                 usage();
96                         break;
97                 case 'e':
98                         eflag = 1;
99                         break;
100                 case 'g':
101                         hflag = 'G';
102                         break;
103                 case 'h':
104                         hflag = 'H';
105                         break;
106                 case 'k':
107                         hflag = 'K';
108                         break;
109                 case 'l':
110                         lflag = 1;
111                         break;
112                 case 'm':
113                         hflag = 'M';
114                         break;
115                 case 'q':
116                         if (which_prog == SWAPON || which_prog == SWAPOFF)
117                                 qflag = 1;
118                         break;
119                 case 's':
120                         sflag = 1;
121                         break;
122                 case 'U':
123                         if (which_prog == SWAPCTL) {
124                                 doall = 1;
125                                 which_prog = SWAPOFF;
126                         } else {
127                                 usage();
128                         }
129                         break;
130                 case '?':
131                 default:
132                         usage();
133                 }
134         }
135         argv += optind;
136
137         ret = 0;
138         if (which_prog == SWAPON || which_prog == SWAPOFF) {
139                 if (doall) {
140                         while ((fsp = getfsent()) != NULL) {
141                                 if (strcmp(fsp->fs_type, FSTAB_SW))
142                                         continue;
143                                 if (strstr(fsp->fs_mntops, "noauto"))
144                                         continue;
145                                 if (swap_on_off(fsp->fs_spec, 1, eflag)) {
146                                         ret = 1;
147                                 } else {
148                                         if (!qflag) {
149                                                 printf("%s: %sing %s as swap device\n",
150                                                     getprogname(),
151                                                     which_prog == SWAPOFF ? "remov" : "add",
152                                                     fsp->fs_spec);
153                                         }
154                                 }
155                         }
156                 } else if (*argv == NULL) {
157                         usage();
158                 }
159                 for (; *argv; ++argv) {
160                         if (swap_on_off(getdevpath(*argv, 0), 0, eflag)) {
161                                 ret = 1;
162                         } else if (orig_prog == SWAPCTL) {
163                                 printf("%s: %sing %s as swap device\n",
164                                     getprogname(),
165                                     which_prog == SWAPOFF ? "remov" : "add",
166                                     *argv);
167                         }
168                 }
169         } else {
170                 if (lflag || sflag)
171                         swaplist(lflag, sflag, hflag);
172                 else
173                         usage();
174         }
175         exit(ret);
176 }
177
178 /*
179  * TRIM the device
180  */
181 static
182 void
183 trim_volume(char * name)
184 {       
185         struct partinfo pinfo;
186         int fd,i,n;
187         size_t bytes = 0,ksize;
188         char *xswbuf;
189         struct xswdev *xsw;
190
191
192         /*
193         * Determine if this device is already being used by swap without 
194         * calling swapon(). 
195         */
196         if ((sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0) ||
197             bytes == 0) {
198                 err(1, "sysctlbyname()");
199         }
200
201         xswbuf = malloc(bytes);
202         if ((sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) ||
203             bytes == 0) {
204                         free(xswbuf);
205                         err(1, "sysctlbyname()");
206         }
207
208         ksize = ((struct xswdev *)xswbuf)->xsw_size;
209         n = (int)(bytes / ksize);
210         for (i = 0; i < n; ++i) {
211                 xsw = (void *)((char *)xswbuf + i * ksize);
212
213                 if (xsw->xsw_dev == NODEV )
214                         continue;
215                 if(!strcmp(devname(xsw->xsw_dev, S_IFCHR),
216                     name + strlen("/dev/"))) {
217                         warnx("%s: device already a swap device", name);
218                         exit(1);
219                 }
220         }
221         
222         /*
223          * Get the size and offset of this parititon/device
224          */
225         fd = open(name, O_RDWR);
226         if (fd < 0)
227                 err(1, "Unable to open %s R+W", name);
228         if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
229                 printf("Cannot trim regular file\n");
230                 usage ();
231         }
232         off_t ioarg[2];
233         
234         /*Trim the Device*/     
235         ioarg[0] = pinfo.media_offset;
236         ioarg[1] = pinfo.media_size;
237         printf("Trimming Device:%s, sectors (%llu -%llu)\n",name,
238              (unsigned long long)ioarg[0]/512,
239              (unsigned long long)ioarg[1]/512);
240         if (ioctl(fd, IOCTLTRIM, ioarg) < 0) {
241                 printf("Device trim failed\n");
242                 usage ();
243         }
244         close(fd);
245 }
246
247 static int
248 swap_on_off(char *name, int doingall, int trim)
249 {
250         if (which_prog == SWAPON && trim){
251                 char sysctl_name[64];
252                 int trim_enabled = 0;
253                 size_t olen = sizeof(trim_enabled);
254                 char *dev_name = strdup(name);
255                 dev_name = strtok(dev_name + strlen("/dev/da"),"s");
256                 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", dev_name);
257                 sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
258                 if(errno == ENOENT) {
259                         printf("Device:%s does not support the TRIM command\n",
260                             name);
261                         usage();
262                 }
263                 if(!trim_enabled) {
264                         printf("Erase device option selected, but sysctl (%s) "
265                             "is not enabled\n",sysctl_name);
266                         usage();
267                 }
268
269                 trim_volume(name);
270
271         }
272         if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
273                 switch(errno) {
274                 case EBUSY:
275                         if (!doingall)
276                                 warnx("%s: device already in use", name);
277                         break;
278                 case EINVAL:
279                         if (which_prog == SWAPON)
280                                 warnx("%s: NSWAPDEV limit reached", name);
281                         else if (!doingall)
282                                 warn("%s", name);
283                         break;
284                 default:
285                         warn("%s", name);
286                         break;
287                 }
288                 return(1);
289         }
290         return(0);
291 }
292
293 static void
294 usage(void)
295 {
296         fprintf(stderr, "usage: %s ", getprogname());
297         switch (orig_prog) {
298         case SWAPON:
299         case SWAPOFF:
300                 fprintf(stderr, "-aeq | file ...\n");
301                 break;
302         case SWAPCTL:
303                 fprintf(stderr, "[-AeghklmsU] [-a file ... | -d file ...]\n");
304                 break;
305         }
306         exit(1);
307 }
308
309 static void
310 sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen,
311     long blocksize)
312 {
313         if (hflag == 'H') {
314                 char tmp[16];
315
316                 humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE,
317                     HN_B | HN_NOSPACE | HN_DECIMAL);
318                 snprintf(buf, bufsize, "%*s", hlen, tmp);
319         } else {
320                 snprintf(buf, bufsize, "%*lld", hlen, val / blocksize);
321         }
322 }
323
324 static void
325 swaplist(int lflag, int sflag, int hflag)
326 {
327         size_t ksize, bytes = 0;
328         char *xswbuf;
329         struct xswdev *xsw;
330         int hlen, pagesize;
331         int i, n;
332         long blocksize;
333         long long total, used, tmp_total, tmp_used;
334         char buf[32];
335
336         pagesize = getpagesize();
337         switch(hflag) {
338         case 'G':
339                 blocksize = 1024 * 1024 * 1024;
340                 strlcpy(buf, "1GB-blocks", sizeof(buf));
341                 hlen = 10;
342                 break;
343         case 'H':
344                 blocksize = -1;
345                 strlcpy(buf, "Bytes", sizeof(buf));
346                 hlen = 10;
347                 break;
348         case 'K':
349                 blocksize = 1024;
350                 strlcpy(buf, "1kB-blocks", sizeof(buf));
351                 hlen = 10;
352                 break;
353         case 'M':
354                 blocksize = 1024 * 1024;
355                 strlcpy(buf, "1MB-blocks", sizeof(buf));
356                 hlen = 10;
357                 break;
358         default:
359                 getbsize(&hlen, &blocksize);
360                 snprintf(buf, sizeof(buf), "%ld-blocks", blocksize);
361                 break;
362         }
363
364         if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
365                 err(1, "sysctlbyname()");
366         if (bytes == 0)
367                 err(1, "sysctlbyname()");
368
369         xswbuf = malloc(bytes);
370         if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
371                 free(xswbuf);
372                 err(1, "sysctlbyname()");
373         }
374         if (bytes == 0) {
375                 free(xswbuf);
376                 err(1, "sysctlbyname()");
377         }
378
379         /*
380          * Calculate size of xsw entry returned by kernel (it can be larger
381          * than the one we have if there is a version mismatch).
382          */
383         ksize = ((struct xswdev *)xswbuf)->xsw_size;
384         n = (int)(bytes / ksize);
385
386         if (lflag) {
387                 printf("%-13s %*s %*s\n",
388                     "Device:",
389                     hlen, buf,
390                     hlen, "Used:");
391         }
392
393         total = used = tmp_total = tmp_used = 0;
394         for (i = 0; i < n; ++i) {
395                 xsw = (void *)((char *)xswbuf + i * ksize);
396
397                 if (xsw->xsw_nblks == 0)
398                         continue;
399
400                 tmp_total = (long long)xsw->xsw_nblks * pagesize;
401                 tmp_used = (long long)xsw->xsw_used * pagesize;
402                 total += tmp_total;
403                 used += tmp_used;
404                 if (lflag) {
405                         sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen,
406                             blocksize);
407                         if (xsw->xsw_dev == NODEV) {
408                                 printf("%-13s %s ", "[NFS swap]", buf);
409                         } else {
410                                 printf("/dev/%-8s %s ",
411                                     devname(xsw->xsw_dev, S_IFCHR), buf);
412                         }
413
414                         sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen,
415                             blocksize);
416                         printf("%s\n", buf);
417                 }
418         }
419
420         if (sflag) {
421                 sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize);
422                 printf("Total:        %s ", buf);
423                 sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize);
424                 printf("%s\n", buf);
425         }
426 }