]>
Commit | Line | Data |
---|---|---|
64c4757e NB |
1 | /* |
2 | * mdctl - manage Linux "md" devices aka RAID arrays. | |
3 | * | |
4 | * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au> | |
5 | * | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
20 | * | |
21 | * Author: Neil Brown | |
22 | * Email: <neilb@cse.unsw.edu.au> | |
23 | * Paper: Neil Brown | |
24 | * School of Computer Science and Engineering | |
25 | * The University of New South Wales | |
26 | * Sydney, 2052 | |
27 | * Australia | |
28 | */ | |
29 | ||
30 | #include "mdctl.h" | |
31 | #include "md_p.h" | |
32 | ||
33 | int main(int argc, char *argv[]) | |
34 | { | |
35 | char mode = '\0'; | |
36 | int opt; | |
37 | char *help_text; | |
38 | char *c; | |
39 | int rv; | |
40 | int i; | |
41 | ||
42 | int chunk = 0; | |
682c7051 | 43 | int size = 0; |
64c4757e NB |
44 | int level = -10; |
45 | int layout = -1; | |
46 | int raiddisks = 0; | |
47 | int sparedisks = 0; | |
48 | int uuid[4]; | |
49 | int uuidset = 0; | |
50 | char *configfile = NULL; | |
51 | int scan = 0; | |
52 | char devmode = 0; | |
53 | int runstop = 0; | |
54 | int readonly = 0; | |
55 | char *mddev = NULL; | |
56 | char *subdev[MD_SB_DISKS]; | |
57 | int devmodes[MD_SB_DISKS]; | |
58 | int subdevs = 0; | |
59 | int verbose = 0; | |
60 | int force = 0; | |
61 | ||
62 | int mdfd = -1; | |
63 | ||
64 | while ((opt=getopt_long(argc, argv, | |
65 | short_options, long_options, | |
66 | NULL)) != -1) { | |
67 | ||
68 | switch(opt) { | |
69 | case '@': /* just incase they say --manage */ | |
70 | case 'A': | |
71 | case 'B': | |
72 | case 'C': | |
73 | case 'D': | |
74 | case 'E': | |
75 | /* setting mode - only once */ | |
76 | if (mode) { | |
682c7051 | 77 | fprintf(stderr, Name ": --%s/-%c not allowed, mode already set to %s\n", |
64c4757e NB |
78 | long_options[opt-'A'+1].name, |
79 | long_options[opt-'A'+1].val, | |
80 | long_options[mode-'A'+1].name); | |
81 | exit(2); | |
82 | } | |
83 | mode = opt; | |
84 | continue; | |
85 | ||
86 | case 'h': | |
87 | help_text = Help; | |
88 | switch (mode) { | |
89 | case 'C': help_text = Help_create; break; | |
90 | case 'B': help_text = Help_build; break; | |
91 | case 'A': help_text = Help_assemble; break; | |
92 | } | |
93 | fputs(help_text,stderr); | |
94 | exit(0); | |
95 | ||
96 | case 'V': | |
97 | fputs(Version, stderr); | |
98 | exit(0); | |
99 | ||
100 | case 'v': verbose = 1; | |
101 | continue; | |
102 | ||
103 | case 1: /* an undecorated option - must be a device name. | |
104 | * The first device is the "md" device unless scan | |
105 | * has been set or mode is Examine or Detail | |
106 | */ | |
107 | if (mddev == NULL && !scan && mode != 'E' && mode != 'D') | |
108 | mddev = optarg; | |
109 | else { | |
110 | if (subdevs +1 >= MD_SB_DISKS) { | |
682c7051 | 111 | fprintf(stderr, Name ": too many devices at %s - current limit -s %d\n", |
64c4757e NB |
112 | optarg, MD_SB_DISKS); |
113 | exit(2); | |
114 | } | |
115 | subdev[subdevs] = optarg; | |
116 | devmodes[subdevs] = devmode; | |
117 | subdevs++; | |
118 | } | |
119 | continue; | |
120 | ||
121 | case ':': | |
122 | case '?': | |
123 | fputs(Usage, stderr); | |
124 | exit(2); | |
125 | default: | |
126 | /* force mode setting - @==manage if nothing else */ | |
127 | if (!mode) mode = '@'; | |
128 | } | |
129 | ||
130 | /* We've got a mode, and opt is now something else which | |
131 | * could depend on the mode */ | |
132 | #define O(a,b) ((a<<8)|b) | |
133 | switch (O(mode,opt)) { | |
134 | case O('C','c'): | |
135 | case O('B','c'): /* chunk or rounding */ | |
136 | if (chunk) { | |
682c7051 | 137 | fprintf(stderr, Name ": chunk/rounding may only be specified once. " |
64c4757e NB |
138 | "Second value is %s.\n", optarg); |
139 | exit(2); | |
140 | } | |
141 | chunk = strtol(optarg, &c, 10); | |
682c7051 NB |
142 | if (!optarg[0] || *c || chunk<4 || ((chunk-1)&chunk)) { |
143 | fprintf(stderr, Name ": invalid chunk/rounding value: %s\n", | |
64c4757e NB |
144 | optarg); |
145 | exit(2); | |
146 | } | |
147 | continue; | |
148 | ||
682c7051 NB |
149 | case O('c','z'): /* size */ |
150 | if (size) { | |
151 | fprintf(stderr, Name ": size may only be specified once. " | |
152 | "Second value is %s.\n", optarg); | |
153 | exit(2); | |
154 | } | |
155 | size = strtol(optarg, &c, 10); | |
156 | if (!optarg[0] || *c || size < 4) { | |
157 | fprintf(stderr, Name ": invalid size: %s\n", | |
158 | optarg); | |
159 | exit(2); | |
160 | } | |
161 | continue; | |
162 | ||
64c4757e NB |
163 | case O('C','l'): |
164 | case O('B','l'): /* set raid level*/ | |
165 | if (level != -10) { | |
682c7051 | 166 | fprintf(stderr, Name ": raid level may only be set once. " |
64c4757e NB |
167 | "Second value is %s.\n", optarg); |
168 | exit(2); | |
169 | } | |
682c7051 NB |
170 | level = map_name(pers, optarg); |
171 | if (level == -10) { | |
172 | fprintf(stderr, Name ": invalid raid level: %s\n", | |
64c4757e NB |
173 | optarg); |
174 | exit(2); | |
175 | } | |
176 | if (level > 0 && mode == 'B') { | |
682c7051 | 177 | fprintf(stderr, Name ": Raid level %s not permitted with --build.\n", |
64c4757e NB |
178 | optarg); |
179 | exit(2); | |
180 | } | |
181 | if (sparedisks > 0 && level < 1) { | |
682c7051 | 182 | fprintf(stderr, Name ": raid level %s is incompatible with spare-disks setting.\n", |
64c4757e NB |
183 | optarg); |
184 | exit(2); | |
185 | } | |
186 | continue; | |
187 | ||
188 | case O('C','p'): /* raid5 layout */ | |
189 | if (layout >= 0) { | |
682c7051 | 190 | fprintf(stderr,Name ": layout may only be sent once. " |
64c4757e NB |
191 | "Second value was %s\n", optarg); |
192 | exit(2); | |
193 | } | |
682c7051 NB |
194 | switch(level) { |
195 | default: | |
196 | fprintf(stderr, Name ": layout now meaningful for %s arrays.\n", | |
197 | map_num(pers, level)); | |
198 | exit(2); | |
199 | case -10: | |
200 | fprintf(stderr, Name ": raid level must be given before layout.\n"); | |
201 | exit(2); | |
202 | ||
203 | case 5: | |
204 | layout = map_name(r5layout, optarg); | |
205 | if (layout==-10) { | |
206 | fprintf(stderr, Name ": layout %s not understood for raid5.\n", | |
207 | optarg); | |
208 | exit(2); | |
209 | } | |
210 | break; | |
64c4757e NB |
211 | } |
212 | continue; | |
213 | ||
214 | case O('C','n'): | |
215 | case O('B','n'): /* number of raid disks */ | |
216 | if (raiddisks) { | |
682c7051 | 217 | fprintf(stderr, Name ": raid-disks set twice: %d and %s\n", |
64c4757e NB |
218 | raiddisks, optarg); |
219 | exit(2); | |
220 | } | |
221 | raiddisks = strtol(optarg, &c, 10); | |
222 | if (!optarg[0] || *c || raiddisks<=0 || raiddisks > MD_SB_DISKS) { | |
682c7051 | 223 | fprintf(stderr, Name ": invalid number of raid disks: %s\n", |
64c4757e NB |
224 | optarg); |
225 | exit(2); | |
226 | } | |
227 | continue; | |
228 | ||
229 | case O('C','x'): /* number of spare (eXtra) discs */ | |
230 | if (sparedisks) { | |
682c7051 | 231 | fprintf(stderr,Name ": spare-disks set twice: %d and %s\n", |
64c4757e NB |
232 | sparedisks, optarg); |
233 | exit(2); | |
234 | } | |
235 | if (level > -10 && level < 1) { | |
682c7051 | 236 | fprintf(stderr, Name ": spare-disks setting is incompatible with raid level %d\n", |
64c4757e NB |
237 | level); |
238 | exit(2); | |
239 | } | |
240 | sparedisks = strtol(optarg, &c, 10); | |
241 | if (!optarg[0] || *c || sparedisks < 0 || sparedisks > MD_SB_DISKS - raiddisks) { | |
682c7051 | 242 | fprintf(stderr, Name ": invalid number of spare disks: %s\n", |
64c4757e NB |
243 | optarg); |
244 | exit(2); | |
245 | } | |
246 | continue; | |
247 | ||
248 | /* now for the Assemble options */ | |
249 | case O('A','f'): /* force assembly */ | |
250 | force = 1; | |
251 | continue; | |
252 | case O('A','u'): /* uuid of array */ | |
253 | if (uuidset) { | |
682c7051 | 254 | fprintf(stderr, Name ": uuid cannot bet set twice. " |
64c4757e NB |
255 | "Second value %s.\n", optarg); |
256 | exit(2); | |
257 | } | |
258 | if (parse_uuid(optarg, uuid)) | |
259 | uuidset = 1; | |
260 | else { | |
682c7051 | 261 | fprintf(stderr,Name ": Bad uuid: %s\n", optarg); |
64c4757e NB |
262 | exit(2); |
263 | } | |
264 | continue; | |
265 | ||
266 | case O('A','c'): /* config file */ | |
267 | if (configfile) { | |
682c7051 | 268 | fprintf(stderr, Name ": configfile cannot be set twice. " |
64c4757e NB |
269 | "Second value is %s.\n", optarg); |
270 | exit(2); | |
271 | } | |
272 | configfile = optarg; | |
273 | /* FIXME possibly check that config file exists. Even parse it */ | |
274 | continue; | |
275 | case O('A','s'): /* scan */ | |
276 | scan = 1; | |
277 | continue; | |
278 | ||
279 | /* now the general management options. Some are applicable | |
280 | * to other modes. None have arguments. | |
281 | */ | |
282 | case O('@','a'): | |
283 | case O('C','a'): | |
284 | case O('B','a'): | |
285 | case O('A','a'): /* add a drive */ | |
286 | devmode = 'a'; | |
287 | continue; | |
288 | case O('@','r'): /* remove a drive */ | |
289 | devmode = 'r'; | |
290 | continue; | |
291 | case O('@','f'): /* set faulty */ | |
292 | case O('C','f'): | |
293 | devmode = 'f'; | |
294 | continue; | |
295 | case O('@','R'): | |
296 | case O('A','R'): | |
297 | case O('B','R'): | |
298 | case O('C','R'): /* Run the array */ | |
299 | if (runstop < 0) { | |
682c7051 | 300 | fprintf(stderr, Name ": Cannot both Stop and Run an array\n"); |
64c4757e NB |
301 | exit(2); |
302 | } | |
303 | runstop = 1; | |
304 | continue; | |
305 | case O('@','S'): | |
306 | if (runstop > 0) { | |
682c7051 | 307 | fprintf(stderr, Name ": Cannot both Run and Stop an array\n"); |
64c4757e NB |
308 | exit(2); |
309 | } | |
310 | runstop = -1; | |
311 | continue; | |
312 | ||
313 | case O('@','o'): | |
314 | if (readonly < 0) { | |
682c7051 | 315 | fprintf(stderr, Name ": Cannot have both readonly and readwrite\n"); |
64c4757e NB |
316 | exit(2); |
317 | } | |
318 | readonly = 1; | |
319 | continue; | |
320 | case O('@','w'): | |
321 | if (readonly > 0) { | |
322 | fprintf(stderr, "mkdctl: Cannot have both readwrite and readonly.\n"); | |
323 | exit(2); | |
324 | } | |
325 | readonly = -1; | |
326 | continue; | |
327 | } | |
328 | /* We have now processed all the valid options. Anything else is | |
329 | * an error | |
330 | */ | |
682c7051 | 331 | fprintf(stderr, Name ": option %c not valid in mode %c\n", |
64c4757e NB |
332 | opt, mode); |
333 | exit(2); | |
334 | ||
335 | } | |
336 | ||
337 | if (!mode) { | |
338 | fputs(Usage, stderr); | |
339 | exit(2); | |
340 | } | |
341 | /* Ok, got the option parsing out of the way | |
342 | * hopefully it's mostly right but there might be some stuff | |
343 | * missing | |
344 | * | |
345 | * That is mosty checked in ther per-mode stuff but... | |
346 | * | |
347 | * There must be an mddev unless D or E or (A and scan) | |
348 | * If there is one, we open it. | |
349 | */ | |
82b27616 | 350 | |
64c4757e NB |
351 | if (mode !='D' && mode !='E' && ! (mode =='A' && scan)) { |
352 | if (!mddev) { | |
682c7051 | 353 | fprintf(stderr, Name ": an md device must be given in this mode\n"); |
64c4757e NB |
354 | exit(2); |
355 | } | |
356 | mdfd = open(mddev, O_RDWR, 0); | |
357 | if (mdfd < 0) { | |
682c7051 | 358 | fprintf(stderr,Name ": error opening %s: %s\n", |
64c4757e NB |
359 | mddev, strerror(errno)); |
360 | exit(1); | |
361 | } | |
362 | if (md_get_version(mdfd) <= 0) { | |
682c7051 | 363 | fprintf(stderr, Name ": %s does not appear to be an md device\n", |
64c4757e NB |
364 | mddev); |
365 | close(mdfd); | |
366 | exit(1); | |
367 | } | |
368 | } | |
369 | ||
82b27616 | 370 | |
64c4757e NB |
371 | rv =0; |
372 | switch(mode) { | |
373 | case '@':/* Management */ | |
374 | /* readonly, add/remove, readwrite, runstop */ | |
82b27616 | 375 | if (readonly>0) |
64c4757e NB |
376 | rv = Manage_ro(mddev, mdfd, readonly); |
377 | if (!rv && subdevs) | |
378 | rv = Manage_subdevs(mddev, mdfd, subdevs, subdev, devmodes); | |
82b27616 | 379 | if (!rv && readonly < 0) |
64c4757e NB |
380 | rv = Manage_ro(mddev, mdfd, readonly); |
381 | if (!rv && runstop) | |
382 | rv = Manage_runstop(mddev, mdfd, runstop); | |
383 | break; | |
384 | case 'A': /* Assemble */ | |
385 | rv = Assemble(mddev, mdfd, uuid, uuidset, configfile, scan, subdevs, subdev, readonly, runstop, verbose, force); | |
386 | break; | |
387 | case 'B': /* Build */ | |
388 | rv = Build(mddev, mdfd, chunk, level, raiddisks, subdevs,subdev); | |
389 | break; | |
390 | case 'C': /* Create */ | |
682c7051 NB |
391 | rv = Create(mddev, mdfd, chunk, level, layout, size, raiddisks, sparedisks, |
392 | subdevs,subdev,runstop, verbose); | |
64c4757e NB |
393 | break; |
394 | case 'D': /* Detail */ | |
395 | for (i=0; i<subdevs; i++) | |
396 | rv |= Detail(subdev[i]); | |
397 | break; | |
398 | case 'E': /* Examine */ | |
399 | for (i=0; i<subdevs; i++) | |
400 | rv |= Examine(subdev[i]); | |
401 | break; | |
402 | } | |
403 | exit(rv); | |
404 | } | |
405 |