1ff942dabb85b8e3792de4b24b853f8921665afc
[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, iflag)) {
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                 printf("\n");
260                 if (c != 'y' && c != 'Y')
261                         return(1);
262
263         }
264         if (which_prog == SWAPON && trim){
265                 char sysctl_name[64];
266                 int trim_enabled = 0;
267                 size_t olen = sizeof(trim_enabled);
268                 char *dev_name = strdup(name);
269                 dev_name = strtok(dev_name + strlen("/dev/da"),"s");
270                 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", dev_name);
271                 sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
272                 if(errno == ENOENT) {
273                         printf("Device:%s does not support the TRIM command\n",
274                             name);
275                         usage();
276                 }
277                 if(!trim_enabled) {
278                         printf("Erase device option selected, but sysctl (%s) "
279                             "is not enabled\n",sysctl_name);
280                         usage();
281                 }
282
283                 trim_volume(name);
284
285         }
286         if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
287                 switch(errno) {
288                 case EBUSY:
289                         if (!doingall)
290                                 warnx("%s: device already in use", name);
291                         break;
292                 case EINVAL:
293                         if (which_prog == SWAPON)
294                                 warnx("%s: NSWAPDEV limit reached", name);
295                         else if (!doingall)
296                                 warn("%s", name);
297                         break;
298                 default:
299                         warn("%s", name);
300                         break;
301                 }
302                 return(1);
303         }
304         return(0);
305 }
306
307 static void
308 usage(void)
309 {
310         fprintf(stderr, "usage: %s ", getprogname());
311         switch (orig_prog) {
312         case SWAPON:
313         case SWAPOFF:
314                 fprintf(stderr, "-aeiq | file ...\n");
315                 break;
316         case SWAPCTL:
317                 fprintf(stderr, "[-AeghiklmsU] [-a file ... | -d file ...]\n");
318                 break;
319         }
320         exit(1);
321 }
322
323 static void
324 sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen,
325     long blocksize)
326 {
327         if (hflag == 'H') {
328                 char tmp[16];
329
330                 humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE,
331                     HN_B | HN_NOSPACE | HN_DECIMAL);
332                 snprintf(buf, bufsize, "%*s", hlen, tmp);
333         } else {
334                 snprintf(buf, bufsize, "%*lld", hlen, val / blocksize);
335         }
336 }
337
338 static void
339 swaplist(int lflag, int sflag, int hflag)
340 {
341         size_t ksize, bytes = 0;
342         char *xswbuf;
343         struct xswdev *xsw;
344         int hlen, pagesize;
345         int i, n;
346         long blocksize;
347         long long total, used, tmp_total, tmp_used;
348         char buf[32];
349
350         pagesize = getpagesize();
351         switch(hflag) {
352         case 'G':
353                 blocksize = 1024 * 1024 * 1024;
354                 strlcpy(buf, "1GB-blocks", sizeof(buf));
355                 hlen = 10;
356                 break;
357         case 'H':
358                 blocksize = -1;
359                 strlcpy(buf, "Bytes", sizeof(buf));
360                 hlen = 10;
361                 break;
362         case 'K':
363                 blocksize = 1024;
364                 strlcpy(buf, "1kB-blocks", sizeof(buf));
365                 hlen = 10;
366                 break;
367         case 'M':
368                 blocksize = 1024 * 1024;
369                 strlcpy(buf, "1MB-blocks", sizeof(buf));
370                 hlen = 10;
371                 break;
372         default:
373                 getbsize(&hlen, &blocksize);
374                 snprintf(buf, sizeof(buf), "%ld-blocks", blocksize);
375                 break;
376         }
377
378         if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
379                 err(1, "sysctlbyname()");
380         if (bytes == 0)
381                 err(1, "sysctlbyname()");
382
383         xswbuf = malloc(bytes);
384         if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
385                 free(xswbuf);
386                 err(1, "sysctlbyname()");
387         }
388         if (bytes == 0) {
389                 free(xswbuf);
390                 err(1, "sysctlbyname()");
391         }
392
393         /*
394          * Calculate size of xsw entry returned by kernel (it can be larger
395          * than the one we have if there is a version mismatch).
396          */
397         ksize = ((struct xswdev *)xswbuf)->xsw_size;
398         n = (int)(bytes / ksize);
399
400         if (lflag) {
401                 printf("%-13s %*s %*s\n",
402                     "Device:",
403                     hlen, buf,
404                     hlen, "Used:");
405         }
406
407         total = used = tmp_total = tmp_used = 0;
408         for (i = 0; i < n; ++i) {
409                 xsw = (void *)((char *)xswbuf + i * ksize);
410
411                 if (xsw->xsw_nblks == 0)
412                         continue;
413
414                 tmp_total = (long long)xsw->xsw_nblks * pagesize;
415                 tmp_used = (long long)xsw->xsw_used * pagesize;
416                 total += tmp_total;
417                 used += tmp_used;
418                 if (lflag) {
419                         sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen,
420                             blocksize);
421                         if (xsw->xsw_dev == NODEV) {
422                                 printf("%-13s %s ", "[NFS swap]", buf);
423                         } else {
424                                 printf("/dev/%-8s %s ",
425                                     devname(xsw->xsw_dev, S_IFCHR), buf);
426                         }
427
428                         sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen,
429                             blocksize);
430                         printf("%s\n", buf);
431                 }
432         }
433
434         if (sflag) {
435                 sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize);
436                 printf("Total:        %s ", buf);
437                 sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize);
438                 printf("%s\n", buf);
439         }
440 }