1/* See LICENSE file for copyright and license details. */
2
3
4#include <sys/mount.h>
5
6#include <mntent.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10
11#include "util.h"
12
13#if FEATURE_UMOUNT_OPTIONS
14static int
15fsopt_matches(const char *opts_list, const char *opt, size_t optlen)
16{
17 int match = 1;
18
19 if (optlen >= 2 && opt[0] == 'n' && opt[1] == 'o') {
20 match--;
21 opt += 2;
22 optlen -= 2;
23 }
24
25 if (optlen == 0)
26 return 0;
27
28 if (match && optlen > 1 && *opt == '+') {
29 opt++;
30 optlen--;
31 }
32
33 while (1) {
34 if (strncmp(opts_list, opt, optlen) == 0) {
35 const char *after_opt = opts_list + optlen;
36 if (*after_opt == '\0' || *after_opt == ',')
37 return match;
38 }
39
40 opts_list = strchr(opts_list, ',');
41 if (!opts_list)
42 break;
43 opts_list++;
44 }
45
46 return !match;
47}
48
49static int
50fsopts_matches(const char *opts_list, const char *reqopts_list)
51{
52 const char *comma;
53 size_t len;
54
55 if (!reqopts_list)
56 return 1;
57
58 while (1) {
59 comma = strchr(reqopts_list, ',');
60 if (!comma)
61 len = strlen(reqopts_list);
62 else
63 len = comma - reqopts_list;
64
65 if (len && !fsopt_matches(opts_list, reqopts_list, len))
66 return 0;
67
68 if (!comma)
69 break;
70 reqopts_list = ++comma;
71 }
72
73 return 1;
74}
75#endif
76
77static int
78umountall(int flags
79#if FEATURE_UMOUNT_OPTIONS
80 , const char *oflag
81#endif
82)
83{
84 FILE *fp;
85 struct mntent *me;
86 int ret = 0;
87 char **mntdirs = NULL;
88 int len = 0;
89
90 fp = setmntent("/proc/mounts", "r");
91 if (!fp)
92 eprintf("setmntent %s:", "/proc/mounts");
93 while ((me = getmntent(fp))) {
94 if (strcmp(me->mnt_type, "proc") == 0)
95 continue;
96#if FEATURE_UMOUNT_OPTIONS
97 if (oflag && !fsopts_matches(me->mnt_opts, oflag))
98 continue;
99#endif
100 mntdirs = erealloc(mntdirs, ++len * sizeof(*mntdirs));
101 mntdirs[len - 1] = estrdup(me->mnt_dir);
102 }
103 endmntent(fp);
104 while (--len >= 0) {
105 if (umount2(mntdirs[len], flags) < 0) {
106 weprintf("umount2 %s:", mntdirs[len]);
107 ret = 1;
108 }
109 free(mntdirs[len]);
110 }
111 free(mntdirs);
112 return ret;
113}
114
115static void
116usage(void)
117{
118#if FEATURE_UMOUNT_OPTIONS
119 weprintf("usage: %s [-lfn] [-O options] target...\n", argv0);
120 weprintf("usage: %s -a [-lfn] [-O options]\n", argv0);
121#else
122 weprintf("usage: %s [-lfn] target...\n", argv0);
123 weprintf("usage: %s -a [-lfn]\n", argv0);
124#endif
125 exit(1);
126}
127
128// ?man umount: unmount filesystems
129// ?man arguments: target...
130// ?man unmount a filesystem from the directory tree
131int
132main(int argc, char *argv[])
133{
134 int i;
135 int aflag = 0;
136 int flags = 0;
137 int ret = 0;
138#if FEATURE_UMOUNT_OPTIONS
139 char *oflag = NULL;
140#endif
141
142 ARGBEGIN {
143 // ?man -a: print or show all entries
144 case 'a':
145 aflag = 1;
146 break;
147 // ?man -f: force the operation
148 case 'f':
149 flags |= MNT_FORCE;
150 break;
151 // ?man -l: list in long format
152 case 'l':
153 flags |= MNT_DETACH;
154 break;
155 // ?man -n: print line numbers or counts
156 case 'n':
157 break;
158#if FEATURE_UMOUNT_OPTIONS
159 // ?man -O:str: specify option flag
160 case 'O':
161 oflag = EARGF(usage());
162 break;
163#endif
164 default:
165 usage();
166 } ARGEND;
167
168 if (argc < 1 && aflag == 0)
169 usage();
170
171#if FEATURE_UMOUNT_OPTIONS
172 if (oflag && aflag == 0)
173 usage();
174#endif
175
176 if (aflag == 1)
177 return umountall(flags
178#if FEATURE_UMOUNT_OPTIONS
179 , oflag
180#endif
181 );
182
183 for (i = 0; i < argc; i++) {
184 if (umount2(argv[i], flags) < 0) {
185 weprintf("umount2 %s:", argv[i]);
186 ret = 1;
187 }
188 }
189 return ret;
190}