fdisk, gpt - Support trim on recent kernels
[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/linker.h>
38 #include <sys/diskslice.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 #include <bus/cam/scsi/scsi_daio.h>
52
53 static void usage(void);
54 static int swap_on_off(char *name, int doingall, int trim, int ask);
55 static char *docrypt(char *fs_spec, int pass);
56 static void swaplist(int lflag, int sflag, int hflag);
57
58 static int qflag;
59
60 enum { SWAPON, SWAPOFF, SWAPCTL } orig_prog, which_prog = SWAPCTL;
61
62 int
63 main(int argc, char **argv)
64 {
65         struct fstab *fsp;
66         char *ptr;
67         int ret;
68         int ch;
69         int doall, sflag, lflag, hflag, eflag, cflag, iflag;
70
71         if ((ptr = strrchr(argv[0], '/')) == NULL)
72                 ptr = argv[0];
73         if (strstr(ptr, "swapon"))
74                 which_prog = SWAPON;
75         else if (strstr(ptr, "swapoff"))
76                 which_prog = SWAPOFF;
77         orig_prog = which_prog;
78
79         sflag = lflag = hflag = doall = eflag = cflag = iflag = 0;
80         while ((ch = getopt(argc, argv, "acdeghiklmqsAEU")) != -1) {
81                 switch((char)ch) {
82                 case 'A':
83                         if (which_prog == SWAPCTL) {
84                                 doall = 1;
85                                 which_prog = SWAPON;
86                         } else {
87                                 usage();
88                         }
89                         break;
90                 case 'a':
91                         if (which_prog == SWAPON || which_prog == SWAPOFF)
92                                 doall = 1;
93                         else
94                                 which_prog = SWAPON;
95                         break;
96                 case 'd':
97                         if (which_prog == SWAPCTL)
98                                 which_prog = SWAPOFF;
99                         else
100                                 usage();
101                         break;
102                 case 'c':
103                         cflag = 1;
104                         break;
105                 case 'E':
106                 case 'e':
107                         eflag = 1;
108                         break;
109                 case 'g':
110                         hflag = 'G';
111                         break;
112                 case 'h':
113                         hflag = 'H';
114                         break;
115                 case 'i':
116                         iflag = 1;
117                         break;
118                 case 'k':
119                         hflag = 'K';
120                         break;
121                 case 'l':
122                         lflag = 1;
123                         break;
124                 case 'm':
125                         hflag = 'M';
126                         break;
127                 case 'q':
128                         if (which_prog == SWAPON || which_prog == SWAPOFF)
129                                 qflag = 1;
130                         break;
131                 case 's':
132                         sflag = 1;
133                         break;
134                 case 'U':
135                         if (which_prog == SWAPCTL) {
136                                 doall = 1;
137                                 which_prog = SWAPOFF;
138                         } else {
139                                 usage();
140                         }
141                         break;
142                 case '?':
143                 default:
144                         usage();
145                 }
146         }
147         argv += optind;
148
149         ret = 0;
150         if (which_prog == SWAPON || which_prog == SWAPOFF) {
151                 if (doall) {
152                         while ((fsp = getfsent()) != NULL) {
153                                 char *fs_spec;
154                                 int dotrim = eflag;
155
156                                 if (strcmp(fsp->fs_type, FSTAB_SW))
157                                         continue;
158                                 if (strstr(fsp->fs_mntops, "noauto"))
159                                         continue;
160
161                                 if (strstr(fsp->fs_mntops, "notrim"))
162                                         dotrim = 0;
163                                 else if (strstr(fsp->fs_mntops, "trim"))
164                                         dotrim = 1;
165
166                                 if (cflag || strstr(fsp->fs_mntops, "crypt"))
167                                         fs_spec = docrypt(fsp->fs_spec, 1);
168                                 else
169                                         fs_spec = strdup(fsp->fs_spec);
170                                 if (swap_on_off(fs_spec, 1, dotrim, iflag)) {
171                                         ret = 1;
172                                 } else {
173                                         if (cflag ||
174                                             strstr(fsp->fs_mntops, "crypt")) {
175                                                 docrypt(fsp->fs_spec, 2);
176                                         }
177                                         if (!qflag) {
178                                                 printf("%s: %sing %s as swap "
179                                                        "device\n",
180                                                     getprogname(),
181                                                     (which_prog == SWAPOFF ?
182                                                             "remov" : "add"),
183                                                     fs_spec);
184                                         }
185                                 }
186                                 free(fs_spec);
187                         }
188                 } else if (*argv == NULL) {
189                         usage();
190                 }
191                 for (; *argv; ++argv) {
192                         char *ospec = getdevpath(*argv, 0);
193                         char *fs_spec;
194                         if (cflag)
195                                 fs_spec = docrypt(ospec, 1);
196                         else
197                                 fs_spec = strdup(ospec);
198                         if (swap_on_off(fs_spec, 0, eflag, iflag)) {
199                                 ret = 1;
200                         } else {
201                                 if (cflag)
202                                         docrypt(ospec, 2);
203                                 if (!qflag) {
204                                         printf("%s: %sing %s as swap device\n",
205                                             getprogname(),
206                                             (which_prog == SWAPOFF ?
207                                                 "remov" : "add"),
208                                             fs_spec);
209                                 }
210                         }
211                         free(fs_spec);
212                 }
213         } else {
214                 if (lflag || sflag)
215                         swaplist(lflag, sflag, hflag);
216                 else
217                         usage();
218         }
219         exit(ret);
220 }
221
222 static
223 char *
224 docrypt(char *fs_spec, int pass)
225 {
226         char *id;
227         char *res;
228         char *buf;
229
230         if ((id = strrchr(fs_spec, '/')) == NULL)
231                 id = fs_spec;
232         else
233                 ++id;
234         asprintf(&id, "swap-%s", id);
235         asprintf(&res, "/dev/mapper/%s", id);
236
237         switch(which_prog) {
238         case SWAPOFF:
239                 if (pass != 2)
240                         break;
241                 asprintf(&buf, "/sbin/cryptsetup remove %s", id);
242                 system(buf);
243                 free(buf);
244                 free(id);
245                 break;
246         case SWAPON:
247                 if (pass != 1)
248                         break;
249                 if (kldfind("dm_target_crypt") < 0)
250                         kldload("dm_target_crypt");
251
252                 asprintf(&buf,
253                          "/sbin/cryptsetup --key-file /dev/urandom "
254                          "--key-size 256 create %s %s",
255                          id, fs_spec);
256                 if (qflag == 0)
257                         printf("%s\n", buf);
258                 system(buf);
259                 free(buf);
260                 free(id);
261
262                 /*
263                  * NOTE: Don't revert to /dev/da* on error because this could
264                  *       inadvertently add both /dev/da* and
265                  *       /dev/mapper/swap-da*.
266                  *
267                  *       Allow the swapon operation to report failure or
268                  *       report a duplicate.
269                  */
270                 break;
271         default:
272                 free(res);
273                 free(id);
274                 res = strdup(fs_spec);
275                 break;
276         }
277
278         if (pass == 2) {
279                 free (res);
280                 res = NULL;
281         }
282         return res;
283 }
284
285 /*
286  * TRIM the device
287  */
288 static
289 void
290 trim_volume(char * name)
291 {       
292         struct partinfo pinfo;
293         int fd,i,n;
294         size_t bytes = 0,ksize;
295         char *xswbuf;
296         struct xswdev *xsw;
297
298
299         /*
300         * Determine if this device is already being used by swap without 
301         * calling swapon(). 
302         */
303         if ((sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0) ||
304             bytes == 0) {
305                 err(1, "sysctlbyname()");
306         }
307
308         xswbuf = malloc(bytes);
309         if ((sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) ||
310             bytes == 0) {
311                         free(xswbuf);
312                         err(1, "sysctlbyname()");
313         }
314
315         ksize = ((struct xswdev *)xswbuf)->xsw_size;
316         n = (int)(bytes / ksize);
317         for (i = 0; i < n; ++i) {
318                 xsw = (void *)((char *)xswbuf + i * ksize);
319
320                 if (xsw->xsw_dev == NODEV )
321                         continue;
322                 if(!strcmp(devname(xsw->xsw_dev, S_IFCHR),
323                     name + strlen("/dev/"))) {
324                         warnx("%s: device already a swap device", name);
325                         exit(1);
326                 }
327         }
328         
329         /*
330          * Get the size and offset of this parititon/device
331          */
332         fd = open(name, O_RDWR);
333         if (fd < 0)
334                 err(1, "Unable to open %s R+W", name);
335         if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
336                 printf("Cannot trim regular file\n");
337                 usage ();
338         }
339         off_t ioarg[2];
340         
341         /*Trim the Device*/     
342         ioarg[0] = pinfo.media_offset;
343         ioarg[1] = pinfo.media_size;
344         printf("Trimming Device:%s, start=%jd bytes=%jd)\n",
345              name, (intmax_t)ioarg[0], (intmax_t)ioarg[1]);
346         if (ioctl(fd, DAIOCTRIM, ioarg) < 0) {
347                 printf("Device trim failed\n");
348                 usage ();
349         }
350         close(fd);
351 }
352
353 static int
354 swap_on_off(char *name, int doingall, int trim, int ask)
355 {
356
357         if (ask && which_prog == SWAPON) {
358                 printf("Do you really want to use device %s as a swap device ?\n", name);
359                 printf("You might loose data. [Y/N]");
360
361                 int c = fgetc(stdin);
362                 printf("\n");
363                 if (c != 'y' && c != 'Y')
364                         return(1);
365
366         }
367         if (which_prog == SWAPON && trim) {
368                 trim_volume(name);
369         }
370         if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
371                 switch(errno) {
372                 case EBUSY:
373                         if (!doingall)
374                                 warnx("%s: device already in use", name);
375                         break;
376                 case EINVAL:
377                         if (which_prog == SWAPON)
378                                 warnx("%s: NSWAPDEV limit reached", name);
379                         else if (!doingall)
380                                 warn("%s", name);
381                         break;
382                 default:
383                         warn("%s", name);
384                         break;
385                 }
386                 return(1);
387         }
388         return(0);
389 }
390
391 static void
392 usage(void)
393 {
394         fprintf(stderr, "usage: %s ", getprogname());
395         switch (orig_prog) {
396         case SWAPON:
397         case SWAPOFF:
398                 fprintf(stderr, "-aeiq | file ...\n");
399                 break;
400         case SWAPCTL:
401                 fprintf(stderr, "[-AeghiklmsU] [-a file ... | -d file ...]\n");
402                 break;
403         }
404         exit(1);
405 }
406
407 static void
408 sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen,
409     long blocksize)
410 {
411         if (hflag == 'H') {
412                 char tmp[16];
413
414                 humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE,
415                     HN_B | HN_NOSPACE | HN_DECIMAL);
416                 snprintf(buf, bufsize, "%*s", hlen, tmp);
417         } else {
418                 snprintf(buf, bufsize, "%*lld", hlen, val / blocksize);
419         }
420 }
421
422 static void
423 swaplist(int lflag, int sflag, int hflag)
424 {
425         size_t ksize, bytes = 0;
426         char *xswbuf;
427         struct xswdev *xsw;
428         int hlen, pagesize;
429         int i, n;
430         long blocksize;
431         long long total, used, tmp_total, tmp_used;
432         char buf[32];
433
434         pagesize = getpagesize();
435         switch(hflag) {
436         case 'G':
437                 blocksize = 1024 * 1024 * 1024;
438                 strlcpy(buf, "1GB-blocks", sizeof(buf));
439                 hlen = 10;
440                 break;
441         case 'H':
442                 blocksize = -1;
443                 strlcpy(buf, "Bytes", sizeof(buf));
444                 hlen = 10;
445                 break;
446         case 'K':
447                 blocksize = 1024;
448                 strlcpy(buf, "1kB-blocks", sizeof(buf));
449                 hlen = 10;
450                 break;
451         case 'M':
452                 blocksize = 1024 * 1024;
453                 strlcpy(buf, "1MB-blocks", sizeof(buf));
454                 hlen = 10;
455                 break;
456         default:
457                 getbsize(&hlen, &blocksize);
458                 snprintf(buf, sizeof(buf), "%ld-blocks", blocksize);
459                 break;
460         }
461
462         if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
463                 err(1, "sysctlbyname()");
464         if (bytes == 0)
465                 err(1, "sysctlbyname()");
466
467         xswbuf = malloc(bytes);
468         if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
469                 free(xswbuf);
470                 err(1, "sysctlbyname()");
471         }
472         if (bytes == 0) {
473                 free(xswbuf);
474                 err(1, "sysctlbyname()");
475         }
476
477         /*
478          * Calculate size of xsw entry returned by kernel (it can be larger
479          * than the one we have if there is a version mismatch).
480          */
481         ksize = ((struct xswdev *)xswbuf)->xsw_size;
482         n = (int)(bytes / ksize);
483
484         if (lflag) {
485                 printf("%-13s %*s %*s\n",
486                     "Device:",
487                     hlen, buf,
488                     hlen, "Used:");
489         }
490
491         total = used = tmp_total = tmp_used = 0;
492         for (i = 0; i < n; ++i) {
493                 xsw = (void *)((char *)xswbuf + i * ksize);
494
495                 if (xsw->xsw_nblks == 0)
496                         continue;
497
498                 tmp_total = (long long)xsw->xsw_nblks * pagesize;
499                 tmp_used = (long long)xsw->xsw_used * pagesize;
500                 total += tmp_total;
501                 used += tmp_used;
502                 if (lflag) {
503                         sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen,
504                             blocksize);
505                         if (xsw->xsw_dev == NODEV) {
506                                 printf("%-13s %s ", "[NFS swap]", buf);
507                         } else {
508                                 printf("/dev/%-8s %s ",
509                                     devname(xsw->xsw_dev, S_IFCHR), buf);
510                         }
511
512                         sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen,
513                             blocksize);
514                         printf("%s\n", buf);
515                 }
516         }
517
518         if (sflag) {
519                 sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize);
520                 printf("Total:        %s ", buf);
521                 sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize);
522                 printf("%s\n", buf);
523         }
524 }