1/* See LICENSE file for copyright and license details. */
2
3#include <sys/mount.h>
4
5#include <mntent.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9
10#include "paths.h"
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(
79 int flags
80#if FEATURE_UMOUNT_OPTIONS
81 ,
82 const char *oflag
83#endif
84)
85{
86 FILE *fp;
87 struct mntent *me;
88 int ret = 0;
89 char **mntdirs = NULL;
90 int len = 0;
91
92 fp = setmntent(ARUU_LINUX_PATH_PROC_MOUNTS, "r");
93 if (!fp)
94 eprintf("setmntent %s:", ARUU_LINUX_PATH_PROC_MOUNTS);
95 while ((me = getmntent(fp))) {
96 if (strcmp(me->mnt_type, "proc") == 0)
97 continue;
98#if FEATURE_UMOUNT_OPTIONS
99 if (oflag && !fsopts_matches(me->mnt_opts, oflag))
100 continue;
101#endif
102 mntdirs = erealloc(mntdirs, ++len * sizeof(*mntdirs));
103 mntdirs[len - 1] = estrdup(me->mnt_dir);
104 }
105 endmntent(fp);
106 while (--len >= 0) {
107 if (umount2(mntdirs[len], flags) < 0) {
108 weprintf("umount2 %s:", mntdirs[len]);
109 ret = 1;
110 }
111 free(mntdirs[len]);
112 }
113 free(mntdirs);
114 return ret;
115}
116
117static void
118usage(void)
119{
120#if FEATURE_UMOUNT_OPTIONS
121 weprintf("usage: %s [-lfn] [-O options] target...\n", argv0);
122 weprintf("usage: %s -a [-lfn] [-O options]\n", argv0);
123#else
124 weprintf("usage: %s [-lfn] target...\n", argv0);
125 weprintf("usage: %s -a [-lfn]\n", argv0);
126#endif
127 exit(1);
128}
129
130// ?man umount: unmount filesystems
131// ?man arguments: target...
132// ?man unmount a filesystem from the directory tree
133int
134main(int argc, char *argv[])
135{
136 int i;
137 int aflag = 0;
138 int flags = 0;
139 int ret = 0;
140#if FEATURE_UMOUNT_OPTIONS
141 char *oflag = NULL;
142#endif
143
144 ARGBEGIN
145 {
146 // ?man -a: print or show all entries
147 case 'a':
148 aflag = 1;
149 break;
150 // ?man -f: force the operation
151 case 'f':
152 flags |= MNT_FORCE;
153 break;
154 // ?man -l: list in long format
155 case 'l':
156 flags |= MNT_DETACH;
157 break;
158 // ?man -n: print line numbers or counts
159 case 'n':
160 break;
161#if FEATURE_UMOUNT_OPTIONS
162 // ?man -O:str: specify option flag
163 case 'O':
164 oflag = EARGF(usage());
165 break;
166#endif
167 default:
168 usage();
169 }
170 ARGEND;
171
172 if (argc < 1 && aflag == 0)
173 usage();
174
175#if FEATURE_UMOUNT_OPTIONS
176 if (oflag && aflag == 0)
177 usage();
178#endif
179
180 if (aflag == 1)
181 return umountall(
182 flags
183#if FEATURE_UMOUNT_OPTIONS
184 ,
185 oflag
186#endif
187 );
188
189 for (i = 0; i < argc; i++) {
190 if (umount2(argv[i], flags) < 0) {
191 weprintf("umount2 %s:", argv[i]);
192 ret = 1;
193 }
194 }
195 return ret;
196}