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