f86c4d895d039d5455339a6ba6e91002330dfbe4
[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, int ask);
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, iflag;
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 = iflag = 0;
75         while ((ch = getopt(argc, argv, "AadeghiklmqsU")) != -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 'i':
107                         iflag = 1;
108                         break;
109                 case 'k':
110                         hflag = 'K';
111                         break;
112                 case 'l':
113                         lflag = 1;
114                         break;
115                 case 'm':
116                         hflag = 'M';
117                         break;
118                 case 'q':
119                         if (which_prog == SWAPON || which_prog == SWAPOFF)
120                                 qflag = 1;
121                         break;
122                 case 's':
123                         sflag = 1;
124                         break;
125                 case 'U':
126                         if (which_prog == SWAPCTL) {
127                                 doall = 1;
128                                 which_prog = SWAPOFF;
129                         } else {
130                                 usage();
131                         }
132                         break;
133                 case '?':
134                 default:
135                         usage();
136                 }
137         }
138         argv += optind;
139
140         ret = 0;
141         if (which_prog == SWAPON || which_prog == SWAPOFF) {
142                 if (doall) {
143                         while ((fsp = getfsent()) != NULL) {
144                                 if (strcmp(fsp->fs_type, FSTAB_SW))
145                                         continue;
146                                 if (strstr(fsp->fs_mntops, "noauto"))
147                                         continue;
148                                 if (swap_on_off(fsp->fs_spec, 1, eflag, iflag)) {
149                                         ret = 1;
150                                 } else {
151                                         if (!qflag) {
152                                                 printf("%s: %sing %s as swap device\n",
153                                                     getprogname(),
154                                                     which_prog == SWAPOFF ? "remov" : "add",
155                                                     fsp->fs_spec);
156                                         }
157                                 }
158                         }
159                 } else if (*argv == NULL) {
160                         usage();
161                 }
162                 for (; *argv; ++argv) {
163                         if (swap_on_off(getdevpath(*argv, 0), 0, eflag, 1)) {
164                                 ret = 1;
165                         } else if (orig_prog == SWAPCTL) {
166                                 printf("%s: %sing %s as swap device\n",
167                                     getprogname(),
168                                     which_prog == SWAPOFF ? "remov" : "add",
169                                     *argv);
170                         }
171                 }
172         } else {
173                 if (lflag || sflag)
174                         swaplist(lflag, sflag, hflag);
175                 else
176                         usage();
177         }
178         exit(ret);
179 }
180
181 /*
182  * TRIM the device
183  */
184 static
185 void
186 trim_volume(char * name)
187 {       
188         struct partinfo pinfo;
189         int fd,i,n;
190         size_t bytes = 0,ksize;
191         char *xswbuf;
192         struct xswdev *xsw;
193
194
195         /*
196         * Determine if this device is already being used by swap without 
197         * calling swapon(). 
198         */
199         if ((sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0) ||
200             bytes == 0) {
201                 err(1, "sysctlbyname()");
202         }
203
204         xswbuf = malloc(bytes);
205         if ((sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) ||
206             bytes == 0) {
207                         free(xswbuf);
208                         err(1, "sysctlbyname()");
209         }
210
211         ksize = ((struct xswdev *)xswbuf)->xsw_size;
212         n = (int)(bytes / ksize);
213         for (i = 0; i < n; ++i) {
214                 xsw = (void *)((char *)xswbuf + i * ksize);
215
216                 if (xsw->xsw_dev == NODEV )
217                         continue;
218                 if(!strcmp(devname(xsw->xsw_dev, S_IFCHR),
219                     name + strlen("/dev/"))) {
220                         warnx("%s: device already a swap device", name);
221                         exit(1);
222                 }
223         }
224         
225         /*
226          * Get the size and offset of this parititon/device
227          */
228         fd = open(name, O_RDWR);
229         if (fd < 0)
230                 err(1, "Unable to open %s R+W", name);
231         if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
232                 printf("Cannot trim regular file\n");
233                 usage ();
234         }
235         off_t ioarg[2];
236         
237         /*Trim the Device*/     
238         ioarg[0] = pinfo.media_offset;
239         ioarg[1] = pinfo.media_size;
240         printf("Trimming Device:%s, sectors (%llu -%llu)\n",name,
241              (unsigned long long)ioarg[0]/512,
242              (unsigned long long)ioarg[1]/512);
243         if (ioctl(fd, IOCTLTRIM, ioarg) < 0) {
244                 printf("Device trim failed\n");
245                 usage ();
246         }
247         close(fd);
248 }
249
250 static int
251 swap_on_off(char *name, int doingall, int trim, int ask)
252 {
253
254         if (ask && which_prog == SWAPON) {
255                 printf("Do you really want to use device %s as a swap device ?\n", name);
256                 printf("You might loose data. [Y/N]");
257
258                 int c = fgetc(stdin);
259                 if (c != 'y' && c != 'Y')
260                         return(1);
261
262         }
263         if (which_prog == SWAPON && trim){
264                 char sysctl_name[64];
265                 int trim_enabled = 0;
266                 size_t olen = sizeof(trim_enabled);
267                 char *dev_name = strdup(name);
268                 dev_name = strtok(dev_name + strlen("/dev/da"),"s");
269                 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", dev_name);
270                 sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
271                 if(errno == ENOENT) {
272                         printf("Device:%s does not support the TRIM command\n",
273                             name);
274                         usage();
275                 }
276                 if(!trim_enabled) {
277                         printf("Erase device option selected, but sysctl (%s) "
278                             "is not enabled\n",sysctl_name);
279                         usage();
280                 }
281
282                 trim_volume(name);
283
284         }
285         if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
286                 switch(errno) {
287                 case EBUSY:
288                         if (!doingall)
289                                 warnx("%s: device already in use", name);
290                         break;
291                 case EINVAL:
292                         if (which_prog == SWAPON)
293                                 warnx("%s: NSWAPDEV limit reached", name);
294                         else if (!doingall)
295                                 warn("%s", name);
296                         break;
297                 default:
298                         warn("%s", name);
299                         break;
300                 }
301                 return(1);
302         }
303         return(0);
304 }
305
306 static void
307 usage(void)
308 {
309         fprintf(stderr, "usage: %s ", getprogname());
310         switch (orig_prog) {
311         case SWAPON:
312         case SWAPOFF:
313                 fprintf(stderr, "-aeiq | file ...\n");
314                 break;
315         case SWAPCTL:
316                 fprintf(stderr, "[-AeghiklmsU] [-a file ... | -d file ...]\n");
317                 break;
318         }
319         exit(1);
320 }
321
322 static void
323 sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen,
324     long blocksize)
325 {
326         if (hflag == 'H') {
327                 char tmp[16];
328
329                 humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE,
330                     HN_B | HN_NOSPACE | HN_DECIMAL);
331                 snprintf(buf, bufsize, "%*s", hlen, tmp);
332         } else {
333                 snprintf(buf, bufsize, "%*lld", hlen, val / blocksize);
334         }
335 }
336
337 static void
338 swaplist(int lflag, int sflag, int hflag)
339 {
340         size_t ksize, bytes = 0;
341         char *xswbuf;
342         struct xswdev *xsw;
343         int hlen, pagesize;
344         int i, n;
345         long blocksize;
346         long long total, used, tmp_total, tmp_used;
347         char buf[32];
348
349         pagesize = getpagesize();
350         switch(hflag) {
351         case 'G':
352                 blocksize = 1024 * 1024 * 1024;
353                 strlcpy(buf, "1GB-blocks", sizeof(buf));
354                 hlen = 10;
355                 break;
356         case 'H':
357                 blocksize = -1;
358                 strlcpy(buf, "Bytes", sizeof(buf));
359                 hlen = 10;
360                 break;
361         case 'K':
362                 blocksize = 1024;
363                 strlcpy(buf, "1kB-blocks", sizeof(buf));
364                 hlen = 10;
365                 break;
366         case 'M':
367                 blocksize = 1024 * 1024;
368                 strlcpy(buf, "1MB-blocks", sizeof(buf));
369                 hlen = 10;
370                 break;
371         default:
372                 getbsize(&hlen, &blocksize);
373                 snprintf(buf, sizeof(buf), "%ld-blocks", blocksize);
374                 break;
375         }
376
377         if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
378                 err(1, "sysctlbyname()");
379         if (bytes == 0)
380                 err(1, "sysctlbyname()");
381
382         xswbuf = malloc(bytes);
383         if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
384                 free(xswbuf);
385                 err(1, "sysctlbyname()");
386         }
387         if (bytes == 0) {
388                 free(xswbuf);
389                 err(1, "sysctlbyname()");
390         }
391
392         /*
393          * Calculate size of xsw entry returned by kernel (it can be larger
394          * than the one we have if there is a version mismatch).
395          */
396         ksize = ((struct xswdev *)xswbuf)->xsw_size;
397         n = (int)(bytes / ksize);
398
399         if (lflag) {
400                 printf("%-13s %*s %*s\n",
401                     "Device:",
402                     hlen, buf,
403                     hlen, "Used:");
404         }
405
406         total = used = tmp_total = tmp_used = 0;
407         for (i = 0; i < n; ++i) {
408                 xsw = (void *)((char *)xswbuf + i * ksize);
409
410                 if (xsw->xsw_nblks == 0)
411                         continue;
412
413                 tmp_total = (long long)xsw->xsw_nblks * pagesize;
414                 tmp_used = (long long)xsw->xsw_used * pagesize;
415                 total += tmp_total;
416                 used += tmp_used;
417                 if (lflag) {
418                         sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen,
419                             blocksize);
420                         if (xsw->xsw_dev == NODEV) {
421                                 printf("%-13s %s ", "[NFS swap]", buf);
422                         } else {
423                                 printf("/dev/%-8s %s ",
424                                     devname(xsw->xsw_dev, S_IFCHR), buf);
425                         }
426
427                         sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen,
428                             blocksize);
429                         printf("%s\n", buf);
430                 }
431         }
432
433         if (sflag) {
434                 sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize);
435                 printf("Total:        %s ", buf);
436                 sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize);
437                 printf("%s\n", buf);
438         }
439 }