xref: /dragonfly/sbin/swapon/swapon.c (revision eeecdae69913500f880d57f02a22a49d8ea89460)
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
main(int argc,char ** argv)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 *
docrypt(char * fs_spec,int pass)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
trim_volume(char * name)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           }
349           close(fd);
350 }
351 
352 static int
swap_on_off(char * name,int doingall,int trim,int ask)353 swap_on_off(char *name, int doingall, int trim, int ask)
354 {
355 
356           if (ask && which_prog == SWAPON) {
357                     printf("Do you really want to use device %s as a swap device ?\n", name);
358                     printf("You might loose data. [Y/N]");
359 
360                     int c = fgetc(stdin);
361                     printf("\n");
362                     if (c != 'y' && c != 'Y')
363                               return(1);
364 
365           }
366           if (which_prog == SWAPON && trim) {
367                     trim_volume(name);
368           }
369           if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
370                     switch(errno) {
371                     case EBUSY:
372                               if (!doingall)
373                                         warnx("%s: device already in use", name);
374                               break;
375                     case EINVAL:
376                               if (which_prog == SWAPON)
377                                         warnx("%s: NSWAPDEV limit reached", name);
378                               else if (!doingall)
379                                         warn("%s", name);
380                               break;
381                     default:
382                               warn("%s", name);
383                               break;
384                     }
385                     return(1);
386           }
387           return(0);
388 }
389 
390 static void
usage(void)391 usage(void)
392 {
393           fprintf(stderr, "usage: %s ", getprogname());
394           switch (orig_prog) {
395           case SWAPON:
396           case SWAPOFF:
397                     fprintf(stderr, "-aeiq | file ...\n");
398                     break;
399           case SWAPCTL:
400                     fprintf(stderr, "[-AeghiklmsU] [-a file ... | -d file ...]\n");
401                     break;
402           }
403           exit(1);
404 }
405 
406 static void
sizetobuf(char * buf,size_t bufsize,int hflag,long long val,int hlen,long blocksize)407 sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen,
408     long blocksize)
409 {
410           if (hflag == 'H') {
411                     char tmp[16];
412 
413                     humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE,
414                         HN_B | HN_NOSPACE | HN_DECIMAL);
415                     snprintf(buf, bufsize, "%*s", hlen, tmp);
416           } else {
417                     snprintf(buf, bufsize, "%*lld", hlen, val / blocksize);
418           }
419 }
420 
421 static void
swaplist(int lflag,int sflag,int hflag)422 swaplist(int lflag, int sflag, int hflag)
423 {
424           size_t ksize, bytes = 0;
425           char *xswbuf;
426           struct xswdev *xsw;
427           int hlen, pagesize;
428           int i, n;
429           long blocksize;
430           long long total, used, tmp_total, tmp_used;
431           char buf[32];
432 
433           pagesize = getpagesize();
434           switch(hflag) {
435           case 'G':
436                     blocksize = 1024 * 1024 * 1024;
437                     strlcpy(buf, "1GB-blocks", sizeof(buf));
438                     hlen = 10;
439                     break;
440           case 'H':
441                     blocksize = -1;
442                     strlcpy(buf, "Bytes", sizeof(buf));
443                     hlen = 10;
444                     break;
445           case 'K':
446                     blocksize = 1024;
447                     strlcpy(buf, "1kB-blocks", sizeof(buf));
448                     hlen = 10;
449                     break;
450           case 'M':
451                     blocksize = 1024 * 1024;
452                     strlcpy(buf, "1MB-blocks", sizeof(buf));
453                     hlen = 10;
454                     break;
455           default:
456                     getbsize(&hlen, &blocksize);
457                     snprintf(buf, sizeof(buf), "%ld-blocks", blocksize);
458                     break;
459           }
460 
461           if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
462                     err(1, "sysctlbyname()");
463           if (bytes == 0)
464                     err(1, "sysctlbyname()");
465 
466           xswbuf = malloc(bytes);
467           if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
468                     free(xswbuf);
469                     err(1, "sysctlbyname()");
470           }
471           if (bytes == 0) {
472                     free(xswbuf);
473                     err(1, "sysctlbyname()");
474           }
475 
476           /*
477            * Calculate size of xsw entry returned by kernel (it can be larger
478            * than the one we have if there is a version mismatch).
479            */
480           ksize = ((struct xswdev *)xswbuf)->xsw_size;
481           n = (int)(bytes / ksize);
482 
483           if (lflag) {
484                     printf("%-13s %*s %*s\n",
485                         "Device:",
486                         hlen, buf,
487                         hlen, "Used:");
488           }
489 
490           total = used = tmp_total = tmp_used = 0;
491           for (i = 0; i < n; ++i) {
492                     xsw = (void *)((char *)xswbuf + i * ksize);
493 
494                     if (xsw->xsw_nblks == 0)
495                               continue;
496 
497                     tmp_total = (long long)xsw->xsw_nblks * pagesize;
498                     tmp_used = (long long)xsw->xsw_used * pagesize;
499                     total += tmp_total;
500                     used += tmp_used;
501                     if (lflag) {
502                               sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen,
503                                   blocksize);
504                               if (xsw->xsw_dev == NODEV) {
505                                         printf("%-13s %s ", "[NFS swap]", buf);
506                               } else {
507                                         printf("/dev/%-8s %s ",
508                                             devname(xsw->xsw_dev, S_IFCHR), buf);
509                               }
510 
511                               sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen,
512                                   blocksize);
513                               printf("%s\n", buf);
514                     }
515           }
516 
517           if (sflag) {
518                     sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize);
519                     printf("Total:        %s ", buf);
520                     sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize);
521                     printf("%s\n", buf);
522           }
523 }
524